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