Mercurial > hg > CbC > CbC_gcc
comparison gcc/unwind-dw2-fde-glibc.c @ 0:a06113de4d67
first commit
author | kent <kent@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 17 Jul 2009 14:47:48 +0900 |
parents | |
children | 77e2b8dfacca |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:a06113de4d67 |
---|---|
1 /* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2009 Free Software Foundation, Inc. | |
2 Contributed by Jakub Jelinek <jakub@redhat.com>. | |
3 | |
4 This file is part of GCC. | |
5 | |
6 GCC is free software; you can redistribute it and/or modify | |
7 it under the terms of the GNU General Public License as published by | |
8 the Free Software Foundation; either version 3, or (at your option) | |
9 any later version. | |
10 | |
11 GCC is distributed in the hope that it will be useful, | |
12 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 GNU General Public License for more details. | |
15 | |
16 Under Section 7 of GPL version 3, you are granted additional | |
17 permissions described in the GCC Runtime Library Exception, version | |
18 3.1, as published by the Free Software Foundation. | |
19 | |
20 You should have received a copy of the GNU General Public License and | |
21 a copy of the GCC Runtime Library Exception along with this program; | |
22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
23 <http://www.gnu.org/licenses/>. */ | |
24 | |
25 /* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF | |
26 segment and dl_iterate_phdr to avoid register/deregister calls at | |
27 DSO load/unload. */ | |
28 | |
29 #ifndef _GNU_SOURCE | |
30 #define _GNU_SOURCE 1 | |
31 #endif | |
32 | |
33 #include "tconfig.h" | |
34 #include "tsystem.h" | |
35 #ifndef inhibit_libc | |
36 #include <link.h> | |
37 #endif | |
38 #include "coretypes.h" | |
39 #include "tm.h" | |
40 #include "dwarf2.h" | |
41 #include "unwind.h" | |
42 #define NO_BASE_OF_ENCODED_VALUE | |
43 #include "unwind-pe.h" | |
44 #include "unwind-dw2-fde.h" | |
45 #include "unwind-compat.h" | |
46 #include "gthr.h" | |
47 | |
48 #if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \ | |
49 && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \ | |
50 || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG))) | |
51 | |
52 #ifndef __RELOC_POINTER | |
53 # define __RELOC_POINTER(ptr, base) ((ptr) + (base)) | |
54 #endif | |
55 | |
56 static const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases); | |
57 | |
58 #define _Unwind_Find_FDE _Unwind_Find_registered_FDE | |
59 #include "unwind-dw2-fde.c" | |
60 #undef _Unwind_Find_FDE | |
61 | |
62 #ifndef PT_GNU_EH_FRAME | |
63 #define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550) | |
64 #endif | |
65 | |
66 struct unw_eh_callback_data | |
67 { | |
68 _Unwind_Ptr pc; | |
69 void *tbase; | |
70 void *dbase; | |
71 void *func; | |
72 const fde *ret; | |
73 int check_cache; | |
74 }; | |
75 | |
76 struct unw_eh_frame_hdr | |
77 { | |
78 unsigned char version; | |
79 unsigned char eh_frame_ptr_enc; | |
80 unsigned char fde_count_enc; | |
81 unsigned char table_enc; | |
82 }; | |
83 | |
84 #define FRAME_HDR_CACHE_SIZE 8 | |
85 | |
86 static struct frame_hdr_cache_element | |
87 { | |
88 _Unwind_Ptr pc_low; | |
89 _Unwind_Ptr pc_high; | |
90 _Unwind_Ptr load_base; | |
91 const ElfW(Phdr) *p_eh_frame_hdr; | |
92 const ElfW(Phdr) *p_dynamic; | |
93 struct frame_hdr_cache_element *link; | |
94 } frame_hdr_cache[FRAME_HDR_CACHE_SIZE]; | |
95 | |
96 static struct frame_hdr_cache_element *frame_hdr_cache_head; | |
97 | |
98 /* Like base_of_encoded_value, but take the base from a struct | |
99 unw_eh_callback_data instead of an _Unwind_Context. */ | |
100 | |
101 static _Unwind_Ptr | |
102 base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data) | |
103 { | |
104 if (encoding == DW_EH_PE_omit) | |
105 return 0; | |
106 | |
107 switch (encoding & 0x70) | |
108 { | |
109 case DW_EH_PE_absptr: | |
110 case DW_EH_PE_pcrel: | |
111 case DW_EH_PE_aligned: | |
112 return 0; | |
113 | |
114 case DW_EH_PE_textrel: | |
115 return (_Unwind_Ptr) data->tbase; | |
116 case DW_EH_PE_datarel: | |
117 return (_Unwind_Ptr) data->dbase; | |
118 default: | |
119 gcc_unreachable (); | |
120 } | |
121 } | |
122 | |
123 static int | |
124 _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr) | |
125 { | |
126 struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr; | |
127 const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic; | |
128 long n, match; | |
129 #ifdef __FRV_FDPIC__ | |
130 struct elf32_fdpic_loadaddr load_base; | |
131 #else | |
132 _Unwind_Ptr load_base; | |
133 #endif | |
134 const unsigned char *p; | |
135 const struct unw_eh_frame_hdr *hdr; | |
136 _Unwind_Ptr eh_frame; | |
137 struct object ob; | |
138 | |
139 struct ext_dl_phdr_info | |
140 { | |
141 ElfW(Addr) dlpi_addr; | |
142 const char *dlpi_name; | |
143 const ElfW(Phdr) *dlpi_phdr; | |
144 ElfW(Half) dlpi_phnum; | |
145 unsigned long long int dlpi_adds; | |
146 unsigned long long int dlpi_subs; | |
147 }; | |
148 | |
149 match = 0; | |
150 phdr = info->dlpi_phdr; | |
151 load_base = info->dlpi_addr; | |
152 p_eh_frame_hdr = NULL; | |
153 p_dynamic = NULL; | |
154 | |
155 struct frame_hdr_cache_element *prev_cache_entry = NULL, | |
156 *last_cache_entry = NULL; | |
157 | |
158 if (data->check_cache && size >= sizeof (struct ext_dl_phdr_info)) | |
159 { | |
160 static unsigned long long adds = -1ULL, subs; | |
161 struct ext_dl_phdr_info *einfo = (struct ext_dl_phdr_info *) info; | |
162 | |
163 /* We use a least recently used cache replacement policy. Also, | |
164 the most recently used cache entries are placed at the head | |
165 of the search chain. */ | |
166 | |
167 if (einfo->dlpi_adds == adds && einfo->dlpi_subs == subs) | |
168 { | |
169 /* Find data->pc in shared library cache. | |
170 Set load_base, p_eh_frame_hdr and p_dynamic | |
171 plus match from the cache and goto | |
172 "Read .eh_frame_hdr header." below. */ | |
173 | |
174 struct frame_hdr_cache_element *cache_entry; | |
175 | |
176 for (cache_entry = frame_hdr_cache_head; | |
177 cache_entry; | |
178 cache_entry = cache_entry->link) | |
179 { | |
180 if (data->pc >= cache_entry->pc_low | |
181 && data->pc < cache_entry->pc_high) | |
182 { | |
183 load_base = cache_entry->load_base; | |
184 p_eh_frame_hdr = cache_entry->p_eh_frame_hdr; | |
185 p_dynamic = cache_entry->p_dynamic; | |
186 | |
187 /* And move the entry we're using to the head. */ | |
188 if (cache_entry != frame_hdr_cache_head) | |
189 { | |
190 prev_cache_entry->link = cache_entry->link; | |
191 cache_entry->link = frame_hdr_cache_head; | |
192 frame_hdr_cache_head = cache_entry; | |
193 } | |
194 goto found; | |
195 } | |
196 | |
197 last_cache_entry = cache_entry; | |
198 /* Exit early if we found an unused entry. */ | |
199 if ((cache_entry->pc_low | cache_entry->pc_high) == 0) | |
200 break; | |
201 if (cache_entry->link != NULL) | |
202 prev_cache_entry = cache_entry; | |
203 } | |
204 } | |
205 else | |
206 { | |
207 adds = einfo->dlpi_adds; | |
208 subs = einfo->dlpi_subs; | |
209 /* Initialize the cache. Create a chain of cache entries, | |
210 with the final one terminated by a NULL link. */ | |
211 int i; | |
212 for (i = 0; i < FRAME_HDR_CACHE_SIZE; i++) | |
213 { | |
214 frame_hdr_cache[i].pc_low = 0; | |
215 frame_hdr_cache[i].pc_high = 0; | |
216 frame_hdr_cache[i].link = &frame_hdr_cache[i+1]; | |
217 } | |
218 frame_hdr_cache[i-1].link = NULL; | |
219 frame_hdr_cache_head = &frame_hdr_cache[0]; | |
220 data->check_cache = 0; | |
221 } | |
222 } | |
223 | |
224 /* Make sure struct dl_phdr_info is at least as big as we need. */ | |
225 if (size < offsetof (struct dl_phdr_info, dlpi_phnum) | |
226 + sizeof (info->dlpi_phnum)) | |
227 return -1; | |
228 | |
229 _Unwind_Ptr pc_low = 0, pc_high = 0; | |
230 | |
231 /* See if PC falls into one of the loaded segments. Find the eh_frame | |
232 segment at the same time. */ | |
233 for (n = info->dlpi_phnum; --n >= 0; phdr++) | |
234 { | |
235 if (phdr->p_type == PT_LOAD) | |
236 { | |
237 _Unwind_Ptr vaddr = (_Unwind_Ptr) | |
238 __RELOC_POINTER (phdr->p_vaddr, load_base); | |
239 if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz) | |
240 { | |
241 match = 1; | |
242 pc_low = vaddr; | |
243 pc_high = vaddr + phdr->p_memsz; | |
244 } | |
245 } | |
246 else if (phdr->p_type == PT_GNU_EH_FRAME) | |
247 p_eh_frame_hdr = phdr; | |
248 else if (phdr->p_type == PT_DYNAMIC) | |
249 p_dynamic = phdr; | |
250 } | |
251 | |
252 if (!match) | |
253 return 0; | |
254 | |
255 if (size >= sizeof (struct ext_dl_phdr_info)) | |
256 { | |
257 /* Move the cache entry we're about to overwrite to the head of | |
258 the list. If either last_cache_entry or prev_cache_entry are | |
259 NULL, that cache entry is already at the head. */ | |
260 if (last_cache_entry != NULL && prev_cache_entry != NULL) | |
261 { | |
262 prev_cache_entry->link = last_cache_entry->link; | |
263 last_cache_entry->link = frame_hdr_cache_head; | |
264 frame_hdr_cache_head = last_cache_entry; | |
265 } | |
266 | |
267 frame_hdr_cache_head->load_base = load_base; | |
268 frame_hdr_cache_head->p_eh_frame_hdr = p_eh_frame_hdr; | |
269 frame_hdr_cache_head->p_dynamic = p_dynamic; | |
270 frame_hdr_cache_head->pc_low = pc_low; | |
271 frame_hdr_cache_head->pc_high = pc_high; | |
272 } | |
273 | |
274 found: | |
275 | |
276 if (!p_eh_frame_hdr) | |
277 return 0; | |
278 | |
279 /* Read .eh_frame_hdr header. */ | |
280 hdr = (const struct unw_eh_frame_hdr *) | |
281 __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base); | |
282 if (hdr->version != 1) | |
283 return 1; | |
284 | |
285 #ifdef CRT_GET_RFIB_DATA | |
286 # ifdef __i386__ | |
287 data->dbase = NULL; | |
288 if (p_dynamic) | |
289 { | |
290 /* For dynamically linked executables and shared libraries, | |
291 DT_PLTGOT is the gp value for that object. */ | |
292 ElfW(Dyn) *dyn = (ElfW(Dyn) *) | |
293 __RELOC_POINTER (p_dynamic->p_vaddr, load_base); | |
294 for (; dyn->d_tag != DT_NULL ; dyn++) | |
295 if (dyn->d_tag == DT_PLTGOT) | |
296 { | |
297 /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it. */ | |
298 data->dbase = (void *) dyn->d_un.d_ptr; | |
299 break; | |
300 } | |
301 } | |
302 # elif defined __FRV_FDPIC__ && defined __linux__ | |
303 data->dbase = load_base.got_value; | |
304 # else | |
305 # error What is DW_EH_PE_datarel base on this platform? | |
306 # endif | |
307 #endif | |
308 | |
309 p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc, | |
310 base_from_cb_data (hdr->eh_frame_ptr_enc, | |
311 data), | |
312 (const unsigned char *) (hdr + 1), | |
313 &eh_frame); | |
314 | |
315 /* We require here specific table encoding to speed things up. | |
316 Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start | |
317 as base, not the processor specific DW_EH_PE_datarel. */ | |
318 if (hdr->fde_count_enc != DW_EH_PE_omit | |
319 && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4)) | |
320 { | |
321 _Unwind_Ptr fde_count; | |
322 | |
323 p = read_encoded_value_with_base (hdr->fde_count_enc, | |
324 base_from_cb_data (hdr->fde_count_enc, | |
325 data), | |
326 p, &fde_count); | |
327 /* Shouldn't happen. */ | |
328 if (fde_count == 0) | |
329 return 1; | |
330 if ((((_Unwind_Ptr) p) & 3) == 0) | |
331 { | |
332 struct fde_table { | |
333 signed initial_loc __attribute__ ((mode (SI))); | |
334 signed fde __attribute__ ((mode (SI))); | |
335 }; | |
336 const struct fde_table *table = (const struct fde_table *) p; | |
337 size_t lo, hi, mid; | |
338 _Unwind_Ptr data_base = (_Unwind_Ptr) hdr; | |
339 fde *f; | |
340 unsigned int f_enc, f_enc_size; | |
341 _Unwind_Ptr range; | |
342 | |
343 mid = fde_count - 1; | |
344 if (data->pc < table[0].initial_loc + data_base) | |
345 return 1; | |
346 else if (data->pc < table[mid].initial_loc + data_base) | |
347 { | |
348 lo = 0; | |
349 hi = mid; | |
350 | |
351 while (lo < hi) | |
352 { | |
353 mid = (lo + hi) / 2; | |
354 if (data->pc < table[mid].initial_loc + data_base) | |
355 hi = mid; | |
356 else if (data->pc >= table[mid + 1].initial_loc + data_base) | |
357 lo = mid + 1; | |
358 else | |
359 break; | |
360 } | |
361 | |
362 gcc_assert (lo < hi); | |
363 } | |
364 | |
365 f = (fde *) (table[mid].fde + data_base); | |
366 f_enc = get_fde_encoding (f); | |
367 f_enc_size = size_of_encoded_value (f_enc); | |
368 read_encoded_value_with_base (f_enc & 0x0f, 0, | |
369 &f->pc_begin[f_enc_size], &range); | |
370 if (data->pc < table[mid].initial_loc + data_base + range) | |
371 data->ret = f; | |
372 data->func = (void *) (table[mid].initial_loc + data_base); | |
373 return 1; | |
374 } | |
375 } | |
376 | |
377 /* We have no sorted search table, so need to go the slow way. | |
378 As soon as GLIBC will provide API so to notify that a library has been | |
379 removed, we could cache this (and thus use search_object). */ | |
380 ob.pc_begin = NULL; | |
381 ob.tbase = data->tbase; | |
382 ob.dbase = data->dbase; | |
383 ob.u.single = (fde *) eh_frame; | |
384 ob.s.i = 0; | |
385 ob.s.b.mixed_encoding = 1; /* Need to assume worst case. */ | |
386 data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc); | |
387 if (data->ret != NULL) | |
388 { | |
389 _Unwind_Ptr func; | |
390 unsigned int encoding = get_fde_encoding (data->ret); | |
391 | |
392 read_encoded_value_with_base (encoding, | |
393 base_from_cb_data (encoding, data), | |
394 data->ret->pc_begin, &func); | |
395 data->func = (void *) func; | |
396 } | |
397 return 1; | |
398 } | |
399 | |
400 const fde * | |
401 _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases) | |
402 { | |
403 struct unw_eh_callback_data data; | |
404 const fde *ret; | |
405 | |
406 ret = _Unwind_Find_registered_FDE (pc, bases); | |
407 if (ret != NULL) | |
408 return ret; | |
409 | |
410 data.pc = (_Unwind_Ptr) pc; | |
411 data.tbase = NULL; | |
412 data.dbase = NULL; | |
413 data.func = NULL; | |
414 data.ret = NULL; | |
415 data.check_cache = 1; | |
416 | |
417 if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0) | |
418 return NULL; | |
419 | |
420 if (data.ret) | |
421 { | |
422 bases->tbase = data.tbase; | |
423 bases->dbase = data.dbase; | |
424 bases->func = data.func; | |
425 } | |
426 return data.ret; | |
427 } | |
428 | |
429 #else | |
430 /* Prevent multiple include of header files. */ | |
431 #define _Unwind_Find_FDE _Unwind_Find_FDE | |
432 #include "unwind-dw2-fde.c" | |
433 #endif | |
434 | |
435 #if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS) | |
436 alias (_Unwind_Find_FDE); | |
437 #endif |