diff options
author | Mikhail Durnev <mikhail_durnev@mentor.com> | 2020-09-10 15:35:49 +1000 |
---|---|---|
committer | Dave Watson <dade.watson@gmail.com> | 2020-09-21 08:54:35 -0700 |
commit | a4014f33775321b4106a1134b89020a7774902dd (patch) | |
tree | 1b14d8e12ed072b1e043b2a17db1de5203c8c910 /src | |
parent | c63c2369a5e9eefb6e5fbb240edaecae8856a02e (diff) | |
download | libunwind-a4014f33775321b4106a1134b89020a7774902dd.tar.gz |
Fix unwinding of pre-linked libraries
Prelinker updates section .eh_frame but does not change section .debug_frame.
Libunwind can work with prelinked .eh_frame, but if fails to find call frame
info in unmodified .debug_frame because it does not add the load offset.
ELF load offset from PT_LOAD p_vaddr has to be used to correctly interpret
addresses in the .debug_frame section.
Signed-off-by: Mikhail Durnev <mikhail_durnev@mentor.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/dwarf/Gfind_proc_info-lsb.c | 42 | ||||
-rw-r--r-- | src/dwarf/Gfind_unwind_table.c | 1 |
2 files changed, 35 insertions, 8 deletions
diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c index 6519740e..1311d9ab 100644 --- a/src/dwarf/Gfind_proc_info-lsb.c +++ b/src/dwarf/Gfind_proc_info-lsb.c @@ -107,13 +107,17 @@ linear_search (unw_addr_space_t as, unw_word_t ip, /* XXX: Could use mmap; but elf_map_image keeps tons mapped in. */ static int -load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) +load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local, unw_word_t *load_offset) { struct elf_image ei; + Elf_W (Ehdr) *ehdr; + Elf_W (Phdr) *phdr; Elf_W (Shdr) *shdr; + int i; int ret; ei.image = NULL; + *load_offset = 0; ret = elf_w (load_debuglink) (file, &ei, is_local); if (ret != 0) @@ -173,6 +177,20 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local) #if defined(SHF_COMPRESSED) } #endif + + ehdr = ei.image; + phdr = (Elf_W (Phdr) *) ((char *) ei.image + ehdr->e_phoff); + + for (i = 0; i < ehdr->e_phnum; ++i) + if (phdr[i].p_type == PT_LOAD) + { + *load_offset = phdr[i].p_vaddr; + + Debug (4, "%s load offset is 0x%zx\n", file, *load_offset); + + break; + } + munmap(ei.image, ei.size); return 0; } @@ -225,6 +243,7 @@ locate_debug_info (unw_addr_space_t as, unw_word_t addr, const char *dlname, int err; char *buf; size_t bufsize; + unw_word_t load_offset; /* First, see if we loaded this frame already. */ @@ -251,7 +270,7 @@ locate_debug_info (unw_addr_space_t as, unw_word_t addr, const char *dlname, else name = (char*) dlname; - err = load_debug_frame (name, &buf, &bufsize, as == unw_local_addr_space); + err = load_debug_frame (name, &buf, &bufsize, as == unw_local_addr_space, &load_offset); if (!err) { @@ -259,6 +278,7 @@ locate_debug_info (unw_addr_space_t as, unw_word_t addr, const char *dlname, fdesc->start = start; fdesc->end = end; + fdesc->load_offset = load_offset; fdesc->debug_frame = buf; fdesc->debug_frame_size = bufsize; fdesc->index = NULL; @@ -454,6 +474,7 @@ dwarf_find_debug_frame (int found, unw_dyn_info_t *di_debug, unw_word_t ip, di->format = UNW_INFO_FORMAT_TABLE; di->start_ip = fdesc->start; di->end_ip = fdesc->end; + di->load_offset = fdesc->load_offset; di->u.ti.name_ptr = (unw_word_t) (uintptr_t) obj_name; di->u.ti.table_data = (unw_word_t *) fdesc; di->u.ti.table_len = sizeof (*fdesc) / sizeof (unw_word_t); @@ -916,12 +937,14 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, ip_base = segbase; } + Debug (6, "lookup IP 0x%lx\n", (long) (ip - ip_base - di->load_offset)); + #ifndef UNW_REMOTE_ONLY if (as == unw_local_addr_space) { - e = lookup (table, table_len, ip - ip_base); + e = lookup (table, table_len, ip - ip_base - di->load_offset); if (e && &e[1] < &table[table_len]) - last_ip = e[1].start_ip_offset + ip_base; + last_ip = e[1].start_ip_offset + ip_base + di->load_offset; else last_ip = di->end_ip; } @@ -929,7 +952,7 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, #endif { #ifndef UNW_LOCAL_ONLY - int32_t last_ip_offset = di->end_ip - ip_base; + int32_t last_ip_offset = di->end_ip - ip_base - di->load_offset; segbase = di->u.rti.segbase; if ((ret = remote_lookup (as, (uintptr_t) table, table_len, ip - ip_base, &ent, &last_ip_offset, arg)) < 0) @@ -937,7 +960,7 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, if (ret) { e = &ent; - last_ip = last_ip_offset + ip_base; + last_ip = last_ip_offset + ip_base + di->load_offset; } else e = NULL; /* no info found */ @@ -951,8 +974,8 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, unwind info. */ return -UNW_ENOINFO; } - Debug (15, "ip=0x%lx, start_ip=0x%lx\n", - (long) ip, (long) (e->start_ip_offset)); + Debug (15, "ip=0x%lx, load_offset=0x%lx, start_ip=0x%lx\n", + (long) ip, (long) di->load_offset, (long) (e->start_ip_offset)); if (debug_frame_base) fde_addr = e->fde_offset + debug_frame_base; else @@ -976,6 +999,9 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, pi->flags = UNW_PI_FLAG_DEBUG_FRAME; } + pi->start_ip += di->load_offset; + pi->end_ip += di->load_offset; + #if defined(NEED_LAST_IP) pi->last_ip = last_ip; #else diff --git a/src/dwarf/Gfind_unwind_table.c b/src/dwarf/Gfind_unwind_table.c index f5f7ad06..a6198ad2 100644 --- a/src/dwarf/Gfind_unwind_table.c +++ b/src/dwarf/Gfind_unwind_table.c @@ -193,6 +193,7 @@ dwarf_find_unwind_table (struct elf_dyn_info *edi, unw_addr_space_t as, edi->di_cache.start_ip = start_ip; edi->di_cache.end_ip = end_ip; + edi->di_cache.load_offset = 0; edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE; edi->di_cache.u.rti.name_ptr = 0; /* two 32-bit values (ip_offset/fde_offset) per table-entry: */ |