diff options
author | Petr Machata <pmachata@redhat.com> | 2013-11-05 01:46:30 +0100 |
---|---|---|
committer | Petr Machata <pmachata@redhat.com> | 2013-11-05 01:58:08 +0100 |
commit | 97d13666cd84589135ba593fa43a800d098026d0 (patch) | |
tree | a4ec260f7018f39317526098183468b8263a589c | |
parent | fec0b3b1b8323220255f9ebdcac7db2b689783ef (diff) | |
download | ltrace-97d13666cd84589135ba593fa43a800d098026d0.tar.gz |
Add elf_add_plt_entry as a separate entry point
- That calls into OS and ARCH callbacks as required and has the same
interface. This also adds os_elf_add_plt_entry.
- arch_elf_add_func_entry was added as well for symmetry.
-rw-r--r-- | backend.h | 21 | ||||
-rw-r--r-- | ltrace-elf.c | 199 | ||||
-rw-r--r-- | ltrace-elf.h | 16 |
3 files changed, 154 insertions, 82 deletions
@@ -319,8 +319,8 @@ enum plt_status { PLT_DEFAULT, }; -/* The following callback has to be implemented in backend if arch.h - * defines ARCH_HAVE_ADD_PLT_ENTRY. +/* The following callback has to be implemented in OS backend if os.h + * defines OS_HAVE_ADD_PLT_ENTRY. * * This is called for every PLT relocation R in ELF file LTE, that * ltrace is about to add to a library constructed in process PROC. @@ -331,11 +331,18 @@ enum plt_status { * calling arch_plt_sym_val, and symbol is allocated. If PLT_OK or * PLT_DEFAULT are returned, the chain of symbols passed back in RET * is added to library under construction. */ +enum plt_status os_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *name, GElf_Rela *rela, + size_t i, struct library_symbol **ret); + +/* Like os_elf_add_plt_entry, but tied to ARCH_HAVE_ADD_PLT_ENTRY in + * arch.h. The arch callback is called first. If it returns + * PLT_DEFAULT, the os callback is called next. */ enum plt_status arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, const char *name, GElf_Rela *rela, size_t i, struct library_symbol **ret); -/* The following callback has to be implemented in backend if arch.h +/* The following callback has to be implemented in OS backend if os.h * defines OS_HAVE_ADD_FUNC_ENTRY. * * This is called for every symbol in ltrace is about to add to the @@ -352,6 +359,14 @@ enum plt_status os_elf_add_func_entry(struct process *proc, struct ltelf *lte, arch_addr_t addr, const char *name, struct library_symbol **ret); +/* Like os_elf_add_func_entry, but tied to ARCH_HAVE_ADD_FUNC_ENTRY in + * arch.h. The arch callback is called first. If it returns + * PLT_DEFAULT, the os callback is called next. */ +enum plt_status arch_elf_add_func_entry(struct process *proc, struct ltelf *lte, + const GElf_Sym *sym, + arch_addr_t addr, const char *name, + struct library_symbol **ret); + /* This callback needs to be implemented if arch.h defines * ARCH_HAVE_DYNLINK_DONE. It is called after the dynamic linker is * done with the process start-up. */ diff --git a/ltrace-elf.c b/ltrace-elf.c index 9100c46..92b642b 100644 --- a/ltrace-elf.c +++ b/ltrace-elf.c @@ -36,6 +36,7 @@ #include <gelf.h> #include <inttypes.h> #include <search.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -64,41 +65,15 @@ arch_elf_destroy(struct ltelf *lte) } #endif -int -default_elf_add_plt_entry(struct process *proc, struct ltelf *lte, - const char *a_name, GElf_Rela *rela, size_t ndx, - struct library_symbol **ret) +#ifndef OS_HAVE_ADD_PLT_ENTRY +enum plt_status +os_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *a_name, GElf_Rela *rela, size_t ndx, + struct library_symbol **ret) { - char *name = strdup(a_name); - if (name == NULL) { - fail_message: - fprintf(stderr, "Couldn't create symbol for PLT entry: %s\n", - strerror(errno)); - fail: - free(name); - return -1; - } - - GElf_Addr addr = arch_plt_sym_val(lte, ndx, rela); - - struct library_symbol *libsym = malloc(sizeof(*libsym)); - if (libsym == NULL) - goto fail_message; - - /* XXX The double cast should be removed when - * arch_addr_t becomes integral type. */ - arch_addr_t taddr = (arch_addr_t) - (uintptr_t)(addr + lte->bias); - - if (library_symbol_init(libsym, taddr, name, 1, LS_TOPLT_EXEC) < 0) { - free(libsym); - goto fail; - } - - libsym->next = *ret; - *ret = libsym; - return 0; + return PLT_DEFAULT; } +#endif #ifndef ARCH_HAVE_ADD_PLT_ENTRY enum plt_status @@ -126,6 +101,17 @@ os_elf_add_func_entry(struct process *proc, struct ltelf *lte, } #endif +#ifndef ARCH_HAVE_ADD_FUNC_ENTRY +enum plt_status +arch_elf_add_func_entry(struct process *proc, struct ltelf *lte, + const GElf_Sym *sym, + arch_addr_t addr, const char *name, + struct library_symbol **ret) +{ + return PLT_DEFAULT; +} +#endif + Elf_Data * elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr) { @@ -698,6 +684,67 @@ arch_get_sym_info(struct ltelf *lte, const char *filename, } #endif +int +default_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *a_name, GElf_Rela *rela, size_t ndx, + struct library_symbol **ret) +{ + char *name = strdup(a_name); + if (name == NULL) { + fail_message: + fprintf(stderr, "Couldn't create symbol for PLT entry: %s\n", + strerror(errno)); + fail: + free(name); + return -1; + } + + GElf_Addr addr = arch_plt_sym_val(lte, ndx, rela); + + struct library_symbol *libsym = malloc(sizeof(*libsym)); + if (libsym == NULL) + goto fail_message; + + /* XXX The double cast should be removed when + * arch_addr_t becomes integral type. */ + arch_addr_t taddr = (arch_addr_t) + (uintptr_t)(addr + lte->bias); + + if (library_symbol_init(libsym, taddr, name, 1, LS_TOPLT_EXEC) < 0) { + free(libsym); + goto fail; + } + + libsym->next = *ret; + *ret = libsym; + return 0; +} + +int +elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *name, GElf_Rela *rela, size_t idx, + struct library_symbol **ret) +{ + enum plt_status plts + = arch_elf_add_plt_entry(proc, lte, name, rela, idx, ret); + + if (plts == PLT_DEFAULT) + plts = os_elf_add_plt_entry(proc, lte, name, rela, idx, ret); + + switch (plts) { + case PLT_DEFAULT: + return default_elf_add_plt_entry(proc, lte, name, + rela, idx, ret); + case PLT_FAIL: + return -1; + case PLT_OK: + return 0; + } + + assert(! "Invalid return from X_elf_add_plt_entry!"); + abort(); +} + static void mark_chain_latent(struct library_symbol *libsym) { @@ -725,23 +772,13 @@ filter_symbol_chain(struct filter *filter, } } -static void -delete_symbol_chain(struct library_symbol *libsym) -{ - while (libsym != NULL) { - struct library_symbol *tmp = libsym->next; - library_symbol_destroy(libsym); - free(libsym); - libsym = tmp; - } -} - static int populate_plt(struct process *proc, const char *filename, - struct ltelf *lte, struct library *lib, - int latent_plts) + struct ltelf *lte, struct library *lib) { - size_t count = vect_size(<e->plt_relocs); + const bool latent_plts = options.export_filter != NULL; + const size_t count = vect_size(<e->plt_relocs); + size_t i; for (i = 0; i < count; ++i) { GElf_Rela *rela = VECT_ELEMENT(<e->plt_relocs, GElf_Rela, i); @@ -761,45 +798,43 @@ populate_plt(struct process *proc, const char *filename, } char const *name = lte->dynstr + sym.st_name; - int matched = filter_matches_symbol(options.plt_filter, name, lib); struct library_symbol *libsym = NULL; - switch (arch_elf_add_plt_entry(proc, lte, name, - rela, i, &libsym)) { - case PLT_FAIL: + if (elf_add_plt_entry(proc, lte, name, rela, i, &libsym) < 0) return -1; - case PLT_DEFAULT: - /* Add default entry to the beginning of LIBSYM. */ - if (default_elf_add_plt_entry(proc, lte, name, - rela, i, &libsym) < 0) - return -1; - /* Fall through. */ - case PLT_OK: - /* If we didn't match the PLT entry up there, - * filter the chain to only include the - * matching symbols (but include all if we are - * adding latent symbols). This is to allow - * arch_elf_add_plt_entry to override the PLT - * symbol's name. */ - if (!matched && !latent_plts) - filter_symbol_chain(options.plt_filter, - &libsym, lib); - if (libsym != NULL) { - /* If we are adding those symbols just - * for tracing exports, mark them all - * latent. */ - if (!matched && latent_plts) - mark_chain_latent(libsym); - library_add_symbol(lib, libsym); - } + /* If we didn't match the PLT entry, filter the chain + * to only include the matching symbols (but include + * all if we are adding latent symbols) to allow + * backends to override the PLT symbol's name. */ + + if (! matched && ! latent_plts) + filter_symbol_chain(options.plt_filter, &libsym, lib); + + if (libsym != NULL) { + /* If we are adding those symbols just for + * tracing exports, mark them all latent. */ + if (! matched && latent_plts) + mark_chain_latent(libsym); + library_add_symbol(lib, libsym); } } return 0; } +static void +delete_symbol_chain(struct library_symbol *libsym) +{ + while (libsym != NULL) { + struct library_symbol *tmp = libsym->next; + library_symbol_destroy(libsym); + free(libsym); + libsym = tmp; + } +} + /* When -x rules result in request to trace several aliases, we only * want to add such symbol once. The only way that those symbols * differ in is their name, e.g. in glibc you have __GI___libc_free, @@ -934,8 +969,14 @@ populate_this_symtab(struct process *proc, const char *filename, } struct library_symbol *libsym = NULL; - switch (os_elf_add_func_entry(proc, lte, &sym, - naddr, full_name, &libsym)) { + enum plt_status plts + = arch_elf_add_func_entry(proc, lte, &sym, + naddr, full_name, &libsym); + if (plts == PLT_DEFAULT) + plts = os_elf_add_func_entry(proc, lte, &sym, + naddr, full_name, &libsym); + + switch (plts) { case PLT_DEFAULT:; /* Put the default symbol to the chain. */ struct library_symbol *tmp = malloc(sizeof *tmp); @@ -986,6 +1027,7 @@ populate_this_symtab(struct process *proc, const char *filename, unique->libsym = tmp; unique->addr = tmp->enter_addr; tmp = tmp->next; + unique->libsym->next = NULL; } else { if (strlen(tmp->name) < strlen(unique->libsym->name)) { @@ -1172,8 +1214,7 @@ read_module(struct library *lib, struct process *proc, int plts = filter_matches_library(options.plt_filter, lib); if ((plts || options.export_filter != NULL) - && populate_plt(proc, filename, <e, lib, - options.export_filter != NULL) < 0) + && populate_plt(proc, filename, <e, lib) < 0) goto fail; int exports = filter_matches_library(options.export_filter, lib); diff --git a/ltrace-elf.h b/ltrace-elf.h index 57ed87d..ea14512 100644 --- a/ltrace-elf.h +++ b/ltrace-elf.h @@ -81,6 +81,22 @@ int ltelf_read_library(struct library *lib, struct process *proc, * point address is stored to *ENTRYP. */ struct library *ltelf_read_main_binary(struct process *proc, const char *path); +/* This is called for every PLT relocation R in ELF file LTE, that + * ltrace is about to add to a library constructed in process PROC. + * The corresponding PLT entry is for symbol called NAME, and it's + * I-th relocation in the file. *RET shall be initialized and + * symbol(s) corresponding to the given PLT entry will be added to the + * front. Returns 0 for success, or a negative value for failures. + * + * This calls os_elf_add_plt_entry and arch_elf_add_plt_entry in the + * background (see backend.h for documentation). The arch callback is + * called first. If it returns PLT_DEFAULT, the os callback is called + * next. If that returns PLT_DEFAULT, default_elf_add_plt_entry is + * called. */ +int elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *name, GElf_Rela *rela, size_t idx, + struct library_symbol **ret); + /* Create a default PLT entry. This can be used instead (or in * addition to) returning PLT_DEFAULT from arch_elf_add_plt_entry. * RET shall be initialized, the created symbol will be added to the |