From: Roland McGrath Date: Thu, 1 Jul 2010 06:47:02 +0000 (-0700) Subject: Add dwarf_getlocation_relocatable_addr. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5912b834aa4bc6d5dbb2275f183cabc709cfa5ea;p=thirdparty%2Felfutils.git Add dwarf_getlocation_relocatable_addr. --- diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 21dd1c6dd..e76d97d26 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,5 +1,19 @@ 2010-06-30 Roland McGrath + * dwarf_getlocation_relocatable_addr.c: New file. + * Makefile.am (libdw_a_SOURCES): Add it. + * libdw.map (ELFUTILS_0.149): Add it. + * libdw.h: Declare it. + + * dwarf_getlocation.c (__libdw_intern_expression): New flag argument. + When set, handle relocatable DW_OP_addr. + (__libdw_getlocation): Likewise, pass it through. + (dwarf_getlocation, dwarf_getlocation_addr): Update callers. + * dwarf_frame_register.c: Likewise. + * dwarf_frame_cfa.c: Likewise. + * dwarf_getlocation_relocatable.c: Likewise. + * libdwP.h: Update decls. + * relocate.c (digest_one_reloc, digest_relocs): Track r_offset values and don't bother sorting if they were already sorted. diff --git a/libdw/Makefile.am b/libdw/Makefile.am index 719d1d580..35a0fd8d0 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -88,6 +88,7 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ relocate.c dwarf_relocatable_info.c \ dwarf_form_relocatable.c dwarf_line_relocatable.c \ dwarf_ranges_relocatable.c dwarf_getlocation_relocatable.c \ + dwarf_getlocation_relocatable_addr.c \ dwarf_haspc_relocatable.c dwarf_getsrc_relocatable.c if MAINTAINER_MODE diff --git a/libdw/dwarf_frame_cfa.c b/libdw/dwarf_frame_cfa.c index 2f3268a89..eb4a16ee0 100644 --- a/libdw/dwarf_frame_cfa.c +++ b/libdw/dwarf_frame_cfa.c @@ -84,7 +84,7 @@ dwarf_frame_cfa (fs, ops, nops) result = __libdw_intern_expression (NULL, fs->cache->other_byte_order, fs->cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8, 4, - &fs->cache->expr_tree, &fs->cfa_data.expr, false, false, + &fs->cache->expr_tree, &fs->cfa_data.expr, false, false, false, ops, nops, IDX_debug_frame); break; diff --git a/libdw/dwarf_frame_register.c b/libdw/dwarf_frame_register.c index ae0db0200..0fc759e67 100644 --- a/libdw/dwarf_frame_register.c +++ b/libdw/dwarf_frame_register.c @@ -130,7 +130,7 @@ dwarf_frame_register (fs, regno, ops_mem, ops, nops) if (__libdw_intern_expression (NULL, fs->cache->other_byte_order, address_size, 4, - &fs->cache->expr_tree, &block, + &fs->cache->expr_tree, &block, false, true, reg->rule == reg_val_expression, ops, nops, IDX_debug_frame) < 0) return -1; diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index fa188dcc9..0c4dc346f 100644 --- a/libdw/dwarf_getlocation.c +++ b/libdw/dwarf_getlocation.c @@ -57,7 +57,7 @@ #include #include -#include +#include "relocate.h" static bool @@ -137,7 +137,7 @@ dwarf_getlocation_implicit_value (attr, op, return_block) struct loc_block_s fake = { .addr = (void *) op }; struct loc_block_s **found = tfind (&fake, &attr->cu->locs, loc_compare); - if (unlikely (found == NULL)) + if (unlikely (found == NULL) || unlikely (op->atom != DW_OP_implicit_value)) { __libdw_seterrno (DWARF_E_NO_BLOCK); return -1; @@ -216,7 +216,7 @@ int internal_function __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, unsigned int address_size, unsigned int ref_size, - void **cache, const Dwarf_Block *block, + void **cache, const Dwarf_Block *block, bool reloc, bool cfap, bool valuep, Dwarf_Op **llbuf, size_t *listlen, int sec_index) { @@ -238,6 +238,9 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, return 0; } + reloc = reloc && (dbg->relocate != NULL + && dbg->relocate->sectionrel[sec_index] != NULL); + const unsigned char *data = block->data; const unsigned char *const end_data = data + block->length; @@ -262,9 +265,30 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, { case DW_OP_addr: /* Address, depends on address size of CU. */ - if (__libdw_read_address_inc (dbg, sec_index, &data, - address_size, &newloc->number)) - return -1; + if (reloc) + { + /* When taking relocatable addresses, we resolve to the + symndx and store that in the Dwarf_Op for later use + with dwarf_getlocation_relocatable_addr. */ + int symndx; + GElf_Sxword addend; + int shndx = __libdw_relocatable (dbg, sec_index, + data, address_size, + &symndx, &addend); + if (unlikely (shndx < 0)) + return -1; + data += address_size; + newloc->number = addend; + if (shndx > 0) + newloc->number2 = symndx; + } + else + { + /* The address has to be resolved now. */ + if (__libdw_read_address_inc (dbg, sec_index, &data, + address_size, &newloc->number)) + return -1; + } break; case DW_OP_call_ref: @@ -520,6 +544,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, int internal_function __libdw_getlocation (Dwarf_Attribute *attr, const Dwarf_Block *block, + bool reloc, Dwarf_Op **llbuf, size_t *listlen, int sec_index) { struct Dwarf_CU *cu = attr->cu; @@ -527,7 +552,7 @@ __libdw_getlocation (Dwarf_Attribute *attr, const Dwarf_Block *block, cu->address_size, (cu->version == 2 ? cu->address_size : cu->offset_size), - &cu->locs, block, + &cu->locs, block, reloc, false, false, llbuf, listlen, sec_index); } @@ -550,7 +575,7 @@ dwarf_getlocation (attr, llbuf, listlen) if (INTUSE(dwarf_formblock) (attr, &block) != 0) return -1; - return __libdw_getlocation (attr, &block, llbuf, listlen, + return __libdw_getlocation (attr, &block, false, llbuf, listlen, cu_sec_idx (attr->cu)); } @@ -577,7 +602,8 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs) if (maxlocs == 0) return 0; if (llbufs != NULL && - __libdw_getlocation (attr, &block, &llbufs[0], &listlens[0], + __libdw_getlocation (attr, &block, false, + &llbufs[0], &listlens[0], cu_sec_idx (attr->cu)) != 0) return -1; return listlens[0] == 0 ? 0 : 1; @@ -667,7 +693,7 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs) { /* This one matches the address. */ if (llbufs != NULL - && unlikely (__libdw_getlocation (attr, &block, + && unlikely (__libdw_getlocation (attr, &block, false, &llbufs[got], &listlens[got], IDX_debug_loc) != 0)) return -1; diff --git a/libdw/dwarf_getlocation_relocatable.c b/libdw/dwarf_getlocation_relocatable.c index a9bf025b3..f2082d9d7 100644 --- a/libdw/dwarf_getlocation_relocatable.c +++ b/libdw/dwarf_getlocation_relocatable.c @@ -105,7 +105,7 @@ dwarf_getlocation_relocatable (Dwarf_Attribute *attr, ptrdiff_t offset, /* Parse the block into internal form. */ if (offset > 0 && expr != NULL - && __libdw_getlocation (attr, &block, expr, exprlen, sec_idx) < 0) + && __libdw_getlocation (attr, &block, true, expr, exprlen, sec_idx) < 0) offset = -1; return offset; diff --git a/libdw/dwarf_getlocation_relocatable_addr.c b/libdw/dwarf_getlocation_relocatable_addr.c new file mode 100644 index 000000000..0886c30b1 --- /dev/null +++ b/libdw/dwarf_getlocation_relocatable_addr.c @@ -0,0 +1,104 @@ +/* Return relocatable address from DW_OP_addr in a DWARF expression. + 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_getlocation_relocatable_addr (attr, op, reloc) + Dwarf_Attribute *attr; + const Dwarf_Op *op; + Dwarf_Relocatable *reloc; +{ + if (attr == NULL) + return -1; + + if (unlikely (op->atom != DW_OP_addr)) + { + __libdw_seterrno (DWARF_E_INVALID_ACCESS); + return -1; + } + + *reloc = (Dwarf_Relocatable) + { + .form = DW_FORM_addr, + .cu = attr->cu, + .adjust = op->number, + .symndx = op->number2, + }; + + if (reloc->symndx != 0) + /* Setting .valp non-null with .symndx nonzero indicates that + the adjustment is relative to a known symndx, but not resolved + to section-relative. */ + reloc->valp = (void *) -1l; + + /* If the attribute this expression came from was a location list, then + the relevant symtab is that of the .rela.debug_loc section; otherwise + it's that of the section where the attribute resides. */ + + switch (attr->form) + { + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_sec_offset: + reloc->sec = IDX_debug_loc; + break; + + default: + reloc->sec = cu_sec_idx (reloc->cu); + break; + } + + return 0; +} diff --git a/libdw/dwarf_relocatable_info.c b/libdw/dwarf_relocatable_info.c index 37868f627..8570276c0 100644 --- a/libdw/dwarf_relocatable_info.c +++ b/libdw/dwarf_relocatable_info.c @@ -112,7 +112,7 @@ dwarf_relocatable_info (reloc, sym, name, addend, secname) } GElf_Sxword adjust = 0; - if (reloc->valp != NULL) + if (reloc->valp != NULL && reloc->symndx == STN_UNDEF) { int result = __libdw_relocatable (reloc->cu->dbg, reloc->sec, reloc->valp, width, diff --git a/libdw/libdw.h b/libdw/libdw.h index 2eaf95a27..75dcd142b 100644 --- a/libdw/libdw.h +++ b/libdw/libdw.h @@ -728,7 +728,6 @@ extern ptrdiff_t dwarf_getlocation_relocatable (Dwarf_Attribute *attr, 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. */ @@ -737,6 +736,14 @@ extern int dwarf_getlocation_implicit_value (Dwarf_Attribute *attr, Dwarf_Block *return_block) __nonnull_attribute__ (2, 3); +/* Return the relocatable form of a DW_OP_addr operation. The OP pointer + must point into an expression that dwarf_getlocation_relocatable has + returned given the same ATTR. */ +extern int dwarf_getlocation_relocatable_addr (Dwarf_Attribute *attr, + const Dwarf_Op *op, + Dwarf_Relocatable *reloc) + __nonnull_attribute__ (2, 3); + /* Compute the byte-size of a type DIE according to DWARF rules. For most types, this is just DW_AT_byte_size. diff --git a/libdw/libdw.map b/libdw/libdw.map index 86505b4bd..d5444f6f6 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -254,6 +254,7 @@ ELFUTILS_0.149 { dwarf_getsrc_relocatable; dwarf_line_relocatable; dwarf_getlocation_relocatable; + dwarf_getlocation_relocatable_addr; dwarf_haspc_relocatable; dwarf_ranges_relocatable; dwarf_relocatable_info; diff --git a/libdw/libdwP.h b/libdw/libdwP.h index e7f66a2ff..ce52ffdf7 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -475,14 +475,15 @@ extern int __libdw_intern_expression (Dwarf *dbg, unsigned int address_size, unsigned int ref_size, void **cache, const Dwarf_Block *block, - bool cfap, bool valuep, + bool reloc, bool cfap, bool valuep, Dwarf_Op **llbuf, size_t *listlen, int sec_index) - __nonnull_attribute__ (5, 6, 9, 10) internal_function; + __nonnull_attribute__ (5, 6, 10, 11) internal_function; extern int __libdw_getlocation (Dwarf_Attribute *attr, const Dwarf_Block *block, + bool reloc, Dwarf_Op **llbuf, size_t *listlen, int sec_idx) - __nonnull_attribute__ (2, 3, 4) internal_function; + __nonnull_attribute__ (2, 4, 5) internal_function; /* Return error code of last failing function call. This value is kept