From e94b0d782e087b5a8b1b008082be07139ee780c4 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Tue, 22 Jun 2010 22:13:01 -0700 Subject: [PATCH] More relocatable libdw interfaces. --- libdw/ChangeLog | 21 ++- libdw/Makefile.am | 4 +- libdw/dwarf_form_relocatable.c | 1 + libdw/dwarf_getlocation.c | 48 ++--- libdw/dwarf_getlocation_relocatable.c | 112 +++++++++++ libdw/dwarf_haspc_relocatable.c | 111 +++++++++++ libdw/dwarf_ranges_relocatable.c | 256 ++++++++++++++++++++++++++ libdw/dwarf_relocatable_info.c | 98 ++++++++-- libdw/libdw.h | 27 ++- libdw/libdw.map | 3 + libdw/libdwP.h | 14 +- libdw/memory-access.h | 8 + libdw/relocate.c | 145 +++------------ libdw/relocate.h | 96 ++++++++++ 14 files changed, 780 insertions(+), 164 deletions(-) create mode 100644 libdw/dwarf_getlocation_relocatable.c create mode 100644 libdw/dwarf_haspc_relocatable.c create mode 100644 libdw/dwarf_ranges_relocatable.c create mode 100644 libdw/relocate.h diff --git a/libdw/ChangeLog b/libdw/ChangeLog index e3b631bc4..486c7e4a7 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,14 +1,31 @@ 2010-06-22 Roland McGrath + * dwarf_ranges_relocatable.c: New file. + * dwarf_getlocation_relocatable.c: New file. + * dwarf_haspc_relocatable.c: New file. + * Makefile.am (libdw_a_SOURCES): Add them. + * libdw.map (ELFUTILS_0.149): Add them. + * libdw.h: Declare them. + * libdwP.h: Add INTDECLs. + + * dwarf_getlocation.c (getlocation): Renamed to ... + (__libdw_getlocation): ... this; made global. + (dwarf_getlocation, dwarf_getlocation_addr): Update callers. + * libdwP.h: Declare it. + + * relocate.h: New file. + * relocate.c: Move structs there. + (relocatable_datum): Renamed to ... + (__libdw_relocatable): ... here, made global. * libdw.h (Dwarf_Relocatable): New type. Declare three new functions. * dwarf_relocatable_info.c: New file. * dwarf_form_relocatable.c: New file. * dwarf_line_relocatable.c: New file. * Makefile.am (libdw_a_SOURCES): Add them. * libdw.map (ELFUTILS_0.149): New set; add those. + * libdwP.h: Add an INTDECL. - * relocate.c (__libdw_relocatable): New function. - * libdwP.h: Declare it. + * memory-access.h (read_8ubyte_unaligned_noncvt): New function/macro. * dwarf_getlocation.c (check_constant_offset): data[48] are constant. diff --git a/libdw/Makefile.am b/libdw/Makefile.am index 146d7f442..e77ea5f4c 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -86,7 +86,9 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_getcfi.c dwarf_getcfi_elf.c dwarf_cfi_end.c \ dwarf_aggregate_size.c \ relocate.c dwarf_relocatable_info.c \ - dwarf_form_relocatable.c dwarf_line_relocatable.c + dwarf_form_relocatable.c dwarf_line_relocatable.c \ + dwarf_ranges_relocatable.c dwarf_getlocation_relocatable.c \ + dwarf_haspc_relocatable.c if MAINTAINER_MODE BUILT_SOURCES = $(srcdir)/known-dwarf.h diff --git a/libdw/dwarf_form_relocatable.c b/libdw/dwarf_form_relocatable.c index 3b2dc9c2d..ee00522a8 100644 --- a/libdw/dwarf_form_relocatable.c +++ b/libdw/dwarf_form_relocatable.c @@ -71,3 +71,4 @@ dwarf_form_relocatable (attr, reloc) return 0; } +INTDEF (dwarf_form_relocatable) diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index e960ef9e9..fa188dcc9 100644 --- a/libdw/dwarf_getlocation.c +++ b/libdw/dwarf_getlocation.c @@ -517,10 +517,12 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, return 0; } -static int -getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, - Dwarf_Op **llbuf, size_t *listlen, int sec_index) +int +internal_function +__libdw_getlocation (Dwarf_Attribute *attr, const Dwarf_Block *block, + Dwarf_Op **llbuf, size_t *listlen, int sec_index) { + struct Dwarf_CU *cu = attr->cu; return __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order, cu->address_size, (cu->version == 2 ? cu->address_size @@ -548,7 +550,8 @@ dwarf_getlocation (attr, llbuf, listlen) if (INTUSE(dwarf_formblock) (attr, &block) != 0) return -1; - return getlocation (attr->cu, &block, llbuf, listlen, cu_sec_idx (attr->cu)); + return __libdw_getlocation (attr, &block, llbuf, listlen, + cu_sec_idx (attr->cu)); } int @@ -567,22 +570,25 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs) /* If it has a block form, it's a single location expression. */ Dwarf_Block block; - if (INTUSE(dwarf_formblock) (attr, &block) == 0) + if (attr->cu->version < 4 || attr->form == DW_FORM_exprloc) { - if (maxlocs == 0) - return 0; - if (llbufs != NULL && - getlocation (attr->cu, &block, &llbufs[0], &listlens[0], - cu_sec_idx (attr->cu)) != 0) - return -1; - return listlens[0] == 0 ? 0 : 1; - } + if (INTUSE(dwarf_formblock) (attr, &block) == 0) + { + if (maxlocs == 0) + return 0; + if (llbufs != NULL && + __libdw_getlocation (attr, &block, &llbufs[0], &listlens[0], + cu_sec_idx (attr->cu)) != 0) + return -1; + return listlens[0] == 0 ? 0 : 1; + } - int error = INTUSE(dwarf_errno) (); - if (unlikely (error != DWARF_E_NO_BLOCK)) - { - __libdw_seterrno (error); - return -1; + int error = INTUSE(dwarf_errno) (); + if (unlikely (error != DWARF_E_NO_BLOCK)) + { + __libdw_seterrno (error); + return -1; + } } int result = check_constant_offset (attr, &llbufs[0], &listlens[0]); @@ -661,9 +667,9 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs) { /* This one matches the address. */ if (llbufs != NULL - && unlikely (getlocation (attr->cu, &block, - &llbufs[got], &listlens[got], - IDX_debug_loc) != 0)) + && unlikely (__libdw_getlocation (attr, &block, + &llbufs[got], &listlens[got], + IDX_debug_loc) != 0)) return -1; ++got; } diff --git a/libdw/dwarf_getlocation_relocatable.c b/libdw/dwarf_getlocation_relocatable.c new file mode 100644 index 000000000..a9bf025b3 --- /dev/null +++ b/libdw/dwarf_getlocation_relocatable.c @@ -0,0 +1,112 @@ +/* Enumerate the PC ranges covered by a location list. + Copyright (C) 2010 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 "relocate.h" +#include +#include + + +ptrdiff_t +dwarf_getlocation_relocatable (Dwarf_Attribute *attr, ptrdiff_t offset, + Dwarf_Relocatable *basep, + Dwarf_Relocatable *startp, + Dwarf_Relocatable *endp, + Dwarf_Op **expr, size_t *exprlen) +{ + if (attr == NULL) + return -1; + + unsigned int sec_idx = IDX_debug_loc; + Dwarf_Block block; + if (offset == 0) + switch (attr->form) + { + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + if (unlikely (attr->cu->version >= 4)) + { + __libdw_seterrno (DWARF_E_NO_LOCLIST); + return -1; + } + + case DW_FORM_exprloc: + if (unlikely (INTUSE(dwarf_formblock) (attr, &block) < 0)) + return -1; + + sec_idx = cu_sec_idx (attr->cu); + *startp = (Dwarf_Relocatable) { .adjust = 0 }; + *endp = (Dwarf_Relocatable) { .adjust = (Dwarf_Addr) -1 }; + + /* A offset into .debug_loc will never be 1, it must be at least a + multiple of 4. So we can return 1 as a special case value to + mark there are no ranges to look for on the next call. */ + offset = 1; + break; + } + else if (offset == 1) + return 0; + + if (offset != 1) + /* Iterate from last position. */ + offset = __libdw_ranges_relocatable (attr->cu, attr, offset, + basep, startp, endp, &block); + + /* Parse the block into internal form. */ + if (offset > 0 && expr != NULL + && __libdw_getlocation (attr, &block, expr, exprlen, sec_idx) < 0) + offset = -1; + + return offset; +} diff --git a/libdw/dwarf_haspc_relocatable.c b/libdw/dwarf_haspc_relocatable.c new file mode 100644 index 000000000..62ba5a428 --- /dev/null +++ b/libdw/dwarf_haspc_relocatable.c @@ -0,0 +1,111 @@ +/* Determine whether a DIE covers a PC address. + Copyright (C) 2010 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 + + +int +dwarf_haspc_relocatable (Dwarf_Die *die, Dwarf_Relocatable *pc) +{ + if (die == NULL) + return -1; + + GElf_Sym pc_sym; + GElf_Sxword pc_addend; + int pc_shndx = INTUSE(dwarf_relocatable_info) (pc, &pc_sym, NULL, + &pc_addend, NULL); + if (pc_shndx < 0) + return pc_shndx; + pc_sym.st_value += pc_addend; + + Dwarf_Relocatable base; + Dwarf_Relocatable begin; + Dwarf_Relocatable end; + ptrdiff_t offset = 0; + while ((offset = INTUSE(dwarf_ranges_relocatable) (die, offset, &base, + &begin, &end)) > 0) + if (begin.valp == NULL && end.valp == NULL && pc->valp == NULL) + { + if (pc->adjust >= begin.adjust && pc->adjust < end.adjust) + return 1; + } + else + { + /* A relocatable address matches if it's in the same section + and the section-relative offsets match. */ + + GElf_Sym sym; + GElf_Sxword addend; + int shndx = INTUSE(dwarf_relocatable_info) (&begin, &sym, NULL, + &addend, NULL); + if (shndx < 0) + return -1; + if (shndx == pc_shndx && sym.st_shndx == pc_sym.st_shndx + && pc_sym.st_value >= sym.st_value + addend) + { + if (pc_sym.st_value == sym.st_value + addend) + return 1; + shndx = INTUSE(dwarf_relocatable_info) (&end, &sym, NULL, + &addend, NULL); + if (shndx < 0) + return -1; + if (shndx == pc_shndx && sym.st_shndx == pc_sym.st_shndx + && pc_sym.st_value < sym.st_value + addend) + return 1; + } + } + + return offset; +} +INTDEF (dwarf_haspc_relocatable) diff --git a/libdw/dwarf_ranges_relocatable.c b/libdw/dwarf_ranges_relocatable.c new file mode 100644 index 000000000..61f52a45d --- /dev/null +++ b/libdw/dwarf_ranges_relocatable.c @@ -0,0 +1,256 @@ +/* Enumerate the PC ranges covered by a DIE. + Copyright (C) 2010 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 "relocate.h" +#include +#include + +ptrdiff_t +internal_function +__libdw_ranges_relocatable (struct Dwarf_CU *cu, Dwarf_Attribute *attr, + ptrdiff_t offset, + Dwarf_Relocatable *basep, + Dwarf_Relocatable *startp, Dwarf_Relocatable *endp, + Dwarf_Block *exprloc) +{ + if (offset == 1) + return 0; + + const unsigned int sec_idx = (exprloc != NULL ? IDX_debug_loc + : IDX_debug_ranges); + unsigned char *readp; + unsigned char *readendp; + if (offset == 0) + { + Dwarf_Word start_offset; + if ((readp = __libdw_formptr (attr, sec_idx, + exprloc != NULL ? DWARF_E_NO_LOCLIST + : DWARF_E_NO_DEBUG_RANGES, + &readendp, &start_offset)) == NULL) + return -1; + + offset = start_offset; + assert ((Dwarf_Word) offset == start_offset); + + /* This is a marker that we need to fetch the CU base address. */ + basep->cu = NULL; + } + else if (__libdw_offset_in_section (cu->dbg, + exprloc != NULL ? IDX_debug_loc + : IDX_debug_ranges, + offset, 1)) + return -1l; + else + { + const Elf_Data *d = cu->dbg->sectiondata[sec_idx]; + readp = d->d_buf + offset; + readendp = d->d_buf + d->d_size; + } + + inline void store (Dwarf_Relocatable *relocp, ptrdiff_t read_adjust) + { + *relocp = (Dwarf_Relocatable) + { + .cu = cu, .sec = sec_idx, .form = DW_FORM_addr, + .valp = readp + }; + readp += read_adjust; + } + + inline bool finalize_reloc (Dwarf_Relocatable *reloc, unsigned int width) + { + if (basep->cu == NULL) + { + /* This is the initial default base address and we have not used it + yet. Find the base address of the compilation unit. It will + normally be specified by DW_AT_low_pc. In DWARF-3 draft 4, the + base address could be overridden by DW_AT_entry_pc. It's been + removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc for + compilation units with discontinuous ranges. */ + + Dwarf_Die cudie = CUDIE (cu); + Dwarf_Attribute loattr; + if (unlikely (INTUSE(dwarf_form_relocatable) + (INTUSE(dwarf_attr) (&cudie, DW_AT_low_pc, &loattr) + ?: INTUSE(dwarf_attr) (&cudie, DW_AT_entry_pc, &loattr), + basep) < 0)) + return true; + } + + GElf_Sxword addend; + if (__libdw_relocatable (cu->dbg, sec_idx, reloc->valp, width, + NULL, &addend) > 0) + { + /* The address itself has a relocation. + The base address entry must not also have a relocation. */ + + if (unlikely (__libdw_relocatable (cu->dbg, sec_idx, basep->valp, width, + NULL, &addend) > 0)) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return true; + } + } + else + /* The address is relative to the base address relocation. */ + reloc->valp = basep->valp; + + reloc->adjust = addend; + + return false; + } + + inline ptrdiff_t finalize (unsigned int width) + { + if (finalize_reloc (startp, width) || finalize_reloc (endp, width)) + return -1; + return readp - (unsigned char *) cu->dbg->sectiondata[sec_idx]->d_buf; + } + +#define READ_RANGES(AS, Addr, READ) \ + while (likely (readendp - readp >= 2 * AS)) \ + { \ + Addr begin = READ (readp); \ + \ + if (begin == (Addr) -1) \ + { \ + /* If this is unrelocated, this is a base address entry. */ \ + int result = __libdw_relocatable (cu->dbg, sec_idx, readp, AS, \ + NULL, NULL); \ + if (unlikely (result < 0)) /* Indigestion. */ \ + return -1; \ + if (result == 0) /* Not relocatable. */ \ + { \ + readp += AS; \ + store (basep, AS); \ + continue; \ + } \ + } \ + else if (begin == 0 && READ (readp + AS) == 0) \ + { \ + /* If these are both unrelocated, this is the end of list entry. */\ + int result = __libdw_relocatable (cu->dbg, sec_idx, readp, AS, \ + NULL, NULL); \ + if (result == 0) /* Not relocatable. */ \ + result = __libdw_relocatable (cu->dbg, sec_idx, readp + AS, AS, \ + NULL, NULL); \ + if (unlikely (result < 0)) /* Indigestion. */ \ + return -1; \ + if (result == 0) /* Not relocatable: end of list entry. */ \ + return 0; \ + } \ + \ + /* This is a pair of addresses. */ \ + store (startp, AS); \ + store (endp, AS); \ + \ + if (exprloc != NULL) \ + { \ + if (unlikely (readendp - readp < 2)) \ + break; \ + exprloc->length = read_2ubyte_unaligned_inc (cu->dbg, readp); \ + if (unlikely ((size_t) (readendp - readp) < exprloc->length)) \ + break; \ + exprloc->data = readp; \ + } \ + \ + return finalize (AS); \ + } + + if (cu->address_size == 8) + READ_RANGES (8, Elf64_Addr, read_8ubyte_unaligned_noncvt) + else + READ_RANGES (4, Elf32_Addr, read_4ubyte_unaligned_noncvt) + + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; +} + +ptrdiff_t +dwarf_ranges_relocatable (Dwarf_Die *die, ptrdiff_t offset, + Dwarf_Relocatable *basep, + Dwarf_Relocatable *startp, Dwarf_Relocatable *endp) +{ + if (die == NULL) + return -1; + + if (offset != 0) + /* Iterate from last position. */ + return __libdw_ranges_relocatable (die->cu, NULL, offset, + basep, startp, endp, NULL); + + Dwarf_Attribute attr_mem; + + /* Usually there is a single contiguous range. */ + if (INTUSE(dwarf_form_relocatable) (INTUSE(dwarf_attr) (die, + DW_AT_high_pc, + &attr_mem), + endp) == 0 + && INTUSE(dwarf_form_relocatable) (INTUSE(dwarf_attr) (die, + DW_AT_low_pc, + &attr_mem), + startp) == 0) + /* A offset into .debug_ranges will never be 1, it must be at least a + multiple of 4. So we can return 1 as a special case value to mark + there are no ranges to look for on the next call. */ + return 1; + + Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_ranges, &attr_mem); + if (attr == NULL) + /* No PC attributes in this DIE at all, so an empty range list. */ + return 0; + + return __libdw_ranges_relocatable (die->cu, attr, 0, + basep, startp, endp, NULL); +} +INTDEF (dwarf_ranges_relocatable) diff --git a/libdw/dwarf_relocatable_info.c b/libdw/dwarf_relocatable_info.c index 67b28f512..c1ad30527 100644 --- a/libdw/dwarf_relocatable_info.c +++ b/libdw/dwarf_relocatable_info.c @@ -51,9 +51,10 @@ # include #endif -#include "libdwP.h" +#include "relocate.h" #include + static int relocatable_form (struct Dwarf_CU *cu, unsigned int sec_idx, @@ -67,24 +68,33 @@ relocatable_form (struct Dwarf_CU *cu, switch (form) { default: - /* This can't be relocatable. We'll let __libdw_relocatable - fill in the SHN_ABS indicator for the constant 0 base. */ + /* This can't be relocatable. */ if (addend != NULL) { - Dwarf_Attribute attr = + if (valp == NULL) + *addend = 0; + else { - .cu = cu, - .form = form, - .valp = (unsigned char *) valp - }; - if (INTUSE(dwarf_formsdata) (&attr, addend)) - return -1; - *addend += adjust; - addend = NULL; + Dwarf_Attribute attr = + { + .cu = cu, + .form = form, + .valp = (unsigned char *) valp + }; + if (INTUSE(dwarf_formsdata) (&attr, addend)) + return -1; + } } - width = 0; - valp = NULL; - break; + noreloc: + if (addend != NULL) + *addend += adjust; + if (sym != NULL) + *sym = (GElf_Sym) { .st_shndx = SHN_ABS }; + if (name != NULL) + *name = NULL; + if (secname != NULL) + *secname = NULL; + return 0; case DW_FORM_addr: width = cu->address_size; @@ -99,10 +109,63 @@ relocatable_form (struct Dwarf_CU *cu, break; } - return __libdw_relocatable (cu->dbg, sec_idx, valp, width, - sym, name, addend, adjust, secname); + int symndx; + int result = __libdw_relocatable (cu->dbg, sec_idx, valp, width, + &symndx, addend); + if (result == 0) + goto noreloc; + else if (likely (result > 0)) + { + struct dwarf_section_reloc *r = cu->dbg->relocate->sectionrel[sec_idx]; + GElf_Sym sym_mem; + GElf_Word shndx; + if (sym == NULL) + sym = &sym_mem; + + if (unlikely (gelf_getsymshndx (r->symdata, r->symxndxdata, + symndx, sym, &shndx) == NULL)) + { + __libdw_seterrno (DWARF_E_RELBADSYM); + return -1; + } + + if (name != NULL) + { + if (sym->st_name == 0) + *name = NULL; + else if (unlikely (sym->st_name >= r->symstrdata->d_size)) + { + __libdw_seterrno (DWARF_E_RELBADSYM); + return -1; + } + else + *name = (const char *) r->symstrdata->d_buf + sym->st_name; + } + + if (addend != NULL) + *addend += adjust; + + result = (sym->st_shndx < SHN_LORESERVE ? sym->st_shndx + : sym->st_shndx == SHN_XINDEX ? shndx : SHN_UNDEF); + + if (secname != NULL) + { + Elf *symelf = ((Elf_Data_Scn *) r->symdata)->s->elf; + size_t shstrndx; + GElf_Shdr shdr; + if (result == 0 + || elf_getshdrstrndx (symelf, &shstrndx) < 0 + || gelf_getshdr (elf_getscn (symelf, result), &shdr) == NULL) + *secname = NULL; + else + *secname = elf_strptr (symelf, shstrndx, shdr.sh_name); + } + } + + return result; } + int dwarf_relocatable_info (reloc, sym, name, addend, secname) Dwarf_Relocatable *reloc; @@ -118,6 +181,7 @@ dwarf_relocatable_info (reloc, sym, name, addend, secname) reloc->form, reloc->valp, reloc->adjust, sym, name, addend, secname); } +INTDEF (dwarf_relocatable_info) #if 0 /* Shorthand for dwarf_relocatable_info(dwarf_form_relocatable). */ diff --git a/libdw/libdw.h b/libdw/libdw.h index f55be48a4..3cac5bd7f 100644 --- a/libdw/libdw.h +++ b/libdw/libdw.h @@ -501,6 +501,9 @@ extern int dwarf_entrypc (Dwarf_Die *die, Dwarf_Addr *return_addr) 0 if not, or -1 for errors. */ extern int dwarf_haspc (Dwarf_Die *die, Dwarf_Addr pc); +/* Like dwarf_haspc, but for a relocatable address. */ +extern int dwarf_haspc_relocatable (Dwarf_Die *die, Dwarf_Relocatable *pc); + /* Enumerate the PC address ranges covered by this DIE, covering all addresses where dwarf_haspc returns true. In the first call OFFSET should be zero and *BASEP need not be initialized. Returns -1 for @@ -510,7 +513,16 @@ extern int dwarf_haspc (Dwarf_Die *die, Dwarf_Addr pc); *STARTP and *ENDP with a contiguous address range. */ extern ptrdiff_t dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep, - Dwarf_Addr *startp, Dwarf_Addr *endp); + Dwarf_Addr *startp, Dwarf_Addr *endp) + __nonnull_attribute__ (3, 4, 5); + +/* Like dwarf_ranges, but with relocatable addresses. */ +extern ptrdiff_t dwarf_ranges_relocatable (Dwarf_Die *die, + ptrdiff_t offset, + Dwarf_Relocatable *basep, + Dwarf_Relocatable *startp, + Dwarf_Relocatable *endp) + __nonnull_attribute__ (3, 4, 5); /* Return byte size attribute of DIE. */ @@ -698,6 +710,19 @@ extern int dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address, Dwarf_Op **exprs, size_t *exprlens, size_t nlocs); +/* Enumerate the locations in a location list. + The interface is as for dwarf_ranges_relocatable, above, + but also fills in *EXPR and *EXPRLEN as for dwarf_getlocation. */ +extern ptrdiff_t dwarf_getlocation_relocatable (Dwarf_Attribute *attr, + ptrdiff_t offset, + Dwarf_Relocatable *basep, + Dwarf_Relocatable *startp, + Dwarf_Relocatable *endp, + Dwarf_Op **expr, + size_t *exprlen) + __nonnull_attribute__ (3, 4, 5, 6, 7); + + /* Return the block associated with a DW_OP_implicit_value operation. The OP pointer must point into an expression that dwarf_getlocation or dwarf_getlocation_addr has returned given the same ATTR. */ diff --git a/libdw/libdw.map b/libdw/libdw.map index ff530702e..1c3e1c30f 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -254,5 +254,8 @@ ELFUTILS_0.149 { global: dwarf_form_relocatable; dwarf_line_relocatable; + dwarf_getlocation_relocatable; + dwarf_haspc_relocatable; + dwarf_ranges_relocatable; dwarf_relocatable_info; } ELFUTILS_0.148; diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 017920e9a..c970c9aed 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -480,6 +480,10 @@ extern int __libdw_intern_expression (Dwarf *dbg, int sec_index) __nonnull_attribute__ (5, 6, 9, 10) internal_function; +extern int __libdw_getlocation (Dwarf_Attribute *attr, const Dwarf_Block *block, + Dwarf_Op **llbuf, size_t *listlen, int sec_idx) + __nonnull_attribute__ (2, 3, 4) internal_function; + /* Return error code of last failing function call. This value is kept separately for each thread. */ @@ -619,13 +623,6 @@ __libdw_read_offset (Dwarf *dbg, ret, sec_ret, size); } -extern int __libdw_relocatable (Dwarf *dbg, int sec_idx, - const unsigned char *valp, unsigned int width, - GElf_Sym *sym, const char **name, - GElf_Sxword *addend, GElf_Sxword offset, - const char **secname) - __nonnull_attribute__ (1) internal_function; - static inline size_t cu_sec_idx (struct Dwarf_CU *cu) @@ -674,6 +671,7 @@ INTDECL (dwarf_formref_die) INTDECL (dwarf_formsdata) INTDECL (dwarf_formstring) INTDECL (dwarf_formudata) +INTDECL (dwarf_form_relocatable) INTDECL (dwarf_getarange_addr) INTDECL (dwarf_getarangeinfo) INTDECL (dwarf_getaranges) @@ -688,6 +686,8 @@ INTDECL (dwarf_nextcu) INTDECL (dwarf_next_unit) INTDECL (dwarf_offdie) INTDECL (dwarf_ranges) +INTDECL (dwarf_ranges_relocatable) +INTDECL (dwarf_relocatable_info) INTDECL (dwarf_siblingof) INTDECL (dwarf_srclang) INTDECL (dwarf_tag) diff --git a/libdw/memory-access.h b/libdw/memory-access.h index e2946b92a..181bcdeb7 100644 --- a/libdw/memory-access.h +++ b/libdw/memory-access.h @@ -168,6 +168,8 @@ __libdw_get_sleb128 (int64_t acc, unsigned int i, const unsigned char **addrp) ? (int32_t) bswap_32 (*((const int32_t *) (Addr))) \ : *((const int32_t *) (Addr))) +# define read_8ubyte_unaligned_noncvt(Addr) \ + *((const uint64_t *) (Addr)) # define read_8ubyte_unaligned(Dbg, Addr) \ (unlikely ((Dbg)->other_byte_order) \ ? bswap_64 (*((const uint64_t *) (Addr))) \ @@ -243,6 +245,12 @@ read_4sbyte_unaligned_1 (bool other_byte_order, const void *p) return up->s4; } +static inline uint64_t +read_8ubyte_unaligned_noncvt (const void *p) +{ + const union unaligned *up = p; + return up->u8; +} static inline uint64_t read_8ubyte_unaligned_1 (bool other_byte_order, const void *p) { diff --git a/libdw/relocate.c b/libdw/relocate.c index 195687390..2a38e51c0 100644 --- a/libdw/relocate.c +++ b/libdw/relocate.c @@ -51,43 +51,13 @@ # include #endif -#include "libdwP.h" -#include "../libelf/libelfP.h" +#include "relocate.h" #include "../libebl/libebl.h" #include #include #include -struct dwarf_reloc_table -{ - size_t n; /* Number of elements in this table. */ - const unsigned char **datum; /* Sorted pointers into section data. */ - int *symndx; /* Corresponding symtab indices. */ - size_t hint; /* Index of last search. */ -}; - -struct dwarf_section_reloc -{ - /* This is set only in the laziest state from startup: the reloc - section needs to be examined, and the rest is uninitialized. - When this is cleared, the rest is initialized and safe to use. */ - Elf_Scn *scn; - - Elf_Data *symdata; - Elf_Data *symstrdata; - Elf_Data *symxndxdata; - - /* Two tables of predigested relocations, segregated by size. */ - struct dwarf_reloc_table rel4; - struct dwarf_reloc_table rel8; - - /* For SHT_RELA, a parallel table of addends. - For SHT_REL, these are null. */ - Elf32_Sword *rela4; - Elf64_Sxword *rela8; -}; - static int internal_function noresolve_symbol (bool undef, @@ -424,13 +394,25 @@ find_reloc (struct dwarf_reloc_table *table, const unsigned char *datum) return -1; } -static int -relocatable_datum (Dwarf *dbg, int sec_index, struct dwarf_section_reloc *r, - const unsigned char *datum, int width, - int *symndx, GElf_Sxword *addend) +int +internal_function +__libdw_relocatable (Dwarf *dbg, int sec_index, + const unsigned char *datum, int width, + int *symndx, GElf_Sxword *addend) { + if (dbg->relocate == NULL) + { + noreloc: + if (addend != NULL) + *addend = (width == 8 + ? read_8ubyte_unaligned (dbg, datum) + : read_4ubyte_unaligned (dbg, datum)); + return 0; + } + + struct dwarf_section_reloc *const r = dbg->relocate->sectionrel[sec_index]; if (r == NULL) - return 0; + goto noreloc; if (r->scn != NULL) { @@ -446,7 +428,8 @@ relocatable_datum (Dwarf *dbg, int sec_index, struct dwarf_section_reloc *r, if (width == 4) { i = find_reloc (&r->rel4, datum); - *symndx = i < 0 ? STN_UNDEF : r->rel4.symndx[i]; + if (symndx != NULL) + *symndx = i < 0 ? STN_UNDEF : r->rel4.symndx[i]; if (addend != NULL) *addend = ((i < 0 || r->rela4 == NULL) ? read_4sbyte_unaligned (dbg, datum) : r->rela4[i]); @@ -456,7 +439,8 @@ relocatable_datum (Dwarf *dbg, int sec_index, struct dwarf_section_reloc *r, assert (width == 8); i = find_reloc (&r->rel8, datum); - *symndx = i < 0 ? STN_UNDEF : r->rel8.symndx[i]; + if (symndx != NULL) + *symndx = i < 0 ? STN_UNDEF : r->rel8.symndx[i]; if (addend != NULL) *addend = ((i < 0 || r->rela8 == NULL) ? read_8sbyte_unaligned (dbg, datum) : r->rela8[i]); @@ -483,16 +467,16 @@ internal_function __libdw_relocate_address (Dwarf *dbg, int sec_index, const void *datum, int width, Dwarf_Addr *val) { - struct dwarf_section_reloc *const r = dbg->relocate->sectionrel[sec_index]; int symndx; GElf_Sxword addend; - int result = relocatable_datum (dbg, sec_index, r, datum, width, - &symndx, &addend); + int result = __libdw_relocatable (dbg, sec_index, datum, width, + &symndx, &addend); if (result > 0 && symndx != STN_UNDEF) { GElf_Sym sym; GElf_Word shndx; - result = reloc_getsym (r, symndx, &sym, &shndx); + result = reloc_getsym (dbg->relocate->sectionrel[sec_index], + symndx, &sym, &shndx); if (result > 0) { result = (*dbg->relocate->resolve_symbol) @@ -512,16 +496,16 @@ internal_function __libdw_relocate_offset (Dwarf *dbg, int sec_index, const void *datum, int width, Dwarf_Off *val) { - struct dwarf_section_reloc *const r = dbg->relocate->sectionrel[sec_index]; int symndx; GElf_Sxword addend; - int result = relocatable_datum (dbg, sec_index, r, datum, width, - &symndx, &addend); + int result = __libdw_relocatable (dbg, sec_index, datum, width, + &symndx, &addend); if (result > 0 && symndx != STN_UNDEF) { GElf_Sym sym; GElf_Word shndx; - result = reloc_getsym (r, symndx, &sym, &shndx); + result = reloc_getsym (dbg->relocate->sectionrel[sec_index], + symndx, &sym, &shndx); if (result > 0) { if (unlikely (sym.st_shndx == SHN_UNDEF) @@ -544,72 +528,3 @@ __libdw_relocate_offset (Dwarf *dbg, int sec_index, *val = addend; return result; } - -int -internal_function -__libdw_relocatable (Dwarf *dbg, int sec_idx, - const unsigned char *valp, unsigned int width, - GElf_Sym *sym, const char **name, GElf_Sxword *addend, - GElf_Sxword offset, const char **secname) -{ - struct dwarf_section_reloc *const r = ((valp != NULL && dbg->relocate != NULL) - ? dbg->relocate->sectionrel[sec_idx] - : NULL); - int symndx; - int result = relocatable_datum (dbg, sec_idx, r, valp, width, - &symndx, addend); - if (result == 0) - { - if (sym != NULL) - *sym = (GElf_Sym) { .st_shndx = SHN_ABS }; - if (name != NULL) - *name = NULL; - if (addend != NULL) - *addend = offset + (width == 8 - ? read_8ubyte_unaligned (dbg, valp) - : read_4ubyte_unaligned (dbg, valp)); - if (secname != NULL) - *secname = NULL; - } - else if (likely (result > 0)) - { - GElf_Sym sym_mem; - GElf_Word shndx; - if (sym == NULL) - sym = &sym_mem; - result = reloc_getsym (r, symndx, sym, &shndx); - if (likely (result > 0)) - { - if (name != NULL) - { - if (sym->st_name == 0) - *name = NULL; - else if (unlikely (sym->st_name >= r->symstrdata->d_size)) - { - __libdw_seterrno (DWARF_E_RELBADSYM); - return -1; - } - else - *name = (const char *) r->symstrdata->d_buf + sym->st_name; - } - if (addend != NULL) - *addend += offset; - result = (sym->st_shndx < SHN_LORESERVE ? sym->st_shndx - : sym->st_shndx == SHN_XINDEX ? shndx : SHN_UNDEF); - if (secname != NULL) - { - Elf *symelf = ((Elf_Data_Scn *) r->symdata)->s->elf; - size_t shstrndx; - GElf_Shdr shdr; - if (result == 0 - || elf_getshdrstrndx (symelf, &shstrndx) < 0 - || gelf_getshdr (elf_getscn (symelf, result), &shdr) == NULL) - *secname = NULL; - else - *secname = elf_strptr (symelf, shstrndx, shdr.sh_name); - } - } - } - - return result; -} diff --git a/libdw/relocate.h b/libdw/relocate.h new file mode 100644 index 000000000..eaf8e6285 --- /dev/null +++ b/libdw/relocate.h @@ -0,0 +1,96 @@ +/* Internal definitions for libdw relocation handling. + Copyright (C) 2010 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 "libdwP.h" +#include "libelfP.h" + + +struct dwarf_reloc_table +{ + size_t n; /* Number of elements in this table. */ + const unsigned char **datum; /* Sorted pointers into section data. */ + int *symndx; /* Corresponding symtab indices. */ + size_t hint; /* Index of last search. */ +}; + +struct dwarf_section_reloc +{ + /* This is set only in the laziest state from startup: the reloc + section needs to be examined, and the rest is uninitialized. + When this is cleared, the rest is initialized and safe to use. */ + Elf_Scn *scn; + + Elf_Data *symdata; + Elf_Data *symstrdata; + Elf_Data *symxndxdata; + + /* Two tables of predigested relocations, segregated by size. */ + struct dwarf_reloc_table rel4; + struct dwarf_reloc_table rel8; + + /* For SHT_RELA, a parallel table of addends. + For SHT_REL, these are null. */ + Elf32_Sword *rela4; + Elf64_Sxword *rela8; +}; + + +extern int __libdw_relocatable (Dwarf *dbg, int sec_index, + const unsigned char *datum, int width, + int *symndx, GElf_Sxword *addend) + __nonnull_attribute__ (1) internal_function; + +extern ptrdiff_t __libdw_ranges_relocatable (struct Dwarf_CU *cu, + Dwarf_Attribute *attr, + ptrdiff_t offset, + Dwarf_Relocatable *basep, + Dwarf_Relocatable *startp, + Dwarf_Relocatable *endp, + Dwarf_Block *exprloc) + __nonnull_attribute__ (1, 4, 5, 6) internal_function; -- 2.47.2