Home | History | Annotate | Download | only in libdw
      1 /* Return location expression list.
      2    Copyright (C) 2000, 2001, 2002, 2004 Red Hat, Inc.
      3    Written by Ulrich Drepper <drepper (at) redhat.com>, 2000.
      4 
      5    This program is Open Source software; you can redistribute it and/or
      6    modify it under the terms of the Open Software License version 1.0 as
      7    published by the Open Source Initiative.
      8 
      9    You should have received a copy of the Open Software License along
     10    with this program; if not, you may obtain a copy of the Open Software
     11    License version 1.0 from http://www.opensource.org/licenses/osl.php or
     12    by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
     13    3001 King Ranch Road, Ukiah, CA 95482.   */
     14 
     15 #ifdef HAVE_CONFIG_H
     16 # include <config.h>
     17 #endif
     18 
     19 #include <dwarf.h>
     20 #include <search.h>
     21 #include <stdlib.h>
     22 
     23 #include <libdwP.h>
     24 
     25 
     26 struct loclist
     27 {
     28   uint8_t atom;
     29   Dwarf_Word number;
     30   Dwarf_Word number2;
     31   Dwarf_Word offset;
     32   struct loclist *next;
     33 };
     34 
     35 
     36 static int
     37 loc_compare (const void *p1, const void *p2)
     38 {
     39   const struct loc_s *l1 = (const struct loc_s *) p1;
     40   const struct loc_s *l2 = (const struct loc_s *) p2;
     41 
     42   if ((uintptr_t) l1->addr < (uintptr_t) l2->addr)
     43     return -1;
     44   if ((uintptr_t) l1->addr > (uintptr_t) l2->addr)
     45     return 1;
     46 
     47   return 0;
     48 }
     49 
     50 
     51 int
     52 dwarf_getloclist (attr, llbuf, listlen)
     53      Dwarf_Attribute *attr;
     54      Dwarf_Loc **llbuf;
     55      size_t *listlen;
     56 {
     57   /* Must by one of the attribute listed below.  */
     58   if (attr->code != DW_AT_location
     59       && attr->code != DW_AT_data_member_location
     60       && attr->code != DW_AT_vtable_elem_location
     61       && attr->code != DW_AT_string_length
     62       && attr->code != DW_AT_use_location
     63       && attr->code != DW_AT_return_addr)
     64     {
     65       __libdw_seterrno (DWARF_E_NO_LOCLIST);
     66       return -1;
     67     }
     68 
     69   struct Dwarf_CU *cu = attr->cu;
     70   Dwarf *dbg = cu->dbg;
     71 
     72   /* Must have the form data4 or data8 which act as an offset.  */
     73   Dwarf_Block block;
     74   if (dwarf_formblock (attr, &block) != 0)
     75     return -1;
     76 
     77   /* Check whether we already looked at this list.  */
     78   struct loc_s fake = { .addr = block.data };
     79   struct loc_s **found = tfind (&fake, &cu->locs, loc_compare);
     80   if (found != NULL)
     81     {
     82       /* We already saw it.  */
     83       *llbuf = (*found)->loc;
     84       *listlen = (*found)->nloc;
     85 
     86       return 0;
     87     }
     88 
     89   unsigned char *data = block.data;
     90   unsigned char *const end_data = data + block.length;
     91 
     92   struct loclist *loclist = NULL;
     93   unsigned int n = 0;
     94   /* Decode the opcodes.  It is possible in some situations to have a
     95      block of size zero.  */
     96   while (data < end_data)
     97     {
     98       struct loclist *newloc;
     99       newloc = (struct loclist *) alloca (sizeof (struct loclist));
    100       newloc->number = 0;
    101       newloc->number2 = 0;
    102       newloc->offset = data - block.data;
    103       newloc->next = loclist;
    104       loclist = newloc;
    105       ++n;
    106 
    107       switch ((newloc->atom = *data++))
    108 	{
    109 	case DW_OP_addr:
    110 	  /* Address, depends on address size of CU.  */
    111 	  if (cu->address_size == 4)
    112 	    {
    113 	      if (unlikely (data + 4 > end_data))
    114 		{
    115 		invalid:
    116 		  __libdw_seterrno (DWARF_E_INVALID_DWARF);
    117 		  return -1;
    118 		}
    119 
    120 	      newloc->number = read_4ubyte_unaligned_inc (dbg, data);
    121 	    }
    122 	  else
    123 	    {
    124 	      if (unlikely (data + 8 > end_data))
    125 		goto invalid;
    126 
    127 	      newloc->number = read_8ubyte_unaligned_inc (dbg, data);
    128 	    }
    129 	  break;
    130 
    131 	case DW_OP_deref:
    132 	case DW_OP_dup:
    133 	case DW_OP_drop:
    134 	case DW_OP_over:
    135 	case DW_OP_swap:
    136 	case DW_OP_rot:
    137 	case DW_OP_xderef:
    138 	case DW_OP_abs:
    139 	case DW_OP_and:
    140 	case DW_OP_div:
    141 	case DW_OP_minus:
    142 	case DW_OP_mod:
    143 	case DW_OP_mul:
    144 	case DW_OP_neg:
    145 	case DW_OP_not:
    146 	case DW_OP_or:
    147 	case DW_OP_plus:
    148 	case DW_OP_shl:
    149 	case DW_OP_shr:
    150 	case DW_OP_shra:
    151 	case DW_OP_xor:
    152 	case DW_OP_eq:
    153 	case DW_OP_ge:
    154 	case DW_OP_gt:
    155 	case DW_OP_le:
    156 	case DW_OP_lt:
    157 	case DW_OP_ne:
    158 	case DW_OP_lit0 ... DW_OP_lit31:
    159 	case DW_OP_reg0 ... DW_OP_reg31:
    160 	case DW_OP_nop:
    161 	case DW_OP_push_object_address:
    162 	case DW_OP_call_ref:
    163 	  /* No operand.  */
    164 	  break;
    165 
    166 	case DW_OP_const1u:
    167 	case DW_OP_pick:
    168 	case DW_OP_deref_size:
    169 	case DW_OP_xderef_size:
    170 	  if (unlikely (data >= end_data))
    171 	    goto invalid;
    172 
    173 	  newloc->number = *data++;
    174 	  break;
    175 
    176 	case DW_OP_const1s:
    177 	  if (unlikely (data >= end_data))
    178 	    goto invalid;
    179 
    180 	  newloc->number = *((int8_t *) data);
    181 	  ++data;
    182 	  break;
    183 
    184 	case DW_OP_const2u:
    185 	  if (unlikely (data + 2 > end_data))
    186 	    goto invalid;
    187 
    188 	  newloc->number = read_2ubyte_unaligned_inc (dbg, data);
    189 	  break;
    190 
    191 	case DW_OP_const2s:
    192 	case DW_OP_skip:
    193 	case DW_OP_bra:
    194 	case DW_OP_call2:
    195 	  if (unlikely (data + 2 > end_data))
    196 	    goto invalid;
    197 
    198 	  newloc->number = read_2sbyte_unaligned_inc (dbg, data);
    199 	  break;
    200 
    201 	case DW_OP_const4u:
    202 	  if (unlikely (data + 4 > end_data))
    203 	    goto invalid;
    204 
    205 	  newloc->number = read_4ubyte_unaligned_inc (dbg, data);
    206 	  break;
    207 
    208 	case DW_OP_const4s:
    209 	case DW_OP_call4:
    210 	  if (unlikely (data + 4 > end_data))
    211 	    goto invalid;
    212 
    213 	  newloc->number = read_4sbyte_unaligned_inc (dbg, data);
    214 	  break;
    215 
    216 	case DW_OP_const8u:
    217 	  if (unlikely (data + 8 > end_data))
    218 	    goto invalid;
    219 
    220 	  newloc->number = read_8ubyte_unaligned_inc (dbg, data);
    221 	  break;
    222 
    223 	case DW_OP_const8s:
    224 	  if (unlikely (data + 8 > end_data))
    225 	    goto invalid;
    226 
    227 	  newloc->number = read_8sbyte_unaligned_inc (dbg, data);
    228 	  break;
    229 
    230 	case DW_OP_constu:
    231 	case DW_OP_plus_uconst:
    232 	case DW_OP_regx:
    233 	case DW_OP_piece:
    234 	  /* XXX Check size.  */
    235 	  get_uleb128 (newloc->number, data);
    236 	  break;
    237 
    238 	case DW_OP_consts:
    239 	case DW_OP_breg0 ... DW_OP_breg31:
    240 	case DW_OP_fbreg:
    241 	  /* XXX Check size.  */
    242 	  get_sleb128 (newloc->number, data);
    243 	  break;
    244 
    245 	case DW_OP_bregx:
    246 	  /* XXX Check size.  */
    247 	  get_uleb128 (newloc->number, data);
    248 	  get_sleb128 (newloc->number2, data);
    249 	  break;
    250 
    251 	default:
    252 	  goto invalid;
    253 	}
    254     }
    255 
    256   if (unlikely (n == 0))
    257     {
    258       /* This is not allowed.
    259 
    260 	 XXX Is it?  */
    261       goto invalid;
    262     }
    263 
    264   /* Allocate the array.  */
    265   Dwarf_Loc *result = libdw_alloc (dbg, Dwarf_Loc, sizeof (Dwarf_Loc), n);
    266 
    267   /* Store the result.  */
    268   *llbuf = result;
    269   *listlen = n;
    270 
    271   do
    272     {
    273       /* We populate the array from the back since the list is
    274          backwards.  */
    275       --n;
    276       result[n].atom = loclist->atom;
    277       result[n].number = loclist->number;
    278       result[n].number2 = loclist->number2;
    279       result[n].offset = loclist->offset;
    280 
    281       loclist = loclist->next;
    282     }
    283   while (n > 0);
    284 
    285   /* Insert a record in the search tree so that we can find it again
    286      later.  */
    287   struct loc_s *newp = libdw_alloc (dbg, struct loc_s, sizeof (struct loc_s),
    288 				    1);
    289   newp->addr = block.data;
    290   newp->loc = result;
    291   newp->nloc = *listlen;
    292   (void) tsearch (newp, &cu->locs, loc_compare);
    293 
    294   /* We did it.  */
    295   return 0;
    296 }
    297