diff options
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/linux-gnu/hooks.c | 224 | ||||
-rw-r--r-- | sysdeps/linux-gnu/trace.c | 220 |
2 files changed, 220 insertions, 224 deletions
diff --git a/sysdeps/linux-gnu/hooks.c b/sysdeps/linux-gnu/hooks.c index 10f1a11..1e375fb 100644 --- a/sysdeps/linux-gnu/hooks.c +++ b/sysdeps/linux-gnu/hooks.c @@ -23,22 +23,15 @@ #include <alloca.h> #include <errno.h> #include <pwd.h> -#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "backend.h" -#include "breakpoint.h" #include "dict.h" -#include "fetch.h" -#include "library.h" #include "options.h" -#include "prototype.h" #include "sysdep.h" -#include "type.h" -#include "value.h" #include "vect.h" static char * @@ -217,220 +210,3 @@ os_get_ltrace_conf_filenames(struct vect *retp) return 0; } - -static struct prototype * -void_prototype(void) -{ - static struct prototype ret; - if (ret.return_info == NULL) { - prototype_init(&ret); - ret.return_info = type_get_voidptr(); - ret.own_return_info = 0; - } - return &ret; -} - -int -os_library_symbol_init(struct library_symbol *libsym) -{ - libsym->os = (struct os_library_symbol_data){}; - return 0; -} - -void -os_library_symbol_destroy(struct library_symbol *libsym) -{ -} - -int -os_library_symbol_clone(struct library_symbol *retp, - struct library_symbol *libsym) -{ - retp->os = libsym->os; - return 0; -} - -enum plt_status -os_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) -{ - if (GELF_ST_TYPE(sym->st_info) == STT_FUNC) - return PLT_DEFAULT; - - bool ifunc = false; -#ifdef STT_GNU_IFUNC - ifunc = GELF_ST_TYPE(sym->st_info) == STT_GNU_IFUNC; -#endif - - if (ifunc) { -#define S ".IFUNC" - char *tmp_name = malloc(strlen(name) + sizeof S); - struct library_symbol *tmp = malloc(sizeof *tmp); - if (tmp_name == NULL || tmp == NULL) { - fail: - free(tmp_name); - free(tmp); - return PLT_FAIL; - } - sprintf(tmp_name, "%s%s", name, S); -#undef S - - if (library_symbol_init(tmp, addr, tmp_name, 1, - LS_TOPLT_NONE) < 0) - goto fail; - tmp->proto = void_prototype(); - tmp->os.is_ifunc = 1; - - *ret = tmp; - return PLT_OK; - } - - *ret = NULL; - return PLT_OK; -} - -static enum callback_status -libsym_at_address(struct library_symbol *libsym, void *addrp) -{ - arch_addr_t addr = *(arch_addr_t *)addrp; - return CBS_STOP_IF(addr == libsym->enter_addr); -} - -static void -ifunc_ret_hit(struct breakpoint *bp, struct process *proc) -{ - struct fetch_context *fetch = fetch_arg_init(LT_TOF_FUNCTION, proc, - type_get_voidptr()); - if (fetch == NULL) - return; - - struct breakpoint *nbp = NULL; - int own_libsym = 0; - - struct value value; - value_init(&value, proc, NULL, type_get_voidptr(), 0); - size_t sz = value_size(&value, NULL); - union { - uint64_t u64; - uint32_t u32; - arch_addr_t a; - } u; - - if (fetch_retval(fetch, LT_TOF_FUNCTIONR, proc, - value.type, &value) < 0 - || sz > 8 /* Captures failure as well. */ - || value_extract_buf(&value, (void *) &u, NULL) < 0) { - fail: - fprintf(stderr, - "Couldn't trace the function " - "indicated by IFUNC resolver.\n"); - goto done; - } - - assert(sz == 4 || sz == 8); - /* XXX double casts below: */ - if (sz == 4) - u.a = (arch_addr_t)(uintptr_t)u.u32; - else - u.a = (arch_addr_t)(uintptr_t)u.u64; - if (arch_translate_address_dyn(proc, u.a, &u.a) < 0) { - fprintf(stderr, "Couldn't OPD-translate the address returned" - " by the IFUNC resolver.\n"); - goto done; - } - - assert(bp->os.ret_libsym != NULL); - - struct library *lib = bp->os.ret_libsym->lib; - assert(lib != NULL); - - /* Look if we already have a symbol with this address. - * Otherwise create a new one. */ - struct library_symbol *libsym - = library_each_symbol(lib, NULL, libsym_at_address, &u.a); - if (libsym == NULL) { - libsym = malloc(sizeof *libsym); - char *name = strdup(bp->os.ret_libsym->name); - - if (libsym == NULL - || name == NULL - || library_symbol_init(libsym, u.a, name, 1, - LS_TOPLT_NONE) < 0) { - free(libsym); - free(name); - goto fail; - } - - /* Snip the .IFUNC token. */ - *strrchr(name, '.') = 0; - - own_libsym = 1; - library_add_symbol(lib, libsym); - } - - nbp = malloc(sizeof *bp); - if (nbp == NULL || breakpoint_init(nbp, proc, u.a, libsym) < 0) - goto fail; - - /* If there already is a breakpoint at that address, that is - * suspicious, but whatever. */ - struct breakpoint *pre_bp = insert_breakpoint(proc, nbp); - if (pre_bp == NULL) - goto fail; - if (pre_bp == nbp) { - /* PROC took our breakpoint, so these resources are - * not ours anymore. */ - nbp = NULL; - own_libsym = 0; - } - -done: - free(nbp); - if (own_libsym) { - library_symbol_destroy(libsym); - free(libsym); - } - fetch_arg_done(fetch); -} - -static int -create_ifunc_ret_bp(struct breakpoint **ret, - struct breakpoint *bp, struct process *proc) -{ - *ret = create_default_return_bp(proc); - if (*ret == NULL) - return -1; - static struct bp_callbacks cbs = { - .on_hit = ifunc_ret_hit, - }; - breakpoint_set_callbacks(*ret, &cbs); - - (*ret)->os.ret_libsym = bp->libsym; - - return 0; -} - -int -os_breakpoint_init(struct process *proc, struct breakpoint *bp) -{ - if (bp->libsym != NULL && bp->libsym->os.is_ifunc) { - static struct bp_callbacks cbs = { - .get_return_bp = create_ifunc_ret_bp, - }; - breakpoint_set_callbacks(bp, &cbs); - } - return 0; -} - -void -os_breakpoint_destroy(struct breakpoint *bp) -{ -} - -int -os_breakpoint_clone(struct breakpoint *retp, struct breakpoint *bp) -{ - return 0; -} diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c index 6c3258e..220486c 100644 --- a/sysdeps/linux-gnu/trace.c +++ b/sysdeps/linux-gnu/trace.c @@ -46,11 +46,14 @@ #include "breakpoint.h" #include "debug.h" #include "events.h" +#include "fetch.h" #include "ltrace-elf.h" #include "options.h" #include "proc.h" +#include "prototype.h" #include "ptrace.h" #include "type.h" +#include "value.h" void trace_fail_warning(pid_t pid) @@ -1287,3 +1290,220 @@ linux_elf_add_plt_entry_irelative(struct process *proc, struct ltelf *lte, free(name); return i < 0 ? PLT_FAIL : PLT_OK; } + +static struct prototype * +void_prototype(void) +{ + static struct prototype ret; + if (ret.return_info == NULL) { + prototype_init(&ret); + ret.return_info = type_get_voidptr(); + ret.own_return_info = 0; + } + return &ret; +} + +int +os_library_symbol_init(struct library_symbol *libsym) +{ + libsym->os = (struct os_library_symbol_data){}; + return 0; +} + +void +os_library_symbol_destroy(struct library_symbol *libsym) +{ +} + +int +os_library_symbol_clone(struct library_symbol *retp, + struct library_symbol *libsym) +{ + retp->os = libsym->os; + return 0; +} + +enum plt_status +os_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) +{ + if (GELF_ST_TYPE(sym->st_info) == STT_FUNC) + return PLT_DEFAULT; + + bool ifunc = false; +#ifdef STT_GNU_IFUNC + ifunc = GELF_ST_TYPE(sym->st_info) == STT_GNU_IFUNC; +#endif + + if (ifunc) { +#define S ".IFUNC" + char *tmp_name = malloc(strlen(name) + sizeof S); + struct library_symbol *tmp = malloc(sizeof *tmp); + if (tmp_name == NULL || tmp == NULL) { + fail: + free(tmp_name); + free(tmp); + return PLT_FAIL; + } + sprintf(tmp_name, "%s%s", name, S); +#undef S + + if (library_symbol_init(tmp, addr, tmp_name, 1, + LS_TOPLT_NONE) < 0) + goto fail; + tmp->proto = void_prototype(); + tmp->os.is_ifunc = 1; + + *ret = tmp; + return PLT_OK; + } + + *ret = NULL; + return PLT_OK; +} + +static enum callback_status +libsym_at_address(struct library_symbol *libsym, void *addrp) +{ + arch_addr_t addr = *(arch_addr_t *)addrp; + return CBS_STOP_IF(addr == libsym->enter_addr); +} + +static void +ifunc_ret_hit(struct breakpoint *bp, struct process *proc) +{ + struct fetch_context *fetch = fetch_arg_init(LT_TOF_FUNCTION, proc, + type_get_voidptr()); + if (fetch == NULL) + return; + + struct breakpoint *nbp = NULL; + int own_libsym = 0; + + struct value value; + value_init(&value, proc, NULL, type_get_voidptr(), 0); + size_t sz = value_size(&value, NULL); + union { + uint64_t u64; + uint32_t u32; + arch_addr_t a; + } u; + + if (fetch_retval(fetch, LT_TOF_FUNCTIONR, proc, + value.type, &value) < 0 + || sz > 8 /* Captures failure as well. */ + || value_extract_buf(&value, (void *) &u, NULL) < 0) { + fail: + fprintf(stderr, + "Couldn't trace the function " + "indicated by IFUNC resolver.\n"); + goto done; + } + + assert(sz == 4 || sz == 8); + /* XXX double casts below: */ + if (sz == 4) + u.a = (arch_addr_t)(uintptr_t)u.u32; + else + u.a = (arch_addr_t)(uintptr_t)u.u64; + if (arch_translate_address_dyn(proc, u.a, &u.a) < 0) { + fprintf(stderr, "Couldn't OPD-translate the address returned" + " by the IFUNC resolver.\n"); + goto done; + } + + assert(bp->os.ret_libsym != NULL); + + struct library *lib = bp->os.ret_libsym->lib; + assert(lib != NULL); + + /* Look if we already have a symbol with this address. + * Otherwise create a new one. */ + struct library_symbol *libsym + = library_each_symbol(lib, NULL, libsym_at_address, &u.a); + if (libsym == NULL) { + libsym = malloc(sizeof *libsym); + char *name = strdup(bp->os.ret_libsym->name); + + if (libsym == NULL + || name == NULL + || library_symbol_init(libsym, u.a, name, 1, + LS_TOPLT_NONE) < 0) { + free(libsym); + free(name); + goto fail; + } + + /* Snip the .IFUNC token. */ + *strrchr(name, '.') = 0; + + own_libsym = 1; + library_add_symbol(lib, libsym); + } + + nbp = malloc(sizeof *bp); + if (nbp == NULL || breakpoint_init(nbp, proc, u.a, libsym) < 0) + goto fail; + + /* If there already is a breakpoint at that address, that is + * suspicious, but whatever. */ + struct breakpoint *pre_bp = insert_breakpoint(proc, nbp); + if (pre_bp == NULL) + goto fail; + if (pre_bp == nbp) { + /* PROC took our breakpoint, so these resources are + * not ours anymore. */ + nbp = NULL; + own_libsym = 0; + } + +done: + free(nbp); + if (own_libsym) { + library_symbol_destroy(libsym); + free(libsym); + } + fetch_arg_done(fetch); +} + +static int +create_ifunc_ret_bp(struct breakpoint **ret, + struct breakpoint *bp, struct process *proc) +{ + *ret = create_default_return_bp(proc); + if (*ret == NULL) + return -1; + static struct bp_callbacks cbs = { + .on_hit = ifunc_ret_hit, + }; + breakpoint_set_callbacks(*ret, &cbs); + + (*ret)->os.ret_libsym = bp->libsym; + + return 0; +} + +int +os_breakpoint_init(struct process *proc, struct breakpoint *bp) +{ + if (bp->libsym != NULL && bp->libsym->os.is_ifunc) { + static struct bp_callbacks cbs = { + .get_return_bp = create_ifunc_ret_bp, + }; + breakpoint_set_callbacks(bp, &cbs); + } + return 0; +} + +void +os_breakpoint_destroy(struct breakpoint *bp) +{ +} + +int +os_breakpoint_clone(struct breakpoint *retp, struct breakpoint *bp) +{ + return 0; +} |