dwarf_getlocation.c revision f0371041995308d197447019eb2ac9285c96477b
1/* Return location expression list.
2   Copyright (C) 2000-2009 Red Hat, Inc.
3   This file is part of Red Hat elfutils.
4   Written by Ulrich Drepper <drepper@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#include <assert.h>
59
60#include <libdwP.h>
61
62
63static bool
64attr_ok (Dwarf_Attribute *attr)
65{
66  if (attr == NULL)
67    return false;
68
69  /* Must be one of the attributes listed below.  */
70  switch (attr->code)
71    {
72    case DW_AT_location:
73    case DW_AT_data_member_location:
74    case DW_AT_vtable_elem_location:
75    case DW_AT_string_length:
76    case DW_AT_use_location:
77    case DW_AT_frame_base:
78    case DW_AT_return_addr:
79    case DW_AT_static_link:
80      break;
81
82    default:
83      __libdw_seterrno (DWARF_E_NO_LOCLIST);
84      return false;
85    }
86
87  return true;
88}
89
90
91struct loclist
92{
93  uint8_t atom;
94  Dwarf_Word number;
95  Dwarf_Word number2;
96  Dwarf_Word offset;
97  struct loclist *next;
98};
99
100
101static int
102loc_compare (const void *p1, const void *p2)
103{
104  const struct loc_s *l1 = (const struct loc_s *) p1;
105  const struct loc_s *l2 = (const struct loc_s *) p2;
106
107  if ((uintptr_t) l1->addr < (uintptr_t) l2->addr)
108    return -1;
109  if ((uintptr_t) l1->addr > (uintptr_t) l2->addr)
110    return 1;
111
112  return 0;
113}
114
115/* For each DW_OP_implicit_value, we store a special entry in the cache.
116   This points us directly to the block data for later fetching.  */
117static void
118store_implicit_value (Dwarf *dbg, void **cache, Dwarf_Op *op,
119		      unsigned char *data)
120{
121  struct loc_block_s *block = libdw_alloc (dbg, struct loc_block_s,
122					   sizeof (struct loc_block_s), 1);
123  block->addr = op;
124  block->data = data + op->number2;
125  block->length = op->number;
126  (void) tsearch (block, cache, loc_compare);
127}
128
129int
130dwarf_getlocation_implicit_value (attr, op, return_block)
131     Dwarf_Attribute *attr;
132     Dwarf_Op *op;
133     Dwarf_Block *return_block;
134{
135  if (attr == NULL)
136    return -1;
137
138  struct loc_block_s fake = { .addr = op };
139  struct loc_block_s **found = tfind (&fake, &attr->cu->locs, loc_compare);
140  if (unlikely (found == NULL))
141    {
142      __libdw_seterrno (DWARF_E_NO_BLOCK);
143      return -1;
144    }
145
146  return_block->length = (*found)->length;
147  return_block->data = (*found)->data;
148  return 0;
149}
150
151/* DW_AT_data_member_location can be a constant as well as a loclistptr.
152   Only data[48] indicate a loclistptr.  */
153static int
154check_constant_offset (Dwarf_Attribute *attr,
155		       Dwarf_Op **llbuf, size_t *listlen)
156{
157  if (attr->code != DW_AT_data_member_location
158      || attr->form == DW_FORM_data4
159      || attr->form == DW_FORM_data8)
160    return 1;
161
162  /* Check whether we already cached this location.  */
163  struct loc_s fake = { .addr = attr->valp };
164  struct loc_s **found = tfind (&fake, &attr->cu->locs, loc_compare);
165
166  if (found == NULL)
167    {
168      Dwarf_Word offset;
169      if (INTUSE(dwarf_formudata) (attr, &offset) != 0)
170	return -1;
171
172      Dwarf_Op *result = libdw_alloc (attr->cu->dbg,
173				      Dwarf_Op, sizeof (Dwarf_Op), 1);
174
175      result->atom = DW_OP_plus_uconst;
176      result->number = offset;
177      result->number2 = 0;
178      result->offset = 0;
179
180      /* Insert a record in the search tree so we can find it again later.  */
181      struct loc_s *newp = libdw_alloc (attr->cu->dbg,
182					struct loc_s, sizeof (struct loc_s),
183					1);
184      newp->addr = attr->valp;
185      newp->loc = result;
186      newp->nloc = 1;
187
188      found = tsearch (newp, &attr->cu->locs, loc_compare);
189    }
190
191  assert ((*found)->nloc == 1);
192
193  if (llbuf != NULL)
194    {
195      *llbuf = (*found)->loc;
196      *listlen = 1;
197    }
198
199  return 0;
200}
201
202int
203internal_function
204__libdw_intern_expression (Dwarf *dbg,
205			   bool other_byte_order, unsigned int address_size,
206			   void **cache, const Dwarf_Block *block, bool valuep,
207			   Dwarf_Op **llbuf, size_t *listlen, int sec_index)
208{
209  /* Check whether we already looked at this list.  */
210  struct loc_s fake = { .addr = block->data };
211  struct loc_s **found = tfind (&fake, cache, loc_compare);
212  if (found != NULL)
213    {
214      /* We already saw it.  */
215      *llbuf = (*found)->loc;
216      *listlen = (*found)->nloc;
217
218      if (valuep)
219	{
220	  assert (*listlen > 1);
221	  assert ((*llbuf)[*listlen - 1].atom == DW_OP_stack_value);
222	}
223
224      return 0;
225    }
226
227  const unsigned char *data = block->data;
228  const unsigned char *const end_data = data + block->length;
229
230  const struct { bool other_byte_order; } bo = { other_byte_order };
231
232  struct loclist *loclist = NULL;
233  unsigned int n = 0;
234  /* Decode the opcodes.  It is possible in some situations to have a
235     block of size zero.  */
236  while (data < end_data)
237    {
238      struct loclist *newloc;
239      newloc = (struct loclist *) alloca (sizeof (struct loclist));
240      newloc->number = 0;
241      newloc->number2 = 0;
242      newloc->offset = data - block->data;
243      newloc->next = loclist;
244      loclist = newloc;
245      ++n;
246
247      switch ((newloc->atom = *data++))
248	{
249	case DW_OP_addr:
250	  /* Address, depends on address size of CU.  */
251	  if (__libdw_read_address_inc (dbg, sec_index, &data,
252					address_size, &newloc->number))
253	    return -1;
254	  break;
255
256	case DW_OP_deref:
257	case DW_OP_dup:
258	case DW_OP_drop:
259	case DW_OP_over:
260	case DW_OP_swap:
261	case DW_OP_rot:
262	case DW_OP_xderef:
263	case DW_OP_abs:
264	case DW_OP_and:
265	case DW_OP_div:
266	case DW_OP_minus:
267	case DW_OP_mod:
268	case DW_OP_mul:
269	case DW_OP_neg:
270	case DW_OP_not:
271	case DW_OP_or:
272	case DW_OP_plus:
273	case DW_OP_shl:
274	case DW_OP_shr:
275	case DW_OP_shra:
276	case DW_OP_xor:
277	case DW_OP_eq:
278	case DW_OP_ge:
279	case DW_OP_gt:
280	case DW_OP_le:
281	case DW_OP_lt:
282	case DW_OP_ne:
283	case DW_OP_lit0 ... DW_OP_lit31:
284	case DW_OP_reg0 ... DW_OP_reg31:
285	case DW_OP_nop:
286	case DW_OP_push_object_address:
287	case DW_OP_call_ref:
288	case DW_OP_call_frame_cfa:
289	case DW_OP_form_tls_address:
290	case DW_OP_GNU_push_tls_address:
291	case DW_OP_stack_value:
292	  /* No operand.  */
293	  break;
294
295	case DW_OP_const1u:
296	case DW_OP_pick:
297	case DW_OP_deref_size:
298	case DW_OP_xderef_size:
299	  if (unlikely (data >= end_data))
300	    {
301	    invalid:
302	      __libdw_seterrno (DWARF_E_INVALID_DWARF);
303	      return -1;
304	    }
305
306	  newloc->number = *data++;
307	  break;
308
309	case DW_OP_const1s:
310	  if (unlikely (data >= end_data))
311	    goto invalid;
312
313	  newloc->number = *((int8_t *) data);
314	  ++data;
315	  break;
316
317	case DW_OP_const2u:
318	  if (unlikely (data + 2 > end_data))
319	    goto invalid;
320
321	  newloc->number = read_2ubyte_unaligned_inc (&bo, data);
322	  break;
323
324	case DW_OP_const2s:
325	case DW_OP_skip:
326	case DW_OP_bra:
327	case DW_OP_call2:
328	  if (unlikely (data + 2 > end_data))
329	    goto invalid;
330
331	  newloc->number = read_2sbyte_unaligned_inc (&bo, data);
332	  break;
333
334	case DW_OP_const4u:
335	  if (unlikely (data + 4 > end_data))
336	    goto invalid;
337
338	  newloc->number = read_4ubyte_unaligned_inc (&bo, data);
339	  break;
340
341	case DW_OP_const4s:
342	case DW_OP_call4:
343	  if (unlikely (data + 4 > end_data))
344	    goto invalid;
345
346	  newloc->number = read_4sbyte_unaligned_inc (&bo, data);
347	  break;
348
349	case DW_OP_const8u:
350	  if (unlikely (data + 8 > end_data))
351	    goto invalid;
352
353	  newloc->number = read_8ubyte_unaligned_inc (&bo, data);
354	  break;
355
356	case DW_OP_const8s:
357	  if (unlikely (data + 8 > end_data))
358	    goto invalid;
359
360	  newloc->number = read_8sbyte_unaligned_inc (&bo, data);
361	  break;
362
363	case DW_OP_constu:
364	case DW_OP_plus_uconst:
365	case DW_OP_regx:
366	case DW_OP_piece:
367	  /* XXX Check size.  */
368	  get_uleb128 (newloc->number, data);
369	  break;
370
371	case DW_OP_consts:
372	case DW_OP_breg0 ... DW_OP_breg31:
373	case DW_OP_fbreg:
374	  /* XXX Check size.  */
375	  get_sleb128 (newloc->number, data);
376	  break;
377
378	case DW_OP_bregx:
379	  /* XXX Check size.  */
380	  get_uleb128 (newloc->number, data);
381	  get_sleb128 (newloc->number2, data);
382	  break;
383
384	case DW_OP_bit_piece:
385	  /* XXX Check size.  */
386	  get_uleb128 (newloc->number, data);
387	  get_uleb128 (newloc->number2, data);
388	  break;
389
390	case DW_OP_implicit_value:
391	  /* This cannot be used in a CFI expression.  */
392	  if (unlikely (dbg == NULL))
393	    goto invalid;
394
395	  /* XXX Check size.  */
396	  get_uleb128 (newloc->number, data); /* Block length.  */
397	  if (unlikely ((Dwarf_Word) (end_data - data) < newloc->number))
398	    goto invalid;
399	  newloc->number2 = data - block->data; /* Relative block offset.  */
400	  data += newloc->number;		/* Skip the block.  */
401	  break;
402
403	default:
404	  goto invalid;
405	}
406    }
407
408  if (unlikely (n == 0))
409    {
410      /* This is not allowed.
411
412	 XXX Is it?  */
413      goto invalid;
414    }
415
416  if (valuep)
417    {
418      struct loclist *newloc;
419      newloc = (struct loclist *) alloca (sizeof (struct loclist));
420      newloc->atom = DW_OP_stack_value;
421      newloc->number = 0;
422      newloc->number2 = 0;
423      newloc->offset = data - block->data;
424      newloc->next = loclist;
425      loclist = newloc;
426      ++n;
427    }
428
429  /* Allocate the array.  */
430  Dwarf_Op *result;
431  if (dbg != NULL)
432    result = libdw_alloc (dbg, Dwarf_Op, sizeof (Dwarf_Op), n);
433  else
434    {
435      result = malloc (sizeof *result * n);
436      if (result == NULL)
437	{
438	nomem:
439	  __libdw_seterrno (DWARF_E_NOMEM);
440	  return -1;
441	}
442    }
443
444  /* Store the result.  */
445  *llbuf = result;
446  *listlen = n;
447
448  do
449    {
450      /* We populate the array from the back since the list is backwards.  */
451      --n;
452      result[n].atom = loclist->atom;
453      result[n].number = loclist->number;
454      result[n].number2 = loclist->number2;
455      result[n].offset = loclist->offset;
456
457      if (result[n].atom == DW_OP_implicit_value)
458	store_implicit_value (dbg, cache, &result[n], block->data);
459
460      loclist = loclist->next;
461    }
462  while (n > 0);
463
464  /* Insert a record in the search tree so that we can find it again later.  */
465  struct loc_s *newp;
466  if (dbg != NULL)
467    newp = libdw_alloc (dbg, struct loc_s, sizeof (struct loc_s), 1);
468  else
469    {
470      newp = malloc (sizeof *newp);
471      if (newp == NULL)
472	{
473	  free (result);
474	  goto nomem;
475	}
476    }
477
478  newp->addr = block->data;
479  newp->loc = result;
480  newp->nloc = *listlen;
481  (void) tsearch (newp, cache, loc_compare);
482
483  /* We did it.  */
484  return 0;
485}
486
487static int
488getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
489	     Dwarf_Op **llbuf, size_t *listlen, int sec_index)
490{
491  return __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order,
492				    cu->address_size, &cu->locs, block, false,
493				    llbuf, listlen, sec_index);
494}
495
496int
497dwarf_getlocation (attr, llbuf, listlen)
498     Dwarf_Attribute *attr;
499     Dwarf_Op **llbuf;
500     size_t *listlen;
501{
502  int result = check_constant_offset (attr, llbuf, listlen);
503  if (result != 1)
504    return result;
505
506  if (! attr_ok (attr))
507    return -1;
508
509  /* If it has a block form, it's a single location expression.  */
510  Dwarf_Block block;
511  if (INTUSE(dwarf_formblock) (attr, &block) != 0)
512    return -1;
513
514  return getlocation (attr->cu, &block, llbuf, listlen, IDX_debug_info);
515}
516
517int
518dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
519     Dwarf_Attribute *attr;
520     Dwarf_Addr address;
521     Dwarf_Op **llbufs;
522     size_t *listlens;
523     size_t maxlocs;
524{
525  if (! attr_ok (attr))
526    return -1;
527
528  if (llbufs == NULL)
529    maxlocs = SIZE_MAX;
530
531  /* If it has a block form, it's a single location expression.  */
532  Dwarf_Block block;
533  if (INTUSE(dwarf_formblock) (attr, &block) == 0)
534    {
535      if (maxlocs == 0)
536	return 0;
537      if (llbufs != NULL &&
538	  getlocation (attr->cu, &block, &llbufs[0], &listlens[0],
539		       IDX_debug_info) != 0)
540	return -1;
541      return listlens[0] == 0 ? 0 : 1;
542    }
543
544  int error = INTUSE(dwarf_errno) ();
545  if (unlikely (error != DWARF_E_NO_BLOCK))
546    {
547      __libdw_seterrno (error);
548      return -1;
549    }
550
551  int result = check_constant_offset (attr, &llbufs[0], &listlens[0]);
552  if (result != 1)
553    return result ?: 1;
554
555  unsigned char *endp;
556  unsigned char *readp = __libdw_formptr (attr, IDX_debug_loc,
557					  DWARF_E_NO_LOCLIST, &endp, NULL);
558  if (readp == NULL)
559    return -1;
560
561  Dwarf_Addr base = (Dwarf_Addr) -1;
562  size_t got = 0;
563  while (got < maxlocs)
564    {
565      if (endp - readp < attr->cu->address_size * 2)
566	{
567	invalid:
568	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
569	  return -1;
570	}
571
572      Dwarf_Addr begin;
573      Dwarf_Addr end;
574
575      int status
576	= __libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc,
577					   &readp, attr->cu->address_size,
578					   &begin, &end, &base);
579      if (status == 2) /* End of list entry.  */
580	break;
581      else if (status == 1) /* Base address selected.  */
582	continue;
583      else if (status < 0)
584	return status;
585
586      if (endp - readp < 2)
587	goto invalid;
588
589      /* We have a location expression.  */
590      block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp);
591      block.data = readp;
592      if (endp - readp < (ptrdiff_t) block.length)
593	goto invalid;
594      readp += block.length;
595
596      if (base == (Dwarf_Addr) -1)
597	{
598	  /* Fetch the CU's base address.  */
599	  Dwarf_Die cudie = CUDIE (attr->cu);
600
601	  /* Find the base address of the compilation unit.  It will
602	     normally be specified by DW_AT_low_pc.  In DWARF-3 draft 4,
603	     the base address could be overridden by DW_AT_entry_pc.  It's
604	     been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc
605	     for compilation units with discontinuous ranges.  */
606	  Dwarf_Attribute attr_mem;
607	  if (unlikely (INTUSE(dwarf_lowpc) (&cudie, &base) != 0)
608	      && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
609							     DW_AT_entry_pc,
610							     &attr_mem),
611					 &base) != 0)
612	    {
613	      if (INTUSE(dwarf_errno) () != 0)
614		return -1;
615
616	      /* The compiler provided no base address when it should
617		 have.  Buggy GCC does this when it used absolute
618		 addresses in the location list and no DW_AT_ranges.  */
619	      base = 0;
620	    }
621	}
622
623      if (address >= base + begin && address < base + end)
624	{
625	  /* This one matches the address.  */
626	  if (llbufs != NULL
627	      && unlikely (getlocation (attr->cu, &block,
628					&llbufs[got], &listlens[got],
629					IDX_debug_loc) != 0))
630	    return -1;
631	  ++got;
632	}
633    }
634
635  return got;
636}
637