From: Roland McGrath Date: Wed, 10 Dec 2008 22:41:45 +0000 (-0800) Subject: Unwind branch migrated from Monotone. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bb92741a0227fd0c882271c6959fe82450a737f8;p=thirdparty%2Felfutils.git Unwind branch migrated from Monotone. --- diff --git a/backends/ChangeLog b/backends/ChangeLog index 85609b155..6ec64df0d 100644 --- a/backends/ChangeLog +++ b/backends/ChangeLog @@ -1,3 +1,13 @@ +2007-01-18 Roland McGrath + + * x86_64_cfi.c (x86_64_abi_cfi): New file. + * Makefile.am (x86_64_SRCS): Add it. + * x86_64_init.c (x86_64_init): Add initializer. + + * i386_cfi.c (i386_abi_cfi): New file. + * Makefile.am (i386_SRCS): Add it. + * i386_init.c (i386_init): Initialize abi_cfi hook. + 2008-10-04 Ulrich Drepper * i386_reloc.def: Fix entries for TLS_GOTDESC, TLS_DESC_CALL, and diff --git a/backends/Makefile.am b/backends/Makefile.am index 5b552946c..a47519f19 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -60,7 +60,7 @@ endif textrel_check = if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi -i386_SRCS = i386_init.c i386_symbol.c i386_corenote.c \ +i386_SRCS = i386_init.c i386_symbol.c i386_corenote.c i386_cfi.c \ i386_retval.c i386_regs.c i386_auxv.c i386_syscall.c cpu_i386 = ../libcpu/libcpu_i386.a libebl_i386_pic_a_SOURCES = $(i386_SRCS) @@ -70,7 +70,7 @@ sh_SRCS = sh_init.c sh_symbol.c libebl_sh_pic_a_SOURCES = $(sh_SRCS) am_libebl_sh_pic_a_OBJECTS = $(sh_SRCS:.c=.os) -x86_64_SRCS = x86_64_init.c x86_64_symbol.c x86_64_corenote.c \ +x86_64_SRCS = x86_64_init.c x86_64_symbol.c x86_64_corenote.c x86_64_cfi.c \ x86_64_retval.c x86_64_regs.c i386_auxv.c x86_64_syscall.c cpu_x86_64 = ../libcpu/libcpu_x86_64.a libebl_x86_64_pic_a_SOURCES = $(x86_64_SRCS) diff --git a/backends/i386_cfi.c b/backends/i386_cfi.c new file mode 100644 index 000000000..304946944 --- /dev/null +++ b/backends/i386_cfi.c @@ -0,0 +1,65 @@ +/* i386 ABI-specified defaults for DWARF CFI. + Copyright (C) 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#define BACKEND i386_ +#include "libebl_CPU.h" + +int +i386_abi_cfi (Ebl *ebl __attribute__ ((unused)), Dwarf_CIE *abi_info) +{ + static const uint8_t abi_cfi[] = + { + /* Call-saved regs. */ + DW_CFA_same_value, ULEB128_7 (3), /* %ebx */ + DW_CFA_same_value, ULEB128_7 (5), /* %ebp */ + DW_CFA_same_value, ULEB128_7 (6), /* %esi */ + DW_CFA_same_value, ULEB128_7 (7), /* %edi */ + + /* The CFA is the SP. */ + DW_CFA_val_offset, ULEB128_7 (4), ULEB128_7 (0), + + /* Segment registers are call-saved if ever used at all. */ + DW_CFA_same_value, ULEB128_7 (40), /* %es */ + DW_CFA_same_value, ULEB128_7 (41), /* %cs */ + DW_CFA_same_value, ULEB128_7 (42), /* %ss */ + DW_CFA_same_value, ULEB128_7 (43), /* %ds */ + DW_CFA_same_value, ULEB128_7 (44), /* %fs */ + DW_CFA_same_value, ULEB128_7 (45), /* %gs */ + }; + + abi_info->initial_instructions = abi_cfi; + abi_info->initial_instructions_end = &abi_cfi[sizeof abi_cfi]; + abi_info->data_alignment_factor = 4; + + abi_info->return_address_register = 8; /* %eip */ + + return 0; +} diff --git a/backends/i386_init.c b/backends/i386_init.c index f046dfb69..b468ae5a8 100644 --- a/backends/i386_init.c +++ b/backends/i386_init.c @@ -59,6 +59,7 @@ i386_init (elf, machine, eh, ehlen) HOOK (eh, syscall_abi); HOOK (eh, auxv_info); HOOK (eh, disasm); + HOOK (eh, abi_cfi); return MODVERSION; } diff --git a/backends/x86_64_cfi.c b/backends/x86_64_cfi.c new file mode 100644 index 000000000..3057700ad --- /dev/null +++ b/backends/x86_64_cfi.c @@ -0,0 +1,60 @@ +/* x86-64 ABI-specified defaults for DWARF CFI. + Copyright (C) 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#define BACKEND x86_64_ +#include "libebl_CPU.h" + +int +x86_64_abi_cfi (Ebl *ebl __attribute__ ((unused)), Dwarf_CIE *abi_info) +{ + static const uint8_t abi_cfi[] = + { + /* Call-saved regs. */ + DW_CFA_same_value, ULEB128_7 (0), /* %rbx */ + DW_CFA_same_value, ULEB128_7 (6), /* %rbp */ + DW_CFA_same_value, ULEB128_7 (12), /* %r12 */ + DW_CFA_same_value, ULEB128_7 (13), /* %r13 */ + DW_CFA_same_value, ULEB128_7 (14), /* %r14 */ + DW_CFA_same_value, ULEB128_7 (15), /* %r15 */ + DW_CFA_same_value, ULEB128_7 (16), /* %r16 */ + + /* The CFA is the SP. */ + DW_CFA_val_offset, ULEB128_7 (7), ULEB128_7 (0), + }; + + abi_info->initial_instructions = abi_cfi; + abi_info->initial_instructions_end = &abi_cfi[sizeof abi_cfi]; + abi_info->data_alignment_factor = 8; + + abi_info->return_address_register = 8; /* %eip */ + + return 0; +} diff --git a/backends/x86_64_init.c b/backends/x86_64_init.c index a2eaffa5f..83b9d9dca 100644 --- a/backends/x86_64_init.c +++ b/backends/x86_64_init.c @@ -56,6 +56,7 @@ x86_64_init (elf, machine, eh, ehlen) HOOK (eh, syscall_abi); HOOK (eh, auxv_info); HOOK (eh, disasm); + HOOK (eh, abi_cfi); return MODVERSION; } diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 235fac013..8a0ea1591 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -160,6 +160,65 @@ * libdw.map (ELFUTILS_0.127): New version set, inherits from ELFUTILS_0.126. Add dwfl_module_addrsym. +2007-02-10 Roland McGrath + + * libdw.map (ELFUTILS_0.127): Add dwfl_addrframe, dwfl_module_getcfi. + + * dwarf.h: Define DW_EH_PE_* constants. + + * unwindP.h (struct Dwarf_CFI_s): Add `ebl' member. + * dwarf_cfi_setebl.c: New file. + * Makefile.am (libdw_a_SOURCES): Add it. + * unwind.h: Declare dwarf_cfi_setebl. + * unwindP.h: Add INTDECL. + + * libdwP.h (struct Dwarf): Add member `cfi'. + * dwarf_end.c (dwarf_end): Call __libdw_destroy_frame_cache on it. + * dwarf_getcfi.c: New file. + * dwarf_getcfi_elf.c: New file. + * dwarf_cfi_end.c: New file. + * dwarf_cfi_addrframe.c: New file. + * dwarf_frame_cfa.c: New file. + * dwarf_frame_register.c: New file. + * dwarf_frame_return_address_register.c: New file. + * Makefile.am (libdw_a_SOURCES): Add them. + * unwind.h: Declare those functions. + * libdw.map (ELFUTILS_0.127): Export them. + + * dwarf_getlocation.c (__libdw_intern_expression): New function, + broken out of ... + (getlocation): ... here, call it. + * libdwP.h: Declare it. + + * cie.c: New file. + * fde.c: New file. + * frame-cache.c: New file. + * unwind.c: New file. + * unwindP.h: New file. + * encoded-value.h: New file. + * Makefile.am (libdw_a_SOURCES, noinst_HEADERS): Add them. + * libdwP.h: Add DWARF_E_INVALID_CFI to errors enum. + * dwarf_error.c (errmsgs): Add element for it. + + * dwarf.h (DW_CFA_low_user, DW_CFA_high_user): Renamed to + DW_CFA_lo_user, DW_CFA_hi_user, to match DWARF 3 spec. + + * unwind.h: New file. + * dwarf_next_cfi.c: New file. + * Makefile.am (euinclude_HEADERS, libdw_a_SOURCES): Add them. + * libdw.map (ELFUTILS_0.127): New set inheriting from ELFUTILS_0.126. + Add dwarf_next_cfi. + + * memory-access.h [! ALLOW_UNALIGNED] + (read_2ubyte_unaligned): Renamed to ... + (read_2ubyte_unaligned_1): ... this. Take bool rather than Dwarf *. + (read_2ubyte_unaligned): Define as macro passing dbg->other_byte_order. + (read_2sbyte_unaligned): Likewise. + (read_4ubyte_unaligned): Likewise. + (read_4sbyte_unaligned): Likewise. + (read_8ubyte_unaligned): Likewise. + (read_8sbyte_unaligned): Likewise. + 2007-02-10 Roland McGrath * dwarf.h (DW_OP_fbreg): Comment fix. diff --git a/libdw/Makefile.am b/libdw/Makefile.am index 69ce526c6..51b2cd4c3 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -47,7 +47,7 @@ noinst_PROGRAMS = $(noinst_LIBRARIES:_pic.a=.so) endif include_HEADERS = dwarf.h -pkginclude_HEADERS = libdw.h +pkginclude_HEADERS = libdw.h unwind.h libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_getpubnames.c dwarf_getabbrev.c dwarf_tag.c \ @@ -83,7 +83,14 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_func_inline.c dwarf_getsrc_file.c \ libdw_findcu.c libdw_form.c libdw_alloc.c memory-access.c \ libdw_visit_scopes.c \ - dwarf_entry_breakpoints.c + dwarf_entry_breakpoints.c \ + dwarf_next_cfi.c \ + cie.c fde.c unwind.c frame-cache.c \ + dwarf_frame_cfa.c dwarf_frame_register.c \ + dwarf_frame_return_address_register.c \ + dwarf_cfi_addrframe.c \ + dwarf_getcfi.c dwarf_getcfi_elf.c \ + dwarf_cfi_setebl.c dwarf_cfi_end.c if !MUDFLAP libdw_pic_a_SOURCES = @@ -126,7 +133,8 @@ endif libdw_a_LIBADD = $(addprefix ../libdwfl/,$(shell $(AR) t ../libdwfl/libdwfl.a)) -noinst_HEADERS = libdwP.h memory-access.h dwarf_abbrev_hash.h +noinst_HEADERS = libdwP.h memory-access.h dwarf_abbrev_hash.h \ + unwindP.h encoded-value.h EXTRA_DIST = libdw.map diff --git a/libdw/cie.c b/libdw/cie.c new file mode 100644 index 000000000..6f86037a6 --- /dev/null +++ b/libdw/cie.c @@ -0,0 +1,193 @@ +/* CIE reading. + Copyright (C) 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "unwindP.h" +#include "encoded-value.h" +#include +#include + + +static int +compare_cie (const void *a, const void *b) +{ + const struct dwarf_cie *cie1 = a; + const struct dwarf_cie *cie2 = b; + if (cie1->offset < cie2->offset) + return -1; + if (cie1->offset > cie2->offset) + return 1; + return 0; +} + +/* There is no CIE at OFFSET in the tree. Add it. */ +static struct dwarf_cie * +intern_new_cie (Dwarf_CFI *cache, Dwarf_Off offset, const Dwarf_CIE *info) +{ + struct dwarf_cie *cie = malloc (sizeof (struct dwarf_cie)); + if (cie == NULL) + { + __libdw_seterrno (DWARF_E_NOMEM); + return NULL; + } + + cie->offset = offset; + cie->code_alignment_factor = info->code_alignment_factor; + cie->data_alignment_factor = info->data_alignment_factor; + cie->return_address_register = info->return_address_register; + + cie->fde_augmentation_data_size = 0; + cie->sized_augmentation_data = false; + cie->signal_frame = false; + + cie->fde_encoding = DW_EH_PE_absptr; + cie->lsda_encoding = DW_EH_PE_omit; + + /* Grok the augmentation string and its data. */ + const uint8_t *data = info->augmentation_data; + for (const char *ap = info->augmentation; *ap != '\0'; ++ap) + { + uint8_t encoding; + switch (*ap) + { + case 'z': + cie->sized_augmentation_data = true; + continue; + + case 'S': + cie->signal_frame = true; + continue; + + case 'L': /* LSDA pointer encoding byte. */ + cie->lsda_encoding = *data++; + if (!cie->sized_augmentation_data) + cie->fde_augmentation_data_size + += encoded_value_size (&cache->data, cache->e_ident, + cie->lsda_encoding, NULL); + continue; + + case 'R': /* FDE address encoding byte. */ + cie->fde_encoding = *data++; + continue; + + case 'P': /* Skip personality routine. */ + encoding = *data++; + data += encoded_value_size (&cache->data, cache->e_ident, + encoding, data); + continue; + + default: + /* Unknown augmentation string. If we have 'z' we can ignore it, + otherwise we must bail out. */ + if (cie->sized_augmentation_data) + continue; + } + /* We only get here when we need to bail out. */ + break; + } + + /* Save the initial instructions to be played out into initial state. */ + cie->initial_instructions = info->initial_instructions; + cie->initial_instructions_end = info->initial_instructions_end; + cie->initial_state = NULL; + + /* Add the new entry to the search tree. */ + if (tsearch (cie, &cache->cie_tree, &compare_cie) == NULL) + { + free (cie); + __libdw_seterrno (DWARF_E_NOMEM); + return NULL; + } + + return cie; +} + +/* Look up a CIE_pointer for random access. */ +struct dwarf_cie * +internal_function +__libdw_find_cie (Dwarf_CFI *cache, Dwarf_Off offset) +{ + const struct dwarf_cie cie_key = { .offset = offset }; + struct dwarf_cie **found = tfind (&cie_key, &cache->cie_tree, &compare_cie); + if (found != NULL) + return *found; + + /* We have not read this CIE yet. Go find it. */ + Dwarf_Off next_offset = offset; + Dwarf_CFI_Entry entry; + int result = INTUSE(dwarf_next_cfi) (cache->e_ident, + &cache->data, cache->eh_frame, + offset, &next_offset, &entry); + if (result != 0 || entry.cie.CIE_id != CIE_ID) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return NULL; + } + + /* If this happened to be what we would have read next, notice it. */ + if (cache->next_offset == offset) + cache->next_offset = next_offset; + + return intern_new_cie (cache, offset, &entry.cie); +} + +/* Enter a CIE encountered while reading through for FDEs. */ +void +internal_function +__libdw_intern_cie (Dwarf_CFI *cache, Dwarf_Off offset, const Dwarf_CIE *info) +{ + const struct dwarf_cie cie_key = { .offset = offset }; + struct dwarf_cie **found = tfind (&cie_key, &cache->cie_tree, &compare_cie); + if (found == NULL) + /* We have not read this CIE yet. Enter it. */ + (void) intern_new_cie (cache, offset, info); +} diff --git a/libdw/dwarf.h b/libdw/dwarf.h index f1261c361..adbb992be 100644 --- a/libdw/dwarf.h +++ b/libdw/dwarf.h @@ -661,13 +661,39 @@ enum DW_CFA_val_offset_sf = 0x15, DW_CFA_val_expression = 0x16, - DW_CFA_low_user = 0x1c, + DW_CFA_lo_user = 0x1c, DW_CFA_MIPS_advance_loc8 = 0x1d, DW_CFA_GNU_window_save = 0x2d, DW_CFA_GNU_args_size = 0x2e, - DW_CFA_high_user = 0x3f + DW_CFA_hi_user = 0x3f }; +/* GNU DWARF exception-handling pointer encodings (not part of DWARF 3). + These appear in call-frame information that uses 'R' augmentation, and + in .eh_frame_hdr (PT_GNU_EH_FRAME) header format. */ +enum + { + DW_EH_PE_absptr = 0x00, + DW_EH_PE_omit = 0xff, + + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0a, + DW_EH_PE_sdata4 = 0x0b, + DW_EH_PE_sdata8 = 0x0c, + DW_EH_PE_signed = 0x08, + + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + + DW_EH_PE_indirect = 0x80 + }; /* DWARF XXX. */ #define DW_ADDR_none 0 diff --git a/libdw/dwarf_cfi_addrframe.c b/libdw/dwarf_cfi_addrframe.c new file mode 100644 index 000000000..94ff0bfe8 --- /dev/null +++ b/libdw/dwarf_cfi_addrframe.c @@ -0,0 +1,78 @@ +/* Compute frame state at PC. + Copyright (C) 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "unwindP.h" + +int +dwarf_cfi_addrframe (cache, address, frame) + Dwarf_CFI *cache; + Dwarf_Addr address; + Dwarf_Frame **frame; +{ + /* Maybe there was a previous error. */ + if (cache == NULL) + return -1; + + struct dwarf_fde *fde = __libdw_find_fde (cache, address); + if (fde == NULL) + return -1; + + int error = __libdw_frame_at_address (cache, fde, address, frame); + if (error != DWARF_E_NOERROR) + { + __libdw_seterrno (error); + return -1; + } + return 0; +} +INTDEF (dwarf_cfi_addrframe) diff --git a/libdw/dwarf_cfi_end.c b/libdw/dwarf_cfi_end.c new file mode 100644 index 000000000..82cd77b3c --- /dev/null +++ b/libdw/dwarf_cfi_end.c @@ -0,0 +1,70 @@ +/* Clean up Dwarf_CFI structure. + Copyright (C) 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "libdwP.h" +#include "unwindP.h" +#include + +int +dwarf_cfi_end (cache) + Dwarf_CFI *cache; +{ + if (cache != NULL) + { + __libdw_destroy_frame_cache (cache); + free (cache); + } + + return 0; +} +INTDEF (dwarf_cfi_end) diff --git a/libdw/dwarf_cfi_setebl.c b/libdw/dwarf_cfi_setebl.c new file mode 100644 index 000000000..10da0c23c --- /dev/null +++ b/libdw/dwarf_cfi_setebl.c @@ -0,0 +1,70 @@ +/* Associate Ebl handle with Dwarf_CFI handle. + Copyright (C) 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "libdwP.h" +#include "unwindP.h" +#include "../libebl/libebl.h" + +int +dwarf_cfi_setebl (cfi, ebl) + Dwarf_CFI *cfi; + Ebl *ebl; +{ + if (cfi == NULL) + return -1; + + cfi->ebl = ebl; + + return 0; +} +INTDEF (dwarf_cfi_setebl) diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c index 60c9716e4..33f863e98 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -1,5 +1,5 @@ /* Release debugging handling context. - Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. + Copyright (C) 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. This file is part of Red Hat elfutils. Written by Ulrich Drepper , 2002. @@ -56,7 +56,7 @@ #include #include "libdwP.h" - +#include "unwindP.h" static void @@ -82,6 +82,10 @@ dwarf_end (dwarf) { if (dwarf != NULL) { + if (dwarf->cfi != NULL) + /* Clean up the CFI cache. */ + __libdw_destroy_frame_cache (dwarf->cfi); + /* The search tree for the CUs. NB: the CU data itself is allocated separately, but the abbreviation hash tables need to be handled. */ diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c index fe9166414..5f505429e 100644 --- a/libdw/dwarf_error.c +++ b/libdw/dwarf_error.c @@ -1,5 +1,5 @@ /* Retrieve ELF descriptor used for DWARF access. - Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. + Copyright (C) 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. This file is part of Red Hat elfutils. Written by Ulrich Drepper , 2002. @@ -143,6 +143,7 @@ static const char *errmsgs[] = [DWARF_E_NO_FLAG] = N_("no flag value"), [DWARF_E_INVALID_OFFSET] = N_("invalid offset"), [DWARF_E_NO_DEBUG_RANGES] = N_(".debug_ranges section missing"), + [DWARF_E_INVALID_CFI] = N_("invalid CFI section"), }; #define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0])) diff --git a/libdw/dwarf_frame_cfa.c b/libdw/dwarf_frame_cfa.c new file mode 100644 index 000000000..766f0cf1b --- /dev/null +++ b/libdw/dwarf_frame_cfa.c @@ -0,0 +1,100 @@ +/* Get CFA expression for frame. + Copyright (C) 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "unwindP.h" +#include +#include + +int +dwarf_frame_cfa (fs, ops) + Dwarf_Frame *fs; + Dwarf_Op **ops; +{ + /* Maybe there was a previous error. */ + if (fs == NULL) + return -1; + + switch (fs->cfa_rule) + { + case cfa_undefined: + *ops = NULL; + return 0; + + case cfa_offset: + /* The Dwarf_Op was already fully initialized by execute_cfi. */ + *ops = &fs->cfa_data.offset; + return 1; + + case cfa_expr: + { + unsigned int address_size = (fs->cache->e_ident[EI_CLASS] == ELFCLASS32 + ? 4 : 8); + size_t nops; + + /* Parse the expression into internal form. */ + int result = __libdw_intern_expression (NULL, + fs->cache->other_byte_order, + address_size, + &fs->cache->expr_tree, + &fs->cfa_data.expr, + ops, &nops); + return result ?: (int) nops; + } + + default: + abort (); + } + + /*NOTREACHED*/ + return -1; +} diff --git a/libdw/dwarf_frame_register.c b/libdw/dwarf_frame_register.c new file mode 100644 index 000000000..c57c70007 --- /dev/null +++ b/libdw/dwarf_frame_register.c @@ -0,0 +1,143 @@ +/* Get register location expression for frame. + Copyright (C) 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "unwindP.h" +#include + +int +dwarf_frame_register (fs, regno, ops_mem, ops, nops) + Dwarf_Frame *fs; + int regno; + Dwarf_Op ops_mem[2]; + Dwarf_Op **ops; + size_t *nops; +{ + /* Maybe there was a previous error. */ + if (fs == NULL) + return -1; + + if (unlikely (regno < 0)) + { + __libdw_seterrno (DWARF_E_INVALID_ACCESS); + return -1; + } + + int result = 0; /* A location, not a value. */ + + if (unlikely ((size_t) regno >= fs->nregs)) + goto default_rule; + + const struct dwarf_frame_register *reg = &fs->regs[regno]; + + switch (reg->rule) + { + case reg_unspecified: + default_rule: + /* Use the default rule for registers not yet mentioned in CFI. */ + if (fs->cache->default_same_value) + goto same_value; + /*FALLTHROUGH*/ + case reg_undefined: + /* The value is known to be unavailable. */ + result = 1; + /*FALLTHROUGH*/ + case reg_same_value: + same_value: + /* The location is not known here, but the caller might know it. */ + *ops = NULL; + *nops = 0; + break; + + case reg_val_offset: + result = 1; /* A value, not a location. */ + /*FALLTHROUGH*/ + case reg_offset: + ops_mem[0] = (Dwarf_Op) { .atom = DW_OP_call_frame_cfa }; + ops_mem[1] = (Dwarf_Op) { .atom = DW_OP_plus_uconst, + .number = reg->value }; + *ops = ops_mem; + *nops = reg->value == 0 ? 1 : 2; + break; + + case reg_register: + ops_mem[0] = (Dwarf_Op) { .atom = DW_OP_regx, .number = reg->value }; + *ops = ops_mem; + *nops = 1; + break; + + case reg_val_expression: + result = 1; /* A value, not a location. */ + /*FALLTHROUGH*/ + case reg_expression: + { + unsigned int address_size = (fs->cache->e_ident[EI_CLASS] == ELFCLASS32 + ? 4 : 8); + + Dwarf_Block block; + const uint8_t *p = fs->cache->data.d_buf + reg->value; + get_uleb128 (block.length, p); + block.data = (void *) p; + + /* Parse the expression into internal form. */ + if (__libdw_intern_expression (NULL, + fs->cache->other_byte_order, + address_size, + &fs->cache->expr_tree, + &block, ops, nops) < 0) + result = -1; + break; + } + } + + return result; +} diff --git a/libdw/dwarf_frame_return_address_register.c b/libdw/dwarf_frame_return_address_register.c new file mode 100644 index 000000000..0ca38840b --- /dev/null +++ b/libdw/dwarf_frame_return_address_register.c @@ -0,0 +1,63 @@ +/* Get return address register for frame. + Copyright (C) 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "unwindP.h" + +int +dwarf_frame_return_address_register (fs, signalp) + Dwarf_Frame *fs; + bool *signalp; +{ + *signalp = fs->cie->signal_frame; + return fs->cie->return_address_register; +} diff --git a/libdw/dwarf_getcfi.c b/libdw/dwarf_getcfi.c new file mode 100644 index 000000000..16b5d1794 --- /dev/null +++ b/libdw/dwarf_getcfi.c @@ -0,0 +1,99 @@ +/* Get CFI from DWARF file. + Copyright (C) 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "libdwP.h" +#include "unwindP.h" +#include + +Dwarf_CFI * +dwarf_getcfi (dbg) + Dwarf *dbg; +{ + if (dbg == NULL) + return NULL; + + if (dbg->cfi == NULL && (dbg->sectiondata[IDX_debug_frame] != NULL + || dbg->sectiondata[IDX_eh_frame] != NULL)) + { + Dwarf_CFI *cfi = libdw_typed_alloc (dbg, Dwarf_CFI); + + cfi->eh_frame = dbg->sectiondata[IDX_debug_frame] == NULL; + cfi->data = *dbg->sectiondata[cfi->eh_frame ? IDX_eh_frame + : IDX_debug_frame]; + cfi->rawchunk = false; + cfi->elf = dbg->elf; + + cfi->search_table = NULL; + cfi->search_table_vaddr = 0; + cfi->search_table_rawchunk = NULL; + cfi->search_table_entries = 0; + cfi->search_table_encoding = DW_EH_PE_omit; + + cfi->frame_vaddr = 0; + cfi->textrel = 0; + cfi->datarel = 0; + if (cfi->eh_frame) + cfi->frame_vaddr = -1; // XXX .eh_frame sh_addr + + cfi->e_ident = (unsigned char *) elf_getident (dbg->elf, NULL); + cfi->other_byte_order = dbg->other_byte_order; + + cfi->next_offset = 0; + cfi->cie_tree = cfi->fde_tree = cfi->expr_tree = NULL; + + dbg->cfi = cfi; + } + + return dbg->cfi; +} +INTDEF (dwarf_getcfi) diff --git a/libdw/dwarf_getcfi_elf.c b/libdw/dwarf_getcfi_elf.c new file mode 100644 index 000000000..94c46eaf2 --- /dev/null +++ b/libdw/dwarf_getcfi_elf.c @@ -0,0 +1,340 @@ +/* Get CFI from ELF file's exception-handling info. + Copyright (C) 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "libdwP.h" +#include "unwindP.h" +#include "encoded-value.h" +#include + + +static Dwarf_CFI * +allocate_cfi (Elf *elf, GElf_Addr vaddr) +{ + Dwarf_CFI *cfi = calloc (1, sizeof *cfi); + if (cfi == NULL) + { + __libdw_seterrno (DWARF_E_NOMEM); + return NULL; + } + + cfi->elf = elf; + cfi->e_ident = (unsigned char *) elf_getident (elf, NULL); + if (cfi->e_ident == NULL) + { + free (cfi); + __libdw_seterrno (DWARF_E_GETEHDR_ERROR); + return NULL; + } + + cfi->eh_frame = true; + if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB) + || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB)) + cfi->other_byte_order = true; + + cfi->frame_vaddr = vaddr; + cfi->textrel = 0; /* XXX ? */ + cfi->datarel = 0; /* XXX ? */ + + return cfi; +} + +static const uint8_t * +parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr, + const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr, + size_t *table_entries, uint8_t *table_encoding) +{ + const uint8_t *h = hdr; + + if (*h++ != 1) /* version */ + return (void *) -1l; + + uint8_t eh_frame_ptr_encoding = *h++; + uint8_t fde_count_encoding = *h++; + uint8_t fde_table_encoding = *h++; + + if (eh_frame_ptr_encoding == DW_EH_PE_omit) + return (void *) -1l; + + /* Dummy used by read_encoded_value. */ + Dwarf_CFI dummy_cfi = + { + .e_ident = ehdr->e_ident, + .datarel = hdr_vaddr, + .frame_vaddr = hdr_vaddr, + .data = { .d_buf = (void *) hdr, .d_size = hdr_size } + }; + + *eh_frame_vaddr = read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h); + + if (fde_count_encoding != DW_EH_PE_omit) + { + Dwarf_Word fde_count = read_encoded_value (&dummy_cfi, + fde_count_encoding, &h); + if (fde_count != 0 && (size_t) fde_count == fde_count + && fde_table_encoding != DW_EH_PE_omit + && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128) + { + *table_entries = fde_count; + *table_encoding = fde_table_encoding; + return h; + } + } + + return NULL; +} + +static Dwarf_CFI * +getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr) +{ + if (unlikely (phdr->p_filesz < 4)) + goto invalid; + + void *const hdr = gelf_rawchunk (elf, phdr->p_offset, phdr->p_filesz); + if (hdr == NULL) + { + invalid_hdr: + gelf_freechunk (elf, hdr); + invalid: + /* XXX might be read error or corrupt phdr */ + __libdw_seterrno (DWARF_E_INVALID_CFI); + return NULL; + } + + Dwarf_Addr eh_frame_ptr; + size_t search_table_entries; + uint8_t search_table_encoding; + const uint8_t *search_table = parse_eh_frame_hdr (hdr, phdr->p_filesz, + phdr->p_vaddr, ehdr, + &eh_frame_ptr, + &search_table_entries, + &search_table_encoding); + if (search_table == (void *) -1l) + goto invalid_hdr; + + Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset; + Dwarf_Word eh_frame_size = 0; + + /* XXX we have no way without section headers to know the size + of the .eh_frame data. Calculate the largest it might possibly be. + This won't be wasteful if the file is already mmap'd, but if it isn't + it might be quite excessive. */ + size_t filesize; + if (elf_rawfile (elf, &filesize) != NULL) + eh_frame_size = filesize - eh_frame_offset; + + Elf_Data data = { .d_version = EV_CURRENT }; + data.d_buf = gelf_rawchunk (elf, eh_frame_offset, eh_frame_size); + data.d_size = eh_frame_size; + if (data.d_buf == NULL) + { + __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */ + gelf_freechunk (elf, hdr); + return NULL; + } + Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr); + if (cfi == NULL) + { + gelf_freechunk (elf, hdr); + gelf_freechunk (elf, data.d_buf); + } + else + { + cfi->data = data; + cfi->rawchunk = true; + + if (search_table != NULL) + { + cfi->search_table = search_table; + cfi->search_table_vaddr = phdr->p_vaddr; + cfi->search_table_encoding = search_table_encoding; + cfi->search_table_entries = search_table_entries; + cfi->search_table_rawchunk = hdr; + } + else + gelf_freechunk (elf, hdr); + } + return cfi; +} + +/* Search the phdrs for PT_GNU_EH_FRAME. */ +static Dwarf_CFI * +getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr) +{ + const uint_fast16_t phnum = ehdr->e_phnum; + + for (uint_fast16_t i = 0; i < phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); + if (unlikely (phdr == NULL)) + return NULL; + if (phdr->p_type == PT_GNU_EH_FRAME) + return getcfi_gnu_eh_frame (elf, ehdr, phdr); + } + + __libdw_seterrno (DWARF_E_NO_DWARF); + return NULL; +} + +static Dwarf_CFI * +getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, + Elf_Scn *scn, GElf_Shdr *shdr, + Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr) +{ + Elf_Data *data = elf_rawdata (scn, NULL); + if (data == NULL) + { + __libdw_seterrno (DWARF_E_INVALID_ELF); + return NULL; + } + Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr); + if (cfi != NULL) + { + cfi->data = *data; + if (hdr_scn != NULL) + { + Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL); + if (hdr_data != NULL) + { + GElf_Addr eh_frame_vaddr; + cfi->search_table_vaddr = hdr_vaddr; + cfi->search_table + = parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size, + hdr_vaddr, ehdr, &eh_frame_vaddr, + &cfi->search_table_entries, + &cfi->search_table_encoding); + if (cfi->search_table == (void *) -1l) + { + free (cfi); + /* XXX might be read error or corrupt phdr */ + __libdw_seterrno (DWARF_E_INVALID_CFI); + return NULL; + } + + /* Sanity check. */ + if (unlikely (eh_frame_vaddr != shdr->sh_addr)) + cfi->search_table = NULL; + } + } + } + return cfi; +} + +/* Search for the section named ".eh_frame". */ +static Dwarf_CFI * +getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr) +{ + size_t shstrndx; + if (elf_getshstrndx (elf, &shstrndx) != 0) + { + __libdw_seterrno (DWARF_E_GETEHDR_ERROR); + return NULL; + } + + if (shstrndx != 0) + { + Elf_Scn *hdr_scn = NULL; + GElf_Addr hdr_vaddr = 0; + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + continue; + const char *name = elf_strptr (elf, shstrndx, shdr->sh_name); + if (name == NULL) + continue; + if (!strcmp (name, ".eh_frame_hdr")) + { + hdr_scn = scn; + hdr_vaddr = shdr->sh_addr; + } + else if (!strcmp (name, ".eh_frame")) + return getcfi_scn_eh_frame (elf, ehdr, scn, shdr, + hdr_scn, hdr_vaddr); + } + } + + return (void *) -1l; +} + +Dwarf_CFI * +dwarf_getcfi_elf (elf) + Elf *elf; +{ + if (elf_kind (elf) != ELF_K_ELF) + { + __libdw_seterrno (DWARF_E_NOELF); + return NULL; + } + + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (unlikely (ehdr == NULL)) + { + __libdw_seterrno (DWARF_E_INVALID_ELF); + return NULL; + } + + Dwarf_CFI *result = getcfi_shdr (elf, ehdr); + if (result == (void *) -1l) + result = getcfi_phdr (elf, ehdr); + + return result; +} +INTDEF (dwarf_getcfi_elf) diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index f680aa969..c3233e293 100644 --- a/libdw/dwarf_getlocation.c +++ b/libdw/dwarf_getlocation.c @@ -1,5 +1,5 @@ /* Return location expression list. - Copyright (C) 2000, 2001, 2002, 2004, 2005, 2006 Red Hat, Inc. + Copyright (C) 2000, 2001, 2002, 2004, 2005, 2006, 2007 Red Hat, Inc. This file is part of Red Hat elfutils. Written by Ulrich Drepper , 2000. @@ -111,15 +111,16 @@ loc_compare (const void *p1, const void *p2) return 0; } -static int -getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, - Dwarf_Op **llbuf, size_t *listlen) +int +internal_function +__libdw_intern_expression (Dwarf *dbg, + bool other_byte_order, unsigned int address_size, + void **cache, const Dwarf_Block *block, + Dwarf_Op **llbuf, size_t *listlen) { - Dwarf *dbg = cu->dbg; - /* Check whether we already looked at this list. */ struct loc_s fake = { .addr = block->data }; - struct loc_s **found = tfind (&fake, &cu->locs, loc_compare); + struct loc_s **found = tfind (&fake, cache, loc_compare); if (found != NULL) { /* We already saw it. */ @@ -132,6 +133,8 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, const unsigned char *data = block->data; const unsigned char *const end_data = data + block->length; + const struct { bool other_byte_order; } bo = { other_byte_order }; + struct loclist *loclist = NULL; unsigned int n = 0; /* Decode the opcodes. It is possible in some situations to have a @@ -151,7 +154,7 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, { case DW_OP_addr: /* Address, depends on address size of CU. */ - if (cu->address_size == 4) + if (address_size == 4) { if (unlikely (data + 4 > end_data)) { @@ -160,14 +163,14 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, return -1; } - newloc->number = read_4ubyte_unaligned_inc (dbg, data); + newloc->number = read_4ubyte_unaligned_inc (&bo, data); } else { if (unlikely (data + 8 > end_data)) goto invalid; - newloc->number = read_8ubyte_unaligned_inc (dbg, data); + newloc->number = read_8ubyte_unaligned_inc (&bo, data); } break; @@ -228,7 +231,7 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, if (unlikely (data + 2 > end_data)) goto invalid; - newloc->number = read_2ubyte_unaligned_inc (dbg, data); + newloc->number = read_2ubyte_unaligned_inc (&bo, data); break; case DW_OP_const2s: @@ -238,14 +241,14 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, if (unlikely (data + 2 > end_data)) goto invalid; - newloc->number = read_2sbyte_unaligned_inc (dbg, data); + newloc->number = read_2sbyte_unaligned_inc (&bo, data); break; case DW_OP_const4u: if (unlikely (data + 4 > end_data)) goto invalid; - newloc->number = read_4ubyte_unaligned_inc (dbg, data); + newloc->number = read_4ubyte_unaligned_inc (&bo, data); break; case DW_OP_const4s: @@ -253,21 +256,21 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, if (unlikely (data + 4 > end_data)) goto invalid; - newloc->number = read_4sbyte_unaligned_inc (dbg, data); + newloc->number = read_4sbyte_unaligned_inc (&bo, data); break; case DW_OP_const8u: if (unlikely (data + 8 > end_data)) goto invalid; - newloc->number = read_8ubyte_unaligned_inc (dbg, data); + newloc->number = read_8ubyte_unaligned_inc (&bo, data); break; case DW_OP_const8s: if (unlikely (data + 8 > end_data)) goto invalid; - newloc->number = read_8sbyte_unaligned_inc (dbg, data); + newloc->number = read_8sbyte_unaligned_inc (&bo, data); break; case DW_OP_constu: @@ -305,7 +308,19 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, } /* Allocate the array. */ - Dwarf_Op *result = libdw_alloc (dbg, Dwarf_Op, sizeof (Dwarf_Op), n); + Dwarf_Op *result; + if (dbg != NULL) + result = libdw_alloc (dbg, Dwarf_Op, sizeof (Dwarf_Op), n); + else + { + result = malloc (sizeof *result * n); + if (result == NULL) + { + nomem: + __libdw_seterrno (DWARF_E_NOMEM); + return -1; + } + } /* Store the result. */ *llbuf = result; @@ -327,17 +342,37 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, /* Insert a record in the search tree so that we can find it again later. */ - struct loc_s *newp = libdw_alloc (dbg, struct loc_s, sizeof (struct loc_s), - 1); + struct loc_s *newp; + if (dbg != NULL) + newp = libdw_alloc (dbg, struct loc_s, sizeof (struct loc_s), 1); + else + { + newp = malloc (sizeof *newp); + if (newp == NULL) + { + free (result); + goto nomem; + } + } + newp->addr = block->data; newp->loc = result; newp->nloc = *listlen; - (void) tsearch (newp, &cu->locs, loc_compare); + (void) tsearch (newp, cache, loc_compare); /* We did it. */ return 0; } +static int +getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, + Dwarf_Op **llbuf, size_t *listlen) +{ + return __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order, + cu->address_size, &cu->locs, + block, llbuf, listlen); +} + int dwarf_getlocation (attr, llbuf, listlen) Dwarf_Attribute *attr; diff --git a/libdw/dwarf_next_cfi.c b/libdw/dwarf_next_cfi.c new file mode 100644 index 000000000..2fe381a4b --- /dev/null +++ b/libdw/dwarf_next_cfi.c @@ -0,0 +1,234 @@ +/* Advance to next CFI entry. + Copyright (C) 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "unwindP.h" +#include "encoded-value.h" + +#include + + +int +dwarf_next_cfi (e_ident, data, eh_frame_p, off, next_off, entry) + const unsigned char e_ident[]; + Elf_Data *data; + bool eh_frame_p; + Dwarf_Off off; + Dwarf_Off *next_off; + Dwarf_CFI_Entry *entry; +{ + /* Dummy struct for memory-access.h macros. */ + BYTE_ORDER_DUMMY (dw, e_ident); + + /* If we reached the end before don't do anything. */ + if (off == (Dwarf_Off) -1l + /* Make sure there is enough space in the .debug_frame section + for at least the initial word. We cannot test the rest since + we don't know yet whether this is a 64-bit object or not. */ + || unlikely (off + 4 >= data->d_size)) + { + *next_off = (Dwarf_Off) -1l; + return 1; + } + + /* This points into the .debug_frame section at the start of the entry. */ + const uint8_t *bytes = data->d_buf + off; + const uint8_t *limit = data->d_buf + data->d_size; + + /* The format of a CFI entry is described in DWARF3 6.4.1: + */ + + uint64_t length = read_4ubyte_unaligned_inc (&dw, bytes); + size_t offset_size = 4; + if (length == 0xffffffffu) + { + /* This is the 64-bit DWARF format. */ + offset_size = 8; + if (unlikely (limit - bytes < 8)) + { + invalid: + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + length = read_8ubyte_unaligned_inc (&dw, bytes); + } + if (unlikely ((uint64_t) (limit - bytes) < length) + || unlikely (length < offset_size + 1)) + goto invalid; + + /* Now we know how large the entry is. Note the trick in the + computation. If the offset_size is 4 the '- 4' term undoes the + '2 *'. If offset_size is 8 this term computes the size of the + escape value plus the 8 byte offset. */ + *next_off = off + (2 * offset_size - 4) + length; + + limit = bytes + length; + + const uint8_t *const cie_pointer_start = bytes; + if (offset_size == 8) + entry->cie.CIE_id = read_8ubyte_unaligned_inc (&dw, bytes); + else + { + entry->cie.CIE_id = read_4ubyte_unaligned_inc (&dw, bytes); + /* Canonicalize the 32-bit CIE_ID value to 64 bits. */ + if (!eh_frame_p && entry->cie.CIE_id == 0xffffffffu) + entry->cie.CIE_id = CIE_ID; + } + if (eh_frame_p) + { + /* Canonicalize the .eh_frame CIE pointer to .debug_frame format. */ + if (entry->cie.CIE_id == 0) + entry->cie.CIE_id = CIE_ID; + else + { + /* In .eh_frame format, a CIE pointer is the distance from where + it appears back to the beginning of the CIE. */ + ptrdiff_t pos = cie_pointer_start - (const uint8_t *) data->d_buf; + if (unlikely (entry->cie.CIE_id > (Dwarf_Off) pos) + || unlikely (pos <= (ptrdiff_t) offset_size)) + goto invalid; + entry->cie.CIE_id = pos - entry->cie.CIE_id; + } + } + + if (entry->cie.CIE_id == CIE_ID) + { + /* Read the version stamp. Always an 8-bit value. */ + uint8_t version = *bytes++; + + if (version != 1 && version != 3) + goto invalid; + + entry->cie.augmentation = (const char *) bytes; + + bytes = memchr (bytes, '\0', limit - bytes); + if (bytes == NULL) + goto invalid; + ++bytes; + + const char *ap = entry->cie.augmentation; + + /* g++ v2 "eh" has pointer immediately following augmentation string, + so it must be handled first. */ + if (unlikely (ap[0] == 'e' && ap[1] == 'h')) + { + /* The address size for CFI is implicit in the ELF class. */ + unsigned int address_size = e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + ap += 2; + bytes += address_size; + } + + get_uleb128 (entry->cie.code_alignment_factor, bytes); + get_sleb128 (entry->cie.data_alignment_factor, bytes); + + if (version == 3) /* DWARF 3 */ + get_uleb128 (entry->cie.return_address_register, bytes); + else /* DWARF 2 */ + entry->cie.return_address_register = *bytes++; + + /* If we have sized augmentation data, + we don't need to grok it all. */ + entry->cie.fde_augmentation_data_size = 0; + bool sized_augmentation = *ap == 'z'; + if (sized_augmentation) + { + get_uleb128 (entry->cie.augmentation_data_size, bytes); + if ((Dwarf_Word) (limit - bytes) < entry->cie.augmentation_data_size) + goto invalid; + entry->cie.augmentation_data = bytes; + bytes += entry->cie.augmentation_data_size; + } + else + { + entry->cie.augmentation_data = bytes; + + for (; *ap != '\0'; ++ap) + { + uint8_t encoding; + switch (*ap) + { + case 'L': /* Skip LSDA pointer encoding byte. */ + case 'R': /* Skip FDE address encoding byte. */ + encoding = *bytes++; + entry->cie.fde_augmentation_data_size + += encoded_value_size (data, e_ident, encoding, NULL); + continue; + case 'P': /* Skip encoded personality routine pointer. */ + encoding = *bytes++; + bytes += encoded_value_size (data, e_ident, encoding, bytes); + continue; + case 'S': /* Skip signal-frame flag. */ + continue; + default: + /* Unknown augmentation string. initial_instructions might + actually start with some augmentation data. */ + break; + } + break; + } + entry->cie.augmentation_data_size + = bytes - entry->cie.augmentation_data; + } + + entry->cie.initial_instructions = bytes; + entry->cie.initial_instructions_end = limit; + } + else + { + entry->fde.start = bytes; + entry->fde.end = limit; + } + + return 0; +} +INTDEF (dwarf_next_cfi) diff --git a/libdw/encoded-value.h b/libdw/encoded-value.h new file mode 100644 index 000000000..d653dbc2e --- /dev/null +++ b/libdw/encoded-value.h @@ -0,0 +1,174 @@ +/* DW_EH_PE_* support for libdw unwinder. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifndef _ENCODED_VALUE_H +#define _ENCODED_VALUE_H 1 + +#include +#include +#include "unwind.h" /* XXX */ + +static size_t __attribute__ ((unused)) +encoded_value_size (const Elf_Data *data, const unsigned char e_ident[], + uint8_t encoding, const uint8_t *p) +{ + if (encoding == DW_EH_PE_omit) + return 0; + + switch (encoding & 0x07) + { + case DW_EH_PE_udata2: + return 2; + case DW_EH_PE_udata4: + return 4; + case DW_EH_PE_udata8: + return 8; + + case DW_EH_PE_absptr: + return e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + case DW_EH_PE_uleb128: + if (p != NULL) + { + const uint8_t *end = p; + while (end < (uint8_t *) data->d_buf + data->d_size) + if (*end++ & 0x80u) + return end - p; + } + + default: + abort (); + return 0; + } +} + +static Dwarf_Addr __attribute__ ((unused)) +read_encoded_value (const Dwarf_CFI *cache, + uint8_t encoding, const uint8_t **p) +{ + Dwarf_Addr base = 0; + switch (encoding & 0x70) + { + case DW_EH_PE_absptr: + break; + case DW_EH_PE_pcrel: + base = cache->frame_vaddr + (*p - (const uint8_t *) cache->data.d_buf); + break; + case DW_EH_PE_textrel: + // ia64: segrel + base = cache->textrel; + break; + case DW_EH_PE_datarel: + // i386: GOTOFF + // ia64: gprel + base = cache->datarel; + break; + case DW_EH_PE_funcrel: /* XXX */ + break; + case DW_EH_PE_aligned: + { + const size_t address_size + = cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + size_t align = ((cache->frame_vaddr + + (*p - (const uint8_t *) cache->data.d_buf)) + & (address_size - 1)); + if (align != 0) + *p += address_size - align; + break; + } + + default: + abort (); + } + + Dwarf_Addr value; + switch (encoding & 0x0f) + { + case DW_EH_PE_udata2: + value = read_2ubyte_unaligned_inc (cache, *p); + break; + case DW_EH_PE_udata4: + value = read_4ubyte_unaligned_inc (cache, *p); + break; + case DW_EH_PE_udata8: + value = read_8ubyte_unaligned_inc (cache, *p); + break; + + case DW_EH_PE_sdata2: + value = read_2sbyte_unaligned_inc (cache, *p); + break; + case DW_EH_PE_sdata4: + value = read_4sbyte_unaligned_inc (cache, *p); + break; + case DW_EH_PE_sdata8: + value = read_8sbyte_unaligned_inc (cache, *p); + break; + + case DW_EH_PE_absptr: + if (cache->e_ident[EI_CLASS] == ELFCLASS32) + value = read_4ubyte_unaligned_inc (cache, *p); + else + value = read_8ubyte_unaligned_inc (cache, *p); + break; + + case DW_EH_PE_uleb128: + get_uleb128 (value, *p); + break; + case DW_EH_PE_sleb128: + get_sleb128 (value, *p); + break; + + default: + abort (); + } + + return base + value; +} + +#endif /* encoded-value.h */ diff --git a/libdw/fde.c b/libdw/fde.c new file mode 100644 index 000000000..acfa39da2 --- /dev/null +++ b/libdw/fde.c @@ -0,0 +1,298 @@ +/* FDE reading. + Copyright (C) 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "unwindP.h" +#include +#include + +#include "encoded-value.h" + +static int +compare_fde (const void *a, const void *b) +{ + const struct dwarf_fde *fde1 = a; + const struct dwarf_fde *fde2 = b; + + /* Find out which of the two arguments is the search value. + It has end offset 0. */ + if (fde1->end == 0) + { + if (fde1->start < fde2->start) + return -1; + if (fde1->start >= fde2->end) + return 1; + } + else + { + if (fde2->start < fde1->start) + return 1; + if (fde2->start >= fde1->end) + return -1; + } + + return 0; +} + +static struct dwarf_fde * +intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry) +{ + /* Look up the new entry's CIE. */ + struct dwarf_cie *cie = __libdw_find_cie (cache, entry->CIE_pointer); + if (cie == NULL) + return (void *) -1l; + + struct dwarf_fde *fde = malloc (sizeof (struct dwarf_fde)); + if (fde == NULL) + { + __libdw_seterrno (DWARF_E_NOMEM); + return NULL; + } + + fde->instructions = entry->start; + fde->instructions_end = entry->end; + fde->start = read_encoded_value (cache, cie->fde_encoding, + &fde->instructions); + fde->end = fde->start + read_encoded_value (cache, cie->fde_encoding & 0x0f, + &fde->instructions); + + fde->cie = cie; + + if (cie->sized_augmentation_data) + { + /* The CIE augmentation says the FDE has a DW_FORM_block + before its actual instruction stream. */ + Dwarf_Word len; + get_uleb128 (len, fde->instructions); + if ((Dwarf_Word) (fde->instructions_end < fde->instructions) < len) + { + free (fde); + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return NULL; + } + fde->instructions += len; + } + else + /* We had to understand all of the CIE augmentation string. + We've recorded the number of data bytes in FDEs. */ + fde->instructions += cie->fde_augmentation_data_size; + + /* Add the new entry to the search tree. */ + if (tsearch (fde, &cache->fde_tree, &compare_fde) == NULL) + { + free (fde); + __libdw_seterrno (DWARF_E_NOMEM); + return NULL; + } + + return fde; +} + +static struct dwarf_fde * +fde_by_offset (Dwarf_CFI *cache, Dwarf_Addr address, Dwarf_Off offset) +{ + Dwarf_CFI_Entry entry; + Dwarf_Off next_offset; + int result = INTUSE(dwarf_next_cfi) (cache->e_ident, + &cache->data, cache->eh_frame, + offset, &next_offset, &entry); + if (result != 0) + { + if (result > 0) + invalid: + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return NULL; + } + + if (unlikely (dwarf_cfi_cie_p (&entry))) + goto invalid; + + /* We have a new FDE to consider. */ + struct dwarf_fde *fde = intern_fde (cache, &entry.fde); + if (fde == (void *) -1l || fde == NULL) + return NULL; + + /* If this happened to be what we would have read next, notice it. */ + if (cache->next_offset == offset) + cache->next_offset = next_offset; + + /* Sanity check the address range. */ + if (address < fde->start || address >= fde->end) + goto invalid; + + return fde; +} + +/* Use a binary search table in .eh_frame_hdr format, yield an FDE offset. */ +static Dwarf_Off +binary_search_fde (Dwarf_CFI *cache, Dwarf_Addr address) +{ + const size_t size = 2 * encoded_value_size (&cache->data, cache->e_ident, + cache->search_table_encoding, + NULL); + + + /* Dummy used by read_encoded_value. */ + Dwarf_CFI dummy_cfi = + { + .e_ident = cache->e_ident, + .datarel = cache->search_table_vaddr, + .frame_vaddr = cache->search_table_vaddr, + }; + + size_t l = 0, u = cache->search_table_entries; + while (l < u) + { + size_t idx = (l + u) / 2; + + const uint8_t *p = &cache->search_table[idx * size]; + Dwarf_Addr start = read_encoded_value (&dummy_cfi, + cache->search_table_encoding, &p); + if (address < start) + u = idx; + else + { + Dwarf_Addr fde = read_encoded_value (&dummy_cfi, + cache->search_table_encoding, + &p); + if (address > start) + { + l = idx + 1; + + /* If this is the last entry, its upper bound is assumed to be + the end of the module. + XXX really should be end of containing PT_LOAD segment */ + if (l < cache->search_table_entries) + { + /* Look at the start address in the following entry. */ + Dwarf_Addr end + = read_encoded_value (&dummy_cfi, + cache->search_table_encoding, &p); + if (address >= end) + continue; + } + + return fde - cache->frame_vaddr; + } + } + } + + return (Dwarf_Off) -1l; +} + +struct dwarf_fde * +internal_function +__libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address) +{ + /* Look for a cached FDE covering this address. */ + + const struct dwarf_fde fde_key = { .start = address, .end = 0 }; + struct dwarf_fde **found = tfind (&fde_key, &cache->fde_tree, &compare_fde); + if (found != NULL) + return *found; + + /* Use .eh_frame_hdr binary search table if possible. */ + if (cache->search_table != NULL) + { + Dwarf_Off offset = binary_search_fde (cache, address); + if (offset == (Dwarf_Off) -1l) + goto no_match; + return fde_by_offset (cache, address, offset); + } + + /* It's not there. Read more CFI entries until we find it. */ + while (1) + { + Dwarf_Off last_offset = cache->next_offset; + Dwarf_CFI_Entry entry; + int result = INTUSE(dwarf_next_cfi) (cache->e_ident, + &cache->data, cache->eh_frame, + last_offset, &cache->next_offset, + &entry); + if (result > 0) + break; + if (result < 0) + { + if (cache->next_offset == last_offset) + /* We couldn't progress past the bogus FDE. */ + break; + /* Skip the loser and look at the next entry. */ + continue; + } + + if (dwarf_cfi_cie_p (&entry)) + { + /* This is a CIE, not an FDE. We eagerly intern these + because the next FDE will usually refer to this CIE. */ + __libdw_intern_cie (cache, last_offset, &entry.cie); + continue; + } + + /* We have a new FDE to consider. */ + struct dwarf_fde *fde = intern_fde (cache, &entry.fde); + + if (fde == (void *) -1l) /* Bad FDE, but we can keep looking. */ + continue; + + if (fde == NULL) /* Bad data. */ + return NULL; + + /* Is this the one we're looking for? */ + if (fde->start <= address && fde->end > address) + return fde; + } + + no_match: + /* We found no FDE covering this address. */ + __libdw_seterrno (DWARF_E_NO_MATCH); + return NULL; +} diff --git a/libdw/frame-cache.c b/libdw/frame-cache.c new file mode 100644 index 000000000..2182ad382 --- /dev/null +++ b/libdw/frame-cache.c @@ -0,0 +1,90 @@ +/* Frame cache handling. + Copyright (C) 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "unwindP.h" +#include +#include + + +static void +free_cie (void *arg) +{ + struct dwarf_cie *cie = arg; + + free (cie->initial_state); + free (cie); +} + +#define free_fde free + +static void +free_expr (void *arg) +{ + struct loc_s *loc = arg; + + free (loc->loc); + free (loc); +} + +void +internal_function +__libdw_destroy_frame_cache (Dwarf_CFI *cache) +{ + /* Most of the data is in our two search trees. */ + tdestroy (cache->fde_tree, free_fde); + tdestroy (cache->cie_tree, free_cie); + tdestroy (cache->expr_tree, free_expr); + + if (cache->rawchunk) + gelf_freechunk (cache->elf, cache->data.d_buf); +} diff --git a/libdw/libdw.map b/libdw/libdw.map index 8e2f6441b..b53918c61 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -1,7 +1,6 @@ ELFUTILS_0 { }; ELFUTILS_0.122 { - global: - dwarf_abbrevhaschildren; + global: dwarf_abbrevhaschildren; dwarf_addrdie; dwarf_arrayorder; dwarf_attr; @@ -185,3 +184,19 @@ ELFUTILS_0.136 { local: *; } ELFUTILS_0.130; + +ELFUTILS_0.138_UNWIND { + global: + # XXX new unwind stuff not decided yet + dwarf_next_cfi; + dwarf_getcfi; + dwarf_getcfi_elf; + dwarf_cfi_addrframe; + dwarf_cfi_end; + dwarf_cfi_setebl; + dwarf_frame_cfa; + dwarf_frame_register; + dwarf_frame_return_address_register; + dwfl_addrframe; + dwfl_module_getcfi; +} ELFUTILS_0.136; diff --git a/libdw/libdwP.h b/libdw/libdwP.h index f805295ff..6937ace87 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -136,6 +136,7 @@ enum DWARF_E_NO_FLAG, DWARF_E_INVALID_OFFSET, DWARF_E_NO_DEBUG_RANGES, + DWARF_E_INVALID_CFI, }; @@ -172,6 +173,9 @@ struct Dwarf /* Address ranges. */ Dwarf_Aranges *aranges; + /* Cached info from the CFI section. */ + struct Dwarf_CFI_s *cfi; + /* Internal memory handling. This is basically a simplified reimplementation of obstacks. Unfortunately the standard obstack implementation is not usable in libraries. */ @@ -413,6 +417,16 @@ extern int __libdw_visit_scopes (unsigned int depth, void *arg) __nonnull_attribute__ (2, 3) internal_function; +/* Parse a DWARF Dwarf_Block into an array of Dwarf_Op's, + and cache the result (via tsearch). */ +extern int __libdw_intern_expression (Dwarf *dbg, + bool other_byte_order, + unsigned int address_size, + void **cache, const Dwarf_Block *block, + Dwarf_Op **llbuf, size_t *listlen) + __nonnull_attribute__ (4, 5, 6, 7) internal_function; + + /* Return error code of last failing function call. This value is kept separately for each thread. */ extern int __dwarf_errno_internal (void); diff --git a/libdw/memory-access.h b/libdw/memory-access.h index 52b41b5b0..ee3efa882 100644 --- a/libdw/memory-access.h +++ b/libdw/memory-access.h @@ -1,5 +1,5 @@ /* Unaligned memory access functionality. - Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 Red Hat, Inc. This file is part of Red Hat elfutils. Written by Ulrich Drepper , 2001. @@ -186,19 +186,32 @@ union unaligned int64_t s8; } __attribute__ ((packed)); +# define read_2ubyte_unaligned(Dbg, Addr) \ + read_2ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr)) +# define read_2sbyte_unaligned(Dbg, Addr) \ + read_2sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr)) +# define read_4ubyte_unaligned(Dbg, Addr) \ + read_4ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr)) +# define read_4sbyte_unaligned(Dbg, Addr) \ + read_4sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr)) +# define read_8ubyte_unaligned(Dbg, Addr) \ + read_8ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr)) +# define read_8sbyte_unaligned(Dbg, Addr) \ + read_8sbyte_unaligned_1 ((Dbg)->other_byte_order, (Addr)) + static inline uint16_t -read_2ubyte_unaligned (Dwarf *dbg, const void *p) +read_2ubyte_unaligned_1 (bool other_byte_order, const void *p) { const union unaligned *up = p; - if (dbg->other_byte_order) + if (unlikely (other_byte_order)) return bswap_16 (up->u2); return up->u2; } static inline int16_t -read_2sbyte_unaligned (Dwarf *dbg, const void *p) +read_2sbyte_unaligned_1 (bool other_byte_order, const void *p) { const union unaligned *up = p; - if (dbg->other_byte_order) + if (unlikely (other_byte_order)) return (int16_t) bswap_16 (up->u2); return up->s2; } @@ -210,35 +223,35 @@ read_4ubyte_unaligned_noncvt (const void *p) return up->u4; } static inline uint32_t -read_4ubyte_unaligned (Dwarf *dbg, const void *p) +read_4ubyte_unaligned_1 (bool other_byte_order, const void *p) { const union unaligned *up = p; - if (dbg->other_byte_order) + if (unlikely (other_byte_order)) return bswap_32 (up->u4); return up->u4; } static inline int32_t -read_4sbyte_unaligned (Dwarf *dbg, const void *p) +read_4sbyte_unaligned_1 (bool other_byte_order, const void *p) { const union unaligned *up = p; - if (dbg->other_byte_order) + if (unlikely (other_byte_order)) return (int32_t) bswap_32 (up->u4); return up->s4; } static inline uint64_t -read_8ubyte_unaligned (Dwarf *dbg, const void *p) +read_8ubyte_unaligned_1 (bool other_byte_order, const void *p) { const union unaligned *up = p; - if (dbg->other_byte_order) + if (unlikely (other_byte_order)) return bswap_64 (up->u8); return up->u8; } static inline int64_t -read_8sbyte_unaligned (Dwarf *dbg, const void *p) +read_8sbyte_unaligned_1 (bool other_byte_order, const void *p) { const union unaligned *up = p; - if (dbg->other_byte_order) + if (unlikely (other_byte_order)) return (int64_t) bswap_64 (up->u8); return up->s8; } diff --git a/libdw/unwind.c b/libdw/unwind.c new file mode 100644 index 000000000..1524fe00b --- /dev/null +++ b/libdw/unwind.c @@ -0,0 +1,452 @@ +/* CFI program execution. + Copyright (C) 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "../libebl/libebl.h" +#include "unwindP.h" +#include "memory-access.h" +#include "encoded-value.h" +#include +#include +#include + +#define CFI_PRIMARY_MAX 0x3f + +static Dwarf_Frame * +duplicate_frame_state (Dwarf_Frame *original, + Dwarf_Frame *prev) +{ + size_t size = offsetof (Dwarf_Frame, regs[original->nregs]); + Dwarf_Frame *copy = malloc (size); + if (likely (copy != NULL)) + { + memcpy (copy, original, size); + copy->prev = prev; + } + return copy; +} + +/* Returns a DWARF_E_* error code, usually NOERROR or INVALID_CFI. */ +static int +execute_cfi (Dwarf_CFI *cache, + const struct dwarf_cie *cie, + Dwarf_Frame **state, + const uint8_t *program, const uint8_t *const end, bool abi_cfi, + Dwarf_Addr loc, Dwarf_Addr find_pc) +{ + /* The caller should not give us anything out of range. */ + assert (loc <= find_pc); + + int result = DWARF_E_NOERROR; + +#define cfi_assert(ok) do { \ + if (likely (ok)) break; \ + result = DWARF_E_INVALID_CFI; \ + goto out; \ + } while (0) + + Dwarf_Frame *fs = *state; + inline bool enough_registers (Dwarf_Word reg) + { + if (fs->nregs <= reg) + { + size_t size = offsetof (Dwarf_Frame, regs[reg + 1]); + Dwarf_Frame *bigger = realloc (fs, size); + if (unlikely (bigger == NULL)) + { + result = DWARF_E_NOMEM; + return false; + } + else + { + bigger->nregs = reg + 1; + fs = bigger; + } + } + return true; + } + + +#define register_rule(regno, r_rule, r_value) do { \ + if (unlikely (! enough_registers (regno))) \ + goto out; \ + fs->regs[regno].rule = reg_##r_rule; \ + fs->regs[regno].value = (r_value); \ + } while (0) + + while (program < end) + { + uint8_t opcode = *program++; + Dwarf_Word regno; + Dwarf_Word offset; + Dwarf_Word sf_offset; + Dwarf_Word operand = opcode & CFI_PRIMARY_MAX; + switch (opcode) + { + case DW_CFA_advance_loc1: + operand = *program++; + case DW_CFA_advance_loc + 0 ... DW_CFA_advance_loc + CFI_PRIMARY_MAX: + advance_loc: + loc += operand * cie->code_alignment_factor; + break; + + case DW_CFA_advance_loc2: + operand = read_2ubyte_unaligned_inc (cache, program); + goto advance_loc; + case DW_CFA_advance_loc4: + operand = read_4ubyte_unaligned_inc (cache, program); + goto advance_loc; + case DW_CFA_MIPS_advance_loc8: + operand = read_8ubyte_unaligned_inc (cache, program); + goto advance_loc; + + case DW_CFA_set_loc: + loc = read_encoded_value (cache, cie->fde_encoding, &program); + break; + + case DW_CFA_def_cfa: + get_uleb128 (operand, program); + get_uleb128 (offset, program); + def_cfa: + fs->cfa_rule = cfa_offset; + fs->cfa_val_reg = operand; + fs->cfa_val_offset = offset; + /* Prime the rest of the Dwarf_Op so dwarf_frame_cfa can use it. */ + fs->cfa_data.offset.atom = DW_OP_bregx; + fs->cfa_data.offset.offset = 0; + break; + + case DW_CFA_def_cfa_register: + get_uleb128 (regno, program); + cfi_assert (fs->cfa_rule == cfa_offset); + fs->cfa_val_reg = regno; + break; + + case DW_CFA_def_cfa_sf: + get_uleb128 (operand, program); + get_sleb128 (sf_offset, program); + offset = sf_offset * cie->data_alignment_factor; + goto def_cfa; + + case DW_CFA_def_cfa_offset: + get_uleb128 (offset, program); + def_cfa_offset: + cfi_assert (fs->cfa_rule == cfa_offset); + fs->cfa_val_offset = offset; + break; + + case DW_CFA_def_cfa_offset_sf: + get_sleb128 (sf_offset, program); + offset = sf_offset * cie->data_alignment_factor; + goto def_cfa_offset; + + case DW_CFA_def_cfa_expression: + /* DW_FORM_block is a ULEB128 length followed by that many bytes. */ + get_uleb128 (operand, program); + cfi_assert (operand <= (Dwarf_Word) (end - program)); + fs->cfa_rule = cfa_expr; + fs->cfa_data.expr.data = (unsigned char *) program; + fs->cfa_data.expr.length = operand; + program += operand; + break; + + case DW_CFA_undefined: + get_uleb128 (regno, program); + register_rule (regno, undefined, 0); + break; + + case DW_CFA_same_value: + get_uleb128 (regno, program); + register_rule (regno, same_value, 0); + break; + + case DW_CFA_offset_extended: + get_uleb128 (operand, program); + case DW_CFA_offset + 0 ... DW_CFA_offset + CFI_PRIMARY_MAX: + get_uleb128 (offset, program); + offset *= cie->data_alignment_factor; + offset_extended: + register_rule (operand, offset, offset); + break; + + case DW_CFA_offset_extended_sf: + get_uleb128 (operand, program); + get_sleb128 (sf_offset, program); + offset = sf_offset * cie->data_alignment_factor; + goto offset_extended; + + case DW_CFA_val_offset: + get_uleb128 (operand, program); + get_uleb128 (offset, program); + offset *= cie->data_alignment_factor; + val_offset: + register_rule (operand, val_offset, offset); + break; + + case DW_CFA_val_offset_sf: + get_uleb128 (operand, program); + get_sleb128 (sf_offset, program); + offset = sf_offset * cie->data_alignment_factor; + goto val_offset; + + case DW_CFA_register: + get_uleb128 (regno, program); + get_uleb128 (operand, program); + register_rule (regno, register, operand); + break; + + case DW_CFA_expression: + get_uleb128 (regno, program); + offset = program - (const uint8_t *) cache->data.d_buf; + /* DW_FORM_block is a ULEB128 length followed by that many bytes. */ + get_uleb128 (operand, program); + cfi_assert (operand <= (Dwarf_Word) (end - program)); + register_rule (regno, expression, offset); + program += operand; + break; + + case DW_CFA_val_expression: + get_uleb128 (regno, program); + /* DW_FORM_block is a ULEB128 length followed by that many bytes. */ + offset = program - (const uint8_t *) cache->data.d_buf; + get_uleb128 (operand, program); + cfi_assert (operand <= (Dwarf_Word) (end - program)); + register_rule (regno, val_expression, offset); + program += operand; + break; + + case DW_CFA_restore_extended: + get_uleb128 (operand, program); + case DW_CFA_restore + 0 ... DW_CFA_restore + CFI_PRIMARY_MAX: + + if (unlikely (abi_cfi) && likely (opcode == DW_CFA_restore)) + { + /* Special case hack to give backend abi_cfi a shorthand. */ + cache->default_same_value = true; + break; + } + + /* This can't be used in the CIE's own initial instructions. */ + cfi_assert (cie->initial_state != NULL); + + /* Restore the CIE's initial rule for this register. */ + if (unlikely (! enough_registers (operand))) + goto out; + if (cie->initial_state->nregs > operand) + fs->regs[operand] = cie->initial_state->regs[operand]; + else + fs->regs[operand].rule = reg_unspecified; + break; + + case DW_CFA_remember_state: + { + /* Duplicate the state and chain the copy on. */ + Dwarf_Frame *copy = duplicate_frame_state (fs, fs); + if (unlikely (copy == NULL)) + { + result = DWARF_E_NOMEM; + goto out; + } + fs = copy; + break; + } + + case DW_CFA_restore_state: + { + /* Pop the current state off and use the old one instead. */ + Dwarf_Frame *prev = fs->prev; + cfi_assert (prev != NULL); + free (fs); + fs = prev; + } + break; + + case DW_CFA_nop: + break; + + case DW_CFA_GNU_window_save: + /* This is magic shorthand used only by SPARC. It's equivalent + to a bunch of DW_CFA_register and DW_CFA_offset operations. */ + if (unlikely (! enough_registers (31))) + goto out; + for (regno = 8; regno < 16; ++regno) + { + /* Find each %oN in %iN. */ + fs->regs[regno].rule = reg_register; + fs->regs[regno].value = regno + 16; + } + unsigned int address_size = (cache->e_ident[EI_CLASS] == ELFCLASS32 + ? 4 : 8); + for (; regno < 32; ++regno) + { + /* Find %l0..%l7 and %i0..%i7 in a block at the CFA. */ + fs->regs[regno].rule = reg_offset; + fs->regs[regno].value = (regno - 16) * address_size; + } + break; + + case DW_CFA_GNU_args_size: + /* XXX is this useful for anything? */ + get_uleb128 (operand, program); + break; + + default: + cfi_assert (false); + break; + } + + if (find_pc < loc) + /* We have just advanced past the address we're looking for. + The state currently described is what we want to see. */ + break; + } + + /* "The end of the instruction stream can be thought of as a + DW_CFA_set_loc (initial_location + address_range) instruction." + (DWARF 3.0 Section 6.4.3) + + When we fall off the end of the program without an advance_loc/set_loc + that put us past FIND_PC, the final state left by the FDE program + applies to this address (the caller ensured it was inside the FDE). */ + +#undef register_rule +#undef cfi_assert + + out: + + /* Pop any remembered states left on the stack. */ + while (fs->prev != NULL) + { + Dwarf_Frame *prev = fs->prev; + fs->prev = prev->prev; + free (prev); + } + + if (result == DWARF_E_NOERROR) + *state = fs; + + return result; +} + +int +internal_function +__libdw_frame_at_address (Dwarf_CFI *cache, struct dwarf_fde *fde, + Dwarf_Addr address, Dwarf_Frame **frame) +{ + if (fde->cie->initial_state == NULL) + { + /* This CIE has not been used before. Play out its initial + instructions and cache the initial state that results. */ + + Dwarf_Frame *cie_fs = calloc (1, sizeof (Dwarf_Frame)); + if (unlikely (cie_fs == NULL)) + return DWARF_E_NOMEM; + + int result = DWARF_E_NOERROR; + + /* First we'll let the backend fill in the default initial + state for this machine's ABI. */ + + Dwarf_CIE abi_info = { CIE_ID, 1, 1, -1, "", NULL, 0, 0, NULL, NULL }; + if (cache->ebl != NULL && ebl_abi_cfi (cache->ebl, &abi_info) < 0) + return DWARF_E_UNKNOWN_ERROR; + + /* If the default state of any register is not "undefined" + (i.e. call-clobbered), then the backend supplies instructions + for the standard initial state. */ + if (abi_info.initial_instructions_end > abi_info.initial_instructions) + { + /* Dummy CIE for backend's instructions. */ + struct dwarf_cie abi_cie = + { + .code_alignment_factor = abi_info.code_alignment_factor, + .data_alignment_factor = abi_info.data_alignment_factor, + }; + result = execute_cfi (cache, &abi_cie, &cie_fs, + abi_info.initial_instructions, + abi_info.initial_instructions_end, true, + 0, (Dwarf_Addr) -1l); + } + + /* Now run the CIE's initial instructions. */ + if (fde->cie->initial_instructions_end > fde->cie->initial_instructions + && result == DWARF_E_NOERROR) + result = execute_cfi (cache, fde->cie, &cie_fs, + fde->cie->initial_instructions, + fde->cie->initial_instructions_end, false, + 0, (Dwarf_Addr) -1l); + + if (result != DWARF_E_NOERROR) + return result; + + /* Now we have the initial state of things that all + FDEs using this CIE will start from. */ + cie_fs->cache = cache; + cie_fs->cie = fde->cie; + fde->cie->initial_state = cie_fs; + } + + Dwarf_Frame *fs = duplicate_frame_state (fde->cie->initial_state, NULL); + if (unlikely (fs == NULL)) + return DWARF_E_NOMEM; + + int result = execute_cfi (cache, fde->cie, &fs, + fde->instructions, fde->instructions_end, false, + fde->start, address); + if (unlikely (result != DWARF_E_NOERROR)) + free (fs); + else + *frame = fs; + + return result; +} diff --git a/libdw/unwind.h b/libdw/unwind.h new file mode 100644 index 000000000..486822a0a --- /dev/null +++ b/libdw/unwind.h @@ -0,0 +1,185 @@ +#ifndef _libdw_unwind_h /* XXX */ +#define _libdw_unwind_h 1 + +#include "libdw.h" +struct ebl; + +/* This describes one Common Information Entry read from a CFI section. + Pointers here point into the DATA->d_buf block passed to dwarf_next_cfi. */ +typedef struct +{ + Dwarf_Off CIE_id; /* Always CIE_ID in Dwarf_CIE structures. */ +#define CIE_ID ((Dwarf_Off) -1l) + + Dwarf_Word code_alignment_factor; + Dwarf_Sword data_alignment_factor; + Dwarf_Word return_address_register; + + const char *augmentation; /* Augmentation string. */ + + /* Augmentation data, might be NULL. The size is correct only if + we understood the augmentation string sufficiently. */ + const uint8_t *augmentation_data; + size_t augmentation_data_size; + size_t fde_augmentation_data_size; + + /* Instruction stream describing initial state used by FDEs. If + we did not understand the whole augmentation string and it did + not use 'z', then there might be more augmentation data here + (and in FDEs) before the actual instructions. */ + const uint8_t *initial_instructions; + const uint8_t *initial_instructions_end; +} Dwarf_CIE; + +/* This describes one Frame Description Entry read from a CFI section. + Pointers here point into the DATA->d_buf block passed to dwarf_next_cfi. */ +typedef struct +{ + /* Section offset of CIE this FDE refers to. This will never be + CIE_ID in an FDE. If this value is CIE_ID, this is actually a + Dwarf_CIE structure. */ + Dwarf_Off CIE_pointer; + + /* We can't really decode anything further without looking up the CIE + and checking its augmentation string. Here follows the encoded + initial_location and address_range, then any augmentation data, + then the instruction stream. This FDE describes PC locations in + the byte range [initial_location, initial_location+address_range). + When the CIE augmentation string uses 'z', the augmentation data is + a DW_FORM_block (self-sized). Otherwise, when we understand the + augmentation string completely, fde_augmentation_data_size gives + the number of bytes of augmentation data before the instructions. */ + const uint8_t *start; + const uint8_t *end; +} Dwarf_FDE; + +typedef union +{ + Dwarf_CIE cie; + Dwarf_FDE fde; +} Dwarf_CFI_Entry; + +#define dwarf_cfi_cie_p(entry) ((entry)->cie.CIE_id == CIE_ID) + +/* Decode one DWARF CFI entry (CIE or FDE) from the raw section data. + The E_IDENT from the originating ELF file indicates the address + size and byte order used in the CFI section contained in DATA; + EH_FRAME_P should be true for .eh_frame format and false for + .debug_frame format. OFFSET is the byte position in the section + to start at; on return *NEXT_OFFSET is filled in with the byte + position immediately after this entry. + + On success, returns 0 and fills in *ENTRY; use dwarf_cfi_cie_p to + see whether ENTRY->cie or ENTRY->fde is valid. + + On errors, returns -1. Some format errors will permit safely + skipping to the next CFI entry though the current one is unusable. + In that case, *NEXT_OFF will be updated before a -1 return. + + If there are no more CFI entries left in the section, + returns 1 and sets *NEXT_OFFSET to (Dwarf_Off) -1. */ +extern int dwarf_next_cfi (const unsigned char e_ident[], + Elf_Data *data, bool eh_frame_p, + Dwarf_Off offset, Dwarf_Off *next_offset, + Dwarf_CFI_Entry *entry) + __nonnull_attribute__ (1, 2, 5, 6); + + +/* Opaque type representing a frame state described by CFI. */ +typedef struct Dwarf_Frame_s Dwarf_Frame; + +/* Opaque type representing a CFI section found in a DWARF or ELF file. */ +typedef struct Dwarf_CFI_s Dwarf_CFI; + +/* Use the CFI in the DWARF .debug_frame or .eh_frame section. + Returns NULL if there is no such section. + The pointer returned can be used until dwarf_end is called on DWARF, + and must not be passed to dwarf_cfi_end. + Calling this more than once returns the same pointer. */ +extern Dwarf_CFI *dwarf_getcfi (Dwarf *dwarf); + +/* Use the CFI in the ELF file's exception-handling data. + Returns NULL if there is no such data. + The pointer returned can be used until elf_end is called on ELF, + and must be passed to dwarf_cfi_end before then. + Calling this more than once allocates independent data structures. */ +extern Dwarf_CFI *dwarf_getcfi_elf (Elf *elf); + +/* Release resources allocated by dwarf_getcfi_elf. */ +extern int dwarf_cfi_end (Dwarf_CFI *cache); + +/* Associate an open backend from with this Dwarf_CFI handle. + The backend may be required to perform CFI unwinding correctly. */ +extern int dwarf_cfi_setebl (Dwarf_CFI *cache, struct ebl *ebl); + + +/* Compute what's known about a call frame when the PC is at ADDRESS. + Returns 0 for success or -1 for errors. + On success, *FRAME is a malloc'd pointer. */ +extern int dwarf_cfi_addrframe (Dwarf_CFI *cache, + Dwarf_Addr address, Dwarf_Frame **frame) + __nonnull_attribute__ (3); + +/* Deliver a DWARF expression that yields the Canonical Frame Address at + this frame state. Returns -1 for errors, or the number of operations + stored at *OPS. That pointer can be used only as long as FRAME is alive + and unchanged. Returns zero if the CFA cannot be determined here. */ +extern int dwarf_frame_cfa (Dwarf_Frame *frame, Dwarf_Op **ops) + __nonnull_attribute__ (2); + +/* Return the DWARF register number used in FRAME to denote + the return address in FRAME's caller frame. + + Fill in *SIGNALP to indicate whether this is a signal-handling frame. + If true, this is the implicit call frame that calls a signal handler. + This frame's "caller" is actually the interrupted state, not a call; + its return address is an exact PC, not a PC after a call instruction. */ +extern int dwarf_frame_return_address_register (Dwarf_Frame *frame, + bool *signalp) + __nonnull_attribute__ (1, 2); + +/* Deliver a DWARF expression that yields the location or value of + DWARF register number REGNO in the state described by FRAME. + + Returns -1 for errors, 0 if REGNO has an accessible location, + or 1 if REGNO has only a computable value. Stores at *NOPS + the number of operations in the array stored at *OPS. + With return value 0, this is a DWARF location expression. + With return value 1, this is a DWARF expression that computes the value. + + Return value 1 with *NOPS zero means CFI says the caller's REGNO is + "undefined" here, i.e. it's call-clobbered and cannot be recovered. + + Return value 0 with *NOPS zero means CFI says the caller's REGNO is + "same_value" here, i.e. this frame did not change it; ask the caller + frame where to find it. + + For common simple expressions *OPS is OPS_MEM. For arbitrary DWARF + expressions in the CFI, *OPS is an internal pointer that can be used as + long as the Dwarf_CFI used to create FRAME remains alive. */ +extern int dwarf_frame_register (Dwarf_Frame *frame, int regno, + Dwarf_Op ops_mem[2], + Dwarf_Op **ops, size_t *nops) + __nonnull_attribute__ (3, 4, 5); + + +// XXX libdwfl front-end +#include "../libdwfl/libdwfl.h" + + +/* Find the CFI for this module. Returns NULL if there is no CFI. + On success, fills in *BIAS with the difference between addresses + within the loaded module and those in the CFI referring to it. + This does dwarf_cfi_setebl for you, so another call is not required. + The pointer returned can be used until the module is cleaned up. + Calling this more than once returns the same pointer. */ +extern Dwarf_CFI *dwfl_module_getcfi (Dwfl_Module *mod, Dwarf_Addr *bias); + +// XXX needs module bias? for DW_OP_addr in exprs? +/* Compute what's known about a call frame when the PC is at ADDRESS. + Returns 0 for success or -1 for errors. + On success, *FRAME is a malloc'd pointer. */ +extern int dwfl_addrframe (Dwfl *dwfl, Dwarf_Addr address, Dwarf_Frame **frame) + __nonnull_attribute__ (3); + +#endif /* XXX */ diff --git a/libdw/unwindP.h b/libdw/unwindP.h new file mode 100644 index 000000000..240388f7f --- /dev/null +++ b/libdw/unwindP.h @@ -0,0 +1,247 @@ +/* Internal definitions for libdw unwinder code. + Copyright (C) 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifndef _UNWINDP_H +#define _UNWINDP_H 1 + +#include "libdwP.h" +#include "unwind.h" /* XXX */ +struct ebl; + +/* Cached CIE representation. */ +struct dwarf_cie +{ + Dwarf_Off offset; /* Our position, as seen in FDEs' CIE_pointer. */ + + Dwarf_Word code_alignment_factor; + Dwarf_Sword data_alignment_factor; + Dwarf_Word return_address_register; + + size_t fde_augmentation_data_size; + + // play out to initial state + const uint8_t *initial_instructions; + const uint8_t *initial_instructions_end; + + Dwarf_Frame *initial_state; + + uint8_t fde_encoding; /* DW_EH_PE_* for addresses in FDEs. */ + uint8_t lsda_encoding; /* DW_EH_PE_* for LSDA in FDE augmentation. */ + + bool sized_augmentation_data; /* Saw 'z': FDEs have self-sized data. */ + bool signal_frame; /* Saw 'S': FDE is for a signal frame. */ +}; + +/* Cached FDE representation. */ +struct dwarf_fde +{ + struct dwarf_cie *cie; + + /* This FDE describes PC values in [start, end). */ + Dwarf_Addr start; + Dwarf_Addr end; + + const uint8_t *instructions; + const uint8_t *instructions_end; +}; + +/* This holds everything we cache about the CFI from each ELF file's + .debug_frame or .eh_frame section. */ +struct Dwarf_CFI_s +{ + /* Data of the .debug_frame or .eh_frame section. */ + Elf_Data data; + const unsigned char *e_ident; /* For EI_DATA and EI_CLASS. */ + + /* True if the file has a byte order different from the host. */ + bool other_byte_order; + + /* True if the section data is in .eh_frame format. */ + bool eh_frame; + + /* True if data.d_buf came from gelf_rawchunk. */ + bool rawchunk; + Elf *elf; /* Originating ELF file. */ + + Dwarf_Addr frame_vaddr; /* DW_EH_PE_pcrel, address of frame section. */ + Dwarf_Addr textrel; /* DW_EH_PE_textrel base address. */ + Dwarf_Addr datarel; /* DW_EH_PE_datarel base address. */ + + /* Default rule for registers not previously mentioned + is same_value, not undefined. */ + bool default_same_value; + + /* Location of next unread entry in the section. */ + Dwarf_Off next_offset; + + /* Search tree for the CIEs, indexed by CIE_pointer (section offset). */ + void *cie_tree; + + /* Search tree for the FDEs, indexed by PC address. */ + void *fde_tree; + + /* Search tree for parsed DWARF expressions, indexed by raw pointer. */ + void *expr_tree; + + /* Backend hook. */ + struct ebl *ebl; + + /* Binary search table in .eh_frame_hdr section. */ + const uint8_t *search_table; + Dwarf_Addr search_table_vaddr; + void *search_table_rawchunk; + size_t search_table_entries; + uint8_t search_table_encoding; +}; + + +enum dwarf_frame_rule + { + reg_unspecified, /* Uninitialized state. */ + reg_undefined, /* DW_CFA_undefined */ + reg_same_value, /* DW_CFA_same_value */ + reg_offset, /* DW_CFA_offset_extended et al */ + reg_val_offset, /* DW_CFA_val_offset et al */ + reg_register, /* DW_CFA_register */ + reg_expression, /* DW_CFA_expression */ + reg_val_expression, /* DW_CFA_val_expression */ + }; + +/* This describes what we know about an individual register. */ +struct dwarf_frame_register +{ + enum dwarf_frame_rule rule:3; + + /* The meaning of the value bits depends on the rule: + + Rule Value + ---- ----- + undefined unused + same_value unused + offset(N) N (register saved at CFA + value) + val_offset(N) N (register = CFA + value) + register(R) R (register = register #value) + expression(E) section offset of DW_FORM_block containing E + (register saved at address E computes) + val_expression(E) section offset of DW_FORM_block containing E + (register = value E computes) + */ + Dwarf_Sword value:(sizeof (Dwarf_Sword) * 8 - 3); +}; + +/* This holds everything we know about the state of the frame + at a particular PC location described by an FDE. */ +struct Dwarf_Frame_s +{ + Dwarf_CFI *cache; + + Dwarf_Frame *prev; + + /* The CIE we got this FDE from. + This has the return_address_register and signal_frame flag. */ + struct dwarf_cie *cie; + + /* The CFA is unknown, is R+N, or is computed by a DWARF expression. */ + enum { cfa_undefined, cfa_offset, cfa_expr } cfa_rule; + union + { + Dwarf_Op offset; + Dwarf_Block expr; + } cfa_data; + /* We store an offset rule as a DW_OP_bregx operation. */ +#define cfa_val_reg cfa_data.offset.number +#define cfa_val_offset cfa_data.offset.number2 + + size_t nregs; + struct dwarf_frame_register regs[]; +}; + + +/* Clean up the data structure and all it points to. */ +extern void __libdw_destroy_frame_cache (Dwarf_CFI *cache) + __nonnull_attribute__ (1) internal_function; + +/* Enter a CIE encountered while reading through for FDEs. */ +extern void __libdw_intern_cie (Dwarf_CFI *cache, Dwarf_Off offset, + const Dwarf_CIE *info) + __nonnull_attribute__ (1, 3) internal_function; + +/* Look up a CIE_pointer for random access. */ +extern struct dwarf_cie *__libdw_find_cie (Dwarf_CFI *cache, Dwarf_Off offset) + __nonnull_attribute__ (1) internal_function; + + +/* Look for an FDE covering the given PC address. */ +extern struct dwarf_fde *__libdw_find_fde (Dwarf_CFI *cache, + Dwarf_Addr address) + __nonnull_attribute__ (1) internal_function; + +/* Process the FDE that contains the given PC address, + to yield the frame state when stopped there. + The return value is a DWARF_E_* error code. */ +extern int __libdw_frame_at_address (Dwarf_CFI *cache, struct dwarf_fde *fde, + Dwarf_Addr address, Dwarf_Frame **frame) + __nonnull_attribute__ (1, 2, 4) internal_function; + + +/* Dummy struct for memory-access.h macros. */ +#define BYTE_ORDER_DUMMY(var, e_ident) \ + const struct { bool other_byte_order; } var = \ + { ((BYTE_ORDER == LITTLE_ENDIAN && e_ident[EI_DATA] == ELFDATA2MSB) \ + || (BYTE_ORDER == BIG_ENDIAN && e_ident[EI_DATA] == ELFDATA2LSB)) } + + +INTDECL (dwarf_next_cfi) +INTDECL (dwarf_getcfi) +INTDECL (dwarf_getcfi_elf) +INTDECL (dwarf_cfi_end) +INTDECL (dwarf_cfi_setebl) +INTDECL (dwarf_cfi_addrframe) + +#endif /* unwindP.h */ diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 41ff69bca..16248415c 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,11 @@ +2007-02-10 Roland McGrath + + * libdwflP.h (struct Dwfl_Module): New members `cfi', `cfi_elf'. + Add INTDECL for dwfl_module_getcfi. + * dwfl_module_getcfi.c: New file. + * dwfl_addrframe.c: New file. + * Makefile.am (libdwfl_a_SOURCES): Add them. + 2008-12-02 Roland McGrath * dwfl_getmodules.c (dwfl_getmodules): Typo fix in last change. @@ -555,7 +563,7 @@ 2006-09-05 Roland McGrath * derelocate.c (cache_sections): Use alloca instead of variable-sized - auto array, in function already alloca. + auto array, in function already using alloca. 2006-08-14 Roland McGrath diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am index db14db2a3..45dd0b3a5 100644 --- a/libdwfl/Makefile.am +++ b/libdwfl/Makefile.am @@ -73,8 +73,8 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \ dwfl_module_return_value_location.c \ dwfl_module_register_names.c \ dwfl_segment_report_module.c \ - link_map.c core-file.c - + link_map.c core-file.c \ + dwfl_module_getcfi.c dwfl_addrframe.c if MUDFLAP libdwfl = libdwfl.a $(libdw) $(libebl) $(libelf) $(libeu) diff --git a/libdwfl/dwfl_addrframe.c b/libdwfl/dwfl_addrframe.c new file mode 100644 index 000000000..62125feb9 --- /dev/null +++ b/libdwfl/dwfl_addrframe.c @@ -0,0 +1,69 @@ +/* + Copyright (C) 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#include "libdwflP.h" +#include "../libdw/unwindP.h" /* XXX */ + +int +dwfl_addrframe (dwfl, address, frame) + Dwfl *dwfl; + Dwarf_Addr address; + Dwarf_Frame **frame; +{ + Dwarf_Addr dwbias; + Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (dwfl, address); + Dwarf_CFI *cfi = INTUSE(dwfl_module_getcfi) (mod, &dwbias); + if (cfi == NULL) + return -1; + + int result = INTUSE(dwarf_cfi_addrframe) (cfi, address - dwbias, frame); + if (result != 0) + __libdwfl_seterrno (DWFL_E_LIBDW); + return result; +} diff --git a/libdwfl/dwfl_module_getcfi.c b/libdwfl/dwfl_module_getcfi.c new file mode 100644 index 000000000..816b49395 --- /dev/null +++ b/libdwfl/dwfl_module_getcfi.c @@ -0,0 +1,92 @@ +/* Find CFI for a module in libdwfl. + Copyright (C) 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#include "libdwflP.h" +#include "../libdw/unwindP.h" +#include "../libdw/unwind.h" /* XXX */ + +Dwarf_CFI * +dwfl_module_getcfi (mod, bias) + Dwfl_Module *mod; + Dwarf_Addr *bias; +{ + if (mod == NULL) + return NULL; + + if (mod->cfi == NULL) + { + Elf *elf = INTUSE(dwfl_module_getelf) (mod, bias); + if (elf != NULL) + { + Dwarf *dw = INTUSE(dwfl_module_getdwarf) (mod, bias); + mod->cfi_elf = dw == NULL; + mod->cfi = (mod->cfi_elf ? INTUSE(dwarf_getcfi_elf) (elf) + : INTUSE(dwarf_getcfi) (dw)); + if (mod->cfi == NULL) + __libdwfl_seterrno (DWFL_E_LIBDW); + } + + if (mod->cfi != NULL && mod->cfi->ebl == NULL) + { + Dwfl_Error error = __libdwfl_module_getebl (mod); + if (error == DWFL_E_NOERROR) + mod->cfi->ebl = mod->ebl; + else + { + if (mod->cfi_elf) + INTUSE(dwarf_cfi_end) (mod->cfi); + mod->cfi = NULL; + __libdwfl_seterrno (error); + } + } + } + + return mod->cfi; +} +INTDEF (dwfl_module_getcfi) diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 6ba5c96e8..625de0bf5 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -62,6 +62,7 @@ #include #include "../libdw/libdwP.h" /* We need its INTDECLs. */ +#include "../libdw/unwind.h" /* XXX */ /* gettext helper macros. */ #define _(Str) dgettext ("elfutils", Str) @@ -180,6 +181,9 @@ struct Dwfl_Module struct dwfl_arange *aranges; /* Mapping of addresses in module to CUs. */ unsigned int naranges; + Dwarf_CFI *cfi; /* Cached CFI for this module. */ + bool cfi_elf; /* cfi is from dwarf_getcfi_elf. */ + int segment; /* Index of first segment table entry. */ bool gc; /* Mark/sweep flag. */ }; @@ -403,6 +407,7 @@ INTDECL (dwfl_linux_kernel_report_modules) INTDECL (dwfl_linux_kernel_report_offline) INTDECL (dwfl_offline_section_address) INTDECL (dwfl_module_relocate_address) +INTDECL (dwfl_module_getcfi) /* Leading arguments standard to callbacks passed a Dwfl_Module. */ #define MODCB_ARGS(mod) (mod), &(mod)->userdata, (mod)->name, (mod)->low_addr diff --git a/libebl/ChangeLog b/libebl/ChangeLog index 51b3b705e..475fe6bd1 100644 --- a/libebl/ChangeLog +++ b/libebl/ChangeLog @@ -1,3 +1,12 @@ +2007-01-18 Roland McGrath + + * ebl-hooks.h: Add abi_cfi hook. + * eblopenbackend.c (default_abi_cfi): New function. + (fill_defaults): Add initializer. + * eblabicfi.c: New file. + * Makefile.am (gen_SOURCES): Add it. + * libebl.h: Declare ebl_abi_cfi. + 2008-08-01 Roland McGrath * eblcorenotetypename.c: Handle NT_386_IOPERM. diff --git a/libebl/Makefile.am b/libebl/Makefile.am index c4e4a0762..af3ae6a72 100644 --- a/libebl/Makefile.am +++ b/libebl/Makefile.am @@ -59,7 +59,7 @@ gen_SOURCES = eblopenbackend.c eblclosebackend.c eblstrtab.c \ ebl_check_special_symbol.c eblbsspltp.c eblretval.c \ eblreginfo.c eblnonerelocp.c eblrelativerelocp.c \ eblsysvhashentrysize.c eblauxvinfo.c eblcheckobjattr.c \ - ebl_check_special_section.c ebl_syscall_abi.c + ebl_check_special_section.c ebl_syscall_abi.c eblabicfi.c libebl_a_SOURCES = $(gen_SOURCES) diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h index 2db1e2087..fd328aee2 100644 --- a/libebl/ebl-hooks.h +++ b/libebl/ebl-hooks.h @@ -167,6 +167,8 @@ int EBLHOOK(disasm) (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, const char *fmt, DisasmOutputCB_t outcb, DisasmGetSymCB_t symcb, void *outcbarg, void *symcbarg); +/* Supply the machine-specific state of CFI before CIE initial programs. */ +int EBLHOOK(abi_cfi) (Ebl *ebl, Dwarf_CIE *abi_info); /* Destructor for ELF backend handle. */ void EBLHOOK(destr) (struct ebl *); diff --git a/libebl/eblabicfi.c b/libebl/eblabicfi.c new file mode 100644 index 000000000..fe5c7a5d6 --- /dev/null +++ b/libebl/eblabicfi.c @@ -0,0 +1,63 @@ +/* Return ABI-specific DWARF CFI details. + Copyright (C) 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + + +int +ebl_abi_cfi (ebl, abi_info) + Ebl *ebl; + Dwarf_CIE *abi_info; +{ + return ebl == NULL ? -1 : ebl->abi_cfi (ebl, abi_info); +} diff --git a/libebl/eblopenbackend.c b/libebl/eblopenbackend.c index 8cf421893..30383ed7e 100644 --- a/libebl/eblopenbackend.c +++ b/libebl/eblopenbackend.c @@ -218,6 +218,7 @@ static bool default_check_object_attribute (Ebl *ebl, const char *vendor, int tag, uint64_t value, const char **tag_name, const char **value_name); +static int default_abi_cfi (Ebl *ebl, Dwarf_CIE *abi_info); static void @@ -258,6 +259,7 @@ fill_defaults (Ebl *result) result->syscall_abi = default_syscall_abi; result->check_object_attribute = default_check_object_attribute; result->disasm = NULL; + result->abi_cfi = default_abi_cfi; result->destr = default_destr; result->sysvhash_entrysize = sizeof (Elf32_Word); } @@ -746,3 +748,10 @@ default_check_object_attribute (Ebl *ebl __attribute__ ((unused)), *value_name = NULL; return false; } + +static int +default_abi_cfi (Ebl *ebl __attribute__ ((unused)), + Dwarf_CIE *abi_info __attribute__ ((unused))) +{ + return 0; +} diff --git a/libebl/libebl.h b/libebl/libebl.h index 50258690c..11de24aa1 100644 --- a/libebl/libebl.h +++ b/libebl/libebl.h @@ -52,6 +52,7 @@ #include #include "libdw.h" +#include "unwind.h" /* XXX */ #include #include #include @@ -256,6 +257,38 @@ extern ssize_t ebl_register_info (Ebl *ebl, extern int ebl_syscall_abi (Ebl *ebl, int *sp, int *pc, int *callno, int args[6]); +/* Supply the ABI-specified state of DWARF CFI before CIE initial programs. + + The DWARF 3.0 spec says that the default initial states of all registers + are "undefined", unless otherwise specified by the machine/compiler ABI. + + This default is wrong for every machine with the CFI generated by GCC. + The EH unwinder does not really distinguish "same_value" and "undefined", + since it doesn't matter for unwinding (in either case there is no change + to make for that register). GCC generates CFI that says nothing at all + about registers it hasn't spilled somewhere. For our unwinder to give + the true story, the backend must supply an initial state that uses + "same_value" rules for all the callee-saves registers. + + This can fill in the initial_instructions, initial_instructions_end + members of *ABI_INFO to point at a CFI instruction stream to process + before each CIE's initial instructions. It should set the + data_alignment_factor member if it affects the initial instructions. + + As a shorthand for some common cases, for this instruction stream + we overload some CFI instructions that cannot be used in a CIE: + + DW_CFA_restore -- Change default rule for all unmentioned + registers from undefined to same_value. + + This function can also fill in ABI_INFO->return_address_register with the + DWARF register number that identifies the actual PC in machine state. + If there is no canonical DWARF register number with that meaning, it's + left unchanged (callers usually initialize with (Dwarf_Word) -1). + This value is not used by CFI per se. */ +extern int ebl_abi_cfi (Ebl *ebl, Dwarf_CIE *abi_info) + __nonnull_attribute__ (2); + /* ELF string table handling. */ struct Ebl_Strtab; struct Ebl_Strent; diff --git a/libebl/libeblP.h b/libebl/libeblP.h index 7bfa650f5..9bb886e83 100644 --- a/libebl/libeblP.h +++ b/libebl/libeblP.h @@ -94,4 +94,10 @@ typedef const char *(*ebl_bhinit_t) (Elf *, GElf_Half, Ebl *, size_t); #undef _ #define _(Str) dgettext ("elfutils", Str) + +/* LEB128 constant helper macros. */ +#define ULEB128_7(x) (BUILD_BUG_ON_ZERO ((x) >= (1U << 7)) + (x)) + +#define BUILD_BUG_ON_ZERO(x) (sizeof (char [(x) ? -1 : 1]) - 1) + #endif /* libeblP.h */ diff --git a/src/ChangeLog b/src/ChangeLog index fb17835fd..8ae0274ac 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,18 @@ +2007-02-17 Roland McGrath + + * readelf.c (parse_opt): Take "frames" as alias for "frame" in + --debug-dump argument, matching bintuils readelf. + (print_debug_frame_section): Implemented. + (print_debug): Call it on .eh_frame section even dwarf_begin_elf fails. + + * readelf.c (print_debug): Pass section name as new parameter to + print_debug_*_section functions. + (print_debug_abbrev_section): Take new argument. + (print_debug_aranges_section, print_debug_ranges_section) + (print_debug_info_section, print_debug_line_section): Likewise. + (print_debug_loc_section, print_debug_macinfo_section): Likewise. + (print_debug_pubnames_section, print_debug_str_section): Likewise. + 2008-12-02 Roland McGrath * readelf.c (count_dwflmod, process_file): Don't presume encoding of diff --git a/src/readelf.c b/src/readelf.c index 2797a8496..101f7ae33 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -57,6 +57,8 @@ #include "../libdw/libdwP.h" #include "../libdwfl/libdwflP.h" #include "../libdw/memory-access.h" +#include "../libdw/unwindP.h" +#include "../libdw/encoded-value.h" /* Name and version of program. */ @@ -344,7 +346,7 @@ parse_opt (int key, char *arg, print_debug_sections |= section_aranges; else if (strcmp (arg, "ranges") == 0) print_debug_sections |= section_ranges; - else if (strcmp (arg, "frame") == 0) + else if (strcmp (arg, "frame") == 0 || strcmp (arg, "frames") == 0) print_debug_sections |= section_frame; else if (strcmp (arg, "info") == 0) print_debug_sections |= section_info; @@ -3961,11 +3963,11 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), - GElf_Shdr *shdr, Dwarf *dbg) + GElf_Shdr *shdr, const char *secname, Dwarf *dbg) { printf (gettext ("\nDWARF section '%s' at offset %#" PRIx64 ":\n" " [ Code]\n"), - ".debug_abbrev", (uint64_t) shdr->sh_offset); + secname, (uint64_t) shdr->sh_offset); Dwarf_Off offset = 0; while (offset < shdr->sh_size) @@ -4033,14 +4035,14 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), - GElf_Shdr *shdr, Dwarf *dbg) + GElf_Shdr *shdr, const char *name, Dwarf *dbg) { Dwarf_Aranges *aranges; size_t cnt; if (unlikely (dwarf_getaranges (dbg, &aranges, &cnt) != 0)) { - error (0, 0, gettext ("cannot get .debug_aranges content: %s"), - dwarf_errmsg (-1)); + error (0, 0, gettext ("cannot get %s content: %s"), + name, dwarf_errmsg (-1)); return; } @@ -4049,7 +4051,7 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), "\ \nDWARF section '%s' at offset %#" PRIx64 " contains %zu entries:\n", cnt), - ".debug_aranges", (uint64_t) shdr->sh_offset, cnt); + name, (uint64_t) shdr->sh_offset, cnt); /* Compute floor(log16(cnt)). */ size_t tmp = cnt; @@ -4089,20 +4091,20 @@ static void print_debug_ranges_section (Dwfl_Module *dwflmod, Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr, - Dwarf *dbg) + const char *name, Dwarf *dbg) { Elf_Data *data = elf_rawdata (scn, NULL); if (unlikely (data == NULL)) { - error (0, 0, gettext ("cannot get .debug_ranges content: %s"), - elf_errmsg (-1)); + error (0, 0, gettext ("cannot get %s content: %s"), + name, elf_errmsg (-1)); return; } printf (gettext ("\ \nDWARF section '%s' at offset %#" PRIx64 ":\n"), - ".debug_ranges", (uint64_t) shdr->sh_offset); + name, (uint64_t) shdr->sh_offset); size_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; @@ -4158,6 +4160,271 @@ print_debug_ranges_section (Dwfl_Module *dwflmod, } } +static void +print_cfi (unsigned int indent, Dwarf *dbg, Dwarf_CFI *cfi, + unsigned int addrsize, uint8_t fde_encoding, + const uint8_t *program, const uint8_t *program_end, + Dwarf_Addr loc) +{ + static const char *cfi_extended[] = + { + [DW_CFA_nop] = "nop", + [DW_CFA_set_loc] = "set_loc", + [DW_CFA_advance_loc1] = "advance_loc1", + [DW_CFA_advance_loc2] = "advance_loc2", + [DW_CFA_advance_loc4] = "advance_loc4", + [DW_CFA_offset_extended] = "offset_extended", + [DW_CFA_restore_extended] = "restore_extended", + [DW_CFA_undefined] = "undefined", + [DW_CFA_same_value] = "same_value", + [DW_CFA_register] = "register", + [DW_CFA_remember_state] = "remember_state", + [DW_CFA_restore_state] = "restore_state", + [DW_CFA_def_cfa] = "def_cfa", + [DW_CFA_def_cfa_register] = "def_cfa_register", + [DW_CFA_def_cfa_offset] = "def_cfa_offset", + [DW_CFA_def_cfa_expression] = "def_cfa_expression", + [DW_CFA_expression] = "expression", + [DW_CFA_offset_extended_sf] = "offset_extended_sf", + [DW_CFA_def_cfa_sf] = "def_cfa_sf", + [DW_CFA_def_cfa_offset_sf] = "def_cfa_offset_sf", + [DW_CFA_val_offset] = "val_offset", + [DW_CFA_val_offset_sf] = "val_offset_sf", + [DW_CFA_val_expression] = "val_expression", + + [DW_CFA_lo_user] = "lo_user", + [DW_CFA_MIPS_advance_loc8] = "MIPS_advance_loc8", + [DW_CFA_GNU_window_save] = "GNU_window_save", + [DW_CFA_GNU_args_size] = "GNU_args_size", + [DW_CFA_hi_user] = "hi_user" + }; + +#define REGFMT "r%" PRIu64 + + while (program < program_end) + { + uint8_t opcode = *program++; + uint8_t ophi = opcode & 0xc0; + uint8_t oplo = opcode & 0x3f; + Dwarf_Word operand = oplo; + + switch (ophi) + { + case DW_CFA_advance_loc: + printf ("%*s%s + %#x", indent, "", "advance_loc", oplo); + goto advance_loc; + case DW_CFA_offset: + printf ("%*s%s + %#x", indent, "", "offset", oplo); + goto offset; + case DW_CFA_restore: + printf ("%*s%s + %#x", indent, "", "restore", oplo); + goto restore; + case DW_CFA_extended: + break; + } + + if (cfi_extended[oplo] != NULL) + printf ("%*s%s", indent, "", cfi_extended[oplo]); + else if (oplo >= DW_CFA_lo_user && oplo < DW_CFA_hi_user) + printf ("%*s%s + %#x", indent, "", "lo_user", oplo); + else + printf ("%*s??? %#x", indent, "", oplo); + + Dwarf_Word offset; + Dwarf_Sword sf_offset; + Dwarf_Word regno; + switch (opcode) + { + case DW_CFA_advance_loc1: + operand = *program++; + advance_loc: + printf (gettext (": advance location %" PRIu64 + " * code_alignment_factor\n"), + operand); + break; + + case DW_CFA_advance_loc2: + operand = read_2ubyte_unaligned_inc (dbg, program); + goto advance_loc; + case DW_CFA_advance_loc4: + operand = read_4ubyte_unaligned_inc (dbg, program); + goto advance_loc; + case DW_CFA_MIPS_advance_loc8: + operand = read_8ubyte_unaligned_inc (dbg, program); + goto advance_loc; + + case DW_CFA_set_loc: + if (cfi == NULL) + { + puts (gettext (": invalid in CIE initial instructions")); + program += addrsize; + } + else + { + loc = read_encoded_value (cfi, fde_encoding, &program); + printf (gettext (": set location to %#" PRIx64 "\n"), loc); + } + break; + + case DW_CFA_def_cfa: + get_uleb128 (operand, program); + get_uleb128 (offset, program); + printf (gettext (": CFA is " REGFMT " + %#" PRIx64 "\n"), + operand, offset); + break; + + case DW_CFA_def_cfa_register: + get_uleb128 (regno, program); + printf (gettext (": CFA is " REGFMT " + old offset\n"), regno); + break; + + case DW_CFA_def_cfa_sf: + get_uleb128 (operand, program); + get_sleb128 (sf_offset, program); + printf (gettext (": CFA is " REGFMT + " + %" PRId64 " * data_alignment_factor\n"), + operand, sf_offset); + break; + + case DW_CFA_def_cfa_offset: + get_uleb128 (offset, program); + printf (gettext (": CFA is old register + %#" PRIx64 "\n"), offset); + break; + + case DW_CFA_def_cfa_offset_sf: + get_sleb128 (sf_offset, program); + printf (gettext (": CFA is old register" + " + %" PRId64 " * data_alignment_factor\n"), + sf_offset); + break; + + case DW_CFA_def_cfa_expression: + /* DW_FORM_block is a ULEB128 length followed by that many bytes. */ + get_uleb128 (operand, program); + if ((Dwarf_Word) (program_end - program) < operand) + { + printf (gettext (": invalid expression length %" PRIu64 + " (> %tu)\n"), operand, program_end - program); + program = program_end; + } + else + { + size_t len = indent + printf (gettext (": CFA computed by:\n")); + print_ops (dbg, len, len, addrsize, operand, program); + program += operand; + } + break; + + case DW_CFA_undefined: + get_uleb128 (operand, program); + printf (gettext (": " REGFMT " is undefined\n"), operand); + break; + + case DW_CFA_same_value: + get_uleb128 (operand, program); + printf (gettext (": " REGFMT " has caller's value\n"), operand); + break; + + case DW_CFA_offset_extended: + get_uleb128 (operand, program); + offset: + get_uleb128 (offset, program); + printf (gettext (": " REGFMT " at CFA" + " + %" PRId64 " * data_alignment_factor\n"), + operand, offset); + break; + + case DW_CFA_offset_extended_sf: + get_uleb128 (operand, program); + get_sleb128 (sf_offset, program); + printf (gettext (": " REGFMT " saved at CFA" + " + %" PRId64 " * data_alignment_factor\n"), + operand, sf_offset); + break; + + case DW_CFA_val_offset: + get_uleb128 (operand, program); + get_uleb128 (offset, program); + printf (gettext (": " REGFMT " has value CFA" + " + %" PRIu64 " * data_alignment_factor\n"), + operand, offset); + break; + + case DW_CFA_val_offset_sf: + get_uleb128 (operand, program); + get_sleb128 (sf_offset, program); + printf (gettext (": " REGFMT " has value CFA" + " + %" PRId64 " * data_alignment_factor\n"), + operand, sf_offset); + break; + + case DW_CFA_register: + get_uleb128 (regno, program); + get_uleb128 (operand, program); + printf (gettext (": " REGFMT " saved in " REGFMT "\n"), + regno, operand); + break; + + case DW_CFA_expression: + get_uleb128 (regno, program); + /* DW_FORM_block is a ULEB128 length followed by that many bytes. */ + get_uleb128 (operand, program); + if ((Dwarf_Word) (program_end - program) < operand) + { + printf (gettext (": invalid expression length %" PRIu64 + " (> %tu)\n"), operand, program_end - program); + program = program_end; + } + else + { + size_t len = indent + printf (gettext (": " REGFMT + " saved at:\n"), regno); + print_ops (dbg, len, len, addrsize, operand, program); + program += operand; + } + break; + + case DW_CFA_val_expression: + get_uleb128 (regno, program); + /* DW_FORM_block is a ULEB128 length followed by that many bytes. */ + get_uleb128 (operand, program); + if ((Dwarf_Word) (program_end - program) < operand) + { + printf (gettext (": invalid expression length %" PRIu64 + " (> %tu)\n"), operand, program_end - program); + program = program_end; + } + else + { + size_t len = indent + printf (gettext (": " REGFMT + " value is:\n"), regno); + print_ops (dbg, len, len, addrsize, operand, program); + program += operand; + } + break; + + case DW_CFA_restore_extended: + get_uleb128 (operand, program); + restore: + printf (gettext ("restore " REGFMT " to CIE initial state\n"), + operand); + break; + + case DW_CFA_GNU_args_size: + get_uleb128 (operand, program); + printf (gettext ("argument size %" PRIu64 "\n"), operand); + break; + + case DW_CFA_GNU_window_save: + case DW_CFA_remember_state: + case DW_CFA_restore_state: + case DW_CFA_nop: + default: + puts (""); + break; + } + } +} static void print_debug_frame_section (Dwfl_Module *dwflmod __attribute__ ((unused)), @@ -4165,8 +4432,143 @@ print_debug_frame_section (Dwfl_Module *dwflmod __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), GElf_Shdr *shdr __attribute__ ((unused)), + const char *name, Dwarf *dbg __attribute__ ((unused))) { + Elf_Data *data = elf_rawdata (scn, NULL); + + if (data == NULL) + { + error (0, 0, gettext ("cannot get %s content: %s"), + name, elf_errmsg (-1)); + return; + } + + printf (gettext ("\ +\nDWARF section '%s' at offset %#" PRIx64 ":\n"), + name, (uint64_t) shdr->sh_offset); + + bool eh_frame_p = !strcmp (name, ".eh_frame"); + + /* Dummy struct for memory-access.h macros. */ + Dwarf dw = + { + .other_byte_order = ((BYTE_ORDER == LITTLE_ENDIAN + && ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + || (BYTE_ORDER == BIG_ENDIAN + && ehdr->e_ident[EI_DATA] == ELFDATA2LSB)) + }; + if (dbg == NULL) + dbg = &dw; + else + assert (dw.other_byte_order == dbg->other_byte_order); + + unsigned int addrsize = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + + Dwarf_Off last_offset; + Dwarf_Off offset = 0; + uint8_t fde_encoding = DW_EH_PE_omit; + size_t fde_augmentation_size = 0; + Dwarf_Off last_cie = CIE_ID; + do + { + last_offset = offset; + Dwarf_CFI_Entry entry; + int result = dwarf_next_cfi (ehdr->e_ident, data, eh_frame_p, + offset, &offset, &entry); + if (result > 0) + /* No more entries. */ + break; + if (result < 0) + printf ("cannot read CFI at offset %#" PRIx64 ": %s\n", + (uint64_t) last_offset, dwarf_errmsg (-1)); + else if (dwarf_cfi_cie_p (&entry)) + { /* CIE */ + printf (gettext (" [%6" PRIx64 "] CIE, augmentation: \"%s\"" + " (data bytes: %zu)\n"), + last_offset, entry.cie.augmentation, + entry.cie.augmentation_data_size); + printf (gettext (" code_alignment_factor: %" PRIu64 ", " + "data_alignment_factor: %" PRId64 "\n"), + entry.cie.code_alignment_factor, + entry.cie.data_alignment_factor); + printf (gettext (" return_address_register: %" PRIu64 "\n"), + entry.cie.return_address_register); + printf (gettext (" initial_instructions:\n")); + print_cfi (12, dbg, NULL, addrsize, DW_EH_PE_omit, + entry.cie.initial_instructions, + entry.cie.initial_instructions_end, + 0); + } + else + { /* FDE */ + printf (gettext (" [%6" PRIx64 "] FDE, " + "CIE_pointer: [%6" PRIx64 "]\n"), + last_offset, entry.fde.CIE_pointer); + + if (entry.fde.CIE_pointer != last_cie + || fde_encoding == DW_EH_PE_omit) + { + /* We have to go find the referenced CIE to be sure of the + encodings used in this FDE. */ + + Dwarf_CFI_Entry cie_entry; + Dwarf_Off next; + result = dwarf_next_cfi (ehdr->e_ident, data, eh_frame_p, + entry.fde.CIE_pointer, + &next, &cie_entry); + if (result != 0 || !dwarf_cfi_cie_p (&cie_entry)) + continue; + + last_cie = entry.fde.CIE_pointer; + fde_encoding = DW_EH_PE_absptr; + const char *p = cie_entry.cie.augmentation; + const uint8_t *dp = cie_entry.cie.augmentation_data; + while (*p != '\0') + { + switch (*p++) + { + case 'R': + fde_encoding = *dp++; + break; + case 'L': /* Skip LSDA pointer encoding byte. */ + ++dp; + continue; + case 'P': /* Skip encoded personality routine pointer. */ + dp += 1 + encoded_value_size (data, ehdr->e_ident, + *dp, dp + 1); + continue; + default: + continue; + } + break; + } + + fde_augmentation_size = cie_entry.cie.fde_augmentation_data_size; + } + + Dwarf_CFI *cfi = (eh_frame_p ? dwarf_getcfi_elf (ebl->elf) + : dwarf_getcfi (dbg)); + if (cfi == NULL) + continue; + + Dwarf_Addr initial_location = read_encoded_value (cfi, + fde_encoding, + &entry.fde.start); + Dwarf_Addr address_range = read_encoded_value (cfi, + fde_encoding & 0x0f, + &entry.fde.start); + entry.fde.start += fde_augmentation_size; + + printf (gettext (" initial_location: %#" PRIx64 ", " + "address_range: %#" PRIx64 "\n"), + initial_location, address_range); + + printf (gettext (" instructions:\n")); + print_cfi (12, dbg, cfi, addrsize, fde_encoding, + entry.fde.start, entry.fde.end, initial_location); + } + } while (offset != last_offset); } @@ -4390,11 +4792,11 @@ print_debug_info_section (Dwfl_Module *dwflmod, Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), - GElf_Shdr *shdr, Dwarf *dbg) + GElf_Shdr *shdr, const char *name, Dwarf *dbg) { printf (gettext ("\ \nDWARF section '%s' at offset %#" PRIx64 ":\n [Offset]\n"), - ".debug_info", (uint64_t) shdr->sh_offset); + name, (uint64_t) shdr->sh_offset); /* If the section is empty we don't have to do anything. */ if (shdr->sh_size == 0) @@ -4437,7 +4839,7 @@ print_debug_info_section (Dwfl_Module *dwflmod, { error (0, 0, gettext ("cannot get DIE at offset %" PRIu64 " in section '%s': %s"), - (uint64_t) offset, ".debug_info", dwarf_errmsg (-1)); + (uint64_t) offset, name, dwarf_errmsg (-1)); goto do_return; } @@ -4456,7 +4858,7 @@ print_debug_info_section (Dwfl_Module *dwflmod, { error (0, 0, gettext ("cannot get tag of DIE at offset %" PRIu64 " in section '%s': %s"), - (uint64_t) offset, ".debug_info", dwarf_errmsg (-1)); + (uint64_t) offset, name, dwarf_errmsg (-1)); goto do_return; } @@ -4511,11 +4913,12 @@ print_debug_info_section (Dwfl_Module *dwflmod, static void print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr __attribute__ ((unused)), - Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) + Elf_Scn *scn, GElf_Shdr *shdr, const char *name, + Dwarf *dbg) { printf (gettext ("\ \nDWARF section '%s' at offset %#" PRIx64 ":\n"), - ".debug_line", (uint64_t) shdr->sh_offset); + name, (uint64_t) shdr->sh_offset); if (shdr->sh_size == 0) return; @@ -4548,7 +4951,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, { invalid_data: error (0, 0, gettext ("invalid data in section [%zu] '%s'"), - elf_ndxscn (scn), ".debug_line"); + elf_ndxscn (scn), name); return; } unit_length = read_8ubyte_unaligned_inc (dbg, linep); @@ -4949,21 +5352,21 @@ print_debug_loc_section (Dwfl_Module *dwflmod, Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), - GElf_Shdr *shdr, + GElf_Shdr *shdr, const char *name, Dwarf *dbg __attribute__ ((unused))) { Elf_Data *data = elf_rawdata (scn, NULL); if (unlikely (data == NULL)) { - error (0, 0, gettext ("cannot get .debug_loc content: %s"), - elf_errmsg (-1)); + error (0, 0, gettext ("cannot get %s content: %s"), + name, elf_errmsg (-1)); return; } printf (gettext ("\ \nDWARF section '%s' at offset %#" PRIx64 ":\n"), - ".debug_loc", (uint64_t) shdr->sh_offset); + name, (uint64_t) shdr->sh_offset); size_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; @@ -5054,11 +5457,12 @@ static void print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), - Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg) + Elf_Scn *scn, GElf_Shdr *shdr, const char *name, + Dwarf *dbg) { printf (gettext ("\ \nDWARF section '%s' at offset %#" PRIx64 ":\n"), - ".debug_macinfo", (uint64_t) shdr->sh_offset); + name, (uint64_t) shdr->sh_offset); putc_unlocked ('\n', stdout); /* There is no function in libdw to iterate over the raw content of @@ -5227,10 +5631,10 @@ print_debug_pubnames_section (Dwfl_Module *dwflmod __attribute__ ((unused)), Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), - GElf_Shdr *shdr, Dwarf *dbg) + GElf_Shdr *shdr, const char *name, Dwarf *dbg) { printf (gettext ("\nDWARF section '%s' at offset %#" PRIx64 ":\n"), - ".debug_pubnames", (uint64_t) shdr->sh_offset); + name, (uint64_t) shdr->sh_offset); int n = 0; (void) dwarf_getpubnames (dbg, print_pubnames, &n, 0); @@ -5242,7 +5646,7 @@ print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), Ebl *ebl __attribute__ ((unused)), GElf_Ehdr *ehdr __attribute__ ((unused)), Elf_Scn *scn __attribute__ ((unused)), - GElf_Shdr *shdr, Dwarf *dbg) + GElf_Shdr *shdr, const char *name, Dwarf *dbg) { /* Compute floor(log16(shdr->sh_size)). */ GElf_Addr tmp = shdr->sh_size; @@ -5256,7 +5660,7 @@ print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), printf (gettext ("\nDWARF section '%s' at offset %#" PRIx64 ":\n" " %*s String\n"), - ".debug_str", (uint64_t) shdr->sh_offset, + name, (uint64_t) shdr->sh_offset, /* TRANS: the debugstr| prefix makes the string unique. */ digits + 2, sgettext ("debugstr|Offset")); @@ -5281,6 +5685,12 @@ print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), static void print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) { + /* Find the debug information sections. For this we have to search + through the section table. */ + Dwarf *dbg; + Elf_Scn *scn; + size_t shstrndx; + /* Before we start the real work get a debug context descriptor. */ Dwarf_Addr dwbias; Dwarf *dbg = dwfl_module_getdwarf (dwflmod, &dwbias); @@ -5310,8 +5720,8 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) { const char *name; enum section_e bitmask; - void (*fp) (Dwfl_Module *, Ebl *, - GElf_Ehdr *, Elf_Scn *, GElf_Shdr *, Dwarf *); + void (*fp) (Dwfl_Module *, Ebl *, GElf_Ehdr *, + Elf_Scn *, GElf_Shdr *, const char *, Dwarf *); } debug_sections[] = { #define NEW_SECTION(name) \ @@ -5326,7 +5736,6 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) NEW_SECTION (str), NEW_SECTION (macinfo), NEW_SECTION (ranges), - { ".eh_frame", section_frame, print_debug_frame_section } }; const int ndebug_sections = (sizeof (debug_sections) / sizeof (debug_sections[0])); @@ -5334,11 +5743,19 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) shdr->sh_name); int n; + if (strcmp (name, ".eh_frame") == 0) + { + if (print_debug_sections & section_frame) + print_debug_frame_section (ebl, ehdr, scn, shdr, name, dbg); + continue; + } + for (n = 0; n < ndebug_sections; ++n) if (strcmp (name, debug_sections[n].name) == 0) { if (print_debug_sections & debug_sections[n].bitmask) - debug_sections[n].fp (dwflmod, ebl, ehdr, scn, shdr, dbg); + debug_sections[n].fp (dwflmod, ebl, ehdr, + scn, shdr, name, dbg); break; } } diff --git a/tests/ChangeLog b/tests/ChangeLog index b1471c1b0..876ae24b6 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,9 @@ +2007-02-10 Roland McGrath + + * addrcfi.c: New file. + * Makefile.am (noinst_PROGRAMS): Add it. + (addrcfi_LDADD): New variable. + 2008-11-26 Roland McGrath * dwfl-bug-getmodules.c: New file. diff --git a/tests/Makefile.am b/tests/Makefile.am index b533521c5..90b593d39 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -60,7 +60,7 @@ noinst_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ find-prologues funcretval allregs rdwrmmap \ dwfl-bug-addr-overflow arls dwfl-bug-fd-leak \ dwfl-addr-sect dwfl-bug-report early-offscn \ - dwfl-bug-getmodules + dwfl-bug-getmodules addrcfi # get-ciefde asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ asm-tst6 asm-tst7 asm-tst8 asm-tst9 @@ -233,6 +233,7 @@ dwfl_bug_report_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl dwfl_bug_getmodules_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl dwfl_addr_sect_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl sha1_tst_LDADD = $(libeu) $(libmudflap) +addrcfi_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl CLEANFILES = xxx *.gcno *.gcda *gconv diff --git a/tests/addrcfi.c b/tests/addrcfi.c new file mode 100644 index 000000000..79e31e295 --- /dev/null +++ b/tests/addrcfi.c @@ -0,0 +1,175 @@ +/* Test program for CFI handling. + Copyright (C) 2006 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#include +#include +#include +#include ELFUTILS_HEADER(dwfl) +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../libdw/unwind.h" /* XXX */ + +static void +print_detail (int result, const Dwarf_Op *ops, size_t nops) +{ + if (result < 0) + printf ("indeterminate (%s)\n", dwarf_errmsg (-1)); + else if (nops == 0) + printf ("%s\n", result == 0 ? "same_value" : "undefined"); + else + { + printf ("%s expression:", result == 0 ? "location" : "value"); + for (size_t i = 0; i < nops; ++i) + { + printf (" %#x", ops[i].atom); + if (ops[i].number2 == 0) + { + if (ops[i].number != 0) + printf ("(%" PRId64 ")", ops[i].number); + } + else + printf ("(%" PRId64 ",%" PRId64 ")", + ops[i].number, ops[i].number2); + } + puts (""); + } +} + +static int +print_register (void *arg, + int regno, + const char *setname, + const char *prefix, + const char *regname, + int bits __attribute__ ((unused)), + int type __attribute__ ((unused))) +{ + Dwarf_Frame *frame = arg; + + printf ("\t%s reg%u (%s%s): ", setname, regno, prefix, regname); + + Dwarf_Op ops_mem[2]; + Dwarf_Op *ops; + size_t nops; + int result = dwarf_frame_register (frame, regno, ops_mem, &ops, &nops); + print_detail (result, ops, nops); + + return DWARF_CB_OK; +} + +static void +handle_address (GElf_Addr pc, Dwfl *dwfl) +{ + Dwarf_Frame *frame; + int result = dwfl_addrframe (dwfl, pc, &frame); + if (result != 0) + error (EXIT_FAILURE, 0, "dwfl_addrframe: %s", dwfl_errmsg (-1)); + + printf ("%#" PRIx64 ":\n", pc); + + bool signalp; + int ra_regno = dwarf_frame_return_address_register (frame, &signalp); + if (ra_regno < 0) + printf ("\treturn address register unavailable (%s)\n", + dwarf_errmsg (0)); + else + printf ("\treturn address in reg%u%s\n", + ra_regno, signalp ? " (signal frame)" : ""); + + Dwarf_Op *cfa_ops; + int cfa_nops = dwarf_frame_cfa (frame, &cfa_ops); + if (cfa_nops < 0) + error (EXIT_FAILURE, 0, "dwarf_frame_cfa: %s", dwarf_errmsg (-1)); + + printf ("\tCFA "); + print_detail (1, cfa_ops, cfa_nops); + + (void) dwfl_module_register_names (dwfl_addrmodule (dwfl, pc), + &print_register, frame); +} + +int +main (int argc, char *argv[]) +{ + int remaining; + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + Dwfl *dwfl = NULL; + (void) argp_parse (dwfl_standard_argp (), argc, argv, 0, &remaining, &dwfl); + assert (dwfl != NULL); + + int result = 0; + + /* Now handle the addresses. In case none are given on the command + line, read from stdin. */ + if (remaining == argc) + { + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER); + + char *buf = NULL; + size_t len = 0; + while (!feof_unlocked (stdin)) + { + if (getline (&buf, &len, stdin) < 0) + break; + + char *endp; + uintmax_t addr = strtoumax (buf, &endp, 0); + if (endp != buf) + handle_address (addr, dwfl); + else + result = 1; + } + + free (buf); + } + else + { + do + { + char *endp; + uintmax_t addr = strtoumax (argv[remaining], &endp, 0); + if (endp != argv[remaining]) + handle_address (addr, dwfl); + else + result = 1; + } + while (++remaining < argc); + } + + dwfl_end (dwfl); + + return result; +}