Blob Blame History Raw
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