Blame SOURCES/ltrace-0.7.91-ppc64-unprelink.patch

e7106e
From a0093ca43cf40d7e5f6cebeb64156062d2de46d9 Mon Sep 17 00:00:00 2001
e7106e
From: Petr Machata <pmachata@redhat.com>
e7106e
Date: Fri, 10 Jan 2014 20:06:51 +0100
e7106e
Subject: [PATCH 2/2] Don't crash untraced calls via PLT in prelinked PPC64
e7106e
 binaries
e7106e
e7106e
In prelinked binaries, ltrace has to unprelinks PLT slots in order to
e7106e
catch calls done through PLT.  This makes the calls done through these
e7106e
slots invalid, because the special first PLT slot is not initialized,
e7106e
and dynamic linker SIGSEGVs because of this.  Ltrace relies on
e7106e
arranging breakpoints such that the dynamic linker is not actually
e7106e
entered, and moves PC around itself to simulate the effects of a call
e7106e
through PLT.
e7106e
e7106e
Originally, arch_elf_add_plt_entry was called only for symbols that
e7106e
were actually traced.  Later this was changed and it's now called for
e7106e
all PLT entries, and the resulting candidate list is filtered
e7106e
afterwards.  This gives backends a chance to rename the symbol, as is
e7106e
useful with IRELATIVE PLT calls, where symbol name may not be
e7106e
available at all.  But the PPC backend was never updated to reflect
e7106e
this, and unresolved all symbols for which arch_elf_add_plt_entry was
e7106e
called, thus rendering _all_ PLT slots invalid, even those that
e7106e
weren't later procted by breakpoints.  Thus calls done through any
e7106e
untraced slots failed.
e7106e
e7106e
This patch fixes this problem by deferring the unprelinking of PLT
e7106e
slots into the on_install hook of breakpoints.
e7106e
---
e7106e
 sysdeps/linux-gnu/ppc/arch.h |   21 ++++++++-
e7106e
 sysdeps/linux-gnu/ppc/plt.c  |   94 +++++++++++++++++++++++++++++++++--------
e7106e
 2 files changed, 94 insertions(+), 21 deletions(-)
e7106e
e7106e
diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h
e7106e
index 2add3b8..bf9b5dc 100644
e7106e
--- a/sysdeps/linux-gnu/ppc/arch.h
e7106e
+++ b/sysdeps/linux-gnu/ppc/arch.h
e7106e
@@ -1,6 +1,6 @@
e7106e
 /*
e7106e
  * This file is part of ltrace.
e7106e
- * Copyright (C) 2012,2013 Petr Machata
e7106e
+ * Copyright (C) 2012,2013,2014 Petr Machata
e7106e
  * Copyright (C) 2006 Paul Gilliam
e7106e
  * Copyright (C) 2002,2004 Juan Cespedes
e7106e
  *
e7106e
@@ -87,12 +87,29 @@ enum ppc64_plt_type {
e7106e
 	/* Very similar to PPC_PLT_UNRESOLVED, but for JMP_IREL
e7106e
 	 * slots.  */
e7106e
 	PPC_PLT_IRELATIVE,
e7106e
+
e7106e
+	/* Transitional state before the breakpoint is enabled.  */
e7106e
+	PPC_PLT_NEED_UNRESOLVE,
e7106e
 };
e7106e
 
e7106e
 #define ARCH_HAVE_LIBRARY_SYMBOL_DATA
e7106e
+struct ppc_unresolve_data;
e7106e
 struct arch_library_symbol_data {
e7106e
 	enum ppc64_plt_type type;
e7106e
-	GElf_Addr resolved_value;
e7106e
+
e7106e
+	/* State		Contents
e7106e
+	 *
e7106e
+	 * PPC_DEFAULT		N/A
e7106e
+	 * PPC64_PLT_STUB	N/A
e7106e
+	 * PPC_PLT_UNRESOLVED	PLT entry address.
e7106e
+	 * PPC_PLT_IRELATIVE	Likewise.
e7106e
+	 * PPC_PLT_RESOLVED	The original value the slot was resolved to.
e7106e
+	 * PPC_PLT_NEED_UNRESOLVE	DATA.
e7106e
+	 */
e7106e
+	union {
e7106e
+		GElf_Addr resolved_value;
e7106e
+		struct ppc_unresolve_data *data;
e7106e
+	};
e7106e
 
e7106e
 	/* Address of corresponding slot in .plt.  */
e7106e
 	GElf_Addr plt_slot_addr;
e7106e
diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c
e7106e
index 8715da6..332daa8 100644
e7106e
--- a/sysdeps/linux-gnu/ppc/plt.c
e7106e
+++ b/sysdeps/linux-gnu/ppc/plt.c
e7106e
@@ -679,6 +679,14 @@ arch_elf_add_func_entry(struct process *proc, struct ltelf *lte,
e7106e
 	return PLT_OK;
e7106e
 }
e7106e
 
e7106e
+struct ppc_unresolve_data {
e7106e
+	struct ppc_unresolve_data *self; /* A canary.  */
e7106e
+	GElf_Addr plt_entry_addr;
e7106e
+	GElf_Addr plt_slot_addr;
e7106e
+	GElf_Addr plt_slot_value;
e7106e
+	bool is_irelative;
e7106e
+};
e7106e
+
e7106e
 enum plt_status
e7106e
 arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
e7106e
 		       const char *a_name, GElf_Rela *rela, size_t ndx,
e7106e
@@ -778,28 +786,23 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
e7106e
 	    && (plt_slot_value == plt_entry_addr || plt_slot_value == 0)) {
e7106e
 		libsym->arch.type = PPC_PLT_UNRESOLVED;
e7106e
 		libsym->arch.resolved_value = plt_entry_addr;
e7106e
-
e7106e
 	} else {
e7106e
-		/* Unresolve the .plt slot.  If the binary was
e7106e
-		 * prelinked, this makes the code invalid, because in
e7106e
-		 * case of prelinked binary, the dynamic linker
e7106e
-		 * doesn't update .plt[0] and .plt[1] with addresses
e7106e
-		 * of the resover.  But we don't care, we will never
e7106e
-		 * need to enter the resolver.  That just means that
e7106e
-		 * we have to un-un-resolve this back before we
e7106e
-		 * detach.  */
e7106e
-
e7106e
-		if (unresolve_plt_slot(proc, plt_slot_addr, plt_entry_addr) < 0) {
e7106e
-			library_symbol_destroy(libsym);
e7106e
+		/* Mark the symbol for later unresolving.  We may not
e7106e
+		 * do this right away, as this is called by ltrace
e7106e
+		 * core for all symbols, and only later filtered.  We
e7106e
+		 * only unresolve the symbol before the breakpoint is
e7106e
+		 * enabled.  */
e7106e
+
e7106e
+		libsym->arch.type = PPC_PLT_NEED_UNRESOLVE;
e7106e
+		libsym->arch.data = malloc(sizeof *libsym->arch.data);
e7106e
+		if (libsym->arch.data == NULL)
e7106e
 			goto fail2;
e7106e
-		}
e7106e
 
e7106e
-		if (! is_irelative) {
e7106e
-			mark_as_resolved(libsym, plt_slot_value);
e7106e
-		} else {
e7106e
-			libsym->arch.type = PPC_PLT_IRELATIVE;
e7106e
-			libsym->arch.resolved_value = plt_entry_addr;
e7106e
-		}
e7106e
+		libsym->arch.data->self = libsym->arch.data;
e7106e
+		libsym->arch.data->plt_entry_addr = plt_entry_addr;
e7106e
+		libsym->arch.data->plt_slot_addr = plt_slot_addr;
e7106e
+		libsym->arch.data->plt_slot_value = plt_slot_value;
e7106e
+		libsym->arch.data->is_irelative = is_irelative;
e7106e
 	}
e7106e
 
e7106e
 	*ret = libsym;
e7106e
@@ -999,6 +1002,7 @@ ppc_plt_bp_continue(struct breakpoint *bp, struct process *proc)
e7106e
 		return;
e7106e
 
e7106e
 	case PPC64_PLT_STUB:
e7106e
+	case PPC_PLT_NEED_UNRESOLVE:
e7106e
 		/* These should never hit here.  */
e7106e
 		break;
e7106e
 	}
e7106e
@@ -1050,6 +1054,52 @@ ppc_plt_bp_retract(struct breakpoint *bp, struct process *proc)
e7106e
 	}
e7106e
 }
e7106e
 
e7106e
+static void
e7106e
+ppc_plt_bp_install(struct breakpoint *bp, struct process *proc)
e7106e
+{
e7106e
+	/* This should not be an artificial breakpoint.  */
e7106e
+	struct library_symbol *libsym = bp->libsym;
e7106e
+	if (libsym == NULL)
e7106e
+		libsym = bp->arch.irel_libsym;
e7106e
+	assert(libsym != NULL);
e7106e
+
e7106e
+	if (libsym->arch.type == PPC_PLT_NEED_UNRESOLVE) {
e7106e
+		/* Unresolve the .plt slot.  If the binary was
e7106e
+		 * prelinked, this makes the code invalid, because in
e7106e
+		 * case of prelinked binary, the dynamic linker
e7106e
+		 * doesn't update .plt[0] and .plt[1] with addresses
e7106e
+		 * of the resover.  But we don't care, we will never
e7106e
+		 * need to enter the resolver.  That just means that
e7106e
+		 * we have to un-un-resolve this back before we
e7106e
+		 * detach.  */
e7106e
+
e7106e
+		struct ppc_unresolve_data *data = libsym->arch.data;
e7106e
+		libsym->arch.data = NULL;
e7106e
+		assert(data->self == data);
e7106e
+
e7106e
+		GElf_Addr plt_slot_addr = data->plt_slot_addr;
e7106e
+		GElf_Addr plt_slot_value = data->plt_slot_value;
e7106e
+		GElf_Addr plt_entry_addr = data->plt_entry_addr;
e7106e
+
e7106e
+		if (unresolve_plt_slot(proc, plt_slot_addr,
e7106e
+				       plt_entry_addr) == 0) {
e7106e
+			if (! data->is_irelative) {
e7106e
+				mark_as_resolved(libsym, plt_slot_value);
e7106e
+			} else {
e7106e
+				libsym->arch.type = PPC_PLT_IRELATIVE;
e7106e
+				libsym->arch.resolved_value = plt_entry_addr;
e7106e
+			}
e7106e
+		} else {
e7106e
+			fprintf(stderr, "Couldn't unresolve %s@%p.  Not tracing"
e7106e
+				" this symbol.\n",
e7106e
+				breakpoint_name(bp), bp->addr);
e7106e
+			proc_remove_breakpoint(proc, bp);
e7106e
+		}
e7106e
+
e7106e
+		free(data);
e7106e
+	}
e7106e
+}
e7106e
+
e7106e
 int
e7106e
 arch_library_init(struct library *lib)
e7106e
 {
e7106e
@@ -1080,6 +1130,11 @@ arch_library_symbol_init(struct library_symbol *libsym)
e7106e
 void
e7106e
 arch_library_symbol_destroy(struct library_symbol *libsym)
e7106e
 {
e7106e
+	if (libsym->arch.type == PPC_PLT_NEED_UNRESOLVE) {
e7106e
+		assert(libsym->arch.data->self == libsym->arch.data);
e7106e
+		free(libsym->arch.data);
e7106e
+		libsym->arch.data = NULL;
e7106e
+	}
e7106e
 }
e7106e
 
e7106e
 int
e7106e
@@ -1115,6 +1170,7 @@ arch_breakpoint_init(struct process *proc, struct breakpoint *bp)
e7106e
 	static struct bp_callbacks cbs = {
e7106e
 		.on_continue = ppc_plt_bp_continue,
e7106e
 		.on_retract = ppc_plt_bp_retract,
e7106e
+		.on_install = ppc_plt_bp_install,
e7106e
 	};
e7106e
 	breakpoint_set_callbacks(bp, &cbs);
e7106e
 
e7106e
-- 
e7106e
1.7.6.5
e7106e