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 #include <stdint.h>
30
31 #include <sys/mman.h>
32
33 #include "libunwind_i.h"
34 #include "dwarf-eh.h"
35 #include "dwarf_i.h"
36
37 #define to_unw_word(p) ((unw_word_t) (uintptr_t) (p))
38 #ifndef PAGE_SIZE
39 #define PAGE_SIZE 4096
40 #endif
41
42 #ifndef NT_GNU_BUILD_ID
43 #define NT_GNU_BUILD_ID 3
44 #endif
45
46 #ifndef ElfW
47 #define ElfW(type) Elf_##type
48 #endif
49
50 #define ALIGN(val, align) (((val) + (align) - 1) & ~((align) - 1))
51
52 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)53 dwarf_find_unwind_table (struct elf_dyn_info *edi, struct elf_image *ei,
54 unw_addr_space_t as, char *path,
55 unw_word_t segbase, unw_word_t mapoff, unw_word_t ip)
56 {
57 Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL;
58 unw_word_t addr, eh_frame_start, fde_count, load_base;
59 unw_word_t max_load_addr = (unw_word_t)(ei->image) + ei->size;
60 unw_word_t min_load_addr = (unw_word_t)(ei->image);
61 unw_word_t start_ip = to_unw_word (-1);
62 unw_word_t end_ip = 0;
63 struct dwarf_eh_frame_hdr *hdr;
64 unw_proc_info_t pi;
65 unw_accessors_t *a;
66 Elf_W(Ehdr) *ehdr;
67 int first_load_section = 0;
68 int first_load_offset = 0;
69 #if UNW_TARGET_ARM
70 const Elf_W(Phdr) *param_exidx = NULL;
71 #endif
72 int i, ret, found = 0;
73
74 /* XXX: Much of this code is Linux/LSB-specific. */
75 if (!elf_w(valid_object) (ei))
76 return -UNW_ENOINFO;
77
78 if (path == NULL || strlen(path) > PAGE_SIZE || max_load_addr < min_load_addr) {
79 return -UNW_ENOINFO;
80 }
81
82 if (ei->has_dyn_info == 1 &&
83 ei->elf_dyn_info.start_ip <= ip &&
84 ei->elf_dyn_info.end_ip >= ip) {
85 *edi = ei->elf_dyn_info;
86 return 1; // found
87 }
88
89 ehdr = ei->image;
90 phdr = (Elf_W(Phdr) *) ((char *) ei->image + ehdr->e_phoff);
91 if (((unw_word_t)phdr) + sizeof(Elf_W(Phdr)) > max_load_addr ||
92 ((unw_word_t)phdr) + sizeof(Elf_W(Phdr)) < min_load_addr) {
93 return -UNW_ENOINFO;
94 }
95
96 #ifdef PARSE_BUILD_ID
97 struct build_id_note* note;
98 int note_len;
99 size_t note_offset;
100 #endif
101 unsigned long pagesize_alignment_mask = ~(((unsigned long)getpagesize()) - 1UL);
102 for (i = 0; i < ehdr->e_phnum; ++i)
103 {
104 if (((unw_word_t)(&(phdr[i])) + sizeof(Elf_W(Phdr))) > max_load_addr ||
105 ((unw_word_t)(&(phdr[i])) + sizeof(Elf_W(Phdr))) < min_load_addr) {
106 break;
107 }
108
109 switch (phdr[i].p_type)
110 {
111 case PT_LOAD:
112 if ((phdr[i].p_flags & PF_X) == 0) {
113 continue;
114 }
115
116 if (first_load_section == 0) {
117 ei->load_bias = phdr[i].p_vaddr - phdr[i].p_offset;
118 first_load_section = 1;
119 }
120
121 if (phdr[i].p_vaddr < start_ip)
122 start_ip = phdr[i].p_vaddr;
123
124 if (phdr[i].p_vaddr + phdr[i].p_memsz > end_ip)
125 end_ip = phdr[i].p_vaddr + phdr[i].p_memsz;
126
127 if ((phdr[i].p_offset & (-PAGE_SIZE)) == mapoff)
128 ptxt = phdr + i;
129
130 if (first_load_offset == 0) {
131 if ((phdr[i].p_offset & pagesize_alignment_mask) == mapoff) {
132 ei->load_offset = phdr[i].p_vaddr - (phdr[i].p_offset & (~pagesize_alignment_mask));
133 first_load_offset = 1;
134 } else {
135 ei->load_offset = 0;
136 }
137 }
138 break;
139 case PT_GNU_EH_FRAME:
140 #if defined __sun
141 case PT_SUNW_UNWIND:
142 #endif
143 peh_hdr = phdr + i;
144 break;
145
146 case PT_DYNAMIC:
147 pdyn = phdr + i;
148 break;
149
150 #if UNW_TARGET_ARM
151 case PT_ARM_EXIDX:
152 param_exidx = phdr + i;
153 break;
154 #endif
155 #ifdef PARSE_BUILD_ID
156 case PT_NOTE: {
157 note = (void *)(ei->image + phdr[i].p_offset);
158 note_len = phdr[i].p_filesz;
159 if (((unw_word_t)note + note_len) > max_load_addr ||
160 ((unw_word_t)note + note_len) < min_load_addr) {
161 Dprintf("Target Note Section is not valid:%s\n", path);
162 break;
163 }
164
165 while (note_len >= (int)(sizeof(struct build_id_note))) {
166 if (note->nhdr.n_type == NT_GNU_BUILD_ID &&
167 note->nhdr.n_descsz != 0 &&
168 note->nhdr.n_namesz == 4 &&
169 memcmp(note->name, "GNU", 4) == 0) {
170 ei->build_id_note = note;
171 break;
172 }
173
174 note_offset = sizeof(ElfW(Nhdr)) +
175 ALIGN(note->nhdr.n_namesz, 4) +
176 ALIGN(note->nhdr.n_descsz, 4);
177 // 05 00 00 00 04 00 00 00 4f 48 4f 53 00 01 00 00 00 00 00 00
178 if (note->nhdr.n_type == 0x534f484f && note_len > 20) {
179 // .note.ohos.ident is not a valid PT_NOTE section, use offset in section header later
180 note_offset = 20;
181 }
182 note = (struct build_id_note*)((char *)note + note_offset);
183 note_len -= note_offset;
184 }
185 }
186 break;
187 #endif
188 default:
189 break;
190 }
191 }
192
193 if (!ptxt)
194 return 0;
195
196 load_base = segbase - (ptxt->p_vaddr & (-PAGE_SIZE));
197 start_ip += load_base;
198 end_ip += load_base;
199
200 if (peh_hdr)
201 {
202 if (pdyn)
203 {
204 /* For dynamicly linked executables and shared libraries,
205 DT_PLTGOT is the value that data-relative addresses are
206 relative to for that object. We call this the "gp". */
207 Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset + (char *) ei->image);
208 for (; dyn->d_tag != DT_NULL; ++dyn) {
209 if (((unw_word_t)dyn + sizeof(Elf_W(Dyn))) > max_load_addr ||
210 ((unw_word_t)dyn + sizeof(Elf_W(Dyn))) < min_load_addr) {
211 break;
212 }
213
214 if (dyn->d_tag == DT_PLTGOT) {
215 /* Assume that _DYNAMIC is writable and GLIBC has
216 relocated it (true for x86 at least). */
217 edi->di_cache.gp = dyn->d_un.d_ptr;
218 break;
219 }
220 }
221 }
222 else
223 /* Otherwise this is a static executable with no _DYNAMIC. Assume
224 that data-relative addresses are relative to 0, i.e.,
225 absolute. */
226 edi->di_cache.gp = 0;
227
228 hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset
229 + (char *) ei->image);
230 if (((unw_word_t)hdr + sizeof(struct dwarf_eh_frame_hdr)) > max_load_addr ||
231 ((unw_word_t)hdr + sizeof(struct dwarf_eh_frame_hdr)) < min_load_addr) {
232 return -UNW_ENOINFO;
233 }
234
235 if (hdr->version != DW_EH_VERSION)
236 {
237 if (path != NULL) {
238 Debug (1, "table `%s' has unexpected version %d\n", path, hdr->version);
239 }
240 return -UNW_ENOINFO;
241 }
242
243 a = unw_get_accessors_int (unw_local_addr_space);
244 addr = to_unw_word (&hdr->eh_frame);
245
246 /* Fill in a dummy proc_info structure. We just need to fill in
247 enough to ensure that dwarf_read_encoded_pointer() can do it's
248 job. Since we don't have a procedure-context at this point, all
249 we have to do is fill in the global-pointer. */
250 memset (&pi, 0, sizeof (pi));
251 pi.gp = edi->di_cache.gp;
252
253 /* (Optionally) read eh_frame_ptr: */
254 if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
255 &addr, hdr->eh_frame_ptr_enc, &pi,
256 &eh_frame_start, NULL)) < 0)
257 return -UNW_ENOINFO;
258
259 /* (Optionally) read fde_count: */
260 if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
261 &addr, hdr->fde_count_enc, &pi,
262 &fde_count, NULL)) < 0)
263 return -UNW_ENOINFO;
264
265 if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
266 {
267 #if 0
268 abort ();
269
270 unw_word_t eh_frame_end;
271
272 /* If there is no search table or it has an unsupported
273 encoding, fall back on linear search. */
274 if (hdr->table_enc == DW_EH_PE_omit)
275 Debug (4, "EH lacks search table; doing linear search\n");
276 else
277 Debug (4, "EH table has encoding 0x%x; doing linear search\n",
278 hdr->table_enc);
279
280 eh_frame_end = max_load_addr; /* XXX can we do better? */
281
282 if (hdr->fde_count_enc == DW_EH_PE_omit)
283 fde_count = ~0UL;
284 if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit)
285 abort ();
286
287 return linear_search (unw_local_addr_space, ip,
288 eh_frame_start, eh_frame_end, fde_count,
289 pi, need_unwind_info, NULL);
290 #endif
291 }
292
293 edi->di_cache.start_ip = start_ip;
294 edi->di_cache.end_ip = end_ip;
295 edi->di_cache.load_offset = 0;
296 edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
297 edi->di_cache.u.rti.name_ptr = 0;
298 /* two 32-bit values (ip_offset/fde_offset) per table-entry: */
299 edi->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
300 edi->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
301 + (addr - (unw_word_t) ei->image
302 - peh_hdr->p_offset));
303
304 /* For the binary-search table in the eh_frame_hdr, data-relative
305 means relative to the start of that section... */
306 /* Add For Cache MAP And ELF */
307 edi->di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
308 + ((unw_word_t) hdr - (unw_word_t) ei->image
309 - peh_hdr->p_offset));
310 /* Add For Cache MAP And ELF */
311 found = 1;
312 }
313
314 #if UNW_TARGET_ARM
315 if (param_exidx)
316 {
317 edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
318 edi->di_arm.start_ip = start_ip;
319 edi->di_arm.end_ip = end_ip;
320 edi->di_arm.u.rti.name_ptr = to_unw_word (path);
321 edi->di_arm.u.rti.table_data = load_base + param_exidx->p_vaddr;
322 edi->di_arm.u.rti.table_len = param_exidx->p_memsz;
323 found = 1;
324 }
325 #endif
326
327 #ifdef CONFIG_DEBUG_FRAME
328 /* Try .debug_frame. */
329 found = dwarf_find_debug_frame (found, &edi->di_debug, ip, load_base, path,
330 start_ip, end_ip);
331 #endif
332 if (found == 1) {
333 edi->start_ip = start_ip;
334 edi->end_ip = end_ip;
335 ei->elf_dyn_info = *edi;
336 ei->has_dyn_info = 1;
337 }
338 return found;
339 }
340