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

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