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