summaryrefslogtreecommitdiffstats
path: root/libdwarf/dwarf_loclist.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdwarf/dwarf_loclist.c')
-rw-r--r--libdwarf/dwarf_loclist.c470
1 files changed, 470 insertions, 0 deletions
diff --git a/libdwarf/dwarf_loclist.c b/libdwarf/dwarf_loclist.c
new file mode 100644
index 00000000..22a3944f
--- /dev/null
+++ b/libdwarf/dwarf_loclist.c
@@ -0,0 +1,470 @@
+/* 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;
+}