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