• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Return location expression list.
2    Copyright (C) 2000, 2001, 2002, 2004 Red Hat, Inc.
3    Written by Ulrich Drepper <drepper@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
loc_compare(const void * p1,const void * p2)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
dwarf_getloclist(attr,llbuf,listlen)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