Support tracing prelinked stripped PPC64 binaries
- distinguish stub breakpoints from unresolved PLT breakpoints. The latter
have special on_continue handler that just moves IP
- unprelinked stripped case not supported yet
diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c
index 49fa7b4..6cf252a 100644
--- a/sysdeps/linux-gnu/ppc/plt.c
+++ b/sysdeps/linux-gnu/ppc/plt.c
@@ -9,6 +9,7 @@
#include "proc.h"
#include "common.h"
#include "library.h"
+#include "breakpoint.h"
/* There are two PLT types on 32-bit PPC: old-style, BSS PLT, and
* new-style "secure" PLT. We can tell one from the other by the
@@ -78,6 +79,7 @@
*/
#define PPC_PLT_STUB_SIZE 16
+#define PPC64_PLT_STUB_SIZE 8 //xxx
static inline int
host_powerpc64()
@@ -100,10 +102,11 @@
return rela->r_offset;
} else {
- assert(lte->ehdr.e_machine == EM_PPC64);
- fprintf(stderr, "PPC64\n");
- abort();
- return rela->r_offset;
+ /* If we get here, we don't have stub symbols. In
+ * that case we put brakpoints to PLT entries the same
+ * as the PPC32 secure PLT case does. */
+ assert(lte->arch.plt_stub_vma != 0);
+ return lte->arch.plt_stub_vma + PPC64_PLT_STUB_SIZE * ndx;
}
}
@@ -111,7 +114,8 @@
arch_translate_address(struct Process *proc,
target_address_t addr, target_address_t *ret)
{
- if (host_powerpc64() && proc->e_machine == EM_PPC64) {
+ if (proc->e_machine == EM_PPC64) {
+ assert(host_powerpc64());
long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0);
fprintf(stderr, "arch_translate_address %p->%#lx\n",
addr, l);
@@ -273,6 +277,12 @@
return load_dynamic_entry(lte, DT_PPC_GOT, ppcgotp);
}
+static int
+load_ppc64_glink(struct ltelf *lte, GElf_Addr *glinkp)
+{
+ return load_dynamic_entry(lte, DT_PPC64_GLINK, glinkp);
+}
+
int
arch_elf_init(struct ltelf *lte)
{
@@ -290,6 +300,16 @@
lte->arch.plt_stub_vma = glink_vma
- (GElf_Addr)count * PPC_PLT_STUB_SIZE;
debug(1, "stub_vma is %#" PRIx64, lte->arch.plt_stub_vma);
+
+ } else if (lte->ehdr.e_machine == EM_PPC64) {
+ GElf_Addr glink_vma;
+ if (load_ppc64_glink(lte, &glink_vma) < 0) {
+ fprintf(stderr, "Couldn't find DT_PPC64_GLINK.\n");
+ return -1;
+ }
+
+ /* The first glink stub starts at offset 32. */
+ lte->arch.plt_stub_vma = glink_vma + 32;
}
/* Override the value that we gleaned from flags on the .plt
@@ -365,6 +385,7 @@
= (target_address_t)sym.st_value + lte->bias;
library_symbol_init(libsym, addr, sym_name, 1,
LS_TOPLT_EXEC);
+ libsym->arch.type = PPC64PLT_STUB;
libsym->next = lte->arch.stubs;
lte->arch.stubs = libsym;
}
@@ -412,8 +433,67 @@
return plt_ok;
}
- fprintf(stderr, "NO STUBS!\n");
- abort();
+ /* We don't have stub symbols. Find corresponding .plt slot,
+ * and check whether it contains the corresponding PLT address
+ * (or 0 if the dynamic linker hasn't run yet). N.B. we don't
+ * want read this from ELF file, but from process image. That
+ * makes a difference if we are attaching to a running
+ * process. */
+
+ GElf_Addr plt_entry_addr = arch_plt_sym_val(lte, ndx, rela);
+ GElf_Addr plt_slot_addr = rela->r_offset;
+ assert(plt_slot_addr >= lte->plt_addr
+ || plt_slot_addr < lte->plt_addr + lte->plt_size);
+
+ long plt_slot_value = ptrace(PTRACE_PEEKTEXT, proc->pid,
+ plt_slot_addr, 0);
+ if (plt_slot_value == -1 && errno != 0) {
+ error(0, errno, "ptrace .plt slot value @%#" PRIx64,
+ plt_slot_addr);
+ return plt_fail;
+ }
+
+ char *name = strdup(a_name);
+ struct library_symbol *libsym = malloc(sizeof(*libsym));
+ if (name == NULL || libsym == NULL) {
+ error(0, errno, "allocation for .plt slot");
+ fail:
+ free(name);
+ free(libsym);
+ return plt_fail;
+ }
+
+ library_symbol_init(libsym, (target_address_t)plt_entry_addr,
+ name, 1, LS_TOPLT_EXEC);
+ if ((GElf_Addr)plt_slot_value == plt_entry_addr
+ || plt_slot_value == 0) {
+ libsym->arch.type = PPC64PLT_UNRESOLVED;
+ libsym->arch.orig_addr = 0;
+ } else {
+ /* Unresolve the .plt slot. If the binary was
+ * prelinked, this makes the code invalid, because in
+ * case of prelinked binary, the dynamic linker
+ * doesn't update .plt[0] and .plt[1] with addresses
+ * of the resover. But we don't care, we will never
+ * need to enter the resolver. That just means that
+ * we have to un-un-resolve this back before we
+ * detach, which is nothing new: we already need to
+ * retract breakpoints. */
+ /* We only modify plt_entry[0], which holds the
+ * resolved address of the routine. We keep the TOC
+ * and environment pointers intact. Hence the only
+ * adjustment that we need to do is to IP. */
+ if (ptrace(PTRACE_POKETEXT, proc->pid,
+ plt_slot_addr, plt_entry_addr) < 0) {
+ error(0, errno, "unresolve .plt slot");
+ goto fail;
+ }
+ libsym->arch.type = PPC64PLT_RESOLVED;
+ libsym->arch.orig_addr = plt_slot_value;
+ }
+
+ *ret = libsym;
+ return plt_ok;
}
void
@@ -427,3 +507,41 @@
sym = next;
}
}
+
+static void
+ppc64_resolved_bp_continue(struct breakpoint *bp, struct Process *proc)
+{
+ fprintf(stderr, "ppc64_resolved_bp_continue\n");
+ set_instruction_pointer(proc,
+ (target_address_t)bp->libsym->arch.orig_addr);
+ continue_process(proc->pid);
+}
+
+int
+arch_breakpoint_init(struct Process *proc, struct breakpoint *bp)
+{
+ if (proc->e_machine == EM_PPC
+ || bp->libsym == NULL
+ || bp->libsym->arch.type == PPC64PLT_STUB)
+ return 0;
+
+ if (bp->libsym->arch.type == PPC64PLT_RESOLVED) {
+ fprintf(stderr, "arch_breakpoint_init RESOLVED\n");
+ static struct bp_callbacks resolved_cbs = {
+ .on_continue = ppc64_resolved_bp_continue,
+ };
+ breakpoint_set_callbacks(bp, &resolved_cbs);
+
+ } else {
+ fprintf(stderr, "arch_breakpoint_init UNRESOLVED\n");
+ fprintf(stderr, "a.k.a the insane case\n");
+ abort();
+ }
+
+ return 0;
+}
+
+void
+arch_breakpoint_destroy(struct breakpoint *bp)
+{
+}