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
36/* ANDROID support update. */
37int
38dwarf_find_unwind_table (struct elf_dyn_info *edi, struct elf_image *ei,
39			 unw_addr_space_t as, char *path,
40			 unw_word_t segbase, unw_word_t mapoff, unw_word_t ip)
41/* End of ANDROID update. */
42{
43  Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL;
44  unw_word_t addr, eh_frame_start, fde_count, load_base;
45  unw_word_t max_load_addr = 0;
46  unw_word_t start_ip = (unw_word_t) -1;
47  unw_word_t end_ip = 0;
48  struct dwarf_eh_frame_hdr *hdr;
49  unw_proc_info_t pi;
50  unw_accessors_t *a;
51  Elf_W(Ehdr) *ehdr;
52#if UNW_TARGET_ARM
53  const Elf_W(Phdr) *parm_exidx = NULL;
54#endif
55  int i, ret, found = 0;
56
57  /* XXX: Much of this code is Linux/LSB-specific.  */
58
59  /* ANDROID support update. */
60  if (!elf_w(valid_object) (ei))
61  /* End of ANDROID update. */
62    return -UNW_ENOINFO;
63
64  /* ANDROID support update. */
65  ehdr = ei->image;
66  phdr = (Elf_W(Phdr) *) ((char *) ei->image + ehdr->e_phoff);
67  /* End of ANDROID update. */
68
69  for (i = 0; i < ehdr->e_phnum; ++i)
70    {
71      switch (phdr[i].p_type)
72	{
73	case PT_LOAD:
74	  if (phdr[i].p_vaddr < start_ip)
75	    start_ip = phdr[i].p_vaddr;
76
77	  if (phdr[i].p_vaddr + phdr[i].p_memsz > end_ip)
78	    end_ip = phdr[i].p_vaddr + phdr[i].p_memsz;
79
80	  if (phdr[i].p_offset == mapoff)
81	    ptxt = phdr + i;
82
83          /* ANDROID support update. */
84	  if ((uintptr_t) ei->image + phdr->p_filesz > max_load_addr)
85	    max_load_addr = (uintptr_t) ei->image + phdr->p_filesz;
86	  break;
87          /* End of ANDROID update. */
88
89	case PT_GNU_EH_FRAME:
90	  peh_hdr = phdr + i;
91	  break;
92
93	case PT_DYNAMIC:
94	  pdyn = phdr + i;
95	  break;
96
97#if UNW_TARGET_ARM
98	case PT_ARM_EXIDX:
99	  parm_exidx = phdr + i;
100	  break;
101#endif
102
103	default:
104	  break;
105	}
106    }
107
108  if (!ptxt)
109    return 0;
110
111  load_base = segbase - ptxt->p_vaddr;
112  start_ip += load_base;
113  end_ip += load_base;
114
115  if (peh_hdr)
116    {
117      if (pdyn)
118	{
119	  /* For dynamicly linked executables and shared libraries,
120	     DT_PLTGOT is the value that data-relative addresses are
121	     relative to for that object.  We call this the "gp".  */
122		/* ANDROID support update. */
123		Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset
124						 + (char *) ei->image);
125		/* End of ANDROID update. */
126	  for (; dyn->d_tag != DT_NULL; ++dyn)
127	    if (dyn->d_tag == DT_PLTGOT)
128	      {
129		/* Assume that _DYNAMIC is writable and GLIBC has
130		   relocated it (true for x86 at least).  */
131		edi->di_cache.gp = dyn->d_un.d_ptr;
132		break;
133	      }
134	}
135      else
136	/* Otherwise this is a static executable with no _DYNAMIC.  Assume
137	   that data-relative addresses are relative to 0, i.e.,
138	   absolute.  */
139	edi->di_cache.gp = 0;
140
141      /* ANDROID support update. */
142      hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset
143					   + (char *) ei->image);
144      /* End of ANDROID update. */
145      if (hdr->version != DW_EH_VERSION)
146	{
147	  Debug (1, "table `%s' has unexpected version %d\n",
148		 path, hdr->version);
149	  return -UNW_ENOINFO;
150	}
151
152      a = unw_get_accessors (unw_local_addr_space);
153      /* ANDROID support update. */
154      addr = (unw_word_t) (uintptr_t) (hdr + 1);
155      /* End of ANDROID update. */
156
157      /* Fill in a dummy proc_info structure.  We just need to fill in
158	 enough to ensure that dwarf_read_encoded_pointer() can do it's
159	 job.  Since we don't have a procedure-context at this point, all
160	 we have to do is fill in the global-pointer.  */
161      memset (&pi, 0, sizeof (pi));
162      pi.gp = edi->di_cache.gp;
163
164      /* (Optionally) read eh_frame_ptr: */
165      if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
166					     &addr, hdr->eh_frame_ptr_enc, &pi,
167					     &eh_frame_start, NULL)) < 0)
168	return -UNW_ENOINFO;
169
170      /* (Optionally) read fde_count: */
171      if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
172					     &addr, hdr->fde_count_enc, &pi,
173					     &fde_count, NULL)) < 0)
174	return -UNW_ENOINFO;
175
176      if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
177	{
178    #if 1
179	  abort ();
180    #else
181	  unw_word_t eh_frame_end;
182
183	  /* If there is no search table or it has an unsupported
184	     encoding, fall back on linear search.  */
185	  if (hdr->table_enc == DW_EH_PE_omit)
186	    Debug (4, "EH lacks search table; doing linear search\n");
187	  else
188	    Debug (4, "EH table has encoding 0x%x; doing linear search\n",
189		   hdr->table_enc);
190
191	  eh_frame_end = max_load_addr;	/* XXX can we do better? */
192
193	  if (hdr->fde_count_enc == DW_EH_PE_omit)
194	    fde_count = ~0UL;
195	  if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit)
196	    abort ();
197
198	  return linear_search (unw_local_addr_space, ip,
199				eh_frame_start, eh_frame_end, fde_count,
200				pi, need_unwind_info, NULL);
201    #endif
202	}
203
204      edi->di_cache.start_ip = start_ip;
205      edi->di_cache.end_ip = end_ip;
206      edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
207      edi->di_cache.u.rti.name_ptr = 0;
208      /* two 32-bit values (ip_offset/fde_offset) per table-entry: */
209      edi->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
210      /* ANDROID support update. */
211      edi->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
212				       + (addr - (uintptr_t) ei->image
213					  - peh_hdr->p_offset));
214      /* End of ANDROID update. */
215
216      /* For the binary-search table in the eh_frame_hdr, data-relative
217	 means relative to the start of that section... */
218
219      /* ANDROID support update. */
220      edi->di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
221				    + ((uintptr_t) hdr - (uintptr_t) ei->image
222				       - peh_hdr->p_offset));
223      /* End of ANDROID update. */
224      found = 1;
225    }
226
227#if UNW_TARGET_ARM
228  if (parm_exidx)
229    {
230      edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
231      edi->di_arm.start_ip = start_ip;
232      edi->di_arm.end_ip = end_ip;
233      edi->di_arm.u.rti.name_ptr = (unw_word_t) path;
234      edi->di_arm.u.rti.table_data = load_base + parm_exidx->p_vaddr;
235      edi->di_arm.u.rti.table_len = parm_exidx->p_memsz;
236      found = 1;
237    }
238#endif
239
240#ifdef CONFIG_DEBUG_FRAME
241  /* Try .debug_frame. */
242  found = dwarf_find_debug_frame (found, &edi->di_debug, ip, load_base, path,
243				  start_ip, end_ip);
244#endif
245
246  return found;
247}
248