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

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