diff -urp ltrace-0.5/elf.c ltrace-0.5-pm/elf.c
--- ltrace-0.5/elf.c 2010-05-17 14:49:20.004577987 -0400
+++ ltrace-0.5-pm/elf.c 2010-05-17 14:54:31.841118855 -0400
@@ -11,6 +11,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <assert.h>
#include "ltrace.h"
#include "elf.h"
@@ -29,6 +30,114 @@ static GElf_Addr opd2addr(struct ltelf *
extern char *PLTs_initialized_by_here;
#endif
+// xxx make it only appear on PPC
+#ifndef DT_PPC_GOT
+# define DT_PPC_GOT (DT_LOPROC + 0)
+#endif
+
+#define PPC_PLT_STUB_SIZE 16
+
+static Elf_Data *loaddata(Elf_Scn *scn, GElf_Shdr *shdr)
+{
+ Elf_Data *data = elf_getdata(scn, NULL);
+ if (data == NULL || elf_getdata(scn, data) != NULL
+ || data->d_off || data->d_size != shdr->sh_size)
+ return NULL;
+ return data;
+}
+
+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)
+{
+ if (inside (addr, in_shdr)) {
+ *tgt_sec = in_sec;
+ *tgt_shdr = *in_shdr;
+ return 1;
+ }
+ return 0;
+}
+
+static int 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) {
+ Elf_Scn *scn;
+ GElf_Shdr shdr;
+
+ scn = elf_getscn(lte->elf, i);
+ if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) {
+ debug(1, "Couldn't read section or header.");
+ return 0;
+ }
+
+ if (maybe_pick_section(addr, scn, &shdr, tgt_sec, tgt_shdr))
+ 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 %z.",
+ offset);
+ 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]);
+}
+
+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=%#llx, 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: %#llx",
+ glink_vma);
+ return glink_vma;
+ }
+ }
+ }
+
+ if (plt_data != NULL) {
+ GElf_Addr glink_vma = read32be(plt_data, 0);
+ debug(1, ".plt glink_vma address: %#llx", glink_vma);
+ return glink_vma;
+ }
+
+ return 0;
+}
+
static void do_init_elf(struct ltelf *lte, const char *filename)
{
int i;
@@ -74,6 +183,9 @@ static void do_init_elf(struct ltelf *lt
error(EXIT_FAILURE, 0,
"\"%s\" is ELF from incompatible architecture", filename);
+ Elf_Data *plt_data = NULL;
+ GElf_Addr ppcgot = 0;
+
for (i = 1; i < lte->ehdr.e_shnum; ++i) {
Elf_Scn *scn;
GElf_Shdr shdr;
@@ -164,6 +276,10 @@ static void do_init_elf(struct ltelf *lt
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 %#llx", ppcgot);
+ }
}
} else if (shdr.sh_type == SHT_HASH) {
Elf_Data *data;
@@ -226,9 +342,8 @@ static void do_init_elf(struct ltelf *lt
filename, shdr.sh_entsize);
}
- data = elf_getdata(scn, NULL);
- if (data == NULL || elf_getdata(scn, data) != NULL
- || data->d_off || data->d_size != shdr.sh_size)
+ data = loaddata(scn, &shdr);
+ if (data == NULL)
error(EXIT_FAILURE, 0,
"Couldn't get .gnu.hash data from \"%s\"",
filename);
@@ -243,6 +358,12 @@ static void do_init_elf(struct ltelf *lt
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");
+ }
} else if (strcmp(name, ".opd") == 0) {
lte->opd_addr = (GElf_Addr *) (long) shdr.sh_addr;
lte->opd_size = shdr.sh_size;
@@ -259,7 +380,22 @@ static void do_init_elf(struct ltelf *lt
debug(1, "%s has no PLT relocations", filename);
lte->relplt = NULL;
lte->relplt_count = 0;
+ } else if (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 %#llx", lte->plt_stub_vma);
+ }
+
for (i = 1; i < lte->ehdr.e_shnum; ++i) {
Elf_Scn *scn;
GElf_Shdr shdr;
@@ -482,6 +619,13 @@ struct library_symbol *read_elf(struct p
enum toplt pltt;
if (lte->ehdr.e_machine == EM_PPC) {
addr = sym.st_value;
+ /* If we have neither the symbol
+ * address, nor the PLT stub address,
+ * the tracing will probably fail. */
+ if (addr == 0 && lte->plt_stub_vma != 0) {
+ addr = lte->plt_stub_vma
+ + PPC_PLT_STUB_SIZE * i;
+ }
pltt = LS_TOPLT_EXEC;
}
else {
diff -urp ltrace-0.5/elf.h ltrace-0.5-pm/elf.h
--- ltrace-0.5/elf.h 2010-05-17 14:49:19.844578787 -0400
+++ ltrace-0.5-pm/elf.h 2010-05-17 14:00:52.844954178 -0400
@@ -26,6 +26,7 @@ struct ltelf {
Elf32_Word *hash;
int hash_type;
int lte_flags;
+ GElf_Addr plt_stub_vma;
};
#define LTE_HASH_MALLOCED 1