1 /* libunwind - a platform-independent unwind library
2    Copyright (C) 2003-2004 Hewlett-Packard Co
3 	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4 
5 This file is part of libunwind.
6 
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14 
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
25 
26 #include <fcntl.h>
27 #include <string.h>
28 #include <unistd.h>
29 
30 #include <sys/mman.h>
31 
32 #include "libunwind_i.h"
33 #include "dwarf-eh.h"
34 #include "dwarf_i.h"
35 
get_dyn_gp(struct elf_image * ei,Elf_W (Off)dyn_phdr_offset,unw_word_t * gp)36 static bool get_dyn_gp(struct elf_image* ei, Elf_W(Off) dyn_phdr_offset, unw_word_t* gp) {
37   Elf_W(Phdr) phdr;
38   GET_PHDR_FIELD(ei, dyn_phdr_offset, &phdr, p_offset);
39   Elf_W(Dyn) dyn;
40   Elf_W(Off) dyn_offset = phdr.p_offset;
41   unw_word_t map_size = ei->u.memory.map->end - ei->u.memory.map->start;
42   while (dyn_offset + sizeof(dyn) < map_size) {
43     GET_DYN_FIELD(ei, dyn_offset, &dyn, d_tag);
44     if (dyn.d_tag == DT_NULL) {
45       break;
46     }
47     if (dyn.d_tag == DT_PLTGOT) {
48       // Assume that _DYNAMIC is writable and GLIBC has
49       // relocated it (true for x86 at least).
50       GET_DYN_FIELD(ei, dyn_offset, &dyn, d_un.d_ptr);
51       *gp = dyn.d_un.d_ptr;
52       return true;
53     }
54     dyn_offset += sizeof(dyn);
55   }
56   Debug(1, "DT_PLTGOT not found in dynamic header\n");
57   return false;
58 }
59 
get_eh_frame_info(struct elf_image * ei,unw_word_t phdr_offset,unw_word_t load_base,unw_dyn_info_t * di_cache)60 static bool get_eh_frame_info(
61     struct elf_image* ei, unw_word_t phdr_offset, unw_word_t load_base, unw_dyn_info_t* di_cache) {
62   Elf_W(Phdr) phdr;
63   GET_PHDR_FIELD(ei, phdr_offset, &phdr, p_offset);
64   unw_word_t hdr_offset = phdr.p_offset;
65   struct dwarf_eh_frame_hdr hdr;
66   // Read the entire hdr since we are going to use every value in the struct.
67   if (sizeof(hdr) != elf_w (memory_read) (ei, ei->u.memory.map->start +phdr.p_offset,
68                             (uint8_t*) &hdr, sizeof(hdr), false)) {
69     Debug(1, "Failed to read dwarf_eh_frame_hdr from in memory elf image.\n");
70     return false;
71   }
72 
73   if (hdr.version != DW_EH_VERSION) {
74     Debug (1, "table has unexpected version %d\n", hdr.version);
75     return false;
76   }
77 
78   // Fill in a dummy proc_info structure.  We just need to fill in
79   // enough to ensure that dwarf_read_encoded_pointer() can do its
80   // job.  Since we don't have a procedure-context at this point, all
81   // we have to do is fill in the global-pointer.
82   unw_proc_info_t pi;
83   memset (&pi, 0, sizeof (pi));
84   pi.gp = di_cache->gp;
85 
86   unw_accessors_t* a = unw_get_accessors (ei->u.memory.as);
87   unw_word_t addr = (unw_word_t) (uintptr_t) (hdr_offset + sizeof(struct dwarf_eh_frame_hdr));
88   addr += ei->u.memory.map->start;
89 
90   unw_word_t eh_frame_start;
91   if (dwarf_read_encoded_pointer (ei->u.memory.as, a, &addr, hdr.eh_frame_ptr_enc, &pi,
92                                   &eh_frame_start, ei->u.memory.as_arg) < 0) {
93     Debug(1, "Failed to read encoded frame start.\n");
94     return false;
95   }
96 
97   unw_word_t fde_count;
98   if (dwarf_read_encoded_pointer (ei->u.memory.as, a, &addr, hdr.fde_count_enc, &pi,
99                                   &fde_count, ei->u.memory.as_arg) < 0) {
100     Debug(1, "Failed to read fde count.\n");
101     return false;
102   }
103 
104   if (hdr.table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
105     // Unsupported table format.
106     Debug(1, "Unsupported header table format %d\n", hdr.table_enc);
107     return false;
108   }
109 
110   di_cache->u.rti.name_ptr = 0;
111   // two 32-bit values (ip_offset/fde_offset) per table-entry:
112   di_cache->u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
113 
114   GET_PHDR_FIELD(ei, phdr_offset, &phdr, p_vaddr);
115   GET_PHDR_FIELD(ei, phdr_offset, &phdr, p_offset);
116   di_cache->u.rti.table_data =
117       load_base + phdr.p_vaddr + addr - (uintptr_t) ei->u.memory.map->start - phdr.p_offset;
118 
119   // For the binary-search table in the eh_frame_hdr, data-relative
120   // means relative to the start of that section...
121   di_cache->u.rti.segbase = ((load_base + phdr.p_vaddr) + (hdr_offset - phdr.p_offset));
122 
123   return true;
124 }
125 
dwarf_find_unwind_table_memory(struct elf_dyn_info * edi,struct elf_image * ei,unw_addr_space_t as,char * path,unw_word_t segbase,unw_word_t mapoff,unw_word_t ip)126 static bool dwarf_find_unwind_table_memory (
127     struct elf_dyn_info *edi, struct elf_image *ei, unw_addr_space_t as, char *path,
128     unw_word_t segbase, unw_word_t mapoff, unw_word_t ip) {
129   Elf_W(Ehdr) ehdr;
130   GET_EHDR_FIELD(ei, &ehdr, e_phoff, false);
131   GET_EHDR_FIELD(ei, &ehdr, e_phnum, false);
132 
133   Elf_W(Phdr) phdr;
134   Elf_W(Off) offset = ehdr.e_phoff;
135   Elf_W(Off) txt_phdr_offset = 0;
136   Elf_W(Addr) txt_pvaddr = 0;
137   Elf_W(Off) dyn_phdr_offset = 0;
138 #if UNW_TARGET_ARM
139   Elf_W(Off) arm_exidx_phdr_offset = 0;
140 #endif
141   int i;
142   unw_word_t start_ip = (unw_word_t) -1;
143   unw_word_t end_ip = 0;
144   Elf_W(Off) eh_frame_phdr_offset = 0;
145   for (i = 0; i < ehdr.e_phnum; ++i) {
146     Elf_W(Phdr) phdr;
147     GET_PHDR_FIELD(ei, offset, &phdr, p_type);
148     switch (phdr.p_type) {
149       case PT_LOAD:
150         GET_PHDR_FIELD(ei, offset, &phdr, p_vaddr);
151         if (phdr.p_vaddr < start_ip) {
152           start_ip = phdr.p_vaddr;
153         }
154 
155         GET_PHDR_FIELD(ei, offset, &phdr, p_memsz);
156         if (phdr.p_vaddr + phdr.p_memsz > end_ip) {
157           end_ip = phdr.p_vaddr + phdr.p_memsz;
158         }
159 
160         GET_PHDR_FIELD(ei, offset, &phdr, p_offset);
161         if (phdr.p_offset == mapoff) {
162           txt_phdr_offset = offset;
163           txt_pvaddr = phdr.p_vaddr;
164         }
165         break;
166 
167       case PT_GNU_EH_FRAME:
168         eh_frame_phdr_offset = offset;
169         break;
170 
171       case PT_DYNAMIC:
172         dyn_phdr_offset = offset;
173         break;
174 
175 #if UNW_TARGET_ARM
176       case PT_ARM_EXIDX:
177         arm_exidx_phdr_offset = offset;
178         break;
179 #endif
180 
181       default:
182         break;
183     }
184     offset += sizeof(phdr);
185   }
186 
187   if (txt_phdr_offset == 0) {
188     Debug(1, "PT_LOAD section not found.\n");
189     return false;
190   }
191 
192   unw_word_t load_base = segbase - txt_pvaddr;
193   start_ip += load_base;
194   end_ip += load_base;
195 
196   bool found = false;
197   if (eh_frame_phdr_offset) {
198     // For dynamicly linked executables and shared libraries,
199     // DT_PLTGOT is the value that data-relative addresses are
200     // relative to for that object.  We call this the "gp".
201     // Otherwise this is a static executable with no _DYNAMIC.  Assume
202     // that data-relative addresses are relative to 0, i.e.,
203     // absolute.
204     edi->di_cache.gp = 0;
205     if (dyn_phdr_offset) {
206       // Ignore failures, we'll attempt to keep going with a zero gp.
207       get_dyn_gp(ei, dyn_phdr_offset, &edi->di_cache.gp);
208     }
209 
210     found = get_eh_frame_info(ei, eh_frame_phdr_offset, load_base, &edi->di_cache);
211     if (found) {
212       edi->di_cache.start_ip = start_ip;
213       edi->di_cache.end_ip = end_ip;
214       edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
215     }
216   }
217 
218 #if UNW_TARGET_ARM
219   // Verify that the map contains enough space for the arm unwind data.
220   if (arm_exidx_phdr_offset &&
221     arm_exidx_phdr_offset + sizeof(phdr) < ei->u.memory.map->end - ei->u.memory.map->start) {
222     Elf_W(Phdr) phdr;
223     GET_PHDR_FIELD(ei, arm_exidx_phdr_offset, &phdr, p_vaddr);
224     GET_PHDR_FIELD(ei, arm_exidx_phdr_offset, &phdr, p_memsz);
225     edi->di_arm.u.rti.table_data = load_base + phdr.p_vaddr;
226     edi->di_arm.u.rti.table_len = phdr.p_memsz;
227 
228     edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
229     edi->di_arm.start_ip = start_ip;
230     edi->di_arm.end_ip = end_ip;
231     edi->di_arm.u.rti.name_ptr = (unw_word_t) path;
232     found = true;
233   }
234 #endif
235 
236   return found;
237 }
238 
239 int
dwarf_find_unwind_table(struct elf_dyn_info * edi,struct elf_image * ei,unw_addr_space_t as,char * path,unw_word_t segbase,unw_word_t mapoff,unw_word_t ip)240 dwarf_find_unwind_table (struct elf_dyn_info *edi, struct elf_image *ei,
241 			 unw_addr_space_t as, char *path,
242 			 unw_word_t segbase, unw_word_t mapoff, unw_word_t ip)
243 {
244   Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL;
245   unw_word_t addr, eh_frame_start, fde_count, load_base;
246 #if 0
247   // Not currently used.
248   unw_word_t max_load_addr = 0;
249 #endif
250   unw_word_t start_ip = (unw_word_t) -1;
251   unw_word_t end_ip = 0;
252   struct dwarf_eh_frame_hdr *hdr;
253   unw_proc_info_t pi;
254   unw_accessors_t *a;
255   Elf_W(Ehdr) *ehdr;
256 #if UNW_TARGET_ARM
257   const Elf_W(Phdr) *parm_exidx = NULL;
258 #endif
259   int i, ret, found = 0;
260 
261   /* XXX: Much of this code is Linux/LSB-specific.  */
262 
263   if (!ei->valid)
264     return -UNW_ENOINFO;
265 
266   if (!ei->mapped) {
267     if (dwarf_find_unwind_table_memory (edi, ei, as, path, segbase, mapoff, ip)) {
268       return 1;
269     }
270     return -UNW_ENOINFO;
271   }
272 
273   /* ANDROID support update. */
274   ehdr = ei->u.mapped.image;
275   phdr = (Elf_W(Phdr) *) ((char *) ei->u.mapped.image + ehdr->e_phoff);
276   /* End of ANDROID update. */
277 
278   for (i = 0; i < ehdr->e_phnum; ++i)
279     {
280       switch (phdr[i].p_type)
281 	{
282 	case PT_LOAD:
283 	  if (phdr[i].p_vaddr < start_ip)
284 	    start_ip = phdr[i].p_vaddr;
285 
286 	  if (phdr[i].p_vaddr + phdr[i].p_memsz > end_ip)
287 	    end_ip = phdr[i].p_vaddr + phdr[i].p_memsz;
288 
289 	  if (phdr[i].p_offset == mapoff)
290 	    ptxt = phdr + i;
291 
292 #if 0
293 	  // Not currently used.
294 	  if ((uintptr_t) ei->u.mapped.image + phdr->p_filesz > max_load_addr)
295 	    max_load_addr = (uintptr_t) ei->u.mapped.image + phdr->p_filesz;
296 #endif
297 	  break;
298 
299 	case PT_GNU_EH_FRAME:
300 	  peh_hdr = phdr + i;
301 	  break;
302 
303 	case PT_DYNAMIC:
304 	  pdyn = phdr + i;
305 	  break;
306 
307 #if UNW_TARGET_ARM
308 	case PT_ARM_EXIDX:
309 	  parm_exidx = phdr + i;
310 	  break;
311 #endif
312 
313 	default:
314 	  break;
315 	}
316     }
317 
318   if (!ptxt)
319     return 0;
320 
321   load_base = segbase - ptxt->p_vaddr;
322   start_ip += load_base;
323   end_ip += load_base;
324 
325   if (peh_hdr)
326     {
327       // For dynamicly linked executables and shared libraries,
328       // DT_PLTGOT is the value that data-relative addresses are
329       // relative to for that object.  We call this the "gp".
330       // Otherwise this is a static executable with no _DYNAMIC.  Assume
331       // that data-relative addresses are relative to 0, i.e.,
332       // absolute.
333       edi->di_cache.gp = 0;
334       if (pdyn) {
335         Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset + (char *) ei->u.mapped.image);
336         while ((char*) dyn - (char*) ei->u.mapped.image + sizeof(Elf_W(Dyn)) < ei->u.mapped.size
337                && dyn->d_tag != DT_NULL) {
338           if (dyn->d_tag == DT_PLTGOT) {
339             // Assume that _DYNAMIC is writable and GLIBC has
340             // relocated it (true for x86 at least).
341             edi->di_cache.gp = dyn->d_un.d_ptr;
342             break;
343           }
344           dyn++;
345         }
346       }
347 
348       /* ANDROID support update. */
349       hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset
350 					   + (char *) ei->u.mapped.image);
351       /* End of ANDROID update. */
352       if (hdr->version != DW_EH_VERSION)
353 	{
354 	  Debug (1, "table `%s' has unexpected version %d\n",
355 		 path, hdr->version);
356 	  return -UNW_ENOINFO;
357 	}
358 
359       a = unw_get_accessors (unw_local_addr_space);
360       /* ANDROID support update. */
361       addr = (unw_word_t) (uintptr_t) (hdr + 1);
362       /* End of ANDROID update. */
363 
364       /* Fill in a dummy proc_info structure.  We just need to fill in
365 	 enough to ensure that dwarf_read_encoded_pointer() can do its
366 	 job.  Since we don't have a procedure-context at this point, all
367 	 we have to do is fill in the global-pointer.  */
368       memset (&pi, 0, sizeof (pi));
369       pi.gp = edi->di_cache.gp;
370 
371       if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
372 					     &addr, hdr->eh_frame_ptr_enc, &pi,
373 					     &eh_frame_start, NULL)) < 0)
374 	return -UNW_ENOINFO;
375 
376       if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
377 					     &addr, hdr->fde_count_enc, &pi,
378 					     &fde_count, NULL)) < 0)
379 	return -UNW_ENOINFO;
380 
381       if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
382 	{
383 #if 1
384           // Right now do nothing.
385 	  //abort ();
386 #else
387 	  unw_word_t eh_frame_end;
388 
389 	  /* If there is no search table or it has an unsupported
390 	     encoding, fall back on linear search.  */
391 	  if (hdr->table_enc == DW_EH_PE_omit)
392 	    Debug (4, "EH lacks search table; doing linear search\n");
393 	  else
394 	    Debug (4, "EH table has encoding 0x%x; doing linear search\n",
395 		   hdr->table_enc);
396 
397 	  eh_frame_end = max_load_addr;	/* XXX can we do better? */
398 
399 	  if (hdr->fde_count_enc == DW_EH_PE_omit)
400 	    fde_count = ~0UL;
401 	  if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit)
402 	    abort ();
403 
404 	  return linear_search (unw_local_addr_space, ip,
405 				eh_frame_start, eh_frame_end, fde_count,
406 				pi, need_unwind_info, NULL);
407 #endif
408 	}
409       else
410         {
411           edi->di_cache.start_ip = start_ip;
412           edi->di_cache.end_ip = end_ip;
413           edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
414           edi->di_cache.u.rti.name_ptr = 0;
415           /* two 32-bit values (ip_offset/fde_offset) per table-entry: */
416           edi->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
417           /* ANDROID support update. */
418           edi->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
419                                            + (addr - (uintptr_t) ei->u.mapped.image
420                                            - peh_hdr->p_offset));
421           /* End of ANDROID update. */
422 
423           /* For the binary-search table in the eh_frame_hdr, data-relative
424              means relative to the start of that section... */
425 
426           /* ANDROID support update. */
427           edi->di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
428                                         + ((uintptr_t) hdr - (uintptr_t) ei->u.mapped.image
429                                         - peh_hdr->p_offset));
430           /* End of ANDROID update. */
431           found = 1;
432         }
433     }
434 
435 #if UNW_TARGET_ARM
436   if (parm_exidx)
437     {
438       edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
439       edi->di_arm.start_ip = start_ip;
440       edi->di_arm.end_ip = end_ip;
441       edi->di_arm.u.rti.name_ptr = (unw_word_t) path;
442       edi->di_arm.u.rti.table_data = load_base + parm_exidx->p_vaddr;
443       edi->di_arm.u.rti.table_len = parm_exidx->p_memsz;
444       found = 1;
445     }
446 #endif
447 
448 #ifdef CONFIG_DEBUG_FRAME
449   /* Try .debug_frame. */
450   found = dwarf_find_debug_frame (found, &edi->di_debug, ip, load_base, path,
451 				  start_ip, end_ip);
452 #endif
453 
454   return found;
455 }
456