From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 26210 invoked by alias); 11 Dec 2007 14:36:56 -0000 Received: (qmail 26201 invoked by uid 22791); 11 Dec 2007 14:36:53 -0000 X-Spam-Status: No, hits=-1.9 required=5.0 tests=AWL,BAYES_00,DK_POLICY_SIGNSOME,FORGED_RCVD_HELO,TW_CP,TW_JB,TW_PT X-Spam-Check-By: sourceware.org Received: from wildebeest.demon.nl (HELO gnu.wildebeest.org) (83.160.170.119) by sourceware.org (qpsmtpd/0.31) with ESMTP; Tue, 11 Dec 2007 14:36:45 +0000 Received: from dijkstra.wildebeest.org ([192.168.1.29]) by gnu.wildebeest.org with esmtp (Exim 4.63) (envelope-from ) id 1J26Dw-0003o1-Qj for frysk@sourceware.org; Tue, 11 Dec 2007 15:36:42 +0100 Subject: [patch] libunwind table accessor interface From: Mark Wielaard To: frysk@sourceware.org Content-Type: multipart/mixed; boundary="=-+XZbkIh4oqDlgZcu9H6A" Date: Tue, 11 Dec 2007 14:36:00 -0000 Message-Id: <1197383800.3084.66.camel@dijkstra.wildebeest.org> Mime-Version: 1.0 X-Mailer: Evolution 2.12.2 (2.12.2-2.fc8) X-Spam-Score: -4.4 (----) X-Virus-Checked: Checked by ClamAV on sourceware.org X-IsSubscribed: yes Mailing-List: contact frysk-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Post: List-Help: , Sender: frysk-owner@sourceware.org X-SW-Source: 2007-q4/txt/msg00217.txt.bz2 --=-+XZbkIh4oqDlgZcu9H6A Content-Type: text/plain Content-Transfer-Encoding: 7bit Content-length: 1988 Hi, This patch reworks the libunwind interface so it just uses the given accessors to read the unwind table we are feeding it. All mucking in elf images is now done on the frysk side. (The patch also adds a name field to ElfImage, which is only used for generating a nice debugging string.) frysk-imports/libunwind/ChangeLog 2007-12-11 Mark Wielaard * include/libunwind-common.h.in (unw_get_unwind_table): Add unw_accessor_t, remove elf image arguments. * src/mi/Gget_unwind_table.c (unw_get_unwind_table): Likewise. Rewrite to use unw_accessor_t for reading all data. frysk-sys/lib/unwind/ChangeLog 2007-12-11 Mark Wielaard * ElfImage.java (name): New field. (ElfImage): Set name. (toString): Add name. * cni/ElfImage.cxx (mapElfImage): Pass in name. * cni/UnwindH.hxx (access_mem): Handle memory holes. (get_eh_frame_hdr_addr): New static function. (local_access_mem): Likewise. (createProcInfoFromElfImage): Use get_eh_frame_hdr_addr and local_access_mem. (createElfImageFromVDSO): Set name to [vdso]. The careful reader will notice that the interface is still a little eh_frame_hdr specific and that in theory for the eh_frame_hdr one remote address space accessor would suffice. This is right, since the eh_frame and eh_frame_hdr are guaranteed to live in the remote address space. But for debug_frame tables we need two different accessors since the debug_frame doesn't live in the inferior address space (it also doesn't have a nice binary search table). Also the last argument of the interface is still eh_frame_hdr specific because for the table has addresses relative to its own hdr start address. Both issues mean that we can actually get rid of the whole copying of the elfimage completely (when not dealing with debug_frames) when we just have an eh_frame_hdr. A future cleanup will do that. Cheers, Mark --=-+XZbkIh4oqDlgZcu9H6A Content-Disposition: inline; filename=unwind-table-interface.patch Content-Type: text/x-patch; name=unwind-table-interface.patch; charset=UTF-8 Content-Transfer-Encoding: 7bit Content-length: 17511 diff --git a/frysk-imports/libunwind/include/libunwind-common.h.in b/frysk-imports/libunwind/include/libunwind-common.h.in index 497a7b4..a837055 100644 --- a/frysk-imports/libunwind/include/libunwind-common.h.in +++ b/frysk-imports/libunwind/include/libunwind-common.h.in @@ -252,8 +252,10 @@ 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); + void *arg, + unw_accessors_t *eh_frame_accessors, + unw_word_t eh_frame_hdr_address, + void *eh_frame_arg, + unw_word_t peh_vaddr); extern unw_addr_space_t unw_local_addr_space; diff --git a/frysk-imports/libunwind/src/mi/Gget_unwind_table.c b/frysk-imports/libunwind/src/mi/Gget_unwind_table.c index 2acb947..1d905b6 100644 --- a/frysk-imports/libunwind/src/mi/Gget_unwind_table.c +++ b/frysk-imports/libunwind/src/mi/Gget_unwind_table.c @@ -22,177 +22,72 @@ 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_i.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) +unw_get_unwind_table(unw_addr_space_t as, unw_word_t ip, + unw_proc_info_t *pi, int need_unwind_info, void *arg, + unw_accessors_t *eh_frame_accessors, + unw_word_t eh_frame_hdr_address, + void *eh_frame_arg, + unw_word_t peh_vaddr) { - Debug(99, "Entering get_unwind_table\n"); - Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL; - unw_word_t eh_frame_start, fde_count, load_base; - char *addr; - 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 = 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 local address space memory accessor used by -// dwarf_read_encoded_pointer. The arg pointer is the base address, -// addr is the offset from the base address. - 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) - { - // Writing is not supported - return -UNW_EINVAL; - } - else - { - *val = *(unw_word_t *) (addr + (char *) arg); - Debug (16, "mem[%x] -> %x\n", (addr + (char *) arg), *val); - } - Debug(99, "leaving local_access_mem\n"); - return 0; - } - - unw_accessors_t local_accessors = {NULL, NULL, NULL, local_access_mem, - NULL, NULL, NULL, NULL, NULL}; - unw_addr_space_t local_addr_space - = unw_create_addr_space(&local_accessors, 0); - - unw_word_t start = 0; + int ret; + unw_word_t start = eh_frame_hdr_address; + + // Version + unsigned char version; + if ((ret = dwarf_readu8 (as, eh_frame_accessors, &start, + &version, eh_frame_arg)) < 0) + return -UNW_ENOINFO; + + if (version != DW_EH_VERSION) + return -UNW_ENOINFO; + + unsigned char eh_frame_ptr_enc; + if ((ret = dwarf_readu8 (as, eh_frame_accessors, &start, + &eh_frame_ptr_enc, eh_frame_arg)) < 0) + return -UNW_ENOINFO; + + unsigned char fde_count_enc; + if ((ret = dwarf_readu8 (as, eh_frame_accessors, &start, + &fde_count_enc, eh_frame_arg)) < 0) + return -UNW_ENOINFO; + + unsigned char table_enc; + if ((ret = dwarf_readu8 (as, eh_frame_accessors, &start, + &table_enc, eh_frame_arg)) < 0) + return -UNW_ENOINFO; - /* (Optionally) read eh_frame_ptr: */ - if ((ret = dwarf_read_encoded_pointer (local_addr_space, &local_accessors, - &start, hdr->eh_frame_ptr_enc, pi, - &eh_frame_start, addr)) < 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 (local_addr_space, &local_accessors, - &start, hdr->fde_count_enc, pi, - &fde_count, addr)) < 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; - } + if (table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) + return -UNW_ENOINFO; + + unw_word_t eh_frame_start; + if ((ret = dwarf_read_encoded_pointer (as, eh_frame_accessors, + &start, eh_frame_ptr_enc, pi, + &eh_frame_start, eh_frame_arg)) < 0) + return -UNW_ENOINFO; - addr += start; - - 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); + unw_word_t fde_count; + if ((ret = dwarf_read_encoded_pointer (as, eh_frame_accessors, + &start, fde_count_enc, pi, + &fde_count, eh_frame_arg)) < 0) + return -UNW_ENOINFO; + + unw_dyn_info_t di; + di.start_ip = pi->start_ip; + di.end_ip = pi->end_ip; + di.format = UNW_INFO_FORMAT_REMOTE_TABLE; + di.gp = pi->gp; + di.u.rti.name_ptr = 0; + /* two 32-bit values (ip_offset/fde_offset) per table-entry: + For the binary-search table in the eh_frame_hdr, data-relative + means relative to the start of that section... + So for now we pass in peh_vaddr and use the main as for access. */ + di.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t); + di.u.rti.table_data = peh_vaddr + 12; + di.u.rti.segbase = peh_vaddr; + + return tdep_search_unwind_table (as, ip, &di, pi, need_unwind_info, arg); } diff --git a/frysk-sys/lib/unwind/ElfImage.java b/frysk-sys/lib/unwind/ElfImage.java index 7707f09..e938c96 100644 --- a/frysk-sys/lib/unwind/ElfImage.java +++ b/frysk-sys/lib/unwind/ElfImage.java @@ -43,6 +43,8 @@ package lib.unwind; public class ElfImage { + final String name; + long elfImage; long size; @@ -53,8 +55,9 @@ public class ElfImage int ret = 0; - public ElfImage (long elfImage, long size, long segbase, long mapoff) + public ElfImage (String name, long elfImage, long size, long segbase, long mapoff) { + this.name = name; this.elfImage = elfImage; this.size = size; this.segbase = segbase; @@ -63,6 +66,7 @@ public class ElfImage public ElfImage (int ret) { + this.name = "ERROR: " + Integer.toString(ret); this.ret = ret; } @@ -71,7 +75,7 @@ public class ElfImage if (ret != 0) return "Bad Elf Image, ret: " + ret; - return "Elf Image: 0x" + Long.toHexString(elfImage) + " size: " + size + return "Elf Image (" + name + "): 0x" + Long.toHexString(elfImage) + " size: " + size + " segbase: 0x" + Long.toHexString(segbase) + " mapoff: 0x" + Long.toHexString(mapoff); } diff --git a/frysk-sys/lib/unwind/cni/ElfImage.cxx b/frysk-sys/lib/unwind/cni/ElfImage.cxx index d521643..3f7fdd3 100644 --- a/frysk-sys/lib/unwind/cni/ElfImage.cxx +++ b/frysk-sys/lib/unwind/cni/ElfImage.cxx @@ -80,7 +80,7 @@ lib::unwind::ElfImage::mapElfImage(jstring elfImageName, jlong segbase, jlong hi return new lib::unwind::ElfImage((jint) -1); lib::unwind::ElfImage* elfImage - = new lib::unwind::ElfImage((jlong) image, (jlong) size, + = new lib::unwind::ElfImage(elfImageName, (jlong) image, (jlong) size, (jlong) segbase, (jlong) mapoff); return elfImage; diff --git a/frysk-sys/lib/unwind/cni/UnwindH.hxx b/frysk-sys/lib/unwind/cni/UnwindH.hxx index 1d13156..e1cc430 100644 --- a/frysk-sys/lib/unwind/cni/UnwindH.hxx +++ b/frysk-sys/lib/unwind/cni/UnwindH.hxx @@ -47,7 +47,9 @@ #include #include LIBUNWIND_TARGET_H -#include + +#include +#include #include @@ -135,11 +137,22 @@ static int access_mem (::unw_addr_space_t as, ::unw_word_t addr, ::unw_word_t *valp, int write, void *arg) { - jbyteArray tmp = JvNewByteArray (sizeof (unw_word_t)); - memcpy (elements(tmp), valp, JvGetArrayLength(tmp)); - int ret = addressSpace(arg)->accessMem((jlong) addr, tmp, (jboolean) write); - memcpy(valp, elements(tmp), JvGetArrayLength(tmp)); - return ret; + try + { + jbyteArray tmp = JvNewByteArray (sizeof (unw_word_t)); + memcpy (elements(tmp), valp, JvGetArrayLength(tmp)); + int ret = addressSpace(arg)->accessMem((jlong) addr, + tmp, (jboolean) write); + memcpy(valp, elements(tmp), JvGetArrayLength(tmp)); + return ret; + } + catch (java::lang::RuntimeException *t) + { + // We have to catch all RuntimeExceptions here since there + // is no indicator for just "invalid memory location". + // Core files might have "holes" in their memory. + return -UNW_EINVAL; + } } /* @@ -429,26 +442,177 @@ lib::unwind::TARGET::getProcInfo(gnu::gcj::RawDataManaged* cursor) return myInfo; } +// Return NULL when eh_frame_hdr cannot be found. +// Also fills in ip->start_ip, ip->end_ip and ip->gp. +// peh_vaddr will point to the address of the eh_frame_hdr in the main +// address space of the inferior. +static void * +get_eh_frame_hdr_addr(unw_proc_info_t *pi, char *image, size_t size, + unsigned long segbase, unw_word_t *peh_vaddr) +{ + if (elf_version(EV_CURRENT) == EV_NONE) + return NULL; + + Elf *elf = elf_memory(image, size); + if (elf == NULL) + return NULL; + + GElf_Ehdr ehdr; + if (gelf_getehdr(elf, &ehdr) == NULL) + return NULL; + + GElf_Phdr phdr; + int ptxt_ndx = -1, peh_hdr_ndx = -1, pdyn_ndx = -1; + for (int i = 0; i < ehdr.e_phnum; i++) + { + if (gelf_getphdr (elf, i, &phdr) == NULL) + return NULL; + + switch (phdr.p_type) + { + case PT_LOAD: + if (phdr.p_vaddr == segbase) + ptxt_ndx = i; + break; + + case PT_GNU_EH_FRAME: + peh_hdr_ndx = i; + break; + + case PT_DYNAMIC: + pdyn_ndx = i; + break; + + default: + break; + } + } + + if (ptxt_ndx == -1 || peh_hdr_ndx == -1) + return NULL; + + GElf_Phdr ptxt, peh_hdr; + if (gelf_getphdr (elf, ptxt_ndx, &ptxt) == NULL) + return NULL; + + if (gelf_getphdr (elf, peh_hdr_ndx, &peh_hdr) == NULL) + return NULL; + + if (pdyn_ndx != -1) + { + /* 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". */ + GElf_Phdr pdyn; + if (gelf_getphdr (elf, pdyn_ndx, &pdyn) == NULL) + return NULL; + + Elf_Scn *pdyn_scn = gelf_offscn(elf, pdyn.p_offset); + if (pdyn_scn == NULL) + return NULL; + + Elf_Data *pdyn_data; + pdyn_data = elf_getdata (pdyn_scn, NULL); + if (pdyn_data == NULL) + return NULL; + + GElf_Shdr pdyn_shdr; + if (gelf_getshdr (pdyn_scn, &pdyn_shdr) == NULL) + return NULL; + + for (unsigned int i = 0; + i < pdyn_shdr.sh_size / pdyn_shdr.sh_entsize; i++) + { + GElf_Dyn dyn; + if (gelf_getdyn (pdyn_data, i, &dyn) == NULL) + return NULL; + + if (dyn.d_tag == DT_PLTGOT) + { + /* Assume that _DYNAMIC is writable and GLIBC has + relocated it (true for x86 at least). */ + pi->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. */ + pi->gp = 0; + + pi->start_ip = segbase; + pi->end_ip = segbase + ptxt.p_memsz; + + *peh_vaddr = peh_hdr.p_vaddr; + + char *hdr = image + peh_hdr.p_offset; + return hdr; +} + +// The following is a local address space memory accessor used by +// unw_get_unwind_table to access the eh_frame_hdr. The arg pointer +// is the base address, addr is the offset from the base address. +static int +local_access_mem (unw_addr_space_t as, unw_word_t addr, + unw_word_t *val, int write, void *arg) +{ + // Writing is not supported + if (write) + return -UNW_EINVAL; + else + *val = *(unw_word_t *) (addr + (char *) arg); + + return UNW_ESUCCESS; +} + +static unw_accessors_t local_accessors + = {NULL, NULL, NULL, local_access_mem, NULL, NULL, NULL, NULL}; + lib::unwind::ProcInfo* lib::unwind::TARGET::createProcInfoFromElfImage(lib::unwind::AddressSpace* addressSpace, jlong ip, jboolean needUnwindInfo, lib::unwind::ElfImage* elfImage) { - if (elfImage == NULL) - return new lib::unwind::ProcInfo(UNW_ENOINFO); + if (elfImage == NULL || elfImage->ret != 0) + return new lib::unwind::ProcInfo(-UNW_ENOINFO); unw_proc_info_t *procInfo = (::unw_proc_info_t *) JvAllocBytes(sizeof (::unw_proc_info_t)); + unw_addr_space_t as = (unw_addr_space_t) addressSpace->addressSpace; + logFine(this, logger, "Pre unw_get_unwind_table"); - int ret = unw_get_unwind_table((unw_addr_space_t) addressSpace->addressSpace, - (unw_word_t) ip, procInfo, + + unw_word_t peh_vaddr = 0; + void *eh_table_hdr = get_eh_frame_hdr_addr(procInfo, + (char *) elfImage->elfImage, + elfImage->size, + elfImage->segbase, + &peh_vaddr); + + //jsize length = JvGetStringUTFLength (elfImage->name); + //char buffer[length + 1]; + //JvGetStringUTFRegion (elfImage->name, 0, elfImage->name->length(), buffer); + //buffer[length] = '\0'; + //fprintf(stderr, "%s: %p\n", buffer, eh_table_hdr); + + if (eh_table_hdr == NULL) + return new lib::unwind::ProcInfo(-UNW_ENOINFO); + + int ret = unw_get_unwind_table(as, + (unw_word_t) ip, + procInfo, (int) needUnwindInfo, - (void *) elfImage->elfImage, - elfImage->size, elfImage->segbase, - elfImage->mapoff, (void *) addressSpace); - + (void *) addressSpace, + &local_accessors, + 0, + eh_table_hdr, + peh_vaddr); + + logFine(this, logger, "Post unw_get_unwind_table"); lib::unwind::ProcInfo *myInfo; if (ret < 0) @@ -541,7 +705,7 @@ lib::unwind::TARGET::createElfImageFromVDSO(lib::unwind::AddressSpace* addressSp mapoff = 0; lib::unwind::ElfImage* elfImage - = new lib::unwind::ElfImage((jlong) image, (jlong) size, + = new lib::unwind::ElfImage(JvNewStringLatin1("[vdso]"), (jlong) image, (jlong) size, (jlong) segbase, (jlong) mapoff); jLogFine(this, logger, "elfImage returned: {1}", elfImage); --=-+XZbkIh4oqDlgZcu9H6A--