diff --git a/src/ctrl/ctrl_core.c b/src/ctrl/ctrl_core.c index ef1f9755..bea4d853 100644 --- a/src/ctrl/ctrl_core.c +++ b/src/ctrl/ctrl_core.c @@ -1485,13 +1485,13 @@ ctrl_unwind_from_thread(Arena *arena, CTRL_EntityStore *store, CTRL_MachineID ma unwind.count += 1; // rjf: unwind one step - UNW_Result unwind_step = {0}; + UNW_Step unwind_step = {0}; switch(arch) { default:{unwind_step.dead = 1;}break; case Architecture_x64: { - unwind_step = unw_pe_x64(binary_data, &dbgi->pe, module_vaddr_range.min, &memview, (UNW_X64_Regs *)regs_block); + unwind_step = unw_unwind_pe_x64(binary_data, &dbgi->pe, module_vaddr_range.min, &memview, (REGS_RegBlockX64 *)regs_block); }break; } diff --git a/src/raddbgi_from_dwarf/raddbgi_dwarf.h b/src/raddbgi_from_dwarf/raddbgi_dwarf.h index ca868988..186f0486 100644 --- a/src/raddbgi_from_dwarf/raddbgi_dwarf.h +++ b/src/raddbgi_from_dwarf/raddbgi_dwarf.h @@ -1260,6 +1260,167 @@ if (success__) \ (x) = dwarf_leb128_decode(T,first__, (p)); \ }while(0) + +//////////////////////////////// +//~ allen: ELF/DW Unwind Types +// +// TODO(rjf): OLD TYPES FROM UNWINDER CODE. bucketing this here, and deferring dwarf-based +// unwinding info to future DWARF/linux work. +// +#if 0 + +// * applies to (any X: unwind(ELF/DW, X)) + +// EH: Exception Frames +typedef U8 UNW_DW_EhPtrEnc; +enum{ + UNW_DW_EhPtrEnc_TYPE_MASK = 0x0F, + UNW_DW_EhPtrEnc_PTR = 0x00, // Pointer sized unsigned value + UNW_DW_EhPtrEnc_ULEB128 = 0x01, // Unsigned LE base-128 value + UNW_DW_EhPtrEnc_UDATA2 = 0x02, // Unsigned 16-bit value + UNW_DW_EhPtrEnc_UDATA4 = 0x03, // Unsigned 32-bit value + UNW_DW_EhPtrEnc_UDATA8 = 0x04, // Unsigned 64-bit value + UNW_DW_EhPtrEnc_SIGNED = 0x08, // Signed pointer + UNW_DW_EhPtrEnc_SLEB128 = 0x09, // Signed LE base-128 value + UNW_DW_EhPtrEnc_SDATA2 = 0x0A, // Signed 16-bit value + UNW_DW_EhPtrEnc_SDATA4 = 0x0B, // Signed 32-bit value + UNW_DW_EhPtrEnc_SDATA8 = 0x0C, // Signed 64-bit value +}; +enum{ + UNW_DW_EhPtrEnc_MODIF_MASK = 0x70, + UNW_DW_EhPtrEnc_PCREL = 0x10, // Value is relative to the current program counter. + UNW_DW_EhPtrEnc_TEXTREL = 0x20, // Value is relative to the .text section. + UNW_DW_EhPtrEnc_DATAREL = 0x30, // Value is relative to the .got or .eh_frame_hdr section. + UNW_DW_EhPtrEnc_FUNCREL = 0x40, // Value is relative to the function. + UNW_DW_EhPtrEnc_ALIGNED = 0x50, // Value is aligned to an address unit sized boundary. +}; +enum{ + UNW_DW_EhPtrEnc_INDIRECT = 0x80, // This flag indicates that value is stored in virtual memory. + UNW_DW_EhPtrEnc_OMIT = 0xFF, +}; + +typedef struct UNW_DW_EhPtrCtx{ + U64 raw_base_vaddr; // address where pointer is being read + U64 text_vaddr; // base address of section with instructions (used for encoding pointer on SH and IA64) + U64 data_vaddr; // base address of data section (used for encoding pointer on x86-64) + U64 func_vaddr; // base address of function where IP is located +} UNW_DW_EhPtrCtx; + +// CIE: Common Information Entry +typedef struct UNW_DW_CIEUnpacked{ + U8 version; + UNW_DW_EhPtrEnc lsda_encoding; + UNW_DW_EhPtrEnc addr_encoding; + + B8 has_augmentation_size; + U64 augmentation_size; + String8 augmentation; + + U64 code_align_factor; + S64 data_align_factor; + U64 ret_addr_reg; + + U64 handler_ip; + + U64 cfi_range_min; + U64 cfi_range_max; +} UNW_DW_CIEUnpacked; + +typedef struct UNW_DW_CIEUnpackedNode{ + struct UNW_DW_CIEUnpackedNode *next; + UNW_DW_CIEUnpacked cie; + U64 offset; +} UNW_DW_CIEUnpackedNode; + +// FDE: Frame Description Entry +typedef struct UNW_DW_FDEUnpacked{ + U64 ip_voff_min; + U64 ip_voff_max; + U64 lsda_ip; + + U64 cfi_range_min; + U64 cfi_range_max; +} UNW_DW_FDEUnpacked; + +// CFI: Call Frame Information +typedef struct UNW_DW_CFIRecords{ + B32 valid; + UNW_DW_CIEUnpacked cie; + UNW_DW_FDEUnpacked fde; +} UNW_DW_CFIRecords; + +typedef enum UNW_DW_CFICFARule{ + UNW_DW_CFICFARule_REGOFF, + UNW_DW_CFICFARule_EXPR, +} UNW_DW_CFICFARule; + +typedef struct UNW_DW_CFICFACell{ + UNW_DW_CFICFARule rule; + union{ + struct{ + U64 reg_idx; + S64 offset; + }; + U64 expr_min; + U64 expr_max; + }; +} UNW_DW_CFICFACell; + +typedef enum UNW_DW_CFIRegisterRule{ + UNW_DW_CFIRegisterRule_SAME_VALUE, + UNW_DW_CFIRegisterRule_UNDEFINED, + UNW_DW_CFIRegisterRule_OFFSET, + UNW_DW_CFIRegisterRule_VAL_OFFSET, + UNW_DW_CFIRegisterRule_REGISTER, + UNW_DW_CFIRegisterRule_EXPRESSION, + UNW_DW_CFIRegisterRule_VAL_EXPRESSION, +} UNW_DW_CFIRegisterRule; + +typedef struct UNW_DW_CFICell{ + UNW_DW_CFIRegisterRule rule; + union{ + S64 n; + struct{ + U64 expr_min; + U64 expr_max; + }; + }; +} UNW_DW_CFICell; + +typedef struct UNW_DW_CFIRow{ + struct UNW_DW_CFIRow *next; + UNW_DW_CFICell *cells; + UNW_DW_CFICFACell cfa_cell; +} UNW_DW_CFIRow; + +typedef struct UNW_DW_CFIMachine{ + U64 cells_per_row; + UNW_DW_CIEUnpacked *cie; + UNW_DW_EhPtrCtx *ptr_ctx; + UNW_DW_CFIRow *initial_row; + U64 fde_ip; +} UNW_DW_CFIMachine; + +typedef U8 UNW_DW_CFADecode; +enum{ + UNW_DW_CFADecode_NOP = 0x0, + // 1,2,4,8 reserved for literal byte sizes + UNW_DW_CFADecode_ADDRESS = 0x9, + UNW_DW_CFADecode_ULEB128 = 0xA, + UNW_DW_CFADecode_SLEB128 = 0xB, +}; + +typedef U16 UNW_DW_CFAControlBits; +enum{ + UNW_DW_CFAControlBits_DEC1_MASK = 0x00F, + UNW_DW_CFAControlBits_DEC2_MASK = 0x0F0, + UNW_DW_CFAControlBits_IS_REG_0 = 0x100, + UNW_DW_CFAControlBits_IS_REG_1 = 0x200, + UNW_DW_CFAControlBits_IS_REG_2 = 0x400, + UNW_DW_CFAControlBits_NEW_ROW = 0x800, +}; +#endif + //////////////////////////////// //~ Dwarf Parser Functions diff --git a/src/unwind/unwind.c b/src/unwind/unwind.c index 85704be8..51c65f53 100644 --- a/src/unwind/unwind.c +++ b/src/unwind/unwind.c @@ -2,401 +2,39 @@ // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// -//~ allen: Unwind Functions - -//- mem view construction +//~ rjf: Memory View Helpers internal UNW_MemView -unw_memview_from_data(String8 data, U64 base_vaddr){ +unw_memview_from_data(String8 data, U64 base_vaddr) +{ UNW_MemView result = {0}; result.data = data.str; result.addr_first = base_vaddr; result.addr_opl = base_vaddr + data.size; - return(result); + return result; } -//- mem view user face for unwind users - internal B32 -unw_memview_read(UNW_MemView *memview, U64 addr, U64 size, void *out){ +unw_memview_read(UNW_MemView *memview, U64 addr, U64 size, void *out) +{ B32 result = 0; - if (memview->addr_first <= addr && addr + size <= memview->addr_opl){ + if(memview->addr_first <= addr && addr + size <= memview->addr_opl) + { MemoryCopy(out, (U8*)memview->data + addr - memview->addr_first, size); result = 1; } - return(result); + return result; } - //////////////////////////////// -//~ allen: PE X64 Unwind Functions +//~ rjf: PE/X64 Unwind Implementation -//- main interface +//- rjf: helpers -internal UNW_Result -unw_pe_x64(String8 bindata, PE_BinInfo *bin, - U64 base_vaddr, UNW_MemView *memview, UNW_X64_Regs *regs){ - UNW_Result result = {0}; - U64 missed_read_addr = 0; - - //- grab ip_voff (several places can use this) - U64 ip_voff = regs->rip.u64 - base_vaddr; - - //- get pdata entry from current ip - PE_IntelPdata *initial_pdata = 0; - { - U64 initial_pdata_off = pe_intel_pdata_off_from_voff__binary_search(bindata, bin, ip_voff); - if (initial_pdata_off != 0){ - initial_pdata = (PE_IntelPdata*)(bindata.str + initial_pdata_off); - } - } - - //- no pdata; unwind by reading stack pointer - if (initial_pdata == 0){ - // read ip from stack pointer - U64 sp = regs->rsp.u64; - U64 new_ip = 0; - if (!unw_memview_read_struct(memview, sp, &new_ip)){ - missed_read_addr = sp; - goto error_out; - } - - // advance stack pointer - U64 new_sp = sp + 8; - - // commit registers - regs->rip.u64 = new_ip; - regs->rsp.u64 = new_sp; - } - - //- got pdata; perform unwinding with exception handling - else{ - // try epilog unwind - B32 did_epilog_unwind = 0; - if (unw_pe_x64__voff_is_in_epilog(bindata, bin, ip_voff, initial_pdata)){ - result = unw_pe_x64__epilog(bindata, bin, base_vaddr, memview, regs); - did_epilog_unwind = 1; - } - - // try xdata unwind - if (!did_epilog_unwind){ - B32 did_machframe = 0; - - // get frame reg - REGS_Reg64 *frame_reg = 0; - U64 frame_off = 0; - - { - U64 unwind_info_off = initial_pdata->voff_unwind_info; - UNW_PE_Info *unwind_info = (UNW_PE_Info*)(pe_ptr_from_voff(bindata, bin, unwind_info_off)); - - U32 frame_reg_id = UNW_PE_INFO_REG_FROM_FRAME(unwind_info->frame); - U64 frame_off_val = UNW_PE_INFO_OFF_FROM_FRAME(unwind_info->frame); - - if (frame_reg_id != 0){ - frame_reg = unw_pe_x64__gpr_reg(regs, frame_reg_id); - // TODO(allen): at this point if frame_reg is zero, the exe is corrupted. - } - frame_off = frame_off_val; - } - - PE_IntelPdata *last_pdata = 0; - PE_IntelPdata *pdata = initial_pdata; - for (;pdata != last_pdata;){ - //- get unwind info & codes - U64 unwind_info_off = pdata->voff_unwind_info; - UNW_PE_Info *unwind_info = (UNW_PE_Info*)(pe_ptr_from_voff(bindata, bin, unwind_info_off)); - UNW_PE_Code *unwind_codes = (UNW_PE_Code*)(unwind_info + 1); - - //- get frame base - U64 frame_base = regs->rsp.u64; - if (frame_reg != 0){ - U64 raw_frame_base = frame_reg->u64; - U64 adjusted_frame_base = raw_frame_base - frame_off*16; - if (adjusted_frame_base < raw_frame_base){ - frame_base = adjusted_frame_base; - } - else{ - frame_base = 0; - } - } - - //- rjf: bad unwind info -> abort - if(unwind_info == 0) - { - result.dead = 1; - goto error_out; - } - - //- op code interpreter - UNW_PE_Code *code_ptr = unwind_codes; - UNW_PE_Code *code_opl = unwind_codes + unwind_info->codes_num; - for (UNW_PE_Code *next_code_ptr = 0; code_ptr < code_opl; code_ptr = next_code_ptr){ - // extract op code parts - U32 op_code = UNW_PE_CODE_FROM_FLAGS(code_ptr->flags); - U32 op_info = UNW_PE_INFO_FROM_FLAGS(code_ptr->flags); - - // determine number of op code slots - U32 slot_count = unw_pe_x64__slot_count_from_op_code(op_code); - if (op_code == UNW_PE_OpCode_ALLOC_LARGE && op_info == 1){ - slot_count += 1; - } - - // check op code slot count - if (slot_count == 0 || code_ptr + slot_count > code_opl){ - result.dead = 1; - goto end_xdata_unwind; - } - - // set next op code pointer - next_code_ptr = code_ptr + slot_count; - - // interpret this op code - U64 code_voff = pdata->voff_first + code_ptr->off_in_prolog; - if (code_voff <= ip_voff){ - switch (op_code){ - case UNW_PE_OpCode_PUSH_NONVOL: - { - // read value from stack pointer - U64 sp = regs->rsp.u64; - U64 value = 0; - if (!unw_memview_read_struct(memview, sp, &value)){ - missed_read_addr = sp; - goto error_out; - } - - // advance stack pointer - U64 new_sp = sp + 8; - - // commit registers - REGS_Reg64 *reg = unw_pe_x64__gpr_reg(regs, op_info); - reg->u64 = value; - regs->rsp.u64 = new_sp; - }break; - - case UNW_PE_OpCode_ALLOC_LARGE: - { - // read alloc size - U64 size = 0; - if (op_info == 0){ - size = code_ptr[1].u16*8; - } - else if (op_info == 1){ - size = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); - } - else{ - result.dead = 1; - goto end_xdata_unwind; - } - - // advance stack pointer - U64 sp = regs->rsp.u64; - U64 new_sp = sp + size; - - // advance stack pointer - regs->rsp.u64 = new_sp; - }break; - - case UNW_PE_OpCode_ALLOC_SMALL: - { - // advance stack pointer - regs->rsp.u64 += op_info*8 + 8; - }break; - - case UNW_PE_OpCode_SET_FPREG: - { - // put stack pointer back to the frame base - regs->rsp.u64 = frame_base; - }break; - - case UNW_PE_OpCode_SAVE_NONVOL: - { - // read value from frame base - U64 off = code_ptr[1].u16*8; - U64 addr = frame_base + off; - U64 value = 0; - if (!unw_memview_read_struct(memview, addr, &value)){ - missed_read_addr = addr; - goto error_out; - } - - // commit to register - REGS_Reg64 *reg = unw_pe_x64__gpr_reg(regs, op_info); - reg->u64 = value; - }break; - - case UNW_PE_OpCode_SAVE_NONVOL_FAR: - { - // read value from frame base - U64 off = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); - U64 addr = frame_base + off; - U64 value = 0; - if (!unw_memview_read_struct(memview, addr, &value)){ - missed_read_addr = addr; - goto error_out; - } - - // commit to register - REGS_Reg64 *reg = unw_pe_x64__gpr_reg(regs, op_info); - reg->u64 = value; - }break; - - case UNW_PE_OpCode_EPILOG: - { - result.dead = 1; - }break; - - case UNW_PE_OpCode_SPARE_CODE: - { - result.dead = 1; - // Assert(!"Hit me!"); - // TODO(allen): ??? - }break; - - case UNW_PE_OpCode_SAVE_XMM128: - { - // read new register values - U8 buf[16]; - U64 off = code_ptr[1].u16*16; - U64 addr = frame_base + off; - if (!unw_memview_read(memview, addr, 16, buf)){ - missed_read_addr = addr; - goto error_out; - } - - // commit to register - void *xmm_reg = (®s->ymm0) + op_info; - MemoryCopy(xmm_reg, buf, sizeof(buf)); - }break; - - case UNW_PE_OpCode_SAVE_XMM128_FAR: - { - // read new register values - U8 buf[16]; - U64 off = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); - U64 addr = frame_base + off; - if (!unw_memview_read(memview, addr, 16, buf)){ - missed_read_addr = addr; - goto error_out; - } - - // commit to register - void *xmm_reg = (®s->ymm0) + op_info; - MemoryCopy(xmm_reg, buf, sizeof(buf)); - }break; - - case UNW_PE_OpCode_PUSH_MACHFRAME: - { - // NOTE(rjf): this was found by stepping through kernel code after an exception was - // thrown, encountered in the exception_stepping_tests (after the throw) in mule_main - - if (op_info > 1){ - result.dead = 1; - goto end_xdata_unwind; - } - - // read values - U64 sp_og = regs->rsp.u64; - U64 sp_adj = sp_og; - if (op_info == 1){ - sp_adj += 8; - } - - U64 ip_value = 0; - if (!unw_memview_read_struct(memview, sp_adj, &ip_value)){ - missed_read_addr = sp_adj; - goto error_out; - } - - U64 sp_after_ip = sp_adj + 8; - U16 ss_value = 0; - if (!unw_memview_read_struct(memview, sp_after_ip, &ss_value)){ - missed_read_addr = sp_after_ip; - goto error_out; - } - - U64 sp_after_ss = sp_after_ip + 8; - U64 rflags_value = 0; - if (!unw_memview_read_struct(memview, sp_after_ss, &rflags_value)){ - missed_read_addr = sp_after_ss; - goto error_out; - } - - U64 sp_after_rflags = sp_after_ss + 8; - U64 sp_value = 0; - if (!unw_memview_read_struct(memview, sp_after_rflags, &sp_value)){ - missed_read_addr = sp_after_rflags; - goto error_out; - } - - // commit registers - regs->rip.u64 = ip_value; - regs->ss.u16 = ss_value; - regs->rflags.u64 = rflags_value; - regs->rsp.u64 = sp_value; - - // mark machine frame - did_machframe = 1; - }break; - } - } - } - - //- iterate pdata chain - U32 flags = UNW_PE_INFO_FLAGS_FROM_HDR(unwind_info->header); - if (!(flags & UNW_PE_InfoFlag_CHAINED)){ - break; - } - - U64 code_count_rounded = AlignPow2(unwind_info->codes_num, 2); - U64 code_size = code_count_rounded*sizeof(UNW_PE_OpCode); - U64 chained_pdata_off = unwind_info_off + sizeof(UNW_PE_Info) + code_size; - - last_pdata = pdata; - pdata = (PE_IntelPdata*)pe_ptr_from_voff(bindata, bin, chained_pdata_off); - } - - if (!did_machframe){ - U64 sp = regs->rsp.u64; - U64 new_ip = 0; - if (!unw_memview_read_struct(memview, sp, &new_ip)){ - missed_read_addr = sp; - goto error_out; - } - - // advance stack pointer - U64 new_sp = sp + 8; - - // commit registers - regs->rip.u64 = new_ip; - regs->rsp.u64 = new_sp; - } - - end_xdata_unwind:; - } - } - - error_out:; - - if (missed_read_addr != 0){ - result.dead = 1; - result.missed_read = 1; - result.missed_read_addr = missed_read_addr; - } - - if (!result.dead){ - result.stack_pointer = regs->rsp.u64; - } - - return(result); -} - -//- pe x64 helpers - -internal UNW_Result -unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, - U64 base_vaddr, UNW_MemView*memview, UNW_X64_Regs *regs){ - UNW_Result result = {0}; +internal UNW_Step +unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, U64 base_vaddr, UNW_MemView*memview, REGS_RegBlockX64 *regs) +{ + UNW_Step result = {0}; U64 missed_read_addr = 0; //- setup parsing context @@ -412,27 +50,32 @@ unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, //- parsing loop - for (;keep_parsing;){ + for(;keep_parsing;) + { keep_parsing = 0; U8 inst_byte = 0; - if (off + sizeof(inst_byte) <= inst_size){ + if(off + sizeof(inst_byte) <= inst_size) + { void *ptr = (U8*)inst_base + off; MemoryCopy(&inst_byte, ptr, sizeof(inst_byte)); } off += 1; U8 rex = 0; - if ((inst_byte & 0xF0) == 0x40){ + if((inst_byte & 0xF0) == 0x40) + { rex = inst_byte & 0xF; // rex prefix - if (off + sizeof(inst_base) <= inst_size){ + if(off + sizeof(inst_base) <= inst_size) + { void *ptr = (U8*)inst_base + off; MemoryCopy(&inst_byte, ptr, sizeof(inst_byte)); } off += 1; } - switch (inst_byte){ + switch(inst_byte) + { // pop case 0x58: case 0x59: @@ -445,13 +88,14 @@ unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, { U64 sp = regs->rsp.u64; U64 value = 0; - if (!unw_memview_read_struct(memview, sp, &value)){ + if(!unw_memview_read_struct(memview, sp, &value)) + { missed_read_addr = sp; goto error_out; } // modify register - UNW_PE_X64_GprReg gpr_reg = (inst_byte - 0x58) + (rex & 1)*8; + PE_UnwindGprRegX64 gpr_reg = (inst_byte - 0x58) + (rex & 1)*8; REGS_Reg64 *reg = unw_pe_x64__gpr_reg(regs, gpr_reg); // not a final instruction @@ -470,7 +114,8 @@ unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, // read the 4-byte immediate S32 imm = 0; - if (off + sizeof(imm) < inst_size){ + if(off + sizeof(imm) < inst_size) + { void *ptr = (U8*)inst_base + off; MemoryCopy(&imm, ptr, sizeof(imm)); } @@ -491,7 +136,8 @@ unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, // read the 1-byte immediate S8 imm = 0; - if (off + sizeof(imm) < inst_size){ + if(off + sizeof(imm) < inst_size) + { void *ptr = (U8*)inst_base + off; MemoryCopy(&imm, ptr, sizeof(imm)); } @@ -507,11 +153,12 @@ unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, { // read source register U8 modrm = 0; - if (off + sizeof(modrm) < inst_size){ + if(off + sizeof(modrm) < inst_size) + { void *ptr = (U8*)inst_base + off; MemoryCopy(&modrm, ptr, sizeof(modrm)); } - UNW_PE_X64_GprReg gpr_reg = (modrm & 7) + (rex & 1)*8; + PE_UnwindGprRegX64 gpr_reg = (modrm & 7) + (rex & 1)*8; REGS_Reg64 *reg = unw_pe_x64__gpr_reg(regs, gpr_reg); U64 reg_value = reg->u64; @@ -520,9 +167,11 @@ unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, S32 imm = 0; // read 1-byte immediate - if ((modrm >> 6) == 1){ + if((modrm >> 6) == 1) + { S8 imm8 = 0; - if (off + sizeof(imm8) < inst_size){ + if(off + sizeof(imm8) < inst_size) + { void *ptr = (U8*)inst_base + off; MemoryCopy(&imm8, ptr, sizeof(imm8)); } @@ -531,8 +180,10 @@ unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, } // read 4-byte immediate - else{ - if (off + sizeof(imm) < inst_size){ + else + { + if(off + sizeof(imm) < inst_size) + { void *ptr = (U8*)inst_base + off; MemoryCopy(&imm, ptr, sizeof(imm)); } @@ -549,14 +200,16 @@ unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, // read new ip U64 sp = regs->rsp.u64; U64 new_ip = 0; - if (!unw_memview_read_struct(memview, sp, &new_ip)){ + if(!unw_memview_read_struct(memview, sp, &new_ip)) + { missed_read_addr = sp; goto error_out; } // read 2-byte immediate & advance stack pointer U16 imm = 0; - if (off + sizeof(imm) < inst_size){ + if(off + sizeof(imm) < inst_size) + { void *ptr = (U8*)inst_base + off; MemoryCopy(&imm, ptr, sizeof(imm)); } @@ -577,7 +230,8 @@ unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, // read new ip U64 sp = regs->rsp.u64; U64 new_ip = 0; - if (!unw_memview_read_struct(memview, sp, &new_ip)){ + if(!unw_memview_read_struct(memview, sp, &new_ip)) + { missed_read_addr = sp; goto error_out; } @@ -611,7 +265,8 @@ unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, error_out:; - if (missed_read_addr != 0){ + if(missed_read_addr != 0) + { result.dead = 1; result.missed_read = 1; result.missed_read_addr = missed_read_addr; @@ -620,28 +275,9 @@ unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, return(result); } -internal U32 -unw_pe_x64__slot_count_from_op_code(UNW_PE_OpCode opcode){ - U32 result = 0; - switch (opcode){ - case UNW_PE_OpCode_PUSH_NONVOL: result = 1; break; - case UNW_PE_OpCode_ALLOC_LARGE: result = 2; break; - case UNW_PE_OpCode_ALLOC_SMALL: result = 1; break; - case UNW_PE_OpCode_SET_FPREG: result = 1; break; - case UNW_PE_OpCode_SAVE_NONVOL: result = 2; break; - case UNW_PE_OpCode_SAVE_NONVOL_FAR: result = 3; break; - case UNW_PE_OpCode_EPILOG: result = 2; break; - case UNW_PE_OpCode_SPARE_CODE: result = 3; break; - case UNW_PE_OpCode_SAVE_XMM128: result = 2; break; - case UNW_PE_OpCode_SAVE_XMM128_FAR: result = 3; break; - case UNW_PE_OpCode_PUSH_MACHFRAME: result = 1; break; - } - return(result); -} - internal B32 -unw_pe_x64__voff_is_in_epilog(String8 bindata, PE_BinInfo *bin, - U64 voff, PE_IntelPdata *final_pdata){ +unw_pe_x64__voff_is_in_epilog(String8 bindata, PE_BinInfo *bin, U64 voff, PE_IntelPdata *final_pdata) +{ // NOTE(allen): There are restrictions placed on how an epilog is allowed // to be formed (https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog?view=msvc-160) // Here we interpret machine code directly according to the rules @@ -815,27 +451,409 @@ unw_pe_x64__voff_is_in_epilog(String8 bindata, PE_BinInfo *bin, } internal REGS_Reg64* -unw_pe_x64__gpr_reg(UNW_X64_Regs *regs, UNW_PE_X64_GprReg unw_reg){ +unw_pe_x64__gpr_reg(REGS_RegBlockX64 *regs, PE_UnwindGprRegX64 unw_reg){ static REGS_Reg64 dummy = {0}; REGS_Reg64 *result = &dummy; switch (unw_reg){ - case UNW_PE_X64_GprReg_RAX: result = ®s->rax; break; - case UNW_PE_X64_GprReg_RCX: result = ®s->rcx; break; - case UNW_PE_X64_GprReg_RDX: result = ®s->rdx; break; - case UNW_PE_X64_GprReg_RBX: result = ®s->rbx; break; - case UNW_PE_X64_GprReg_RSP: result = ®s->rsp; break; - case UNW_PE_X64_GprReg_RBP: result = ®s->rbp; break; - case UNW_PE_X64_GprReg_RSI: result = ®s->rsi; break; - case UNW_PE_X64_GprReg_RDI: result = ®s->rdi; break; - case UNW_PE_X64_GprReg_R8 : result = ®s->r8 ; break; - case UNW_PE_X64_GprReg_R9 : result = ®s->r9 ; break; - case UNW_PE_X64_GprReg_R10: result = ®s->r10; break; - case UNW_PE_X64_GprReg_R11: result = ®s->r11; break; - case UNW_PE_X64_GprReg_R12: result = ®s->r12; break; - case UNW_PE_X64_GprReg_R13: result = ®s->r13; break; - case UNW_PE_X64_GprReg_R14: result = ®s->r14; break; - case UNW_PE_X64_GprReg_R15: result = ®s->r15; break; + case PE_UnwindGprRegX64_RAX: result = ®s->rax; break; + case PE_UnwindGprRegX64_RCX: result = ®s->rcx; break; + case PE_UnwindGprRegX64_RDX: result = ®s->rdx; break; + case PE_UnwindGprRegX64_RBX: result = ®s->rbx; break; + case PE_UnwindGprRegX64_RSP: result = ®s->rsp; break; + case PE_UnwindGprRegX64_RBP: result = ®s->rbp; break; + case PE_UnwindGprRegX64_RSI: result = ®s->rsi; break; + case PE_UnwindGprRegX64_RDI: result = ®s->rdi; break; + case PE_UnwindGprRegX64_R8 : result = ®s->r8 ; break; + case PE_UnwindGprRegX64_R9 : result = ®s->r9 ; break; + case PE_UnwindGprRegX64_R10: result = ®s->r10; break; + case PE_UnwindGprRegX64_R11: result = ®s->r11; break; + case PE_UnwindGprRegX64_R12: result = ®s->r12; break; + case PE_UnwindGprRegX64_R13: result = ®s->r13; break; + case PE_UnwindGprRegX64_R14: result = ®s->r14; break; + case PE_UnwindGprRegX64_R15: result = ®s->r15; break; } return(result); } +//- rjf: unwind step + +internal UNW_Step +unw_unwind_pe_x64(String8 bindata, PE_BinInfo *bin, U64 base_vaddr, UNW_MemView *memview, REGS_RegBlockX64 *regs) +{ + UNW_Step result = {0}; + U64 missed_read_addr = 0; + + //- grab ip_voff (several places can use this) + U64 ip_voff = regs->rip.u64 - base_vaddr; + + //- get pdata entry from current ip + PE_IntelPdata *initial_pdata = 0; + { + U64 initial_pdata_off = pe_intel_pdata_off_from_voff__binary_search(bindata, bin, ip_voff); + if(initial_pdata_off != 0) + { + initial_pdata = (PE_IntelPdata*)(bindata.str + initial_pdata_off); + } + } + + //- no pdata; unwind by reading stack pointer + if(initial_pdata == 0) + { + // read ip from stack pointer + U64 sp = regs->rsp.u64; + U64 new_ip = 0; + if(!unw_memview_read_struct(memview, sp, &new_ip)) + { + missed_read_addr = sp; + goto error_out; + } + + // advance stack pointer + U64 new_sp = sp + 8; + + // commit registers + regs->rip.u64 = new_ip; + regs->rsp.u64 = new_sp; + } + + //- got pdata; perform unwinding with exception handling + else + { + // try epilog unwind + B32 did_epilog_unwind = 0; + if(unw_pe_x64__voff_is_in_epilog(bindata, bin, ip_voff, initial_pdata)) + { + result = unw_pe_x64__epilog(bindata, bin, base_vaddr, memview, regs); + did_epilog_unwind = 1; + } + + // try xdata unwind + if(!did_epilog_unwind) + { + B32 did_machframe = 0; + + // get frame reg + REGS_Reg64 *frame_reg = 0; + U64 frame_off = 0; + + { + U64 unwind_info_off = initial_pdata->voff_unwind_info; + PE_UnwindInfo *unwind_info = (PE_UnwindInfo*)(pe_ptr_from_voff(bindata, bin, unwind_info_off)); + + U32 frame_reg_id = PE_UNWIND_INFO_REG_FROM_FRAME(unwind_info->frame); + U64 frame_off_val = PE_UNWIND_INFO_OFF_FROM_FRAME(unwind_info->frame); + + if (frame_reg_id != 0){ + frame_reg = unw_pe_x64__gpr_reg(regs, frame_reg_id); + // TODO(allen): at this point if frame_reg is zero, the exe is corrupted. + } + frame_off = frame_off_val; + } + + PE_IntelPdata *last_pdata = 0; + PE_IntelPdata *pdata = initial_pdata; + for (;pdata != last_pdata;) + { + //- rjf: unpack unwind info & codes + U64 unwind_info_off = pdata->voff_unwind_info; + PE_UnwindInfo *unwind_info = (PE_UnwindInfo*)(pe_ptr_from_voff(bindata, bin, unwind_info_off)); + PE_UnwindCode *unwind_codes = (PE_UnwindCode*)(unwind_info + 1); + + //- rjf: unpack frame base + U64 frame_base = regs->rsp.u64; + if(frame_reg != 0) + { + U64 raw_frame_base = frame_reg->u64; + U64 adjusted_frame_base = raw_frame_base - frame_off*16; + if(adjusted_frame_base < raw_frame_base) + { + frame_base = adjusted_frame_base; + } + else + { + frame_base = 0; + } + } + + //- rjf: bad unwind info -> abort + if(unwind_info == 0) + { + result.dead = 1; + goto error_out; + } + + //- op code interpreter + PE_UnwindCode *code_ptr = unwind_codes; + PE_UnwindCode *code_opl = unwind_codes + unwind_info->codes_num; + for(PE_UnwindCode *next_code_ptr = 0; code_ptr < code_opl; code_ptr = next_code_ptr) + { + // extract op code parts + U32 op_code = PE_UNWIND_OPCODE_FROM_FLAGS(code_ptr->flags); + U32 op_info = PE_UNWIND_INFO_FROM_FLAGS(code_ptr->flags); + + // determine number of op code slots + U32 slot_count = pe_slot_count_from_unwind_op_code(op_code); + if(op_code == PE_UnwindOpCode_ALLOC_LARGE && op_info == 1) + { + slot_count += 1; + } + + // check op code slot count + if (slot_count == 0 || code_ptr + slot_count > code_opl){ + result.dead = 1; + goto end_xdata_unwind; + } + + // set next op code pointer + next_code_ptr = code_ptr + slot_count; + + // interpret this op code + U64 code_voff = pdata->voff_first + code_ptr->off_in_prolog; + if (code_voff <= ip_voff){ + switch (op_code){ + case PE_UnwindOpCode_PUSH_NONVOL: + { + // read value from stack pointer + U64 sp = regs->rsp.u64; + U64 value = 0; + if(!unw_memview_read_struct(memview, sp, &value)) + { + missed_read_addr = sp; + goto error_out; + } + + // advance stack pointer + U64 new_sp = sp + 8; + + // commit registers + REGS_Reg64 *reg = unw_pe_x64__gpr_reg(regs, op_info); + reg->u64 = value; + regs->rsp.u64 = new_sp; + }break; + + case PE_UnwindOpCode_ALLOC_LARGE: + { + // read alloc size + U64 size = 0; + if (op_info == 0){ + size = code_ptr[1].u16*8; + } + else if (op_info == 1){ + size = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); + } + else{ + result.dead = 1; + goto end_xdata_unwind; + } + + // advance stack pointer + U64 sp = regs->rsp.u64; + U64 new_sp = sp + size; + + // advance stack pointer + regs->rsp.u64 = new_sp; + }break; + + case PE_UnwindOpCode_ALLOC_SMALL: + { + // advance stack pointer + regs->rsp.u64 += op_info*8 + 8; + }break; + + case PE_UnwindOpCode_SET_FPREG: + { + // put stack pointer back to the frame base + regs->rsp.u64 = frame_base; + }break; + + case PE_UnwindOpCode_SAVE_NONVOL: + { + // read value from frame base + U64 off = code_ptr[1].u16*8; + U64 addr = frame_base + off; + U64 value = 0; + if (!unw_memview_read_struct(memview, addr, &value)){ + missed_read_addr = addr; + goto error_out; + } + + // commit to register + REGS_Reg64 *reg = unw_pe_x64__gpr_reg(regs, op_info); + reg->u64 = value; + }break; + + case PE_UnwindOpCode_SAVE_NONVOL_FAR: + { + // read value from frame base + U64 off = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); + U64 addr = frame_base + off; + U64 value = 0; + if (!unw_memview_read_struct(memview, addr, &value)){ + missed_read_addr = addr; + goto error_out; + } + + // commit to register + REGS_Reg64 *reg = unw_pe_x64__gpr_reg(regs, op_info); + reg->u64 = value; + }break; + + case PE_UnwindOpCode_EPILOG: + { + result.dead = 1; + }break; + + case PE_UnwindOpCode_SPARE_CODE: + { + result.dead = 1; + // Assert(!"Hit me!"); + // TODO(allen): ??? + }break; + + case PE_UnwindOpCode_SAVE_XMM128: + { + // read new register values + U8 buf[16]; + U64 off = code_ptr[1].u16*16; + U64 addr = frame_base + off; + if (!unw_memview_read(memview, addr, 16, buf)){ + missed_read_addr = addr; + goto error_out; + } + + // commit to register + void *xmm_reg = (®s->ymm0) + op_info; + MemoryCopy(xmm_reg, buf, sizeof(buf)); + }break; + + case PE_UnwindOpCode_SAVE_XMM128_FAR: + { + // read new register values + U8 buf[16]; + U64 off = code_ptr[1].u16 + ((U32)code_ptr[2].u16 << 16); + U64 addr = frame_base + off; + if (!unw_memview_read(memview, addr, 16, buf)){ + missed_read_addr = addr; + goto error_out; + } + + // commit to register + void *xmm_reg = (®s->ymm0) + op_info; + MemoryCopy(xmm_reg, buf, sizeof(buf)); + }break; + + case PE_UnwindOpCode_PUSH_MACHFRAME: + { + // NOTE(rjf): this was found by stepping through kernel code after an exception was + // thrown, encountered in the exception_stepping_tests (after the throw) in mule_main + + if(op_info > 1) + { + result.dead = 1; + goto end_xdata_unwind; + } + + // read values + U64 sp_og = regs->rsp.u64; + U64 sp_adj = sp_og; + if(op_info == 1) + { + sp_adj += 8; + } + + U64 ip_value = 0; + if(!unw_memview_read_struct(memview, sp_adj, &ip_value)) + { + missed_read_addr = sp_adj; + goto error_out; + } + + U64 sp_after_ip = sp_adj + 8; + U16 ss_value = 0; + if(!unw_memview_read_struct(memview, sp_after_ip, &ss_value)) + { + missed_read_addr = sp_after_ip; + goto error_out; + } + + U64 sp_after_ss = sp_after_ip + 8; + U64 rflags_value = 0; + if(!unw_memview_read_struct(memview, sp_after_ss, &rflags_value)) + { + missed_read_addr = sp_after_ss; + goto error_out; + } + + U64 sp_after_rflags = sp_after_ss + 8; + U64 sp_value = 0; + if(!unw_memview_read_struct(memview, sp_after_rflags, &sp_value)) + { + missed_read_addr = sp_after_rflags; + goto error_out; + } + + // commit registers + regs->rip.u64 = ip_value; + regs->ss.u16 = ss_value; + regs->rflags.u64 = rflags_value; + regs->rsp.u64 = sp_value; + + // mark machine frame + did_machframe = 1; + }break; + } + } + } + + //- iterate pdata chain + U32 flags = PE_UNWIND_INFO_FLAGS_FROM_HDR(unwind_info->header); + if (!(flags & PE_UnwindInfoFlag_CHAINED)){ + break; + } + + U64 code_count_rounded = AlignPow2(unwind_info->codes_num, 2); + U64 code_size = code_count_rounded*sizeof(PE_UnwindOpCode); + U64 chained_pdata_off = unwind_info_off + sizeof(PE_UnwindInfo) + code_size; + + last_pdata = pdata; + pdata = (PE_IntelPdata*)pe_ptr_from_voff(bindata, bin, chained_pdata_off); + } + + if(!did_machframe) + { + U64 sp = regs->rsp.u64; + U64 new_ip = 0; + if(!unw_memview_read_struct(memview, sp, &new_ip)) + { + missed_read_addr = sp; + goto error_out; + } + + // advance stack pointer + U64 new_sp = sp + 8; + + // commit registers + regs->rip.u64 = new_ip; + regs->rsp.u64 = new_sp; + } + + end_xdata_unwind:; + } + } + + error_out:; + + if(missed_read_addr != 0) + { + result.dead = 1; + result.missed_read = 1; + result.missed_read_addr = missed_read_addr; + } + + if(!result.dead) + { + result.stack_pointer = regs->rsp.u64; + } + + return(result); +} diff --git a/src/unwind/unwind.h b/src/unwind/unwind.h index 3d34d9bf..310e940c 100644 --- a/src/unwind/unwind.h +++ b/src/unwind/unwind.h @@ -5,11 +5,20 @@ #define UNWIND_H //////////////////////////////// -//~ allen: Unwind Types +//~ rjf: Memory View Types +// +// Memory views are used to provide a slice (or, in the future, slices) of data +// required to do a proper unwind. This is generally a very small region in an +// address space, generally some things on the stack. But, some formats don't +// restrict this in an organized way, and so theoretically you might have some +// unwind information require arbitrary reads at an unknown address. So this +// "memory view" concept serves as kind of a "stand-in" for "provided memory +// info from the user". This keeps the control flow of this layer simpler, so +// we aren't calling a user-supplied hook to read memory or anything like that. -// * applies to (any X,Y: unwind(X, Y)) - -typedef struct UNW_MemView{ +typedef struct UNW_MemView UNW_MemView; +struct UNW_MemView +{ // Upgrade Path: // 1. A list of ranges like this one // 2. Binary-searchable list of ranges @@ -18,297 +27,36 @@ typedef struct UNW_MemView{ void *data; U64 addr_first; U64 addr_opl; -} UNW_MemView; +}; -typedef struct UNW_Result{ +//////////////////////////////// +//~ rjf: Unwind Step Results + +typedef struct UNW_Step UNW_Step; +struct UNW_Step +{ B32 dead; B32 missed_read; U64 missed_read_addr; U64 stack_pointer; -} UNW_Result; - +}; //////////////////////////////// -//~ allen: X64 Unwind Types +//~ rjf: Memory View Helpers -// * applies to (any X: unwind(X, X64)) - -typedef REGS_RegBlockX64 UNW_X64_Regs; - - - - -//////////////////////////////// -//~ allen: PE X64 Unwind Types - -//- pe format unwind types - -#define UNW_PE_OpCodeXList(X) \ -X(PUSH_NONVOL , 0) \ -X(ALLOC_LARGE , 1) \ -X(ALLOC_SMALL , 2) \ -X(SET_FPREG , 3) \ -X(SAVE_NONVOL , 4) \ -X(SAVE_NONVOL_FAR, 5) \ -X(EPILOG , 6) \ -X(SPARE_CODE , 7) \ -X(SAVE_XMM128 , 8) \ -X(SAVE_XMM128_FAR, 9) \ -X(PUSH_MACHFRAME , 10) - -#define UNW_PE_CODE_FROM_FLAGS(f) ((f)&0xF) -#define UNW_PE_INFO_FROM_FLAGS(f) (((f) >> 4)&0xF) - -typedef U32 UNW_PE_OpCode; -enum UNW_PE_OpCodeEnum{ -#define X(N,C) UNW_PE_OpCode_##N = C, - UNW_PE_OpCodeXList(X) -#undef X -}; - -typedef union UNW_PE_Code{ - struct{ - U8 off_in_prolog; - U8 flags; - }; - U16 u16; -} UNW_PE_Code; - -typedef U8 UNW_PE_InfoFlags; -enum UNW_PE_InfoFlagsEnum{ - UNW_PE_InfoFlag_EHANDLER = (1 << 0), - UNW_PE_InfoFlag_UHANDLER = (1 << 1), - UNW_PE_InfoFlag_FHANDLER = 3, - UNW_PE_InfoFlag_CHAINED = (1 << 2), -} UNW_PE_InfoFlagsEnum; - -#define UNW_PE_INFO_VERSION_FROM_HDR(x) ((x)&0x7) -#define UNW_PE_INFO_FLAGS_FROM_HDR(x) (((x) >> 3)&0x1F) -#define UNW_PE_INFO_REG_FROM_FRAME(x) ((x)&0xF) -#define UNW_PE_INFO_OFF_FROM_FRAME(x) (((x) >> 4)&0xF) - -typedef struct UNW_PE_Info{ - U8 header; - U8 prolog_size; - U8 codes_num; - U8 frame; -} UNW_PE_Info; - - -//////////////////////////////// -//~ allen: PE X64 Unwind Types - -// * applies to (unwind(PE, X64)) - -typedef U8 UNW_PE_X64_GprReg; -enum{ - UNW_PE_X64_GprReg_RAX = 0, - UNW_PE_X64_GprReg_RCX = 1, - UNW_PE_X64_GprReg_RDX = 2, - UNW_PE_X64_GprReg_RBX = 3, - UNW_PE_X64_GprReg_RSP = 4, - UNW_PE_X64_GprReg_RBP = 5, - UNW_PE_X64_GprReg_RSI = 6, - UNW_PE_X64_GprReg_RDI = 7, - UNW_PE_X64_GprReg_R8 = 8, - UNW_PE_X64_GprReg_R9 = 9, - UNW_PE_X64_GprReg_R10 = 10, - UNW_PE_X64_GprReg_R11 = 11, - UNW_PE_X64_GprReg_R12 = 12, - UNW_PE_X64_GprReg_R13 = 13, - UNW_PE_X64_GprReg_R14 = 14, - UNW_PE_X64_GprReg_R15 = 15, -}; - - - - -//////////////////////////////// -//~ allen: ELF/DW Unwind Types - -// * applies to (any X: unwind(ELF/DW, X)) - -// EH: Exception Frames -typedef U8 UNW_DW_EhPtrEnc; -enum{ - UNW_DW_EhPtrEnc_TYPE_MASK = 0x0F, - UNW_DW_EhPtrEnc_PTR = 0x00, // Pointer sized unsigned value - UNW_DW_EhPtrEnc_ULEB128 = 0x01, // Unsigned LE base-128 value - UNW_DW_EhPtrEnc_UDATA2 = 0x02, // Unsigned 16-bit value - UNW_DW_EhPtrEnc_UDATA4 = 0x03, // Unsigned 32-bit value - UNW_DW_EhPtrEnc_UDATA8 = 0x04, // Unsigned 64-bit value - UNW_DW_EhPtrEnc_SIGNED = 0x08, // Signed pointer - UNW_DW_EhPtrEnc_SLEB128 = 0x09, // Signed LE base-128 value - UNW_DW_EhPtrEnc_SDATA2 = 0x0A, // Signed 16-bit value - UNW_DW_EhPtrEnc_SDATA4 = 0x0B, // Signed 32-bit value - UNW_DW_EhPtrEnc_SDATA8 = 0x0C, // Signed 64-bit value -}; -enum{ - UNW_DW_EhPtrEnc_MODIF_MASK = 0x70, - UNW_DW_EhPtrEnc_PCREL = 0x10, // Value is relative to the current program counter. - UNW_DW_EhPtrEnc_TEXTREL = 0x20, // Value is relative to the .text section. - UNW_DW_EhPtrEnc_DATAREL = 0x30, // Value is relative to the .got or .eh_frame_hdr section. - UNW_DW_EhPtrEnc_FUNCREL = 0x40, // Value is relative to the function. - UNW_DW_EhPtrEnc_ALIGNED = 0x50, // Value is aligned to an address unit sized boundary. -}; -enum{ - UNW_DW_EhPtrEnc_INDIRECT = 0x80, // This flag indicates that value is stored in virtual memory. - UNW_DW_EhPtrEnc_OMIT = 0xFF, -}; - -typedef struct UNW_DW_EhPtrCtx{ - U64 raw_base_vaddr; // address where pointer is being read - U64 text_vaddr; // base address of section with instructions (used for encoding pointer on SH and IA64) - U64 data_vaddr; // base address of data section (used for encoding pointer on x86-64) - U64 func_vaddr; // base address of function where IP is located -} UNW_DW_EhPtrCtx; - -// CIE: Common Information Entry -typedef struct UNW_DW_CIEUnpacked{ - U8 version; - UNW_DW_EhPtrEnc lsda_encoding; - UNW_DW_EhPtrEnc addr_encoding; - - B8 has_augmentation_size; - U64 augmentation_size; - String8 augmentation; - - U64 code_align_factor; - S64 data_align_factor; - U64 ret_addr_reg; - - U64 handler_ip; - - U64 cfi_range_min; - U64 cfi_range_max; -} UNW_DW_CIEUnpacked; - -typedef struct UNW_DW_CIEUnpackedNode{ - struct UNW_DW_CIEUnpackedNode *next; - UNW_DW_CIEUnpacked cie; - U64 offset; -} UNW_DW_CIEUnpackedNode; - -// FDE: Frame Description Entry -typedef struct UNW_DW_FDEUnpacked{ - U64 ip_voff_min; - U64 ip_voff_max; - U64 lsda_ip; - - U64 cfi_range_min; - U64 cfi_range_max; -} UNW_DW_FDEUnpacked; - -// CFI: Call Frame Information -typedef struct UNW_DW_CFIRecords{ - B32 valid; - UNW_DW_CIEUnpacked cie; - UNW_DW_FDEUnpacked fde; -} UNW_DW_CFIRecords; - -typedef enum UNW_DW_CFICFARule{ - UNW_DW_CFICFARule_REGOFF, - UNW_DW_CFICFARule_EXPR, -} UNW_DW_CFICFARule; - -typedef struct UNW_DW_CFICFACell{ - UNW_DW_CFICFARule rule; - union{ - struct{ - U64 reg_idx; - S64 offset; - }; - U64 expr_min; - U64 expr_max; - }; -} UNW_DW_CFICFACell; - -typedef enum UNW_DW_CFIRegisterRule{ - UNW_DW_CFIRegisterRule_SAME_VALUE, - UNW_DW_CFIRegisterRule_UNDEFINED, - UNW_DW_CFIRegisterRule_OFFSET, - UNW_DW_CFIRegisterRule_VAL_OFFSET, - UNW_DW_CFIRegisterRule_REGISTER, - UNW_DW_CFIRegisterRule_EXPRESSION, - UNW_DW_CFIRegisterRule_VAL_EXPRESSION, -} UNW_DW_CFIRegisterRule; - -typedef struct UNW_DW_CFICell{ - UNW_DW_CFIRegisterRule rule; - union{ - S64 n; - struct{ - U64 expr_min; - U64 expr_max; - }; - }; -} UNW_DW_CFICell; - -typedef struct UNW_DW_CFIRow{ - struct UNW_DW_CFIRow *next; - UNW_DW_CFICell *cells; - UNW_DW_CFICFACell cfa_cell; -} UNW_DW_CFIRow; - -typedef struct UNW_DW_CFIMachine{ - U64 cells_per_row; - UNW_DW_CIEUnpacked *cie; - UNW_DW_EhPtrCtx *ptr_ctx; - UNW_DW_CFIRow *initial_row; - U64 fde_ip; -} UNW_DW_CFIMachine; - -typedef U8 UNW_DW_CFADecode; -enum{ - UNW_DW_CFADecode_NOP = 0x0, - // 1,2,4,8 reserved for literal byte sizes - UNW_DW_CFADecode_ADDRESS = 0x9, - UNW_DW_CFADecode_ULEB128 = 0xA, - UNW_DW_CFADecode_SLEB128 = 0xB, -}; - -typedef U16 UNW_DW_CFAControlBits; -enum{ - UNW_DW_CFAControlBits_DEC1_MASK = 0x00F, - UNW_DW_CFAControlBits_DEC2_MASK = 0x0F0, - UNW_DW_CFAControlBits_IS_REG_0 = 0x100, - UNW_DW_CFAControlBits_IS_REG_1 = 0x200, - UNW_DW_CFAControlBits_IS_REG_2 = 0x400, - UNW_DW_CFAControlBits_NEW_ROW = 0x800, -}; - - - -//////////////////////////////// -//~ allen: Unwind Functions - -//- mem view construction internal UNW_MemView unw_memview_from_data(String8 data, U64 base_vaddr); - -//- mem view user face for unwind users internal B32 unw_memview_read(UNW_MemView *memview, U64 addr, U64 size, void *out); - #define unw_memview_read_struct(v,addr,p) unw_memview_read((v), (addr), sizeof(*(p)), (p)) - //////////////////////////////// -//~ allen: PE X64 Unwind Functions +//~ rjf: PE/X64 Unwind Implementation -//- main interface -internal UNW_Result unw_pe_x64(String8 bindata, PE_BinInfo *bin, - U64 base_vaddr, UNW_MemView *memview, - UNW_X64_Regs *regs_inout); +//- rjf: helpers +internal UNW_Step unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, U64 base_vaddr, UNW_MemView *memview, REGS_RegBlockX64 *regs_inout); +internal B32 unw_pe_x64__voff_is_in_epilog(String8 bindata, PE_BinInfo *bin, U64 voff, PE_IntelPdata *final_pdata); +internal REGS_Reg64 *unw_pe_x64__gpr_reg(REGS_RegBlockX64 *regs, PE_UnwindGprRegX64 unw_reg); +//- rjf: unwind step +internal UNW_Step unw_unwind_pe_x64(String8 bindata, PE_BinInfo *bin, U64 base_vaddr, UNW_MemView *memview, REGS_RegBlockX64 *regs_inout); -//- pe x64 helpers -internal UNW_Result unw_pe_x64__epilog(String8 bindata, PE_BinInfo *bin, - U64 base_vaddr, UNW_MemView*memview, - UNW_X64_Regs *regs_inout); - -internal U32 unw_pe_x64__slot_count_from_op_code(UNW_PE_OpCode opcode); -internal B32 unw_pe_x64__voff_is_in_epilog(String8 bindata, PE_BinInfo *bin, - U64 voff, PE_IntelPdata *final_pdata); - -internal REGS_Reg64 *unw_pe_x64__gpr_reg(UNW_X64_Regs *regs, UNW_PE_X64_GprReg unw_reg); - -#endif //UNWIND_H +#endif // UNWIND_H