diff options
author | jhibbits <jhibbits@FreeBSD.org> | 2013-10-15 15:00:29 +0000 |
---|---|---|
committer | jhibbits <jhibbits@FreeBSD.org> | 2013-10-15 15:00:29 +0000 |
commit | 0b9629ab6aa50543b52662bc77d8e116517dc6bb (patch) | |
tree | 8e64b4f75c8e219a401f5b73b50c31c41b239708 /cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c | |
parent | 3b8af4143e19aab252e8bb964fdf98a090e9cb41 (diff) | |
download | FreeBSD-src-0b9629ab6aa50543b52662bc77d8e116517dc6bb.zip FreeBSD-src-0b9629ab6aa50543b52662bc77d8e116517dc6bb.tar.gz |
Add fasttrap for PowerPC. This is the last piece of the dtrace/ppc puzzle.
It's incomplete, it doesn't contain full instruction emulation, but it should be
sufficient for most cases.
MFC after: 1 month
Diffstat (limited to 'cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c')
-rw-r--r-- | cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c | 89 |
1 files changed, 85 insertions, 4 deletions
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c index 2d0428a..ee68479 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c @@ -242,8 +242,14 @@ printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); /* XXX */ printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); #elif defined(__powerpc__) -/* XXX */ -printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); + /* + * Add 4 bytes to hit the low half of this 64-bit + * big-endian address. + */ + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset + 4; + rel->r_info = ELF32_R_INFO(count + dep->de_global, + R_PPC_REL32); #elif defined(__sparc) /* * Add 4 bytes to hit the low half of this 64-bit @@ -423,7 +429,10 @@ prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep) #elif defined(__mips__) /* XXX */ #elif defined(__powerpc__) -/* XXX */ + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset; + rel->r_info = ELF64_R_INFO(count + dep->de_global, + R_PPC64_REL64); #elif defined(__i386) || defined(__amd64) rel->r_offset = s->dofs_offset + dofr[j].dofr_offset; @@ -824,12 +833,84 @@ printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); return (0); } #elif defined(__powerpc__) +/* The sentinel is 'xor r3,r3,r3'. */ +#define DT_OP_XOR_R3 0x7c631a78 + +#define DT_OP_NOP 0x60000000 +#define DT_OP_BLR 0x4e800020 + +/* This captures all forms of branching to address. */ +#define DT_IS_BRANCH(inst) ((inst & 0xfc000000) == 0x48000000) +#define DT_IS_BL(inst) (DT_IS_BRANCH(inst) && (inst & 0x01)) + /* XXX */ static int dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, uint32_t *off) { -printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); + uint32_t *ip; + + if ((rela->r_offset & (sizeof (uint32_t) - 1)) != 0) + return (-1); + + /*LINTED*/ + ip = (uint32_t *)(p + rela->r_offset); + + /* + * We only know about some specific relocation types. + */ + if (GELF_R_TYPE(rela->r_info) != R_PPC_REL24 && + GELF_R_TYPE(rela->r_info) != R_PPC_PLTREL24) + return (-1); + + /* + * We may have already processed this object file in an earlier linker + * invocation. Check to see if the present instruction sequence matches + * the one we would install below. + */ + if (isenabled) { + if (ip[0] == DT_OP_XOR_R3) { + (*off) += sizeof (ip[0]); + return (0); + } + } else { + if (ip[0] == DT_OP_NOP) { + (*off) += sizeof (ip[0]); + return (0); + } + } + + /* + * We only expect branch to address instructions. + */ + if (!DT_IS_BRANCH(ip[0])) { + dt_dprintf("found %x instead of a branch instruction at %llx\n", + ip[0], (u_longlong_t)rela->r_offset); + return (-1); + } + + if (isenabled) { + /* + * It would necessarily indicate incorrect usage if an is- + * enabled probe were tail-called so flag that as an error. + * It's also potentially (very) tricky to handle gracefully, + * but could be done if this were a desired use scenario. + */ + if (!DT_IS_BL(ip[0])) { + dt_dprintf("tail call to is-enabled probe at %llx\n", + (u_longlong_t)rela->r_offset); + return (-1); + } + + ip[0] = DT_OP_XOR_R3; + (*off) += sizeof (ip[0]); + } else { + if (DT_IS_BL(ip[0])) + ip[0] = DT_OP_NOP; + else + ip[0] = DT_OP_BLR; + } + return (0); } |