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