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(&lte, 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(&lte)
-				?  LS_TOPLT_EXEC : LS_TOPLT_POINT;
-			addr = arch_plt_sym_val(&lte, i, &rela);
-		}
+		enum toplt pltt = PLTS_ARE_EXECUTABLE(&lte)
+			?  LS_TOPLT_EXEC : LS_TOPLT_POINT;
+		GElf_Addr addr = arch_plt_sym_val(&lte, 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;
+}