summaryrefslogtreecommitdiffstats
path: root/src/libdw/dwarf_getlocation.c
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2015-02-25 16:51:55 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-02-25 16:51:55 +0000
commit412f6b917fc658c24dd7d624bb82bf1a1e791b95 (patch)
treea63e40b5dd9927bd04ec7427c8797975c2a71f85 /src/libdw/dwarf_getlocation.c
parenta969285f70219c5d35ee5aa8ceab0f532fc1e54d (diff)
parent7401a30e8b3a0de25d7e8a6b635fc33f45894118 (diff)
downloadandroid_external_elfutils-412f6b917fc658c24dd7d624bb82bf1a1e791b95.tar.gz
android_external_elfutils-412f6b917fc658c24dd7d624bb82bf1a1e791b95.tar.bz2
android_external_elfutils-412f6b917fc658c24dd7d624bb82bf1a1e791b95.zip
am 7401a30e: am 36e62782: Merge "Upgrade to elfutils 0.161."
* commit '7401a30e8b3a0de25d7e8a6b635fc33f45894118': Upgrade to elfutils 0.161.
Diffstat (limited to 'src/libdw/dwarf_getlocation.c')
-rw-r--r--src/libdw/dwarf_getlocation.c453
1 files changed, 312 insertions, 141 deletions
diff --git a/src/libdw/dwarf_getlocation.c b/src/libdw/dwarf_getlocation.c
index f7a60f9f..068f3853 100644
--- a/src/libdw/dwarf_getlocation.c
+++ b/src/libdw/dwarf_getlocation.c
@@ -1,52 +1,31 @@
/* Return location expression list.
- Copyright (C) 2000-2010 Red Hat, Inc.
- This file is part of Red Hat elfutils.
+ Copyright (C) 2000-2010, 2013, 2014 Red Hat, Inc.
+ This file is part of elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2000.
- 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.
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
- Red Hat elfutils is distributed in the hope that it will be useful, but
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ 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
- <http://www.openinventionnetwork.com>. */
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
@@ -77,6 +56,11 @@ attr_ok (Dwarf_Attribute *attr)
case DW_AT_frame_base:
case DW_AT_return_addr:
case DW_AT_static_link:
+ case DW_AT_segment:
+ case DW_AT_GNU_call_site_value:
+ case DW_AT_GNU_call_site_data_value:
+ case DW_AT_GNU_call_site_target:
+ case DW_AT_GNU_call_site_target_clobbered:
break;
default:
@@ -115,13 +99,15 @@ loc_compare (const void *p1, const void *p2)
/* For each DW_OP_implicit_value, we store a special entry in the cache.
This points us directly to the block data for later fetching. */
static void
-store_implicit_value (Dwarf *dbg, void **cache, Dwarf_Op *op,
- unsigned char *data)
+store_implicit_value (Dwarf *dbg, void **cache, Dwarf_Op *op)
{
struct loc_block_s *block = libdw_alloc (dbg, struct loc_block_s,
sizeof (struct loc_block_s), 1);
+ const unsigned char *data = (const unsigned char *) (uintptr_t) op->number2;
+ // Ignored, equal to op->number. And data length already checked.
+ (void) __libdw_get_uleb128 (&data, data + len_leb128 (Dwarf_Word));
block->addr = op;
- block->data = data + op->number2;
+ block->data = (unsigned char *) data;
block->length = op->number;
(void) tsearch (block, cache, loc_compare);
}
@@ -220,6 +206,13 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
bool cfap, bool valuep,
Dwarf_Op **llbuf, size_t *listlen, int sec_index)
{
+ /* Empty location expressions don't have any ops to intern. */
+ if (block->length == 0)
+ {
+ *listlen = 0;
+ return 0;
+ }
+
/* Check whether we already looked at this list. */
struct loc_s fake = { .addr = block->data };
struct loc_s **found = tfind (&fake, cache, loc_compare);
@@ -245,6 +238,21 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
struct loclist *loclist = NULL;
unsigned int n = 0;
+
+ if (cfap)
+ {
+ /* Synthesize the operation to push the CFA before the expression. */
+ struct loclist *newloc;
+ newloc = (struct loclist *) alloca (sizeof (struct loclist));
+ newloc->atom = DW_OP_call_frame_cfa;
+ newloc->number = 0;
+ newloc->number2 = 0;
+ newloc->offset = -1;
+ newloc->next = loclist;
+ loclist = newloc;
+ ++n;
+ }
+
/* Decode the opcodes. It is possible in some situations to have a
block of size zero. */
while (data < end_data)
@@ -360,6 +368,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
case DW_OP_const4s:
case DW_OP_call4:
+ case DW_OP_GNU_parameter_ref:
if (unlikely (data + 4 > end_data))
goto invalid;
@@ -384,39 +393,43 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
case DW_OP_plus_uconst:
case DW_OP_regx:
case DW_OP_piece:
- /* XXX Check size. */
- get_uleb128 (newloc->number, data);
+ case DW_OP_GNU_convert:
+ case DW_OP_GNU_reinterpret:
+ get_uleb128 (newloc->number, data, end_data);
break;
case DW_OP_consts:
case DW_OP_breg0 ... DW_OP_breg31:
case DW_OP_fbreg:
- /* XXX Check size. */
- get_sleb128 (newloc->number, data);
+ get_sleb128 (newloc->number, data, end_data);
break;
case DW_OP_bregx:
- /* XXX Check size. */
- get_uleb128 (newloc->number, data);
- get_sleb128 (newloc->number2, data);
+ get_uleb128 (newloc->number, data, end_data);
+ if (unlikely (data >= end_data))
+ goto invalid;
+ get_sleb128 (newloc->number2, data, end_data);
break;
case DW_OP_bit_piece:
- /* XXX Check size. */
- get_uleb128 (newloc->number, data);
- get_uleb128 (newloc->number2, data);
+ case DW_OP_GNU_regval_type:
+ get_uleb128 (newloc->number, data, end_data);
+ if (unlikely (data >= end_data))
+ goto invalid;
+ get_uleb128 (newloc->number2, data, end_data);
break;
case DW_OP_implicit_value:
+ case DW_OP_GNU_entry_value:
/* This cannot be used in a CFI expression. */
if (unlikely (dbg == NULL))
goto invalid;
- /* XXX Check size. */
- get_uleb128 (newloc->number, data); /* Block length. */
+ /* start of block inc. len. */
+ newloc->number2 = (Dwarf_Word) (uintptr_t) data;
+ get_uleb128 (newloc->number, data, end_data); /* Block length. */
if (unlikely ((Dwarf_Word) (end_data - data) < newloc->number))
goto invalid;
- newloc->number2 = data - block->data; /* Relative block offset. */
data += newloc->number; /* Skip the block. */
break;
@@ -425,8 +438,32 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
if (__libdw_read_offset_inc (dbg, sec_index, &data, ref_size,
&newloc->number, IDX_debug_info, 0))
return -1;
- /* XXX Check size. */
- get_uleb128 (newloc->number2, data); /* Byte offset. */
+ if (unlikely (data >= end_data))
+ goto invalid;
+ get_uleb128 (newloc->number2, data, end_data); /* Byte offset. */
+ break;
+
+ case DW_OP_GNU_deref_type:
+ if (unlikely (data + 1 >= end_data))
+ goto invalid;
+ newloc->number = *data++;
+ get_uleb128 (newloc->number2, data, end_data);
+ break;
+
+ case DW_OP_GNU_const_type:
+ {
+ size_t size;
+ get_uleb128 (newloc->number, data, end_data);
+ if (unlikely (data >= end_data))
+ goto invalid;
+
+ /* start of block inc. len. */
+ newloc->number2 = (Dwarf_Word) (uintptr_t) data;
+ size = *data++;
+ if (unlikely ((Dwarf_Word) (end_data - data) < size))
+ goto invalid;
+ data += size; /* Skip the block. */
+ }
break;
default:
@@ -437,8 +474,8 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
if (unlikely (n == 0))
{
/* This is not allowed.
-
- XXX Is it? */
+ It would mean an empty location expression, which we handled
+ already as a special case above. */
goto invalid;
}
@@ -455,9 +492,6 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
++n;
}
- if (cfap)
- ++n;
-
/* Allocate the array. */
Dwarf_Op *result;
if (dbg != NULL)
@@ -477,16 +511,6 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
*llbuf = result;
*listlen = n;
- if (cfap)
- {
- /* Synthesize the operation to push the CFA before the expression. */
- --n;
- result[0].atom = DW_OP_call_frame_cfa;
- result[0].number = 0;
- result[0].number2 = 0;
- result[0].offset = -1;
- }
-
do
{
/* We populate the array from the back since the list is backwards. */
@@ -497,7 +521,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
result[n].offset = loclist->offset;
if (result[n].atom == DW_OP_implicit_value)
- store_implicit_value (dbg, cache, &result[n], block->data);
+ store_implicit_value (dbg, cache, &result[n]);
loclist = loclist->next;
}
@@ -530,6 +554,14 @@ static int
getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
Dwarf_Op **llbuf, size_t *listlen, int sec_index)
{
+ /* Empty location expressions don't have any ops to intern.
+ Note that synthetic empty_cu doesn't have an associated DWARF dbg. */
+ if (block->length == 0)
+ {
+ *listlen = 0;
+ return 0;
+ }
+
return __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order,
cu->address_size, (cu->version == 2
? cu->address_size
@@ -560,6 +592,121 @@ dwarf_getlocation (attr, llbuf, listlen)
return getlocation (attr->cu, &block, llbuf, listlen, cu_sec_idx (attr->cu));
}
+static int
+attr_base_address (attr, basep)
+ Dwarf_Attribute *attr;
+ Dwarf_Addr *basep;
+{
+ /* Fetch the CU's base address. */
+ Dwarf_Die cudie = CUDIE (attr->cu);
+
+ /* 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_Attribute attr_mem;
+ if (unlikely (INTUSE(dwarf_lowpc) (&cudie, basep) != 0)
+ && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
+ DW_AT_entry_pc,
+ &attr_mem),
+ basep) != 0)
+ {
+ if (INTUSE(dwarf_errno) () != 0)
+ return -1;
+
+ /* The compiler provided no base address when it should
+ have. Buggy GCC does this when it used absolute
+ addresses in the location list and no DW_AT_ranges. */
+ *basep = 0;
+ }
+ return 0;
+}
+
+static int
+initial_offset_base (attr, offset, basep)
+ Dwarf_Attribute *attr;
+ ptrdiff_t *offset;
+ Dwarf_Addr *basep;
+{
+ if (attr_base_address (attr, basep) != 0)
+ return -1;
+
+ Dwarf_Word start_offset;
+ if (__libdw_formptr (attr, IDX_debug_loc,
+ DWARF_E_NO_LOCLIST,
+ NULL, &start_offset) == NULL)
+ return -1;
+
+ *offset = start_offset;
+ return 0;
+}
+
+static ptrdiff_t
+getlocations_addr (attr, offset, basep, startp, endp, address,
+ locs, expr, exprlen)
+ Dwarf_Attribute *attr;
+ ptrdiff_t offset;
+ Dwarf_Addr *basep;
+ Dwarf_Addr *startp;
+ Dwarf_Addr *endp;
+ Dwarf_Addr address;
+ Elf_Data *locs;
+ Dwarf_Op **expr;
+ size_t *exprlen;
+{
+ unsigned char *readp = locs->d_buf + offset;
+ unsigned char *readendp = locs->d_buf + locs->d_size;
+
+ next:
+ if (readendp - readp < attr->cu->address_size * 2)
+ {
+ invalid:
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
+
+ Dwarf_Addr begin;
+ Dwarf_Addr end;
+
+ switch (__libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc,
+ &readp, attr->cu->address_size,
+ &begin, &end, basep))
+ {
+ case 0: /* got location range. */
+ break;
+ case 1: /* base address setup. */
+ goto next;
+ case 2: /* end of loclist */
+ return 0;
+ default: /* error */
+ return -1;
+ }
+
+ if (readendp - readp < 2)
+ goto invalid;
+
+ /* We have a location expression. */
+ Dwarf_Block block;
+ block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp);
+ block.data = readp;
+ if (readendp - readp < (ptrdiff_t) block.length)
+ goto invalid;
+ readp += block.length;
+
+ *startp = *basep + begin;
+ *endp = *basep + end;
+
+ /* If address is minus one we want them all, otherwise only matching. */
+ if (address != (Dwarf_Word) -1 && (address < *startp || address >= *endp))
+ goto next;
+
+ if (getlocation (attr->cu, &block, expr, exprlen, IDX_debug_loc) != 0)
+ return -1;
+
+ return readp - (unsigned char *) locs->d_buf;
+}
+
int
dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
Dwarf_Attribute *attr;
@@ -598,85 +745,109 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
if (result != 1)
return result ?: 1;
- unsigned char *endp;
- unsigned char *readp = __libdw_formptr (attr, IDX_debug_loc,
- DWARF_E_NO_LOCLIST, &endp, NULL);
- if (readp == NULL)
+ Dwarf_Addr base, start, end;
+ Dwarf_Op *expr;
+ size_t expr_len;
+ ptrdiff_t off = 0;
+ size_t got = 0;
+
+ /* This is a true loclistptr, fetch the initial base address and offset. */
+ if (initial_offset_base (attr, &off, &base) != 0)
return -1;
- Dwarf_Addr base = (Dwarf_Addr) -1;
- size_t got = 0;
- while (got < maxlocs)
+ const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
+ if (d == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NO_LOCLIST);
+ return -1;
+ }
+
+ while (got < maxlocs
+ && (off = getlocations_addr (attr, off, &base, &start, &end,
+ address, d, &expr, &expr_len)) > 0)
{
- if (endp - readp < attr->cu->address_size * 2)
+ /* This one matches the address. */
+ if (llbufs != NULL)
{
- invalid:
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- return -1;
+ llbufs[got] = expr;
+ listlens[got] = expr_len;
}
+ ++got;
+ }
- Dwarf_Addr begin;
- Dwarf_Addr end;
+ /* We might stop early, so off can be zero or positive on success. */
+ if (off < 0)
+ return -1;
- int status
- = __libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc,
- &readp, attr->cu->address_size,
- &begin, &end, &base);
- if (status == 2) /* End of list entry. */
- break;
- else if (status == 1) /* Base address selected. */
- continue;
- else if (status < 0)
- return status;
-
- if (endp - readp < 2)
- goto invalid;
-
- /* We have a location expression. */
- block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp);
- block.data = readp;
- if (endp - readp < (ptrdiff_t) block.length)
- goto invalid;
- readp += block.length;
-
- if (base == (Dwarf_Addr) -1)
+ return got;
+}
+
+ptrdiff_t
+dwarf_getlocations (attr, offset, basep, startp, endp, expr, exprlen)
+ Dwarf_Attribute *attr;
+ ptrdiff_t offset;
+ Dwarf_Addr *basep;
+ Dwarf_Addr *startp;
+ Dwarf_Addr *endp;
+ Dwarf_Op **expr;
+ size_t *exprlen;
+{
+ if (! attr_ok (attr))
+ return -1;
+
+ /* 1 is an invalid offset, meaning no more locations. */
+ if (offset == 1)
+ return 0;
+
+ if (offset == 0)
+ {
+ /* If it has a block form, it's a single location expression. */
+ Dwarf_Block block;
+ if (INTUSE(dwarf_formblock) (attr, &block) == 0)
{
- /* Fetch the CU's base address. */
- Dwarf_Die cudie = CUDIE (attr->cu);
-
- /* 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_Attribute attr_mem;
- if (unlikely (INTUSE(dwarf_lowpc) (&cudie, &base) != 0)
- && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
- DW_AT_entry_pc,
- &attr_mem),
- &base) != 0)
- {
- if (INTUSE(dwarf_errno) () != 0)
- return -1;
+ if (getlocation (attr->cu, &block, expr, exprlen,
+ cu_sec_idx (attr->cu)) != 0)
+ return -1;
- /* The compiler provided no base address when it should
- have. Buggy GCC does this when it used absolute
- addresses in the location list and no DW_AT_ranges. */
- base = 0;
- }
+ /* This is the one and only location covering everything. */
+ *startp = 0;
+ *endp = -1;
+ return 1;
}
- if (address >= base + begin && address < base + end)
+ int error = INTUSE(dwarf_errno) ();
+ if (unlikely (error != DWARF_E_NO_BLOCK))
{
- /* This one matches the address. */
- if (llbufs != NULL
- && unlikely (getlocation (attr->cu, &block,
- &llbufs[got], &listlens[got],
- IDX_debug_loc) != 0))
- return -1;
- ++got;
+ __libdw_seterrno (error);
+ return -1;
+ }
+
+ int result = check_constant_offset (attr, expr, exprlen);
+ if (result != 1)
+ {
+ if (result == 0)
+ {
+ /* This is the one and only location covering everything. */
+ *startp = 0;
+ *endp = -1;
+ return 1;
+ }
+ return result;
}
+
+ /* We must be looking at a true loclistptr, fetch the initial
+ base address and offset. */
+ if (initial_offset_base (attr, &offset, basep) != 0)
+ return -1;
}
- return got;
+ const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
+ if (d == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NO_LOCLIST);
+ return -1;
+ }
+
+ return getlocations_addr (attr, offset, basep, startp, endp,
+ (Dwarf_Word) -1, d, expr, exprlen);
}