mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-12 23:31:38 -07:00
added CFA instruction parser, interpreter, and a dumper
This commit is contained in:
+94
-5
@@ -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;
|
||||
}
|
||||
|
||||
+66
-51
@@ -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
|
||||
|
||||
+164
-111
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
+336
-15
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+55
-17
@@ -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
|
||||
|
||||
+258
-1056
File diff suppressed because it is too large
Load Diff
+59
-119
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user