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