1 /* Keeping track of DWARF compilation units in libdwfl.
2    Copyright (C) 2005-2010, 2015, 2016, 2017 Red Hat, Inc.
3    This file is part of elfutils.
4 
5    This file is free software; you can redistribute it and/or modify
6    it under the terms of either
7 
8      * the GNU Lesser General Public License as published by the Free
9        Software Foundation; either version 3 of the License, or (at
10        your option) any later version
11 
12    or
13 
14      * the GNU General Public License as published by the Free
15        Software Foundation; either version 2 of the License, or (at
16        your option) any later version
17 
18    or both in parallel, as here.
19 
20    elfutils is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23    General Public License for more details.
24 
25    You should have received copies of the GNU General Public License and
26    the GNU Lesser General Public License along with this program.  If
27    not, see <http://www.gnu.org/licenses/>.  */
28 
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32 
33 #include "libdwflP.h"
34 #include "../libdw/libdwP.h"
35 #include "../libdw/memory-access.h"
36 #include <search.h>
37 
38 
39 static inline Dwarf_Arange *
dwar(Dwfl_Module * mod,unsigned int idx)40 dwar (Dwfl_Module *mod, unsigned int idx)
41 {
42   return &mod->dw->aranges->info[mod->aranges[idx].arange];
43 }
44 
45 
46 static Dwfl_Error
addrarange(Dwfl_Module * mod,Dwarf_Addr addr,struct dwfl_arange ** arange)47 addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange)
48 {
49   if (mod->aranges == NULL)
50     {
51       struct dwfl_arange *aranges = NULL;
52       Dwarf_Aranges *dwaranges = NULL;
53       size_t naranges;
54       if (INTUSE(dwarf_getaranges) (mod->dw, &dwaranges, &naranges) != 0)
55 	return DWFL_E_LIBDW;
56 
57       /* If the module has no aranges (when no code is included) we
58 	 allocate nothing.  */
59       if (naranges != 0)
60 	{
61 	  aranges = malloc (naranges * sizeof *aranges);
62 	  if (unlikely (aranges == NULL))
63 	    return DWFL_E_NOMEM;
64 
65 	  /* libdw has sorted its list by address, which is how we want it.
66 	     But the sorted list is full of not-quite-contiguous runs pointing
67 	     to the same CU.  We don't care about the little gaps inside the
68 	     module, we'll consider them part of the surrounding CU anyway.
69 	     Collect our own array with just one record for each run of ranges
70 	     pointing to one CU.  */
71 
72 	  naranges = 0;
73 	  Dwarf_Off lastcu = 0;
74 	  for (size_t i = 0; i < dwaranges->naranges; ++i)
75 	    if (i == 0 || dwaranges->info[i].offset != lastcu)
76 	      {
77 		aranges[naranges].arange = i;
78 		aranges[naranges].cu = NULL;
79 		++naranges;
80 		lastcu = dwaranges->info[i].offset;
81 	      }
82 	}
83 
84       /* Store the final array, which is probably much smaller than before.  */
85       mod->naranges = naranges;
86       if (naranges > 0)
87         mod->aranges = (realloc (aranges, naranges * sizeof aranges[0])
88 			?: aranges);
89       else if (aranges != NULL)
90 	free (aranges);
91       mod->lazycu += naranges;
92     }
93 
94   /* The address must be inside the module to begin with.  */
95   addr = dwfl_deadjust_dwarf_addr (mod, addr);
96 
97   /* The ranges are sorted by address, so we can use binary search.  */
98   size_t l = 0, u = mod->naranges;
99   while (l < u)
100     {
101       size_t idx = (l + u) / 2;
102       Dwarf_Addr start = dwar (mod, idx)->addr;
103       if (addr < start)
104 	{
105 	  u = idx;
106 	  continue;
107 	}
108       else if (addr > start)
109 	{
110 	  if (idx + 1 < mod->naranges)
111 	    {
112 	      if (addr >= dwar (mod, idx + 1)->addr)
113 		{
114 		  l = idx + 1;
115 		  continue;
116 		}
117 	    }
118 	  else
119 	    {
120 	      /* It might be in the last range.  */
121 	      const Dwarf_Arange *last
122 		= &mod->dw->aranges->info[mod->dw->aranges->naranges - 1];
123 	      if (addr > last->addr + last->length)
124 		break;
125 	    }
126 	}
127 
128       *arange = &mod->aranges[idx];
129       return DWFL_E_NOERROR;
130     }
131 
132   return DWFL_E_ADDR_OUTOFRANGE;
133 }
134 
135 
136 static void
nofree(void * arg)137 nofree (void *arg)
138 {
139   struct dwfl_cu *cu = arg;
140   if (cu == (void *) -1l)
141     return;
142 
143   assert (cu->mod->lazycu == 0);
144 }
145 
146 /* One reason fewer to keep the lazy lookup table for CUs.  */
147 static inline void
less_lazy(Dwfl_Module * mod)148 less_lazy (Dwfl_Module *mod)
149 {
150   if (--mod->lazycu > 0)
151     return;
152 
153   /* We know about all the CUs now, we don't need this table.  */
154   tdestroy (mod->lazy_cu_root, nofree);
155   mod->lazy_cu_root = NULL;
156 }
157 
158 static inline Dwarf_Off
cudie_offset(const struct dwfl_cu * cu)159 cudie_offset (const struct dwfl_cu *cu)
160 {
161   return __libdw_first_die_off_from_cu (cu->die.cu);
162 }
163 
164 static int
compare_cukey(const void * a,const void * b)165 compare_cukey (const void *a, const void *b)
166 {
167   Dwarf_Off a_off = cudie_offset (a);
168   Dwarf_Off b_off = cudie_offset (b);
169   return (a_off < b_off) ? -1 : ((a_off > b_off) ? 1 : 0);
170 }
171 
172 /* Intern the CU if necessary.  */
173 static Dwfl_Error
intern_cu(Dwfl_Module * mod,Dwarf_Off cuoff,struct dwfl_cu ** result)174 intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result)
175 {
176   if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size))
177     {
178       if (likely (mod->lazycu == 1))
179 	{
180 	  /* This is the EOF marker.  Now we have interned all the CUs.
181 	     One increment in MOD->lazycu counts not having hit EOF yet.  */
182 	  *result = (void *) -1;
183 	  less_lazy (mod);
184 	  return DWFL_E_NOERROR;
185 	}
186       else
187 	{
188 	  /* Unexpected EOF, most likely a bogus aranges.  */
189 	  return (DWFL_E (LIBDW, DWARF_E_INVALID_DWARF));
190 	}
191     }
192 
193   /* Make sure the cuoff points to a real DIE.  */
194   Dwarf_Die cudie;
195   Dwarf_Die *die = INTUSE(dwarf_offdie) (mod->dw, cuoff, &cudie);
196   if (die == NULL)
197     return DWFL_E_LIBDW;
198 
199   struct dwfl_cu key;
200   key.die.cu = die->cu;
201   struct dwfl_cu **found = tsearch (&key, &mod->lazy_cu_root, &compare_cukey);
202   if (unlikely (found == NULL))
203     return DWFL_E_NOMEM;
204 
205   if (*found == &key || *found == NULL)
206     {
207       /* This is a new entry, meaning we haven't looked at this CU.  */
208 
209       *found = NULL;
210 
211       struct dwfl_cu *cu = malloc (sizeof *cu);
212       if (unlikely (cu == NULL))
213 	return DWFL_E_NOMEM;
214 
215       cu->mod = mod;
216       cu->next = NULL;
217       cu->lines = NULL;
218       cu->die = cudie;
219 
220       struct dwfl_cu **newvec = realloc (mod->cu, ((mod->ncu + 1)
221 						   * sizeof (mod->cu[0])));
222       if (newvec == NULL)
223 	{
224 	  free (cu);
225 	  return DWFL_E_NOMEM;
226 	}
227       mod->cu = newvec;
228 
229       mod->cu[mod->ncu++] = cu;
230       if (cu->die.cu->start == 0)
231 	mod->first_cu = cu;
232 
233       *found = cu;
234     }
235 
236   *result = *found;
237   return DWFL_E_NOERROR;
238 }
239 
240 
241 /* Traverse all the CUs in the module.  */
242 
243 Dwfl_Error
244 internal_function
__libdwfl_nextcu(Dwfl_Module * mod,struct dwfl_cu * lastcu,struct dwfl_cu ** cu)245 __libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu,
246 		  struct dwfl_cu **cu)
247 {
248   Dwarf_Off cuoff;
249   struct dwfl_cu **nextp;
250 
251   if (lastcu == NULL)
252     {
253       /* Start the traversal.  */
254       cuoff = 0;
255       nextp = &mod->first_cu;
256     }
257   else
258     {
259       /* Continue following LASTCU.  */
260       cuoff = lastcu->die.cu->end;
261       nextp = &lastcu->next;
262     }
263 
264   if (*nextp == NULL)
265     {
266       size_t cuhdrsz;
267       Dwarf_Off nextoff;
268       int end = INTUSE(dwarf_nextcu) (mod->dw, cuoff, &nextoff, &cuhdrsz,
269 				      NULL, NULL, NULL);
270       if (end < 0)
271 	return DWFL_E_LIBDW;
272       if (end > 0)
273 	{
274 	  *cu = NULL;
275 	  return DWFL_E_NOERROR;
276 	}
277 
278       Dwfl_Error result = intern_cu (mod, cuoff + cuhdrsz, nextp);
279       if (result != DWFL_E_NOERROR)
280 	return result;
281 
282       if (*nextp != (void *) -1
283 	  && (*nextp)->next == NULL && nextoff == (Dwarf_Off) -1l)
284 	(*nextp)->next = (void *) -1l;
285     }
286 
287   *cu = *nextp == (void *) -1l ? NULL : *nextp;
288   return DWFL_E_NOERROR;
289 }
290 
291 
292 /* Intern the CU arange points to, if necessary.  */
293 
294 static Dwfl_Error
arangecu(Dwfl_Module * mod,struct dwfl_arange * arange,struct dwfl_cu ** cu)295 arangecu (Dwfl_Module *mod, struct dwfl_arange *arange, struct dwfl_cu **cu)
296 {
297   if (arange->cu == NULL)
298     {
299       const Dwarf_Arange *dwarange = &mod->dw->aranges->info[arange->arange];
300       Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu);
301       if (result != DWFL_E_NOERROR)
302 	return result;
303       assert (arange->cu != NULL && arange->cu != (void *) -1l);
304       less_lazy (mod);		/* Each arange with null ->cu counts once.  */
305     }
306 
307   *cu = arange->cu;
308   return DWFL_E_NOERROR;
309 }
310 
311 Dwfl_Error
312 internal_function
__libdwfl_addrcu(Dwfl_Module * mod,Dwarf_Addr addr,struct dwfl_cu ** cu)313 __libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_cu **cu)
314 {
315   struct dwfl_arange *arange;
316   return addrarange (mod, addr, &arange) ?: arangecu (mod, arange, cu);
317 }
318