Split arch_translate_address into this and arch_translate_address_dyn
The former is called when ltelf is available. The latter in dynamic context
when ltelf is not necessary anymore (or if the data is necessary, it will
have been copied out to struct library).
diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h
index 67f146e..6e258e7 100644
--- a/sysdeps/linux-gnu/ppc/arch.h
+++ b/sysdeps/linux-gnu/ppc/arch.h
@@ -30,6 +30,9 @@
struct arch_ltelf_data {
GElf_Addr plt_stub_vma;
struct library_symbol *stubs;
+ Elf_Data *opd_data;
+ GElf_Addr opd_base;
+ GElf_Xword opd_size;
int secure_plt;
};
diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c
index 54b2cac..012f7b2 100644
--- a/sysdeps/linux-gnu/ppc/plt.c
+++ b/sysdeps/linux-gnu/ppc/plt.c
@@ -216,9 +216,13 @@
}
}
+/* This entry point is called when ltelf is not available
+ * anymore--during runtime. At that point we don't have to concern
+ * ourselves with bias, as the values in OPD have been resolved
+ * already. */
int
-arch_translate_address(struct Process *proc,
- target_address_t addr, target_address_t *ret)
+arch_translate_address_dyn(struct Process *proc,
+ target_address_t addr, target_address_t *ret)
{
if (proc->e_machine == EM_PPC64) {
assert(host_powerpc64());
@@ -228,6 +232,8 @@
return -1;
}
*ret = (target_address_t)l;
+ fprintf(stderr, "arch_translate_address_dyn: %p->%p\n",
+ addr, *ret);
return 0;
}
@@ -235,6 +241,42 @@
return 0;
}
+int
+arch_translate_address(struct ltelf *lte,
+ target_address_t addr, target_address_t *ret)
+{
+ GElf_Xword offset = (GElf_Addr)addr - lte->arch.opd_base;
+ uint64_t value;
+ if (elf_read_u64(lte->arch.opd_data, offset, &value) < 0) {
+ error(0, 0, "static .opd translation of %p: %s", addr,
+ elf_errmsg(-1));
+ return -1;
+ }
+ *ret = (target_address_t)(value + lte->bias);
+ return 0;
+}
+
+static int
+load_opd_data(struct ltelf *lte, struct library *lib)
+{
+ Elf_Scn *sec;
+ GElf_Shdr shdr;
+ if (elf_get_section_named(lte, ".opd", &sec, &shdr) < 0) {
+ fail:
+ fprintf(stderr, "couldn't find .opd data\n");
+ return -1;
+ }
+
+ lte->arch.opd_data = elf_rawdata(sec, NULL);
+ if (lte->arch.opd_data == NULL)
+ goto fail;
+
+ lte->arch.opd_base = shdr.sh_addr + lte->bias;
+ lte->arch.opd_size = shdr.sh_size;
+
+ return 0;
+}
+
void *
sym2addr(struct Process *proc, struct library_symbol *sym)
{
@@ -351,6 +393,10 @@
int
arch_elf_init(struct ltelf *lte, struct library *lib)
{
+ if (lte->ehdr.e_machine == EM_PPC64
+ && load_opd_data(lte, lib) < 0)
+ return -1;
+
lte->arch.secure_plt = !(lte->plt_flags & SHF_EXECINSTR);
/* For PPC32 BSS, it is important whether the binary was
diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c
index b3b41c9..b5123fe 100644
--- a/sysdeps/linux-gnu/proc.c
+++ b/sysdeps/linux-gnu/proc.c
@@ -564,7 +564,7 @@
/* XXX The double cast should be removed when
* target_address_t becomes integral type. */
target_address_t addr = (target_address_t)(uintptr_t)rdbg.r_brk;
- if (arch_translate_address(proc, addr, &addr) < 0)
+ if (arch_translate_address_dyn(proc, addr, &addr) < 0)
goto fail;
struct breakpoint *rdebug_bp = insert_breakpoint(proc, addr, NULL);