Home | History | Annotate | Download | only in libdw
      1 /* Return location expression list.
      2    Copyright (C) 2000, 2001, 2002, 2004, 2005, 2006 Red Hat, Inc.
      3    This file is part of Red Hat elfutils.
      4    Written by Ulrich Drepper <drepper (at) redhat.com>, 2000.
      5 
      6    Red Hat elfutils is free software; you can redistribute it and/or modify
      7    it under the terms of the GNU General Public License as published by the
      8    Free Software Foundation; version 2 of the License.
      9 
     10    Red Hat elfutils is distributed in the hope that it will be useful, but
     11    WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13    General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License along
     16    with Red Hat elfutils; if not, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     18 
     19    In addition, as a special exception, Red Hat, Inc. gives You the
     20    additional right to link the code of Red Hat elfutils with code licensed
     21    under any Open Source Initiative certified open source license
     22    (http://www.opensource.org/licenses/index.php) which requires the
     23    distribution of source code with any binary distribution and to
     24    distribute linked combinations of the two.  Non-GPL Code permitted under
     25    this exception must only link to the code of Red Hat elfutils through
     26    those well defined interfaces identified in the file named EXCEPTION
     27    found in the source code files (the "Approved Interfaces").  The files
     28    of Non-GPL Code may instantiate templates or use macros or inline
     29    functions from the Approved Interfaces without causing the resulting
     30    work to be covered by the GNU General Public License.  Only Red Hat,
     31    Inc. may make changes or additions to the list of Approved Interfaces.
     32    Red Hat's grant of this exception is conditioned upon your not adding
     33    any new exceptions.  If you wish to add a new Approved Interface or
     34    exception, please contact Red Hat.  You must obey the GNU General Public
     35    License in all respects for all of the Red Hat elfutils code and other
     36    code used in conjunction with Red Hat elfutils except the Non-GPL Code
     37    covered by this exception.  If you modify this file, you may extend this
     38    exception to your version of the file, but you are not obligated to do
     39    so.  If you do not wish to provide this exception without modification,
     40    you must delete this exception statement from your version and license
     41    this file solely under the GPL without exception.
     42 
     43    Red Hat elfutils is an included package of the Open Invention Network.
     44    An included package of the Open Invention Network is a package for which
     45    Open Invention Network licensees cross-license their patents.  No patent
     46    license is granted, either expressly or impliedly, by designation as an
     47    included package.  Should you wish to participate in the Open Invention
     48    Network licensing program, please visit www.openinventionnetwork.com
     49    <http://www.openinventionnetwork.com>.  */
     50 
     51 #ifdef HAVE_CONFIG_H
     52 # include <config.h>
     53 #endif
     54 
     55 #include <dwarf.h>
     56 #include <search.h>
     57 #include <stdlib.h>
     58 
     59 #include <libdwP.h>
     60 
     61 
     62 static bool
     63 attr_ok (Dwarf_Attribute *attr)
     64 {
     65   if (attr == NULL)
     66     return false;
     67 
     68   /* Must be one of the attributes listed below.  */
     69   switch (attr->code)
     70     {
     71     case DW_AT_location:
     72     case DW_AT_data_member_location:
     73     case DW_AT_vtable_elem_location:
     74     case DW_AT_string_length:
     75     case DW_AT_use_location:
     76     case DW_AT_frame_base:
     77     case DW_AT_return_addr:
     78     case DW_AT_static_link:
     79       break;
     80 
     81     default:
     82       __libdw_seterrno (DWARF_E_NO_LOCLIST);
     83       return false;
     84     }
     85 
     86   return true;
     87 }
     88 
     89 
     90 struct loclist
     91 {
     92   uint8_t atom;
     93   Dwarf_Word number;
     94   Dwarf_Word number2;
     95   Dwarf_Word offset;
     96   struct loclist *next;
     97 };
     98 
     99 
    100 static int
    101 loc_compare (const void *p1, const void *p2)
    102 {
    103   const struct loc_s *l1 = (const struct loc_s *) p1;
    104   const struct loc_s *l2 = (const struct loc_s *) p2;
    105 
    106   if ((uintptr_t) l1->addr < (uintptr_t) l2->addr)
    107     return -1;
    108   if ((uintptr_t) l1->addr > (uintptr_t) l2->addr)
    109     return 1;
    110 
    111   return 0;
    112 }
    113 
    114 static int
    115 getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
    116 	     Dwarf_Op **llbuf, size_t *listlen)
    117 {
    118   Dwarf *dbg = cu->dbg;
    119 
    120   /* Check whether we already looked at this list.  */
    121   struct loc_s fake = { .addr = block->data };
    122   struct loc_s **found = tfind (&fake, &cu->locs, loc_compare);
    123   if (found != NULL)
    124     {
    125       /* We already saw it.  */
    126       *llbuf = (*found)->loc;
    127       *listlen = (*found)->nloc;
    128 
    129       return 0;
    130     }
    131 
    132   const unsigned char *data = block->data;
    133   const unsigned char *const end_data = data + block->length;
    134 
    135   struct loclist *loclist = NULL;
    136   unsigned int n = 0;
    137   /* Decode the opcodes.  It is possible in some situations to have a
    138      block of size zero.  */
    139   while (data < end_data)
    140     {
    141       struct loclist *newloc;
    142       newloc = (struct loclist *) alloca (sizeof (struct loclist));
    143       newloc->number = 0;
    144       newloc->number2 = 0;
    145       newloc->offset = data - block->data;
    146       newloc->next = loclist;
    147       loclist = newloc;
    148       ++n;
    149 
    150       switch ((newloc->atom = *data++))
    151 	{
    152 	case DW_OP_addr:
    153 	  /* Address, depends on address size of CU.  */
    154 	  if (cu->address_size == 4)
    155 	    {
    156 	      if (unlikely (data + 4 > end_data))
    157 		{
    158 		invalid:
    159 		  __libdw_seterrno (DWARF_E_INVALID_DWARF);
    160 		  return -1;
    161 		}
    162 
    163 	      newloc->number = read_4ubyte_unaligned_inc (dbg, data);
    164 	    }
    165 	  else
    166 	    {
    167 	      if (unlikely (data + 8 > end_data))
    168 		goto invalid;
    169 
    170 	      newloc->number = read_8ubyte_unaligned_inc (dbg, data);
    171 	    }
    172 	  break;
    173 
    174 	case DW_OP_deref:
    175 	case DW_OP_dup:
    176 	case DW_OP_drop:
    177 	case DW_OP_over:
    178 	case DW_OP_swap:
    179 	case DW_OP_rot:
    180 	case DW_OP_xderef:
    181 	case DW_OP_abs:
    182 	case DW_OP_and:
    183 	case DW_OP_div:
    184 	case DW_OP_minus:
    185 	case DW_OP_mod:
    186 	case DW_OP_mul:
    187 	case DW_OP_neg:
    188 	case DW_OP_not:
    189 	case DW_OP_or:
    190 	case DW_OP_plus:
    191 	case DW_OP_shl:
    192 	case DW_OP_shr:
    193 	case DW_OP_shra:
    194 	case DW_OP_xor:
    195 	case DW_OP_eq:
    196 	case DW_OP_ge:
    197 	case DW_OP_gt:
    198 	case DW_OP_le:
    199 	case DW_OP_lt:
    200 	case DW_OP_ne:
    201 	case DW_OP_lit0 ... DW_OP_lit31:
    202 	case DW_OP_reg0 ... DW_OP_reg31:
    203 	case DW_OP_nop:
    204 	case DW_OP_push_object_address:
    205 	case DW_OP_call_ref:
    206 	  /* No operand.  */
    207 	  break;
    208 
    209 	case DW_OP_const1u:
    210 	case DW_OP_pick:
    211 	case DW_OP_deref_size:
    212 	case DW_OP_xderef_size:
    213 	  if (unlikely (data >= end_data))
    214 	    goto invalid;
    215 
    216 	  newloc->number = *data++;
    217 	  break;
    218 
    219 	case DW_OP_const1s:
    220 	  if (unlikely (data >= end_data))
    221 	    goto invalid;
    222 
    223 	  newloc->number = *((int8_t *) data);
    224 	  ++data;
    225 	  break;
    226 
    227 	case DW_OP_const2u:
    228 	  if (unlikely (data + 2 > end_data))
    229 	    goto invalid;
    230 
    231 	  newloc->number = read_2ubyte_unaligned_inc (dbg, data);
    232 	  break;
    233 
    234 	case DW_OP_const2s:
    235 	case DW_OP_skip:
    236 	case DW_OP_bra:
    237 	case DW_OP_call2:
    238 	  if (unlikely (data + 2 > end_data))
    239 	    goto invalid;
    240 
    241 	  newloc->number = read_2sbyte_unaligned_inc (dbg, data);
    242 	  break;
    243 
    244 	case DW_OP_const4u:
    245 	  if (unlikely (data + 4 > end_data))
    246 	    goto invalid;
    247 
    248 	  newloc->number = read_4ubyte_unaligned_inc (dbg, data);
    249 	  break;
    250 
    251 	case DW_OP_const4s:
    252 	case DW_OP_call4:
    253 	  if (unlikely (data + 4 > end_data))
    254 	    goto invalid;
    255 
    256 	  newloc->number = read_4sbyte_unaligned_inc (dbg, data);
    257 	  break;
    258 
    259 	case DW_OP_const8u:
    260 	  if (unlikely (data + 8 > end_data))
    261 	    goto invalid;
    262 
    263 	  newloc->number = read_8ubyte_unaligned_inc (dbg, data);
    264 	  break;
    265 
    266 	case DW_OP_const8s:
    267 	  if (unlikely (data + 8 > end_data))
    268 	    goto invalid;
    269 
    270 	  newloc->number = read_8sbyte_unaligned_inc (dbg, data);
    271 	  break;
    272 
    273 	case DW_OP_constu:
    274 	case DW_OP_plus_uconst:
    275 	case DW_OP_regx:
    276 	case DW_OP_piece:
    277 	  /* XXX Check size.  */
    278 	  get_uleb128 (newloc->number, data);
    279 	  break;
    280 
    281 	case DW_OP_consts:
    282 	case DW_OP_breg0 ... DW_OP_breg31:
    283 	case DW_OP_fbreg:
    284 	  /* XXX Check size.  */
    285 	  get_sleb128 (newloc->number, data);
    286 	  break;
    287 
    288 	case DW_OP_bregx:
    289 	  /* XXX Check size.  */
    290 	  get_uleb128 (newloc->number, data);
    291 	  get_sleb128 (newloc->number2, data);
    292 	  break;
    293 
    294 	default:
    295 	  goto invalid;
    296 	}
    297     }
    298 
    299   if (unlikely (n == 0))
    300     {
    301       /* This is not allowed.
    302 
    303 	 XXX Is it?  */
    304       goto invalid;
    305     }
    306 
    307   /* Allocate the array.  */
    308   Dwarf_Op *result = libdw_alloc (dbg, Dwarf_Op, sizeof (Dwarf_Op), n);
    309 
    310   /* Store the result.  */
    311   *llbuf = result;
    312   *listlen = n;
    313 
    314   do
    315     {
    316       /* We populate the array from the back since the list is
    317          backwards.  */
    318       --n;
    319       result[n].atom = loclist->atom;
    320       result[n].number = loclist->number;
    321       result[n].number2 = loclist->number2;
    322       result[n].offset = loclist->offset;
    323 
    324       loclist = loclist->next;
    325     }
    326   while (n > 0);
    327 
    328   /* Insert a record in the search tree so that we can find it again
    329      later.  */
    330   struct loc_s *newp = libdw_alloc (dbg, struct loc_s, sizeof (struct loc_s),
    331 				    1);
    332   newp->addr = block->data;
    333   newp->loc = result;
    334   newp->nloc = *listlen;
    335   (void) tsearch (newp, &cu->locs, loc_compare);
    336 
    337   /* We did it.  */
    338   return 0;
    339 }
    340 
    341 int
    342 dwarf_getlocation (attr, llbuf, listlen)
    343      Dwarf_Attribute *attr;
    344      Dwarf_Op **llbuf;
    345      size_t *listlen;
    346 {
    347   if (! attr_ok (attr))
    348     return -1;
    349 
    350   /* If it has a block form, it's a single location expression.  */
    351   Dwarf_Block block;
    352   if (INTUSE(dwarf_formblock) (attr, &block) != 0)
    353     return -1;
    354 
    355   return getlocation (attr->cu, &block, llbuf, listlen);
    356 }
    357 
    358 int
    359 dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
    360      Dwarf_Attribute *attr;
    361      Dwarf_Addr address;
    362      Dwarf_Op **llbufs;
    363      size_t *listlens;
    364      size_t maxlocs;
    365 {
    366   if (! attr_ok (attr))
    367     return -1;
    368 
    369   if (llbufs == NULL)
    370     maxlocs = SIZE_MAX;
    371 
    372   /* If it has a block form, it's a single location expression.  */
    373   Dwarf_Block block;
    374   if (INTUSE(dwarf_formblock) (attr, &block) == 0)
    375     {
    376       if (maxlocs == 0)
    377 	return 0;
    378       if (llbufs != NULL &&
    379 	  getlocation (attr->cu, &block, &llbufs[0], &listlens[0]) != 0)
    380 	return -1;
    381       return listlens[0] == 0 ? 0 : 1;
    382     }
    383 
    384   int error = INTUSE(dwarf_errno) ();
    385   if (unlikely (error != DWARF_E_NO_BLOCK))
    386     {
    387       __libdw_seterrno (error);
    388       return -1;
    389     }
    390 
    391   /* Must have the form data4 or data8 which act as an offset.  */
    392   Dwarf_Word offset;
    393   if (unlikely (INTUSE(dwarf_formudata) (attr, &offset) != 0))
    394     return -1;
    395 
    396   const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
    397   if (unlikely (d == NULL))
    398     {
    399       __libdw_seterrno (DWARF_E_NO_LOCLIST);
    400       return -1;
    401     }
    402 
    403   Dwarf_Addr base = (Dwarf_Addr) -1;
    404   unsigned char *readp = d->d_buf + offset;
    405   size_t got = 0;
    406   while (got < maxlocs)
    407     {
    408       if ((unsigned char *) d->d_buf + d->d_size - readp
    409 	  < attr->cu->address_size * 2)
    410 	{
    411 	invalid:
    412 	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
    413 	  return -1;
    414 	}
    415 
    416       Dwarf_Addr begin;
    417       Dwarf_Addr end;
    418       if (attr->cu->address_size == 8)
    419 	{
    420 	  begin = read_8ubyte_unaligned_inc (attr->cu->dbg, readp);
    421 	  end = read_8ubyte_unaligned_inc (attr->cu->dbg, readp);
    422 
    423 	  if (begin == (Elf64_Addr) -1l) /* Base address entry.  */
    424 	    {
    425 	      base = end;
    426 	      if (unlikely (base == (Dwarf_Addr) -1))
    427 		goto invalid;
    428 	      continue;
    429 	    }
    430 	}
    431       else
    432 	{
    433 	  begin = read_4ubyte_unaligned_inc (attr->cu->dbg, readp);
    434 	  end = read_4ubyte_unaligned_inc (attr->cu->dbg, readp);
    435 
    436 	  if (begin == (Elf32_Addr) -1) /* Base address entry.  */
    437 	    {
    438 	      base = end;
    439 	      continue;
    440 	    }
    441 	}
    442 
    443       if (begin == 0 && end == 0) /* End of list entry.  */
    444 	break;
    445 
    446       if ((unsigned char *) d->d_buf + d->d_size - readp < 2)
    447 	goto invalid;
    448 
    449       /* We have a location expression.  */
    450       block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp);
    451       block.data = readp;
    452       if ((unsigned char *) d->d_buf + d->d_size - readp
    453 	  < (ptrdiff_t) block.length)
    454 	goto invalid;
    455       readp += block.length;
    456 
    457       if (base == (Dwarf_Addr) -1)
    458 	{
    459 	  /* Fetch the CU's base address.  */
    460 	  Dwarf_Die cudie = CUDIE (attr->cu);
    461 
    462 	  /* Find the base address of the compilation unit.  It will
    463 	     normally be specified by DW_AT_low_pc.  In DWARF-3 draft 4,
    464 	     the base address could be overridden by DW_AT_entry_pc.  It's
    465 	     been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc
    466 	     for compilation units with discontinuous ranges.  */
    467 	  Dwarf_Attribute attr_mem;
    468 	  if (unlikely (INTUSE(dwarf_lowpc) (&cudie, &base) != 0)
    469 	      && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
    470 							     DW_AT_entry_pc,
    471 							     &attr_mem),
    472 					 &base) != 0)
    473 	    {
    474 	      if (INTUSE(dwarf_errno) () != 0)
    475 		return -1;
    476 
    477 	      /* The compiler provided no base address when it should
    478 		 have.  Buggy GCC does this when it used absolute
    479 		 addresses in the location list and no DW_AT_ranges.  */
    480 	      base = 0;
    481 	    }
    482 	}
    483 
    484       if (address >= base + begin && address < base + end)
    485 	{
    486 	  /* This one matches the address.  */
    487 	  if (llbufs != NULL
    488 	      && unlikely (getlocation (attr->cu, &block,
    489 					&llbufs[got], &listlens[got]) != 0))
    490 	    return -1;
    491 	  ++got;
    492 	}
    493     }
    494 
    495   return got;
    496 }
    497