Index: libunwind/src/mi/Gget_unwind_table.c =================================================================== --- /dev/null +++ libunwind/src/mi/Gget_unwind_table.c @@ -0,0 +1,192 @@ +// Copyright 2007, Red Hat Inc. + +/* This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "libunwind_i.h" +//#include "remote.h" +#include "dwarf-eh.h" + +int +unw_get_unwind_table(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *image, size_t size, + unsigned long segbase, unsigned long mapoff, void *arg) +{ + Debug(99, "Entering get_unwind_table\n"); + Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL; + unw_word_t addr, eh_frame_start, fde_count, load_base; + struct dwarf_eh_frame_hdr *hdr; + Elf_W(Ehdr) *ehdr; + int i, ret; + unw_dyn_info_t di_cache; + + if (size <= EI_CLASS) + return -1; + + Debug(99, "Checking elf size\n"); + if (!(memcmp (image, ELFMAG, SELFMAG) == 0 + && ((uint8_t *) image)[EI_CLASS] == ELF_CLASS)) + return -1; + + Debug(99, "Checked elf class\n"); + ehdr = image; + phdr = (Elf_W(Phdr) *) ((char *) image + ehdr->e_phoff); + for (i = 0; i < ehdr->e_phnum; ++i) + { + switch (phdr[i].p_type) + { + case PT_LOAD: + if (phdr[i].p_offset == mapoff) + ptxt = phdr + i; + break; + + case PT_GNU_EH_FRAME: + peh_hdr = phdr + i; + break; + + case PT_DYNAMIC: + pdyn = phdr + i; + break; + + default: + break; + } + } + + Debug(99, "Traversed headers\n"); + if (!ptxt || !peh_hdr) + return -UNW_ENOINFO; + + if (pdyn) + { + Debug(99, "Got dynamic header\n"); + /* For dynamicly linked executables and shared libraries, + DT_PLTGOT is the value that data-relative addresses are + relative to for that object. We call this the "gp". */ + Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset + + (char *) image); + for (; dyn->d_tag != DT_NULL; ++dyn) + if (dyn->d_tag == DT_PLTGOT) + { + /* Assume that _DYNAMIC is writable and GLIBC has + relocated it (true for x86 at least). */ + di_cache.gp = dyn->d_un.d_ptr; + break; + } + } + else + /* Otherwise this is a static executable with no _DYNAMIC. Assume + that data-relative addresses are relative to 0, i.e., + absolute. */ + di_cache.gp = 0; + + Debug(99, "Got eh_frame_hdr\n"); + hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset + + (char *) image); + if (hdr->version != DW_EH_VERSION) + { + Debug (1, "table has unexpected version %d\n", + hdr->version); + return 0; + } + + Debug(99, "EH_VERSION is correct\n"); + + addr = (unw_word_t) (hdr + 1); + Debug (99, "Got addr\n"); + /* Fill in a dummy proc_info structure. We just need to fill in + enough to ensure that dwarf_read_encoded_pointer() can do it's + job. Since we don't have a procedure-context at this point, all + we have to do is fill in the global-pointer. */ + memset (pi, 0, sizeof (*pi)); + Debug(99, "cleared pi\n"); + pi->gp = di_cache.gp; + + Debug(99, "set pi gp\n"); + + +//The following is a dummy local address space used by dwarf_read_encoded_pointer. + int + local_access_mem (unw_addr_space_t as, unw_word_t addr, + unw_word_t *val, int write, void *arg) + { + Debug(99, "entering local_access_mem, reading addr: 0x%lx into: %p\n", + (long) addr, val); + if (write) + { + Debug (16, "mem[%x] <- %x\n", addr, *val); + *(unw_word_t *) addr = *val; + } + else + { + *val = *(unw_word_t *) addr; + Debug (16, "mem[%x] -> %x\n", addr, *val); + } + Debug(99, "leaving local_access_mem\n"); + return 0; + } + + unw_accessors_t temp_local_accessors = {NULL, NULL, NULL, local_access_mem, + NULL, NULL, NULL, NULL, NULL}; + unw_addr_space_t temp_local_addr_space + = unw_create_addr_space(&temp_local_accessors, 0); + + /* (Optionally) read eh_frame_ptr: */ + if ((ret = dwarf_read_encoded_pointer (temp_local_addr_space, &temp_local_accessors, + &addr, hdr->eh_frame_ptr_enc, pi, + &eh_frame_start, NULL)) < 0) + return -1; + + Debug(99, "read eh_frame_start: 0x%lx\n", (long) eh_frame_start); + + /* (Optionally) read fde_count: */ + if ((ret = dwarf_read_encoded_pointer (temp_local_addr_space, &temp_local_accessors, + &addr, hdr->fde_count_enc, pi, + &fde_count, NULL)) < 0) + return -1; + + Debug(99, "read fde_count: 0x%lx\n", (long) fde_count); + if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) + { + return -1; + } + + load_base = segbase - ptxt->p_vaddr; + + di_cache.start_ip = segbase; + di_cache.end_ip = di_cache.start_ip + ptxt->p_memsz; + di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE; + di_cache.u.rti.name_ptr = 0; + /* two 32-bit values (ip_offset/fde_offset) per table-entry: */ + di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t); + di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr) + + (addr - (unw_word_t) image + - peh_hdr->p_offset)); + + /* For the binary-search table in the eh_frame_hdr, data-relative + means relative to the start of that section... */ + di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr) + + ((unw_word_t) hdr - (unw_word_t) image + - peh_hdr->p_offset)); + + Debug(99, "Leaving get_unwind_table\n"); + return tdep_search_unwind_table (as, ip, &di_cache, pi, need_unwind_info, arg); +} Index: libunwind/include/libunwind-common.h.in =================================================================== --- libunwind.orig/include/libunwind-common.h.in +++ libunwind/include/libunwind-common.h.in @@ -225,6 +225,7 @@ unw_save_loc_t; #define unw_regname UNW_ARCH_OBJ(regname) #define unw_flush_cache UNW_ARCH_OBJ(flush_cache) #define unw_strerror UNW_ARCH_OBJ(strerror) +#define unw_get_unwind_table UNW_ARCH_OBJ(get_unwind_table) extern unw_addr_space_t unw_create_addr_space (unw_accessors_t *, int); extern void unw_destroy_addr_space (unw_addr_space_t); @@ -248,5 +249,10 @@ extern int unw_get_save_loc (unw_cursor_ extern int unw_is_signal_frame (unw_cursor_t *); extern int unw_get_proc_name (unw_cursor_t *, char *, size_t, unw_word_t *); extern const char *unw_strerror (int); +extern int unw_get_unwind_table(unw_addr_space_t as, unw_word_t ip, + unw_proc_info_t *pi, int need_unwind_info, + void *image, size_t size, + unsigned long segbase, unsigned long mapoff, + void *arg); extern unw_addr_space_t unw_local_addr_space; Index: libunwind/src/Makefile.am =================================================================== --- libunwind.orig/src/Makefile.am +++ libunwind/src/Makefile.am @@ -56,7 +56,7 @@ libunwind_la_SOURCES_generic = \ mi/Gput_dynamic_unwind_info.c mi/Gdestroy_addr_space.c \ mi/Gget_reg.c mi/Gset_reg.c \ mi/Gget_fpreg.c mi/Gset_fpreg.c \ - mi/Gset_caching_policy.c + mi/Gset_caching_policy.c mi/Gget_unwind_table.c # List of arch-independent files needed by local-only library (libunwind): libunwind_la_SOURCES_local = \