Move arch-specific bits from ltrace-elf.c to PPC and MIPS back ends
diff --git a/common.h b/common.h
index 00fd27b..6d7892e 100644
--- a/common.h
+++ b/common.h
@@ -215,11 +215,14 @@
* If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the
* request, OS_LTRACE_EXITING is called when the next event is
* generated. Therefore it's called in "safe" context, without
- * re-entrancy concerns, but it's only called after an even is
+ * re-entrancy concerns, but it's only called after an event is
* generated. */
int os_ltrace_exiting_sighandler(void);
void os_ltrace_exiting(void);
+int arch_elf_dynamic_tag(struct ltelf *lte, GElf_Dyn dyn);
+int arch_elf_init(struct ltelf *lte);
+
extern struct ltelf main_lte;
#endif
diff --git a/ltrace-elf.c b/ltrace-elf.c
index 231b56e..de1b0fc 100644
--- a/ltrace-elf.c
+++ b/ltrace-elf.c
@@ -24,9 +24,23 @@
# define DT_PPC_GOT (DT_LOPROC + 0)
#endif
-#define PPC_PLT_STUB_SIZE 16
-static Elf_Data *loaddata(Elf_Scn *scn, GElf_Shdr *shdr)
+#ifndef ARCH_HAVE_LTELF_DATA
+int
+arch_elf_dynamic_tag(struct ltelf *lte, GElf_Dyn dyn)
+{
+ return 0;
+}
+
+int
+arch_elf_init(struct ltelf *lte)
+{
+ return 0;
+}
+#endif
+
+Elf_Data *
+elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr)
{
Elf_Data *data = elf_getdata(scn, NULL);
if (data == NULL || elf_getdata(scn, data) != NULL
@@ -35,15 +49,17 @@
return data;
}
-static int inside(GElf_Addr addr, GElf_Shdr *shdr)
+static int
+inside(GElf_Addr addr, GElf_Shdr *shdr)
{
return addr >= shdr->sh_addr
&& addr < shdr->sh_addr + shdr->sh_size;
}
-static int maybe_pick_section(GElf_Addr addr,
- Elf_Scn *in_sec, GElf_Shdr *in_shdr,
- Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr)
+static int
+section_covers(GElf_Addr addr,
+ Elf_Scn *in_sec, GElf_Shdr *in_shdr,
+ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr)
{
if (inside(addr, in_shdr)) {
*tgt_sec = in_sec;
@@ -53,8 +69,9 @@
return 0;
}
-static int get_section_covering(struct ltelf *lte, GElf_Addr addr,
- Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr)
+int
+elf_get_section_covering(struct ltelf *lte, GElf_Addr addr,
+ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr)
{
int i;
for (i = 1; i < lte->ehdr.e_shnum; ++i) {
@@ -64,68 +81,49 @@
scn = elf_getscn(lte->elf, i);
if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) {
debug(1, "Couldn't read section or header.");
+ return -1;
+ }
+
+ if (section_covers(addr, scn, &shdr, tgt_sec, tgt_shdr))
return 0;
- }
-
- if (maybe_pick_section(addr, scn, &shdr, tgt_sec, tgt_shdr))
- return 1;
}
+ return -1;
+}
+
+static int
+need_data(Elf_Data *data, size_t offset, size_t size)
+{
+ assert(data != NULL);
+ if (data->d_size < size || offset > data->d_size - size) {
+ debug(1, "Not enough data to read %zd-byte value"
+ " at offset %zd.", size, offset);
+ return -1;
+ }
return 0;
}
-static GElf_Addr read32be(Elf_Data *data, size_t offset)
-{
- if (data->d_size < offset + 4) {
- debug(1, "Not enough data to read 32bit value at offset %zd.",
- offset);
- return 0;
+#define DEF_READER(NAME, SIZE) \
+ int \
+ NAME(Elf_Data *data, size_t offset, uint##SIZE##_t *retp) \
+ { \
+ if (!need_data(data, offset, SIZE / 8) < 0) \
+ return -1; \
+ \
+ union { \
+ uint##SIZE##_t dst; \
+ char buf[0]; \
+ } u; \
+ memcpy(u.buf, data->d_buf + offset, sizeof(u.dst)); \
+ *retp = u.dst; \
+ return 0; \
}
- unsigned char const *buf = data->d_buf + offset;
- return ((Elf32_Word)buf[0] << 24)
- | ((Elf32_Word)buf[1] << 16)
- | ((Elf32_Word)buf[2] << 8)
- | ((Elf32_Word)buf[3]);
-}
+DEF_READER(elf_read_u16, 16)
+DEF_READER(elf_read_u32, 32)
+DEF_READER(elf_read_u64, 64)
-static GElf_Addr get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot,
- Elf_Data *plt_data)
-{
- Elf_Scn *ppcgot_sec = NULL;
- GElf_Shdr ppcgot_shdr;
- if (ppcgot != 0
- && !get_section_covering(lte, ppcgot, &ppcgot_sec, &ppcgot_shdr))
- // xxx should be the log out
- fprintf(stderr,
- "DT_PPC_GOT=%#" PRIx64 ", but no such section found.\n",
- ppcgot);
-
- if (ppcgot_sec != NULL) {
- Elf_Data *data = loaddata(ppcgot_sec, &ppcgot_shdr);
- if (data == NULL
- || data->d_size < 8 )
- debug(1, "Couldn't read GOT data.");
- else {
- // where PPCGOT begins in .got
- size_t offset = ppcgot - ppcgot_shdr.sh_addr;
- GElf_Addr glink_vma = read32be(data, offset + 4);
- if (glink_vma != 0) {
- debug(1, "PPC GOT glink_vma address: %#" PRIx64,
- glink_vma);
- return glink_vma;
- }
- }
- }
-
- if (plt_data != NULL) {
- GElf_Addr glink_vma = read32be(plt_data, 0);
- debug(1, ".plt glink_vma address: %#" PRIx64, glink_vma);
- return glink_vma;
- }
-
- return 0;
-}
+#undef DEF_READER
int
open_elf(struct ltelf *lte, const char *filename)
@@ -171,14 +169,12 @@
return 0;
}
-/* XXX temporarily non-static */
-int
-do_init_elf(struct ltelf *lte, const char *filename, GElf_Addr base)
+static int
+do_init_elf(struct ltelf *lte, const char *filename, GElf_Addr bias)
{
int i;
GElf_Addr relplt_addr = 0;
GElf_Addr soname_offset = 0;
- size_t relplt_size = 0;
debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename);
debug(1, "Reading ELF from %s...", filename);
@@ -301,58 +297,26 @@
error(EXIT_FAILURE, 0,
"Couldn't get .dynamic data from \"%s\"",
filename);
-#ifdef __mips__
-/**
- MIPS ABI Supplement:
-
- DT_PLTGOT This member holds the address of the .got section.
-
- DT_MIPS_SYMTABNO This member holds the number of entries in the
- .dynsym section.
-
- DT_MIPS_LOCAL_GOTNO This member holds the number of local global
- offset table entries.
-
- DT_MIPS_GOTSYM This member holds the index of the first dyamic
- symbol table entry that corresponds to an entry in the gobal offset
- table.
-
- */
- if(dyn.d_tag==DT_PLTGOT){
- lte->pltgot_addr=dyn.d_un.d_ptr;
- }
- if(dyn.d_tag==DT_MIPS_LOCAL_GOTNO){
- lte->mips_local_gotno=dyn.d_un.d_val;
- }
- if(dyn.d_tag==DT_MIPS_GOTSYM){
- lte->mips_gotsym=dyn.d_un.d_val;
- }
-#endif // __mips__
if (dyn.d_tag == DT_JMPREL)
relplt_addr = dyn.d_un.d_ptr;
else if (dyn.d_tag == DT_PLTRELSZ)
- relplt_size = dyn.d_un.d_val;
- else if (dyn.d_tag == DT_PPC_GOT) {
- ppcgot = dyn.d_un.d_val;
- debug(1, "ppcgot %#" PRIx64, ppcgot);
- } else if (dyn.d_tag == DT_SONAME) {
+ lte->relplt_size = dyn.d_un.d_val;
+ else if (dyn.d_tag == DT_SONAME)
soname_offset = dyn.d_un.d_val;
- }
+ else if (arch_elf_dynamic_tag(lte, dyn) < 0)
+ goto backend_fail;
}
} else if (shdr.sh_type == SHT_PROGBITS
|| shdr.sh_type == SHT_NOBITS) {
if (strcmp(name, ".plt") == 0) {
lte->plt_addr = shdr.sh_addr;
lte->plt_size = shdr.sh_size;
- if (shdr.sh_flags & SHF_EXECINSTR) {
+ lte->plt_data = elf_loaddata(scn, &shdr);
+ if (lte->plt_data == NULL)
+ fprintf(stderr,
+ "Can't load .plt data\n");
+ if (shdr.sh_flags & SHF_EXECINSTR)
lte->lte_flags |= LTE_PLT_EXECUTABLE;
- }
- if (lte->ehdr.e_machine == EM_PPC) {
- plt_data = loaddata(scn, &shdr);
- if (plt_data == NULL)
- fprintf(stderr,
- "Can't load .plt data\n");
- }
}
#ifdef ARCH_SUPPORTS_OPD
else if (strcmp(name, ".opd") == 0) {
@@ -368,25 +332,21 @@
error(EXIT_FAILURE, 0,
"Couldn't find .dynsym or .dynstr in \"%s\"", filename);
+ if (arch_elf_init(lte) < 0) {
+ backend_fail:
+ fprintf(stderr, "Backend initialization failed.\n");
+ return -1;
+ }
+
if (!relplt_addr || !lte->plt_addr) {
debug(1, "%s has no PLT relocations", filename);
lte->relplt = NULL;
lte->relplt_count = 0;
- } else if (relplt_size == 0) {
+ } else if (lte->relplt_size == 0) {
debug(1, "%s has unknown PLT size", filename);
lte->relplt = NULL;
lte->relplt_count = 0;
} else {
- if (lte->ehdr.e_machine == EM_PPC) {
- GElf_Addr glink_vma
- = get_glink_vma(lte, ppcgot, plt_data);
-
- assert (relplt_size % 12 == 0);
- size_t count = relplt_size / 12; // size of RELA entry
- lte->plt_stub_vma = glink_vma
- - (GElf_Addr)count * PPC_PLT_STUB_SIZE;
- debug(1, "stub_vma is %#" PRIx64, lte->plt_stub_vma);
- }
for (i = 1; i < lte->ehdr.e_shnum; ++i) {
Elf_Scn *scn;
@@ -398,7 +358,7 @@
"Couldn't get section header from \"%s\"",
filename);
if (shdr.sh_addr == relplt_addr
- && shdr.sh_size == relplt_size) {
+ && shdr.sh_size == lte->relplt_size) {
lte->relplt = elf_getdata(scn, NULL);
lte->relplt_count =
shdr.sh_size / shdr.sh_entsize;
@@ -461,6 +421,7 @@
struct ltelf lte = {};
if (do_init_elf(<e, filename, base) < 0)
return NULL;
+ proc->e_machine = lte.ehdr.e_machine;
struct library *lib = malloc(sizeof(*lib));
char *soname = NULL;
@@ -488,7 +449,6 @@
GElf_Rel rel;
GElf_Rela rela;
GElf_Sym sym;
- GElf_Addr addr;
void *ret;
if (lte.relplt->d_type == ELF_T_REL) {
@@ -518,15 +478,9 @@
goto fail;
}
- enum toplt pltt;
- if (sym.st_value == 0 && lte.plt_stub_vma != 0) {
- pltt = LS_TOPLT_EXEC;
- addr = lte.plt_stub_vma + PPC_PLT_STUB_SIZE * i;
- } else {
- pltt = PLTS_ARE_EXECUTABLE(<e)
- ? LS_TOPLT_EXEC : LS_TOPLT_POINT;
- addr = arch_plt_sym_val(<e, i, &rela);
- }
+ enum toplt pltt = PLTS_ARE_EXECUTABLE(<e)
+ ? LS_TOPLT_EXEC : LS_TOPLT_POINT;
+ GElf_Addr addr = arch_plt_sym_val(<e, i, &rela);
struct library_symbol *libsym = malloc(sizeof(*libsym));
if (libsym == NULL)
diff --git a/ltrace-elf.h b/ltrace-elf.h
index d579f4f..8a47cc1 100644
--- a/ltrace-elf.h
+++ b/ltrace-elf.h
@@ -4,6 +4,15 @@
#include <gelf.h>
#include <stdlib.h>
+#define DEFINING_LTELF
+#include "arch.h"
+#undef DEFINING_LTELF
+
+#ifndef ARCH_HAVE_LTELF_DATA
+struct arch_ltelf_data {
+};
+#endif
+
struct Process;
struct library;
@@ -23,6 +32,7 @@
GElf_Addr plt_addr;
size_t plt_size;
Elf_Data *relplt;
+ Elf_Data *plt_data;
size_t relplt_count;
Elf_Data *symtab;
const char *strtab;
@@ -34,15 +44,11 @@
int lte_flags;
GElf_Addr dyn_addr;
size_t dyn_sz;
+ size_t relplt_size;
GElf_Addr bias;
GElf_Addr entry_addr;
GElf_Addr base_addr;
-#ifdef __mips__
- size_t pltgot_addr;
- size_t mips_local_gotno;
- size_t mips_gotsym;
-#endif // __mips__
- GElf_Addr plt_stub_vma;
+ struct arch_ltelf_data arch;
};
#define ELF_MAX_SEGMENTS 50
@@ -63,6 +69,18 @@
GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *);
+Elf_Data *elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr);
+int elf_get_section_covering(struct ltelf *lte, GElf_Addr addr,
+ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr);
+
+/* Read, respectively, 2, 4, or 8 bytes from Elf data at given OFFSET,
+ * and store it in *RETP. Returns 0 on success or a negative value if
+ * there's not enough data. */
+int elf_read_u16(Elf_Data *data, size_t offset, uint16_t *retp);
+int elf_read_u32(Elf_Data *data, size_t offset, uint32_t *retp);
+int elf_read_u64(Elf_Data *data, size_t offset, uint64_t *retp);
+
+
#if __WORDSIZE == 32
#define PRI_ELF_ADDR PRIx32
#define GELF_ADDR_CAST(x) (void *)(uint32_t)(x)
diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h
index dd0ca35..e2fdbf8 100644
--- a/sysdeps/linux-gnu/mipsel/arch.h
+++ b/sysdeps/linux-gnu/mipsel/arch.h
@@ -7,3 +7,12 @@
#define PLTs_INIT_BY_HERE "_start"
#define E_ENTRY_NAME "_start"
+
+#ifdef DEFINING_LTELF
+# define ARCH_HAVE_LTELF_DATA
+struct arch_ltelf_data {
+ size_t pltgot_addr;
+ size_t mips_local_gotno;
+ size_t mips_gotsym;
+};
+#endif
diff --git a/sysdeps/linux-gnu/mipsel/plt.c b/sysdeps/linux-gnu/mipsel/plt.c
index 86f7cb4..77807b1 100644
--- a/sysdeps/linux-gnu/mipsel/plt.c
+++ b/sysdeps/linux-gnu/mipsel/plt.c
@@ -70,4 +70,41 @@
return (void *)ret;;
}
+/**
+ MIPS ABI Supplement:
+
+ DT_PLTGOT This member holds the address of the .got section.
+
+ DT_MIPS_SYMTABNO This member holds the number of entries in the
+ .dynsym section.
+
+ DT_MIPS_LOCAL_GOTNO This member holds the number of local global
+ offset table entries.
+
+ DT_MIPS_GOTSYM This member holds the index of the first dyamic
+ symbol table entry that corresponds to an entry in the gobal offset
+ table.
+
+ */
+int
+arch_elf_dynamic_tag(struct ltelf *lte, GElf_Dyn dyn)
+{
+ if(dyn.d_tag == DT_PLTGOT) {
+ lte->arch.pltgot_addr = dyn.d_un.d_ptr;
+ }
+ if(dyn.d_tag == DT_MIPS_LOCAL_GOTNO){
+ lte->arch.mips_local_gotno = dyn.d_un.d_val;
+ }
+ if(dyn.d_tag == DT_MIPS_GOTSYM){
+ lte->arch.mips_gotsym = dyn.d_un.d_val;
+ }
+ return 0;
+}
+
+int
+arch_elf_init(struct ltelf *lte)
+{
+ return 0;
+}
+
/**@}*/
diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h
index 64c1821..15673e0 100644
--- a/sysdeps/linux-gnu/ppc/arch.h
+++ b/sysdeps/linux-gnu/ppc/arch.h
@@ -23,3 +23,11 @@
#if (PPC_NOP_LENGTH != BREAKPOINT_LENGTH)
#error "Length of the breakpoint value not equal to the length of a nop instruction"
#endif
+
+#ifdef DEFINING_LTELF
+# define ARCH_HAVE_LTELF_DATA
+struct arch_ltelf_data {
+ GElf_Addr ppcgot;
+ GElf_Addr plt_stub_vma;
+};
+#endif
diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c
index 707d9d9..94a500b 100644
--- a/sysdeps/linux-gnu/ppc/plt.c
+++ b/sysdeps/linux-gnu/ppc/plt.c
@@ -1,11 +1,60 @@
#include <gelf.h>
#include <sys/ptrace.h>
+#include <errno.h>
+#include <error.h>
+#include <inttypes.h>
+#include <assert.h>
+
#include "proc.h"
#include "common.h"
+#include "library.h"
+
+#define PPC_PLT_STUB_SIZE 16
+
+static inline int
+is_ppc64()
+{
+#ifdef __powerpc64__
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+static inline int
+is_ppc32()
+{
+ return !is_ppc64();
+}
GElf_Addr
arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) {
- return rela->r_offset;
+ if (lte->arch.plt_stub_vma != 0)
+ return lte->arch.plt_stub_vma + PPC_PLT_STUB_SIZE * ndx;
+ else
+ return rela->r_offset;
+}
+
+int
+arch_translate_address(struct Process *proc,
+ target_address_t addr, target_address_t *ret)
+{
+ if (is_ppc64() && proc->e_machine == EM_PPC64) {
+ fprintf (stderr, "32-bit\n");
+
+ long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0);
+ fprintf(stderr, "arch_translate_address %p->%#lx\n",
+ addr, l);
+ if (l == -1 && errno) {
+ error(0, errno, ".opd translation of %p", addr);
+ return -1;
+ }
+ *ret = (target_address_t)l;
+ return 0;
+ }
+
+ *ret = addr;
+ return 0;
}
/* XXX Apparently PPC64 doesn't support PLT breakpoints. */
@@ -61,3 +110,88 @@
return addr;
}
+
+static GElf_Addr
+get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, Elf_Data *plt_data)
+{
+ Elf_Scn *ppcgot_sec = NULL;
+ GElf_Shdr ppcgot_shdr;
+ if (ppcgot != 0
+ && elf_get_section_covering(lte, ppcgot,
+ &ppcgot_sec, &ppcgot_shdr) < 0)
+ // xxx should be the log out
+ fprintf(stderr,
+ "DT_PPC_GOT=%#" PRIx64 ", but no such section found.\n",
+ ppcgot);
+
+ if (ppcgot_sec != NULL) {
+ Elf_Data *data = elf_loaddata(ppcgot_sec, &ppcgot_shdr);
+ if (data == NULL || data->d_size < 8 ) {
+ fprintf(stderr, "Couldn't read GOT data.\n");
+ } else {
+ // where PPCGOT begins in .got
+ size_t offset = ppcgot - ppcgot_shdr.sh_addr;
+ assert(offset % 4 == 0);
+ uint32_t glink_vma;
+ if (elf_read_u32(data, offset + 4, &glink_vma) < 0) {
+ fprintf(stderr,
+ "Couldn't read glink VMA address"
+ " at %zd@GOT\n", offset);
+ return 0;
+ }
+ if (glink_vma != 0) {
+ debug(1, "PPC GOT glink_vma address: %#" PRIx32,
+ glink_vma);
+ fprintf(stderr, "PPC GOT glink_vma "
+ "address: %#"PRIx32"\n", glink_vma);
+ return (GElf_Addr)glink_vma;
+ }
+ }
+ }
+
+ if (plt_data != NULL) {
+ uint32_t glink_vma;
+ if (elf_read_u32(plt_data, 0, &glink_vma) < 0) {
+ fprintf(stderr,
+ "Couldn't read glink VMA address at 0@.plt\n");
+ return 0;
+ }
+ debug(1, ".plt glink_vma address: %#" PRIx32, glink_vma);
+ fprintf(stderr, ".plt glink_vma address: "
+ "%#"PRIx32"\n", glink_vma);
+ return (GElf_Addr)glink_vma;
+ }
+
+ return 0;
+}
+
+int
+arch_elf_dynamic_tag(struct ltelf *lte, GElf_Dyn dyn)
+{
+ if (dyn.d_tag == DT_PPC_GOT) {
+ lte->arch.ppcgot = dyn.d_un.d_val;
+ debug(1, "ppcgot %#" PRIx64, lte->arch.ppcgot);
+ }
+ return 0;
+}
+
+int
+arch_elf_init(struct ltelf *lte)
+{
+ if (lte->ehdr.e_machine == EM_PPC) {
+ GElf_Addr glink_vma
+ = get_glink_vma(lte, lte->arch.ppcgot, lte->plt_data);
+
+ assert (lte->relplt_size % 12 == 0);
+ size_t count = lte->relplt_size / 12; // size of RELA entry
+ 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);
+ }
+
+ /* Override the value that we gleaned from flags on the .plt
+ * section. The PLT entries are in fact executable, they are
+ * just not in .plt. */
+ lte->lte_flags |= LTE_PLT_EXECUTABLE;
+ return 0;
+}