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