diff options
author | Petr Machata <pmachata@redhat.com> | 2013-10-15 10:46:28 +0200 |
---|---|---|
committer | Petr Machata <pmachata@redhat.com> | 2013-10-23 01:00:03 +0200 |
commit | b420a226cd2fc5d6028adcaf236c512a1f1fb437 (patch) | |
tree | 59421b8cf2d90e1220bf1cc6828d1697bf6a55d0 /sysdeps/linux-gnu/trace.c | |
parent | 7a29f9e7a2bd5849886519eb82e9c043d24c6a40 (diff) | |
download | ltrace-b420a226cd2fc5d6028adcaf236c512a1f1fb437.tar.gz |
Add support for tracing of IRELATIVE PLT entries
- Because the IRELATIVE entries have no associated symbol name, we
need to allow arch_elf_add_plt_entry to override the name. This is
done by that callback returning PLT_OK and returning the new symbol
via libsym-chain return argument. Filtering is postponed until we
have that symbol, and the filter is applied to the whole returned
chain.
- Add linux_elf_add_plt_entry_irelative to support proper naming of
IRELATIVE PLT entries. This needs to be called from arch backend,
as the numbers of IRELATIVE relocations differ per-architecture.
Diffstat (limited to 'sysdeps/linux-gnu/trace.c')
-rw-r--r-- | sysdeps/linux-gnu/trace.c | 67 |
1 files changed, 64 insertions, 3 deletions
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c index 677438e..5567eb6 100644 --- a/sysdeps/linux-gnu/trace.c +++ b/sysdeps/linux-gnu/trace.c @@ -24,25 +24,28 @@ #include "config.h" #include <asm/unistd.h> -#include <sys/types.h> -#include <sys/wait.h> #include <assert.h> #include <errno.h> +#include <gelf.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/types.h> +#include <sys/wait.h> #include <unistd.h> #ifdef HAVE_LIBSELINUX # include <selinux/selinux.h> #endif -#include "linux-gnu/trace.h" #include "linux-gnu/trace-defs.h" +#include "linux-gnu/trace.h" #include "backend.h" #include "breakpoint.h" #include "debug.h" #include "events.h" +#include "ltrace-elf.h" #include "options.h" #include "proc.h" #include "ptrace.h" @@ -1220,3 +1223,61 @@ umovebytes(struct process *proc, void *addr, void *laddr, size_t len) return bytes_read; } + +struct irelative_name_data_t { + GElf_Addr addr; + const char *found_name; +}; + +static enum callback_status +irelative_name_cb(GElf_Sym *symbol, const char *name, void *d) +{ + struct irelative_name_data_t *data = d; + + if (symbol->st_value == data->addr) { + bool is_ifunc = false; +#ifdef STT_GNU_IFUNC + is_ifunc = GELF_ST_TYPE(symbol->st_info) == STT_GNU_IFUNC; +#endif + data->found_name = name; + + /* Keep looking, unless we found the actual IFUNC + * symbol. What we matched may have been a symbol + * denoting the resolver function, which would have + * the same address. */ + return CBS_STOP_IF(is_ifunc); + } + + return CBS_CONT; +} + +enum plt_status +linux_elf_add_plt_entry_irelative(struct process *proc, struct ltelf *lte, + GElf_Rela *rela, size_t ndx, + struct library_symbol **ret) + +{ + struct irelative_name_data_t data = { rela->r_addend, NULL }; + if (rela->r_addend != 0 + && elf_each_symbol(lte, 0, + irelative_name_cb, &data).status < 0) + return -1; + + const char *name; + if (data.found_name != NULL) { + name = data.found_name; + } else { +#define NAME "IREL." + /* NAME\0 + 0x + digits. */ + char *tmp_name = alloca(sizeof NAME + 2 + 16); + sprintf(tmp_name, NAME "%#" PRIx64, + (uint64_t)rela->r_addend); + name = tmp_name; +#undef NAME + } + + if (default_elf_add_plt_entry(proc, lte, name, rela, ndx, ret) < 0) + return PLT_FAIL; + + return PLT_OK; +} |