Currently, unwinding the unix libraries (or full ELF builds) requires having libunwind available.
When starting up a wine environment, an exception of type `RPC_S_SERVER_UNAVAILABLE` gets thrown - and if libunwind isn't available, this breaks the startup. Thus, currently on ARM/ARM64, libunwind is essentially mandatory.
Additionally, at least on ARM, libunwind seems brittle (commits in latest git master breaks the unwinding use cases in Wine, see e.g. https://github.com/libunwind/libunwind/pull/203#issuecomment-984126066.
This MR tries to resolve all of this, by including the preexisting DWARF parser from x86_64 in the aarch64 version too. This bit was mostly quite straightforward.
For ARM, libunwind has currently relied on DWARF debug info in the `.debug_frame` section, since Linux on ARM doesn't use DWARF for runtime unwinding. This MR adds ARM EHABI unwind opcodes where necessary, and adds a local reimplementation of an ARM EHABI/EXIDX/EXTBL unwinder, similar to the DWARF one.
With these changes, unwinding on both ARM and ARM64 seems to work fine, even without libunwind.
See the individual commit messages for more commentary on the decisions taken so far (I've tried to write up all relevant considerations there - it's a fair amount of commentery).
A couple open questions: - So far, the shared DWARF code was put in a header, `dwarf.h`. Do you want to make this a proper standalone `.c` file too? - I wrote the ARM EHABI unwind code mostly with `stdint.h` types like `uint32_t`(as it's in the ntdll/unix directory anyway), but I can rewrite it with `DWORD` or similar if that's preferred. - The ARM EHABI opcodes are enabled for `defined(__arm__) && defined(__ELF__) && defined(__GNUC__) && !defined(__SEH__) && !defined(__ARM_DWARF_EH__)` - there's no define to check for for knowing it's used and enabled, but it has to be implicitly assumed if no other other unwind mechanism is signaled. - The `dl_iterate_phdr` function, used for finding the EXIDX section for locating ARM EHABI unwind info, is used within `#ifdef linux`. It might be available on other OSes too (like FreeBSD etc). Or should I go ahead and add a configure check for it?
CC @julliard @rbernon @AndreRH
-- v2: ntdll: Use the local dwarf implementation on arm64 ntdll: Add support for aarch64 in the dwarf implementation ntdll: Move the dwarf reading routines to a shareable header
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
From: Martin Storsjö martin@martin.st
This is essentially about mapping dwarf register numbers to aarch64 registers, and for updating the right CONTEXT variable with the CFA.
Signed-off-by: Martin Storsjö martin@martin.st --- dlls/ntdll/unix/dwarf.h | 164 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+)
diff --git a/dlls/ntdll/unix/dwarf.h b/dlls/ntdll/unix/dwarf.h index 33cc8315bd6..cae7d6288ee 100644 --- a/dlls/ntdll/unix/dwarf.h +++ b/dlls/ntdll/unix/dwarf.h @@ -377,7 +377,13 @@ enum reg_rule RULE_VAL_EXPRESSION /* value specified by expression */ };
+#ifdef __x86_64__ #define NB_FRAME_REGS 41 +#elif defined(__aarch64__) +#define NB_FRAME_REGS 96 +#else +#error Unsupported architecture +#endif #define MAX_SAVED_STATES 16
struct frame_state @@ -404,11 +410,26 @@ struct frame_info
static const char *dwarf_reg_names[NB_FRAME_REGS] = { +#ifdef __x86_64__ /* 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" +#elif defined(__aarch64__) +/* 0-7 */ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", +/* 8-15 */ "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", +/* 16-23 */ "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", +/* 24-31 */ "x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp", +/* 32-39 */ "pc", NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/* 40-47 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/* 48-55 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/* 56-63 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +/* 64-71 */ "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", +/* 72-79 */ "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", +/* 80-87 */ "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", +/* 88-95 */ "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", +#endif };
static BOOL valid_reg( ULONG_PTR reg ) @@ -617,6 +638,7 @@ static void *get_context_reg( CONTEXT *context, ULONG_PTR dw_reg ) { switch (dw_reg) { +#ifdef __x86_64__ case 0: return &context->Rax; case 1: return &context->Rdx; case 2: return &context->Rcx; @@ -658,6 +680,74 @@ static void *get_context_reg( CONTEXT *context, ULONG_PTR dw_reg ) case 38: return &context->u.s.Legacy[5]; case 39: return &context->u.s.Legacy[6]; case 40: return &context->u.s.Legacy[7]; +#elif defined(__aarch64__) + case 0: return &context->u.s.X0; + case 1: return &context->u.s.X1; + case 2: return &context->u.s.X2; + case 3: return &context->u.s.X3; + case 4: return &context->u.s.X4; + case 5: return &context->u.s.X5; + case 6: return &context->u.s.X6; + case 7: return &context->u.s.X7; + case 8: return &context->u.s.X8; + case 9: return &context->u.s.X9; + case 10: return &context->u.s.X10; + case 11: return &context->u.s.X11; + case 12: return &context->u.s.X12; + case 13: return &context->u.s.X13; + case 14: return &context->u.s.X14; + case 15: return &context->u.s.X15; + case 16: return &context->u.s.X16; + case 17: return &context->u.s.X17; + case 18: return &context->u.s.X18; + case 19: return &context->u.s.X19; + case 20: return &context->u.s.X20; + case 21: return &context->u.s.X21; + case 22: return &context->u.s.X22; + case 23: return &context->u.s.X23; + case 24: return &context->u.s.X24; + case 25: return &context->u.s.X25; + case 26: return &context->u.s.X26; + case 27: return &context->u.s.X27; + case 28: return &context->u.s.X28; + case 29: return &context->u.s.Fp; + case 30: return &context->u.s.Lr; + case 31: return &context->Sp; + case 32: return &context->Pc; + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + return &context->V[dw_reg - 64]; +#endif default: return NULL; } } @@ -667,6 +757,7 @@ static void set_context_reg( CONTEXT *context, ULONG_PTR dw_reg, void *val ) { switch (dw_reg) { +#ifdef __x86_64__ case 0: context->Rax = *(ULONG64 *)val; break; case 1: context->Rdx = *(ULONG64 *)val; break; case 2: context->Rcx = *(ULONG64 *)val; break; @@ -708,6 +799,75 @@ static void set_context_reg( CONTEXT *context, ULONG_PTR dw_reg, void *val ) 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; +#elif defined(__aarch64__) + case 0: context->u.s.X0 = *(DWORD64 *)val; break; + case 1: context->u.s.X1 = *(DWORD64 *)val; break; + case 2: context->u.s.X2 = *(DWORD64 *)val; break; + case 3: context->u.s.X3 = *(DWORD64 *)val; break; + case 4: context->u.s.X4 = *(DWORD64 *)val; break; + case 5: context->u.s.X5 = *(DWORD64 *)val; break; + case 6: context->u.s.X6 = *(DWORD64 *)val; break; + case 7: context->u.s.X7 = *(DWORD64 *)val; break; + case 8: context->u.s.X8 = *(DWORD64 *)val; break; + case 9: context->u.s.X9 = *(DWORD64 *)val; break; + case 10: context->u.s.X10 = *(DWORD64 *)val; break; + case 11: context->u.s.X11 = *(DWORD64 *)val; break; + case 12: context->u.s.X12 = *(DWORD64 *)val; break; + case 13: context->u.s.X13 = *(DWORD64 *)val; break; + case 14: context->u.s.X14 = *(DWORD64 *)val; break; + case 15: context->u.s.X15 = *(DWORD64 *)val; break; + case 16: context->u.s.X16 = *(DWORD64 *)val; break; + case 17: context->u.s.X17 = *(DWORD64 *)val; break; + case 18: context->u.s.X18 = *(DWORD64 *)val; break; + case 19: context->u.s.X19 = *(DWORD64 *)val; break; + case 20: context->u.s.X20 = *(DWORD64 *)val; break; + case 21: context->u.s.X21 = *(DWORD64 *)val; break; + case 22: context->u.s.X22 = *(DWORD64 *)val; break; + case 23: context->u.s.X23 = *(DWORD64 *)val; break; + case 24: context->u.s.X24 = *(DWORD64 *)val; break; + case 25: context->u.s.X25 = *(DWORD64 *)val; break; + case 26: context->u.s.X26 = *(DWORD64 *)val; break; + case 27: context->u.s.X27 = *(DWORD64 *)val; break; + case 28: context->u.s.X28 = *(DWORD64 *)val; break; + case 29: context->u.s.Fp = *(DWORD64 *)val; break; + case 30: context->u.s.Lr = *(DWORD64 *)val; break; + case 31: context->Sp = *(DWORD64 *)val; break; + case 32: context->Pc = *(DWORD64 *)val; break; + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + memcpy( &context->V[dw_reg - 64], val, sizeof(ARM64_NT_NEON128) ); + break; +#endif } }
@@ -842,7 +1002,11 @@ static void apply_frame_state( CONTEXT *context, struct frame_state *state, break; } } +#ifdef __x86_64__ new_context.Rsp = cfa; +#elif defined(__aarch64__) + new_context.Sp = cfa; +#endif *context = new_context; }
From: Martin Storsjö martin@martin.st
This makes unwinding work if libunwind is unavailable.
The dwarf_virtual_unwind function is modelled heavily on the x86_64 version of it. (Going forward, if there are changes to either of them, one should probably look at whether those changes should be mirrored to the other one too.)
Signed-off-by: Martin Storsjö martin@martin.st --- dlls/ntdll/unix/signal_arm64.c | 171 +++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 9 deletions(-)
diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 4e552e0f10a..19d08e2270a 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -68,6 +68,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(seh);
+#include "dwarf.h" + /*********************************************************************** * signal context platform-specific definitions */ @@ -172,14 +174,146 @@ static BOOL is_inside_syscall( ucontext_t *sigcontext ) extern void raise_func_trampoline( EXCEPTION_RECORD *rec, CONTEXT *context, void *dispatcher );
/*********************************************************************** - * unwind_builtin_dll + * dwarf_virtual_unwind * * Equivalent of RtlVirtualUnwind for builtin modules. */ -NTSTATUS CDECL unwind_builtin_dll( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEXT *context ) +static NTSTATUS dwarf_virtual_unwind( ULONG64 ip, ULONG64 *frame, CONTEXT *context, + const struct dwarf_fde *fde, const struct dwarf_eh_bases *bases, + PEXCEPTION_ROUTINE *handler, void **handler_data ) { + const struct dwarf_cie *cie; + const unsigned char *ptr, *augmentation, *end; + ULONG_PTR len, code_end; + struct frame_info info; + struct frame_state state_stack[MAX_SAVED_STATES]; + int aug_z_format = 0; + unsigned char lsda_encoding = DW_EH_PE_omit; + + memset( &info, 0, sizeof(info) ); + info.state_stack = state_stack; + info.ip = (ULONG_PTR)bases->func; + *handler = NULL; + + cie = (const struct dwarf_cie *)((const char *)&fde->cie_offset - fde->cie_offset); + + /* parse the CIE first */ + + if (cie->version != 1 && cie->version != 3) + { + FIXME( "unknown CIE version %u at %p\n", cie->version, cie ); + return STATUS_INVALID_DISPOSITION; + } + ptr = cie->augmentation + strlen((const char *)cie->augmentation) + 1; + + info.code_align = dwarf_get_uleb128( &ptr ); + info.data_align = dwarf_get_sleb128( &ptr ); + if (cie->version == 1) + info.retaddr_reg = *ptr++; + else + info.retaddr_reg = dwarf_get_uleb128( &ptr ); + info.state.cfa_rule = RULE_CFA_OFFSET; + + TRACE( "function %lx base %p cie %p len %x id %x version %x aug '%s' code_align %lu data_align %ld retaddr %s\n", + ip, bases->func, cie, cie->length, cie->id, cie->version, cie->augmentation, + info.code_align, info.data_align, dwarf_reg_names[info.retaddr_reg] ); + + end = NULL; + for (augmentation = cie->augmentation; *augmentation; augmentation++) + { + switch (*augmentation) + { + case 'z': + len = dwarf_get_uleb128( &ptr ); + end = ptr + len; + aug_z_format = 1; + continue; + case 'L': + lsda_encoding = *ptr++; + continue; + case 'P': + { + unsigned char encoding = *ptr++; + *handler = (void *)dwarf_get_ptr( &ptr, encoding, bases ); + continue; + } + case 'R': + info.fde_encoding = *ptr++; + continue; + case 'S': + info.signal_frame = 1; + continue; + } + FIXME( "unknown augmentation '%c'\n", *augmentation ); + if (!end) return STATUS_INVALID_DISPOSITION; /* cannot continue */ + break; + } + if (end) ptr = end; + + end = (const unsigned char *)(&cie->length + 1) + cie->length; + execute_cfa_instructions( ptr, end, ip, &info, bases ); + + ptr = (const unsigned char *)(fde + 1); + info.ip = dwarf_get_ptr( &ptr, info.fde_encoding, bases ); /* fde code start */ + code_end = info.ip + dwarf_get_ptr( &ptr, info.fde_encoding & 0x0f, bases ); /* fde code length */ + + if (aug_z_format) /* get length of augmentation data */ + { + len = dwarf_get_uleb128( &ptr ); + end = ptr + len; + } + else end = NULL; + + *handler_data = (void *)dwarf_get_ptr( &ptr, lsda_encoding, bases ); + if (end) ptr = end; + + end = (const unsigned char *)(&fde->length + 1) + fde->length; + TRACE( "fde %p len %x personality %p lsda %p code %lx-%lx\n", + fde, fde->length, *handler, *handler_data, info.ip, code_end ); + execute_cfa_instructions( ptr, end, ip, &info, bases ); + *frame = context->Sp; + apply_frame_state( context, &info.state, bases ); + context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; + /* Set Pc based on Lr; libunwind also does this as part of unw_step. */ + context->Pc = context->u.s.Lr; + + if (bases->func == (void *)raise_func_trampoline) { + /* raise_func_trampoline has a full CONTEXT stored on the stack; + * restore the original Lr value from there. The function we unwind + * to might be a leaf function that hasn't backed up its own original + * Lr value on the stack. + * We could also just restore the full context here without doing + * unw_step at all. */ + const CONTEXT *next_ctx = (const CONTEXT *) *frame; + context->u.s.Lr = next_ctx->u.s.Lr; + } + + TRACE( "next function pc=%016lx\n", context->Pc ); + TRACE(" x0=%016lx x1=%016lx x2=%016lx x3=%016lx\n", + context->u.s.X0, context->u.s.X1, context->u.s.X2, context->u.s.X3 ); + TRACE(" x4=%016lx x5=%016lx x6=%016lx x7=%016lx\n", + context->u.s.X4, context->u.s.X5, context->u.s.X6, context->u.s.X7 ); + TRACE(" x8=%016lx x9=%016lx x10=%016lx x11=%016lx\n", + context->u.s.X8, context->u.s.X9, context->u.s.X10, context->u.s.X11 ); + TRACE(" x12=%016lx x13=%016lx x14=%016lx x15=%016lx\n", + context->u.s.X12, context->u.s.X13, context->u.s.X14, context->u.s.X15 ); + TRACE(" x16=%016lx x17=%016lx x18=%016lx x19=%016lx\n", + context->u.s.X16, context->u.s.X17, context->u.s.X18, context->u.s.X19 ); + TRACE(" x20=%016lx x21=%016lx x22=%016lx x23=%016lx\n", + context->u.s.X20, context->u.s.X21, context->u.s.X22, context->u.s.X23 ); + TRACE(" x24=%016lx x25=%016lx x26=%016lx x27=%016lx\n", + context->u.s.X24, context->u.s.X25, context->u.s.X26, context->u.s.X27 ); + TRACE(" x28=%016lx fp=%016lx lr=%016lx sp=%016lx\n", + context->u.s.X28, context->u.s.Fp, context->u.s.Lr, context->Sp ); + + return STATUS_SUCCESS; +} + + #ifdef HAVE_LIBUNWIND - ULONG_PTR ip = context->Pc; +static NTSTATUS libunwind_virtual_unwind( ULONG_PTR ip, ULONG_PTR *frame, CONTEXT *context, + PEXCEPTION_ROUTINE *handler, void **handler_data ) +{ unw_context_t unw_context; unw_cursor_t cursor; unw_proc_info_t info; @@ -222,8 +356,8 @@ NTSTATUS CDECL unwind_builtin_dll( ULONG type, DISPATCHER_CONTEXT *dispatch, CON { TRACE( "no info found for %lx ip %lx-%lx, assuming leaf function\n", ip, info.start_ip, info.end_ip ); - dispatch->LanguageHandler = NULL; - dispatch->EstablisherFrame = context->Sp; + *handler = NULL; + *frame = context->Sp; context->Pc = context->u.s.Lr; context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; return STATUS_SUCCESS; @@ -240,9 +374,9 @@ NTSTATUS CDECL unwind_builtin_dll( ULONG type, DISPATCHER_CONTEXT *dispatch, CON return STATUS_INVALID_DISPOSITION; }
- dispatch->LanguageHandler = (void *)info.handler; - dispatch->HandlerData = (void *)info.lsda; - dispatch->EstablisherFrame = context->Sp; + *handler = (void *)info.handler; + *handler_data = (void *)info.lsda; + *frame = context->Sp; #ifdef __APPLE__ { int i; @@ -296,7 +430,7 @@ NTSTATUS CDECL unwind_builtin_dll( ULONG type, DISPATCHER_CONTEXT *dispatch, CON * Lr value on the stack. * We could also just restore the full context here without doing * unw_step at all. */ - const CONTEXT *next_ctx = (const CONTEXT *) dispatch->EstablisherFrame; + const CONTEXT *next_ctx = (const CONTEXT *) *frame; context->u.s.Lr = next_ctx->u.s.Lr; }
@@ -318,6 +452,25 @@ NTSTATUS CDECL unwind_builtin_dll( ULONG type, DISPATCHER_CONTEXT *dispatch, CON TRACE(" x28=%016lx fp=%016lx lr=%016lx sp=%016lx\n", context->u.s.X28, context->u.s.Fp, context->u.s.Lr, context->Sp ); return STATUS_SUCCESS; +} +#endif + +/*********************************************************************** + * unwind_builtin_dll + * + * Equivalent of RtlVirtualUnwind for builtin modules. + */ +NTSTATUS CDECL unwind_builtin_dll( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEXT *context ) +{ + struct dwarf_eh_bases bases; + const struct dwarf_fde *fde = _Unwind_Find_FDE( (void *)(context->Pc - 1), &bases ); + + if (fde) + return dwarf_virtual_unwind( context->Pc, &dispatch->EstablisherFrame, context, fde, + &bases, &dispatch->LanguageHandler, &dispatch->HandlerData ); +#ifdef HAVE_LIBUNWIND + return libunwind_virtual_unwind( context->Pc, &dispatch->EstablisherFrame, context, + &dispatch->LanguageHandler, &dispatch->HandlerData ); #else ERR("libunwind not available, unable to unwind\n"); return STATUS_INVALID_DISPOSITION;
I split out the 32 bit arm changes to a separate MR.
This merge request was approved by André Zwing.