From 0ce1641f4c4a4fa84d979869e9b141b013e88cf3 Mon Sep 17 00:00:00 2001 From: Aaron Esau Date: Wed, 20 Oct 2021 16:55:56 -0700 Subject: [PATCH] #29/#1: Add support for caller symbol resolution (closes #29) --- inc/context.h | 5 ++- inc/logging.h | 11 +++--- inc/symbol.h | 7 ++++ src/context.c | 4 +- src/debugger.c | 5 ++- src/handlers.c | 25 +++---------- src/symbol.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 125 insertions(+), 31 deletions(-) diff --git a/inc/context.h b/inc/context.h index 8495531..5ad8442 100644 --- a/inc/context.h +++ b/inc/context.h @@ -44,11 +44,12 @@ typedef struct HeaptraceContext { uint should_map_syms; char *between_pre_and_post; - ProcELFType ret_ptr_section_type; UBPWhen h_when; ProcessState h_state; uint64_t h_rip; + uint64_t h_ret_ptr; + ProcELFType h_ret_ptr_section_type; size_t h_size; uint64_t h_ptr; @@ -84,6 +85,8 @@ typedef struct HeaptraceFile { uint is_dynamic; uint is_stripped; SymbolEntry *se_head; + SymbolEntry *all_static_se_head; // all static symbols + ProcMapsEntry *pme; } HeaptraceFile; void *free_ctx(HeaptraceContext *ctx); diff --git a/inc/logging.h b/inc/logging.h index 9cec3bf..7fc730d 100644 --- a/inc/logging.h +++ b/inc/logging.h @@ -42,16 +42,18 @@ extern FILE *output_fd; #define fatal(fmt, ...) { fprintf(output_fd, (COLOR_ERROR_BOLD "heaptrace error: " fmt COLOR_RESET), ##__VA_ARGS__); } #define fatal2(f_, fmt, ...) { fprintf((f_), (COLOR_ERROR_BOLD "heaptrace error: " fmt COLOR_RESET), ##__VA_ARGS__); } +#define U64T "0x%" PRIx64 + #define SYM COLOR_SYMBOL_BOLD "#" COLOR_SYMBOL "%lu" COLOR_LOG #define SYM_IT COLOR_SYMBOL_ITALIC "#%lu" COLOR_LOG #define SZ COLOR_LOG_BOLD "0x%02lx" COLOR_LOG #define SZ_ERR COLOR_ERROR_BOLD "0x%02lx" COLOR_ERROR #define SZ_ARG(sz) ((long unsigned int)(sz)) #define CNT COLOR_LOG_BOLD "%lu" COLOR_LOG -#define PTR COLOR_LOG_BOLD "0x%llx" COLOR_LOG -#define PTR_ERR COLOR_ERROR_BOLD "0x%llx" COLOR_ERROR -#define PTR_IT COLOR_LOG_ITALIC "0x%llx" COLOR_LOG -#define PTR_ARG(ptr) ((long long unsigned int)(ptr)) +#define PTR COLOR_LOG_BOLD U64T COLOR_LOG +#define PTR_ERR COLOR_ERROR_BOLD U64T COLOR_ERROR +#define PTR_IT COLOR_LOG_ITALIC U64T COLOR_LOG +#define PTR_ARG(ptr) ((long unsigned int)(ptr)) #define log_heap(fmt, ...) { fprintf(output_fd, (COLOR_LOG fmt COLOR_RESET), ##__VA_ARGS__); } #define verbose_heap(fmt, ...) { if (OPT_VERBOSE) { fprintf(output_fd, (COLOR_LOG "\t^-- " COLOR_LOG_ITALIC fmt COLOR_RESET "\n"), ##__VA_ARGS__); } } @@ -62,4 +64,3 @@ extern FILE *output_fd; void describe_symbol(void *ptr); -#define U64T "0x%" PRIx64 diff --git a/inc/symbol.h b/inc/symbol.h index a3d0973..4dc3307 100644 --- a/inc/symbol.h +++ b/inc/symbol.h @@ -26,6 +26,7 @@ typedef struct SymbolEntry { char *name; uint64_t offset; + uint64_t size; int section; int type; // SE_TYPE_STATIC, SE_TYPE_DYNAMIC, SE_TYPE_DYNAMIC_PLT @@ -39,4 +40,10 @@ int all_se_type(SymbolEntry *se_head, int type); SymbolEntry *find_se_name(SymbolEntry *se_head, char *name); void free_se_list(SymbolEntry *se_head); +SymbolEntry *find_symbol_by_address(HeaptraceFile *hf, uint64_t addr); +HeaptraceFile *find_heaptrace_file_by_address(HeaptraceContext *ctx, uint64_t addr); +char *find_symbol_name_by_address(HeaptraceContext *ctx, uint64_t addr); + +char *get_source_function(HeaptraceContext *ctx); + #endif diff --git a/src/context.c b/src/context.c index d752f68..ac230e1 100644 --- a/src/context.c +++ b/src/context.c @@ -13,7 +13,7 @@ HeaptraceFile *alloc_file(HeaptraceContext *ctx) { HeaptraceContext *alloc_ctx() { HeaptraceContext *ctx = (HeaptraceContext *)calloc(1, sizeof(HeaptraceContext)); - ctx->ret_ptr_section_type = PROCELF_TYPE_UNKNOWN; + ctx->h_ret_ptr_section_type = PROCELF_TYPE_UNKNOWN; ctx->target = alloc_file(ctx); ctx->libc = alloc_file(ctx); return ctx; @@ -28,7 +28,9 @@ void *free_ctx(HeaptraceContext *ctx) { free(ctx->se_names); free_se_list(ctx->target->se_head); + free_se_list(ctx->target->all_static_se_head); free_se_list(ctx->libc->se_head); + free_se_list(ctx->libc->all_static_se_head); free(ctx->target); free(ctx->libc); diff --git a/src/debugger.c b/src/debugger.c index c6156ae..86a56e4 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -73,7 +73,8 @@ void _check_breakpoints(HeaptraceContext *ctx) { if (OPT_VERBOSE) { ProcMapsEntry *pme = pme_find_addr(ctx->pme_head, val_at_reg_rsp); if (pme) { - ctx->ret_ptr_section_type = pme->pet; + ctx->h_ret_ptr_section_type = pme->pet; + ctx->h_ret_ptr = val_at_reg_rsp; } } @@ -410,6 +411,8 @@ uint map_syms(HeaptraceContext *ctx) { if (!ctx->pme_head) ctx->pme_head = build_pme_list(ctx->pid); // already built if attaching ProcMapsEntry *bin_pme = pme_walk(ctx->pme_head, PROCELF_TYPE_BINARY); ProcMapsEntry *libc_pme = pme_walk(ctx->pme_head, PROCELF_TYPE_LIBC); + ctx->target->pme = bin_pme; + ctx->libc->pme = libc_pme; // quick debug info about addresses/paths we found ASSERT(bin_pme, "failed to find target binary in process mapping (!bin_pme). Please report this!"); diff --git a/src/handlers.c b/src/handlers.c index cfb040c..fd94a56 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -4,23 +4,8 @@ #include "options.h" #include "user-breakpoint.h" -static inline char *_get_source_section(HeaptraceContext *ctx) { - if (OPT_VERBOSE) { - switch (ctx->ret_ptr_section_type) { - case PROCELF_TYPE_LIBC: - return "caller: libc"; - break; - case PROCELF_TYPE_UNKNOWN: - return "caller: a library"; - break; - case PROCELF_TYPE_BINARY: - return "caller: binary"; - break; - } - } - return "caller: (unknown)"; -} +#define PRINT_SOURCE() { if (OPT_VERBOSE) { char *SRC_FUNC = get_source_function(ctx); verbose_heap("called by: " COLOR_LOG_BOLD "%s" COLOR_LOG "; returns to " PTR, SRC_FUNC, ctx->h_ret_ptr); free(SRC_FUNC); } } // check if pointer is in stack, libc, or binary, and error if so @@ -54,7 +39,7 @@ void pre_calloc(HeaptraceContext *ctx, uint64_t nmemb, uint64_t isize) { void post_calloc(HeaptraceContext *ctx, uint64_t ptr) { log_heap("= " PTR "\n", PTR_ARG(ptr)); - verbose_heap("%s", _get_source_section(ctx)); + PRINT_SOURCE() // store meta info Chunk *chunk = alloc_chunk(ctx, ptr); @@ -101,7 +86,7 @@ void pre_malloc(HeaptraceContext *ctx, uint64_t isize) { void post_malloc(HeaptraceContext *ctx, uint64_t ptr) { log_heap("= " PTR "\n", PTR_ARG(ptr)); - verbose_heap("%s", _get_source_section(ctx)); + PRINT_SOURCE() // store meta info Chunk *chunk = alloc_chunk(ctx, ptr); @@ -179,7 +164,7 @@ void pre_free(HeaptraceContext *ctx, uint64_t iptr) { void post_free(HeaptraceContext *ctx, uint64_t retval) { ctx->between_pre_and_post = 0; log(COLOR_RESET); - verbose_heap("%s", _get_source_section(ctx)); + PRINT_SOURCE() } @@ -246,7 +231,7 @@ static inline void _post_realloc(HeaptraceContext *ctx, int _type, uint64_t new_ log("\t%s(" SYM_IT "=" PTR_IT ")", COLOR_LOG_ITALIC, ctx->h_orig_chunk->ops[STATE_MALLOC], PTR_ARG(ctx->h_ptr)); } log_heap("\n"); - verbose_heap("%s", _get_source_section(ctx)); + PRINT_SOURCE() //warn("this code is untested; please report any issues you come across @ https://github.com/Arinerron/heaptrace/issues/new/choose"); Chunk *new_chunk = alloc_chunk(ctx, new_ptr); diff --git a/src/symbol.c b/src/symbol.c index b963714..026f38e 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -260,6 +260,7 @@ void lookup_symbols(HeaptraceFile *hf, char *names[]) { } // resolve static symbols + SymbolEntry *all_static_se_head = 0; if (strtab_off && symtab_off) { for (size_t j = 0; j * sizeof(Elf64_Sym) < symtab_sz; j++) { Elf64_Sym sym; @@ -271,30 +272,87 @@ void lookup_symbols(HeaptraceFile *hf, char *names[]) { _CHECK_BOUNDS(name, "static: name"); size_t n = strlen(name); + uint64_t offset = (uint64_t)(sym.st_value); + // XXX: for some reason libc has a load addr of 0x40 that's throwing stuff off. This is a stopgap solution for that. + if (!is_dynamic) offset -= load_addr; + SymbolEntry *cse = se_head; while (1) { if (!cse) break; if (((!cse->offset && sym.st_value) || cse->type == SE_TYPE_UNRESOLVED) && strcmp(cse->name, name) == 0) { debug("tab: st_name: %s @ " U64T "\n", name, (uint64_t)sym.st_value); cse->type = SE_TYPE_STATIC; - //cse->offset = (uint64_t)(sym.st_value); - cse->offset = (uint64_t)(sym.st_value) - load_addr; - cse->_sub_offset = load_addr; // XXX: for some reason libc has a load addr of 0x40 that's throwing stuff off. This is a stopgap solution for that. + cse->offset = offset; + cse->_sub_offset = 0; + cse->size = sym.st_size; cse->section = sym.st_shndx; if (sym.st_value) is_stripped = 0; } cse = cse->_next; } + + // now add to all_static_se_head + SymbolEntry *_cur_static_se = (SymbolEntry *)calloc(1, sizeof(SymbolEntry)); + _cur_static_se->_next = all_static_se_head; + all_static_se_head = _cur_static_se; + + _cur_static_se->name = strdup(name); + _cur_static_se->offset = offset; + _cur_static_se->size = sym.st_size; + _cur_static_se->_sub_offset = 0; + _cur_static_se->type = SE_TYPE_STATIC; + _cur_static_se->section = sym.st_shndx; + + //printf("%s\toffset=%p, size=%p\n", _cur_static_se->name, _cur_static_se->offset, _cur_static_se->size); } } } + hf->all_static_se_head = all_static_se_head; hf->se_head = se_head; hf->is_stripped = is_stripped; hf->is_dynamic = is_dynamic; } +SymbolEntry *find_symbol_by_address(HeaptraceFile *hf, uint64_t addr) { + if (!(hf->pme) || addr < hf->pme->base || addr >= hf->pme->end) return 0; // not in bounds + addr -= hf->pme->base; + + SymbolEntry *cur_se = hf->all_static_se_head; + while(cur_se) { + //printf("... %s\taddr=%p, offset=%p, offset+size=%p\n", cur_se->name, addr, cur_se->offset, cur_se->offset + cur_se->size); + if (addr >= cur_se->offset && addr < cur_se->offset + cur_se->size) return cur_se; + cur_se = cur_se->_next; + } + + return 0; +} + + +HeaptraceFile *find_heaptrace_file_by_address(HeaptraceContext *ctx, uint64_t addr) { + HeaptraceFile *hfs[] = {ctx->target, ctx->libc}; + HeaptraceFile *cur_hf; + for (int i = 0; i < sizeof(hfs) / sizeof(hfs[0]); i++) { + cur_hf = hfs[i]; + if (cur_hf->pme && addr >= cur_hf->pme->base && addr < cur_hf->pme->end) { + return cur_hf; + } + } + return 0; +} + + +char *find_symbol_name_by_address(HeaptraceContext *ctx, uint64_t addr) { + HeaptraceFile *hf = find_heaptrace_file_by_address(ctx, addr); + if (!hf) return 0; + SymbolEntry *se = find_symbol_by_address(hf, addr); + if (!se) return 0; + return se->name; +} + + + SymbolEntry *any_se_type(SymbolEntry *se_head, int type) { SymbolEntry *cse = se_head; while (cse) { @@ -341,3 +399,38 @@ void free_se_list(SymbolEntry *se_head) { cse = next_cse; } } + + +char *get_source_function(HeaptraceContext *ctx) { + char *section = ""; + if (OPT_VERBOSE) { + switch (ctx->h_ret_ptr_section_type) { + case PROCELF_TYPE_LIBC: + section = "libc"; + break; + case PROCELF_TYPE_UNKNOWN: + section = ""; + break; + case PROCELF_TYPE_BINARY: + section = 0; + break; + } + } + + char *symbol_name = find_symbol_name_by_address(ctx, ctx->h_ret_ptr); + size_t buf_size = 2; + if (symbol_name) buf_size += strlen(symbol_name); + if (section) buf_size += 1 + strlen(section); + + char *buf = calloc(1, buf_size); + if (symbol_name) strcat(buf, symbol_name); + if (symbol_name && section) strcat(buf, "@"); + if (section) strcat(buf, section); + + if (!strlen(buf)) { + free(buf); + buf = strdup("binary"); // this is the only way `section` can be NULL + } + + return buf; +}