summaryrefslogtreecommitdiff
path: root/libdwarf/dwarf_loc.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdwarf/dwarf_loc.c')
-rw-r--r--libdwarf/dwarf_loc.c1197
1 files changed, 1197 insertions, 0 deletions
diff --git a/libdwarf/dwarf_loc.c b/libdwarf/dwarf_loc.c
new file mode 100644
index 0000000..3a2025a
--- /dev/null
+++ b/libdwarf/dwarf_loc.c
@@ -0,0 +1,1197 @@
+/*
+
+ Copyright (C) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ Portions Copyright (C) 2007-2011 David Anderson. All Rights Reserved.
+ Portions Copyright (C) 2010 SN Systems Ltd. All Rights Reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2.1 of the GNU Lesser General Public License
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it would be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ Further, this software is distributed without any warranty that it is
+ free of the rightful claim of any third person regarding infringement
+ or the like. Any license provided herein, whether implied or
+ otherwise, applies only to this software file. Patent licenses, if
+ any, provided herein do not apply to combinations of this program with
+ other software, or any other product whatsoever.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301,
+ USA.
+
+ Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane,
+ Mountain View, CA 94043, or:
+
+ http://www.sgi.com
+
+ For further information regarding this notice, see:
+
+ http://oss.sgi.com/projects/GenInfo/NoticeExplan
+
+*/
+/* The address of the Free Software Foundation is
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ SGI has moved from the Crittenden Lane address.
+*/
+
+
+
+
+#include "config.h"
+#include "dwarf_incl.h"
+#include "dwarf_loc.h"
+#include <stdio.h> /* for debugging only. */
+
+/* Richard Henderson: The operand is an absolute
+ address. The first byte of the value
+ is an encoding length: 0 2 4 or 8. If zero
+ it means the following is address-size.
+ The address then follows immediately for
+ that number of bytes. */
+static int
+read_encoded_addr(Dwarf_Small *loc_ptr, Dwarf_Debug dbg,
+ Dwarf_Unsigned * val_out,
+ int * len_out,
+ Dwarf_Error *error)
+{
+ int totallen = 0;
+ int oplen = 0;
+ int len = 0;
+ Dwarf_Small op = *loc_ptr;
+ Dwarf_Unsigned operand = 0;
+ len++;
+ if(op == 0) {
+ /* FIXME: should be CU specific. */
+ op = dbg->de_pointer_size;
+ }
+ switch(op) {
+ case 1:
+ *val_out = *loc_ptr;
+ len++;
+ break;
+
+ case 2:
+ READ_UNALIGNED(dbg, operand, Dwarf_Unsigned, loc_ptr, 2);
+ *val_out = operand;
+ len +=2;
+ break;
+ case 4:
+ READ_UNALIGNED(dbg, operand, Dwarf_Unsigned, loc_ptr, 4);
+ *val_out = operand;
+ len +=4;
+ break;
+ case 8:
+ READ_UNALIGNED(dbg, operand, Dwarf_Unsigned, loc_ptr, 8);
+ *val_out = operand;
+ len +=8;
+ break;
+ default:
+ /* We do not know how much to read. */
+ _dwarf_error(dbg, error, DW_DLE_GNU_OPCODE_ERROR);
+ return DW_DLV_ERROR;
+ };
+ *len_out = len;
+ return DW_DLV_OK;
+}
+
+
+
+/* Given a Dwarf_Block that represents a location expression,
+ this function returns a pointer to a Dwarf_Locdesc struct
+ that has its ld_cents field set to the number of location
+ operators in the block, and its ld_s field pointing to a
+ contiguous block of Dwarf_Loc structs. However, the
+ ld_lopc and ld_hipc values are uninitialized. Returns
+ NULL on error. This function assumes that the length of
+ the block is greater than 0. Zero length location expressions
+ to represent variables that have been optimized away are
+ handled in the calling function.
+*/
+static Dwarf_Locdesc *
+_dwarf_get_locdesc(Dwarf_Debug dbg,
+ Dwarf_Block * loc_block,
+ Dwarf_Half address_size,
+ Dwarf_Addr lowpc,
+ Dwarf_Addr highpc,
+ Dwarf_Error * error)
+{
+ /* Size of the block containing the location expression. */
+ Dwarf_Unsigned loc_len = 0;
+
+ /* Sweeps the block containing the location expression. */
+ Dwarf_Small *loc_ptr = 0;
+
+ /* Offset of current operator from start of block. */
+ Dwarf_Unsigned offset = 0;
+
+ /* Used to chain the Dwarf_Loc_Chain_s structs. */
+ Dwarf_Loc_Chain curr_loc = NULL;
+ Dwarf_Loc_Chain prev_loc = NULL;
+ Dwarf_Loc_Chain head_loc = NULL;
+
+ /* Count of the number of location operators. */
+ Dwarf_Unsigned op_count = 0;
+
+ /* Contiguous block of Dwarf_Loc's for Dwarf_Locdesc. */
+ Dwarf_Loc *block_loc = 0;
+
+ /* Dwarf_Locdesc pointer to be returned. */
+ Dwarf_Locdesc *locdesc = 0;
+
+ Dwarf_Word leb128_length = 0;
+ Dwarf_Unsigned i = 0;
+
+ /* ***** BEGIN CODE ***** */
+
+ loc_len = loc_block->bl_len;
+ loc_ptr = loc_block->bl_data;
+
+ offset = 0;
+ op_count = 0;
+ while (offset < loc_len) {
+ Dwarf_Unsigned operand1 = 0;
+ Dwarf_Unsigned operand2 = 0;
+ Dwarf_Small atom = 0;
+
+ op_count++;
+ atom = *(Dwarf_Small *) loc_ptr;
+ loc_ptr++;
+ offset++;
+ curr_loc =
+ (Dwarf_Loc_Chain) _dwarf_get_alloc(dbg, DW_DLA_LOC_CHAIN,
+ 1);
+ if (curr_loc == NULL) {
+ /* Some memory may leak here. */
+ _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
+ return (NULL);
+ }
+ curr_loc->lc_offset = offset;
+ curr_loc->lc_atom = atom;
+ switch (atom) {
+
+ case DW_OP_reg0:
+ case DW_OP_reg1:
+ case DW_OP_reg2:
+ case DW_OP_reg3:
+ case DW_OP_reg4:
+ case DW_OP_reg5:
+ case DW_OP_reg6:
+ case DW_OP_reg7:
+ case DW_OP_reg8:
+ case DW_OP_reg9:
+ case DW_OP_reg10:
+ case DW_OP_reg11:
+ case DW_OP_reg12:
+ case DW_OP_reg13:
+ case DW_OP_reg14:
+ case DW_OP_reg15:
+ case DW_OP_reg16:
+ case DW_OP_reg17:
+ case DW_OP_reg18:
+ case DW_OP_reg19:
+ case DW_OP_reg20:
+ case DW_OP_reg21:
+ case DW_OP_reg22:
+ case DW_OP_reg23:
+ case DW_OP_reg24:
+ case DW_OP_reg25:
+ case DW_OP_reg26:
+ case DW_OP_reg27:
+ case DW_OP_reg28:
+ case DW_OP_reg29:
+ case DW_OP_reg30:
+ case DW_OP_reg31:
+ break;
+
+ case DW_OP_regx:
+ operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+ break;
+
+ case DW_OP_lit0:
+ case DW_OP_lit1:
+ case DW_OP_lit2:
+ case DW_OP_lit3:
+ case DW_OP_lit4:
+ case DW_OP_lit5:
+ case DW_OP_lit6:
+ case DW_OP_lit7:
+ case DW_OP_lit8:
+ case DW_OP_lit9:
+ case DW_OP_lit10:
+ case DW_OP_lit11:
+ case DW_OP_lit12:
+ case DW_OP_lit13:
+ case DW_OP_lit14:
+ case DW_OP_lit15:
+ case DW_OP_lit16:
+ case DW_OP_lit17:
+ case DW_OP_lit18:
+ case DW_OP_lit19:
+ case DW_OP_lit20:
+ case DW_OP_lit21:
+ case DW_OP_lit22:
+ case DW_OP_lit23:
+ case DW_OP_lit24:
+ case DW_OP_lit25:
+ case DW_OP_lit26:
+ case DW_OP_lit27:
+ case DW_OP_lit28:
+ case DW_OP_lit29:
+ case DW_OP_lit30:
+ case DW_OP_lit31:
+ operand1 = atom - DW_OP_lit0;
+ break;
+
+ case DW_OP_addr:
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned,
+ loc_ptr, address_size);
+ loc_ptr += address_size;
+ offset += address_size;
+ break;
+
+ case DW_OP_const1u:
+ operand1 = *(Dwarf_Small *) loc_ptr;
+ loc_ptr = loc_ptr + 1;
+ offset = offset + 1;
+ break;
+
+ case DW_OP_const1s:
+ operand1 = *(Dwarf_Sbyte *) loc_ptr;
+ SIGN_EXTEND(operand1,1);
+ loc_ptr = loc_ptr + 1;
+ offset = offset + 1;
+ break;
+
+ case DW_OP_const2u:
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 2);
+ loc_ptr = loc_ptr + 2;
+ offset = offset + 2;
+ break;
+
+ case DW_OP_const2s:
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 2);
+ SIGN_EXTEND(operand1,2);
+ loc_ptr = loc_ptr + 2;
+ offset = offset + 2;
+ break;
+
+ case DW_OP_const4u:
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 4);
+ loc_ptr = loc_ptr + 4;
+ offset = offset + 4;
+ break;
+
+ case DW_OP_const4s:
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 4);
+ SIGN_EXTEND(operand1,4);
+ loc_ptr = loc_ptr + 4;
+ offset = offset + 4;
+ break;
+
+ case DW_OP_const8u:
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 8);
+ loc_ptr = loc_ptr + 8;
+ offset = offset + 8;
+ break;
+
+ case DW_OP_const8s:
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 8);
+ loc_ptr = loc_ptr + 8;
+ offset = offset + 8;
+ break;
+
+ case DW_OP_constu:
+ operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+ break;
+
+ case DW_OP_consts:
+ operand1 = _dwarf_decode_s_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+ break;
+
+ case DW_OP_fbreg:
+ operand1 = _dwarf_decode_s_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+ break;
+
+ case DW_OP_breg0:
+ case DW_OP_breg1:
+ case DW_OP_breg2:
+ case DW_OP_breg3:
+ case DW_OP_breg4:
+ case DW_OP_breg5:
+ case DW_OP_breg6:
+ case DW_OP_breg7:
+ case DW_OP_breg8:
+ case DW_OP_breg9:
+ case DW_OP_breg10:
+ case DW_OP_breg11:
+ case DW_OP_breg12:
+ case DW_OP_breg13:
+ case DW_OP_breg14:
+ case DW_OP_breg15:
+ case DW_OP_breg16:
+ case DW_OP_breg17:
+ case DW_OP_breg18:
+ case DW_OP_breg19:
+ case DW_OP_breg20:
+ case DW_OP_breg21:
+ case DW_OP_breg22:
+ case DW_OP_breg23:
+ case DW_OP_breg24:
+ case DW_OP_breg25:
+ case DW_OP_breg26:
+ case DW_OP_breg27:
+ case DW_OP_breg28:
+ case DW_OP_breg29:
+ case DW_OP_breg30:
+ case DW_OP_breg31:
+ operand1 = _dwarf_decode_s_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+ break;
+
+ case DW_OP_bregx:
+ /* uleb reg num followed by sleb offset */
+ operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+
+ operand2 = _dwarf_decode_s_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+ break;
+
+ case DW_OP_dup:
+ case DW_OP_drop:
+ break;
+
+ case DW_OP_pick:
+ operand1 = *(Dwarf_Small *) loc_ptr;
+ loc_ptr = loc_ptr + 1;
+ offset = offset + 1;
+ break;
+
+ case DW_OP_over:
+ case DW_OP_swap:
+ case DW_OP_rot:
+ case DW_OP_deref:
+ break;
+
+ case DW_OP_deref_size:
+ operand1 = *(Dwarf_Small *) loc_ptr;
+ loc_ptr = loc_ptr + 1;
+ offset = offset + 1;
+ break;
+
+ case DW_OP_xderef:
+ break;
+
+ case DW_OP_xderef_size:
+ operand1 = *(Dwarf_Small *) loc_ptr;
+ loc_ptr = loc_ptr + 1;
+ offset = offset + 1;
+ break;
+
+ case DW_OP_abs:
+ case DW_OP_and:
+ case DW_OP_div:
+ case DW_OP_minus:
+ case DW_OP_mod:
+ case DW_OP_mul:
+ case DW_OP_neg:
+ case DW_OP_not:
+ case DW_OP_or:
+ case DW_OP_plus:
+ break;
+
+ case DW_OP_plus_uconst:
+ operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+ break;
+
+ case DW_OP_shl:
+ case DW_OP_shr:
+ case DW_OP_shra:
+ case DW_OP_xor:
+ break;
+
+ case DW_OP_le:
+ case DW_OP_ge:
+ case DW_OP_eq:
+ case DW_OP_lt:
+ case DW_OP_gt:
+ case DW_OP_ne:
+ break;
+
+ case DW_OP_skip:
+ case DW_OP_bra:
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 2);
+ loc_ptr = loc_ptr + 2;
+ offset = offset + 2;
+ break;
+
+ case DW_OP_piece:
+ operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+ break;
+
+ case DW_OP_nop:
+ break;
+ case DW_OP_push_object_address: /* DWARF3 */
+ break;
+ case DW_OP_call2: /* DWARF3 */
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 2);
+ loc_ptr = loc_ptr + 2;
+ offset = offset + 2;
+ break;
+
+ case DW_OP_call4: /* DWARF3 */
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr, 4);
+ loc_ptr = loc_ptr + 4;
+ offset = offset + 4;
+ break;
+ case DW_OP_call_ref: /* DWARF3 */
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr,
+ dbg->de_length_size);
+ loc_ptr = loc_ptr + dbg->de_length_size;
+ offset = offset + dbg->de_length_size;
+ break;
+
+ case DW_OP_form_tls_address: /* DWARF3f */
+ break;
+ case DW_OP_call_frame_cfa: /* DWARF3f */
+ break;
+ case DW_OP_bit_piece: /* DWARF3f */
+ /* uleb size in bits followed by uleb offset in bits */
+ operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+
+ operand2 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+ break;
+
+ /* The operator means: push the currently computed
+ (by the operations encountered so far in this
+ expression) onto the expression stack as the offset
+ in thread-local-storage of the variable. */
+ case DW_OP_GNU_push_tls_address:
+ break;
+
+ case DW_OP_implicit_value: /* DWARF4 */
+ /* uleb length of value bytes followed by that
+ number of bytes of the value. */
+ operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+
+ /* Second operand is block of 'operand1' bytes of stuff. */
+ /* This using the second operand as a pointer
+ is quite ugly. */
+ /* This gets an ugly compiler warning. Sorry. */
+ operand2 = (Dwarf_Unsigned)loc_ptr;
+ offset = offset + operand1;
+ loc_ptr = loc_ptr + operand1;
+ break;
+ case DW_OP_stack_value: /* DWARF4 */
+ break;
+ case DW_OP_GNU_uninit: /* 0xf0 GNU */
+ /* Carolyn Tice: Follws a DW_OP_reg or DW_OP_regx
+ and marks the reg as being uninitialized. */
+ break;
+ case DW_OP_GNU_encoded_addr: { /* 0xf1 GNU */
+ /* Richard Henderson: The operand is an absolute
+ address. The first byte of the value
+ is an encoding length: 0 2 4 or 8. If zero
+ it means the following is address-size.
+ The address then follows immediately for
+ that number of bytes. */
+ int length = 0;
+ int reares = read_encoded_addr(loc_ptr,dbg,&operand1,
+ &length,error);
+ if(reares != DW_DLV_OK) {
+ /* Oops. The caller will notice and
+ will issue DW_DLV_ERROR. */
+ return NULL;
+ }
+ loc_ptr += length;
+ offset += length;
+ }
+ break;
+ case DW_OP_GNU_implicit_pointer: /* 0xf2 GNU */
+ /* Jakub Jelinek: The value is an optimized-out
+ pointer value. Represented as
+ an offset_size (address_size) DIE offset
+ (as simple unsigned integer) followed by
+ a signed leb128 offset.
+ http://www.dwarfstd.org/ShowIssue.php?issue=100831.1 */
+ READ_UNALIGNED(dbg, operand1, Dwarf_Unsigned, loc_ptr,
+ dbg->de_length_size);
+ loc_ptr = loc_ptr + dbg->de_length_size;
+ offset = offset + dbg->de_length_size;
+
+ operand2 = _dwarf_decode_s_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+
+ break;
+ case DW_OP_GNU_entry_value: /* 0xf3 GNU */
+ /* Jakub Jelinek: A register reused really soon,
+ but the value is unchanged. So to represent
+ that value we have a uleb128 size followed
+ by a DWARF expression block that size.
+ http://www.dwarfstd.org/ShowIssue.php?issue=100909.1 */
+
+ /* uleb length of value bytes followed by that
+ number of bytes of the value. */
+ operand1 = _dwarf_decode_u_leb128(loc_ptr, &leb128_length);
+ loc_ptr = loc_ptr + leb128_length;
+ offset = offset + leb128_length;
+
+ /* Second operand is block of 'operand1' bytes of stuff. */
+ /* This using the second operand as a pointer
+ is quite ugly. */
+ /* This gets an ugly compiler warning. Sorry. */
+ operand2 = (Dwarf_Unsigned)loc_ptr;
+ offset = offset + operand1;
+ loc_ptr = loc_ptr + operand1;
+ break;
+
+ default:
+ /* Some memory does leak here. */
+
+ _dwarf_error(dbg, error, DW_DLE_LOC_EXPR_BAD);
+ return (NULL);
+ }
+
+ /* If offset == loc_len this would be normal end-of-expression. */
+ if (offset > loc_len) {
+ /* We stepped past the end of the expression.
+ This has to be a compiler bug.
+ Operators missing their values cannot be detected
+ as such except at the end of an expression (like this).
+ The results would be wrong if returned.
+ Some memory may leak here.
+ */
+ _dwarf_error(dbg, error, DW_DLE_LOC_BAD_TERMINATION);
+ return (NULL);
+ }
+
+ curr_loc->lc_number = operand1;
+ curr_loc->lc_number2 = operand2;
+
+ if (head_loc == NULL)
+ head_loc = prev_loc = curr_loc;
+ else {
+ prev_loc->lc_next = curr_loc;
+ prev_loc = curr_loc;
+ }
+ }
+
+ block_loc =
+ (Dwarf_Loc *) _dwarf_get_alloc(dbg, DW_DLA_LOC_BLOCK, op_count);
+ if (block_loc == NULL) {
+ /* Some memory does leak here. */
+ _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
+ return (NULL);
+ }
+
+ curr_loc = head_loc;
+ for (i = 0; i < op_count; i++) {
+ (block_loc + i)->lr_atom = curr_loc->lc_atom;
+ (block_loc + i)->lr_number = curr_loc->lc_number;
+ (block_loc + i)->lr_number2 = curr_loc->lc_number2;
+ (block_loc + i)->lr_offset = curr_loc->lc_offset;
+
+ prev_loc = curr_loc;
+ curr_loc = curr_loc->lc_next;
+ dwarf_dealloc(dbg, prev_loc, DW_DLA_LOC_CHAIN);
+ }
+
+ locdesc =
+ (Dwarf_Locdesc *) _dwarf_get_alloc(dbg, DW_DLA_LOCDESC, 1);
+ if (locdesc == NULL) {
+ _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
+ return (NULL);
+ }
+
+ locdesc->ld_cents = op_count;
+ locdesc->ld_s = block_loc;
+ locdesc->ld_from_loclist = loc_block->bl_from_loclist;
+ locdesc->ld_section_offset = loc_block->bl_section_offset;
+ locdesc->ld_lopc = lowpc;
+ locdesc->ld_hipc = highpc;
+
+ return (locdesc);
+}
+
+/* Using a loclist offset to get the in-memory
+ address of .debug_loc data to read, returns the loclist
+ 'header' info in return_block.
+*/
+
+#define MAX_ADDR ((address_size == 8)?0xffffffffffffffffULL:0xffffffff)
+
+static int
+_dwarf_read_loc_section(Dwarf_Debug dbg,
+ Dwarf_Block * return_block,
+ Dwarf_Addr * lowpc, Dwarf_Addr * hipc,
+ Dwarf_Off sec_offset,
+ Dwarf_Half address_size,
+ Dwarf_Error * error)
+{
+ Dwarf_Small *beg = dbg->de_debug_loc.dss_data + sec_offset;
+
+ Dwarf_Addr start_addr = 0;
+ Dwarf_Addr end_addr = 0;
+ Dwarf_Half exprblock_size = 0;
+ Dwarf_Unsigned exprblock_off =
+ 2 * address_size + sizeof(Dwarf_Half);
+
+ if (sec_offset >= dbg->de_debug_loc.dss_size) {
+ /* We're at the end. No more present. */
+ return DW_DLV_NO_ENTRY;
+ }
+
+ /* If it goes past end, error */
+ if (exprblock_off > dbg->de_debug_loc.dss_size) {
+ _dwarf_error(NULL, error, DW_DLE_DEBUG_LOC_SECTION_SHORT);
+ return DW_DLV_ERROR;
+ }
+
+ READ_UNALIGNED(dbg, start_addr, Dwarf_Addr, beg, address_size);
+ READ_UNALIGNED(dbg, end_addr, Dwarf_Addr,
+ beg + address_size, address_size);
+ if (start_addr == 0 && end_addr == 0) {
+ /* If start_addr and end_addr are 0, it's the end and no
+ exprblock_size field follows. */
+ exprblock_size = 0;
+ exprblock_off -= sizeof(Dwarf_Half);
+ } else if (start_addr == MAX_ADDR) {
+ /* End address is a base address, no exprblock_size field here
+ either */
+ exprblock_size = 0;
+ exprblock_off -= sizeof(Dwarf_Half);
+ } else {
+
+ READ_UNALIGNED(dbg, exprblock_size, Dwarf_Half,
+ beg + 2 * address_size, sizeof(Dwarf_Half));
+ /* exprblock_size can be zero, means no expression */
+ if ((exprblock_off + exprblock_size) > dbg->de_debug_loc.dss_size) {
+ _dwarf_error(NULL, error, DW_DLE_DEBUG_LOC_SECTION_SHORT);
+ return DW_DLV_ERROR;
+ }
+ }
+#undef MAX_ADDR
+ *lowpc = start_addr;
+ *hipc = end_addr;
+
+ return_block->bl_len = exprblock_size;
+ return_block->bl_from_loclist = 1;
+ return_block->bl_data = beg + exprblock_off;
+ return_block->bl_section_offset =
+ ((Dwarf_Small *) return_block->bl_data) - dbg->de_debug_loc.dss_data;
+
+ return DW_DLV_OK;
+
+}
+static int
+_dwarf_get_loclist_count(Dwarf_Debug dbg,
+ Dwarf_Off loclist_offset,
+ Dwarf_Half address_size,
+ int *loclist_count, Dwarf_Error * error)
+{
+ int count = 0;
+ Dwarf_Off offset = loclist_offset;
+
+
+ for (;;) {
+ Dwarf_Block b;
+ Dwarf_Addr lowpc;
+ Dwarf_Addr highpc;
+ int res = _dwarf_read_loc_section(dbg, &b,
+ &lowpc, &highpc,
+ offset, address_size,error);
+ if (res != DW_DLV_OK) {
+ return res;
+ }
+ offset = b.bl_len + b.bl_section_offset;
+ if (lowpc == 0 && highpc == 0) {
+ break;
+ }
+ count++;
+ }
+ *loclist_count = count;
+ return DW_DLV_OK;
+}
+
+/* Helper routine to avoid code duplication.
+*/
+static int
+_dwarf_setup_loc(Dwarf_Attribute attr,
+ Dwarf_Debug * dbg_ret,
+ Dwarf_CU_Context *cucontext_ret,
+ Dwarf_Half * form_ret, Dwarf_Error * error)
+{
+ Dwarf_Debug dbg = 0;
+ Dwarf_Half form = 0;
+ int blkres = DW_DLV_ERROR;
+
+ if (attr == NULL) {
+ _dwarf_error(NULL, error, DW_DLE_ATTR_NULL);
+ return (DW_DLV_ERROR);
+ }
+ if (attr->ar_cu_context == NULL) {
+ _dwarf_error(NULL, error, DW_DLE_ATTR_NO_CU_CONTEXT);
+ return (DW_DLV_ERROR);
+ }
+ *cucontext_ret = attr->ar_cu_context;
+
+ dbg = attr->ar_cu_context->cc_dbg;
+ if (dbg == NULL) {
+ _dwarf_error(NULL, error, DW_DLE_ATTR_DBG_NULL);
+ return (DW_DLV_ERROR);
+ }
+ *dbg_ret = dbg;
+ blkres = dwarf_whatform(attr, &form, error);
+ if (blkres != DW_DLV_OK) {
+ _dwarf_error(dbg, error, DW_DLE_LOC_EXPR_BAD);
+ return blkres;
+ }
+ *form_ret = form;
+ return DW_DLV_OK;
+}
+
+/* Helper routine to avoid code duplication.
+*/
+static int
+_dwarf_get_loclist_header_start(Dwarf_Debug dbg,
+ Dwarf_Attribute attr,
+ Dwarf_Unsigned * loclist_offset,
+ Dwarf_Error * error)
+{
+ int blkres = dwarf_global_formref(attr, loclist_offset, error);
+ if (blkres != DW_DLV_OK) {
+ return (blkres);
+ }
+
+ if (!dbg->de_debug_loc.dss_data) {
+ int secload = _dwarf_load_section(dbg, &dbg->de_debug_loc,error);
+ if (secload != DW_DLV_OK) {
+ return secload;
+ }
+ if (!dbg->de_debug_loc.dss_size) {
+ return (DW_DLV_NO_ENTRY);
+ }
+ }
+ return DW_DLV_OK;
+}
+
+/* When llbuf (see dwarf_loclist_n) is partially set up
+ and an error is encountered, tear it down as it
+ won't be used.
+*/
+static void
+_dwarf_cleanup_llbuf(Dwarf_Debug dbg, Dwarf_Locdesc ** llbuf, int count)
+{
+ int i;
+ for (i = 0; i < count; ++i) {
+ dwarf_dealloc(dbg, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
+ dwarf_dealloc(dbg, llbuf[i], DW_DLA_LOCDESC);
+ }
+ dwarf_dealloc(dbg, llbuf, DW_DLA_LIST);
+}
+
+/* Handles simple location entries and loclists.
+ Returns all the Locdesc's thru llbuf. */
+int
+dwarf_loclist_n(Dwarf_Attribute attr,
+ Dwarf_Locdesc *** llbuf_out,
+ Dwarf_Signed * listlen_out, Dwarf_Error * error)
+{
+ Dwarf_Debug dbg;
+
+ /* Dwarf_Attribute that describes the DW_AT_location in die, if
+ present. */
+ Dwarf_Attribute loc_attr = attr;
+
+ /* Dwarf_Block that describes a single location expression. */
+ Dwarf_Block loc_block;
+
+ /* A pointer to the current Dwarf_Locdesc read. */
+ Dwarf_Locdesc *locdesc = 0;
+
+ Dwarf_Half form = 0;
+ Dwarf_Addr lowpc = 0;
+ Dwarf_Addr highpc = 0;
+ Dwarf_Signed listlen = 0;
+ Dwarf_Locdesc **llbuf = 0;
+ Dwarf_CU_Context cucontext = 0;
+ unsigned address_size = 0;
+
+ int blkres = DW_DLV_ERROR;
+ int setup_res = DW_DLV_ERROR;
+
+ /* ***** BEGIN CODE ***** */
+ setup_res = _dwarf_setup_loc(attr, &dbg,&cucontext, &form, error);
+ if (setup_res != DW_DLV_OK) {
+ return setup_res;
+ }
+ address_size = cucontext->cc_address_size;
+ /* If this is a form_block then it's a location expression. If it's
+ DW_FORM_data4 or DW_FORM_data8 it's a loclist offset */
+ if (((cucontext->cc_version_stamp == CURRENT_VERSION_STAMP ||
+ cucontext->cc_version_stamp == CURRENT_VERSION_STAMP3) &&
+ (form == DW_FORM_data4 || form == DW_FORM_data8)) ||
+ (cucontext->cc_version_stamp == CURRENT_VERSION_STAMP4 &&
+ form == DW_FORM_sec_offset)) {
+
+
+ /* A reference to .debug_loc, with an offset in .debug_loc of a
+ loclist */
+ Dwarf_Unsigned loclist_offset = 0;
+ int off_res = DW_DLV_ERROR;
+ int count_res = DW_DLV_ERROR;
+ int loclist_count;
+ int lli;
+
+ off_res = _dwarf_get_loclist_header_start(dbg,
+ attr, &loclist_offset, error);
+ if (off_res != DW_DLV_OK) {
+ return off_res;
+ }
+ count_res = _dwarf_get_loclist_count(dbg, loclist_offset,
+ address_size, &loclist_count, error);
+ listlen = loclist_count;
+ if (count_res != DW_DLV_OK) {
+ return count_res;
+ }
+ if (loclist_count == 0) {
+ return DW_DLV_NO_ENTRY;
+ }
+
+ llbuf = (Dwarf_Locdesc **)
+ _dwarf_get_alloc(dbg, DW_DLA_LIST, loclist_count);
+ if (!llbuf) {
+ _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
+ return (DW_DLV_ERROR);
+ }
+
+ for (lli = 0; lli < loclist_count; ++lli) {
+ blkres = _dwarf_read_loc_section(dbg, &loc_block,
+ &lowpc,
+ &highpc,
+ loclist_offset,
+ address_size,
+ error);
+ if (blkres != DW_DLV_OK) {
+ _dwarf_cleanup_llbuf(dbg, llbuf, lli);
+ return (blkres);
+ }
+ locdesc = _dwarf_get_locdesc(dbg, &loc_block,
+ address_size,
+ lowpc, highpc, error);
+ if (locdesc == NULL) {
+ _dwarf_cleanup_llbuf(dbg, llbuf, lli);
+ /* low level error already set: let it be passed back */
+ return (DW_DLV_ERROR);
+ }
+ llbuf[lli] = locdesc;
+
+ /* Now get to next loclist entry offset. */
+ loclist_offset = loc_block.bl_section_offset +
+ loc_block.bl_len;
+ }
+
+
+ } else {
+ Dwarf_Block *tblock = 0;
+
+ blkres = dwarf_formblock(loc_attr, &tblock, error);
+ if (blkres != DW_DLV_OK) {
+ return (blkres);
+ }
+ loc_block = *tblock;
+ /* We copied tblock contents to the stack var, so can dealloc
+ tblock now. Avoids leaks. */
+ dwarf_dealloc(dbg, tblock, DW_DLA_BLOCK);
+ listlen = 1; /* One by definition of a location entry. */
+ lowpc = 0; /* HACK */
+ highpc = (Dwarf_Unsigned) (-1LL); /* HACK */
+
+ /* An empty location description (block length 0) means the
+ code generator emitted no variable, the variable was not
+ generated, it was unused or perhaps never tested after being
+ set. Dwarf2, section 2.4.1 In other words, it is not an
+ error, and we don't test for block length 0 specially here. */
+ locdesc = _dwarf_get_locdesc(dbg, &loc_block,
+ address_size,
+ lowpc, highpc, error);
+ if (locdesc == NULL) {
+ /* low level error already set: let it be passed back */
+ return (DW_DLV_ERROR);
+ }
+ llbuf = (Dwarf_Locdesc **)
+ _dwarf_get_alloc(dbg, DW_DLA_LIST, listlen);
+ if (!llbuf) {
+ /* Free the locdesc we allocated but won't use. */
+ dwarf_dealloc(dbg, locdesc, DW_DLA_LOCDESC);
+ _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
+ return (DW_DLV_ERROR);
+ }
+ llbuf[0] = locdesc;
+ }
+
+ *llbuf_out = llbuf;
+ *listlen_out = listlen;
+ return (DW_DLV_OK);
+}
+
+
+/* Handles only a location expression.
+ If called on a loclist, just returns one of those.
+ Cannot not handle a real loclist.
+ It returns the location expression as a loclist with
+ a single entry.
+ See dwarf_loclist_n() which handles any number
+ of location list entries.
+
+ This is the original definition, and it simply
+ does not work for loclists. Kept for compatibility.
+*/
+int
+dwarf_loclist(Dwarf_Attribute attr,
+ Dwarf_Locdesc ** llbuf,
+ Dwarf_Signed * listlen, Dwarf_Error * error)
+{
+ Dwarf_Debug dbg;
+
+ /* Dwarf_Attribute that describes the DW_AT_location in die, if
+ present. */
+ Dwarf_Attribute loc_attr = attr;
+
+ /* Dwarf_Block that describes a single location expression. */
+ Dwarf_Block loc_block;
+
+ /* A pointer to the current Dwarf_Locdesc read. */
+ Dwarf_Locdesc *locdesc = 0;
+
+ Dwarf_Half form = 0;
+ Dwarf_Addr lowpc = 0;
+ Dwarf_Addr highpc = 0;
+ Dwarf_CU_Context cucontext = 0;
+ unsigned address_size = 0;
+
+ int blkres = DW_DLV_ERROR;
+ int setup_res = DW_DLV_ERROR;
+
+ /* ***** BEGIN CODE ***** */
+ setup_res = _dwarf_setup_loc(attr, &dbg, &cucontext, &form, error);
+ if (setup_res != DW_DLV_OK) {
+ return setup_res;
+ }
+ address_size = cucontext->cc_address_size;
+ /* If this is a form_block then it's a location expression. If it's
+ DW_FORM_data4 or DW_FORM_data8 it's a loclist offset */
+ if (((cucontext->cc_version_stamp == CURRENT_VERSION_STAMP ||
+ cucontext->cc_version_stamp == CURRENT_VERSION_STAMP3) &&
+ (form == DW_FORM_data4 || form == DW_FORM_data8)) ||
+ (cucontext->cc_version_stamp == CURRENT_VERSION_STAMP4 &&
+ form == DW_FORM_sec_offset))
+ {
+
+ /* A reference to .debug_loc, with an offset in .debug_loc of a
+ loclist. */
+ Dwarf_Unsigned loclist_offset = 0;
+ int off_res = DW_DLV_ERROR;
+
+ off_res = _dwarf_get_loclist_header_start(dbg,
+ attr, &loclist_offset,
+ error);
+ if (off_res != DW_DLV_OK) {
+ return off_res;
+ }
+
+ /* With dwarf_loclist, just read a single entry */
+ blkres = _dwarf_read_loc_section(dbg, &loc_block,
+ &lowpc,
+ &highpc,
+ loclist_offset,
+ address_size,
+ error);
+ if (blkres != DW_DLV_OK) {
+ return (blkres);
+ }
+ } else {
+ Dwarf_Block *tblock = 0;
+
+ blkres = dwarf_formblock(loc_attr, &tblock, error);
+ if (blkres != DW_DLV_OK) {
+ return (blkres);
+ }
+ loc_block = *tblock;
+ /* We copied tblock contents to the stack var, so can dealloc
+ tblock now. Avoids leaks. */
+ dwarf_dealloc(dbg, tblock, DW_DLA_BLOCK);
+ lowpc = 0; /* HACK */
+ highpc = (Dwarf_Unsigned) (-1LL); /* HACK */
+ }
+
+ /* An empty location description (block length 0) means the code
+ generator emitted no variable, the variable was not generated,
+ it was unused or perhaps never tested after being set. Dwarf2,
+ section 2.4.1 In other words, it is not an error, and we don't
+ test for block length 0 specially here.
+ See *dwarf_loclist_n() which handles the general case, this case
+ handles only a single location expression. */
+ locdesc = _dwarf_get_locdesc(dbg, &loc_block,
+ address_size,
+ lowpc, highpc, error);
+ if (locdesc == NULL) {
+ /* low level error already set: let it be passed back */
+ return (DW_DLV_ERROR);
+ }
+
+ *llbuf = locdesc;
+ *listlen = 1;
+ return (DW_DLV_OK);
+}
+
+
+
+/* Handles only a location expression.
+ It returns the location expression as a loclist with
+ a single entry.
+
+ Usable to access dwarf expressions from any source, but
+ specifically from
+ DW_CFA_def_cfa_expression
+ DW_CFA_expression
+ DW_CFA_val_expression
+
+ expression_in must point to a valid dwarf expression
+ set of bytes of length expression_length. Not
+ a DW_FORM_block*, just the expression bytes.
+
+ If the address_size != de_pointer_size this will not work
+ right. FIXME.
+*/
+int
+dwarf_loclist_from_expr(Dwarf_Debug dbg,
+ Dwarf_Ptr expression_in,
+ Dwarf_Unsigned expression_length,
+ Dwarf_Locdesc ** llbuf,
+ Dwarf_Signed * listlen, Dwarf_Error * error)
+{
+ int res = 0;
+ Dwarf_Half addr_size = dbg->de_pointer_size;
+ res = dwarf_loclist_from_expr_a(dbg,expression_in,
+ expression_length, addr_size,llbuf,listlen,error);
+ return res;
+}
+
+/* New April 27 2009. Adding addr_size argument for the rare
+ cases where an object has CUs with a different address_size. */
+int
+dwarf_loclist_from_expr_a(Dwarf_Debug dbg,
+ Dwarf_Ptr expression_in,
+ Dwarf_Unsigned expression_length,
+ Dwarf_Half addr_size,
+ Dwarf_Locdesc ** llbuf,
+ Dwarf_Signed * listlen, Dwarf_Error * error)
+{
+ /* Dwarf_Block that describes a single location expression. */
+ Dwarf_Block loc_block;
+
+ /* A pointer to the current Dwarf_Locdesc read. */
+ Dwarf_Locdesc *locdesc = 0;
+ Dwarf_Addr lowpc = 0;
+ Dwarf_Addr highpc = (Dwarf_Unsigned) (-1LL);
+
+ memset(&loc_block,0,sizeof(loc_block));
+ loc_block.bl_len = expression_length;
+ loc_block.bl_data = expression_in;
+ loc_block.bl_from_loclist = 0; /* Not from loclist. */
+ loc_block.bl_section_offset = 0; /* Fake. Not meaningful. */
+
+ /* An empty location description (block length 0) means the code
+ generator emitted no variable, the variable was not generated,
+ it was unused or perhaps never tested after being set. Dwarf2,
+ section 2.4.1 In other words, it is not an error, and we don't
+ test for block length 0 specially here. */
+ locdesc = _dwarf_get_locdesc(dbg, &loc_block,
+ addr_size,lowpc, highpc, error);
+ if (locdesc == NULL) {
+ /* low level error already set: let it be passed back */
+ return (DW_DLV_ERROR);
+ }
+
+ *llbuf = locdesc;
+ *listlen = 1;
+ return (DW_DLV_OK);
+}
+
+/* Usable to read a single loclist or to read a block of them
+ or to read an entire section's loclists.
+
+ It's broken because it's not safe to read a loclist entry
+ when we do not know the address size (in any object where
+ address size can vary by compilation unit).
+*/
+
+/*ARGSUSED*/ int
+dwarf_get_loclist_entry(Dwarf_Debug dbg,
+ Dwarf_Unsigned offset,
+ Dwarf_Addr * hipc_offset,
+ Dwarf_Addr * lopc_offset,
+ Dwarf_Ptr * data,
+ Dwarf_Unsigned * entry_len,
+ Dwarf_Unsigned * next_entry,
+ Dwarf_Error * error)
+{
+ Dwarf_Block b;
+ Dwarf_Addr lowpc = 0;
+ Dwarf_Addr highpc = 0;
+ Dwarf_Half address_size = 0;
+ int res = DW_DLV_ERROR;
+
+ if (!dbg->de_debug_loc.dss_data) {
+ int secload = _dwarf_load_section(dbg, &dbg->de_debug_loc,error);
+ if (secload != DW_DLV_OK) {
+ return secload;
+ }
+ }
+
+ /* FIXME: address_size is not necessarily the same in every frame. */
+ address_size = dbg->de_pointer_size;
+ res = _dwarf_read_loc_section(dbg,
+ &b, &lowpc, &highpc, offset,
+ address_size,error);
+ if (res != DW_DLV_OK) {
+ return res;
+ }
+ *hipc_offset = highpc;
+ *lopc_offset = lowpc;
+ *entry_len = b.bl_len;
+ *data = b.bl_data;
+ *next_entry = b.bl_len + b.bl_section_offset;
+ return DW_DLV_OK;
+}
+
+