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@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
62static bool
63attr_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
90struct 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
100static int
101loc_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
114static int
115getlocation (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
341int
342dwarf_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
358int
359dwarf_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