[Ltrace-devel] [PATCH v3 6/8] mipsel: Add mips specific symbol info loading

edgar.iglesias at gmail.com edgar.iglesias at gmail.com
Thu Sep 27 15:02:38 UTC 2012


From: "Edgar E. Iglesias" <edgar at axis.com>

MIPS needs a backend specific way to load symbol info.
We add fields to the symbol representation to keep track
of the state of the dynamic symbol.

At arch_dynlink_done we go through the symbols that are
connected to a GOT entry but that where not resolved at
startup time (e.g function pointers to external syms).

Signed-off-by: Edgar E. Iglesias <edgar at axis.com>
---
 proc.c                          |   13 +++
 sysdeps/linux-gnu/mipsel/arch.h |   18 ++++
 sysdeps/linux-gnu/mipsel/plt.c  |  168 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 199 insertions(+), 0 deletions(-)

diff --git a/proc.c b/proc.c
index 319ef31..99bf31b 100644
--- a/proc.c
+++ b/proc.c
@@ -637,6 +637,19 @@ breakpoint_for_symbol(struct library_symbol *libsym, void *data)
 	assert(proc->leader == proc);
 
 	bp_addr = sym2addr(proc, libsym);
+
+	/* For external function pointers, MIPS brings in stub-less funcs
+	 * that point to zero at startup. These symbols get resolved by
+	 * the dynamic linker and are ready to use at arch_dynlink_done().
+	 *
+	 * Allow the backend to add these into the process representation
+	 * but don't put breakpoints at this point. Let the backend fix that
+	 * up later.  */
+	if (bp_addr == 0 && libsym->plt_type == LS_TOPLT_GOTONLY) {
+		/* Don't add breakpoints yet.  */
+		return CBS_CONT;
+	}
+
 	/* If there is an artificial breakpoint on the same address,
 	 * its libsym will be NULL, and we can smuggle our libsym
 	 * there.  That artificial breakpoint is there presumably for
diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h
index 1209423..ffb9b34 100644
--- a/sysdeps/linux-gnu/mipsel/arch.h
+++ b/sysdeps/linux-gnu/mipsel/arch.h
@@ -22,6 +22,7 @@
 #define LTRACE_MIPS_ARCH_H
 
 #include <stddef.h>
+#include <gelf.h>
 
 #define BREAKPOINT_VALUE { 0x0d, 0x00, 0x00, 0x00 }
 #define BREAKPOINT_LENGTH 4
@@ -38,4 +39,21 @@ struct arch_ltelf_data {
 	size_t mips_gotsym;
 };
 
+#define ARCH_HAVE_GET_SYMINFO
+#define ARCH_HAVE_DYNLINK_DONE
+#define ARCH_HAVE_ADD_PLT_ENTRY
+
+#define ARCH_HAVE_LIBRARY_SYMBOL_DATA
+enum mips_plt_type
+{
+	MIPS_PLT_UNRESOLVED,
+	MIPS_PLT_RESOLVED,
+};
+
+struct arch_library_symbol_data {
+	enum mips_plt_type type;
+	GElf_Addr resolved_addr;
+	GElf_Addr stub_addr;
+};
+
 #endif /* LTRACE_MIPS_ARCH_H */
diff --git a/sysdeps/linux-gnu/mipsel/plt.c b/sysdeps/linux-gnu/mipsel/plt.c
index 6ef67b2..8778781 100644
--- a/sysdeps/linux-gnu/mipsel/plt.c
+++ b/sysdeps/linux-gnu/mipsel/plt.c
@@ -1,4 +1,6 @@
+#include <string.h>
 #include <error.h>
+#include <errno.h>
 #include <gelf.h>
 #include <sys/ptrace.h>
 
@@ -6,6 +8,7 @@
 #include "debug.h"
 #include "proc.h"
 #include "library.h"
+#include "breakpoint.h"
 #include "backend.h"
 
 /**
@@ -81,6 +84,36 @@ sym2addr(Process *proc, struct library_symbol *sym) {
     return (void *)ret;;
 }
 
+/*
+ * MIPS doesn't have traditional got.plt entries with corresponding
+ * relocations.
+ *
+ * sym_index is an offset into the external GOT entries. Filter out
+ * stuff that are not functions.
+ */
+int
+arch_get_sym_info(struct ltelf *lte, const char *filename,
+		  size_t sym_index, GElf_Rela *rela, GElf_Sym *sym)
+{
+	const char *name;
+
+	/* Fixup the offset.  */
+	sym_index += lte->arch.mips_gotsym;
+
+	if (gelf_getsym(lte->dynsym, sym_index, sym) == NULL){
+		error(EXIT_FAILURE, 0,
+			"Couldn't get relocation from \"%s\"", filename);
+	}
+
+	name = lte->dynstr + sym->st_name;
+	if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) {
+		debug(2, "sym %s not a function", name);
+		return -1;
+	}
+
+	return 0;
+}
+
 /**
   MIPS ABI Supplement:
 
@@ -131,6 +164,9 @@ arch_elf_init(struct ltelf *lte, struct library *lib)
 		}
 	}
 
+	/* Tell the generic code how many dynamic trace:able symbols
+	 * we've got.  */
+	lte->relplt_count = lte->dynsym_count - lte->arch.mips_gotsym;
 	return 0;
 }
 
@@ -139,4 +175,136 @@ arch_elf_destroy(struct ltelf *lte)
 {
 }
 
+static enum callback_status
+cb_enable_breakpoint_sym(struct library_symbol *libsym, void *data)
+{
+	struct Process *proc = data;
+	struct breakpoint *bp;
+	arch_addr_t bp_addr;
+
+	if (libsym->plt_type != LS_TOPLT_GOTONLY)
+		return CBS_CONT;
+
+	/* Update state.  */
+	bp_addr = sym2addr(proc, libsym);
+	/* XXX The cast to uintptr_t should be removed when
+	 * arch_addr_t becomes integral type.  keywords: double cast.  */
+	libsym->arch.resolved_addr = (uintptr_t) bp_addr;
+
+	if (libsym->arch.resolved_addr == 0)
+		/* FIXME: What does this mean?  */
+		return CBS_CONT;
+
+	libsym->arch.type = MIPS_PLT_RESOLVED;
+
+	/* Add breakpoint.  */
+	bp = malloc(sizeof *bp);
+	if (bp == NULL
+	    || breakpoint_init(bp, proc, bp_addr, libsym) < 0) {
+		goto fail;
+	}
+
+	if (proc_add_breakpoint(proc, bp) < 0) {
+		breakpoint_destroy(bp);
+		goto fail;
+	}
+
+	if (breakpoint_turn_on(bp, proc) < 0) {
+		proc_remove_breakpoint(proc, bp);
+		breakpoint_destroy(bp);
+		goto fail;
+	}
+
+	return CBS_CONT;
+fail:
+	free(bp);
+	fprintf(stderr, "Failed to add breakpoint for %s\n", libsym->name);
+	return CBS_CONT;
+}
+
+static enum callback_status
+cb_enable_breakpoint_lib(struct Process *proc, struct library *lib, void *data)
+{
+	library_each_symbol(lib, NULL, cb_enable_breakpoint_sym, proc);
+	return CBS_CONT;
+}
+
+void arch_dynlink_done(struct Process *proc)
+{
+	proc_each_library(proc, NULL, cb_enable_breakpoint_lib, NULL);
+}
+
+enum plt_status
+arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte,
+                       const char *a_name, GElf_Rela *rela, size_t ndx,
+                       struct library_symbol **ret)
+{
+	char *name = NULL;
+	int sym_index = ndx + lte->arch.mips_gotsym;
+
+	struct library_symbol *libsym = malloc(sizeof(*libsym));
+	if (libsym == NULL)
+		return plt_fail;
+
+	GElf_Addr addr = arch_plt_sym_val(lte, sym_index, 0);
+
+	name = strdup(a_name);
+	if (name == NULL) {
+		fprintf(stderr, "%s: failed %s(%#llx): %s\n", __func__,
+			name, addr, strerror(errno));
+		goto fail;
+	}
+
+	/* XXX The double cast should be removed when
+	 * arch_addr_t becomes integral type.  */
+	if (library_symbol_init(libsym,
+				(arch_addr_t) (uintptr_t) addr,
+				name, 1, LS_TOPLT_EXEC) < 0) {
+		fprintf(stderr, "%s: failed %s : %llx\n", __func__, name, addr);
+		goto fail;
+	}
+
+	arch_addr_t bp_addr = sym2addr(proc, libsym);
+	/* XXX This cast should be removed when
+	 * arch_addr_t becomes integral type.  keywords: double cast. */
+	libsym->arch.stub_addr = (uintptr_t) bp_addr;
+
+	if (bp_addr == 0) {
+		/* Function pointers without PLT entries.  */
+		libsym->plt_type = LS_TOPLT_GOTONLY;
+		libsym->arch.type = MIPS_PLT_UNRESOLVED;
+	}
+
+	*ret = libsym;
+	return plt_ok;
+
+fail:
+	free(name);
+	free(libsym);
+	return plt_fail;
+}
+
+int
+arch_library_symbol_init(struct library_symbol *libsym)
+{
+	libsym->arch.type = MIPS_PLT_UNRESOLVED;
+	if (libsym->plt_type == LS_TOPLT_NONE) {
+		libsym->arch.type = MIPS_PLT_RESOLVED;
+	}
+	return 0;
+}
+
+void
+arch_library_symbol_destroy(struct library_symbol *libsym)
+{
+}
+
+int
+arch_library_symbol_clone(struct library_symbol *retp,
+                          struct library_symbol *libsym)
+{
+	retp->arch = libsym->arch;
+	return 0;
+}
+
 /**@}*/
-- 
1.7.8.6




More information about the Ltrace-devel mailing list