From: Martin Storsjö martin@martin.st
Alternatively the implementations could reside in their own standalone .c file, but this was the least amount of effort.
Signed-off-by: Martin Storsjö martin@martin.st --- dlls/ntdll/unix/dwarf.h | 849 ++++++++++++++++++++++++++++++++ dlls/ntdll/unix/signal_x86_64.c | 823 +------------------------------ 2 files changed, 851 insertions(+), 821 deletions(-) create mode 100644 dlls/ntdll/unix/dwarf.h
diff --git a/dlls/ntdll/unix/dwarf.h b/dlls/ntdll/unix/dwarf.h new file mode 100644 index 00000000000..33cc8315bd6 --- /dev/null +++ b/dlls/ntdll/unix/dwarf.h @@ -0,0 +1,849 @@ +/* + * DWARF unwinding routines + * + * Copyright 2009 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __NTDLL_DWARF_H +#define __NTDLL_DWARF_H + +#define WIN32_NO_STATUS +#include "windef.h" +#include "winnt.h" + +/*********************************************************************** + * Definitions for Dwarf unwind tables + */ + +#define DW_CFA_advance_loc 0x40 +#define DW_CFA_offset 0x80 +#define DW_CFA_restore 0xc0 +#define DW_CFA_nop 0x00 +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_def_cfa_expression 0x0f +#define DW_CFA_expression 0x10 +#define DW_CFA_offset_extended_sf 0x11 +#define DW_CFA_def_cfa_sf 0x12 +#define DW_CFA_def_cfa_offset_sf 0x13 +#define DW_CFA_val_offset 0x14 +#define DW_CFA_val_offset_sf 0x15 +#define DW_CFA_val_expression 0x16 + +#define DW_OP_addr 0x03 +#define DW_OP_deref 0x06 +#define DW_OP_const1u 0x08 +#define DW_OP_const1s 0x09 +#define DW_OP_const2u 0x0a +#define DW_OP_const2s 0x0b +#define DW_OP_const4u 0x0c +#define DW_OP_const4s 0x0d +#define DW_OP_const8u 0x0e +#define DW_OP_const8s 0x0f +#define DW_OP_constu 0x10 +#define DW_OP_consts 0x11 +#define DW_OP_dup 0x12 +#define DW_OP_drop 0x13 +#define DW_OP_over 0x14 +#define DW_OP_pick 0x15 +#define DW_OP_swap 0x16 +#define DW_OP_rot 0x17 +#define DW_OP_xderef 0x18 +#define DW_OP_abs 0x19 +#define DW_OP_and 0x1a +#define DW_OP_div 0x1b +#define DW_OP_minus 0x1c +#define DW_OP_mod 0x1d +#define DW_OP_mul 0x1e +#define DW_OP_neg 0x1f +#define DW_OP_not 0x20 +#define DW_OP_or 0x21 +#define DW_OP_plus 0x22 +#define DW_OP_plus_uconst 0x23 +#define DW_OP_shl 0x24 +#define DW_OP_shr 0x25 +#define DW_OP_shra 0x26 +#define DW_OP_xor 0x27 +#define DW_OP_bra 0x28 +#define DW_OP_eq 0x29 +#define DW_OP_ge 0x2a +#define DW_OP_gt 0x2b +#define DW_OP_le 0x2c +#define DW_OP_lt 0x2d +#define DW_OP_ne 0x2e +#define DW_OP_skip 0x2f +#define DW_OP_lit0 0x30 +#define DW_OP_lit1 0x31 +#define DW_OP_lit2 0x32 +#define DW_OP_lit3 0x33 +#define DW_OP_lit4 0x34 +#define DW_OP_lit5 0x35 +#define DW_OP_lit6 0x36 +#define DW_OP_lit7 0x37 +#define DW_OP_lit8 0x38 +#define DW_OP_lit9 0x39 +#define DW_OP_lit10 0x3a +#define DW_OP_lit11 0x3b +#define DW_OP_lit12 0x3c +#define DW_OP_lit13 0x3d +#define DW_OP_lit14 0x3e +#define DW_OP_lit15 0x3f +#define DW_OP_lit16 0x40 +#define DW_OP_lit17 0x41 +#define DW_OP_lit18 0x42 +#define DW_OP_lit19 0x43 +#define DW_OP_lit20 0x44 +#define DW_OP_lit21 0x45 +#define DW_OP_lit22 0x46 +#define DW_OP_lit23 0x47 +#define DW_OP_lit24 0x48 +#define DW_OP_lit25 0x49 +#define DW_OP_lit26 0x4a +#define DW_OP_lit27 0x4b +#define DW_OP_lit28 0x4c +#define DW_OP_lit29 0x4d +#define DW_OP_lit30 0x4e +#define DW_OP_lit31 0x4f +#define DW_OP_reg0 0x50 +#define DW_OP_reg1 0x51 +#define DW_OP_reg2 0x52 +#define DW_OP_reg3 0x53 +#define DW_OP_reg4 0x54 +#define DW_OP_reg5 0x55 +#define DW_OP_reg6 0x56 +#define DW_OP_reg7 0x57 +#define DW_OP_reg8 0x58 +#define DW_OP_reg9 0x59 +#define DW_OP_reg10 0x5a +#define DW_OP_reg11 0x5b +#define DW_OP_reg12 0x5c +#define DW_OP_reg13 0x5d +#define DW_OP_reg14 0x5e +#define DW_OP_reg15 0x5f +#define DW_OP_reg16 0x60 +#define DW_OP_reg17 0x61 +#define DW_OP_reg18 0x62 +#define DW_OP_reg19 0x63 +#define DW_OP_reg20 0x64 +#define DW_OP_reg21 0x65 +#define DW_OP_reg22 0x66 +#define DW_OP_reg23 0x67 +#define DW_OP_reg24 0x68 +#define DW_OP_reg25 0x69 +#define DW_OP_reg26 0x6a +#define DW_OP_reg27 0x6b +#define DW_OP_reg28 0x6c +#define DW_OP_reg29 0x6d +#define DW_OP_reg30 0x6e +#define DW_OP_reg31 0x6f +#define DW_OP_breg0 0x70 +#define DW_OP_breg1 0x71 +#define DW_OP_breg2 0x72 +#define DW_OP_breg3 0x73 +#define DW_OP_breg4 0x74 +#define DW_OP_breg5 0x75 +#define DW_OP_breg6 0x76 +#define DW_OP_breg7 0x77 +#define DW_OP_breg8 0x78 +#define DW_OP_breg9 0x79 +#define DW_OP_breg10 0x7a +#define DW_OP_breg11 0x7b +#define DW_OP_breg12 0x7c +#define DW_OP_breg13 0x7d +#define DW_OP_breg14 0x7e +#define DW_OP_breg15 0x7f +#define DW_OP_breg16 0x80 +#define DW_OP_breg17 0x81 +#define DW_OP_breg18 0x82 +#define DW_OP_breg19 0x83 +#define DW_OP_breg20 0x84 +#define DW_OP_breg21 0x85 +#define DW_OP_breg22 0x86 +#define DW_OP_breg23 0x87 +#define DW_OP_breg24 0x88 +#define DW_OP_breg25 0x89 +#define DW_OP_breg26 0x8a +#define DW_OP_breg27 0x8b +#define DW_OP_breg28 0x8c +#define DW_OP_breg29 0x8d +#define DW_OP_breg30 0x8e +#define DW_OP_breg31 0x8f +#define DW_OP_regx 0x90 +#define DW_OP_fbreg 0x91 +#define DW_OP_bregx 0x92 +#define DW_OP_piece 0x93 +#define DW_OP_deref_size 0x94 +#define DW_OP_xderef_size 0x95 +#define DW_OP_nop 0x96 +#define DW_OP_push_object_address 0x97 +#define DW_OP_call2 0x98 +#define DW_OP_call4 0x99 +#define DW_OP_call_ref 0x9a +#define DW_OP_form_tls_address 0x9b +#define DW_OP_call_frame_cfa 0x9c +#define DW_OP_bit_piece 0x9d +#define DW_OP_lo_user 0xe0 +#define DW_OP_hi_user 0xff +#define DW_OP_GNU_push_tls_address 0xe0 +#define DW_OP_GNU_uninit 0xf0 +#define DW_OP_GNU_encoded_addr 0xf1 + +#define DW_EH_PE_native 0x00 +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0a +#define DW_EH_PE_sdata4 0x0b +#define DW_EH_PE_sdata8 0x0c +#define DW_EH_PE_signed 0x08 +#define DW_EH_PE_abs 0x00 +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 +#define DW_EH_PE_indirect 0x80 +#define DW_EH_PE_omit 0xff + +struct dwarf_eh_bases +{ + void *tbase; + void *dbase; + void *func; +}; + +struct dwarf_cie +{ + unsigned int length; + int id; + unsigned char version; + unsigned char augmentation[1]; +}; + +struct dwarf_fde +{ + unsigned int length; + unsigned int cie_offset; +}; + +extern const struct dwarf_fde *_Unwind_Find_FDE (void *, struct dwarf_eh_bases *); + +static unsigned char dwarf_get_u1( const unsigned char **p ) +{ + return *(*p)++; +} + +static unsigned short dwarf_get_u2( const unsigned char **p ) +{ + unsigned int ret = (*p)[0] | ((*p)[1] << 8); + (*p) += 2; + return ret; +} + +static unsigned int dwarf_get_u4( const unsigned char **p ) +{ + unsigned int ret = (*p)[0] | ((*p)[1] << 8) | ((*p)[2] << 16) | ((*p)[3] << 24); + (*p) += 4; + return ret; +} + +static ULONG64 dwarf_get_u8( const unsigned char **p ) +{ + ULONG64 low = dwarf_get_u4( p ); + ULONG64 high = dwarf_get_u4( p ); + return low | (high << 32); +} + +static ULONG_PTR dwarf_get_uleb128( const unsigned char **p ) +{ + ULONG_PTR ret = 0; + unsigned int shift = 0; + unsigned char byte; + + do + { + byte = **p; + ret |= (ULONG_PTR)(byte & 0x7f) << shift; + shift += 7; + (*p)++; + } while (byte & 0x80); + return ret; +} + +static LONG_PTR dwarf_get_sleb128( const unsigned char **p ) +{ + ULONG_PTR ret = 0; + unsigned int shift = 0; + unsigned char byte; + + do + { + byte = **p; + ret |= (ULONG_PTR)(byte & 0x7f) << shift; + shift += 7; + (*p)++; + } while (byte & 0x80); + + if ((shift < 8 * sizeof(ret)) && (byte & 0x40)) ret |= -((ULONG_PTR)1 << shift); + return ret; +} + +static ULONG_PTR dwarf_get_ptr( const unsigned char **p, unsigned char encoding, const struct dwarf_eh_bases *bases ) +{ + ULONG_PTR base; + + if (encoding == DW_EH_PE_omit) return 0; + + switch (encoding & 0x70) + { + case DW_EH_PE_abs: + base = 0; + break; + case DW_EH_PE_pcrel: + base = (ULONG_PTR)*p; + break; + case DW_EH_PE_textrel: + base = (ULONG_PTR)bases->tbase; + break; + case DW_EH_PE_datarel: + base = (ULONG_PTR)bases->dbase; + break; + case DW_EH_PE_funcrel: + base = (ULONG_PTR)bases->func; + break; + case DW_EH_PE_aligned: + base = ((ULONG_PTR)*p + 7) & ~7ul; + break; + default: + FIXME( "unsupported encoding %02x\n", encoding ); + return 0; + } + + switch (encoding & 0x0f) + { + case DW_EH_PE_native: base += dwarf_get_u8( p ); break; + case DW_EH_PE_uleb128: base += dwarf_get_uleb128( p ); break; + case DW_EH_PE_udata2: base += dwarf_get_u2( p ); break; + case DW_EH_PE_udata4: base += dwarf_get_u4( p ); break; + case DW_EH_PE_udata8: base += dwarf_get_u8( p ); break; + case DW_EH_PE_sleb128: base += dwarf_get_sleb128( p ); break; + case DW_EH_PE_sdata2: base += (signed short)dwarf_get_u2( p ); break; + case DW_EH_PE_sdata4: base += (signed int)dwarf_get_u4( p ); break; + case DW_EH_PE_sdata8: base += (LONG64)dwarf_get_u8( p ); break; + default: + FIXME( "unsupported encoding %02x\n", encoding ); + return 0; + } + if (encoding & DW_EH_PE_indirect) base = *(ULONG_PTR *)base; + return base; +} + +enum reg_rule +{ + RULE_UNSET, /* not set at all */ + RULE_UNDEFINED, /* undefined value */ + RULE_SAME, /* same value as previous frame */ + RULE_CFA_OFFSET, /* stored at cfa offset */ + RULE_OTHER_REG, /* stored in other register */ + RULE_EXPRESSION, /* address specified by expression */ + RULE_VAL_EXPRESSION /* value specified by expression */ +}; + +#define NB_FRAME_REGS 41 +#define MAX_SAVED_STATES 16 + +struct frame_state +{ + ULONG_PTR cfa_offset; + unsigned char cfa_reg; + enum reg_rule cfa_rule; + enum reg_rule rules[NB_FRAME_REGS]; + ULONG64 regs[NB_FRAME_REGS]; +}; + +struct frame_info +{ + ULONG_PTR ip; + ULONG_PTR code_align; + LONG_PTR data_align; + unsigned char retaddr_reg; + unsigned char fde_encoding; + unsigned char signal_frame; + unsigned char state_sp; + struct frame_state state; + struct frame_state *state_stack; +}; + +static const char *dwarf_reg_names[NB_FRAME_REGS] = +{ +/* 0-7 */ "%rax", "%rdx", "%rcx", "%rbx", "%rsi", "%rdi", "%rbp", "%rsp", +/* 8-16 */ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "%rip", +/* 17-24 */ "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", +/* 25-32 */ "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14", "%xmm15", +/* 33-40 */ "%st0", "%st1", "%st2", "%st3", "%st4", "%st5", "%st6", "%st7" +}; + +static BOOL valid_reg( ULONG_PTR reg ) +{ + if (reg >= NB_FRAME_REGS) FIXME( "unsupported reg %lx\n", reg ); + return (reg < NB_FRAME_REGS); +} + +static void execute_cfa_instructions( const unsigned char *ptr, const unsigned char *end, + ULONG_PTR last_ip, struct frame_info *info, + const struct dwarf_eh_bases *bases ) +{ + while (ptr < end && info->ip < last_ip + info->signal_frame) + { + const unsigned char op = *ptr++; + + if (op & 0xc0) + { + switch (op & 0xc0) + { + case DW_CFA_advance_loc: + { + ULONG_PTR offset = (op & 0x3f) * info->code_align; + TRACE( "%lx: DW_CFA_advance_loc %lu\n", info->ip, offset ); + info->ip += offset; + break; + } + case DW_CFA_offset: + { + ULONG_PTR reg = op & 0x3f; + LONG_PTR offset = dwarf_get_uleb128( &ptr ) * info->data_align; + if (!valid_reg( reg )) break; + TRACE( "%lx: DW_CFA_offset %s, %ld\n", info->ip, dwarf_reg_names[reg], offset ); + info->state.regs[reg] = offset; + info->state.rules[reg] = RULE_CFA_OFFSET; + break; + } + case DW_CFA_restore: + { + ULONG_PTR reg = op & 0x3f; + if (!valid_reg( reg )) break; + TRACE( "%lx: DW_CFA_restore %s\n", info->ip, dwarf_reg_names[reg] ); + info->state.rules[reg] = RULE_UNSET; + break; + } + } + } + else switch (op) + { + case DW_CFA_nop: + break; + case DW_CFA_set_loc: + { + ULONG_PTR loc = dwarf_get_ptr( &ptr, info->fde_encoding, bases ); + TRACE( "%lx: DW_CFA_set_loc %lx\n", info->ip, loc ); + info->ip = loc; + break; + } + case DW_CFA_advance_loc1: + { + ULONG_PTR offset = *ptr++ * info->code_align; + TRACE( "%lx: DW_CFA_advance_loc1 %lu\n", info->ip, offset ); + info->ip += offset; + break; + } + case DW_CFA_advance_loc2: + { + ULONG_PTR offset = dwarf_get_u2( &ptr ) * info->code_align; + TRACE( "%lx: DW_CFA_advance_loc2 %lu\n", info->ip, offset ); + info->ip += offset; + break; + } + case DW_CFA_advance_loc4: + { + ULONG_PTR offset = dwarf_get_u4( &ptr ) * info->code_align; + TRACE( "%lx: DW_CFA_advance_loc4 %lu\n", info->ip, offset ); + info->ip += offset; + break; + } + case DW_CFA_offset_extended: + case DW_CFA_offset_extended_sf: + { + ULONG_PTR reg = dwarf_get_uleb128( &ptr ); + LONG_PTR offset = (op == DW_CFA_offset_extended) ? dwarf_get_uleb128( &ptr ) * info->data_align + : dwarf_get_sleb128( &ptr ) * info->data_align; + if (!valid_reg( reg )) break; + TRACE( "%lx: DW_CFA_offset_extended %s, %ld\n", info->ip, dwarf_reg_names[reg], offset ); + info->state.regs[reg] = offset; + info->state.rules[reg] = RULE_CFA_OFFSET; + break; + } + case DW_CFA_restore_extended: + { + ULONG_PTR reg = dwarf_get_uleb128( &ptr ); + if (!valid_reg( reg )) break; + TRACE( "%lx: DW_CFA_restore_extended %s\n", info->ip, dwarf_reg_names[reg] ); + info->state.rules[reg] = RULE_UNSET; + break; + } + case DW_CFA_undefined: + { + ULONG_PTR reg = dwarf_get_uleb128( &ptr ); + if (!valid_reg( reg )) break; + TRACE( "%lx: DW_CFA_undefined %s\n", info->ip, dwarf_reg_names[reg] ); + info->state.rules[reg] = RULE_UNDEFINED; + break; + } + case DW_CFA_same_value: + { + ULONG_PTR reg = dwarf_get_uleb128( &ptr ); + if (!valid_reg( reg )) break; + TRACE( "%lx: DW_CFA_same_value %s\n", info->ip, dwarf_reg_names[reg] ); + info->state.regs[reg] = reg; + info->state.rules[reg] = RULE_SAME; + break; + } + case DW_CFA_register: + { + ULONG_PTR reg = dwarf_get_uleb128( &ptr ); + ULONG_PTR reg2 = dwarf_get_uleb128( &ptr ); + if (!valid_reg( reg ) || !valid_reg( reg2 )) break; + TRACE( "%lx: DW_CFA_register %s == %s\n", info->ip, dwarf_reg_names[reg], dwarf_reg_names[reg2] ); + info->state.regs[reg] = reg2; + info->state.rules[reg] = RULE_OTHER_REG; + break; + } + case DW_CFA_remember_state: + TRACE( "%lx: DW_CFA_remember_state\n", info->ip ); + if (info->state_sp >= MAX_SAVED_STATES) + FIXME( "%lx: DW_CFA_remember_state too many nested saves\n", info->ip ); + else + info->state_stack[info->state_sp++] = info->state; + break; + case DW_CFA_restore_state: + TRACE( "%lx: DW_CFA_restore_state\n", info->ip ); + if (!info->state_sp) + FIXME( "%lx: DW_CFA_restore_state without corresponding save\n", info->ip ); + else + info->state = info->state_stack[--info->state_sp]; + break; + case DW_CFA_def_cfa: + case DW_CFA_def_cfa_sf: + { + ULONG_PTR reg = dwarf_get_uleb128( &ptr ); + ULONG_PTR offset = (op == DW_CFA_def_cfa) ? dwarf_get_uleb128( &ptr ) + : dwarf_get_sleb128( &ptr ) * info->data_align; + if (!valid_reg( reg )) break; + TRACE( "%lx: DW_CFA_def_cfa %s, %lu\n", info->ip, dwarf_reg_names[reg], offset ); + info->state.cfa_reg = reg; + info->state.cfa_offset = offset; + info->state.cfa_rule = RULE_CFA_OFFSET; + break; + } + case DW_CFA_def_cfa_register: + { + ULONG_PTR reg = dwarf_get_uleb128( &ptr ); + if (!valid_reg( reg )) break; + TRACE( "%lx: DW_CFA_def_cfa_register %s\n", info->ip, dwarf_reg_names[reg] ); + info->state.cfa_reg = reg; + info->state.cfa_rule = RULE_CFA_OFFSET; + break; + } + case DW_CFA_def_cfa_offset: + case DW_CFA_def_cfa_offset_sf: + { + ULONG_PTR offset = (op == DW_CFA_def_cfa_offset) ? dwarf_get_uleb128( &ptr ) + : dwarf_get_sleb128( &ptr ) * info->data_align; + TRACE( "%lx: DW_CFA_def_cfa_offset %lu\n", info->ip, offset ); + info->state.cfa_offset = offset; + info->state.cfa_rule = RULE_CFA_OFFSET; + break; + } + case DW_CFA_def_cfa_expression: + { + ULONG_PTR expr = (ULONG_PTR)ptr; + ULONG_PTR len = dwarf_get_uleb128( &ptr ); + TRACE( "%lx: DW_CFA_def_cfa_expression %lx-%lx\n", info->ip, expr, expr+len ); + info->state.cfa_offset = expr; + info->state.cfa_rule = RULE_VAL_EXPRESSION; + ptr += len; + break; + } + case DW_CFA_expression: + case DW_CFA_val_expression: + { + ULONG_PTR reg = dwarf_get_uleb128( &ptr ); + ULONG_PTR expr = (ULONG_PTR)ptr; + ULONG_PTR len = dwarf_get_uleb128( &ptr ); + if (!valid_reg( reg )) break; + TRACE( "%lx: DW_CFA_%sexpression %s %lx-%lx\n", + info->ip, (op == DW_CFA_expression) ? "" : "val_", dwarf_reg_names[reg], expr, expr+len ); + info->state.regs[reg] = expr; + info->state.rules[reg] = (op == DW_CFA_expression) ? RULE_EXPRESSION : RULE_VAL_EXPRESSION; + ptr += len; + break; + } + default: + FIXME( "%lx: unknown CFA opcode %02x\n", info->ip, op ); + break; + } + } +} + +/* retrieve a context register from its dwarf number */ +static void *get_context_reg( CONTEXT *context, ULONG_PTR dw_reg ) +{ + switch (dw_reg) + { + case 0: return &context->Rax; + case 1: return &context->Rdx; + case 2: return &context->Rcx; + case 3: return &context->Rbx; + case 4: return &context->Rsi; + case 5: return &context->Rdi; + case 6: return &context->Rbp; + case 7: return &context->Rsp; + case 8: return &context->R8; + case 9: return &context->R9; + case 10: return &context->R10; + case 11: return &context->R11; + case 12: return &context->R12; + case 13: return &context->R13; + case 14: return &context->R14; + case 15: return &context->R15; + case 16: return &context->Rip; + case 17: return &context->u.s.Xmm0; + case 18: return &context->u.s.Xmm1; + case 19: return &context->u.s.Xmm2; + case 20: return &context->u.s.Xmm3; + case 21: return &context->u.s.Xmm4; + case 22: return &context->u.s.Xmm5; + case 23: return &context->u.s.Xmm6; + case 24: return &context->u.s.Xmm7; + case 25: return &context->u.s.Xmm8; + case 26: return &context->u.s.Xmm9; + case 27: return &context->u.s.Xmm10; + case 28: return &context->u.s.Xmm11; + case 29: return &context->u.s.Xmm12; + case 30: return &context->u.s.Xmm13; + case 31: return &context->u.s.Xmm14; + case 32: return &context->u.s.Xmm15; + case 33: return &context->u.s.Legacy[0]; + case 34: return &context->u.s.Legacy[1]; + case 35: return &context->u.s.Legacy[2]; + case 36: return &context->u.s.Legacy[3]; + case 37: return &context->u.s.Legacy[4]; + case 38: return &context->u.s.Legacy[5]; + case 39: return &context->u.s.Legacy[6]; + case 40: return &context->u.s.Legacy[7]; + default: return NULL; + } +} + +/* set a context register from its dwarf number */ +static void set_context_reg( CONTEXT *context, ULONG_PTR dw_reg, void *val ) +{ + switch (dw_reg) + { + case 0: context->Rax = *(ULONG64 *)val; break; + case 1: context->Rdx = *(ULONG64 *)val; break; + case 2: context->Rcx = *(ULONG64 *)val; break; + case 3: context->Rbx = *(ULONG64 *)val; break; + case 4: context->Rsi = *(ULONG64 *)val; break; + case 5: context->Rdi = *(ULONG64 *)val; break; + case 6: context->Rbp = *(ULONG64 *)val; break; + case 7: context->Rsp = *(ULONG64 *)val; break; + case 8: context->R8 = *(ULONG64 *)val; break; + case 9: context->R9 = *(ULONG64 *)val; break; + case 10: context->R10 = *(ULONG64 *)val; break; + case 11: context->R11 = *(ULONG64 *)val; break; + case 12: context->R12 = *(ULONG64 *)val; break; + case 13: context->R13 = *(ULONG64 *)val; break; + case 14: context->R14 = *(ULONG64 *)val; break; + case 15: context->R15 = *(ULONG64 *)val; break; + case 16: context->Rip = *(ULONG64 *)val; break; + case 17: memcpy( &context->u.s.Xmm0, val, sizeof(M128A) ); break; + case 18: memcpy( &context->u.s.Xmm1, val, sizeof(M128A) ); break; + case 19: memcpy( &context->u.s.Xmm2, val, sizeof(M128A) ); break; + case 20: memcpy( &context->u.s.Xmm3, val, sizeof(M128A) ); break; + case 21: memcpy( &context->u.s.Xmm4, val, sizeof(M128A) ); break; + case 22: memcpy( &context->u.s.Xmm5, val, sizeof(M128A) ); break; + case 23: memcpy( &context->u.s.Xmm6, val, sizeof(M128A) ); break; + case 24: memcpy( &context->u.s.Xmm7, val, sizeof(M128A) ); break; + case 25: memcpy( &context->u.s.Xmm8, val, sizeof(M128A) ); break; + case 26: memcpy( &context->u.s.Xmm9, val, sizeof(M128A) ); break; + case 27: memcpy( &context->u.s.Xmm10, val, sizeof(M128A) ); break; + case 28: memcpy( &context->u.s.Xmm11, val, sizeof(M128A) ); break; + case 29: memcpy( &context->u.s.Xmm12, val, sizeof(M128A) ); break; + case 30: memcpy( &context->u.s.Xmm13, val, sizeof(M128A) ); break; + case 31: memcpy( &context->u.s.Xmm14, val, sizeof(M128A) ); break; + case 32: memcpy( &context->u.s.Xmm15, val, sizeof(M128A) ); break; + case 33: memcpy( &context->u.s.Legacy[0], val, sizeof(M128A) ); break; + case 34: memcpy( &context->u.s.Legacy[1], val, sizeof(M128A) ); break; + case 35: memcpy( &context->u.s.Legacy[2], val, sizeof(M128A) ); break; + case 36: memcpy( &context->u.s.Legacy[3], val, sizeof(M128A) ); break; + case 37: memcpy( &context->u.s.Legacy[4], val, sizeof(M128A) ); break; + case 38: memcpy( &context->u.s.Legacy[5], val, sizeof(M128A) ); break; + case 39: memcpy( &context->u.s.Legacy[6], val, sizeof(M128A) ); break; + case 40: memcpy( &context->u.s.Legacy[7], val, sizeof(M128A) ); break; + } +} + +static ULONG_PTR eval_expression( const unsigned char *p, CONTEXT *context, + const struct dwarf_eh_bases *bases ) +{ + ULONG_PTR reg, tmp, stack[64]; + int sp = -1; + ULONG_PTR len = dwarf_get_uleb128(&p); + const unsigned char *end = p + len; + + while (p < end) + { + unsigned char opcode = dwarf_get_u1(&p); + + if (opcode >= DW_OP_lit0 && opcode <= DW_OP_lit31) + stack[++sp] = opcode - DW_OP_lit0; + else if (opcode >= DW_OP_reg0 && opcode <= DW_OP_reg31) + stack[++sp] = *(ULONG_PTR *)get_context_reg( context, opcode - DW_OP_reg0 ); + else if (opcode >= DW_OP_breg0 && opcode <= DW_OP_breg31) + stack[++sp] = *(ULONG_PTR *)get_context_reg( context, opcode - DW_OP_breg0 ) + dwarf_get_sleb128(&p); + else switch (opcode) + { + case DW_OP_nop: break; + case DW_OP_addr: stack[++sp] = dwarf_get_u8(&p); break; + case DW_OP_const1u: stack[++sp] = dwarf_get_u1(&p); break; + case DW_OP_const1s: stack[++sp] = (signed char)dwarf_get_u1(&p); break; + case DW_OP_const2u: stack[++sp] = dwarf_get_u2(&p); break; + case DW_OP_const2s: stack[++sp] = (short)dwarf_get_u2(&p); break; + case DW_OP_const4u: stack[++sp] = dwarf_get_u4(&p); break; + case DW_OP_const4s: stack[++sp] = (signed int)dwarf_get_u4(&p); break; + case DW_OP_const8u: stack[++sp] = dwarf_get_u8(&p); break; + case DW_OP_const8s: stack[++sp] = (LONG_PTR)dwarf_get_u8(&p); break; + case DW_OP_constu: stack[++sp] = dwarf_get_uleb128(&p); break; + case DW_OP_consts: stack[++sp] = dwarf_get_sleb128(&p); break; + case DW_OP_deref: stack[sp] = *(ULONG_PTR *)stack[sp]; break; + case DW_OP_dup: stack[sp + 1] = stack[sp]; sp++; break; + case DW_OP_drop: sp--; break; + case DW_OP_over: stack[sp + 1] = stack[sp - 1]; sp++; break; + case DW_OP_pick: stack[sp + 1] = stack[sp - dwarf_get_u1(&p)]; sp++; break; + case DW_OP_swap: tmp = stack[sp]; stack[sp] = stack[sp-1]; stack[sp-1] = tmp; break; + case DW_OP_rot: tmp = stack[sp]; stack[sp] = stack[sp-1]; stack[sp-1] = stack[sp-2]; stack[sp-2] = tmp; break; + case DW_OP_abs: stack[sp] = labs(stack[sp]); break; + case DW_OP_neg: stack[sp] = -stack[sp]; break; + case DW_OP_not: stack[sp] = ~stack[sp]; break; + case DW_OP_and: stack[sp-1] &= stack[sp]; sp--; break; + case DW_OP_or: stack[sp-1] |= stack[sp]; sp--; break; + case DW_OP_minus: stack[sp-1] -= stack[sp]; sp--; break; + case DW_OP_mul: stack[sp-1] *= stack[sp]; sp--; break; + case DW_OP_plus: stack[sp-1] += stack[sp]; sp--; break; + case DW_OP_xor: stack[sp-1] ^= stack[sp]; sp--; break; + case DW_OP_shl: stack[sp-1] <<= stack[sp]; sp--; break; + case DW_OP_shr: stack[sp-1] >>= stack[sp]; sp--; break; + case DW_OP_plus_uconst: stack[sp] += dwarf_get_uleb128(&p); break; + case DW_OP_shra: stack[sp-1] = (LONG_PTR)stack[sp-1] / (1 << stack[sp]); sp--; break; + case DW_OP_div: stack[sp-1] = (LONG_PTR)stack[sp-1] / (LONG_PTR)stack[sp]; sp--; break; + case DW_OP_mod: stack[sp-1] = (LONG_PTR)stack[sp-1] % (LONG_PTR)stack[sp]; sp--; break; + case DW_OP_ge: stack[sp-1] = ((LONG_PTR)stack[sp-1] >= (LONG_PTR)stack[sp]); sp--; break; + case DW_OP_gt: stack[sp-1] = ((LONG_PTR)stack[sp-1] > (LONG_PTR)stack[sp]); sp--; break; + case DW_OP_le: stack[sp-1] = ((LONG_PTR)stack[sp-1] <= (LONG_PTR)stack[sp]); sp--; break; + case DW_OP_lt: stack[sp-1] = ((LONG_PTR)stack[sp-1] < (LONG_PTR)stack[sp]); sp--; break; + case DW_OP_eq: stack[sp-1] = (stack[sp-1] == stack[sp]); sp--; break; + case DW_OP_ne: stack[sp-1] = (stack[sp-1] != stack[sp]); sp--; break; + case DW_OP_skip: tmp = (short)dwarf_get_u2(&p); p += tmp; break; + case DW_OP_bra: tmp = (short)dwarf_get_u2(&p); if (!stack[sp--]) p += tmp; break; + case DW_OP_GNU_encoded_addr: tmp = *p++; stack[++sp] = dwarf_get_ptr( &p, tmp, bases ); break; + case DW_OP_regx: stack[++sp] = *(ULONG_PTR *)get_context_reg( context, dwarf_get_uleb128(&p) ); break; + case DW_OP_bregx: + reg = dwarf_get_uleb128(&p); + tmp = dwarf_get_sleb128(&p); + stack[++sp] = *(ULONG_PTR *)get_context_reg( context, reg ) + tmp; + break; + case DW_OP_deref_size: + switch (*p++) + { + case 1: stack[sp] = *(unsigned char *)stack[sp]; break; + case 2: stack[sp] = *(unsigned short *)stack[sp]; break; + case 4: stack[sp] = *(unsigned int *)stack[sp]; break; + case 8: stack[sp] = *(ULONG_PTR *)stack[sp]; break; + } + break; + default: + FIXME( "unhandled opcode %02x\n", opcode ); + } + } + return stack[sp]; +} + +/* apply the computed frame info to the actual context */ +static void apply_frame_state( CONTEXT *context, struct frame_state *state, + const struct dwarf_eh_bases *bases ) +{ + unsigned int i; + ULONG_PTR cfa, value; + CONTEXT new_context = *context; + + switch (state->cfa_rule) + { + case RULE_EXPRESSION: + cfa = *(ULONG_PTR *)eval_expression( (const unsigned char *)state->cfa_offset, context, bases ); + break; + case RULE_VAL_EXPRESSION: + cfa = eval_expression( (const unsigned char *)state->cfa_offset, context, bases ); + break; + default: + cfa = *(ULONG_PTR *)get_context_reg( context, state->cfa_reg ) + state->cfa_offset; + break; + } + if (!cfa) return; + + for (i = 0; i < NB_FRAME_REGS; i++) + { + switch (state->rules[i]) + { + case RULE_UNSET: + case RULE_UNDEFINED: + case RULE_SAME: + break; + case RULE_CFA_OFFSET: + set_context_reg( &new_context, i, (char *)cfa + state->regs[i] ); + break; + case RULE_OTHER_REG: + set_context_reg( &new_context, i, get_context_reg( context, state->regs[i] )); + break; + case RULE_EXPRESSION: + value = eval_expression( (const unsigned char *)state->regs[i], context, bases ); + set_context_reg( &new_context, i, (void *)value ); + break; + case RULE_VAL_EXPRESSION: + value = eval_expression( (const unsigned char *)state->regs[i], context, bases ); + set_context_reg( &new_context, i, &value ); + break; + } + } + new_context.Rsp = cfa; + *context = new_context; +} + +#endif /* __NTDLL_DWARF_H */ diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 5787f1dc6f9..41f860ac4f4 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -80,6 +80,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(unwind); WINE_DECLARE_DEBUG_CHANNEL(seh);
+#include "dwarf.h" + /*********************************************************************** * signal context platform-specific definitions */ @@ -490,827 +492,6 @@ static USHORT cs64_sel; /* selector for %cs in 64-bit mode */ static USHORT ds64_sel; /* selector for %ds/%es/%ss in 64-bit mode */ static USHORT fs32_sel; /* selector for %fs in 32-bit mode */
-/*********************************************************************** - * Definitions for Dwarf unwind tables - */ - -#define DW_CFA_advance_loc 0x40 -#define DW_CFA_offset 0x80 -#define DW_CFA_restore 0xc0 -#define DW_CFA_nop 0x00 -#define DW_CFA_set_loc 0x01 -#define DW_CFA_advance_loc1 0x02 -#define DW_CFA_advance_loc2 0x03 -#define DW_CFA_advance_loc4 0x04 -#define DW_CFA_offset_extended 0x05 -#define DW_CFA_restore_extended 0x06 -#define DW_CFA_undefined 0x07 -#define DW_CFA_same_value 0x08 -#define DW_CFA_register 0x09 -#define DW_CFA_remember_state 0x0a -#define DW_CFA_restore_state 0x0b -#define DW_CFA_def_cfa 0x0c -#define DW_CFA_def_cfa_register 0x0d -#define DW_CFA_def_cfa_offset 0x0e -#define DW_CFA_def_cfa_expression 0x0f -#define DW_CFA_expression 0x10 -#define DW_CFA_offset_extended_sf 0x11 -#define DW_CFA_def_cfa_sf 0x12 -#define DW_CFA_def_cfa_offset_sf 0x13 -#define DW_CFA_val_offset 0x14 -#define DW_CFA_val_offset_sf 0x15 -#define DW_CFA_val_expression 0x16 - -#define DW_OP_addr 0x03 -#define DW_OP_deref 0x06 -#define DW_OP_const1u 0x08 -#define DW_OP_const1s 0x09 -#define DW_OP_const2u 0x0a -#define DW_OP_const2s 0x0b -#define DW_OP_const4u 0x0c -#define DW_OP_const4s 0x0d -#define DW_OP_const8u 0x0e -#define DW_OP_const8s 0x0f -#define DW_OP_constu 0x10 -#define DW_OP_consts 0x11 -#define DW_OP_dup 0x12 -#define DW_OP_drop 0x13 -#define DW_OP_over 0x14 -#define DW_OP_pick 0x15 -#define DW_OP_swap 0x16 -#define DW_OP_rot 0x17 -#define DW_OP_xderef 0x18 -#define DW_OP_abs 0x19 -#define DW_OP_and 0x1a -#define DW_OP_div 0x1b -#define DW_OP_minus 0x1c -#define DW_OP_mod 0x1d -#define DW_OP_mul 0x1e -#define DW_OP_neg 0x1f -#define DW_OP_not 0x20 -#define DW_OP_or 0x21 -#define DW_OP_plus 0x22 -#define DW_OP_plus_uconst 0x23 -#define DW_OP_shl 0x24 -#define DW_OP_shr 0x25 -#define DW_OP_shra 0x26 -#define DW_OP_xor 0x27 -#define DW_OP_bra 0x28 -#define DW_OP_eq 0x29 -#define DW_OP_ge 0x2a -#define DW_OP_gt 0x2b -#define DW_OP_le 0x2c -#define DW_OP_lt 0x2d -#define DW_OP_ne 0x2e -#define DW_OP_skip 0x2f -#define DW_OP_lit0 0x30 -#define DW_OP_lit1 0x31 -#define DW_OP_lit2 0x32 -#define DW_OP_lit3 0x33 -#define DW_OP_lit4 0x34 -#define DW_OP_lit5 0x35 -#define DW_OP_lit6 0x36 -#define DW_OP_lit7 0x37 -#define DW_OP_lit8 0x38 -#define DW_OP_lit9 0x39 -#define DW_OP_lit10 0x3a -#define DW_OP_lit11 0x3b -#define DW_OP_lit12 0x3c -#define DW_OP_lit13 0x3d -#define DW_OP_lit14 0x3e -#define DW_OP_lit15 0x3f -#define DW_OP_lit16 0x40 -#define DW_OP_lit17 0x41 -#define DW_OP_lit18 0x42 -#define DW_OP_lit19 0x43 -#define DW_OP_lit20 0x44 -#define DW_OP_lit21 0x45 -#define DW_OP_lit22 0x46 -#define DW_OP_lit23 0x47 -#define DW_OP_lit24 0x48 -#define DW_OP_lit25 0x49 -#define DW_OP_lit26 0x4a -#define DW_OP_lit27 0x4b -#define DW_OP_lit28 0x4c -#define DW_OP_lit29 0x4d -#define DW_OP_lit30 0x4e -#define DW_OP_lit31 0x4f -#define DW_OP_reg0 0x50 -#define DW_OP_reg1 0x51 -#define DW_OP_reg2 0x52 -#define DW_OP_reg3 0x53 -#define DW_OP_reg4 0x54 -#define DW_OP_reg5 0x55 -#define DW_OP_reg6 0x56 -#define DW_OP_reg7 0x57 -#define DW_OP_reg8 0x58 -#define DW_OP_reg9 0x59 -#define DW_OP_reg10 0x5a -#define DW_OP_reg11 0x5b -#define DW_OP_reg12 0x5c -#define DW_OP_reg13 0x5d -#define DW_OP_reg14 0x5e -#define DW_OP_reg15 0x5f -#define DW_OP_reg16 0x60 -#define DW_OP_reg17 0x61 -#define DW_OP_reg18 0x62 -#define DW_OP_reg19 0x63 -#define DW_OP_reg20 0x64 -#define DW_OP_reg21 0x65 -#define DW_OP_reg22 0x66 -#define DW_OP_reg23 0x67 -#define DW_OP_reg24 0x68 -#define DW_OP_reg25 0x69 -#define DW_OP_reg26 0x6a -#define DW_OP_reg27 0x6b -#define DW_OP_reg28 0x6c -#define DW_OP_reg29 0x6d -#define DW_OP_reg30 0x6e -#define DW_OP_reg31 0x6f -#define DW_OP_breg0 0x70 -#define DW_OP_breg1 0x71 -#define DW_OP_breg2 0x72 -#define DW_OP_breg3 0x73 -#define DW_OP_breg4 0x74 -#define DW_OP_breg5 0x75 -#define DW_OP_breg6 0x76 -#define DW_OP_breg7 0x77 -#define DW_OP_breg8 0x78 -#define DW_OP_breg9 0x79 -#define DW_OP_breg10 0x7a -#define DW_OP_breg11 0x7b -#define DW_OP_breg12 0x7c -#define DW_OP_breg13 0x7d -#define DW_OP_breg14 0x7e -#define DW_OP_breg15 0x7f -#define DW_OP_breg16 0x80 -#define DW_OP_breg17 0x81 -#define DW_OP_breg18 0x82 -#define DW_OP_breg19 0x83 -#define DW_OP_breg20 0x84 -#define DW_OP_breg21 0x85 -#define DW_OP_breg22 0x86 -#define DW_OP_breg23 0x87 -#define DW_OP_breg24 0x88 -#define DW_OP_breg25 0x89 -#define DW_OP_breg26 0x8a -#define DW_OP_breg27 0x8b -#define DW_OP_breg28 0x8c -#define DW_OP_breg29 0x8d -#define DW_OP_breg30 0x8e -#define DW_OP_breg31 0x8f -#define DW_OP_regx 0x90 -#define DW_OP_fbreg 0x91 -#define DW_OP_bregx 0x92 -#define DW_OP_piece 0x93 -#define DW_OP_deref_size 0x94 -#define DW_OP_xderef_size 0x95 -#define DW_OP_nop 0x96 -#define DW_OP_push_object_address 0x97 -#define DW_OP_call2 0x98 -#define DW_OP_call4 0x99 -#define DW_OP_call_ref 0x9a -#define DW_OP_form_tls_address 0x9b -#define DW_OP_call_frame_cfa 0x9c -#define DW_OP_bit_piece 0x9d -#define DW_OP_lo_user 0xe0 -#define DW_OP_hi_user 0xff -#define DW_OP_GNU_push_tls_address 0xe0 -#define DW_OP_GNU_uninit 0xf0 -#define DW_OP_GNU_encoded_addr 0xf1 - -#define DW_EH_PE_native 0x00 -#define DW_EH_PE_uleb128 0x01 -#define DW_EH_PE_udata2 0x02 -#define DW_EH_PE_udata4 0x03 -#define DW_EH_PE_udata8 0x04 -#define DW_EH_PE_sleb128 0x09 -#define DW_EH_PE_sdata2 0x0a -#define DW_EH_PE_sdata4 0x0b -#define DW_EH_PE_sdata8 0x0c -#define DW_EH_PE_signed 0x08 -#define DW_EH_PE_abs 0x00 -#define DW_EH_PE_pcrel 0x10 -#define DW_EH_PE_textrel 0x20 -#define DW_EH_PE_datarel 0x30 -#define DW_EH_PE_funcrel 0x40 -#define DW_EH_PE_aligned 0x50 -#define DW_EH_PE_indirect 0x80 -#define DW_EH_PE_omit 0xff - -struct dwarf_eh_bases -{ - void *tbase; - void *dbase; - void *func; -}; - -struct dwarf_cie -{ - unsigned int length; - int id; - unsigned char version; - unsigned char augmentation[1]; -}; - -struct dwarf_fde -{ - unsigned int length; - unsigned int cie_offset; -}; - -extern const struct dwarf_fde *_Unwind_Find_FDE (void *, struct dwarf_eh_bases *); - -static unsigned char dwarf_get_u1( const unsigned char **p ) -{ - return *(*p)++; -} - -static unsigned short dwarf_get_u2( const unsigned char **p ) -{ - unsigned int ret = (*p)[0] | ((*p)[1] << 8); - (*p) += 2; - return ret; -} - -static unsigned int dwarf_get_u4( const unsigned char **p ) -{ - unsigned int ret = (*p)[0] | ((*p)[1] << 8) | ((*p)[2] << 16) | ((*p)[3] << 24); - (*p) += 4; - return ret; -} - -static ULONG64 dwarf_get_u8( const unsigned char **p ) -{ - ULONG64 low = dwarf_get_u4( p ); - ULONG64 high = dwarf_get_u4( p ); - return low | (high << 32); -} - -static ULONG_PTR dwarf_get_uleb128( const unsigned char **p ) -{ - ULONG_PTR ret = 0; - unsigned int shift = 0; - unsigned char byte; - - do - { - byte = **p; - ret |= (ULONG_PTR)(byte & 0x7f) << shift; - shift += 7; - (*p)++; - } while (byte & 0x80); - return ret; -} - -static LONG_PTR dwarf_get_sleb128( const unsigned char **p ) -{ - ULONG_PTR ret = 0; - unsigned int shift = 0; - unsigned char byte; - - do - { - byte = **p; - ret |= (ULONG_PTR)(byte & 0x7f) << shift; - shift += 7; - (*p)++; - } while (byte & 0x80); - - if ((shift < 8 * sizeof(ret)) && (byte & 0x40)) ret |= -((ULONG_PTR)1 << shift); - return ret; -} - -static ULONG_PTR dwarf_get_ptr( const unsigned char **p, unsigned char encoding, const struct dwarf_eh_bases *bases ) -{ - ULONG_PTR base; - - if (encoding == DW_EH_PE_omit) return 0; - - switch (encoding & 0x70) - { - case DW_EH_PE_abs: - base = 0; - break; - case DW_EH_PE_pcrel: - base = (ULONG_PTR)*p; - break; - case DW_EH_PE_textrel: - base = (ULONG_PTR)bases->tbase; - break; - case DW_EH_PE_datarel: - base = (ULONG_PTR)bases->dbase; - break; - case DW_EH_PE_funcrel: - base = (ULONG_PTR)bases->func; - break; - case DW_EH_PE_aligned: - base = ((ULONG_PTR)*p + 7) & ~7ul; - break; - default: - FIXME( "unsupported encoding %02x\n", encoding ); - return 0; - } - - switch (encoding & 0x0f) - { - case DW_EH_PE_native: base += dwarf_get_u8( p ); break; - case DW_EH_PE_uleb128: base += dwarf_get_uleb128( p ); break; - case DW_EH_PE_udata2: base += dwarf_get_u2( p ); break; - case DW_EH_PE_udata4: base += dwarf_get_u4( p ); break; - case DW_EH_PE_udata8: base += dwarf_get_u8( p ); break; - case DW_EH_PE_sleb128: base += dwarf_get_sleb128( p ); break; - case DW_EH_PE_sdata2: base += (signed short)dwarf_get_u2( p ); break; - case DW_EH_PE_sdata4: base += (signed int)dwarf_get_u4( p ); break; - case DW_EH_PE_sdata8: base += (LONG64)dwarf_get_u8( p ); break; - default: - FIXME( "unsupported encoding %02x\n", encoding ); - return 0; - } - if (encoding & DW_EH_PE_indirect) base = *(ULONG_PTR *)base; - return base; -} - -enum reg_rule -{ - RULE_UNSET, /* not set at all */ - RULE_UNDEFINED, /* undefined value */ - RULE_SAME, /* same value as previous frame */ - RULE_CFA_OFFSET, /* stored at cfa offset */ - RULE_OTHER_REG, /* stored in other register */ - RULE_EXPRESSION, /* address specified by expression */ - RULE_VAL_EXPRESSION /* value specified by expression */ -}; - -#define NB_FRAME_REGS 41 -#define MAX_SAVED_STATES 16 - -struct frame_state -{ - ULONG_PTR cfa_offset; - unsigned char cfa_reg; - enum reg_rule cfa_rule; - enum reg_rule rules[NB_FRAME_REGS]; - ULONG64 regs[NB_FRAME_REGS]; -}; - -struct frame_info -{ - ULONG_PTR ip; - ULONG_PTR code_align; - LONG_PTR data_align; - unsigned char retaddr_reg; - unsigned char fde_encoding; - unsigned char signal_frame; - unsigned char state_sp; - struct frame_state state; - struct frame_state *state_stack; -}; - -static const char *dwarf_reg_names[NB_FRAME_REGS] = -{ -/* 0-7 */ "%rax", "%rdx", "%rcx", "%rbx", "%rsi", "%rdi", "%rbp", "%rsp", -/* 8-16 */ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "%rip", -/* 17-24 */ "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", -/* 25-32 */ "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14", "%xmm15", -/* 33-40 */ "%st0", "%st1", "%st2", "%st3", "%st4", "%st5", "%st6", "%st7" -}; - -static BOOL valid_reg( ULONG_PTR reg ) -{ - if (reg >= NB_FRAME_REGS) FIXME( "unsupported reg %lx\n", reg ); - return (reg < NB_FRAME_REGS); -} - -static void execute_cfa_instructions( const unsigned char *ptr, const unsigned char *end, - ULONG_PTR last_ip, struct frame_info *info, - const struct dwarf_eh_bases *bases ) -{ - while (ptr < end && info->ip < last_ip + info->signal_frame) - { - const unsigned char op = *ptr++; - - if (op & 0xc0) - { - switch (op & 0xc0) - { - case DW_CFA_advance_loc: - { - ULONG_PTR offset = (op & 0x3f) * info->code_align; - TRACE( "%lx: DW_CFA_advance_loc %lu\n", info->ip, offset ); - info->ip += offset; - break; - } - case DW_CFA_offset: - { - ULONG_PTR reg = op & 0x3f; - LONG_PTR offset = dwarf_get_uleb128( &ptr ) * info->data_align; - if (!valid_reg( reg )) break; - TRACE( "%lx: DW_CFA_offset %s, %ld\n", info->ip, dwarf_reg_names[reg], offset ); - info->state.regs[reg] = offset; - info->state.rules[reg] = RULE_CFA_OFFSET; - break; - } - case DW_CFA_restore: - { - ULONG_PTR reg = op & 0x3f; - if (!valid_reg( reg )) break; - TRACE( "%lx: DW_CFA_restore %s\n", info->ip, dwarf_reg_names[reg] ); - info->state.rules[reg] = RULE_UNSET; - break; - } - } - } - else switch (op) - { - case DW_CFA_nop: - break; - case DW_CFA_set_loc: - { - ULONG_PTR loc = dwarf_get_ptr( &ptr, info->fde_encoding, bases ); - TRACE( "%lx: DW_CFA_set_loc %lx\n", info->ip, loc ); - info->ip = loc; - break; - } - case DW_CFA_advance_loc1: - { - ULONG_PTR offset = *ptr++ * info->code_align; - TRACE( "%lx: DW_CFA_advance_loc1 %lu\n", info->ip, offset ); - info->ip += offset; - break; - } - case DW_CFA_advance_loc2: - { - ULONG_PTR offset = dwarf_get_u2( &ptr ) * info->code_align; - TRACE( "%lx: DW_CFA_advance_loc2 %lu\n", info->ip, offset ); - info->ip += offset; - break; - } - case DW_CFA_advance_loc4: - { - ULONG_PTR offset = dwarf_get_u4( &ptr ) * info->code_align; - TRACE( "%lx: DW_CFA_advance_loc4 %lu\n", info->ip, offset ); - info->ip += offset; - break; - } - case DW_CFA_offset_extended: - case DW_CFA_offset_extended_sf: - { - ULONG_PTR reg = dwarf_get_uleb128( &ptr ); - LONG_PTR offset = (op == DW_CFA_offset_extended) ? dwarf_get_uleb128( &ptr ) * info->data_align - : dwarf_get_sleb128( &ptr ) * info->data_align; - if (!valid_reg( reg )) break; - TRACE( "%lx: DW_CFA_offset_extended %s, %ld\n", info->ip, dwarf_reg_names[reg], offset ); - info->state.regs[reg] = offset; - info->state.rules[reg] = RULE_CFA_OFFSET; - break; - } - case DW_CFA_restore_extended: - { - ULONG_PTR reg = dwarf_get_uleb128( &ptr ); - if (!valid_reg( reg )) break; - TRACE( "%lx: DW_CFA_restore_extended %s\n", info->ip, dwarf_reg_names[reg] ); - info->state.rules[reg] = RULE_UNSET; - break; - } - case DW_CFA_undefined: - { - ULONG_PTR reg = dwarf_get_uleb128( &ptr ); - if (!valid_reg( reg )) break; - TRACE( "%lx: DW_CFA_undefined %s\n", info->ip, dwarf_reg_names[reg] ); - info->state.rules[reg] = RULE_UNDEFINED; - break; - } - case DW_CFA_same_value: - { - ULONG_PTR reg = dwarf_get_uleb128( &ptr ); - if (!valid_reg( reg )) break; - TRACE( "%lx: DW_CFA_same_value %s\n", info->ip, dwarf_reg_names[reg] ); - info->state.regs[reg] = reg; - info->state.rules[reg] = RULE_SAME; - break; - } - case DW_CFA_register: - { - ULONG_PTR reg = dwarf_get_uleb128( &ptr ); - ULONG_PTR reg2 = dwarf_get_uleb128( &ptr ); - if (!valid_reg( reg ) || !valid_reg( reg2 )) break; - TRACE( "%lx: DW_CFA_register %s == %s\n", info->ip, dwarf_reg_names[reg], dwarf_reg_names[reg2] ); - info->state.regs[reg] = reg2; - info->state.rules[reg] = RULE_OTHER_REG; - break; - } - case DW_CFA_remember_state: - TRACE( "%lx: DW_CFA_remember_state\n", info->ip ); - if (info->state_sp >= MAX_SAVED_STATES) - FIXME( "%lx: DW_CFA_remember_state too many nested saves\n", info->ip ); - else - info->state_stack[info->state_sp++] = info->state; - break; - case DW_CFA_restore_state: - TRACE( "%lx: DW_CFA_restore_state\n", info->ip ); - if (!info->state_sp) - FIXME( "%lx: DW_CFA_restore_state without corresponding save\n", info->ip ); - else - info->state = info->state_stack[--info->state_sp]; - break; - case DW_CFA_def_cfa: - case DW_CFA_def_cfa_sf: - { - ULONG_PTR reg = dwarf_get_uleb128( &ptr ); - ULONG_PTR offset = (op == DW_CFA_def_cfa) ? dwarf_get_uleb128( &ptr ) - : dwarf_get_sleb128( &ptr ) * info->data_align; - if (!valid_reg( reg )) break; - TRACE( "%lx: DW_CFA_def_cfa %s, %lu\n", info->ip, dwarf_reg_names[reg], offset ); - info->state.cfa_reg = reg; - info->state.cfa_offset = offset; - info->state.cfa_rule = RULE_CFA_OFFSET; - break; - } - case DW_CFA_def_cfa_register: - { - ULONG_PTR reg = dwarf_get_uleb128( &ptr ); - if (!valid_reg( reg )) break; - TRACE( "%lx: DW_CFA_def_cfa_register %s\n", info->ip, dwarf_reg_names[reg] ); - info->state.cfa_reg = reg; - info->state.cfa_rule = RULE_CFA_OFFSET; - break; - } - case DW_CFA_def_cfa_offset: - case DW_CFA_def_cfa_offset_sf: - { - ULONG_PTR offset = (op == DW_CFA_def_cfa_offset) ? dwarf_get_uleb128( &ptr ) - : dwarf_get_sleb128( &ptr ) * info->data_align; - TRACE( "%lx: DW_CFA_def_cfa_offset %lu\n", info->ip, offset ); - info->state.cfa_offset = offset; - info->state.cfa_rule = RULE_CFA_OFFSET; - break; - } - case DW_CFA_def_cfa_expression: - { - ULONG_PTR expr = (ULONG_PTR)ptr; - ULONG_PTR len = dwarf_get_uleb128( &ptr ); - TRACE( "%lx: DW_CFA_def_cfa_expression %lx-%lx\n", info->ip, expr, expr+len ); - info->state.cfa_offset = expr; - info->state.cfa_rule = RULE_VAL_EXPRESSION; - ptr += len; - break; - } - case DW_CFA_expression: - case DW_CFA_val_expression: - { - ULONG_PTR reg = dwarf_get_uleb128( &ptr ); - ULONG_PTR expr = (ULONG_PTR)ptr; - ULONG_PTR len = dwarf_get_uleb128( &ptr ); - if (!valid_reg( reg )) break; - TRACE( "%lx: DW_CFA_%sexpression %s %lx-%lx\n", - info->ip, (op == DW_CFA_expression) ? "" : "val_", dwarf_reg_names[reg], expr, expr+len ); - info->state.regs[reg] = expr; - info->state.rules[reg] = (op == DW_CFA_expression) ? RULE_EXPRESSION : RULE_VAL_EXPRESSION; - ptr += len; - break; - } - default: - FIXME( "%lx: unknown CFA opcode %02x\n", info->ip, op ); - break; - } - } -} - -/* retrieve a context register from its dwarf number */ -static void *get_context_reg( CONTEXT *context, ULONG_PTR dw_reg ) -{ - switch (dw_reg) - { - case 0: return &context->Rax; - case 1: return &context->Rdx; - case 2: return &context->Rcx; - case 3: return &context->Rbx; - case 4: return &context->Rsi; - case 5: return &context->Rdi; - case 6: return &context->Rbp; - case 7: return &context->Rsp; - case 8: return &context->R8; - case 9: return &context->R9; - case 10: return &context->R10; - case 11: return &context->R11; - case 12: return &context->R12; - case 13: return &context->R13; - case 14: return &context->R14; - case 15: return &context->R15; - case 16: return &context->Rip; - case 17: return &context->u.s.Xmm0; - case 18: return &context->u.s.Xmm1; - case 19: return &context->u.s.Xmm2; - case 20: return &context->u.s.Xmm3; - case 21: return &context->u.s.Xmm4; - case 22: return &context->u.s.Xmm5; - case 23: return &context->u.s.Xmm6; - case 24: return &context->u.s.Xmm7; - case 25: return &context->u.s.Xmm8; - case 26: return &context->u.s.Xmm9; - case 27: return &context->u.s.Xmm10; - case 28: return &context->u.s.Xmm11; - case 29: return &context->u.s.Xmm12; - case 30: return &context->u.s.Xmm13; - case 31: return &context->u.s.Xmm14; - case 32: return &context->u.s.Xmm15; - case 33: return &context->u.s.Legacy[0]; - case 34: return &context->u.s.Legacy[1]; - case 35: return &context->u.s.Legacy[2]; - case 36: return &context->u.s.Legacy[3]; - case 37: return &context->u.s.Legacy[4]; - case 38: return &context->u.s.Legacy[5]; - case 39: return &context->u.s.Legacy[6]; - case 40: return &context->u.s.Legacy[7]; - default: return NULL; - } -} - -/* set a context register from its dwarf number */ -static void set_context_reg( CONTEXT *context, ULONG_PTR dw_reg, void *val ) -{ - switch (dw_reg) - { - case 0: context->Rax = *(ULONG64 *)val; break; - case 1: context->Rdx = *(ULONG64 *)val; break; - case 2: context->Rcx = *(ULONG64 *)val; break; - case 3: context->Rbx = *(ULONG64 *)val; break; - case 4: context->Rsi = *(ULONG64 *)val; break; - case 5: context->Rdi = *(ULONG64 *)val; break; - case 6: context->Rbp = *(ULONG64 *)val; break; - case 7: context->Rsp = *(ULONG64 *)val; break; - case 8: context->R8 = *(ULONG64 *)val; break; - case 9: context->R9 = *(ULONG64 *)val; break; - case 10: context->R10 = *(ULONG64 *)val; break; - case 11: context->R11 = *(ULONG64 *)val; break; - case 12: context->R12 = *(ULONG64 *)val; break; - case 13: context->R13 = *(ULONG64 *)val; break; - case 14: context->R14 = *(ULONG64 *)val; break; - case 15: context->R15 = *(ULONG64 *)val; break; - case 16: context->Rip = *(ULONG64 *)val; break; - case 17: memcpy( &context->u.s.Xmm0, val, sizeof(M128A) ); break; - case 18: memcpy( &context->u.s.Xmm1, val, sizeof(M128A) ); break; - case 19: memcpy( &context->u.s.Xmm2, val, sizeof(M128A) ); break; - case 20: memcpy( &context->u.s.Xmm3, val, sizeof(M128A) ); break; - case 21: memcpy( &context->u.s.Xmm4, val, sizeof(M128A) ); break; - case 22: memcpy( &context->u.s.Xmm5, val, sizeof(M128A) ); break; - case 23: memcpy( &context->u.s.Xmm6, val, sizeof(M128A) ); break; - case 24: memcpy( &context->u.s.Xmm7, val, sizeof(M128A) ); break; - case 25: memcpy( &context->u.s.Xmm8, val, sizeof(M128A) ); break; - case 26: memcpy( &context->u.s.Xmm9, val, sizeof(M128A) ); break; - case 27: memcpy( &context->u.s.Xmm10, val, sizeof(M128A) ); break; - case 28: memcpy( &context->u.s.Xmm11, val, sizeof(M128A) ); break; - case 29: memcpy( &context->u.s.Xmm12, val, sizeof(M128A) ); break; - case 30: memcpy( &context->u.s.Xmm13, val, sizeof(M128A) ); break; - case 31: memcpy( &context->u.s.Xmm14, val, sizeof(M128A) ); break; - case 32: memcpy( &context->u.s.Xmm15, val, sizeof(M128A) ); break; - case 33: memcpy( &context->u.s.Legacy[0], val, sizeof(M128A) ); break; - case 34: memcpy( &context->u.s.Legacy[1], val, sizeof(M128A) ); break; - case 35: memcpy( &context->u.s.Legacy[2], val, sizeof(M128A) ); break; - case 36: memcpy( &context->u.s.Legacy[3], val, sizeof(M128A) ); break; - case 37: memcpy( &context->u.s.Legacy[4], val, sizeof(M128A) ); break; - case 38: memcpy( &context->u.s.Legacy[5], val, sizeof(M128A) ); break; - case 39: memcpy( &context->u.s.Legacy[6], val, sizeof(M128A) ); break; - case 40: memcpy( &context->u.s.Legacy[7], val, sizeof(M128A) ); break; - } -} - -static ULONG_PTR eval_expression( const unsigned char *p, CONTEXT *context, - const struct dwarf_eh_bases *bases ) -{ - ULONG_PTR reg, tmp, stack[64]; - int sp = -1; - ULONG_PTR len = dwarf_get_uleb128(&p); - const unsigned char *end = p + len; - - while (p < end) - { - unsigned char opcode = dwarf_get_u1(&p); - - if (opcode >= DW_OP_lit0 && opcode <= DW_OP_lit31) - stack[++sp] = opcode - DW_OP_lit0; - else if (opcode >= DW_OP_reg0 && opcode <= DW_OP_reg31) - stack[++sp] = *(ULONG_PTR *)get_context_reg( context, opcode - DW_OP_reg0 ); - else if (opcode >= DW_OP_breg0 && opcode <= DW_OP_breg31) - stack[++sp] = *(ULONG_PTR *)get_context_reg( context, opcode - DW_OP_breg0 ) + dwarf_get_sleb128(&p); - else switch (opcode) - { - case DW_OP_nop: break; - case DW_OP_addr: stack[++sp] = dwarf_get_u8(&p); break; - case DW_OP_const1u: stack[++sp] = dwarf_get_u1(&p); break; - case DW_OP_const1s: stack[++sp] = (signed char)dwarf_get_u1(&p); break; - case DW_OP_const2u: stack[++sp] = dwarf_get_u2(&p); break; - case DW_OP_const2s: stack[++sp] = (short)dwarf_get_u2(&p); break; - case DW_OP_const4u: stack[++sp] = dwarf_get_u4(&p); break; - case DW_OP_const4s: stack[++sp] = (signed int)dwarf_get_u4(&p); break; - case DW_OP_const8u: stack[++sp] = dwarf_get_u8(&p); break; - case DW_OP_const8s: stack[++sp] = (LONG_PTR)dwarf_get_u8(&p); break; - case DW_OP_constu: stack[++sp] = dwarf_get_uleb128(&p); break; - case DW_OP_consts: stack[++sp] = dwarf_get_sleb128(&p); break; - case DW_OP_deref: stack[sp] = *(ULONG_PTR *)stack[sp]; break; - case DW_OP_dup: stack[sp + 1] = stack[sp]; sp++; break; - case DW_OP_drop: sp--; break; - case DW_OP_over: stack[sp + 1] = stack[sp - 1]; sp++; break; - case DW_OP_pick: stack[sp + 1] = stack[sp - dwarf_get_u1(&p)]; sp++; break; - case DW_OP_swap: tmp = stack[sp]; stack[sp] = stack[sp-1]; stack[sp-1] = tmp; break; - case DW_OP_rot: tmp = stack[sp]; stack[sp] = stack[sp-1]; stack[sp-1] = stack[sp-2]; stack[sp-2] = tmp; break; - case DW_OP_abs: stack[sp] = labs(stack[sp]); break; - case DW_OP_neg: stack[sp] = -stack[sp]; break; - case DW_OP_not: stack[sp] = ~stack[sp]; break; - case DW_OP_and: stack[sp-1] &= stack[sp]; sp--; break; - case DW_OP_or: stack[sp-1] |= stack[sp]; sp--; break; - case DW_OP_minus: stack[sp-1] -= stack[sp]; sp--; break; - case DW_OP_mul: stack[sp-1] *= stack[sp]; sp--; break; - case DW_OP_plus: stack[sp-1] += stack[sp]; sp--; break; - case DW_OP_xor: stack[sp-1] ^= stack[sp]; sp--; break; - case DW_OP_shl: stack[sp-1] <<= stack[sp]; sp--; break; - case DW_OP_shr: stack[sp-1] >>= stack[sp]; sp--; break; - case DW_OP_plus_uconst: stack[sp] += dwarf_get_uleb128(&p); break; - case DW_OP_shra: stack[sp-1] = (LONG_PTR)stack[sp-1] / (1 << stack[sp]); sp--; break; - case DW_OP_div: stack[sp-1] = (LONG_PTR)stack[sp-1] / (LONG_PTR)stack[sp]; sp--; break; - case DW_OP_mod: stack[sp-1] = (LONG_PTR)stack[sp-1] % (LONG_PTR)stack[sp]; sp--; break; - case DW_OP_ge: stack[sp-1] = ((LONG_PTR)stack[sp-1] >= (LONG_PTR)stack[sp]); sp--; break; - case DW_OP_gt: stack[sp-1] = ((LONG_PTR)stack[sp-1] > (LONG_PTR)stack[sp]); sp--; break; - case DW_OP_le: stack[sp-1] = ((LONG_PTR)stack[sp-1] <= (LONG_PTR)stack[sp]); sp--; break; - case DW_OP_lt: stack[sp-1] = ((LONG_PTR)stack[sp-1] < (LONG_PTR)stack[sp]); sp--; break; - case DW_OP_eq: stack[sp-1] = (stack[sp-1] == stack[sp]); sp--; break; - case DW_OP_ne: stack[sp-1] = (stack[sp-1] != stack[sp]); sp--; break; - case DW_OP_skip: tmp = (short)dwarf_get_u2(&p); p += tmp; break; - case DW_OP_bra: tmp = (short)dwarf_get_u2(&p); if (!stack[sp--]) p += tmp; break; - case DW_OP_GNU_encoded_addr: tmp = *p++; stack[++sp] = dwarf_get_ptr( &p, tmp, bases ); break; - case DW_OP_regx: stack[++sp] = *(ULONG_PTR *)get_context_reg( context, dwarf_get_uleb128(&p) ); break; - case DW_OP_bregx: - reg = dwarf_get_uleb128(&p); - tmp = dwarf_get_sleb128(&p); - stack[++sp] = *(ULONG_PTR *)get_context_reg( context, reg ) + tmp; - break; - case DW_OP_deref_size: - switch (*p++) - { - case 1: stack[sp] = *(unsigned char *)stack[sp]; break; - case 2: stack[sp] = *(unsigned short *)stack[sp]; break; - case 4: stack[sp] = *(unsigned int *)stack[sp]; break; - case 8: stack[sp] = *(ULONG_PTR *)stack[sp]; break; - } - break; - default: - FIXME( "unhandled opcode %02x\n", opcode ); - } - } - return stack[sp]; -} - -/* apply the computed frame info to the actual context */ -static void apply_frame_state( CONTEXT *context, struct frame_state *state, - const struct dwarf_eh_bases *bases ) -{ - unsigned int i; - ULONG_PTR cfa, value; - CONTEXT new_context = *context; - - switch (state->cfa_rule) - { - case RULE_EXPRESSION: - cfa = *(ULONG_PTR *)eval_expression( (const unsigned char *)state->cfa_offset, context, bases ); - break; - case RULE_VAL_EXPRESSION: - cfa = eval_expression( (const unsigned char *)state->cfa_offset, context, bases ); - break; - default: - cfa = *(ULONG_PTR *)get_context_reg( context, state->cfa_reg ) + state->cfa_offset; - break; - } - if (!cfa) return; - - for (i = 0; i < NB_FRAME_REGS; i++) - { - switch (state->rules[i]) - { - case RULE_UNSET: - case RULE_UNDEFINED: - case RULE_SAME: - break; - case RULE_CFA_OFFSET: - set_context_reg( &new_context, i, (char *)cfa + state->regs[i] ); - break; - case RULE_OTHER_REG: - set_context_reg( &new_context, i, get_context_reg( context, state->regs[i] )); - break; - case RULE_EXPRESSION: - value = eval_expression( (const unsigned char *)state->regs[i], context, bases ); - set_context_reg( &new_context, i, (void *)value ); - break; - case RULE_VAL_EXPRESSION: - value = eval_expression( (const unsigned char *)state->regs[i], context, bases ); - set_context_reg( &new_context, i, &value ); - break; - } - } - new_context.Rsp = cfa; - *context = new_context; -} -
/*********************************************************************** * dwarf_virtual_unwind