1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * interface used by unwind support to query frame descriptor info
29 */
30
31 #ifndef _LIBCRUN_
32 #include "lint.h"
33 #endif
34 #include <sys/types.h>
35 #include <limits.h>
36 #include "stack_unwind.h"
37 #include "unwind_context.h"
38 #include <dlfcn.h>
39
40 /*
41 * CIE:
42 * UNUM32 length
43 * UNUM32 ID
44 * UNUM8 version
45 * ZTSTRING augmentation
46 * ULEB128 Code Align Factor
47 * SLEB128 Data Align Factor
48 * UNUM8 RA
49 * ULEB128 length
50 * UNUM8 personality enc
51 * ADDR personality
52 * UNUM8 code_enc
53 * UNUM8 lsda_enc
54 *
55 * FDE:
56 * UNUM32 length
57 * UNUM32 ID
58 * ADDR initial loc
59 * SIZE size
60 * ULEB128 length
61 * ADDR lsda
62 */
63
64
65 struct eh_frame_fields *
_Unw_Decode_FDE(struct eh_frame_fields * f,struct _Unwind_Context * ctx)66 _Unw_Decode_FDE(struct eh_frame_fields *f, struct _Unwind_Context *ctx)
67 {
68 void *fde_data; /* location in this process of fde */
69 void *fde_end;
70 void *data;
71 ptrdiff_t reloc;
72 uintptr_t base;
73 void *cie_data; /* location in this process of cie */
74 void *cie_end;
75 void *cdata;
76 ptrdiff_t creloc;
77 int lsda_enc = 0;
78 int per_enc = 0;
79 int code_enc = 0;
80 char augment[8];
81 char *p;
82 uint64_t scratch;
83
84 uint64_t func = 0;
85 uint64_t range = 0;
86 _Unwind_Personality_Fn pfn = 0;
87 void* lsda = 0;
88
89 /* here is where data mapping would happen ??REMOTE?? */
90 fde_data = ctx->fde;
91 data = fde_data;
92 fde_end = (void *)(((intptr_t)fde_data) + 4 +
93 _Unw_get_val(&data, 0, UNUM32, 1, 1, 0));
94 reloc = 0;
95 base = ((intptr_t)data) + reloc;
96 cie_data = (void *)(base - _Unw_get_val(&data, 0, UNUM32, 1, 1, 0));
97 cdata = cie_data;
98 cie_end = (void *)(((intptr_t)cie_data) + 4 +
99 _Unw_get_val(&cdata, 0, UNUM32, 1, 1, 0));
100 creloc = 0;
101 /* data mapping has happened */
102
103 f->cie_ops_end = cie_end;
104 f->cie_reloc = creloc;
105 f->fde_ops_end = fde_end;
106 f->fde_reloc = reloc;
107
108 (void) _Unw_get_val(&cdata, creloc, UNUM32, 1, 1, 0);
109 (void) _Unw_get_val(&cdata, creloc, UNUM8, 1, 1, 0);
110 /* LINTED alignment */
111 (*((uint64_t *)(&(augment[0])))) =
112 _Unw_get_val(&cdata, creloc, ZTSTRING, 1, 1, 0);
113 f->code_align = _Unw_get_val(&cdata, creloc, ULEB128, 1, 1, 0);
114 f->data_align = _Unw_get_val(&cdata, creloc, SLEB128, 1, 1, 0);
115 (void) _Unw_get_val(&cdata, creloc, UNUM8, 1, 1, 0);
116 if (augment[0] == 'z' &&
117 (scratch = _Unw_get_val(&cdata, creloc, ULEB128, 1, 1, 0)) != 0) {
118 for (p = &(augment[1]); *p != 0; p++) {
119 switch (*p) {
120 case 'P':
121 per_enc = _Unw_get_val(&cdata, creloc,
122 UNUM8, 1, 1, 0);
123 if (per_enc == 0)
124 per_enc = 0x4;
125 pfn = (_Unwind_Personality_Fn)
126 _Unw_get_val(&cdata, creloc,
127 ADDR, 1, 1, per_enc);
128 break;
129 case 'R':
130 code_enc = _Unw_get_val(&cdata, creloc,
131 UNUM8, 1, 1, 0);
132 break;
133 case 'L':
134 lsda_enc = _Unw_get_val(&cdata, creloc,
135 UNUM8, 1, 1, 0);
136 break;
137 }
138 }
139 }
140 if (code_enc == 0)
141 code_enc = 0x4;
142
143 func = _Unw_get_val(&data, reloc, ADDR, 1, 1, code_enc);
144 range = _Unw_get_val(&data, reloc, SIZE, 1, 1, code_enc);
145 if ((ctx->pc < func) || (ctx->pc > (func+range)))
146 return (0);
147 ctx->func = func;
148 ctx->range = range;
149 if (augment[0] == 'z') {
150 scratch = _Unw_get_val(&data, reloc, ULEB128, 1, 1, 0);
151 if (scratch == 4 && lsda_enc) {
152 /*
153 * without the two work-arounds test would be
154 * (scratch > 0 & lsda_enc)
155 */
156 lsda = (void *)_Unw_get_val(&data, reloc,
157 ADDR, 1, 1, lsda_enc);
158 } else if (scratch == 4) {
159 /*
160 * 11/24/04 compiler is sometimes not outputing
161 * lsda_enc
162 */
163 lsda = (void*)_Unw_get_val(&data, reloc,
164 ADDR, 1, 1, 0x1b);
165 } else if (scratch == 8) {
166 /*
167 * 11/12/04 - compiler is putting out relative
168 * encoding byte and absolute data - inconsistancy
169 * is caught here.
170 */
171 lsda = (void *)_Unw_get_val(&data, reloc,
172 ADDR, 1, 1, 0x4);
173 }
174 }
175 if (pfn)
176 ctx->pfn = pfn;
177 if (lsda)
178 ctx->lsda = lsda;
179 f->fde_ops = data;
180 f->cie_ops = cdata;
181 f->code_enc = code_enc;
182 return (f);
183 }
184
185 static int
table_ent_log_size(int enc)186 table_ent_log_size(int enc)
187 {
188 int val = enc & 0xf;
189 int res;
190
191 switch (val) {
192 case 0x3:
193 res = 3;
194 break;
195 case 0x04:
196 res = 4;
197 break;
198 case 0x0b:
199 res = 3;
200 break;
201 case 0x0c:
202 res = 4;
203 break;
204 default:
205 break;
206 }
207 return (res);
208 }
209
210 static void
get_table_ent_val(unsigned char * data,unsigned char * data_end,int enc,ptrdiff_t reloc,uintptr_t base,uint64_t * codep,uint64_t * next_codep,void ** fdep)211 get_table_ent_val(unsigned char *data, unsigned char *data_end,
212 int enc, ptrdiff_t reloc, uintptr_t base,
213 uint64_t *codep, uint64_t *next_codep, void **fdep)
214 {
215 int val = enc & 0xf;
216 int rel = (enc >> 4) & 0xf;
217 unsigned char *second = data;
218 unsigned char *third = data;
219 uint64_t code;
220 void *fde;
221 uint64_t next_code;
222
223 switch (val) {
224 case 0x3:
225 /* LINTED alignment */
226 code = (uint64_t)(*((uint32_t *)data));
227 second += 4;
228 /* LINTED alignment */
229 fde = (void *)(uint64_t)(*((uint32_t *)second));
230 third += 8;
231 next_code = (third >= data_end)? ULONG_MAX :
232 /* LINTED alignment */
233 (uint64_t)(*((uint32_t *)third));
234 break;
235 case 0x04:
236 /* LINTED alignment */
237 code = (uint64_t)(*((uint64_t *)data));
238 second += 8;
239 /* LINTED alignment */
240 fde = (void *)(uint64_t)(*((uint64_t *)second));
241 third += 16;
242 next_code = (third >= data_end)? ULONG_MAX :
243 /* LINTED alignment */
244 (uint64_t)(*((uint64_t *)third));
245 break;
246 case 0x0b:
247 /* LINTED alignment */
248 code = (uint64_t)(int64_t)(*((int32_t *)data));
249 second += 4;
250 /* LINTED alignment */
251 fde = (void *)(uint64_t)(int64_t)(*((int32_t *)second));
252 third += 8;
253 next_code = (third >= data_end)? ULONG_MAX :
254 /* LINTED alignment */
255 (uint64_t)(int64_t)(*((int32_t *)third));
256 break;
257 case 0x0c:
258 /* LINTED alignment */
259 code = (uint64_t)(*((int64_t *)data));
260 second += 8;
261 /* LINTED alignment */
262 fde = (void *)(uint64_t)(*((int64_t *)second));
263 third += 16;
264 next_code = (third >= data_end)? ULONG_MAX :
265 /* LINTED alignment */
266 (uint64_t)(*((int64_t *)third));
267 break;
268 }
269
270 switch (rel) {
271 case 0:
272 break;
273 case 1:
274 code += (uint64_t)data + reloc;
275 fde = (void *)(((uint64_t)fde) + (uint64_t)second + reloc);
276 if (next_code != ULONG_MAX)
277 next_code += (uint64_t)third + reloc;
278 break;
279 case 3:
280 code += base;
281 fde = (void *)(((uint64_t)fde) + base);
282 if (next_code != ULONG_MAX)
283 next_code += base;
284 break;
285 default:
286 /* remainder not implemented */
287 break;
288 }
289 *codep = code;
290 *fdep = fde;
291 *next_codep = next_code;
292 }
293
294
295 static void *
296 locate_fde_for_pc(uint64_t pc, int enc,
297 unsigned char *table, unsigned char *table_end,
298 ptrdiff_t reloc, uintptr_t base);
299
300 /*
301 * Search the eh_frame info with a given pc. Return a pointer to a
302 * FDE. The search is performed in two stages.
303 * First rtld.so identifies the load module containing the target location.
304 * This returns the appropiate eh_frame_hdr, and a binary search is
305 * then performed on the eh_frame_hdr to locate the entry with
306 * a matching pc value.
307 */
308 void *
_Unw_EhfhLookup(struct _Unwind_Context * ctx)309 _Unw_EhfhLookup(struct _Unwind_Context *ctx)
310 {
311 Dl_amd64_unwindinfo dlef;
312 void* data;
313 void* data_end;
314 uint64_t pc = ctx->pc;
315 int fp_enc, fc_enc, ft_enc;
316 unsigned char *pi, *pj;
317 ptrdiff_t reloc;
318 uintptr_t base;
319
320 dlef.dlui_version = 1;
321
322 /* Locate the appropiate exception_range_entry table first */
323 if (0 == dlamd64getunwind((void*)pc, &dlef)) {
324 return (0);
325 }
326
327 /*
328 * you now know size and position of block of data needed for
329 * binary search ??REMOTE??
330 */
331 data = dlef.dlui_unwindstart;
332 if (0 == data)
333 return (0);
334 base = (uintptr_t)data;
335 data_end = dlef.dlui_unwindend;
336 reloc = 0;
337 /* ??REMOTE?? */
338
339 (void) _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0);
340 fp_enc = _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0);
341 fc_enc = _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0);
342 ft_enc = _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0);
343 (void) _Unw_get_val(&data, reloc, ADDR, 1, 1, fp_enc);
344 (void) _Unw_get_val(&data, reloc, SIZE, 1, 1, fc_enc);
345 pi = data;
346 pj = data_end;
347 ctx->fde = locate_fde_for_pc(pc, ft_enc, pi, pj, reloc, base);
348 return ((void *)(ctx->fde));
349 }
350
351 static void *
locate_fde_for_pc(uint64_t pc,int enc,unsigned char * table_bg,unsigned char * table_end,ptrdiff_t reloc,uintptr_t base)352 locate_fde_for_pc(uint64_t pc, int enc,
353 unsigned char *table_bg, unsigned char *table_end,
354 ptrdiff_t reloc, uintptr_t base)
355 {
356 unsigned char *pi = table_bg;
357 unsigned char *pj = table_end;
358 uint64_t range_start, range_end;
359 void* fde;
360 int log_size = table_ent_log_size(enc);
361
362 /*
363 * Invariant -- if there is a containing range,
364 * it must lie in the interval [pi,pj). That is,
365 * pi <= p < pj, if p exists.
366 */
367 while (pi < pj) {
368 unsigned char *pr =
369 pi + (((pj - pi) >> (log_size + 1)) << log_size);
370 /* Don't use (pi+pj)>>1 */
371 get_table_ent_val(pr, table_end, enc, reloc, base,
372 &range_start, &range_end, &fde);
373
374 /* Return fde if tpc is in this range. */
375
376 if (range_start <= pc && pc < range_end) {
377 return ((void*) fde);
378 }
379
380 if (range_start < pc)
381 pi = pr + (1 << log_size);
382 else
383 pj = pr;
384 }
385 return (0);
386 }
387