blob: 22a3944fd2e8734516abff03f390dd6d68db60ec [file] [log] [blame]
/* Return location expression list.
Copyright (C) 2000, 2001, 2002 Red Hat, Inc.
Written by Ulrich Drepper <drepper@redhat.com>, 2000.
This program is Open Source software; you can redistribute it and/or
modify it under the terms of the Open Software License version 1.0 as
published by the Open Source Initiative.
You should have received a copy of the Open Software License along
with this program; if not, you may obtain a copy of the Open Software
License version 1.0 from http://www.opensource.org/licenses/osl.php or
by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
3001 King Ranch Road, Ukiah, CA 95482. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <dwarf.h>
#include <stdlib.h>
#include <libdwarfP.h>
struct loclist
{
Dwarf_Small atom;
Dwarf_Unsigned number;
Dwarf_Unsigned number2;
Dwarf_Unsigned offset;
struct loclist *next;
};
struct locdesclist
{
Dwarf_Addr lopc;
Dwarf_Addr hipc;
Dwarf_Half cents;
struct loclist *s;
struct locdesclist *next;
};
int
dwarf_loclist (attr, llbuf, listlen, error)
Dwarf_Attribute attr;
Dwarf_Locdesc **llbuf;
Dwarf_Signed *listlen;
Dwarf_Error *error;
{
Dwarf_CU_Info cu = attr->cu;
Dwarf_Debug dbg = cu->dbg;
Dwarf_Unsigned offset;
Dwarf_Unsigned offset_end;
Dwarf_Small *locp;
struct locdesclist *locdesclist;
Dwarf_Locdesc *result;
unsigned int n;
/* Must by one of the attribute listed below. */
if (attr->code != DW_AT_location
&& attr->code != DW_AT_data_member_location
&& attr->code != DW_AT_vtable_elem_location
&& attr->code != DW_AT_string_length
&& attr->code != DW_AT_use_location
&& attr->code != DW_AT_return_addr)
{
__libdwarf_error (dbg, error, DW_E_WRONG_ATTR);
return DW_DLV_ERROR;
}
/* Must have the form data4 or data8 which act as an offset. */
if (attr->form == DW_FORM_data4)
offset = read_4ubyte_unaligned (dbg, attr->valp);
else if (likely (attr->form == DW_FORM_data8))
offset = read_8ubyte_unaligned (dbg, attr->valp);
else
{
__libdwarf_error (dbg, error, DW_E_NO_DATA);
return DW_DLV_ERROR;
}
/* Check whether the .debug_loc section is available. */
if (unlikely (dbg->sections[IDX_debug_loc].addr == NULL))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
/* This is the part of the .debug_loc section we can read. */
locp = (Dwarf_Small *) dbg->sections[IDX_debug_loc].addr + offset;
offset_end = offset + dbg->sections[IDX_debug_loc].size;
locdesclist = NULL;
n = 0;
while (1)
{
Dwarf_Addr lopc;
Dwarf_Addr hipc;
Dwarf_Ptr data;
Dwarf_Unsigned len2;
struct locdesclist *newdesc;
Dwarf_Small *locp;
Dwarf_Small *blkendp;
Dwarf_Small *blkstartp;
if (unlikely (dwarf_get_loclist_entry (dbg, offset, &hipc, &lopc, &data,
&len2, &offset, error)
!= DW_DLV_OK))
return DW_DLV_ERROR;
/* Does this signal the end? */
if (lopc == 0 && hipc == 0)
break;
locp = data;
blkstartp = locp;
blkendp = locp + len2;
/* Create a new Locdesc entry. */
newdesc = (struct locdesclist *) alloca (sizeof (struct locdesclist));
newdesc->lopc = lopc;
newdesc->hipc = hipc;
newdesc->cents = 0;
newdesc->s = NULL;
newdesc->next = locdesclist;
locdesclist = newdesc;
++n;
/* Decode the opcodes. It is possible in some situations to
have a block of size zero. */
while (locp < blkendp)
{
struct loclist *newloc;
newloc = (struct loclist *) alloca (sizeof (struct loclist));
newloc->number = 0;
newloc->number2 = 0;
newloc->offset = locp - blkstartp;
newloc->next = newdesc->s;
newdesc->s = newloc;
++newdesc->cents;
newloc->atom = *locp;
switch (*locp++)
{
case DW_OP_addr:
/* Address, depends on address size of CU. */
if (cu->address_size == 4)
{
if (unlikely (locp + 4 > blkendp))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
newloc->number = read_4ubyte_unaligned (dbg, locp);
locp += 4;
}
else
{
if (unlikely (locp + 8 > blkendp))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
newloc->number = read_8ubyte_unaligned (dbg, locp);
locp += 8;
}
break;
case DW_OP_deref:
case DW_OP_dup:
case DW_OP_drop:
case DW_OP_over:
case DW_OP_swap:
case DW_OP_rot:
case DW_OP_xderef:
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:
case DW_OP_shl:
case DW_OP_shr:
case DW_OP_shra:
case DW_OP_xor:
case DW_OP_eq:
case DW_OP_ge:
case DW_OP_gt:
case DW_OP_le:
case DW_OP_lt:
case DW_OP_ne:
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:
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:
case DW_OP_nop:
case DW_OP_push_object_address:
case DW_OP_call_ref:
/* No operand. */
break;
case DW_OP_const1u:
case DW_OP_pick:
case DW_OP_deref_size:
case DW_OP_xderef_size:
if (unlikely (locp >= blkendp))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
newloc->number = *((uint8_t *) locp)++;
break;
case DW_OP_const1s:
if (unlikely (locp >= blkendp))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
newloc->number = *((int8_t *) locp)++;
break;
case DW_OP_const2u:
if (unlikely (locp + 2 > blkendp))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
newloc->number = read_2ubyte_unaligned (dbg, locp);
locp += 2;
break;
case DW_OP_const2s:
case DW_OP_skip:
case DW_OP_bra:
case DW_OP_call2:
if (unlikely (locp + 2 > blkendp))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
newloc->number = read_2sbyte_unaligned (dbg, locp);
locp += 2;
break;
case DW_OP_const4u:
if (unlikely (locp + 4 > blkendp))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
newloc->number = read_4ubyte_unaligned (dbg, locp);
locp += 4;
break;
case DW_OP_const4s:
case DW_OP_call4:
if (unlikely (locp + 4 > blkendp))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
newloc->number = read_4sbyte_unaligned (dbg, locp);
locp += 4;
break;
case DW_OP_const8u:
if (unlikely (locp + 8 > blkendp))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
newloc->number = read_8ubyte_unaligned (dbg, locp);
locp += 8;
break;
case DW_OP_const8s:
if (unlikely (locp + 8 > blkendp))
{
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
newloc->number = read_8sbyte_unaligned (dbg, locp);
locp += 8;
break;
case DW_OP_constu:
case DW_OP_plus_uconst:
case DW_OP_regx:
case DW_OP_piece:
/* XXX Check size. */
get_uleb128 (newloc->number, locp);
break;
case DW_OP_consts:
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:
case DW_OP_fbreg:
/* XXX Check size. */
get_sleb128 (newloc->number, locp);
break;
case DW_OP_bregx:
/* XXX Check size. */
get_uleb128 (newloc->number, locp);
get_sleb128 (newloc->number2, locp);
break;
default:
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
}
}
if (unlikely (n == 0))
{
/* This is not allowed.
XXX Is it? */
__libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
return DW_DLV_ERROR;
}
/* Allocate the array. */
result = (Dwarf_Locdesc *) malloc (n * sizeof (Dwarf_Locdesc));
if (result == NULL)
{
__libdwarf_error (dbg, error, DW_E_NOMEM);
return DW_DLV_ERROR;
}
/* Store the result. */
*llbuf = result;
*listlen = n;
do
{
unsigned int cents;
struct loclist *s;
/* We populate the array from the back since the list is
backwards. */
--n;
result[n].ld_lopc = locdesclist->lopc;
result[n].ld_hipc = locdesclist->hipc;
result[n].ld_cents = cents = locdesclist->cents;
result[n].ld_s = (Dwarf_Loc *) malloc (cents * sizeof (Dwarf_Loc));
if (result == NULL)
{
/* XXX Should be bother freeing memory? */
__libdwarf_error (dbg, error, DW_E_NOMEM);
return DW_DLV_ERROR;
}
s = locdesclist->s;
while (cents-- > 0)
{
/* This list is also backwards. */
result[n].ld_s[cents].lr_atom = s->atom;
result[n].ld_s[cents].lr_number = s->number;
result[n].ld_s[cents].lr_number2 = s->number2;
result[n].ld_s[cents].lr_offset = s->offset;
s = s->next;
}
locdesclist = locdesclist->next;
}
while (n > 0);
/* We did it. */
return DW_DLV_OK;
}