fix dwarf unwinding

This commit is contained in:
Stefani Seibold 2018-04-17 11:28:37 +02:00
parent 73a4eca5ce
commit 495a9d535d
6 changed files with 26 additions and 68 deletions

View File

@ -1035,7 +1035,7 @@ static int sort_total(const struct rb_stack **p, const struct rb_stack **q)
static void _process_dump(struct process *process, int (*sortby)(const struct rb_stack **, const struct rb_stack **), int (*skipfunc)(struct rb_stack *), FILE *file, int lflag) static void _process_dump(struct process *process, int (*sortby)(const struct rb_stack **, const struct rb_stack **), int (*skipfunc)(struct rb_stack *), FILE *file, int lflag)
{ {
struct rb_stack **arr; struct rb_stack **arr = NULL;
unsigned long i; unsigned long i;
void *data; void *data;
unsigned long stack_trees = process->stack_trees; unsigned long stack_trees = process->stack_trees;
@ -1045,9 +1045,12 @@ static void _process_dump(struct process *process, int (*sortby)(const struct rb
if (dump_init(file) == -1) if (dump_init(file) == -1)
return; return;
if (!stack_trees)
goto skip;
arr = malloc(sizeof(struct rb_stack *) * stack_trees); arr = malloc(sizeof(struct rb_stack *) * stack_trees);
if (!arr) if (!arr)
return; goto skip;
for(i = 0, data = rb_first(&process->stack_table); data; data = rb_next(data)) { for(i = 0, data = rb_first(&process->stack_table); data; data = rb_next(data)) {
struct rb_stack *stack_node = container_of(data, struct rb_stack, node); struct rb_stack *stack_node = container_of(data, struct rb_stack, node);
@ -1060,9 +1063,6 @@ static void _process_dump(struct process *process, int (*sortby)(const struct rb
dump_printf("Process dump %d %s\n", process->pid, process->filename ? process->filename : "<unknown>"); dump_printf("Process dump %d %s\n", process->pid, process->filename ? process->filename : "<unknown>");
if (!stack_trees)
goto skip;
qsort(arr, stack_trees, sizeof(struct rb_stack *), (void *)sortby); qsort(arr, stack_trees, sizeof(struct rb_stack *), (void *)sortby);
if (file == stderr) { if (file == stderr) {
@ -1245,8 +1245,10 @@ int process_scan(struct process *process, void *leaks, uint32_t payload_len)
for(i = 0; i < new; ++i) { for(i = 0; i < new; ++i) {
struct rb_block *block = process_rb_search(&process->block_table, process->get_ulong(new_leaks)); struct rb_block *block = process_rb_search(&process->block_table, process->get_ulong(new_leaks));
if (options.verbose > 1) {
if (dump_printf(" leaked at 0x%08lx (%lu bytes)\n", (unsigned long)block->addr, (unsigned long)block->size) == -1) if (dump_printf(" leaked at 0x%08lx (%lu bytes)\n", (unsigned long)block->addr, (unsigned long)block->size) == -1)
break; break;
}
new_leaks += process->ptr_size; new_leaks += process->ptr_size;
} }

20
dwarf.c
View File

@ -871,20 +871,16 @@ static int dwarf_extract_cfi_from_fde(struct dwarf_addr_space *as, void *addrp)
return 0; return 0;
} }
static inline int lib_addr_match(struct libref *libref, arch_addr_t ip)
{
return ip >= libref->txt_vaddr && ip < libref->txt_vaddr + libref->txt_size;
}
int dwarf_locate_map(struct dwarf_addr_space *as, arch_addr_t ip) int dwarf_locate_map(struct dwarf_addr_space *as, arch_addr_t ip)
{ {
struct dwarf_cursor *c = &as->cursor; struct dwarf_cursor *c = &as->cursor;
struct libref *libref = c->libref;
if (c->use_prev_instr) if (c->use_prev_instr)
ip -= 1; ip -= 1;
if (likely(c->libref)) { if (likely(c->libref)) {
if (lib_addr_match(c->libref, ip)) if (ip >= libref->txt_vaddr && ip < libref->txt_vaddr + libref->txt_size)
return 0; return 0;
} }
@ -930,20 +926,19 @@ static int dwarf_search_unwind_table(struct dwarf_addr_space *as, arch_addr_t ip
struct dwarf_cie_info *dci = &as->cursor.dci; struct dwarf_cie_info *dci = &as->cursor.dci;
struct libref *libref = as->cursor.libref; struct libref *libref = as->cursor.libref;
e = lookup(fde_tab, fde_count, ip - libref->txt_vaddr - libref->eh_frame_hdr); e = lookup(fde_tab, fde_count, ip - libref->eh_hdr_vaddr);
if (unlikely(!e)) { if (unlikely(!e)) {
/* IP is inside this table's range, but there is no explicit unwind info. */ /* IP is inside this table's range, but there is no explicit unwind info. */
debug(DEBUG_DWARF, "no unwind info found for IP %#lx", ip); debug(DEBUG_DWARF, "no unwind info found for IP %#lx", ip);
return -DWARF_ENOINFO; return -DWARF_ENOINFO;
} }
fde_addr = libref->mmap_addr - (libref->txt_offset - libref->mmap_offset) + e->fde_offset + libref->eh_frame_hdr; fde_addr = libref->mmap_addr + e->fde_offset + libref->eh_hdr_offset;
if (unlikely((ret = dwarf_extract_cfi_from_fde(as, fde_addr)) < 0)) if (unlikely((ret = dwarf_extract_cfi_from_fde(as, fde_addr)) < 0))
return ret; return ret;
dci->start_ip -= ARCH_ADDR_T(libref->mmap_addr) - libref->txt_vaddr; dci->start_ip = dci->start_ip - (arch_addr_t)libref->mmap_addr + libref->txt_vaddr;
if (!as->is_64bit) if (!as->is_64bit)
dci->start_ip &= 0xffffffff; dci->start_ip &= 0xffffffff;
@ -1942,8 +1937,6 @@ fail:
c->ip = ip; c->ip = ip;
if (ret == -DWARF_ENOINFO) { if (ret == -DWARF_ENOINFO) {
debug(DEBUG_DWARF, "try arch specific step");
ret = dwarf_arch_step(as); ret = dwarf_arch_step(as);
if (!ret) { if (!ret) {
if (dwarf_locate_map(as, c->ip) < 0) if (dwarf_locate_map(as, c->ip) < 0)
@ -1988,6 +1981,7 @@ fail:
break; break;
} }
#endif #endif
if (ret != -DWARF_ENOINFO && ret != -DWARF_EBADFRAME)
debug(DEBUG_DWARF, "error %d", ret); debug(DEBUG_DWARF, "error %d", ret);
c->valid = 0; c->valid = 0;
@ -2003,7 +1997,7 @@ int dwarf_get_unwind_table(struct task *task, struct libref *libref)
arch_addr_t eh_frame = 0; arch_addr_t eh_frame = 0;
int ret; int ret;
struct dwarf_addr_space tmp_as; struct dwarf_addr_space tmp_as;
struct dwarf_eh_frame_hdr *hdr = (struct dwarf_eh_frame_hdr *)(libref->mmap_addr + libref->eh_frame_hdr); struct dwarf_eh_frame_hdr *hdr = (struct dwarf_eh_frame_hdr *)(libref->mmap_addr + libref->eh_hdr_offset);
memset(&tmp_as, 0, sizeof(tmp_as)); memset(&tmp_as, 0, sizeof(tmp_as));

View File

@ -58,7 +58,6 @@
#define DWARF_EINVAL 4 /* unsupported operation or bad value */ #define DWARF_EINVAL 4 /* unsupported operation or bad value */
#define DWARF_EBADVERSION 5 /* unwind info has unsupported version */ #define DWARF_EBADVERSION 5 /* unwind info has unsupported version */
#define DWARF_ENOINFO 6 /* no unwind info found */ #define DWARF_ENOINFO 6 /* no unwind info found */
#define DWARF_STOPUNWIND 7 /* no unwind info found */
struct dwarf_cie_info { struct dwarf_cie_info {
arch_addr_t start_ip; /* first IP covered by this procedure */ arch_addr_t start_ip; /* first IP covered by this procedure */

View File

@ -210,11 +210,6 @@ static void library_each_symbol(struct libref *libref, void (*cb)(struct library
} }
} }
static inline int lib_addr_match(struct libref *libref, arch_addr_t addr)
{
return addr >= libref->txt_vaddr && addr < libref->txt_vaddr + libref->txt_size;
}
struct libref *addr2libref(struct task *leader, arch_addr_t addr) struct libref *addr2libref(struct task *leader, arch_addr_t addr)
{ {
struct rb_node **new = &(leader->libraries_tree.rb_node); struct rb_node **new = &(leader->libraries_tree.rb_node);
@ -223,7 +218,7 @@ struct libref *addr2libref(struct task *leader, arch_addr_t addr)
while (*new) { while (*new) {
struct libref *this = container_of(*new, struct library, rb_node)->libref; struct libref *this = container_of(*new, struct library, rb_node)->libref;
if (lib_addr_match(this, addr)) if (addr >= this->txt_vaddr && addr < this->txt_vaddr + this->txt_size)
return this; return this;
if (this->txt_vaddr < addr) if (this->txt_vaddr < addr)
@ -321,15 +316,3 @@ const char *library_execname(struct task *leader)
return container_of(leader->libraries_list.next, struct library, list)->libref->filename; return container_of(leader->libraries_list.next, struct library, list)->libref->filename;
} }
arch_addr_t vaddr_to_off(struct libref *libref, arch_addr_t addr)
{
for(unsigned int i = 0; i < libref->loadsegs; ++i) {
GElf_Phdr *phdr = &libref->loadseg[i];
if (phdr->p_vaddr >= addr && phdr->p_vaddr + phdr->p_filesz < addr)
return phdr->p_offset + addr - phdr->p_vaddr;
}
fprintf(stderr, "%s:%d\n", __func__, __LINE__);
return ~0LU;
}

View File

@ -62,10 +62,6 @@ struct libref {
unsigned long txt_size; unsigned long txt_size;
unsigned long txt_offset; unsigned long txt_offset;
/* loadable segments */
unsigned int loadsegs;
GElf_Phdr loadseg[4];
/* mapped image */ /* mapped image */
void *mmap_addr; void *mmap_addr;
unsigned long mmap_offset; unsigned long mmap_offset;
@ -73,10 +69,10 @@ struct libref {
/* global-pointer */ /* global-pointer */
arch_addr_t pltgot; arch_addr_t pltgot;
unsigned long eh_frame_hdr; unsigned long eh_hdr_offset;
unsigned long eh_hdr_vaddr;
void *fde_tab; void *fde_tab;
unsigned long fde_count; unsigned long fde_count;
unsigned long eh_frame;
unsigned int type; unsigned int type;
#ifdef __arm__ #ifdef __arm__
@ -141,8 +137,5 @@ void libref_set_filename(struct libref *libref, const char *new_name);
/* find library by address */ /* find library by address */
struct libref *addr2libref(struct task *leader, arch_addr_t addr); struct libref *addr2libref(struct task *leader, arch_addr_t addr);
/* return offset for virtual address */
arch_addr_t vaddr_to_off(struct libref *libref, arch_addr_t addr);
#endif #endif

25
mtelf.c
View File

@ -54,8 +54,6 @@ struct mt_elf {
int fd; int fd;
const char *filename; const char *filename;
Elf *elf; Elf *elf;
unsigned int loadsegs;
GElf_Phdr loadseg[4];
unsigned long loadbase; unsigned long loadbase;
unsigned long loadsize; unsigned long loadsize;
unsigned long vstart; unsigned long vstart;
@ -273,13 +271,10 @@ static int elf_lib_init(struct mt_elf *mte, struct task *task, struct libref *li
libref->txt_size = mte->txt_hdr.p_filesz; libref->txt_size = mte->txt_hdr.p_filesz;
libref->txt_offset = mte->txt_hdr.p_offset - mte->loadbase; libref->txt_offset = mte->txt_hdr.p_offset - mte->loadbase;
libref->bias = mte->bias; libref->bias = mte->bias;
libref->eh_frame_hdr = mte->eh_hdr.p_offset - mte->loadbase; libref->eh_hdr_offset = mte->eh_hdr.p_offset - mte->loadbase;
libref->pltgot = mte->pltgot; libref->eh_hdr_vaddr = mte->eh_hdr.p_vaddr - mte->vstart + mte->bias;
libref->pltgot = mte->pltgot - mte->vstart + mte->bias;
libref->dyn = mte->dyn - mte->vstart + mte->bias; libref->dyn = mte->dyn - mte->vstart + mte->bias;
libref->loadsegs = mte->loadsegs;
for(unsigned int i = 0; i < libref->loadsegs; ++i)
libref->loadseg[i] = mte->loadseg[i];
#ifdef __arm__ #ifdef __arm__
if (mte->exidx_hdr.p_filesz) { if (mte->exidx_hdr.p_filesz) {
@ -316,6 +311,7 @@ static int elf_read(struct mt_elf *mte, struct task *task, const char *filename)
unsigned long loadbase = ~0; unsigned long loadbase = ~0;
unsigned long align; unsigned long align;
unsigned long vstart; unsigned long vstart;
unsigned int loadsegs = 0;
debug(DEBUG_FUNCTION, "filename=%s", filename); debug(DEBUG_FUNCTION, "filename=%s", filename);
@ -329,17 +325,10 @@ static int elf_read(struct mt_elf *mte, struct task *task, const char *filename)
memset(&mte->eh_hdr, 0, sizeof(mte->eh_hdr)); memset(&mte->eh_hdr, 0, sizeof(mte->eh_hdr));
memset(&mte->exidx_hdr, 0, sizeof(mte->exidx_hdr)); memset(&mte->exidx_hdr, 0, sizeof(mte->exidx_hdr));
mte->loadsegs = 0;
for (i = 0; gelf_getphdr(mte->elf, i, &phdr) != NULL; ++i) { for (i = 0; gelf_getphdr(mte->elf, i, &phdr) != NULL; ++i) {
switch (phdr.p_type) { switch (phdr.p_type) {
case PT_LOAD: case PT_LOAD:
if (mte->loadsegs >= ARRAY_SIZE(mte->loadseg)) { loadsegs++;
fprintf(stderr, "Unable to handle more than %lu loadable segments in %s\n", ARRAY_SIZE(mte->loadseg), filename);
return -1;
}
mte->loadseg[mte->loadsegs++] = phdr;
align = phdr.p_align; align = phdr.p_align;
if (align) if (align)
@ -379,15 +368,13 @@ static int elf_read(struct mt_elf *mte, struct task *task, const char *filename)
} }
} }
if (!mte->loadsegs) { if (!loadsegs) {
fprintf(stderr, "No loadable segemnts in %s\n", filename); fprintf(stderr, "No loadable segemnts in %s\n", filename);
return -1; return -1;
} }
//fprintf(stderr, "%s:%d %s loadbase:%#lx loadsize:%#lx\n", __func__, __LINE__, filename, loadbase, loadsize);
mte->loadbase = loadbase & ~PAGEALIGN; mte->loadbase = loadbase & ~PAGEALIGN;
mte->loadsize = (loadsize + (loadbase - mte->loadbase) + PAGEALIGN) & ~PAGEALIGN; mte->loadsize = (loadsize + (loadbase - mte->loadbase) + PAGEALIGN) & ~PAGEALIGN;
fprintf(stderr, "%s:%d %s loadbase:%#lx loadsize:%#lx\n", __func__, __LINE__, mte->filename, mte->loadbase, mte->loadsize);
debug(DEBUG_FUNCTION, "filename=`%s' text offset=%#llx addr=%#llx size=%#llx", debug(DEBUG_FUNCTION, "filename=`%s' text offset=%#llx addr=%#llx size=%#llx",
filename, filename,