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

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