diff --git a/src/dwarf/dwarf.c b/src/dwarf/dwarf.c index 6063f0d7..508c2594 100644 --- a/src/dwarf/dwarf.c +++ b/src/dwarf/dwarf.c @@ -69,6 +69,31 @@ dw_reg_pos_from_code(Arch arch, DW_Reg reg_code) return max_U64; } +internal U64 +dw_reg_count_from_arch(Arch arch) +{ + switch (arch) { + default: { NotImplemented; } // fall-through + case Arch_Null: return 0; + case Arch_x86: return DW_RegX86_Last; + case Arch_x64: return DW_RegX64_Last; + } +} + +internal U64 +dw_reg_max_size_from_arch(Arch arch) +{ + local_persist U64 max_size = 0; + if (max_size == 0) { + U64 max_idx = dw_reg_count_from_arch(arch); + for EachIndex(reg_idx, max_idx) { + U64 reg_size = dw_reg_size_from_code(arch, reg_idx); + max_size = Max(max_size, reg_size); + } + } + return max_size; +} + internal DW_AttribClass dw_attrib_class_from_attrib_v2(DW_AttribKind k) { @@ -431,6 +456,71 @@ dw_push_count_from_expr_op(DW_ExprOp op) return 0; } +internal U64 +dw_operand_count_from_cfa_opcode(DW_CFA_Opcode opcode) +{ + switch (opcode) { +#define X(_N, _ID, ...) case _ID: { local_persist DW_CFA_OperandType t[] = { DW_CFA_OperandType_Null, __VA_ARGS__ }; return ArrayCount(t)-1; } + DW_CFA_Kind_XList(X) +#undef X + default: { NotImplemented; } break; + } + return 0; +} + +internal B32 +dw_is_cfa_expr_opcode_invalid(DW_ExprOp opcode) +{ + B32 is_invalid = 0; + switch (opcode) { + case DW_ExprOp_Addrx: + case DW_ExprOp_Call2: + case DW_ExprOp_Call4: + case DW_ExprOp_CallRef: + case DW_ExprOp_ConstType: + case DW_ExprOp_Constx: + case DW_ExprOp_Convert: + case DW_ExprOp_DerefType: + case DW_ExprOp_RegvalType: + case DW_ExprOp_Reinterpret: + case DW_ExprOp_PushObjectAddress: + case DW_ExprOp_CallFrameCfa: { + is_invalid = 1; + } break; + default: break; + } + return is_invalid; +} + +internal B32 +dw_is_new_row_cfa_opcode(DW_CFA_Opcode opcode) +{ + B32 is_new_row_op = 0; + switch (opcode) { + case DW_CFA_SetLoc: + case DW_CFA_AdvanceLoc: + case DW_CFA_AdvanceLoc1: + case DW_CFA_AdvanceLoc2: + case DW_CFA_AdvanceLoc4: { + is_new_row_op = 1; + } break; + default: break; + } + return is_new_row_op; +} + +internal DW_CFA_OperandType * +dw_operand_types_from_cfa_op(DW_CFA_Opcode opcode) +{ + switch (opcode) { +#define X(_N, _ID, ...) case _ID: { local_persist DW_CFA_OperandType t[] = { DW_CFA_OperandType_Null, __VA_ARGS__ }; return &t[0] + 1; } + DW_CFA_Kind_XList(X) +#undef X + default: { NotImplemented; } break; + } + return 0; +} + //////////////////////////////// //~ rjf: String <=> Enum @@ -717,12 +807,11 @@ dw_string_from_register(Arena *arena, Arch arch, U64 reg_id) } internal String8 -dw_string_from_cfa_opcode(DW_CFA cfa_opcode) +dw_string_from_cfa_opcode(DW_CFA_Opcode opcode) { - switch (cfa_opcode) { -#define X(_NAME, _ID) case _ID: return str8_lit(Stringify(_NAME)); - DW_CFA_Kind1_XList(X) - DW_CFA_Kind2_XList(X) + switch (opcode) { +#define X(_NAME, _ID, ...) case _ID: return str8_lit(Stringify(_NAME)); + DW_CFA_Kind_XList(X) #undef X default: InvalidPath; break; } diff --git a/src/dwarf/dwarf.h b/src/dwarf/dwarf.h index a0515bcc..4e844e75 100644 --- a/src/dwarf/dwarf.h +++ b/src/dwarf/dwarf.h @@ -1337,56 +1337,57 @@ typedef enum DW_LNCTEnum DW_LNCT_UserHi = 0x3fff } DW_LNCTEnum; -#define DW_CFA_Kind1_XList(X) \ -X(Nop, 0x0) \ -X(SetLoc, 0x1) \ -X(AdvanceLoc1, 0x2) \ -X(AdvanceLoc2, 0x3) \ -X(AdvanceLoc4, 0x4) \ -X(OffsetExt, 0x5) \ -X(RestoreExt, 0x6) \ -X(Undefined, 0x7) \ -X(SameValue, 0x8) \ -X(Register, 0x9) \ -X(RememberState, 0xA) \ -X(RestoreState, 0xB) \ -X(DefCfa, 0xC) \ -X(DefCfaRegister, 0xD) \ -X(DefCfaOffset, 0xE) \ -X(DefCfaExpr, 0xF) \ -X(Expr, 0x10) \ -X(OffsetExtSf, 0x11) \ -X(DefCfaSf, 0x12) \ -X(DefCfaOffsetSf, 0x13) \ -X(ValOffset, 0x14) \ -X(ValOffsetSf, 0x15) \ -X(ValExpr, 0x16) +//////////////////////////////// +// CFA -#define DW_CFA_Kind2_XList(X) \ -X(AdvanceLoc, 0x40) \ -X(Offset, 0x80) \ -X(Restore, 0xC0) - -typedef U8 DW_CFA; -typedef enum DW_CFAEnum +typedef enum { -#define X(_N, _ID) DW_CFA_##_N = _ID, - DW_CFA_Kind1_XList(X) - DW_CFA_Kind2_XList(X) + DW_CFA_OperandType_Null, + DW_CFA_OperandType_Value, + DW_CFA_OperandType_Register, + DW_CFA_OperandType_Expression, +} DW_CFA_OperandType; + +// (opcode name, opcode id, operand count, operand types) +#define DW_CFA_Kind_XList(X) \ +X(Nop, 0x0) \ +X(SetLoc, 0x1, DW_CFA_OperandType_Value) \ +X(AdvanceLoc1, 0x2, DW_CFA_OperandType_Value) \ +X(AdvanceLoc2, 0x3, DW_CFA_OperandType_Value) \ +X(AdvanceLoc4, 0x4, DW_CFA_OperandType_Value) \ +X(OffsetExt, 0x5, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ +X(RestoreExt, 0x6) \ +X(Undefined, 0x7, DW_CFA_OperandType_Register) \ +X(SameValue, 0x8, DW_CFA_OperandType_Register) \ +X(Register, 0x9, DW_CFA_OperandType_Register) \ +X(RememberState, 0xa) \ +X(RestoreState, 0xb) \ +X(DefCfa, 0xc, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ +X(DefCfaRegister, 0xd, DW_CFA_OperandType_Register) \ +X(DefCfaOffset, 0xe, DW_CFA_OperandType_Value) \ +X(DefCfaExpr, 0xf, DW_CFA_OperandType_Expression) \ +X(Expr, 0x10, DW_CFA_OperandType_Expression) \ +X(OffsetExtSf, 0x11, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ +X(DefCfaSf, 0x12, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ +X(DefCfaOffsetSf, 0x13, DW_CFA_OperandType_Value) \ +X(ValOffset, 0x14, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ +X(ValOffsetSf, 0x15, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ +X(ValExpr, 0x16, DW_CFA_OperandType_Register, DW_CFA_OperandType_Expression) \ +X(AdvanceLoc, 0x40, DW_CFA_OperandType_Value) \ +X(Offset, 0x80, DW_CFA_OperandType_Register, DW_CFA_OperandType_Value) \ +X(Restore, 0xc0, DW_CFA_OperandType_Register) + +#define DW_CFA_OperandMax 2 +#define DW_CFA_Mask_OpcodeHi 0xc0 +#define DW_CFA_Mask_Operand 0x3f + +typedef U8 DW_CFA_Opcode; +typedef enum DW_CFA_Enum +{ +#define X(_N, _ID, ...) DW_CFA_##_N = _ID, + DW_CFA_Kind_XList(X) #undef X - - DW_CFA_OplKind1 = DW_CFA_ValExpr, - DW_CFA_OplKind2 = DW_CFA_Restore, -} DW_CFAEnum; - -typedef U8 DW_CFAMask; -enum -{ - // kind1: opcode: [0,5] zeroes:[6,7]; kind2: operand:[0,5] opcode:[6,7] - DW_CFAMask_OpcodeHi = 0xC0, - DW_CFAMask_Operand = 0x3F, - DW_CFAMask_Count = 2 -}; +} DW_CFA_Enum; //////////////////////////////// // Expression Opcodes @@ -1561,7 +1562,7 @@ X(RegvalType, 0xa5, 2, 0, 1) \ X(DerefType, 0xa6, 2, 1, 1) \ X(XDerefType, 0xa7, 2, 2, 1) \ X(Convert, 0xa8, 1, 1, 1) \ -X(ReInterpret, 0xa9, 1, 1, 1) +X(Reinterpret, 0xa9, 1, 1, 1) #define DW_Expr_GNU_XList(X) \ X(GNU_PushTlsAddress, 0xe0, 0, 0, 1) \ @@ -1589,7 +1590,7 @@ typedef enum DW_ExprOpEnum //- Regs -#define DW_Regs_X86_XList(X) \ +#define DW_Regs_X86_XList(X) \ X(Eax, 0, eax, 0, 4) \ X(Ecx, 1, ecx, 0, 4) \ X(Edx, 2, edx, 0, 4) \ @@ -1637,7 +1638,7 @@ X(Gs, 45, gs, 0, 2) \ X(Tr, 48, nil, 0, 0) \ X(Ldtr, 49, nil, 0, 0) -#define DW_Regs_X64_XList(X) \ +#define DW_Regs_X64_XList(X) \ X(Rax, 0, rax, 0, 8) \ X(Rdx, 1, rdx, 0, 8) \ X(Rcx, 2, rcx, 0, 8) \ @@ -1723,6 +1724,7 @@ typedef enum DW_RegX86Enum #define X(_N,_ID,...) DW_RegX86_##_N = _ID, DW_Regs_X86_XList(X) #undef X + DW_RegX86_Last } DW_RegX86Enum; typedef DW_Reg DW_RegX64; @@ -1731,6 +1733,7 @@ typedef enum DW_RegX64Enum #define X(_N,_ID,...) DW_RegX64_##_N = _ID, DW_Regs_X64_XList(X) #undef X + DW_RegX64_Last } DW_RegX64Enum; //////////////////////////////// @@ -1741,6 +1744,8 @@ internal U64 dw_reg_size_from_code_x64(DW_Reg reg_code); internal U64 dw_reg_pos_from_code_x64(DW_Reg reg_code); internal U64 dw_reg_size_from_code(Arch arch, DW_Reg reg_code); internal U64 dw_reg_pos_from_code(Arch arch, DW_Reg reg_code); +internal U64 dw_reg_max_size_from_arch(Arch arch); +internal U64 dw_reg_count_from_arch(Arch arch); //- Attrib Class Encodings @@ -1781,6 +1786,16 @@ internal DW_AttribClass dw_pick_attrib_value_class(DW_Version ver, DW_Ext ext, B internal U64 dw_pick_default_lower_bound(DW_Language lang); internal U64 dw_operand_count_from_expr_op(DW_ExprOp op); +internal U64 dw_pop_count_from_expr_op(DW_ExprOp op); +internal U64 dw_push_count_from_expr_op(DW_ExprOp op); + +//////////////////////////////// +//~ CFA + +internal U64 dw_operand_count_from_cfa_opcode(DW_CFA_Opcode opcode); +internal B32 dw_is_cfa_expr_opcode_invalid(DW_ExprOp opcode); +internal B32 dw_is_new_row_cfa_opcode(DW_CFA_Opcode opcode); +internal DW_CFA_OperandType * dw_operand_types_from_cfa_op(DW_CFA_Opcode opcode); //////////////////////////////// //~ rjf: String <=> Enum @@ -1801,6 +1816,6 @@ internal String8 dw_string_from_loc_list_entry_kind(Arena *arena, DW_LLE kind); internal String8 dw_string_from_section_kind(Arena *arena, DW_SectionKind kind); internal String8 dw_string_from_rng_list_entry_kind(Arena *arena, DW_RLE kind); internal String8 dw_string_from_register(Arena *arena, Arch arch, U64 reg_id); -internal String8 dw_string_from_cfa_opcode(DW_CFA cfa_opcode); +internal String8 dw_string_from_cfa_opcode(DW_CFA_Opcodecfa_opcode); #endif // DWARF_H diff --git a/src/dwarf/dwarf_dump.c b/src/dwarf/dwarf_dump.c index 08f586a9..d18da87d 100644 --- a/src/dwarf/dwarf_dump.c +++ b/src/dwarf/dwarf_dump.c @@ -5,7 +5,7 @@ //~ rjf: Stringification Helpers internal String8 -dw_string_from_reg_off(Arena *arena, Arch arch, U64 reg_idx, S64 reg_off) +dw_string_from_reg_off(Arena *arena, Arch arch, DW_Reg reg_idx, S64 reg_off) { Temp scratch = scratch_begin(&arena, 1); String8 reg_str = dw_string_from_register(scratch.arena, arch, reg_idx); @@ -19,6 +19,12 @@ dw_string_from_reg_off(Arena *arena, Arch arch, U64 reg_idx, S64 reg_off) return result; } +internal String8 +dw_string_from_reg(Arena *arena, Arch arch, DW_Reg reg_idx) +{ + return dw_string_from_reg_off(arena, arch, reg_idx, 0); +} + internal String8List dw_string_list_from_expression(Arena *arena, String8 raw_data, U64 cu_base, U64 addr_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format) { @@ -337,166 +343,153 @@ dw_string_list_from_cfi_program(Arena *arena, DW_Ext ext, DW_Format format, U64 pc_begin, - DW_UnpackedCIE *cie, - DW_ReadCfiPtrFunc *read_cfi_ptr_func, - void *read_cfi_ptr_ud, + DW_CIE *cie, + DW_DecodePtr *decode_ptr_func, + void *deocde_ptr_ud, String8 program) { Temp scratch = scratch_begin(&arena, 1); String8List list = {0}; + U64 reg_max = dw_reg_count_from_arch(arch); U64 pc = pc_begin; - for (U64 cursor = 0; cursor < program.size; ) { - DW_CFA opcode = 0; - cursor += str8_deserial_read_struct(program, cursor, &opcode); - - U64 operand = 0; - if ((opcode & DW_CFAMask_OpcodeHi) != 0) { - operand = opcode & DW_CFAMask_Operand; - opcode = opcode & DW_CFAMask_OpcodeHi; + for (U64 cursor = 0, inst_size; cursor < program.size; cursor += inst_size) { + // unpack instruction + DW_CFA_Inst inst = {0}; + DW_CFA_ParseErrorCode error_code = dw_parse_cfa_inst(str8_skip(program, cursor), cie->code_align_factor, cie->data_align_factor, 0, 0, &inst_size, &inst); + if (error_code == DW_CFA_ParseErrorCode_End) { break; } + if (error_code != DW_CFA_ParseErrorCode_NewInst) { + str8_list_pushf(arena, &list, "ERROR: failed to unpack CFA instruction @ 0x%I64", cursor); + break; } + // error check operands + DW_CFA_OperandType *operand_types = dw_operand_types_from_cfa_op(inst.opcode); + U64 operand_count = dw_operand_count_from_cfa_opcode(inst.opcode); + for EachIndex(operand_idx, operand_count) { + switch (operand_types[operand_idx]) { + case DW_CFA_OperandType_Null: break; + case DW_CFA_OperandType_Value: break; + case DW_CFA_OperandType_Register: { + if (inst.operands[operand_idx].u64 >= reg_max) { + str8_list_pushf(arena, &list, "ERROR: DW_CFA_%S @ 0x%I64x has an invalid register", + dw_string_from_cfa_opcode(inst.opcode), + cursor); + } + } break; + case DW_CFA_OperandType_Expression: { + DW_Expr expr = dw_expr_from_data(scratch.arena, format, cie->address_size, inst.operands[operand_idx].block); + for EachNode(inst, DW_ExprInst, expr.first) { + if (dw_is_cfa_expr_opcode_invalid(inst->opcode)) { + String8 expr_opcode_str = dw_string_from_expr_op(scratch.arena, ver, ext, inst->opcode); + str8_list_pushf(arena, &list, "ERROR: Exrepssion in DW_CFA_%S @ 0x%I64x has an invalid opcode DW_ExprOp_%S", expr_opcode_str); + } + } + } break; + default: { InvalidPath; } break; + } + } + + // format operands String8 operand_str = str8_lit("???"); - switch (opcode) { + switch (inst.opcode) { case DW_CFA_Nop: { operand_str = str8_zero(); } break; case DW_CFA_SetLoc: { - U64 address = 0; - cursor += read_cfi_ptr_func(program, cursor, read_cfi_ptr_ud, &address); - operand_str = str8f(arena, "0x%X", address); + operand_str = str8f(arena, "0x%X", inst.operands[0].u64); + pc = inst.operands[0].u64; } break; case DW_CFA_AdvanceLoc1: { - U8 delta = 0; - cursor += str8_deserial_read_struct(program, cursor, &delta); - delta *= cie->code_align_factor; + U64 delta = inst.operands[0].u64; pc += delta; - operand_str = str8f(arena, "%+u; PC 0x%I64x", delta, pc); + operand_str = str8f(arena, "%+u `PC 0x%I64x`", delta, pc); } break; case DW_CFA_AdvanceLoc2: { - U16 delta = 0; - cursor += str8_deserial_read_struct(program, cursor, &delta); - delta *= cie->code_align_factor; + U64 delta = inst.operands[0].u64; pc += delta; - operand_str = str8f(arena, "%+u; PC 0x%I64x", delta, pc); + operand_str = str8f(arena, "%+u `PC 0x%I64x`", delta, pc); } break; case DW_CFA_AdvanceLoc4: { - U32 delta = 0; - cursor += str8_deserial_read_struct(program, cursor, &delta); - delta *= cie->code_align_factor; + U64 delta = inst.operands[0].u64; pc += delta; - operand_str = str8f(arena, "%+u; PC 0x%I64x", delta, pc); + operand_str = str8f(arena, "%+u `PC 0x%I64x`", delta, pc); } break; case DW_CFA_OffsetExt: { - U64 reg = 0, offset = 0; - cursor += str8_deserial_read_uleb128(program, cursor, ®); - cursor += str8_deserial_read_uleb128(program, cursor, &offset); - operand_str = str8f(arena, "%S", dw_string_from_reg_off(scratch.arena, arch, reg, (S64)offset * cie->data_align_factor)); + U64 reg = inst.operands[0].u64; + S64 offset = (S64)inst.operands[1].u64; + operand_str = dw_string_from_reg_off(arena, arch, reg, offset); } break; case DW_CFA_RestoreExt: { operand_str = str8_zero(); } break; case DW_CFA_Undefined: { - U64 reg = 0; - cursor += str8_deserial_read_uleb128(program, cursor, ®); - operand_str = str8f(arena, "%I64u", reg); + operand_str = str8f(arena, "%I64u", inst.operands[0].u64); } break; case DW_CFA_SameValue: { - U64 reg = 0; - cursor += str8_deserial_read_uleb128(program, cursor, ®); - operand_str = str8f(arena, "%S", dw_string_from_reg_off(scratch.arena, arch, reg, 0)); + operand_str = dw_string_from_reg_off(arena, arch, inst.operands[0].u64, 0); } break; case DW_CFA_Register: { - U64 reg = 0, offset = 0; - cursor += str8_deserial_read_uleb128(program, cursor, ®); - cursor += str8_deserial_read_uleb128(program, cursor, &offset); - operand_str = str8f(arena, "%S", dw_string_from_reg_off(scratch.arena, arch, reg, (S64)offset)); + operand_str = dw_string_from_reg_off(arena, arch, inst.operands[0].u64, inst.operands[1].u64); } break; case DW_CFA_RememberState: { operand_str = str8_zero(); } break; case DW_CFA_RestoreState: { operand_str = str8_zero(); } break; case DW_CFA_DefCfa: { - U64 reg = 0, offset = 0; - cursor += str8_deserial_read_uleb128(program, cursor, ®); - cursor += str8_deserial_read_uleb128(program, cursor, &offset); - operand_str = str8f(arena, "%S", dw_string_from_reg_off(scratch.arena, arch, reg, offset)); + operand_str = dw_string_from_reg_off(arena, arch, inst.operands[0].u64, inst.operands[1].u64); } break; case DW_CFA_DefCfaRegister: { - U64 reg = 0; - cursor += str8_deserial_read_uleb128(program, cursor, ®); - operand_str = str8f(arena, "%S", dw_string_from_reg_off(scratch.arena, arch, reg, 0)); + operand_str = dw_string_from_reg_off(arena, arch, inst.operands[0].u64, 0); } break; case DW_CFA_DefCfaOffset: { - U64 offset = 0; - cursor += str8_deserial_read_uleb128(program, cursor, &offset); - operand_str = str8f(arena, "+%I64u", offset); + operand_str = str8f(arena, "+%I64u", inst.operands[0].u64); } break; case DW_CFA_DefCfaExpr: { - U64 expr_size = 0; - cursor += str8_deserial_read_uleb128(program, cursor, &expr_size); - String8 expr = str8_substr(program, rng_1u64(cursor, cursor + expr_size)); - String8 expr_str = dw_string_from_expression(scratch.arena, expr, cu_base, cie->address_size, arch, ver, ext, format); - operand_str = str8f(arena, "%S", expr_str); + operand_str = dw_string_from_expression(arena, inst.operands[0].block, cu_base, cie->address_size, arch, ver, ext, format); } break; case DW_CFA_Expr: { - U64 reg = 0, expr_size = 0; - cursor += str8_deserial_read_uleb128(program, cursor, ®); - cursor += str8_deserial_read_uleb128(program, cursor, &expr_size); - String8 expr = str8_substr(program, rng_1u64(cursor, cursor + expr_size)); - String8 reg_str = dw_string_from_reg_off(scratch.arena, arch, reg, 0); - String8 expr_str = dw_string_from_expression(scratch.arena, expr, cu_base, cie->address_size, arch, ver, ext, format); + String8 reg_str = dw_string_from_reg_off(scratch.arena, arch, inst.operands[0].u64, 0); + String8 expr_str = dw_string_from_expression(scratch.arena, inst.operands[1].block, cu_base, cie->address_size, arch, ver, ext, format); operand_str = str8f(arena, "%S expression %S", reg_str, expr_str); } break; case DW_CFA_OffsetExtSf: { - U64 reg = 0; S64 offset = 0; - cursor += str8_deserial_read_uleb128(program, cursor, ®); - cursor += str8_deserial_read_sleb128(program, cursor, &offset); - String8 reg_str = dw_string_from_reg_off(scratch.arena, arch, reg, offset * cie->data_align_factor); - operand_str = str8f(arena, "%S", reg_str); + operand_str = dw_string_from_reg_off(arena, arch, inst.operands[0].u64, inst.operands[1].s64); } break; case DW_CFA_DefCfaSf: { - U64 reg = 0; S64 offset = 0; - cursor += str8_deserial_read_uleb128(program, cursor, ®); - cursor += str8_deserial_read_sleb128(program, cursor, &offset); - String8 reg_str = dw_string_from_reg_off(scratch.arena, arch, reg, offset * cie->data_align_factor); - operand_str = str8f(arena, "%S", reg_str); + operand_str = dw_string_from_reg_off(arena, arch, inst.operands[0].u64, inst.operands[1].s64); } break; + case DW_CFA_DefCfaOffsetSf: { operand_str = str8_zero(); } break; case DW_CFA_ValOffset: { - U64 val = 0, offset = 0; - cursor += str8_deserial_read_uleb128(program, cursor, &val); - cursor += str8_deserial_read_uleb128(program, cursor, &offset); - operand_str = str8f(arena, "value 0x%llx, offset %+I64d", val, offset); + operand_str = str8f(arena, "value 0x%llx, offset %+I64d", inst.operands[0].u64, inst.operands[1].u64); } break; case DW_CFA_ValOffsetSf: { - U64 val = 0; S64 offset = 0; - cursor += str8_deserial_read_uleb128(program, cursor, &val); - cursor += str8_deserial_read_sleb128(program, cursor, &offset); - operand_str = str8f(arena, "value %llu, offset %+I64d", val, offset); + operand_str = str8f(arena, "value %llu, offset %+I64d", inst.operands[0].u64, inst.operands[1].s64); } break; case DW_CFA_ValExpr: { - U64 val = 0; U64 expr_size = 0; - cursor += str8_deserial_read_uleb128(program, cursor, &val); - cursor += str8_deserial_read_uleb128(program, cursor, &expr_size); - String8 expr = str8_substr(program, rng_1u64(cursor, cursor + expr_size)); - String8 expr_str = dw_string_from_expression(scratch.arena, expr, cu_base, cie->address_size, arch, ver, ext, format); - operand_str = str8f(arena, "value +%I64u, expression %S", val, expr_str); + String8 expr_str = dw_string_from_expression(scratch.arena, inst.operands[1].block, cu_base, cie->address_size, arch, ver, ext, format); + operand_str = str8f(arena, "value +%I64u, expression %S", inst.operands[0].u64, expr_str); } break; case DW_CFA_AdvanceLoc: { - U64 delta = operand * cie->code_align_factor; + U64 delta = inst.operands[0].u64; pc += delta; - operand_str = str8f(arena, "+%I64u; PC 0x%I64x", delta, pc); + operand_str = str8f(arena, "+%I64u `PC 0x%I64x`", delta, pc); } break; case DW_CFA_Offset: { - U64 offset = 0; - cursor += str8_deserial_read_uleb128(program, cursor, &offset); - S64 v = (S64)offset * cie->data_align_factor; - operand_str = dw_string_from_reg_off(scratch.arena, arch, operand, v); + U64 reg = inst.operands[0].u64; + S64 offset = (S64)inst.operands[1].u64; + operand_str = dw_string_from_reg_off(arena, arch, reg, offset); } break; case DW_CFA_Restore: { - String8 reg_str = dw_string_from_reg_off(scratch.arena, arch, operand, 0); - operand_str = str8f(arena, "%S", reg_str); + operand_str = dw_string_from_reg_off(arena, arch, inst.operands[0].u64, 0); } break; - default: { NotImplemented; } break; + default: { + str8_list_pushf(arena, &list, "ERROR: unknown CFA opcode 0x%I64u", inst.opcode); + } goto exit; } - str8_list_pushf(arena, &list, "DW_CFA_%S: %S", dw_string_from_cfa_opcode(opcode), operand_str); + if (operand_str.size) { + str8_list_pushf(arena, &list, "DW_CFA_%S: %S", dw_string_from_cfa_opcode(inst.opcode), operand_str); + } else { + str8_list_pushf(arena, &list, "DW_CFA_%S", dw_string_from_cfa_opcode(inst.opcode)); + } } +exit:; scratch_end(scratch); return list; } @@ -514,7 +507,7 @@ dw_single_line_string_from_expression(Arena *arena, String8 raw_data, U64 cu_bas #if 0 internal String8 -dw_string_from_eh_ptr_enc(Arena *arena, EH_PtrEnc enc) +w_string_from_eh_ptr_enc(Arena *arena, EH_PtrEnc enc) { U8 type = enc & EH_PtrEnc_TypeMask; String8 type_str = str8_lit("NULL"); @@ -549,7 +542,7 @@ internal void dw_print_eh_frame(Arena *arena, String8List *out, String8 indent, String8 raw_eh_frame, Arch arch, DW_Version ver, DW_Ext ext, EH_PtrCtx *ptr_ctx) { Temp scratch = scratch_begin(&arena, 1); - DW_UnpackedCIE cie = {0}; + DW_CIE cie = {0}; for (U64 cursor = 0; cursor < raw_eh_frame.size; ) { U64 header_offset = cursor; @@ -592,7 +585,7 @@ dw_print_eh_frame(Arena *arena, String8List *out, String8 indent, String8 raw_eh } // FDE else { - DW_UnpackedFDE fde = {0}; + DW_FDE fde = {0}; dw_unwind_parse_fde_x64(raw_eh_frame.str, rng_1u64(0,raw_eh_frame.size), ptr_ctx, &cie, 0, &fde); cfi_range = fde.cfi_range; @@ -2166,11 +2159,11 @@ dw_dump_list_from_sections(Arena *arena, if (desc.type == DW_DescriptorEntryType_CIE) { String8 raw_cie = str8_substr(debug_frame, desc.entry_range); U64 restore_pos = arena_pos(scratch.arena); - DW_UnpackedCIE *cie = push_array(scratch.arena, DW_UnpackedCIE, 1); - if (dw_unpack_cie(raw_cie, desc.format, arch, cie)) { + DW_CIE *cie = push_array(scratch.arena, DW_CIE, 1); + if (dw_parse_cie(raw_cie, desc.format, arch, cie)) { hash_table_push_u64_raw(scratch.arena, cie_ht, cursor, cie); } else { - arena_pop_to(arena, restore_pos); + arena_pop_to(scratch.arena, restore_pos); } } } @@ -2182,9 +2175,9 @@ dw_dump_list_from_sections(Arena *arena, switch (desc.type) { case DW_DescriptorEntryType_Null: {} break; case DW_DescriptorEntryType_CIE: { - DW_UnpackedCIE cie = {0}; - if (dw_unpack_cie(raw_desc, desc.format, arch, &cie)) { - String8List init_insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, DW_Version_5, DW_Ext_All, cie.format, 0, &cie, dw_read_cfi_ptr, &cie, cie.init_insts); + DW_CIE cie = {0}; + if (dw_parse_cie(raw_desc, desc.format, arch, &cie)) { + String8List init_insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, DW_Version_5, DW_Ext_All, cie.format, 0, &cie, dw_decode_ptr_debug_frame, &cie, cie.insts); dumpf("CIE: // entry range: %r\n", desc.entry_range); dumpf("{\n"); @@ -2193,7 +2186,7 @@ dw_dump_list_from_sections(Arena *arena, dumpf(" Aug string: %S\n", cie.aug_string.size ? cie.aug_string : str8_lit("None")); dumpf(" Code align: %I64u\n", cie.code_align_factor); dumpf(" Data align: %I64d\n", cie.data_align_factor); - dumpf(" Return addr reg: %S\n", dw_string_from_register(scratch.arena, arch, cie.ret_addr_reg)); + dumpf(" Return addr reg: %u\n", cie.ret_addr_reg); if (cie.version > DW_Version_3) { dumpf(" Address size: %u\n", cie.address_size); dumpf(" Segment selector size: %u\n", cie.segment_selector_size); @@ -2208,10 +2201,13 @@ dw_dump_list_from_sections(Arena *arena, } } break; case DW_DescriptorEntryType_FDE: { - DW_UnpackedFDE fde = {0}; - if (dw_unpack_fde(raw_desc, desc.format, dwarf_dump_cie_from_offset, cie_ht, &fde)) { - DW_UnpackedCIE *cie = hash_table_search_u64_raw(cie_ht, fde.cie_pointer); - String8List insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, DW_Version_5, DW_Ext_All, fde.format, fde.pc_range.min, cie, dw_read_cfi_ptr, cie, fde.insts); + DW_FDE fde = {0}; + if (dw_parse_fde(raw_desc, desc.format, dwarf_dump_cie_from_offset, cie_ht, &fde)) { + DW_Version version = DW_Version_5; + DW_Ext ext = DW_Ext_All; + + DW_CIE *cie = hash_table_search_u64_raw(cie_ht, fde.cie_pointer); + String8List insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, version, ext, fde.format, fde.pc_range.min, cie, dw_decode_ptr_debug_frame, cie, fde.insts); dumpf("FDE: // entry range: %r\n", desc.entry_range, dw_string_from_format(fde.format), fde.cie_pointer, fde.pc_range); dumpf("{\n"); @@ -2224,6 +2220,63 @@ dw_dump_list_from_sections(Arena *arena, for EachNode(n, String8Node, insts_str_list.first) { dumpf(" %S\n", n->string); } dumpf(" }\n"); } + + dumpf(" Unwind:\n"); + dumpf(" {\n"); + DW_CFI_Unwind *cfi_unwind = dw_cfi_unwind_init(scratch.arena, arch, cie, &fde, dw_decode_ptr_debug_frame, cie); + do { + String8 cfa_str = str8_lit("???"); + DW_CFA cfa = cfi_unwind->row->cfa; + switch (cfa.rule) { + case DW_CFA_Rule_Null: {} break; + case DW_CFA_Rule_RegOff: { cfa_str = dw_string_from_reg_off(scratch.arena, arch, cfa.reg, cfa.off); } break; + case DW_CFA_Rule_Expression: { cfa_str = dw_string_from_expression(scratch.arena, cfa.expr, max_U64, cie->address_size, arch, version, ext, fde.format); } break; + default: { InvalidPath; } break; + } + + String8 cfi_regs_str = {0}; + { + String8List cfi_regs_list = {0}; + for EachIndex(reg_idx, cfi_unwind->reg_count) { + DW_CFI_Register *cfi_reg = &cfi_unwind->row->regs[reg_idx]; + String8 rule_str = str8_lit("???"); + switch (cfi_reg->rule) { + case DW_CFI_RegisterRule_Undefined: { + rule_str = str8f(scratch.arena, "Undefined(%S)", dw_string_from_reg(scratch.arena, arch, cfi_reg->n)); + } break; + case DW_CFI_RegisterRule_SameValue: { + rule_str = str8_zero(); + } break; + case DW_CFI_RegisterRule_Offset: { + rule_str = str8f(scratch.arena, "[CFA%+I64d]", cfi_reg->n); + } break; + case DW_CFI_RegisterRule_ValOffset: { + rule_str = str8f(scratch.arena, "Val(CFA%+I64d)", cfi_reg->n); + } break; + case DW_CFI_RegisterRule_Expression: { + rule_str = str8f(scratch.arena, "Expression(%S)", dw_string_from_expression(scratch.arena, cfi_reg->expr, max_U64, cie->address_size, arch, version, ext, fde.format)); + } break; + case DW_CFI_RegisterRule_ValExpression: { + rule_str = str8f(scratch.arena, "ValExpression(%S)", dw_string_from_expression(scratch.arena, cfi_reg->expr, max_U64, cie->address_size, arch, version, ext, fde.format)); + } break; + case DW_CFI_RegisterRule_Architectural: { + rule_str = str8_lit("???"); + } break; + default: { InvalidPath; } break; + } + + if (rule_str.size) { + str8_list_pushf(scratch.arena, &cfi_regs_list, "%S: %S", dw_string_from_reg(scratch.arena, arch, reg_idx), rule_str); + } + } + + cfi_regs_str = str8_list_join(scratch.arena, &cfi_regs_list, &(StringJoin){.sep=str8_lit(", ")}); + } + + dumpf(" { PC: 0x%I64x, CFA: %-7S, Rules: { %S }\n", cfi_unwind->pc, cfa_str, cfi_regs_str); + } while (dw_cfi_next_row(scratch.arena, cfi_unwind)); + dumpf(" }\n"); + dumpf("}\n"); } else { dumpf("ERROR: unable to parse FDE @ %I64x\n", desc.entry_range.min); diff --git a/src/dwarf/dwarf_dump.h b/src/dwarf/dwarf_dump.h index 9e673a71..0882061b 100644 --- a/src/dwarf/dwarf_dump.h +++ b/src/dwarf/dwarf_dump.h @@ -58,13 +58,13 @@ read_only global String8 dw_name_title_from_dump_subset_table[] = //////////////////////////////// //~ rjf: Stringification Helpers -internal String8 dw_string_from_reg_off(Arena *arena, Arch arch, U64 reg_idx, S64 reg_off); +internal String8 dw_string_from_reg_off(Arena *arena, Arch arch, DW_Reg reg_idx, S64 reg_off); internal String8List dw_string_list_from_expression (Arena *arena, String8 raw_data, U64 cu_base, U64 address_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format); internal String8 dw_single_line_string_from_expression(Arena *arena, String8 raw_data, U64 cu_base, U64 address_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format); internal String8 dw_string_from_eh_ptr_enc (Arena *arena, EH_PtrEnc enc); #if 0 -internal void dw_string_from_cfi_program (Arena *arena, String8List *out, String8 indent, String8 raw_data, DW_UnpackedCIE *cie, EH_PtrCtx *ptr_ctx, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format); +internal void dw_string_from_cfi_program (Arena *arena, String8List *out, String8 indent, String8 raw_data, DW_CIE *cie, EH_PtrCtx *ptr_ctx, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format); internal void dw_print_eh_frame (Arena *arena, String8List *out, String8 indent, String8 raw_eh_frame, Arch arch, DW_Version ver, DW_Ext ext, EH_PtrCtx *ptr_ctx); internal void dw_print_debug_loc (Arena *arena, String8List *out, String8 indent, DW_Input *input, Arch arch, ExecutableImageKind image_type, B32 relaxed); diff --git a/src/dwarf/dwarf_parse.c b/src/dwarf/dwarf_parse.c index 89cf0292..ae6d8741 100644 --- a/src/dwarf/dwarf_parse.c +++ b/src/dwarf/dwarf_parse.c @@ -3323,15 +3323,30 @@ dw_expr_from_data(Arena *arena, DW_Format format, U64 addr_size, String8 data) return expr; } -internal -DW_READ_CFI_PTR(dw_read_cfi_ptr) +internal void +dw_cfa_inst_list_push_node(DW_CFA_InstList *list, DW_CFA_InstNode *n) +{ + SLLQueuePush(list->first, list->last, n); + list->count += 1; +} + +internal DW_CFA_InstNode * +dw_cfa_inst_list_push(Arena *arena, DW_CFA_InstList *list, DW_CFA_Inst v) +{ + DW_CFA_InstNode *n = push_array(arena, DW_CFA_InstNode, 1); + n->v = v; + dw_cfa_inst_list_push_node(list, n); + return n; +} + +internal U64 +dw_read_debug_frame_ptr(String8 data, DW_CIE *cie, U64 *ptr_out) { - DW_UnpackedCIE *cie = ud; U64 read_size = 0; if (cie->segment_selector_size) { NotImplemented; } else { - read_size = str8_deserial_read(data, off, ptr_out, cie->address_size, cie->address_size); + read_size = str8_deserial_read(data, 0, ptr_out, cie->address_size, cie->address_size); } return read_size; } @@ -3347,10 +3362,10 @@ dw_parse_descriptor_entry_header(String8 data, U64 off, DW_DescriptorEntry *desc U64 length_size = str8_deserial_read_dwarf_packed_size(data, off, &length); if (length_size == 0) { goto exit; } - Rng1U64 entry_range = rng_1u64(off + length_size, off + length_size + length); + Rng1U64 entry_range = rng_1u64(off, off + length_size + length); String8 entry_data = str8_substr(data, entry_range); U64 id = 0; - U64 id_size = str8_deserial_read_dwarf_uint(entry_data, 0, format, &id); + U64 id_size = str8_deserial_read_dwarf_uint(entry_data, length_size, format, &id); if (id_size == 0) { goto exit; } U64 id_type = format == DW_Format_32Bit ? max_U32 : max_U64; @@ -3363,10 +3378,10 @@ exit:; } internal B32 -dw_unpack_cie(String8 data, DW_Format format, Arch arch, DW_UnpackedCIE *cie_out) +dw_parse_cie(String8 data, DW_Format format, Arch arch, DW_CIE *cie_out) { B32 is_parsed = 0; - U64 cursor = 0; + U64 cursor = format == DW_Format_32Bit ? 4 : 12; U64 cie_id = 0; U64 cie_id_size = str8_deserial_read_dwarf_uint(data, cursor, format, &cie_id); @@ -3416,11 +3431,12 @@ dw_unpack_cie(String8 data, DW_Format format, Arch arch, DW_UnpackedCIE *cie_out if (aug_string.size > 0) { goto exit; } - cie_out->init_insts = str8_skip(data, cursor); + cie_out->insts = str8_skip(data, cursor); cie_out->aug_string = aug_string; cie_out->code_align_factor = code_align_factor; cie_out->data_align_factor = data_align_factor; cie_out->ret_addr_reg = ret_addr_reg; + cie_out->format = format; cie_out->version = version; cie_out->address_size = address_size; cie_out->segment_selector_size = segment_selector_size; @@ -3431,14 +3447,14 @@ exit:; } internal B32 -dw_unpack_fde(String8 data, +dw_parse_fde(String8 data, DW_Format format, DW_CIEFromOffsetFunc *cie_from_offset_func, void *cie_from_offset_ud, - DW_UnpackedFDE *fde_out) + DW_FDE *fde_out) { B32 is_parsed = 0; - U64 cursor = 0; + U64 cursor = format == DW_Format_32Bit ? 4 : 12; // extract CIE pointer U64 cie_pointer = 0; @@ -3447,18 +3463,18 @@ dw_unpack_fde(String8 data, cursor += cie_pointer_size; // map offset -> CIE - DW_UnpackedCIE *cie = cie_from_offset_func(cie_from_offset_ud, cie_pointer); + DW_CIE *cie = cie_from_offset_func(cie_from_offset_ud, cie_pointer); if (cie == 0) { goto exit; } // extract address of first instruction U64 pc_begin = 0; - U64 pc_begin_size = dw_read_cfi_ptr(data, cursor, cie, &pc_begin); + U64 pc_begin_size = dw_read_debug_frame_ptr(str8_skip(data, cursor), cie, &pc_begin); if (pc_begin_size == 0) { goto exit; } cursor += pc_begin_size; // extract instruction range size U64 pc_range = 0; - U64 pc_range_size = dw_read_cfi_ptr(data, cursor, cie, &pc_range); + U64 pc_range_size = dw_read_debug_frame_ptr(str8_skip(data, cursor), cie, &pc_range); if (pc_range_size == 0) { goto exit; } cursor += pc_range_size; @@ -3477,3 +3493,308 @@ exit:; return is_parsed; } +internal DW_CFA_ParseErrorCode +dw_parse_cfa_inst(String8 data, + U64 code_align_factor, + S64 data_align_factor, + DW_DecodePtr *decode_ptr_func, + void *decode_ptr_ud, + U64 *bytes_read_out, + DW_CFA_Inst *inst_out) +{ + *bytes_read_out = 0; + + DW_CFA_ParseErrorCode error_code = DW_CFA_ParseErrorCode_End; + U64 cursor = 0; + + // read opcode + DW_CFA_Opcode raw_opcode = 0; + U64 raw_opcode_size = str8_deserial_read_struct(data, cursor, &raw_opcode); + if (raw_opcode_size == 0) { goto exit; } + cursor += raw_opcode_size; + + // decode opcode implicit operand + U64 opcode = raw_opcode & ~DW_CFA_Mask_OpcodeHi; + U64 implicit_operand = 0; + if ((raw_opcode & DW_CFA_Mask_OpcodeHi) != 0) { + opcode = raw_opcode & DW_CFA_Mask_OpcodeHi; + implicit_operand = raw_opcode & DW_CFA_Mask_Operand; + } + + // decode operands + DW_CFA_Operand operands[DW_CFA_OperandMax] = {0}; + switch (opcode) { + case DW_CFA_SetLoc: { + U64 address_size = decode_ptr_func(str8_skip(data, cursor), decode_ptr_ud, &operands[0].u64); + if (address_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += address_size; + } break; + case DW_CFA_AdvanceLoc: { + operands[0].u64 = implicit_operand * code_align_factor; + } break; + case DW_CFA_AdvanceLoc1: { + U8 delta = 0; + U64 delta_size = str8_deserial_read_struct(data, cursor, &delta); + if (delta_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += delta_size; + operands[0].u64 = delta * code_align_factor; + } break; + case DW_CFA_AdvanceLoc2: { + U16 delta = 0; + U64 delta_size = str8_deserial_read_struct(data, cursor, &delta); + if (delta_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += delta_size; + operands[0].u64 = delta * code_align_factor; + } break; + case DW_CFA_AdvanceLoc4: { + U32 delta = 0; + U64 delta_size = str8_deserial_read_struct(data, cursor, &delta); + if (delta_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + operands[0].u64 = delta * code_align_factor; + } break; + case DW_CFA_DefCfa: { + U64 reg = 0; + U64 reg_size = str8_deserial_read_uleb128(data, cursor, ®); + if (reg_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += reg_size; + + U64 offset = 0; + U64 offset_size = str8_deserial_read_uleb128(data, cursor, &offset); + if (offset_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += offset_size; + + operands[0].u64 = reg; + operands[1].u64 = offset; + } break; + case DW_CFA_DefCfaSf: { + U64 reg = 0; + U64 reg_size = str8_deserial_read_uleb128(data, cursor, ®); + if (reg_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += reg_size; + + S64 offset = 0; + U64 offset_size = str8_deserial_read_sleb128(data, cursor, &offset); + if (offset_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += offset_size; + + operands[0].u64 = reg; + operands[1].s64 = offset * data_align_factor; + } break; + case DW_CFA_DefCfaRegister: { + U64 reg = 0; + U64 reg_size = str8_deserial_read_uleb128(data, cursor, ®); + if (reg_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += reg_size; + + operands[0].u64 = reg; + } break; + case DW_CFA_DefCfaOffset: { + U64 offset = 0; + U64 offset_size = str8_deserial_read_uleb128(data, cursor, &offset); + if (offset_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += offset_size; + + operands[0].u64 = offset; + } break; + case DW_CFA_DefCfaOffsetSf: { + U64 offset = 0; + U64 offset_size = str8_deserial_read_uleb128(data, cursor, &offset); + if (offset_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += offset_size; + + operands[0].u64 = offset * data_align_factor; + } break; + case DW_CFA_DefCfaExpr: { + U64 expr_size = 0; + U64 expr_size_size = str8_deserial_read_uleb128(data, cursor, &expr_size); + if (expr_size_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += expr_size_size; + + if (cursor + expr_size > data.size) { goto exit; } + String8 expr = str8_prefix(str8_skip(data, cursor), expr_size); + + operands[0].block = expr; + cursor += expr_size; + } break; + case DW_CFA_Undefined: { + U64 reg = 0; + U64 reg_size = str8_deserial_read_uleb128(data, cursor, ®); + if (reg_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += reg_size; + + operands[0].u64 = reg; + } break; + case DW_CFA_SameValue: { + U64 reg = 0; + U64 reg_size = str8_deserial_read_uleb128(data, cursor, ®); + if (reg_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += reg_size; + + operands[0].u64 = reg; + } break; + case DW_CFA_Offset: { + U64 offset = 0; + U64 offset_size = str8_deserial_read_uleb128(data, cursor, &offset); + if (offset_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += offset_size; + + operands[0].u64 = implicit_operand; + operands[1].s64 = (S64)offset * data_align_factor; + } break; + case DW_CFA_OffsetExt: { + U64 reg = 0; + U64 reg_size = str8_deserial_read_uleb128(data, cursor, ®); + if (reg_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += reg_size; + + U64 offset = 0; + U64 offset_size = str8_deserial_read_uleb128(data, cursor, &offset); + if (offset_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += offset_size; + + operands[0].u64 = reg; + operands[1].u64 = offset * data_align_factor; + } break; + case DW_CFA_OffsetExtSf: { + U64 reg = 0; + U64 reg_size = str8_deserial_read_uleb128(data, cursor, ®); + if (reg_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += reg_size; + + S64 offset = 0; + U64 offset_size = str8_deserial_read_sleb128(data, cursor, &offset); + if (offset_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += offset_size; + + operands[0].u64 = reg; + operands[1].s64 = offset * data_align_factor; + } break; + case DW_CFA_ValOffset: { + U64 val = 0; + U64 val_size = str8_deserial_read_uleb128(data, cursor, &val); + if (val_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += val_size; + + U64 offset = 0; + U64 offset_size = str8_deserial_read_uleb128(data, cursor, &offset); + if (offset_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += offset_size; + + operands[0].u64 = val; + operands[1].u64 = offset * data_align_factor; + } break; + case DW_CFA_ValOffsetSf: { + U64 val = 0; + U64 val_size = str8_deserial_read_uleb128(data, cursor, &val); + if (val_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += val_size; + + S64 offset = 0; + U64 offset_size = str8_deserial_read_sleb128(data, cursor, &offset); + if (offset_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += offset_size; + + operands[0].u64 = val; + operands[1].s64 = offset; + } break; + case DW_CFA_Register: { + U64 dst_reg = 0; + U64 dst_reg_size = str8_deserial_read_uleb128(data, cursor, &dst_reg); + if (dst_reg_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += dst_reg_size; + + U64 src_reg = 0; + U64 src_reg_size = str8_deserial_read_uleb128(data, cursor, &src_reg); + if (src_reg_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += src_reg_size; + + operands[0].u64 = dst_reg; + operands[1].u64 = src_reg; + } break; + case DW_CFA_Expr: { + U64 reg = 0; + U64 reg_size = str8_deserial_read_uleb128(data, cursor, ®); + if (reg_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += reg_size; + + U64 expr_size = 0; + U64 expr_size_size = str8_deserial_read_uleb128(data, cursor, &expr_size); + if (expr_size_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += expr_size_size; + + if (cursor + expr_size > data.size) { goto exit; } + String8 expr = str8_prefix(str8_skip(data, cursor), expr_size); + cursor += expr_size; + + operands[0].block = expr; + } break; + case DW_CFA_ValExpr: { + U64 val = 0; + U64 val_size = str8_deserial_read_uleb128(data, cursor, &val); + if (val_size == 0) { goto exit; } + cursor += val_size; + + U64 expr_size = 0; + U64 expr_size_size = str8_deserial_read_uleb128(data, cursor, &expr_size); + if (expr_size_size == 0) { error_code = DW_CFA_ParseErrorCode_OutOfData; goto exit; } + cursor += expr_size_size; + + if (cursor + expr_size > data.size) { goto exit; } + String8 expr = str8_prefix(str8_skip(data, cursor), expr_size); + cursor += expr_size; + + operands[0].u64 = val; + operands[1].block = expr; + } break; + case DW_CFA_Restore: { + operands[0].u64 = implicit_operand; + } break; + case DW_CFA_RestoreExt: {} break; + case DW_CFA_RememberState: {} break; + case DW_CFA_RestoreState: {} break; + case DW_CFA_Nop: {} break; + default: { NotImplemented; goto exit; } break; + } + + // fill out output + inst_out->opcode = opcode; + MemoryCopyTyped(&inst_out->operands[0], &operands[0], DW_CFA_OperandMax); + + *bytes_read_out = cursor; + + error_code = DW_CFA_ParseErrorCode_NewInst; + +exit:; + return error_code; +} + +internal DW_CFA_InstList +dw_parse_cfa_inst_list(Arena *arena, + String8 data, + U64 code_align_factor, + S64 data_align_factor, + DW_DecodePtr *decode_ptr_func, + void *decode_ptr_ud) +{ + U64 pos = arena_pos(arena); + DW_CFA_InstList list = {0}; + for (U64 cursor = 0, inst_size;; cursor += inst_size) { + DW_CFA_Inst inst = {0}; + DW_CFA_ParseErrorCode error_code = dw_parse_cfa_inst(str8_skip(data, cursor), code_align_factor, data_align_factor, decode_ptr_func, decode_ptr_ud, &inst_size, &inst); + if (error_code == DW_CFA_ParseErrorCode_End) { break; } + if (error_code != DW_CFA_ParseErrorCode_NewInst) { + MemoryZeroStruct(&list); + arena_pop_to(arena, pos); + break; + } + dw_cfa_inst_list_push(arena, &list, inst); + } + return list; +} + +internal +DW_DECODE_PTR(dw_decode_ptr_debug_frame) +{ + return dw_read_debug_frame_ptr(data, ud, ptr_out); +} + diff --git a/src/dwarf/dwarf_parse.h b/src/dwarf/dwarf_parse.h index b22ac96f..c691c38b 100644 --- a/src/dwarf/dwarf_parse.h +++ b/src/dwarf/dwarf_parse.h @@ -370,9 +370,9 @@ typedef struct DW_DescriptorEntry Rng1U64 entry_range; } DW_DescriptorEntry; -typedef struct DW_UnpackedCIE +typedef struct DW_CIE { - String8 init_insts; + String8 insts; String8 aug_string; String8 aug_data; U64 code_align_factor; @@ -382,25 +382,55 @@ typedef struct DW_UnpackedCIE U8 version; U8 address_size; U8 segment_selector_size; - Rng1U64 cfi_range; -} DW_UnpackedCIE; +} DW_CIE; -typedef struct DW_UnpackedFDE +typedef struct DW_FDE { - DW_Format format; - U64 cie_pointer; - Rng1U64 pc_range; - String8 insts; - Rng1U64 cfi_range; -} DW_UnpackedFDE; + DW_Format format; + U64 cie_pointer; + Rng1U64 pc_range; + String8 insts; +} DW_FDE; -#define DW_READ_CFI_PTR(name) U64 name(String8 data, U64 off, void *ud, U64 *ptr_out) -typedef DW_READ_CFI_PTR(DW_ReadCfiPtrFunc); +typedef union DW_CFA_Operand +{ + U64 u64; + S64 s64; + String8 block; +} DW_CFA_Operand; -#define DW_CIE_FROM_OFFSET_FUNC(name) DW_UnpackedCIE * name(void *ud, U64 offset) +typedef enum +{ + DW_CFA_ParseErrorCode_NewInst, + DW_CFA_ParseErrorCode_End, + DW_CFA_ParseErrorCode_OutOfData +} DW_CFA_ParseErrorCode; + +typedef struct DW_CFA_Inst +{ + DW_CFA_Opcode opcode; + DW_CFA_Operand operands[DW_CFA_OperandMax]; +} DW_CFA_Inst; + +typedef struct DW_CFA_InstNode +{ + DW_CFA_Inst v; + struct DW_CFA_InstNode *next; +} DW_CFA_InstNode; + +typedef struct DW_CFA_InstList +{ + U64 count; + DW_CFA_InstNode *first; + DW_CFA_InstNode *last; +} DW_CFA_InstList; + +#define DW_DECODE_PTR(name) U64 name(String8 data, void *ud, U64 *ptr_out) +typedef DW_DECODE_PTR(DW_DecodePtr); + +#define DW_CIE_FROM_OFFSET_FUNC(name) DW_CIE * name(void *ud, U64 offset) typedef DW_CIE_FROM_OFFSET_FUNC(DW_CIEFromOffsetFunc); - // hasher internal U64 dw_hash_from_string(String8 string); @@ -520,8 +550,16 @@ internal DW_Expr dw_expr_from_data(Arena *arena, DW_Format format, U64 addr_size // debug frame +internal void dw_cfa_inst_list_push_node(DW_CFA_InstList *list, DW_CFA_InstNode *n); +internal DW_CFA_InstNode * dw_cfa_inst_list_push(Arena *arena, DW_CFA_InstList *list, DW_CFA_Inst v); + +internal U64 dw_read_debug_frame_ptr(String8 data, DW_CIE *cie, U64 *ptr_out); + internal U64 dw_parse_descriptor_entry_header(String8 data, U64 off, DW_DescriptorEntry *desc_out); -internal B32 dw_unpack_cie(String8 data, DW_Format format, Arch arch, DW_UnpackedCIE *cie_out); -internal B32 dw_unpack_fde(String8 data, DW_Format format, DW_CIEFromOffsetFunc *cie_from_offset_func, void *cie_from_offset_ud, DW_UnpackedFDE *fde_out); +internal B32 dw_parse_cie(String8 data, DW_Format format, Arch arch, DW_CIE *cie_out); +internal B32 dw_parse_fde(String8 data, DW_Format format, DW_CIEFromOffsetFunc *cie_from_offset_func, void *cie_from_offset_ud, DW_FDE *fde_out); + +internal DW_CFA_ParseErrorCode dw_parse_cfa_inst(String8 data, U64 code_align_factor, S64 data_align_factor, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud, U64 *bytes_read_out, DW_CFA_Inst *inst_out); +internal DW_CFA_InstList dw_parse_cfa_inst_list(Arena *arena, String8 data, U64 code_align_factor, S64 data_align_factor, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud); #endif // DWARF_PARSE_H diff --git a/src/dwarf/dwarf_unwind.c b/src/dwarf/dwarf_unwind.c index 5165da1f..6946d114 100644 --- a/src/dwarf/dwarf_unwind.c +++ b/src/dwarf/dwarf_unwind.c @@ -1,1072 +1,274 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -internal U64 dw_based_range_read(void *base, Rng1U64 range, U64 off, U64 size, void *out) { return 0; } -internal U64 dw_based_range_read_uleb128(void *base, Rng1U64 range, U64 off, U64 *out) { return 0; } -internal U64 dw_based_range_read_sleb128(void *base, Rng1U64 range, U64 off, S64 *out) { return 0; } -internal U64 dw_based_range_read_length(void *base, Rng1U64 range, U64 off, U64 *out) { return 0; } - -//////////////////////////////// -// x64 Unwind Function - -internal DW_UnwindResult -dw_unwind_x64(String8 raw_text, - String8 raw_eh_frame, - String8 raw_eh_frame_hdr, - Rng1U64 text_vrange, - Rng1U64 eh_frame_vrange, - Rng1U64 eh_frame_hdr_vrange, - U64 default_image_base, - U64 image_base, - U64 stack_pointer, - DW_RegsX64 *regs, - DW_ReadMemorySig *read_memory, - void *read_memory_ud) +internal DW_CFA_Row * +dw_make_cfa_row(Arena *arena, U64 reg_count) { - // TODO: What if ELF has two sections with instructions and pointer is ecnoded relative to .text2? - Temp scratch = scratch_begin(0, 0); - - DW_UnwindResult result = {0}; - - dw_unwind_init_x64(); - - // rebase - U64 rebase_voff_to_vaddr = (image_base - default_image_base); - - // get ip register values - U64 ip_value = regs->rip; - U64 ip_voff = ip_value - rebase_voff_to_vaddr; - - // check sections - B32 has_needed_sections = (raw_text.size > 0 && raw_eh_frame.size > 0); - if (!has_needed_sections) { - result.is_invalid = 1; + DW_CFA_Row *row = push_array(arena, DW_CFA_Row, 1); + row->regs = push_array(arena, DW_CFI_Register, reg_count); + for EachIndex(reg_idx, reg_count) { + row->regs[reg_idx].rule = DW_CFI_RegisterRule_SameValue; } - - //- get frame info range - void *frame_base = raw_eh_frame.str; - Rng1U64 frame_range = rng_1u64(0, raw_eh_frame.size); - - //- section vaddrs - U64 text_base_vaddr = text_vrange.min + rebase_voff_to_vaddr; - U64 frame_base_voff = text_vrange.min; - U64 data_base_vaddr = eh_frame_hdr_vrange.min + rebase_voff_to_vaddr; - - //- find cfi records - DW_CFIRecords cfi_recs = {0}; - if (has_needed_sections) { - EH_PtrCtx ptr_ctx = {0}; - ptr_ctx.raw_base_vaddr = frame_base_voff; - ptr_ctx.text_vaddr = text_base_vaddr; - ptr_ctx.data_vaddr = data_base_vaddr; - ptr_ctx.func_vaddr = 0; - if (raw_eh_frame_hdr.size) { - cfi_recs = dw_unwind_eh_frame_hdr_from_ip_fast_x64(raw_eh_frame, raw_eh_frame_hdr, &ptr_ctx, ip_voff); - } else { - cfi_recs = dw_unwind_eh_frame_cfi_from_ip_slow_x64(raw_eh_frame, &ptr_ctx, ip_voff); - } - } - - //- check cfi records - if (!cfi_recs.valid) { - result.is_invalid = 1; - } - - //- cfi machine setup - DW_CFIMachine machine = {0}; - if (cfi_recs.valid) { - EH_PtrCtx ptr_ctx = {0}; - ptr_ctx.raw_base_vaddr = frame_base_voff; - ptr_ctx.text_vaddr = text_base_vaddr; - ptr_ctx.data_vaddr = data_base_vaddr; - ptr_ctx.func_vaddr = cfi_recs.fde.pc_range.min + rebase_voff_to_vaddr; // TODO: it's not super clear how to set up this member, need more test cases - machine = dw_unwind_make_machine_x64(DW_UNWIND_X64__REG_SLOT_COUNT, &cfi_recs.cie, &ptr_ctx); - } - - // initial row - DW_CFIRow *init_row = 0; - if (cfi_recs.valid) { - Rng1U64 init_cfi_range = cfi_recs.cie.cfi_range; - DW_CFIRow *row = dw_unwind_row_alloc_x64(scratch.arena, machine.cells_per_row); - if (dw_unwind_machine_run_to_ip_x64(frame_base, init_cfi_range, &machine, max_U64, row)) { - init_row = row; - } - if (init_row == 0) { - result.is_invalid = 1; - } - } - - // main row - DW_CFIRow *main_row = 0; - if (init_row != 0) { - // upgrade machine with new equipment - dw_unwind_machine_equip_initial_row_x64(&machine, init_row); - dw_unwind_machine_equip_fde_ip_x64(&machine, cfi_recs.fde.pc_range.min); - - // decode main row - Rng1U64 main_cfi_range = cfi_recs.fde.cfi_range; - DW_CFIRow *row = dw_unwind_row_alloc_x64(scratch.arena, machine.cells_per_row); - if (dw_unwind_machine_run_to_ip_x64(frame_base, main_cfi_range, &machine, ip_value, row)) { - main_row = row; - } - if (main_row == 0) { - result.is_invalid = 1; - } - } - - // apply main row to modify the registers - if (main_row != 0) { - result = dw_unwind_x64__apply_frame_rules(raw_eh_frame, main_row, text_base_vaddr, read_memory, read_memory_ud, stack_pointer, regs); - } - + return row; +} + +internal DW_CFA_Row * +dw_copy_cfa_row(Arena *arena, U64 reg_count, DW_CFA_Row *row) +{ + DW_CFA_Row *new_row = dw_make_cfa_row(arena, reg_count); + new_row->cfa = row->cfa; + MemoryCopyTyped(new_row->regs, row->regs, reg_count); + return new_row; +} + +internal DW_CFI_Unwind * +dw_cfi_unwind_init(Arena *arena, + Arch arch, + DW_CIE *cie, + DW_FDE *fde, + DW_DecodePtr *decode_ptr_func, + void *decode_ptr_ud) +{ + Temp scratch = scratch_begin(&arena, 1); + + DW_CFI_Unwind *uw = push_array(arena, DW_CFI_Unwind, 1); + uw->insts = dw_parse_cfa_inst_list(arena, fde->insts, cie->code_align_factor, cie->data_align_factor, decode_ptr_func, decode_ptr_ud); + uw->cie = cie; + uw->fde = fde; + uw->reg_count = dw_reg_count_from_arch(arch); + uw->pc = fde->pc_range.min; + uw->arch = arch; + + // setup initial register rules + DW_CFA_InstList initial_insts = dw_parse_cfa_inst_list(scratch.arena, cie->insts, cie->code_align_factor, cie->data_align_factor, decode_ptr_func, decode_ptr_ud); + uw->row = dw_make_cfa_row(arena, uw->reg_count); + uw->curr_inst = initial_insts.first; + dw_cfi_next_row(arena, uw); + + // make first row from initial rules + uw->initial_row = uw->row; + uw->row = dw_copy_cfa_row(arena, uw->reg_count, uw->initial_row); + uw->curr_inst = uw->insts.first; + scratch_end(scratch); - return result; -} - -internal DW_UnwindResult -dw_unwind_x64__apply_frame_rules(String8 raw_eh_frame, - DW_CFIRow *row, - U64 text_base_vaddr, - DW_ReadMemorySig *read_memory, - void *read_memory_ud, - U64 stack_pointer, - DW_RegsX64 *regs) -{ - DW_UnwindResult result = {0}; - - U64 missed_read_addr = 0; - - //- setup a dwarf expression machine - DW_ExprMachineConfig dwexpr_config = {0}; - dwexpr_config.max_step_count = 0xFFFF; - dwexpr_config.read_memory = read_memory; - dwexpr_config.read_memory_ud = read_memory_ud; - dwexpr_config.regs = regs; - dwexpr_config.text_section_base = &text_base_vaddr; - - //- compute cfa - U64 cfa = 0; - switch (row->cfa_cell.rule) { - case DW_CFI_CFA_Rule_RegOff: { - // TODO: have we done anything to gaurantee reg_idx here? - U64 reg_idx = row->cfa_cell.reg_idx; - - // is this a roll-over CFA? - B32 is_roll_over_cfa = 0; - if (reg_idx == DW_RegX64_Rsp) { - DW_CFIRegisterRule rule = row->cells[reg_idx].rule; - if (rule == DW_CFIRegisterRule_Undefined || rule == DW_CFIRegisterRule_SameValue) { - is_roll_over_cfa = 1; - } - } - - // compute cfa - if (is_roll_over_cfa) { - cfa = stack_pointer + row->cfa_cell.offset; - } else { - cfa = regs->r[reg_idx] + row->cfa_cell.offset; - } - } break; - - case DW_CFI_CFA_Rule_Expr: { - Rng1U64 expr_range = row->cfa_cell.expr; - DW_Location location = dw_expr__eval(0, raw_eh_frame.str, expr_range, &dwexpr_config); - if (location.non_piece_loc.kind == DW_SimpleLocKind_Fail && location.non_piece_loc.fail_kind == DW_LocFailKind_MissingMemory) { - missed_read_addr = location.non_piece_loc.fail_data; - goto error_out; - } - if (location.non_piece_loc.kind == DW_SimpleLocKind_Address) { - cfa = location.non_piece_loc.addr; - } - } break; - } - - // compute registers - { - DW_CFICell *cell = row->cells; - DW_RegsX64 new_regs = {0}; - for (U64 i = 0; i < DW_UNWIND_X64__REG_SLOT_COUNT; ++i, ++cell) { - // compute value - U64 v = 0; - switch (cell->rule) { - default: - { - Assert(!"UNEXPECTED-RULE"); - } break; - - case DW_CFIRegisterRule_Undefined: - { - Assert(!"UNDEFINED"); - } break; - - case DW_CFIRegisterRule_SameValue: - { - v = regs->r[i]; - } break; - - case DW_CFIRegisterRule_Offset: - { - U64 addr = cfa + cell->n; - U64 read_size = read_memory(addr, sizeof(v), &v, read_memory_ud); - if (read_size != sizeof(v)) { - missed_read_addr = addr; - goto error_out; - } - } break; - - case DW_CFIRegisterRule_ValOffset: - { - v = cfa + cell->n; - } break; - - case DW_CFIRegisterRule_Register: - { - v = regs->r[i]; - } break; - - case DW_CFIRegisterRule_Expression: - { - Rng1U64 expr_range = cell->expr; - U64 addr = 0; - DW_Location location = dw_expr__eval(0, raw_eh_frame.str, expr_range, &dwexpr_config); - if (location.non_piece_loc.kind == DW_SimpleLocKind_Fail && location.non_piece_loc.fail_kind == DW_LocFailKind_MissingMemory) { - missed_read_addr = location.non_piece_loc.fail_data; - goto error_out; - } - if (location.non_piece_loc.kind == DW_SimpleLocKind_Address) { - addr = location.non_piece_loc.addr; - } - U64 read_size = read_memory(addr, sizeof(v), &v, read_memory_ud); - if (read_size != sizeof(v)) { - missed_read_addr = addr; - goto error_out; - } - } break; - - case DW_CFIRegisterRule_ValExpression: - { - Rng1U64 expr_range = cell->expr; - DW_Location location = dw_expr__eval(0, raw_eh_frame.str, expr_range, &dwexpr_config); - if (location.non_piece_loc.kind == DW_SimpleLocKind_Fail && location.non_piece_loc.fail_kind == DW_LocFailKind_MissingMemory) { - missed_read_addr = location.non_piece_loc.fail_data; - goto error_out; - } - if (location.non_piece_loc.kind == DW_SimpleLocKind_Address) { - v = location.non_piece_loc.addr; - } - } break; - } - - // commit value to output slot - new_regs.r[i] = v; - } - - // commit all new regs - MemoryCopy(regs, &new_regs, sizeof(new_regs)); - } - - //- save new stack pointer - result.stack_pointer = cfa; - - error_out:; - if (missed_read_addr) { - result.is_invalid = 1; - result.missed_read = 1; - result.missed_read_addr = missed_read_addr; - } - - return result; -} - -//////////////////////////////// -// Helper Functions - -internal void -dw_unwind_init_x64(void) -{ - local_persist B32 did_init = 0; - - if (!did_init) { - did_init = 1; - - // control bits tables - dw_unwind__cfa_control_bits_kind1[DW_CFA_Nop ] = 0x000; - dw_unwind__cfa_control_bits_kind1[DW_CFA_SetLoc ] = 0x809; - dw_unwind__cfa_control_bits_kind1[DW_CFA_AdvanceLoc1 ] = 0x801; - dw_unwind__cfa_control_bits_kind1[DW_CFA_AdvanceLoc2 ] = 0x802; - dw_unwind__cfa_control_bits_kind1[DW_CFA_AdvanceLoc4 ] = 0x804; - dw_unwind__cfa_control_bits_kind1[DW_CFA_OffsetExt ] = 0x2AA; - dw_unwind__cfa_control_bits_kind1[DW_CFA_RestoreExt ] = 0x20A; - dw_unwind__cfa_control_bits_kind1[DW_CFA_Undefined ] = 0x20A; - dw_unwind__cfa_control_bits_kind1[DW_CFA_SameValue ] = 0x20A; - dw_unwind__cfa_control_bits_kind1[DW_CFA_Register ] = 0x6AA; - dw_unwind__cfa_control_bits_kind1[DW_CFA_RememberState ] = 0x000; - dw_unwind__cfa_control_bits_kind1[DW_CFA_RestoreState ] = 0x000; - dw_unwind__cfa_control_bits_kind1[DW_CFA_DefCfa ] = 0x2AA; - dw_unwind__cfa_control_bits_kind1[DW_CFA_DefCfaRegister ] = 0x20A; - dw_unwind__cfa_control_bits_kind1[DW_CFA_DefCfaOffset ] = 0x00A; - dw_unwind__cfa_control_bits_kind1[DW_CFA_DefCfaExpr ] = 0x00A; - dw_unwind__cfa_control_bits_kind1[DW_CFA_Expr ] = 0x2AA; - dw_unwind__cfa_control_bits_kind1[DW_CFA_OffsetExtSf ] = 0x2BA; - dw_unwind__cfa_control_bits_kind1[DW_CFA_DefCfaSf ] = 0x2BA; - dw_unwind__cfa_control_bits_kind1[DW_CFA_DefCfaOffsetSf ] = 0x00B; - dw_unwind__cfa_control_bits_kind1[DW_CFA_ValOffset ] = 0x2AA; - dw_unwind__cfa_control_bits_kind1[DW_CFA_ValOffsetSf ] = 0x2BA; - dw_unwind__cfa_control_bits_kind1[DW_CFA_ValExpr ] = 0x2AA; - - dw_unwind__cfa_control_bits_kind2[DW_CFA_AdvanceLoc >> 6] = 0x800; - dw_unwind__cfa_control_bits_kind2[DW_CFA_Offset >> 6] = 0x10A; - dw_unwind__cfa_control_bits_kind2[DW_CFA_Restore >> 6] = 0x100; - } -} - -//- eh_frame parsing - -internal void -dw_unwind_parse_cie_x64(void *base, Rng1U64 range, EH_PtrCtx *ptr_ctx, U64 off, DW_UnpackedCIE *cie_out) -{ - NotImplemented; -#if 0 - MemoryZeroStruct(cie_out); - - // get version - U64 version_off = off; - U8 version = 0; - dw_based_range_read(base, range, version_off, 1, &version); - - // check version - if (version == 1 || version == 3) { - - // read augmentation - U64 augmentation_off = version_off + 1; - String8 augmentation = dw_based_range_read_string(base, range, augmentation_off); - - // read code align - U64 code_align_factor_off = augmentation_off + augmentation.size + 1; - U64 code_align_factor = 0; - U64 code_align_factor_size = dw_based_range_read_uleb128(base, range, code_align_factor_off, &code_align_factor); - - // read data align - U64 data_align_factor_off = code_align_factor_off + code_align_factor_size; - S64 data_align_factor = 0; - U64 data_align_factor_size = dw_based_range_read_sleb128(base, range, data_align_factor_off, &data_align_factor); - - // return address register - U64 ret_addr_reg_off = data_align_factor_off + data_align_factor_size; - U64 after_ret_addr_reg_off = 0; - U64 ret_addr_reg = 0; - if (version == 1) { - dw_based_range_read(base, range, ret_addr_reg_off, 1, &ret_addr_reg); - after_ret_addr_reg_off = ret_addr_reg_off + 1; - } else { - U64 ret_addr_reg_size = dw_based_range_read_uleb128(base, range, ret_addr_reg_off, &ret_addr_reg); - after_ret_addr_reg_off = ret_addr_reg_off + ret_addr_reg_size; - } - - // TODO: - // Handle "eh" param, it indicates presence of EH Data field. - // On 32bit arch it is a 4-byte and on 64-bit 8-byte value. - // Reference: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html - // Reference doc doesn't clarify structure for EH Data though - - // check for augmentation data - U64 aug_size_off = after_ret_addr_reg_off; - U64 after_aug_size_off = after_ret_addr_reg_off; - B32 has_augmentation_size = 0; - U64 augmentation_size = 0; - if (augmentation.size > 0 && augmentation.str[0] == 'z') { - has_augmentation_size = 1; - U64 aug_size_size = dw_based_range_read_uleb128(base, range, aug_size_off, &augmentation_size); - after_aug_size_off += aug_size_size; - } - - // read augmentation data - U64 aug_data_off = after_aug_size_off; - U64 after_aug_data_off = after_aug_size_off; - - EH_PtrEnc lsda_encoding = EH_PtrEnc_Omit; - U64 handler_ip = 0; - EH_PtrEnc addr_encoding = EH_PtrEnc_UData8; - - if (has_augmentation_size > 0) { - U64 aug_data_cursor = aug_data_off; - for (U8 *ptr = augmentation.str + 1, *opl = augmentation.str + augmentation.size; ptr < opl; ++ptr) { - switch (*ptr) { - case 'L': { - dw_based_range_read_struct(base, range, aug_data_cursor, &lsda_encoding); - aug_data_cursor += sizeof(lsda_encoding); - } break; - case 'P': { - EH_PtrEnc handler_encoding = EH_PtrEnc_Omit; - dw_based_range_read_struct(base, range, aug_data_cursor, &handler_encoding); - - U64 ptr_off = aug_data_cursor + sizeof(handler_encoding); - U64 ptr_size = dw_unwind_parse_pointer_x64(base, range, ptr_ctx, handler_encoding, ptr_off, &handler_ip); - aug_data_cursor = ptr_off + ptr_size; - } break; - case 'R': { - dw_based_range_read_struct(base, range, aug_data_cursor, &addr_encoding); - aug_data_cursor += sizeof(addr_encoding); - } break; - default: { - goto dbl_break_aug; - } break; - } - } - dbl_break_aug:; - after_aug_data_off = aug_data_cursor; - } - - // cfi range - U64 cfi_off = range.min + after_aug_data_off; - U64 cfi_size = 0; - if (range.max > cfi_off) { - cfi_size = range.max - cfi_off; - } - - // commit values to out - cie_out->version = version; - - cie_out->lsda_encoding = lsda_encoding; - cie_out->addr_encoding = addr_encoding; - cie_out->has_augmentation_size = has_augmentation_size; - cie_out->augmentation_size = augmentation_size; - cie_out->augmentation = augmentation; - cie_out->code_align_factor = code_align_factor; - cie_out->data_align_factor = data_align_factor; - cie_out->ret_addr_reg = ret_addr_reg; - cie_out->handler_ip = handler_ip; - cie_out->cfi_range.min = cfi_off; - cie_out->cfi_range.max = cfi_off + cfi_size; - } -#endif -} - -internal DW_CFIRecords -dw_unwind_eh_frame_cfi_from_ip_slow_x64(String8 raw_eh_frame, EH_PtrCtx *ptr_ctx, U64 ip_voff) -{ - Temp scratch = scratch_begin(0, 0); - - DW_CFIRecords result = {0}; - - DW_UnpackedCIENode *cie_first = 0; - DW_UnpackedCIENode *cie_last = 0; - - U64 cursor = 0; - for (;;) { - // CIE/FDE size - U64 rec_off = cursor; - U64 after_rec_size_off = 0; - U64 rec_size = 0; - - { - str8_deserial_read(raw_eh_frame, rec_off, &rec_size, 4, 1); - after_rec_size_off = 4; - if (rec_size == max_U32) { - str8_deserial_read(raw_eh_frame, rec_off + 4, &rec_size, 8, 1); - after_rec_size_off = 12; - } - } - - // zero size is the end of the loop - if (rec_size == 0) { - break; - } - - // compute end offset - U64 rec_opl = rec_off + after_rec_size_off + rec_size; - - // sub-range the rest of the reads - Rng1U64 rec_range = rng_1u64(rec_off, rec_opl); - String8 raw_rec = str8_substr(raw_eh_frame, rec_range); - - - // discriminator - U64 discrim_off = after_rec_size_off; - U32 discrim = 0; - str8_deserial_read(raw_rec, discrim_off, &discrim, 4, 1); - - U64 after_discrim_off = discrim_off + 4; - - // CIE - if (discrim == 0) { - DW_UnpackedCIE cie = {0}; - dw_unwind_parse_cie_x64(raw_rec.str, rng_1u64(0, raw_rec.size), ptr_ctx, after_discrim_off, &cie); - if (cie.version != 0) { - DW_UnpackedCIENode *node = push_array(scratch.arena, DW_UnpackedCIENode, 1); - node->cie = cie; - node->offset = rec_off; - SLLQueuePush(cie_first, cie_last, node); - } - } - // FDE - else { - // compute cie offset - U64 cie_offset = rec_range.min + discrim_off - discrim; - - // get cie node - DW_UnpackedCIENode *cie_node = 0; - for (DW_UnpackedCIENode *node = cie_first; node != 0; node = node->next) { - if (node->offset == cie_offset) { - cie_node = node; - break; - } - } - - // parse fde - DW_UnpackedFDE fde = {0}; - if (cie_node != 0) { - NotImplemented; - //dw_unwind_parse_fde_x64(raw_rec.str, rng_1u64(0,raw_rec.size), ptr_ctx, &cie_node->cie, after_discrim_off, &fde); - } - - if (contains_1u64(fde.pc_range, ip_voff)) { - result.valid = 1; - result.cie = cie_node->cie; - result.fde = fde; - break; - } - } - - // advance cursor - cursor = rec_opl; - } - - scratch_end(scratch); - - return(result); -} - -internal U64 -dw_search_eh_frame_hdr_linear_x64(String8 raw_eh_frame_hdr, EH_PtrCtx *ptr_ctx, U64 location) -{ - // Table contains only addresses for first instruction in a function and we cannot - // guarantee that result is FDE that corresponds to the input location. - // So input location must be cheked against range from FDE header again. - - U64 closest_location = max_U64; - U64 closest_address = max_U64; - - U64 cursor = 0; - - U8 version = 0; - cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &version); - - if (version == 1) { -#if 0 - EH_PtrCtx ptr_ctx = {0}; - // Set this to base address of .eh_frame_hdr. Entries are relative - // to this section for some reason. - ptr_ctx.data_vaddr = range.min; - // If input location is VMA then set this to address of .text. - // Pointer parsing function will adjust "init_location" to correct VMA. - ptr_ctx.text_vaddr = 0; -#endif - - EH_PtrEnc eh_frame_ptr_enc = 0, fde_count_enc = 0, table_enc = 0; - cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &eh_frame_ptr_enc); - cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &fde_count_enc); - cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &table_enc); - - U64 eh_frame_ptr = 0, fde_count = 0; - NotImplemented; - //cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, eh_frame_ptr_enc, cursor, &eh_frame_ptr); - //cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, fde_count_enc, cursor, &fde_count); - - for (U64 fde_idx = 0; fde_idx < fde_count; ++fde_idx) { - U64 init_location = 0, address = 0; - NotImplemented; - //cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, table_enc, cursor, &init_location); - //cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, table_enc, cursor, &address); - - S64 current_delta = (S64)(location - init_location); - S64 closest_delta = (S64)(location - closest_location); - if (0 <= current_delta && current_delta < closest_delta) { - closest_location = init_location; - closest_address = address; - } - } - } - - // address where to find corresponding FDE, this is an absolute offset - // into the image file. - return closest_address; -} - -internal DW_CFIRecords -dw_unwind_eh_frame_hdr_from_ip_fast_x64(String8 raw_eh_frame, String8 raw_eh_frame_hdr, EH_PtrCtx *ptr_ctx, U64 ip_voff) -{ - DW_CFIRecords result = {0}; - - // find FDE offset - void *eh_frame_hdr = raw_eh_frame.str; - U64 fde_offset = dw_search_eh_frame_hdr_linear_x64(raw_eh_frame_hdr, ptr_ctx, ip_voff); - - B32 is_fde_offset_valid = (fde_offset != max_U64); - if (is_fde_offset_valid) { - U64 fde_read_offset = (fde_offset - ptr_ctx->raw_base_vaddr); - - // read FDE size - U64 fde_size = 0; - fde_read_offset += dw_based_range_read_length(raw_eh_frame.str, rng_1u64(0,raw_eh_frame.size), fde_read_offset, &fde_size); - - // read FDE discriminator - U32 fde_discrim = 0; - fde_read_offset += str8_deserial_read_struct(raw_eh_frame, fde_read_offset, &fde_discrim); - - // compute parent CIE offset - U64 cie_read_offset = fde_read_offset - (fde_discrim + sizeof(fde_discrim)); - - // read CIE size - U64 cie_size = 0; - cie_read_offset += dw_based_range_read_length(raw_eh_frame.str, rng_1u64(0,raw_eh_frame.size), cie_read_offset, &cie_size); - - // read CIE discriminator - U32 cie_discrim = max_U32; - cie_read_offset += str8_deserial_read_struct(raw_eh_frame, cie_read_offset, &cie_discrim); - - B32 is_fde = (fde_discrim != 0); - B32 is_cie = (cie_discrim == 0); - if (is_fde && is_cie) { - Rng1U64 cie_range = rng_1u64(0, cie_read_offset + (cie_size - sizeof(cie_discrim))); - Rng1U64 fde_range = rng_1u64(0, fde_read_offset + (fde_size - sizeof(fde_discrim))); - - // parse CIE - DW_UnpackedCIE cie = {0}; - dw_unwind_parse_cie_x64(raw_eh_frame.str, cie_range, ptr_ctx, cie_read_offset, &cie); - - // parse FDE - DW_UnpackedFDE fde = {0}; - NotImplemented; - //dw_unwind_parse_fde_x64(raw_eh_frame.str, fde_range, ptr_ctx, &cie, fde_read_offset, &fde); - - // range check instruction pointer - if (contains_1u64(fde.pc_range, ip_voff)) { - result.valid = 1; - result.cie = cie; - result.fde = fde; - } - } - } - - return result; -} - -//- cfi machine - -internal DW_CFIMachine -dw_unwind_make_machine_x64(U64 cells_per_row, DW_UnpackedCIE *cie, EH_PtrCtx *ptr_ctx) -{ - DW_CFIMachine result = {0}; - result.cells_per_row = cells_per_row; - result.cie = cie; - result.ptr_ctx = ptr_ctx; - return result; -} - -internal void -dw_unwind_machine_equip_initial_row_x64(DW_CFIMachine *machine, DW_CFIRow *initial_row) -{ - machine->initial_row = initial_row; -} - -internal void -dw_unwind_machine_equip_fde_ip_x64(DW_CFIMachine *machine, U64 fde_ip) -{ - machine->fde_ip = fde_ip; -} - -internal DW_CFIRow* -dw_unwind_row_alloc_x64(Arena *arena, U64 cells_per_row) -{ - DW_CFIRow *result = push_array(arena, DW_CFIRow, 1); - result->cells = push_array(arena, DW_CFICell, cells_per_row); - return result; -} - -internal void -dw_unwind_row_zero_x64(DW_CFIRow *row, U64 cells_per_row) { - MemorySet(row->cells, 0, sizeof(*row->cells)*cells_per_row); - MemoryZeroStruct(&row->cfa_cell); -} - -internal void -dw_unwind_row_copy_x64(DW_CFIRow *dst, DW_CFIRow *src, U64 cells_per_row) -{ - MemoryCopy(dst->cells, src->cells, sizeof(*src->cells)*cells_per_row); - dst->cfa_cell = src->cfa_cell; + return uw; } internal B32 -dw_unwind_machine_run_to_ip_x64(void *base, Rng1U64 range, DW_CFIMachine *machine, U64 target_ip, DW_CFIRow *row) +dw_cfi_next_row(Arena *arena, DW_CFI_Unwind *uw) { - Temp scratch = scratch_begin(0, 0); - - B32 result = 0; - - // pull out machine's equipment - DW_UnpackedCIE *cie = machine->cie; - EH_PtrCtx *ptr_ctx = machine->ptr_ctx; - U64 cells_per_row = machine->cells_per_row; - DW_CFIRow *initial_row = machine->initial_row; - - // start with an empty stack - DW_CFIRow *stack = 0; - DW_CFIRow *free_rows = 0; - - // initialize the row - if (initial_row != 0) { - dw_unwind_row_copy_x64(row, initial_row, cells_per_row); - } else { - dw_unwind_row_zero_x64(row, cells_per_row); + B32 is_row_valid = 0; + + while (uw->curr_inst) { + DW_CFA_Inst *inst = &uw->curr_inst->v; + + // validate register operands + DW_CFA_OperandType *operand_types = dw_operand_types_from_cfa_op(inst->opcode); + U64 operand_count = dw_operand_count_from_cfa_opcode(inst->opcode); + for EachIndex(operand_idx, operand_count) { + if (operand_types[operand_idx] == DW_CFA_OperandType_Register) { + if (inst->operands[operand_idx].u64 >= uw->reg_count) { + goto exit; + } + } + } + + switch (inst->opcode) { + // Row Creation Instructions + case DW_CFA_SetLoc: { + uw->pc = inst->operands[0].u64; + } break; + case DW_CFA_AdvanceLoc: + case DW_CFA_AdvanceLoc1: + case DW_CFA_AdvanceLoc2: + case DW_CFA_AdvanceLoc4: { + uw->pc += inst->operands[0].u64; + } break; + + // CFA Definition Instructions + case DW_CFA_DefCfa: + case DW_CFA_DefCfaSf: { + U64 reg = inst->operands[0].u64; + S64 off = inst->operands[1].s64; + uw->row->cfa.rule = DW_CFA_Rule_RegOff; + uw->row->cfa.reg = reg; + uw->row->cfa.off = off; + } break; + case DW_CFA_DefCfaRegister: { + // TODO: report error: this operation is valid only if the current CFA rule is defined to register+offset + if (uw->row->cfa.rule != DW_CFA_Rule_RegOff) { goto exit; } + U64 reg = inst->operands[0].u64; + uw->row->cfa.reg = reg; + } break; + case DW_CFA_DefCfaOffset: + case DW_CFA_DefCfaOffsetSf: { + // TODO: report error: this operation is valid only if the current CFA rule is defined to register+offset + if (uw->row->cfa.rule != DW_CFA_Rule_RegOff) { goto exit; } + uw->row->cfa.off = inst->operands[0].s64; + } break; + case DW_CFA_DefCfaExpr: { + uw->row->cfa.rule = DW_CFA_Rule_Expression; + uw->row->cfa.expr = inst->operands[0].block; + } break; + + // Register Rule Instructions + case DW_CFA_Undefined: { + U64 reg = inst->operands[0].u64; + uw->row->regs[reg].rule = DW_CFI_RegisterRule_Undefined; + } break; + case DW_CFA_SameValue: { + U64 reg = inst->operands[0].u64; + uw->row->regs[reg].rule = DW_CFI_RegisterRule_SameValue; + } break; + case DW_CFA_Offset: + case DW_CFA_OffsetExt: + case DW_CFA_OffsetExtSf: { + U64 reg = inst->operands[0].u64; + uw->row->regs[reg].rule = DW_CFI_RegisterRule_Offset; + uw->row->regs[reg].n = inst->operands[1].s64; + } break; + case DW_CFA_ValOffset: + case DW_CFA_ValOffsetSf: { + U64 reg = inst->operands[0].u64; + uw->row->regs[reg].rule = DW_CFI_RegisterRule_ValOffset; + uw->row->regs[reg].n = inst->operands[1].s64; + } break; + case DW_CFA_Register: { + U64 reg = inst->operands[0].u64; + uw->row->regs[reg].rule = DW_CFI_RegisterRule_Register; + uw->row->regs[reg].n = inst->operands[1].s64; + } break; + case DW_CFA_Expr: { + U64 reg = inst->operands[0].u64; + uw->row->regs[reg].rule = DW_CFI_RegisterRule_Expression; + uw->row->regs[reg].expr = inst->operands[1].block; + } break; + case DW_CFA_ValExpr: { + U64 reg = inst->operands[0].u64; + uw->row->regs[reg].rule = DW_CFI_RegisterRule_ValExpression; + uw->row->regs[reg].expr = inst->operands[1].block; + } break; + case DW_CFA_Restore: + case DW_CFA_RestoreExt: { + U64 reg = inst->operands[0].u64; + uw->row->regs[reg] = uw->initial_row->regs[reg]; + } break; + + // Row State Instructions + case DW_CFA_RememberState: { + DW_CFA_Row *new_row = dw_copy_cfa_row(arena, uw->reg_count, uw->row); + SLLStackPush(uw->row, new_row); + } break; + case DW_CFA_RestoreState: { + if (uw->row == 0) { goto exit; } // TODO: report error: unbalanced number of pushes and pops + DW_CFA_Row *free_row = uw->row; + SLLStackPop(uw->row); + SLLStackPush(uw->free_rows, free_row); + } break; + + case DW_CFA_Nop: {} break; + + default: { NotImplemented; } break; // TODO: report error: unknown CFA opcode + } + + is_row_valid = 1; + + uw->curr_inst = uw->curr_inst->next; + if (uw->curr_inst) { + if (dw_is_new_row_cfa_opcode(uw->curr_inst->v.opcode)) { break; } + } } - U64 table_ip = machine->fde_ip; - - // loop - U64 cfi_off = 0; - for (;;) { - // op variables - DW_CFA opcode = 0; - U64 operand0 = 0; - U64 operand1 = 0; - U64 operand2 = 0; - DW_CFAControlBits control_bits = 0; - - // decode opcode/operand0 - if (!dw_based_range_read(base, range, cfi_off, 1, &opcode)) { - result = 1; - goto done; - } - if ((opcode & DW_CFAMask_OpcodeHi) != 0) { - operand0 = (opcode & DW_CFAMask_Operand); - opcode = (opcode & DW_CFAMask_OpcodeHi); - control_bits = dw_unwind__cfa_control_bits_kind2[opcode >> 6]; - } else { - if (opcode < DW_CFA_OplKind1) { - control_bits = dw_unwind__cfa_control_bits_kind1[opcode]; - } - } - - // decode operand1/operand2 - U64 decode_cursor = cfi_off + 1; - { - // setup loop ins/outs - U64 o[2]; - DW_CFADecode dec[2] = {0}; - dec[0] = (control_bits & 0xF); - dec[1] = ((control_bits >> 4) & 0xF); - - // loop - U64 *out = o; - for (U64 i = 0; i < 2; i += 1, out += 1) { - DW_CFADecode d = dec[i]; - U64 o_size = 0; - switch (d) { - case 0: { - *out = 0; - } break; - default: { - if (d <= 8) { - dw_based_range_read(base, range, decode_cursor, d, out); - o_size = d; - } - } break; - case DW_CFADecode_Address: { - // TODO: - NotImplemented; - //o_size = dw_unwind_parse_pointer_x64(base, range, ptr_ctx, cie->addr_encoding, decode_cursor, out); - } break; - case DW_CFADecode_ULEB128: { - o_size = dw_based_range_read_uleb128(base, range, decode_cursor, out); - } break; - case DW_CFADecode_SLEB128: { - o_size = dw_based_range_read_sleb128(base, range, decode_cursor, (S64*)out); - } break; - } - decode_cursor += o_size; - } - - // commit out values - operand1 = o[0]; - operand2 = o[1]; - } - U64 after_decode_off = decode_cursor; - - // register checks - if (control_bits & DW_CFAControlBits_IsReg0) { - if (operand0 >= cells_per_row) { - goto done; - } - } - if (control_bits & DW_CFAControlBits_IsReg1) { - if (operand1 >= cells_per_row) { - goto done; - } - } - if (control_bits & DW_CFAControlBits_IsReg2) { - if (operand2 >= cells_per_row) { - goto done; - } - } - - // values for deferred work - U64 new_table_ip = table_ip; - - // step - U64 step_cursor = after_decode_off; - switch (opcode) { - default: goto done; - case DW_CFA_Nop:break; - - //// new row/IP opcodes //// - - case DW_CFA_SetLoc: { - new_table_ip = operand1; - } break; - case DW_CFA_AdvanceLoc: { - new_table_ip = table_ip + operand0*cie->code_align_factor; - } break; - case DW_CFA_AdvanceLoc1: - case DW_CFA_AdvanceLoc2: - case DW_CFA_AdvanceLoc4: { - U64 advance = operand1*cie->code_align_factor; - new_table_ip = table_ip + advance; - } break; - - //// change CFA (canonical frame address) opcodes //// - - case DW_CFA_DefCfa: { - row->cfa_cell.rule = DW_CFI_CFA_Rule_RegOff; - row->cfa_cell.reg_idx = operand1; - row->cfa_cell.offset = operand2; - } break; - - case DW_CFA_DefCfaSf: { - row->cfa_cell.rule = DW_CFI_CFA_Rule_RegOff; - row->cfa_cell.reg_idx = operand1; - row->cfa_cell.offset = ((S64)operand2)*cie->data_align_factor; - } break; - - case DW_CFA_DefCfaRegister: { - // check rule - if (row->cfa_cell.rule != DW_CFI_CFA_Rule_RegOff) { - goto done; - } - // commit new cfa - row->cfa_cell.reg_idx = operand1; - } break; - - case DW_CFA_DefCfaOffset: { - // check rule - if (row->cfa_cell.rule != DW_CFI_CFA_Rule_RegOff) { - goto done; - } - // commit new cfa - row->cfa_cell.offset = operand1; - } break; - - case DW_CFA_DefCfaOffsetSf: { - // check rule - if (row->cfa_cell.rule != DW_CFI_CFA_Rule_RegOff) { - goto done; - } - // commit new cfa - row->cfa_cell.offset = ((S64)operand1)*cie->data_align_factor; - } break; - - case DW_CFA_DefCfaExpr: { - // setup expr range - U64 expr_first = range.min + after_decode_off; - U64 expr_size = operand1; - step_cursor += expr_size; - - // commit new cfa - row->cfa_cell.rule = DW_CFI_CFA_Rule_Expr; - row->cfa_cell.expr.min = expr_first; - row->cfa_cell.expr.max = expr_first + expr_size; - } break; - - - //// change register rules //// - - case DW_CFA_Undefined: { - row->cells[operand1].rule = DW_CFIRegisterRule_Undefined; - } break; - - case DW_CFA_SameValue: { - row->cells[operand1].rule = DW_CFIRegisterRule_SameValue; - } break; - - case DW_CFA_Offset: { - DW_CFICell *cell = &row->cells[operand0]; - cell->rule = DW_CFIRegisterRule_Offset; - cell->n = operand1*cie->data_align_factor; - } break; - - case DW_CFA_OffsetExt: { - DW_CFICell *cell = &row->cells[operand1]; - cell->rule = DW_CFIRegisterRule_Offset; - cell->n = operand2*cie->data_align_factor; - } break; - - case DW_CFA_OffsetExtSf: { - DW_CFICell *cell = &row->cells[operand1]; - cell->rule = DW_CFIRegisterRule_Offset; - cell->n = ((S64)operand2)*cie->data_align_factor; - } break; - - case DW_CFA_ValOffset: { - DW_CFICell *cell = &row->cells[operand1]; - cell->rule = DW_CFIRegisterRule_ValOffset; - cell->n = operand2*cie->data_align_factor; - } break; - - case DW_CFA_ValOffsetSf: { - DW_CFICell *cell = &row->cells[operand1]; - cell->rule = DW_CFIRegisterRule_ValOffset; - cell->n = ((S64)operand2)*cie->data_align_factor; - } break; - - case DW_CFA_Register: { - DW_CFICell *cell = &row->cells[operand1]; - cell->rule = DW_CFIRegisterRule_Register; - cell->n = operand2; - } break; - - case DW_CFA_Expr: { - // setup expr range - U64 expr_first = range.min + after_decode_off; - U64 expr_size = operand2; - step_cursor += expr_size; - - // commit new rule - DW_CFICell *cell = &row->cells[operand1]; - cell->rule = DW_CFIRegisterRule_Expression; - cell->expr.min = expr_first; - cell->expr.max = expr_first + expr_size; - } break; - - case DW_CFA_ValExpr: { - // setup expr range - U64 expr_first = range.min + after_decode_off; - U64 expr_size = operand2; - step_cursor += expr_size; - - // commit new rule - DW_CFICell *cell = &row->cells[operand1]; - cell->rule = DW_CFIRegisterRule_ValExpression; - cell->expr.min = expr_first; - cell->expr.max = expr_first + expr_size; - } break; - - case DW_CFA_Restore: { - // check initial row - if (initial_row == 0) { - goto done; - } - // commit new rule - row->cells[operand0] = initial_row->cells[operand0]; - } break; - - case DW_CFA_RestoreExt: { - // check initial row - if (initial_row == 0) { - goto done; - } - // commit new rule - row->cells[operand1] = initial_row->cells[operand1]; - } break; - - - //// row stack //// - - case DW_CFA_RememberState: { - DW_CFIRow *stack_row = free_rows; - if (stack_row != 0) { - SLLStackPop(free_rows); - } else { - stack_row = dw_unwind_row_alloc_x64(scratch.arena, cells_per_row); - } - dw_unwind_row_copy_x64(stack_row, row, cells_per_row); - SLLStackPush(stack, stack_row); - } break; - - case DW_CFA_RestoreState: { - if (stack != 0) { - DW_CFIRow *stack_row = stack; - SLLStackPop(stack); - dw_unwind_row_copy_x64(row, stack_row, cells_per_row); - SLLStackPush(free_rows, stack_row); - } else { - dw_unwind_row_zero_x64(row, cells_per_row); - } - } break; - } - - // apply location change - if (control_bits & DW_CFAControlBits_NewRow) { - // new ip should always grow the ip - if (new_table_ip <= table_ip) { - goto done; - } - // stop if this encloses the target ip - if (table_ip <= target_ip && target_ip < new_table_ip) { - result = 1; - goto done; - } - // commit new ip - table_ip = new_table_ip; - } - - // advance - cfi_off = step_cursor; + +exit:; + return is_row_valid; +} + +internal DW_UnwindStatus +dw_cfi_apply_register_rules(DW_CFI_Unwind *uw, + DW_MemRead *mem_read_func, void *mem_read_ud, + DW_RegRead *reg_read_func, void *reg_read_ud, + DW_RegWrite *reg_write_func, void *reg_write_ud) +{ + Temp scratch = scratch_begin(0,0); + DW_UnwindStatus unwind_status = DW_UnwindStatus_Ok; + + DW_CFA_Row *row = uw->row; + + // establish CFA + U64 cfa = 0; + switch (row->cfa.rule) { + case DW_CFA_Rule_Null: break; + case DW_CFA_Rule_RegOff: { + // TODO: report error (invalid register read) + U64 cfa_reg_value = 0; + U64 reg_size = dw_reg_size_from_code(uw->arch, row->cfa.reg); + AssertAlawys(reg_size <= sizeof(cfa_reg_value)); + unwind_status = reg_read_func(row->cfa.reg, &cfa_reg_value, reg_size, reg_read_ud); + if (unwind_status != DW_UnwindStatus_Ok) { goto exit; } + cfa = cfa_reg_value + row->cfa.off; + } break; + case DW_CFA_Rule_Expression: { + // TODO: evaluate expression + } break; + } + + U64 max_reg_size = dw_max_reg_size_from_arch(uw->arch); + void *reg_buffer = push_array(scratch.arena, U8, max_reg_size); + + for EachIndex(reg_idx, uw->reg_count) { + DW_CFI_Register *reg = &uw->row->regs[reg_idx]; + switch (reg->rule) { + case DW_CFI_RegisterRule_Undefined: { + // TODO: ??? + Assert(!"undefined"); + } break; + case DW_CFI_RegisterRule_SameValue: {} break; + case DW_CFI_RegisterRule_Offset: { + // read register value from memory + U64 addr = cfa + reg->n; + U64 reg_size = dw_reg_size_from_code(uw->arch, reg_idx); + unwind_status = mem_read_func(addr, reg_size, reg_buffer, mem_read_ud); + if (unwind_status != DW_UnwindStatus_Ok) { goto exit; } + + // write register value to the thread context + unwind_status = reg_write(reg_idx, reg_buffer, reg_size, reg_write_ud); + if (unwind_status != DW_UnwindStatus_Ok) { goto exit; } + } break; + case DW_CFI_RegisterRule_ValOffset: { + // compute register value + U64 reg_value = cfa + reg->n; + + // write register value to the thread context + U64 reg_size = dw_reg_size_from_code(uw->arch, reg_idx); + unwind_status = reg_write(reg_idx, ®_value, reg_size, reg_write_ud); + if (unwind_status != DW_UnwindStatus_Ok) { goto exit; } + } break; + case DW_CFI_RegisterRule_Register: { + // read register value from another register + U64 reg_size = dw_reg_size_from_code(uw->arch, reg_idx); + unwind_status = reg_read(reg->n, reg_buffer, reg_size, reg_read_ud); + if (unwind_status != DW_UnwindStatus_Ok) { goto exit; } + + // write register value to the thread context + unwind_status = reg_write(reg_idx, reg_buffer, reg_size, reg_write_ud); + if (unwind_status != DW_UnwindStatus_Ok) { goto exit; } + } break; + case DW_CFI_RegisterRule_Expression: { + // TODO: evaluate expression + NotImplemented; + } break; + case DW_CFI_RegisterRule_ValExpression: { + // TODO: evaluate expression + NotImplemented; + } break; + case DW_CFI_RegisterRule_Architectural: { + NotImplemented; + } break; + } } - done:; +exit:; scratch_end(scratch); - return result; -} - -//////////////////////////////// - -internal String8 -dw_string_from_eh_ptr_enc_type(EH_PtrEnc type) -{ - switch (type) { - case EH_PtrEnc_Ptr: return str8_lit("Ptr"); - case EH_PtrEnc_ULEB128: return str8_lit("ULEB128"); - case EH_PtrEnc_UData2: return str8_lit("UData2"); - case EH_PtrEnc_UData4: return str8_lit("UData4"); - case EH_PtrEnc_UData8: return str8_lit("UData8"); - case EH_PtrEnc_Signed: return str8_lit("Signed"); - case EH_PtrEnc_SLEB128: return str8_lit("SLEB128"); - case EH_PtrEnc_SData2: return str8_lit("SData2"); - case EH_PtrEnc_SData4: return str8_lit("SData4"); - case EH_PtrEnc_SData8: return str8_lit("SData8"); - } - return str8_zero(); -} - -internal String8 -dw_string_from_eh_ptr_enc_modifier(EH_PtrEnc modifier) -{ - switch (modifier) { - case EH_PtrEnc_PcRel: return str8_lit("PcRel"); - case EH_PtrEnc_TextRel: return str8_lit("TextRel"); - case EH_PtrEnc_DataRel: return str8_lit("DataRel"); - case EH_PtrEnc_FuncRel: return str8_lit("FuncRel"); - case EH_PtrEnc_Aligned: return str8_lit("Aligned"); - } - return str8_zero(); -} - -internal String8 -dw_string_from_eh_ptr_enc(Arena *arena, EH_PtrEnc enc) -{ - String8 type_str = dw_string_from_eh_ptr_enc_type(enc & EH_PtrEnc_TypeMask); - String8 modifer_str = dw_string_from_eh_ptr_enc_modifier(enc & EH_PtrEnc_ModifierMask); - String8 indir_str = enc & EH_PtrEnc_Indirect ? str8_lit("Indirect") : str8_zero(); - String8 result = str8f(arena, "Type: %S, Modifier %S (%S)", type_str, modifer_str, indir_str); - return result; + return unwind_status; } diff --git a/src/dwarf/dwarf_unwind.h b/src/dwarf/dwarf_unwind.h index 54f7f187..f5fde8c0 100644 --- a/src/dwarf/dwarf_unwind.h +++ b/src/dwarf/dwarf_unwind.h @@ -4,150 +4,90 @@ #ifndef DWARF_UNWIND_H #define DWARF_UNWIND_H -typedef struct DW_UnwindResult +typedef enum DW_CFI_RegisterRule { - B32 is_invalid; - B32 missed_read; - U64 missed_read_addr; - U64 stack_pointer; -} DW_UnwindResult; + DW_CFI_RegisterRule_Undefined, + DW_CFI_RegisterRule_SameValue, + DW_CFI_RegisterRule_Offset, + DW_CFI_RegisterRule_ValOffset, + DW_CFI_RegisterRule_Register, + DW_CFI_RegisterRule_Expression, + DW_CFI_RegisterRule_ValExpression, + DW_CFI_RegisterRule_Architectural +} DW_CFI_RegisterRule; -// EH: Exception Frames - -typedef struct DW_UnpackedCIENode +typedef enum DW_CFA_Rule { - struct DW_UnpackedCIENode *next; - DW_UnpackedCIE cie; - U64 offset; -} DW_UnpackedCIENode; + DW_CFA_Rule_Null, + DW_CFA_Rule_RegOff, + DW_CFA_Rule_Expression +} DW_CFA_Rule; -// CFI: Call Frame Information -typedef struct DW_CFIRecords +typedef struct DW_CFA { - B32 valid; - DW_UnpackedCIE cie; - DW_UnpackedFDE fde; -} DW_CFIRecords; - -typedef enum DW_CFICFARule{ - DW_CFI_CFA_Rule_RegOff, - DW_CFI_CFA_Rule_Expr, -} DW_CFICFARule; - -typedef struct DW_CFICFACell -{ - DW_CFICFARule rule; + DW_CFA_Rule rule; union { struct { - U64 reg_idx; - S64 offset; + DW_Reg reg; + S64 off; }; - Rng1U64 expr; + String8 expr; }; -} DW_CFICFACell; +} DW_CFA; -typedef enum DW_CFIRegisterRule +typedef struct DW_CFI_Register { - DW_CFIRegisterRule_SameValue, - DW_CFIRegisterRule_Undefined, - DW_CFIRegisterRule_Offset, - DW_CFIRegisterRule_ValOffset, - DW_CFIRegisterRule_Register, - DW_CFIRegisterRule_Expression, - DW_CFIRegisterRule_ValExpression, -} DW_CFIRegisterRule; - -typedef struct DW_CFICell -{ - DW_CFIRegisterRule rule; + DW_CFI_RegisterRule rule; union { - S64 n; - Rng1U64 expr; + S64 n; + String8 expr; }; -} DW_CFICell; +} DW_CFI_Register; -typedef struct DW_CFIRow +typedef struct DW_CFA_Row { - struct DW_CFIRow *next; - DW_CFICell *cells; - DW_CFICFACell cfa_cell; -} DW_CFIRow; + DW_CFA cfa; + DW_CFI_Register *regs; + struct DW_CFA_Row *next; +} DW_CFA_Row; -typedef struct DW_CFIMachine +typedef struct DW_CFI_Unwind { - U64 cells_per_row; - DW_UnpackedCIE *cie; - DW_CFIRow *initial_row; - U64 fde_ip; - EH_PtrCtx *ptr_ctx; -} DW_CFIMachine; + DW_CFA_InstList insts; + DW_CIE *cie; + DW_FDE *fde; + DW_CFA_Row *initial_row; + DW_CFA_Row *row; + DW_CFA_InstNode *curr_inst; + DW_CFA_Row *free_rows; + DW_Reg reg_count; + U64 pc; + Arch arch; +} DW_CFI_Unwind; -typedef U8 DW_CFADecode; -enum -{ - DW_CFADecode_Nop = 0x0, - // 1,2,4,8 reserved for literal byte sizes - DW_CFADecode_Address = 0x9, - DW_CFADecode_ULEB128 = 0xA, - DW_CFADecode_SLEB128 = 0xB, -}; +typedef enum { + DW_UnwindStatus_Ok, + DW_UnwindStatus_Fail, + DW_UnwindStatus_Maybe +} DW_UnwindStatus; -typedef U16 DW_CFAControlBits; -enum -{ - DW_CFAControlBits_Dec1Mask = 0x00F, - DW_CFAControlBits_Dec2Mask = 0x0F0, - DW_CFAControlBits_IsReg0 = 0x100, - DW_CFAControlBits_IsReg1 = 0x200, - DW_CFAControlBits_IsReg2 = 0x400, - DW_CFAControlBits_NewRow = 0x800, -}; +#define DW_REG_READ(name) DW_UnwindStatus name(DW_Reg reg_id, void *buffer, U64 buffer_max, void *ud) +typedef DW_REG_READ(DW_RegRead); -global read_only DW_CFAControlBits dw_unwind__cfa_control_bits_kind1[DW_CFA_OplKind1 + 1]; -global read_only DW_CFAControlBits dw_unwind__cfa_control_bits_kind2[DW_CFA_OplKind2 + 1]; +#define DW_REG_WRITE(name) DW_UnwindStatus name(DW_Reg reg_id, void *value, U64 value_size, void *ud) +typedef DW_REG_WRITE(DW_RegWrite); -// register codes for unwinding match the DW_RegX64 register codes -#define DW_UNWIND_X64__REG_SLOT_COUNT 17 +#define DW_MEM_READ(name) DW_UnwindStatus name(U64 addr, U64 size, void *buffer, void *ud) +typedef DW_MEM_READ(DW_MemRead); //////////////////////////////// -// x64 Unwind Function -internal DW_UnwindResult -dw_unwind_x64(String8 raw_text, - String8 raw_eh_frame, - String8 raw_eh_frame_header, - Rng1U64 text_vrange, - Rng1U64 eh_frame_vrange, - Rng1U64 eh_frame_header_vrange, - U64 default_image_base, - U64 image_base, - U64 stack_pointer, - DW_RegsX64 *regs, - DW_ReadMemorySig *read_memory, - void *read_memory_ud); +internal DW_CFA_Row * dw_make_cfa_row(Arena *arena, U64 reg_count); +internal DW_CFA_Row * dw_copy_cfa_row(Arena *arena, U64 reg_count, DW_CFA_Row *row); -internal DW_UnwindResult dw_unwind_x64__apply_frame_rules(String8 raw_eh_frame, DW_CFIRow *row, U64 text_base_vaddr, DW_ReadMemorySig *read_memory, void *read_memory_ud, U64 stack_pointer, DW_RegsX64 *regs); - -//////////////////////////////// -// x64 Unwind Helper Functions - -internal void dw_unwind_init_x64(void); - -//- eh_frame parsing -internal DW_CFIRecords dw_unwind_eh_frame_cfi_from_ip_slow_x64(String8 raw_eh_frame, EH_PtrCtx *ptr_ctx, U64 ip_voff); -internal DW_CFIRecords dw_unwind_eh_frame_hdr_from_ip_fast_x64(String8 raw_eh_frame, String8 raw_eh_frame_hdr, EH_PtrCtx *ptr_ctx, U64 ip_voff); - -//- cfi machine - -internal DW_CFIMachine dw_unwind_make_machine_x64(U64 cells_per_row, DW_UnpackedCIE *cie, EH_PtrCtx *ptr_ctx); -internal void dw_unwind_machine_equip_initial_row_x64(DW_CFIMachine *machine, DW_CFIRow *initial_row); -internal void dw_unwind_machine_equip_fde_ip_x64(DW_CFIMachine *machine, U64 fde_ip); - -internal DW_CFIRow* dw_unwind_row_alloc_x64(Arena *arena, U64 cells_per_row); -internal void dw_unwind_row_zero_x64(DW_CFIRow *row, U64 cells_per_row); -internal void dw_unwind_row_copy_x64(DW_CFIRow *dst, DW_CFIRow *src, U64 cells_per_row); - -internal B32 dw_unwind_machine_run_to_ip_x64(void *base, Rng1U64 range, DW_CFIMachine *machine, U64 target_ip, DW_CFIRow *row_out); +internal DW_CFI_Unwind * dw_cfi_unwind_init(Arena *arena, Arch arch, DW_CIE *cie, DW_FDE *fde, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud); +internal B32 dw_cfi_next_row(Arena *arena, DW_CFI_Unwind *uw); +internal DW_UnwindStatus dw_cfi_apply_register_rules(DW_CFI_Unwind *uw, DW_MemRead *mem_read_func, void *mem_read_ud, DW_RegRead *reg_read_func, void *reg_read_ud, DW_RegWrite *reg_write_func, void *reg_write_ud); #endif // DWARF_UNWIND_H diff --git a/src/eh/eh_frame.c b/src/eh/eh_frame.c index fd56a313..0b3f56e3 100644 --- a/src/eh/eh_frame.c +++ b/src/eh/eh_frame.c @@ -122,3 +122,160 @@ eh_size_from_aug_data(String8 aug_string, String8 data, EH_PtrCtx *ptr_ctx) return eh_parse_aug_data(aug_string, data, ptr_ctx, 0); } +internal U64 +eh_frame_hdr_search_linear_x64(String8 raw_eh_frame_hdr, EH_PtrCtx *ptr_ctx, U64 location) +{ + // Table contains only addresses for first instruction in a function and we cannot + // guarantee that result is FDE that corresponds to the input location. + // So input location must be cheked against range from FDE header again. + + U64 closest_location = max_U64; + U64 closest_address = max_U64; + + U64 cursor = 0; + + U8 version = 0; + cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &version); + + if (version == 1) { +#if 0 + EH_PtrCtx ptr_ctx = {0}; + // Set this to base address of .eh_frame_hdr. Entries are relative + // to this section for some reason. + ptr_ctx.data_vaddr = range.min; + // If input location is VMA then set this to address of .text. + // Pointer parsing function will adjust "init_location" to correct VMA. + ptr_ctx.text_vaddr = 0; +#endif + + EH_PtrEnc eh_frame_ptr_enc = 0, fde_count_enc = 0, table_enc = 0; + cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &eh_frame_ptr_enc); + cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &fde_count_enc); + cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &table_enc); + + U64 eh_frame_ptr = 0, fde_count = 0; + NotImplemented; + //cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, eh_frame_ptr_enc, cursor, &eh_frame_ptr); + //cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, fde_count_enc, cursor, &fde_count); + + for (U64 fde_idx = 0; fde_idx < fde_count; ++fde_idx) { + U64 init_location = 0, address = 0; + NotImplemented; + //cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, table_enc, cursor, &init_location); + //cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, table_enc, cursor, &address); + + S64 current_delta = (S64)(location - init_location); + S64 closest_delta = (S64)(location - closest_location); + if (0 <= current_delta && current_delta < closest_delta) { + closest_location = init_location; + closest_address = address; + } + } + } + + // address where to find corresponding FDE, this is an absolute offset + // into the image file. + return closest_address; +} + +#if 0 +internal DW_CFIRecords +dw_unwind_eh_frame_hdr_from_ip_fast_x64(String8 raw_eh_frame, String8 raw_eh_frame_hdr, EH_PtrCtx *ptr_ctx, U64 ip_voff) +{ + DW_CFIRecords result = {0}; + + // find FDE offset + void *eh_frame_hdr = raw_eh_frame.str; + U64 fde_offset = dw_search_eh_frame_hdr_linear_x64(raw_eh_frame_hdr, ptr_ctx, ip_voff); + + B32 is_fde_offset_valid = (fde_offset != max_U64); + if (is_fde_offset_valid) { + U64 fde_read_offset = (fde_offset - ptr_ctx->raw_base_vaddr); + + // read FDE size + U64 fde_size = 0; + fde_read_offset += str8_deserial_read_dwarf_packed_size(raw_eh_frame, fde_read_offset, &fde_size); + + // read FDE discriminator + U32 fde_discrim = 0; + fde_read_offset += str8_deserial_read_struct(raw_eh_frame, fde_read_offset, &fde_discrim); + + // compute parent CIE offset + U64 cie_read_offset = fde_read_offset - (fde_discrim + sizeof(fde_discrim)); + + // read CIE size + U64 cie_size = 0; + cie_read_offset += str8_deserial_read_dwarf_packed_size(raw_eh_frame, cie_read_offset, &cie_size); + + // read CIE discriminator + U32 cie_discrim = max_U32; + cie_read_offset += str8_deserial_read_struct(raw_eh_frame, cie_read_offset, &cie_discrim); + + B32 is_fde = (fde_discrim != 0); + B32 is_cie = (cie_discrim == 0); + if (is_fde && is_cie) { + Rng1U64 cie_range = rng_1u64(0, cie_read_offset + (cie_size - sizeof(cie_discrim))); + Rng1U64 fde_range = rng_1u64(0, fde_read_offset + (fde_size - sizeof(fde_discrim))); + + // parse CIE + DW_CIE cie = {0}; + dw_unwind_parse_cie_x64(raw_eh_frame.str, cie_range, ptr_ctx, cie_read_offset, &cie); + + // parse FDE + DW_FDE fde = {0}; + NotImplemented; + //dw_unwind_parse_fde_x64(raw_eh_frame.str, fde_range, ptr_ctx, &cie, fde_read_offset, &fde); + + // range check instruction pointer + if (contains_1u64(fde.pc_range, ip_voff)) { + result.valid = 1; + result.cie = cie; + result.fde = fde; + } + } + } + + return result; +} +#endif + +internal String8 +eh_string_from_ptr_enc_type(EH_PtrEnc type) +{ + switch (type) { + case EH_PtrEnc_Ptr: return str8_lit("Ptr"); + case EH_PtrEnc_ULEB128: return str8_lit("ULEB128"); + case EH_PtrEnc_UData2: return str8_lit("UData2"); + case EH_PtrEnc_UData4: return str8_lit("UData4"); + case EH_PtrEnc_UData8: return str8_lit("UData8"); + case EH_PtrEnc_Signed: return str8_lit("Signed"); + case EH_PtrEnc_SLEB128: return str8_lit("SLEB128"); + case EH_PtrEnc_SData2: return str8_lit("SData2"); + case EH_PtrEnc_SData4: return str8_lit("SData4"); + case EH_PtrEnc_SData8: return str8_lit("SData8"); + } + return str8_zero(); +} + +internal String8 +eh_string_from_ptr_enc_modifier(EH_PtrEnc modifier) +{ + switch (modifier) { + case EH_PtrEnc_PcRel: return str8_lit("PcRel"); + case EH_PtrEnc_TextRel: return str8_lit("TextRel"); + case EH_PtrEnc_DataRel: return str8_lit("DataRel"); + case EH_PtrEnc_FuncRel: return str8_lit("FuncRel"); + case EH_PtrEnc_Aligned: return str8_lit("Aligned"); + } + return str8_zero(); +} + +internal String8 +eh_string_from_ptr_enc(Arena *arena, EH_PtrEnc enc) +{ + String8 type_str = eh_string_from_ptr_enc_type(enc & EH_PtrEnc_TypeMask); + String8 modifer_str = eh_string_from_ptr_enc_modifier(enc & EH_PtrEnc_ModifierMask); + String8 indir_str = enc & EH_PtrEnc_Indirect ? str8_lit("Indirect") : str8_zero(); + String8 result = str8f(arena, "Type: %S, Modifier %S (%S)", type_str, modifer_str, indir_str); + return result; +} diff --git a/src/eh/eh_frame.h b/src/eh/eh_frame.h index f4ee576c..ac5840e5 100644 --- a/src/eh/eh_frame.h +++ b/src/eh/eh_frame.h @@ -70,5 +70,12 @@ internal U64 eh_read_ptr(String8 frame_base, U64 off, EH_PtrCtx *ptr_ctx, EH_Ptr internal U64 eh_parse_aug_data(String8 aug_string, String8 aug_data, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out); internal U64 eh_size_from_aug_data(String8 aug_string, String8 data, EH_PtrCtx *ptr_ctx); +//////////////////////////////// +//~ Enum -> String + +internal String8 eh_string_from_ptr_enc_type(EH_PtrEnc type); +internal String8 eh_string_from_ptr_enc_modifier(EH_PtrEnc modifier); +internal String8 eh_string_from_ptr_enc(Arena *arena, EH_PtrEnc enc); + #endif // EH_FRAME_H