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

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