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/ChangeLog b/ChangeLog
index d7dcf56..a4a4696 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
2012-04-30 Petr Machata <pmachata@redhat.com>
+ * library.h (arch_translate_address): First argument is ltelf.
+ (arch_translate_address_dyn): New function.
+ * sysdeps/linux-gnu/ppc/plt.c: Implement above, use .opd to
+ translate addresses.
+ * breakpoints.c: Update to the above, add default implementation.
+ * ltrace-elf.c, sysdeps/linux-gnu/proc.c: Update callers.
+
+2012-04-30 Petr Machata <pmachata@redhat.com>
+
* ltrace-elf.h (elf_get_section_named): New function.
(elf_get_section_covering, elf_get_section_type): Likewise.
diff --git a/breakpoints.c b/breakpoints.c
index 2b1f304..9536266 100644
--- a/breakpoints.c
+++ b/breakpoints.c
@@ -16,7 +16,16 @@
#ifndef ARCH_HAVE_TRANSLATE_ADDRESS
int
-arch_translate_address(struct Process *proc,
+arch_translate_address_dyn(struct Process *proc,
+ target_address_t addr, target_address_t *ret)
+{
+ *ret = addr;
+ return 0;
+}
+
+struct ltelf;
+int
+arch_translate_address(struct ltelf *lte,
target_address_t addr, target_address_t *ret)
{
*ret = addr;
diff --git a/library.h b/library.h
index 3122e36..c387b02 100644
--- a/library.h
+++ b/library.h
@@ -181,11 +181,16 @@
* target_address_t (which should also be in backend.h, I reckon), so
* stuff it here for the time being. */
/* This function is implemented in the back end. It is called for all
- * raw addresses as gleaned from symbol tables etc. If necessary on
+ * raw addresses as read from symbol tables etc. If necessary on
* given architecture, this function should translate the address
* according to .opd or other indirection mechanism. Returns 0 on
* success and a negative value on failure. */
-int arch_translate_address(struct Process *proc,
+struct ltelf;
+int arch_translate_address(struct ltelf *lte,
target_address_t addr, target_address_t *ret);
+/* This is the same function as arch_translate_address, except it's
+ * used at the point that we don't have ELF available anymore. */
+int arch_translate_address_dyn(struct Process *proc,
+ target_address_t addr, target_address_t *ret);
#endif /* _LIBRARY_H_ */
diff --git a/ltrace-elf.c b/ltrace-elf.c
index 19f1c64..a311c5f 100644
--- a/ltrace-elf.c
+++ b/ltrace-elf.c
@@ -618,23 +618,13 @@
* translate those. */
if (secflags[sym.st_shndx] & SHF_EXECINSTR) {
naddr = addr;
- } else if (arch_translate_address(proc, addr, &naddr) < 0) {
+ } else if (arch_translate_address(lte, addr, &naddr) < 0) {
fprintf(stderr,
"couldn't translate address of %s@%s: %s\n",
name, lib->soname, strerror(errno));
continue;
}
- /* If the translation actually took place, and wasn't
- * a no-op, then bias again. XXX We shouldn't apply
- * second bias for libraries that were open at the
- * time that we attached. In fact what we should do
- * is look at each translated address, whether it
- * falls into a SHF_EXECINSTR section. If it does,
- * it's most likely already translated. */
- if (addr != naddr)
- naddr += lte->bias;
-
char *full_name;
if (lib->type != LT_LIBTYPE_MAIN) {
full_name = malloc(strlen(name) + 1 + lib_len + 1);
@@ -742,7 +732,7 @@
/* XXX The double cast should be removed when
* target_address_t becomes integral type. */
target_address_t entry = (target_address_t)(uintptr_t)lte.entry_addr;
- if (arch_translate_address(proc, entry, &entry) < 0)
+ if (arch_translate_address(<e, entry, &entry) < 0)
goto fail;
/* XXX The double cast should be removed when
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);