diff options
Diffstat (limited to 'libdwarf/dwarf_loclist.c')
-rw-r--r-- | libdwarf/dwarf_loclist.c | 470 |
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; +} |