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