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.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
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.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
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(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
238 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)239 dwarf_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