]>
Commit | Line | Data |
---|---|---|
2b778ceb | 1 | /* Copyright (C) 2001-2021 Free Software Foundation, Inc. |
a0dcb689 UD |
2 | Contributed by Jakub Jelinek <jakub@redhat.com>. |
3 | ||
081ddfaa | 4 | This file is part of the GNU C Library. |
a0dcb689 | 5 | |
081ddfaa RM |
6 | The GNU C Library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Lesser General Public | |
8 | License as published by the Free Software Foundation; either | |
9 | version 2.1 of the License, or (at your option) any later version. | |
a0dcb689 | 10 | |
081ddfaa | 11 | The GNU C Library is distributed in the hope that it will be useful, |
a0dcb689 | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
081ddfaa RM |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | Lesser General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU Lesser General Public | |
59ba27a6 | 17 | License along with the GNU C Library; if not, see |
5a82c748 | 18 | <https://www.gnu.org/licenses/>. */ |
a0dcb689 UD |
19 | |
20 | /* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF | |
21 | segment and dl_iterate_phdr to avoid register/deregister calls at | |
22 | DSO load/unload. */ | |
23 | ||
24 | #ifdef _LIBC | |
25 | # include <shlib-compat.h> | |
26 | #endif | |
27 | ||
28 | #if !defined _LIBC || SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2_5) | |
29 | ||
30 | #include <link.h> | |
31 | #include <stddef.h> | |
32 | ||
33 | #define _Unwind_Find_FDE _Unwind_Find_registered_FDE | |
34 | ||
35 | #include <unwind-dw2-fde.c> | |
36 | ||
37 | #undef _Unwind_Find_FDE | |
38 | ||
7f0244de AJ |
39 | extern fde * _Unwind_Find_registered_FDE (void *pc, |
40 | struct dwarf_eh_bases *bases); | |
41 | extern fde * _Unwind_Find_FDE (void *, struct dwarf_eh_bases *); | |
a0dcb689 UD |
42 | |
43 | struct unw_eh_callback_data | |
44 | { | |
45 | _Unwind_Ptr pc; | |
46 | void *tbase; | |
47 | void *dbase; | |
48 | void *func; | |
49 | fde *ret; | |
50 | }; | |
51 | ||
52 | struct unw_eh_frame_hdr | |
53 | { | |
54 | unsigned char version; | |
55 | unsigned char eh_frame_ptr_enc; | |
56 | unsigned char fde_count_enc; | |
57 | unsigned char table_enc; | |
58 | }; | |
59 | ||
60 | /* Like base_of_encoded_value, but take the base from a struct object | |
61 | instead of an _Unwind_Context. */ | |
e78f615d | 62 | |
a0dcb689 UD |
63 | static _Unwind_Ptr |
64 | base_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data) | |
65 | { | |
66 | if (encoding == DW_EH_PE_omit) | |
67 | return 0; | |
68 | ||
69 | switch (encoding & 0x70) | |
70 | { | |
71 | case DW_EH_PE_absptr: | |
72 | case DW_EH_PE_pcrel: | |
73 | case DW_EH_PE_aligned: | |
74 | return 0; | |
e78f615d | 75 | |
a0dcb689 UD |
76 | case DW_EH_PE_textrel: |
77 | return (_Unwind_Ptr) data->tbase; | |
78 | case DW_EH_PE_datarel: | |
79 | return (_Unwind_Ptr) data->dbase; | |
80 | } | |
81 | abort (); | |
82 | } | |
83 | ||
84 | static int | |
85 | _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr) | |
86 | { | |
87 | struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr; | |
79c9b9fe JM |
88 | const ElfW(Phdr) *phdr, *p_eh_frame_hdr; |
89 | const ElfW(Phdr) *p_dynamic __attribute__ ((unused)); | |
a0dcb689 UD |
90 | long n, match; |
91 | _Unwind_Ptr load_base; | |
92 | const unsigned char *p; | |
93 | const struct unw_eh_frame_hdr *hdr; | |
94 | _Unwind_Ptr eh_frame; | |
95 | struct object ob; | |
96 | ||
97 | /* Make sure struct dl_phdr_info is at least as big as we need. */ | |
98 | if (size < offsetof (struct dl_phdr_info, dlpi_phnum) | |
99 | + sizeof (info->dlpi_phnum)) | |
100 | return -1; | |
101 | ||
102 | match = 0; | |
103 | phdr = info->dlpi_phdr; | |
104 | load_base = info->dlpi_addr; | |
105 | p_eh_frame_hdr = NULL; | |
106 | p_dynamic = NULL; | |
107 | ||
108 | /* See if PC falls into one of the loaded segments. Find the eh_frame | |
109 | segment at the same time. */ | |
110 | for (n = info->dlpi_phnum; --n >= 0; phdr++) | |
111 | { | |
112 | if (phdr->p_type == PT_LOAD) | |
113 | { | |
114 | _Unwind_Ptr vaddr = phdr->p_vaddr + load_base; | |
115 | if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz) | |
116 | match = 1; | |
117 | } | |
118 | else if (phdr->p_type == PT_GNU_EH_FRAME) | |
119 | p_eh_frame_hdr = phdr; | |
120 | else if (phdr->p_type == PT_DYNAMIC) | |
121 | p_dynamic = phdr; | |
122 | } | |
123 | if (!match || !p_eh_frame_hdr) | |
124 | return 0; | |
125 | ||
126 | /* Read .eh_frame_hdr header. */ | |
127 | hdr = (const struct unw_eh_frame_hdr *) | |
128 | (p_eh_frame_hdr->p_vaddr + load_base); | |
129 | if (hdr->version != 1) | |
130 | return 1; | |
131 | ||
132 | #ifdef CRT_GET_RFIB_DATA | |
133 | # ifdef __i386__ | |
134 | data->dbase = NULL; | |
135 | if (p_dynamic) | |
136 | { | |
137 | /* For dynamicly linked executables and shared libraries, | |
138 | DT_PLTGOT is the gp value for that object. */ | |
139 | ElfW(Dyn) *dyn = (ElfW(Dyn) *)(p_dynamic->p_vaddr + load_base); | |
140 | for (; dyn->d_tag != DT_NULL ; dyn++) | |
141 | if (dyn->d_tag == DT_PLTGOT) | |
142 | { | |
143 | /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it. */ | |
144 | data->dbase = (void *) dyn->d_un.d_ptr; | |
145 | break; | |
146 | } | |
147 | } | |
148 | # else | |
149 | # error What is DW_EH_PE_datarel base on this platform? | |
150 | # endif | |
151 | #endif | |
152 | #ifdef CRT_GET_RFIB_TEXT | |
153 | # error What is DW_EH_PE_textrel base on this platform? | |
154 | #endif | |
155 | ||
156 | p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc, | |
157 | base_from_cb_data (hdr->eh_frame_ptr_enc, | |
158 | data), | |
159 | (const unsigned char *) (hdr + 1), | |
160 | &eh_frame); | |
161 | ||
162 | /* We require here specific table encoding to speed things up. | |
163 | Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start | |
164 | as base, not the processor specific DW_EH_PE_datarel. */ | |
165 | if (hdr->fde_count_enc != DW_EH_PE_omit | |
166 | && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4)) | |
167 | { | |
168 | _Unwind_Ptr fde_count; | |
169 | ||
170 | p = read_encoded_value_with_base (hdr->fde_count_enc, | |
171 | base_from_cb_data (hdr->fde_count_enc, | |
172 | data), | |
173 | p, &fde_count); | |
174 | /* Shouldn't happen. */ | |
175 | if (fde_count == 0) | |
176 | return 1; | |
177 | if ((((_Unwind_Ptr) p) & 3) == 0) | |
178 | { | |
179 | struct fde_table { | |
180 | signed initial_loc __attribute__ ((mode (SI))); | |
181 | signed fde __attribute__ ((mode (SI))); | |
182 | }; | |
183 | const struct fde_table *table = (const struct fde_table *) p; | |
184 | size_t lo, hi, mid; | |
185 | _Unwind_Ptr data_base = (_Unwind_Ptr) hdr; | |
186 | fde *f; | |
187 | unsigned int f_enc, f_enc_size; | |
188 | _Unwind_Ptr range; | |
189 | ||
190 | mid = fde_count - 1; | |
191 | if (data->pc < table[0].initial_loc + data_base) | |
192 | return 1; | |
193 | else if (data->pc < table[mid].initial_loc + data_base) | |
194 | { | |
195 | lo = 0; | |
196 | hi = mid; | |
197 | ||
198 | while (lo < hi) | |
199 | { | |
200 | mid = (lo + hi) / 2; | |
201 | if (data->pc < table[mid].initial_loc + data_base) | |
202 | hi = mid; | |
203 | else if (data->pc >= table[mid + 1].initial_loc + data_base) | |
204 | lo = mid + 1; | |
205 | else | |
206 | break; | |
207 | } | |
208 | ||
209 | if (lo >= hi) | |
210 | __gxx_abort (); | |
211 | } | |
212 | ||
213 | f = (fde *) (table[mid].fde + data_base); | |
214 | f_enc = get_fde_encoding (f); | |
215 | f_enc_size = size_of_encoded_value (f_enc); | |
216 | read_encoded_value_with_base (f_enc & 0x0f, 0, | |
217 | &f->pc_begin[f_enc_size], &range); | |
218 | if (data->pc < table[mid].initial_loc + data_base + range) | |
219 | data->ret = f; | |
220 | data->func = (void *) (table[mid].initial_loc + data_base); | |
221 | return 1; | |
222 | } | |
223 | } | |
224 | ||
225 | /* We have no sorted search table, so need to go the slow way. | |
226 | As soon as GLIBC will provide API so to notify that a library has been | |
227 | removed, we could cache this (and thus use search_object). */ | |
228 | ob.pc_begin = NULL; | |
229 | ob.tbase = data->tbase; | |
230 | ob.dbase = data->dbase; | |
231 | ob.u.single = (fde *) eh_frame; | |
232 | ob.s.i = 0; | |
233 | ob.s.b.mixed_encoding = 1; /* Need to assume worst case. */ | |
234 | data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc); | |
235 | if (data->ret != NULL) | |
236 | { | |
237 | unsigned int encoding = get_fde_encoding (data->ret); | |
1475e201 | 238 | _Unwind_Ptr func; |
a0dcb689 UD |
239 | read_encoded_value_with_base (encoding, |
240 | base_from_cb_data (encoding, data), | |
1475e201 UD |
241 | data->ret->pc_begin, &func); |
242 | data->func = (void *) func; | |
a0dcb689 UD |
243 | } |
244 | return 1; | |
245 | } | |
246 | ||
e78f615d RM |
247 | # ifdef _LIBC |
248 | # define dl_iterate_phdr __dl_iterate_phdr | |
249 | # endif | |
250 | ||
a0dcb689 UD |
251 | fde * |
252 | _Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases) | |
253 | { | |
254 | struct unw_eh_callback_data data; | |
255 | fde *ret; | |
256 | ||
257 | ret = _Unwind_Find_registered_FDE (pc, bases); | |
258 | if (ret != NULL) | |
259 | return ret; | |
260 | ||
261 | data.pc = (_Unwind_Ptr) pc; | |
262 | data.tbase = NULL; | |
263 | data.dbase = NULL; | |
264 | data.func = NULL; | |
265 | data.ret = NULL; | |
266 | ||
267 | if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0) | |
268 | return NULL; | |
269 | ||
270 | if (data.ret) | |
271 | { | |
272 | bases->tbase = data.tbase; | |
273 | bases->dbase = data.dbase; | |
274 | bases->func = data.func; | |
275 | } | |
276 | return data.ret; | |
277 | } | |
278 | ||
279 | #endif |