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 * 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 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 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 * 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 * 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