From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 23195 invoked by alias); 17 Jan 2008 11:12:39 -0000 Received: (qmail 23183 invoked by uid 22791); 17 Jan 2008 11:12:37 -0000 X-Spam-Status: No, hits=-0.3 required=5.0 tests=AWL,BAYES_20,J_CHICKENPOX_22,J_CHICKENPOX_26,J_CHICKENPOX_74 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; Thu, 17 Jan 2008 11:12:21 +0000 Received: from dijkstra.wildebeest.org ([192.168.1.29]) by gnu.wildebeest.org with esmtp (Exim 4.63) (envelope-from ) id 1JFSfQ-0005mm-K4 for frysk@sourceware.org; Thu, 17 Jan 2008 12:12:18 +0100 Subject: [patch] Add debug_frame parsing From: Mark Wielaard To: frysk@sourceware.org Content-Type: multipart/mixed; boundary="=-LJSVM6A2UnYrFAIcY1kK" Date: Thu, 17 Jan 2008 11:12:00 -0000 Message-Id: <1200568336.2470.18.camel@dijkstra.wildebeest.org> Mime-Version: 1.0 X-Mailer: Evolution 2.12.2 (2.12.2-3.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: 2008-q1/txt/msg00032.txt.bz2 --=-LJSVM6A2UnYrFAIcY1kK Content-Type: text/plain Content-Transfer-Encoding: 7bit Content-length: 2020 Hi, This patch widens out libunwind interface to accept either eh_frame or debug_frame tables and adds support for parsing debug_frame data. It currently does a linear search through the data to find the right fde and corresponding cie by making the right adjustments. Although it does find the right fde covering the ip ranges, it doesn't always run the cfi program correctly. So for now the default is still the default and debug_frame is only used as fallback when eh_frame data cannot be found (that can be changed in a simple if statement, see the FIXME in UnwindH - that will however give a couple of test failures). It also fixes a couple of libunwind issues found when working on this (direct usage of local memory space, error values not being negative and unw_word being 64bit != dwarf 64 format is always in use). frysk-imports/libunwind/ChangeLog 2008-01-17 Mark Wielaard * include/dwarf.h (dwarf_extract_proc_info_from_fde): Pass table_start. * src/dwarf/Gfde.c (is_cie_id): Removed. (parse_cie): Accept debug and eh cie ids. (dwarf_extract_proc_info_from_fde): Accept table_start. Calculate correct cie_addr. Fix error reporting. * src/dwarf/Gfind_proc_info-lsb.c (linear_search): Don't depend on local address space. Pass table start for fde parsing. (dwarf_search_unwind_table): Handle debug_frame by linear search. * src/mi/Gget_unwind_table.c (get_frame_table, get_debug_table): new functions. (unw_get_unwind_table): Call either get_frame_table or get_debug_table depending on format. frysk-sys/lib/unwind/ChangeLog 2008-01-17 Mark Wielaard * cni/UnwindH.hxx (get_eh_frame_hdr_addr): Find and return debug_frame address and set pi->format. (createProcInfoFromElfImage): Handle either debug_frame or eh_frame addresses. Tested on x86_64 and x86 (fedora and centos, which both have full eh_frame coverage, so admittedly most of the new code paths aren't hit yet). Cheers, Mark --=-LJSVM6A2UnYrFAIcY1kK Content-Disposition: inline; filename=debug_frame.patch Content-Type: text/x-patch; name=debug_frame.patch; charset=UTF-8 Content-Transfer-Encoding: 7bit Content-length: 14394 diff --git a/frysk-imports/libunwind/include/dwarf.h b/frysk-imports/libunwind/include/dwarf.h index 47896f6..cf8a74a 100644 --- a/frysk-imports/libunwind/include/dwarf.h +++ b/frysk-imports/libunwind/include/dwarf.h @@ -362,6 +362,7 @@ extern int dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t *addr, int *is_register); extern int dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, + unw_word_t table_start, unw_word_t *fde_addr, unw_proc_info_t *pi, int need_unwind_info, diff --git a/frysk-imports/libunwind/src/dwarf/Gfde.c b/frysk-imports/libunwind/src/dwarf/Gfde.c index 11a6433..de2b193 100644 --- a/frysk-imports/libunwind/src/dwarf/Gfde.c +++ b/frysk-imports/libunwind/src/dwarf/Gfde.c @@ -1,6 +1,8 @@ /* libunwind - a platform-independent unwind library Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P. Contributed by David Mosberger-Tang + Copyright (c) 2008 Red Hat, Inc. + Contributed by Mark Wielaard This file is part of libunwind. @@ -25,15 +27,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "dwarf_i.h" -static inline int -is_cie_id (unw_word_t val) -{ - /* DWARF spec says CIE_id is 0xffffffff (for 32-bit ELF) or - 0xffffffffffffffff (for 64-bit ELF). However, the GNU toolchain - uses 0. */ - return (val == 0 || val == - (unw_word_t) 1); -} - /* Note: we don't need to keep track of more than the first four characters of the augmentation string, because we (a) ignore any augmentation string contents once we find an unrecognized character @@ -80,7 +73,7 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, if ((ret = dwarf_readu32 (as, a, &addr, &cie_id, arg)) < 0) return ret; /* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */ - if (cie_id != 0) + if (cie_id != 0 && cie_id != 0xffffffff) { Debug (1, "Unexpected CIE id %x\n", cie_id); return -UNW_EINVAL; @@ -99,7 +92,7 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, return ret; /* DWARF says CIE id should be 0xffffffffffffffff, but in .eh_frame, it's 0 */ - if (cie_id != 0) + if (cie_id != 0 && cie_id != 0xffffffffffffffff) { Debug (1, "Unexpected CIE id %llx\n", (long long) cie_id); return -UNW_EINVAL; @@ -217,7 +210,8 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr, HIDDEN int dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, - unw_word_t *addrp, unw_proc_info_t *pi, + unw_word_t table_start, unw_word_t *addrp, + unw_proc_info_t *pi, int need_unwind_info, void *arg) { @@ -252,7 +246,12 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, if ((ret = dwarf_reads32 (as, a, &addr, &cie_offset, arg)) < 0) return ret; - if (is_cie_id (cie_offset)) + /* DWARF spec says CIE_id is 0xffffffff (for 32-bit ELF) or + 0xffffffffffffffff (for 64-bit ELF). However, the GNU toolchain + uses 0. */ + if ((pi->format != UNW_INFO_FORMAT_TABLE && cie_offset == 0) + || (pi->format == UNW_INFO_FORMAT_TABLE + && cie_offset == 0xffffffff)) /* ignore CIEs (happens during linear searches) */ return 0; @@ -260,7 +259,10 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, .debug_frame-relative offset, but the GCC-generated .eh_frame sections instead store a "pcrelative" offset, which is just as fine as it's self-contained. */ - cie_addr = cie_offset_addr - cie_offset; + if (pi->format == UNW_INFO_FORMAT_TABLE) + cie_addr = table_start + cie_offset; + else + cie_addr = cie_offset_addr - cie_offset; } else { @@ -277,7 +279,12 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, if ((ret = dwarf_reads64 (as, a, &addr, &cie_offset, arg)) < 0) return ret; - if (is_cie_id (cie_offset)) + /* DWARF spec says CIE_id is 0xffffffff (for 32-bit ELF) or + 0xffffffffffffffff (for 64-bit ELF). However, the GNU toolchain + uses 0. */ + if ((pi->format != UNW_INFO_FORMAT_TABLE && cie_offset == 0) + || (pi->format == UNW_INFO_FORMAT_TABLE + && cie_offset == 0xffffffffffffffff)) /* ignore CIEs (happens during linear searches) */ return 0; @@ -285,7 +292,10 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, .debug_frame-relative offset, but the GCC-generated .eh_frame sections instead store a "pcrelative" offset, which is just as fine as it's self-contained. */ - cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset); + if (pi->format == UNW_INFO_FORMAT_TABLE) + cie_addr = (unw_word_t) ((uint64_t) table_start + cie_offset); + else + cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset); } if ((ret = parse_cie (as, a, cie_addr, pi, &dci, arg)) < 0) @@ -320,11 +330,12 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a, if (need_unwind_info) { - pi->format = UNW_INFO_FORMAT_TABLE; + // FRYSK LOCAL - we already set this. + // pi->format = UNW_INFO_FORMAT_TABLE; pi->unwind_info_size = sizeof (dci); pi->unwind_info = mempool_alloc (&dwarf_cie_info_pool); if (!pi->unwind_info) - return UNW_ENOMEM; + return -UNW_ENOMEM; if (dci.have_abi_marker) { diff --git a/frysk-imports/libunwind/src/dwarf/Gfind_proc_info-lsb.c b/frysk-imports/libunwind/src/dwarf/Gfind_proc_info-lsb.c index a8a3b69..be22e3e 100644 --- a/frysk-imports/libunwind/src/dwarf/Gfind_proc_info-lsb.c +++ b/frysk-imports/libunwind/src/dwarf/Gfind_proc_info-lsb.c @@ -1,6 +1,8 @@ /* libunwind - a platform-independent unwind library Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P. Contributed by David Mosberger-Tang + Copyright (c) 2008, Red Hat, Inc. + Contributed by Mark Wielaard This file is part of libunwind. @@ -52,20 +54,23 @@ struct callback_data unw_dyn_info_t di; /* table info (if single_fde is false) */ }; +#endif /* !UNW_REMOTE_ONLY */ + static int linear_search (unw_addr_space_t as, unw_word_t ip, unw_word_t eh_frame_start, unw_word_t eh_frame_end, unw_word_t fde_count, unw_proc_info_t *pi, int need_unwind_info, void *arg) { - unw_accessors_t *a = unw_get_accessors (unw_local_addr_space); + unw_accessors_t *a = unw_get_accessors (as); unw_word_t i = 0, fde_addr, addr = eh_frame_start; int ret; while (i++ < fde_count && addr < eh_frame_end) { fde_addr = addr; - if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, 0, arg)) + if ((ret = dwarf_extract_proc_info_from_fde (as, a, eh_frame_start, + &addr, pi, 0, arg)) < 0) return ret; @@ -74,7 +79,8 @@ linear_search (unw_addr_space_t as, unw_word_t ip, if (!need_unwind_info) return 1; addr = fde_addr; - if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, + if ((ret = dwarf_extract_proc_info_from_fde (as, a, eh_frame_start, + &addr, pi, need_unwind_info, arg)) < 0) return ret; @@ -84,6 +90,8 @@ linear_search (unw_addr_space_t as, unw_word_t ip, return -UNW_ENOINFO; } +#ifndef UNW_REMOTE_ONLY + /* Info is a pointer to a unw_dyn_info_t structure and, on entry, member u.rti.segbase contains the instruction-pointer we're looking for. */ @@ -361,6 +369,15 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, #endif int ret; + if (di->format == UNW_INFO_FORMAT_TABLE) + { + ret = linear_search (as, ip, di->u.rti.table_data, + di->u.rti.table_data + + di->u.rti.table_len * sizeof (unw_word_t), + ~0UL, pi, need_unwind_info, arg); + return ret; + } + assert (di->format == UNW_INFO_FORMAT_REMOTE_TABLE && (ip >= di->start_ip && ip < di->end_ip)); @@ -397,7 +414,7 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, Debug (15, "ip=0x%lx, start_ip=0x%lx\n", (long) ip, (long) (e->start_ip_offset + segbase)); fde_addr = e->fde_offset + segbase; - if ((ret = dwarf_extract_proc_info_from_fde (as, a, &fde_addr, pi, + if ((ret = dwarf_extract_proc_info_from_fde (as, a, segbase, &fde_addr, pi, need_unwind_info, arg)) < 0) return ret; diff --git a/frysk-imports/libunwind/src/mi/Gget_unwind_table.c b/frysk-imports/libunwind/src/mi/Gget_unwind_table.c index fc46269..0223013 100644 --- a/frysk-imports/libunwind/src/mi/Gget_unwind_table.c +++ b/frysk-imports/libunwind/src/mi/Gget_unwind_table.c @@ -1,4 +1,4 @@ -// Copyright 2007, Red Hat Inc. +// Copyright 2007, 2008, Red Hat Inc. /* This file is part of libunwind. @@ -25,11 +25,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "dwarf_i.h" #include "dwarf-eh.h" -int -unw_get_unwind_table(unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info, - unw_accessors_t *eh_frame_accessors, - unw_word_t eh_frame_hdr_address, - void *eh_frame_arg) +#include + +static int +get_frame_table(unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info, + unw_accessors_t *eh_frame_accessors, + unw_word_t eh_frame_hdr_address, + void *eh_frame_arg) { int ret; unw_addr_space_t as = unw_create_addr_space (eh_frame_accessors, 0); @@ -87,7 +89,51 @@ unw_get_unwind_table(unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info, di.u.rti.table_data = eh_frame_hdr_address + 12; di.u.rti.segbase = eh_frame_hdr_address; + pi->start_ip = 0; + pi->end_ip = 0; ret = tdep_search_unwind_table (as, ip, &di, pi, need_unwind_info, eh_frame_arg); return ret; } + +static int +get_debug_table(unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info, + unw_accessors_t *accessors, + unw_word_t address, + void *arg) +{ + unw_addr_space_t as = unw_create_addr_space (accessors, 0); + + unw_dyn_info_t di; + di.start_ip = pi->start_ip; + di.end_ip = pi->end_ip; + di.format = UNW_INFO_FORMAT_TABLE; + di.gp = pi->gp; + + // XXX Should we use the ti struct of the union? + di.u.rti.name_ptr = 0; + di.u.rti.segbase = address; + di.u.rti.table_data = address; + di.u.rti.table_len = pi->unwind_info_size; + + pi->start_ip = 0; + pi->end_ip = 0; + return tdep_search_unwind_table (as, ip, &di, pi, need_unwind_info, arg); +} + +int +unw_get_unwind_table(unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info, + unw_accessors_t *accessors, + unw_word_t address, + void *arg) +{ + if (pi->format == UNW_INFO_FORMAT_TABLE) + return get_debug_table(ip, pi, need_unwind_info, accessors, + address, arg); + + if (pi->format == UNW_INFO_FORMAT_REMOTE_TABLE) + return get_frame_table(ip, pi, need_unwind_info, accessors, + address, arg); + + return -UNW_EINVAL; +} diff --git a/frysk-sys/lib/unwind/cni/UnwindH.hxx b/frysk-sys/lib/unwind/cni/UnwindH.hxx index e69e031..08c8ce1 100644 --- a/frysk-sys/lib/unwind/cni/UnwindH.hxx +++ b/frysk-sys/lib/unwind/cni/UnwindH.hxx @@ -1,6 +1,6 @@ // This file is part of the program FRYSK. // -// Copyright 2007, Red Hat Inc. +// Copyright 2007, 2008, Red Hat Inc. // // FRYSK is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by @@ -488,7 +488,26 @@ get_eh_frame_hdr_addr(unw_proc_info_t *pi, char *image, size_t size, } } - if (ptxt_ndx == -1 || peh_hdr_ndx == -1) + Elf_Data *debug_frame_data = NULL; + size_t shstrndx; + if (elf_getshstrndx (elf, &shstrndx) >= 0) + { + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL + && debug_frame_data == NULL) + { + GElf_Shdr shdr; + if (gelf_getshdr (scn, &shdr) != NULL + && shdr.sh_type == SHT_PROGBITS) + { + const char *name = elf_strptr (elf, shstrndx, shdr.sh_name); + if (strcmp (name, ".debug_frame") == 0) + debug_frame_data = elf_getdata (scn, NULL); + } + } + } + + if (ptxt_ndx == -1 || (peh_hdr_ndx == -1 && debug_frame_data == NULL)) return NULL; GElf_Phdr ptxt, peh_hdr; @@ -547,7 +566,23 @@ get_eh_frame_hdr_addr(unw_proc_info_t *pi, char *image, size_t size, *peh_vaddr = peh_hdr.p_vaddr; - char *hdr = image + peh_hdr.p_offset; + char *hdr; + // FIXME. Currently we prefer eh_frame, but we should switch to + // prefer debug_frame when all bugs have been squashed out of that + // in libunwind. + if (peh_hdr_ndx == -1 + && debug_frame_data != NULL && debug_frame_data->d_buf != NULL + && debug_frame_data->d_size != 0) + { + pi->format = UNW_INFO_FORMAT_TABLE; + pi->unwind_info_size = debug_frame_data->d_size / sizeof (unw_word_t); + hdr = (char *) debug_frame_data->d_buf; + } + else + { + pi->format = UNW_INFO_FORMAT_REMOTE_TABLE; + hdr = image + peh_hdr.p_offset; + } return hdr; } @@ -600,14 +635,26 @@ lib::unwind::TARGET::createProcInfoFromElfImage(lib::unwind::AddressSpace* addre if (eh_table_hdr == NULL) return new lib::unwind::ProcInfo(-UNW_ENOINFO); - int ret = unw_get_unwind_table((unw_word_t) ip, - procInfo, - (int) needUnwindInfo, - &local_accessors, - // virtual address - peh_vaddr, - // address adjustment - eh_table_hdr - peh_vaddr); + int ret; + if (procInfo->format == UNW_INFO_FORMAT_REMOTE_TABLE) + ret = unw_get_unwind_table((unw_word_t) ip, + procInfo, + (int) needUnwindInfo, + &local_accessors, + // virtual address + peh_vaddr, + // address adjustment + eh_table_hdr - peh_vaddr); + else + ret = unw_get_unwind_table((unw_word_t) ip, + procInfo, + (int) needUnwindInfo, + &local_accessors, + // virtual address + 0, + // address adjustment + eh_table_hdr); + logFine(this, logger, "Post unw_get_unwind_table"); lib::unwind::ProcInfo *myInfo; --=-LJSVM6A2UnYrFAIcY1kK--