10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*6812Sraf * Common Development and Distribution License (the "License"). 6*6812Sraf * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 21*6812Sraf 220Sstevel@tonic-gate /* 23*6812Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* 300Sstevel@tonic-gate * interface used by unwind support to query frame descriptor info 310Sstevel@tonic-gate */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate #ifndef _LIBCRUN_ 34*6812Sraf #include "lint.h" 350Sstevel@tonic-gate #endif 360Sstevel@tonic-gate #include <sys/types.h> 370Sstevel@tonic-gate #include <limits.h> 380Sstevel@tonic-gate #include "stack_unwind.h" 390Sstevel@tonic-gate #include "unwind_context.h" 400Sstevel@tonic-gate #include <dlfcn.h> 410Sstevel@tonic-gate 420Sstevel@tonic-gate /* 430Sstevel@tonic-gate * CIE: 440Sstevel@tonic-gate * UNUM32 length 450Sstevel@tonic-gate * UNUM32 ID 460Sstevel@tonic-gate * UNUM8 version 470Sstevel@tonic-gate * ZTSTRING augmentation 480Sstevel@tonic-gate * ULEB128 Code Align Factor 490Sstevel@tonic-gate * SLEB128 Data Align Factor 500Sstevel@tonic-gate * UNUM8 RA 510Sstevel@tonic-gate * ULEB128 length 520Sstevel@tonic-gate * UNUM8 personality enc 530Sstevel@tonic-gate * ADDR personality 540Sstevel@tonic-gate * UNUM8 code_enc 550Sstevel@tonic-gate * UNUM8 lsda_enc 560Sstevel@tonic-gate * 570Sstevel@tonic-gate * FDE: 580Sstevel@tonic-gate * UNUM32 length 590Sstevel@tonic-gate * UNUM32 ID 600Sstevel@tonic-gate * ADDR initial loc 610Sstevel@tonic-gate * SIZE size 620Sstevel@tonic-gate * ULEB128 length 630Sstevel@tonic-gate * ADDR lsda 640Sstevel@tonic-gate */ 650Sstevel@tonic-gate 660Sstevel@tonic-gate 670Sstevel@tonic-gate struct eh_frame_fields * 680Sstevel@tonic-gate _Unw_Decode_FDE(struct eh_frame_fields *f, struct _Unwind_Context *ctx) 690Sstevel@tonic-gate { 700Sstevel@tonic-gate void *fde_data; /* location in this process of fde */ 710Sstevel@tonic-gate void *fde_end; 720Sstevel@tonic-gate void *data; 730Sstevel@tonic-gate ptrdiff_t reloc; 740Sstevel@tonic-gate uintptr_t base; 750Sstevel@tonic-gate void *cie_data; /* location in this process of cie */ 760Sstevel@tonic-gate void *cie_end; 770Sstevel@tonic-gate void *cdata; 780Sstevel@tonic-gate ptrdiff_t creloc; 790Sstevel@tonic-gate int lsda_enc = 0; 800Sstevel@tonic-gate int per_enc = 0; 810Sstevel@tonic-gate int code_enc = 0; 820Sstevel@tonic-gate char augment[8]; 830Sstevel@tonic-gate char *p; 840Sstevel@tonic-gate uint64_t scratch; 850Sstevel@tonic-gate 860Sstevel@tonic-gate uint64_t func = 0; 870Sstevel@tonic-gate uint64_t range = 0; 880Sstevel@tonic-gate _Unwind_Personality_Fn pfn = 0; 890Sstevel@tonic-gate void* lsda = 0; 900Sstevel@tonic-gate 910Sstevel@tonic-gate /* here is where data mapping would happen ??REMOTE?? */ 920Sstevel@tonic-gate fde_data = ctx->fde; 930Sstevel@tonic-gate data = fde_data; 940Sstevel@tonic-gate fde_end = (void *)(((intptr_t)fde_data) + 4 + 95*6812Sraf _Unw_get_val(&data, 0, UNUM32, 1, 1, 0)); 960Sstevel@tonic-gate reloc = 0; 970Sstevel@tonic-gate base = ((intptr_t)data) + reloc; 980Sstevel@tonic-gate cie_data = (void *)(base - _Unw_get_val(&data, 0, UNUM32, 1, 1, 0)); 990Sstevel@tonic-gate cdata = cie_data; 1000Sstevel@tonic-gate cie_end = (void *)(((intptr_t)cie_data) + 4 + 101*6812Sraf _Unw_get_val(&cdata, 0, UNUM32, 1, 1, 0)); 1020Sstevel@tonic-gate creloc = 0; 1030Sstevel@tonic-gate /* data mapping has happened */ 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate f->cie_ops_end = cie_end; 1060Sstevel@tonic-gate f->cie_reloc = creloc; 1070Sstevel@tonic-gate f->fde_ops_end = fde_end; 1080Sstevel@tonic-gate f->fde_reloc = reloc; 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate (void) _Unw_get_val(&cdata, creloc, UNUM32, 1, 1, 0); 1110Sstevel@tonic-gate (void) _Unw_get_val(&cdata, creloc, UNUM8, 1, 1, 0); 1120Sstevel@tonic-gate /* LINTED alignment */ 1130Sstevel@tonic-gate (*((uint64_t *)(&(augment[0])))) = 114*6812Sraf _Unw_get_val(&cdata, creloc, ZTSTRING, 1, 1, 0); 1150Sstevel@tonic-gate f->code_align = _Unw_get_val(&cdata, creloc, ULEB128, 1, 1, 0); 1160Sstevel@tonic-gate f->data_align = _Unw_get_val(&cdata, creloc, SLEB128, 1, 1, 0); 1170Sstevel@tonic-gate (void) _Unw_get_val(&cdata, creloc, UNUM8, 1, 1, 0); 1180Sstevel@tonic-gate if (augment[0] == 'z' && 1190Sstevel@tonic-gate (scratch = _Unw_get_val(&cdata, creloc, ULEB128, 1, 1, 0)) != 0) { 1200Sstevel@tonic-gate for (p = &(augment[1]); *p != 0; p++) { 1210Sstevel@tonic-gate switch (*p) { 1220Sstevel@tonic-gate case 'P': 1230Sstevel@tonic-gate per_enc = _Unw_get_val(&cdata, creloc, 1240Sstevel@tonic-gate UNUM8, 1, 1, 0); 1250Sstevel@tonic-gate if (per_enc == 0) 1260Sstevel@tonic-gate per_enc = 0x4; 1270Sstevel@tonic-gate pfn = (_Unwind_Personality_Fn) 1280Sstevel@tonic-gate _Unw_get_val(&cdata, creloc, 1290Sstevel@tonic-gate ADDR, 1, 1, per_enc); 1300Sstevel@tonic-gate break; 1310Sstevel@tonic-gate case 'R': 1320Sstevel@tonic-gate code_enc = _Unw_get_val(&cdata, creloc, 1330Sstevel@tonic-gate UNUM8, 1, 1, 0); 1340Sstevel@tonic-gate break; 1350Sstevel@tonic-gate case 'L': 1360Sstevel@tonic-gate lsda_enc = _Unw_get_val(&cdata, creloc, 1370Sstevel@tonic-gate UNUM8, 1, 1, 0); 1380Sstevel@tonic-gate break; 1390Sstevel@tonic-gate } 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate } 1420Sstevel@tonic-gate if (code_enc == 0) 1430Sstevel@tonic-gate code_enc = 0x4; 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate func = _Unw_get_val(&data, reloc, ADDR, 1, 1, code_enc); 1460Sstevel@tonic-gate range = _Unw_get_val(&data, reloc, SIZE, 1, 1, code_enc); 1470Sstevel@tonic-gate if ((ctx->pc < func) || (ctx->pc > (func+range))) 1480Sstevel@tonic-gate return (0); 1490Sstevel@tonic-gate ctx->func = func; 1500Sstevel@tonic-gate ctx->range = range; 1510Sstevel@tonic-gate if (augment[0] == 'z') { 1520Sstevel@tonic-gate scratch = _Unw_get_val(&data, reloc, ULEB128, 1, 1, 0); 1530Sstevel@tonic-gate if (scratch == 4 && lsda_enc) { 1540Sstevel@tonic-gate /* 1550Sstevel@tonic-gate * without the two work-arounds test would be 1560Sstevel@tonic-gate * (scratch > 0 & lsda_enc) 1570Sstevel@tonic-gate */ 1580Sstevel@tonic-gate lsda = (void *)_Unw_get_val(&data, reloc, 1590Sstevel@tonic-gate ADDR, 1, 1, lsda_enc); 1600Sstevel@tonic-gate } else if (scratch == 4) { 1610Sstevel@tonic-gate /* 1620Sstevel@tonic-gate * 11/24/04 compiler is sometimes not outputing 1630Sstevel@tonic-gate * lsda_enc 1640Sstevel@tonic-gate */ 1650Sstevel@tonic-gate lsda = (void*)_Unw_get_val(&data, reloc, 1660Sstevel@tonic-gate ADDR, 1, 1, 0x1b); 1670Sstevel@tonic-gate } else if (scratch == 8) { 1680Sstevel@tonic-gate /* 1690Sstevel@tonic-gate * 11/12/04 - compiler is putting out relative 1700Sstevel@tonic-gate * encoding byte and absolute data - inconsistancy 1710Sstevel@tonic-gate * is caught here. 1720Sstevel@tonic-gate */ 1730Sstevel@tonic-gate lsda = (void *)_Unw_get_val(&data, reloc, 1740Sstevel@tonic-gate ADDR, 1, 1, 0x4); 1750Sstevel@tonic-gate } 1760Sstevel@tonic-gate } 1770Sstevel@tonic-gate if (pfn) 1780Sstevel@tonic-gate ctx->pfn = pfn; 1790Sstevel@tonic-gate if (lsda) 1800Sstevel@tonic-gate ctx->lsda = lsda; 1810Sstevel@tonic-gate f->fde_ops = data; 1820Sstevel@tonic-gate f->cie_ops = cdata; 1830Sstevel@tonic-gate f->code_enc = code_enc; 1840Sstevel@tonic-gate return (f); 1850Sstevel@tonic-gate } 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate static int 1880Sstevel@tonic-gate table_ent_log_size(int enc) 1890Sstevel@tonic-gate { 1900Sstevel@tonic-gate int val = enc & 0xf; 1910Sstevel@tonic-gate int res; 1920Sstevel@tonic-gate 1930Sstevel@tonic-gate switch (val) { 1940Sstevel@tonic-gate case 0x3: 1950Sstevel@tonic-gate res = 3; 1960Sstevel@tonic-gate break; 1970Sstevel@tonic-gate case 0x04: 1980Sstevel@tonic-gate res = 4; 1990Sstevel@tonic-gate break; 2000Sstevel@tonic-gate case 0x0b: 2010Sstevel@tonic-gate res = 3; 2020Sstevel@tonic-gate break; 2030Sstevel@tonic-gate case 0x0c: 2040Sstevel@tonic-gate res = 4; 2050Sstevel@tonic-gate break; 2060Sstevel@tonic-gate default: 2070Sstevel@tonic-gate break; 2080Sstevel@tonic-gate } 2090Sstevel@tonic-gate return (res); 2100Sstevel@tonic-gate } 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate static void 2130Sstevel@tonic-gate get_table_ent_val(unsigned char *data, unsigned char *data_end, 2140Sstevel@tonic-gate int enc, ptrdiff_t reloc, uintptr_t base, 2150Sstevel@tonic-gate uint64_t *codep, uint64_t *next_codep, void **fdep) 2160Sstevel@tonic-gate { 2170Sstevel@tonic-gate int val = enc & 0xf; 2180Sstevel@tonic-gate int rel = (enc >> 4) & 0xf; 2190Sstevel@tonic-gate unsigned char *second = data; 2200Sstevel@tonic-gate unsigned char *third = data; 2210Sstevel@tonic-gate uint64_t code; 2220Sstevel@tonic-gate void *fde; 2230Sstevel@tonic-gate uint64_t next_code; 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate switch (val) { 2260Sstevel@tonic-gate case 0x3: 2270Sstevel@tonic-gate /* LINTED alignment */ 2280Sstevel@tonic-gate code = (uint64_t)(*((uint32_t *)data)); 2290Sstevel@tonic-gate second += 4; 2300Sstevel@tonic-gate /* LINTED alignment */ 2310Sstevel@tonic-gate fde = (void *)(uint64_t)(*((uint32_t *)second)); 2320Sstevel@tonic-gate third += 8; 2330Sstevel@tonic-gate next_code = (third >= data_end)? ULONG_MAX : 2340Sstevel@tonic-gate /* LINTED alignment */ 2350Sstevel@tonic-gate (uint64_t)(*((uint32_t *)third)); 2360Sstevel@tonic-gate break; 2370Sstevel@tonic-gate case 0x04: 2380Sstevel@tonic-gate /* LINTED alignment */ 2390Sstevel@tonic-gate code = (uint64_t)(*((uint64_t *)data)); 2400Sstevel@tonic-gate second += 8; 2410Sstevel@tonic-gate /* LINTED alignment */ 2420Sstevel@tonic-gate fde = (void *)(uint64_t)(*((uint64_t *)second)); 2430Sstevel@tonic-gate third += 16; 2440Sstevel@tonic-gate next_code = (third >= data_end)? ULONG_MAX : 2450Sstevel@tonic-gate /* LINTED alignment */ 2460Sstevel@tonic-gate (uint64_t)(*((uint64_t *)third)); 2470Sstevel@tonic-gate break; 2480Sstevel@tonic-gate case 0x0b: 2490Sstevel@tonic-gate /* LINTED alignment */ 2500Sstevel@tonic-gate code = (uint64_t)(int64_t)(*((int32_t *)data)); 2510Sstevel@tonic-gate second += 4; 2520Sstevel@tonic-gate /* LINTED alignment */ 2530Sstevel@tonic-gate fde = (void *)(uint64_t)(int64_t)(*((int32_t *)second)); 2540Sstevel@tonic-gate third += 8; 2550Sstevel@tonic-gate next_code = (third >= data_end)? ULONG_MAX : 2560Sstevel@tonic-gate /* LINTED alignment */ 2570Sstevel@tonic-gate (uint64_t)(int64_t)(*((int32_t *)third)); 2580Sstevel@tonic-gate break; 2590Sstevel@tonic-gate case 0x0c: 2600Sstevel@tonic-gate /* LINTED alignment */ 2610Sstevel@tonic-gate code = (uint64_t)(*((int64_t *)data)); 2620Sstevel@tonic-gate second += 8; 2630Sstevel@tonic-gate /* LINTED alignment */ 2640Sstevel@tonic-gate fde = (void *)(uint64_t)(*((int64_t *)second)); 2650Sstevel@tonic-gate third += 16; 2660Sstevel@tonic-gate next_code = (third >= data_end)? ULONG_MAX : 2670Sstevel@tonic-gate /* LINTED alignment */ 2680Sstevel@tonic-gate (uint64_t)(*((int64_t *)third)); 2690Sstevel@tonic-gate break; 2700Sstevel@tonic-gate } 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate switch (rel) { 2730Sstevel@tonic-gate case 0: 2740Sstevel@tonic-gate break; 2750Sstevel@tonic-gate case 1: 2760Sstevel@tonic-gate code += (uint64_t)data + reloc; 2770Sstevel@tonic-gate fde = (void *)(((uint64_t)fde) + (uint64_t)second + reloc); 2780Sstevel@tonic-gate if (next_code != ULONG_MAX) 2790Sstevel@tonic-gate next_code += (uint64_t)third + reloc; 2800Sstevel@tonic-gate break; 2810Sstevel@tonic-gate case 3: 2820Sstevel@tonic-gate code += base; 2830Sstevel@tonic-gate fde = (void *)(((uint64_t)fde) + base); 2840Sstevel@tonic-gate if (next_code != ULONG_MAX) 2850Sstevel@tonic-gate next_code += base; 2860Sstevel@tonic-gate break; 2870Sstevel@tonic-gate default: 2880Sstevel@tonic-gate /* remainder not implemented */ 2890Sstevel@tonic-gate break; 2900Sstevel@tonic-gate } 2910Sstevel@tonic-gate *codep = code; 2920Sstevel@tonic-gate *fdep = fde; 2930Sstevel@tonic-gate *next_codep = next_code; 2940Sstevel@tonic-gate } 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate static void * 2980Sstevel@tonic-gate locate_fde_for_pc(uint64_t pc, int enc, 2990Sstevel@tonic-gate unsigned char *table, unsigned char *table_end, 3000Sstevel@tonic-gate ptrdiff_t reloc, uintptr_t base); 3010Sstevel@tonic-gate 3020Sstevel@tonic-gate /* 3030Sstevel@tonic-gate * Search the eh_frame info with a given pc. Return a pointer to a 3040Sstevel@tonic-gate * FDE. The search is performed in two stages. 3050Sstevel@tonic-gate * First rtld.so identifies the load module containing the target location. 3060Sstevel@tonic-gate * This returns the appropiate eh_frame_hdr, and a binary search is 3070Sstevel@tonic-gate * then performed on the eh_frame_hdr to locate the entry with 3080Sstevel@tonic-gate * a matching pc value. 3090Sstevel@tonic-gate */ 3100Sstevel@tonic-gate void * 3110Sstevel@tonic-gate _Unw_EhfhLookup(struct _Unwind_Context *ctx) 3120Sstevel@tonic-gate { 3130Sstevel@tonic-gate Dl_amd64_unwindinfo dlef; 3140Sstevel@tonic-gate void* data; 3150Sstevel@tonic-gate void* data_end; 3160Sstevel@tonic-gate uint64_t pc = ctx->pc; 3170Sstevel@tonic-gate int fp_enc, fc_enc, ft_enc; 3180Sstevel@tonic-gate unsigned char *pi, *pj; 3190Sstevel@tonic-gate ptrdiff_t reloc; 3200Sstevel@tonic-gate uintptr_t base; 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate dlef.dlui_version = 1; 3230Sstevel@tonic-gate 3240Sstevel@tonic-gate /* Locate the appropiate exception_range_entry table first */ 3250Sstevel@tonic-gate if (0 == dlamd64getunwind((void*)pc, &dlef)) { 3260Sstevel@tonic-gate return (0); 3270Sstevel@tonic-gate } 3280Sstevel@tonic-gate 3290Sstevel@tonic-gate /* 3300Sstevel@tonic-gate * you now know size and position of block of data needed for 3310Sstevel@tonic-gate * binary search ??REMOTE?? 3320Sstevel@tonic-gate */ 3330Sstevel@tonic-gate data = dlef.dlui_unwindstart; 3340Sstevel@tonic-gate if (0 == data) 3350Sstevel@tonic-gate return (0); 3360Sstevel@tonic-gate base = (uintptr_t)data; 3370Sstevel@tonic-gate data_end = dlef.dlui_unwindend; 3380Sstevel@tonic-gate reloc = 0; 3390Sstevel@tonic-gate /* ??REMOTE?? */ 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate (void) _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0); 3420Sstevel@tonic-gate fp_enc = _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0); 3430Sstevel@tonic-gate fc_enc = _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0); 3440Sstevel@tonic-gate ft_enc = _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0); 3450Sstevel@tonic-gate (void) _Unw_get_val(&data, reloc, ADDR, 1, 1, fp_enc); 3460Sstevel@tonic-gate (void) _Unw_get_val(&data, reloc, SIZE, 1, 1, fc_enc); 3470Sstevel@tonic-gate pi = data; 3480Sstevel@tonic-gate pj = data_end; 3490Sstevel@tonic-gate ctx->fde = locate_fde_for_pc(pc, ft_enc, pi, pj, reloc, base); 3500Sstevel@tonic-gate return ((void *)(ctx->fde)); 3510Sstevel@tonic-gate } 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate static void * 3540Sstevel@tonic-gate locate_fde_for_pc(uint64_t pc, int enc, 3550Sstevel@tonic-gate unsigned char *table_bg, unsigned char *table_end, 3560Sstevel@tonic-gate ptrdiff_t reloc, uintptr_t base) 3570Sstevel@tonic-gate { 3580Sstevel@tonic-gate unsigned char *pi = table_bg; 3590Sstevel@tonic-gate unsigned char *pj = table_end; 3600Sstevel@tonic-gate uint64_t range_start, range_end; 3610Sstevel@tonic-gate void* fde; 3620Sstevel@tonic-gate int log_size = table_ent_log_size(enc); 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate /* 3650Sstevel@tonic-gate * Invariant -- if there is a containing range, 3660Sstevel@tonic-gate * it must lie in the interval [pi,pj). That is, 3670Sstevel@tonic-gate * pi <= p < pj, if p exists. 3680Sstevel@tonic-gate */ 3690Sstevel@tonic-gate while (pi < pj) { 3700Sstevel@tonic-gate unsigned char *pr = 3710Sstevel@tonic-gate pi + (((pj - pi) >> (log_size + 1)) << log_size); 3720Sstevel@tonic-gate /* Don't use (pi+pj)>>1 */ 3730Sstevel@tonic-gate get_table_ent_val(pr, table_end, enc, reloc, base, 374*6812Sraf &range_start, &range_end, &fde); 3750Sstevel@tonic-gate 3760Sstevel@tonic-gate /* Return fde if tpc is in this range. */ 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate if (range_start <= pc && pc < range_end) { 3790Sstevel@tonic-gate return ((void*) fde); 3800Sstevel@tonic-gate } 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate if (range_start < pc) 3830Sstevel@tonic-gate pi = pr + (1 << log_size); 3840Sstevel@tonic-gate else 3850Sstevel@tonic-gate pj = pr; 3860Sstevel@tonic-gate } 3870Sstevel@tonic-gate return (0); 3880Sstevel@tonic-gate } 389