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