diff options
author | jhibbits <jhibbits@FreeBSD.org> | 2014-01-15 05:19:37 +0000 |
---|---|---|
committer | jhibbits <jhibbits@FreeBSD.org> | 2014-01-15 05:19:37 +0000 |
commit | b1391adbae9577879c598fce281eb1179d26142f (patch) | |
tree | 4f4aad2d0495e4af20e1796de50d77105fd6f2b6 /cddl/contrib/opensolaris/lib/libdtrace | |
parent | 44cab258a2754ab2d2c9af0fabd9cd6bda591fb9 (diff) | |
download | FreeBSD-src-b1391adbae9577879c598fce281eb1179d26142f.zip FreeBSD-src-b1391adbae9577879c598fce281eb1179d26142f.tar.gz |
MFC r256543,r259245,r259421,r259668,r259674
r256543:
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.
r259245,r259421: (FBT)
FBT now does work fully on PowerPC.
Save r3 before using it for the trap check, else we end up saving the new r3,
containing the trap instruction encoding (0x7c810808), and restoring it back
with the frame on return. This caused it to panic on my ppc32 machine.
r259668,r259674:
Fix a typo in the FBT code.
Diffstat (limited to 'cddl/contrib/opensolaris/lib/libdtrace')
-rw-r--r-- | cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c | 89 | ||||
-rw-r--r-- | cddl/contrib/opensolaris/lib/libdtrace/powerpc/dt_isadep.c | 138 |
2 files changed, 215 insertions, 12 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); } diff --git a/cddl/contrib/opensolaris/lib/libdtrace/powerpc/dt_isadep.c b/cddl/contrib/opensolaris/lib/libdtrace/powerpc/dt_isadep.c index 1aeb95f..f4b02c9 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/powerpc/dt_isadep.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/powerpc/dt_isadep.c @@ -35,14 +35,26 @@ #include <dt_impl.h> #include <dt_pid.h> +#include <libproc_compat.h> + /*ARGSUSED*/ int dt_pid_create_entry_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp) { + ftp->ftps_type = DTFTP_ENTRY; + ftp->ftps_pc = (uintptr_t)symp->st_value; + ftp->ftps_size = (size_t)symp->st_size; + ftp->ftps_noffs = 1; + ftp->ftps_offs[0] = 0; + + if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { + dt_dprintf("fasttrap probe creation ioctl failed: %s\n", + strerror(errno)); + return (dt_set_errno(dtp, errno)); + } - dt_dprintf("%s: unimplemented\n", __func__); - return (DT_PROC_ERR); + return (1); } int @@ -50,8 +62,74 @@ dt_pid_create_return_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret) { - dt_dprintf("%s: unimplemented\n", __func__); - return (DT_PROC_ERR); + uintptr_t temp; + uint32_t *text; + int i; + int srdepth = 0; + + if ((text = malloc(symp->st_size + 4)) == NULL) { + dt_dprintf("mr sparkle: malloc() failed\n"); + return (DT_PROC_ERR); + } + + if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { + dt_dprintf("mr sparkle: Pread() failed\n"); + free(text); + return (DT_PROC_ERR); + } + + /* + * Leave a dummy instruction in the last slot to simplify edge + * conditions. + */ + text[symp->st_size / 4] = 0; + + ftp->ftps_type = DTFTP_RETURN; + ftp->ftps_pc = symp->st_value; + ftp->ftps_size = symp->st_size; + ftp->ftps_noffs = 0; + + for (i = 0; i < symp->st_size / 4; i++) { + + if ((text[i] & 0xfc000001) != 0x48000000 && + text[i] != 0x4e800020) + continue; + + /* + * Check for a jump within this function. If it's outside this + * function then it's a tail-call, so a return point. + */ + if ((text[i] & 0xfc000000) == 0x48000000) { + temp = (text[i] & 0x03fffffc); + /* Bit 30 denotes an absolute address. */ + if (!(text[i] & 0x02)) { + temp += symp->st_value + i * 4; + } + else { + /* Sign extend the absolute address. */ + if (temp & 0x02000000) { + temp |= (UINTPTR_MAX - 0x03ffffff); + } + } + if (temp >= symp->st_value && + temp <= (symp->st_value + symp->st_size)) + continue; + } + dt_dprintf("return at offset %x\n", i * 4); + ftp->ftps_offs[ftp->ftps_noffs++] = i * 4; + } + + free(text); + if (ftp->ftps_noffs > 0) { + if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { + dt_dprintf("fasttrap probe creation ioctl failed: %s\n", + strerror(errno)); + return (dt_set_errno(dtp, errno)); + } + } + + + return (ftp->ftps_noffs); } /*ARGSUSED*/ @@ -59,9 +137,22 @@ int dt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off) { + if (off & 0x3) + return (DT_PROC_ALIGN); + + ftp->ftps_type = DTFTP_OFFSETS; + ftp->ftps_pc = (uintptr_t)symp->st_value; + ftp->ftps_size = (size_t)symp->st_size; + ftp->ftps_noffs = 1; + ftp->ftps_offs[0] = off; + + if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { + dt_dprintf("fasttrap probe creation ioctl failed: %s\n", + strerror(errno)); + return (dt_set_errno(dtp, errno)); + } - dt_dprintf("%s: unimplemented\n", __func__); - return (DT_PROC_ERR); + return (1); } /*ARGSUSED*/ @@ -69,7 +160,38 @@ int dt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern) { + ulong_t i; + + ftp->ftps_type = DTFTP_OFFSETS; + ftp->ftps_pc = (uintptr_t)symp->st_value; + ftp->ftps_size = (size_t)symp->st_size; + ftp->ftps_noffs = 0; + + /* + * If we're matching against everything, just iterate through each + * instruction in the function, otherwise look for matching offset + * names by constructing the string and comparing it against the + * pattern. + */ + if (strcmp("*", pattern) == 0) { + for (i = 0; i < symp->st_size; i += 4) { + ftp->ftps_offs[ftp->ftps_noffs++] = i; + } + } else { + char name[sizeof (i) * 2 + 1]; + + for (i = 0; i < symp->st_size; i += 4) { + (void) sprintf(name, "%lx", i); + if (gmatch(name, pattern)) + ftp->ftps_offs[ftp->ftps_noffs++] = i; + } + } + + if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { + dt_dprintf("fasttrap probe creation ioctl failed: %s\n", + strerror(errno)); + return (dt_set_errno(dtp, errno)); + } - dt_dprintf("%s: unimplemented\n", __func__); - return (DT_PROC_ERR); + return (ftp->ftps_noffs); } |