summaryrefslogtreecommitdiffstats
path: root/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c
diff options
context:
space:
mode:
authorjhibbits <jhibbits@FreeBSD.org>2013-10-15 15:00:29 +0000
committerjhibbits <jhibbits@FreeBSD.org>2013-10-15 15:00:29 +0000
commit0b9629ab6aa50543b52662bc77d8e116517dc6bb (patch)
tree8e64b4f75c8e219a401f5b73b50c31c41b239708 /cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c
parent3b8af4143e19aab252e8bb964fdf98a090e9cb41 (diff)
downloadFreeBSD-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.c89
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);
}
OpenPOWER on IntegriCloud