major clean-up pass over unwinding/module-info abstraction, + usage in the debug engine. simplified / cleaned up DWARF helpers for unwind info & expressions. decoupling unwinding / DWARF evaluation from memory read callbacks. light module info now prepared by demon layer - debug engine no longer duplicates per-backend parsing. unwind abstraction layer now does thin abstraction over unwinding backends, debug engine only calls abstraction. clean up demon to have fewer ad-hoc 'queries' on handles, and prefer sending info over debug events when possible / when it fits the usage pattern. move DWARF enum specifications to metacode, generate enums/xlists/tables/etc. decouple raddbg/radbin completely from old parsing helpers. replace portion of DWARF dumping; pull out some new parsing helpers from converter / dumper.

This commit is contained in:
Ryan Fleury committed 2026-06-02 12:11:13 -07:00
1 parent 541d3afefa
commit d73ce364b9
88 files changed
+9899 -4723

No files matched your search

+448 -764
View File
File diff suppressed because it is too large. Load diff
+54 -1798
View File
File diff suppressed because it is too large. Load diff
+1338 -16
View File
File diff suppressed because it is too large. Load diff
-62
View File
@@ -1,62 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal B32
dw_is_dwarf_present_coff_section_table(String8 string_table, U64 section_count, COFF_SectionHeader *section_table)
{
B32 is_dwarf_present = 0;
for EachIndex(idx, section_count)
{
COFF_SectionHeader *header = &section_table[idx];
String8 name = coff_name_from_section_header(string_table, header);
DW_SectionKind s = dw_section_kind_from_string(name);
if(s == DW_Section_Null)
{
s = dw_section_dwo_kind_from_string(name);
}
is_dwarf_present = s != DW_Section_Null;
if(is_dwarf_present)
{
break;
}
}
return is_dwarf_present;
}
internal DW_Raw
dw_input_from_coff_section_table(Arena *arena,
String8 raw_image,
String8 string_table,
U64 section_count,
COFF_SectionHeader *section_table)
{
DW_Raw input = {0};
B32 sect_status[ArrayCount(input.sec)] = {0};
for (U64 i = 0; i < section_count; ++i) {
COFF_SectionHeader *header = &section_table[i];
Rng1U64 raw_data_range = rng_1u64(header->foff, header->vsize ? header->foff + header->vsize : header->foff + header->fsize);
String8 name = coff_name_from_section_header(string_table, header);
DW_SectionKind s = dw_section_kind_from_string(name);
B32 is_dwo = 0;
if (s == DW_Section_Null) {
s = dw_section_dwo_kind_from_string(name);
is_dwo = 1;
}
if (s != DW_Section_Null) {
if (sect_status[s]) {
Assert(!"too many debug sections with identical name, picking first");
} else {
sect_status[s] = 1;
DW_Section *d = &input.sec[s];
d->name = push_str8_copy(arena, name);
d->data = str8_substr(raw_image, raw_data_range);
d->is_dwo = is_dwo;
}
}
}
return input;
}
+4 -4
View File
@@ -1185,7 +1185,7 @@ DW_PRINTER(dw_print_frame)
for (U64 cursor = 0, desc_size; cursor < debug_frame.size; cursor += desc_size) {
DW_DescriptorEntry desc = {0};
desc_size = dw_parse_descriptor_entry_header(debug_frame, cursor, &desc);
if (desc.type == DW_DescriptorEntryType_CIE) {
if (desc.type == DW_DescriptorEntryKind_CIE) {
String8 raw_cie = str8_substr(debug_frame, desc.entry_range);
U64 restore_pos = arena_pos(scratch.arena);
DW_CIE *cie = push_array(scratch.arena, DW_CIE, 1);
@@ -1202,8 +1202,8 @@ DW_PRINTER(dw_print_frame)
desc_size = dw_parse_descriptor_entry_header(debug_frame, cursor, &desc);
String8 raw_desc = str8_substr(debug_frame, desc.entry_range);
switch (desc.type) {
case DW_DescriptorEntryType_Null: {} break;
case DW_DescriptorEntryType_CIE: {
case DW_DescriptorEntryKind_Null: {} break;
case DW_DescriptorEntryKind_CIE: {
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);
@@ -1229,7 +1229,7 @@ DW_PRINTER(dw_print_frame)
dw_printf("ERROR: unable to parse CIE @ %I64x\n", desc.entry_range.min);
}
} break;
case DW_DescriptorEntryType_FDE: {
case DW_DescriptorEntryKind_FDE: {
DW_DescriptorEntry cie_desc = {0};
U64 cie_desc_size = dw_parse_descriptor_entry_header(debug_frame, desc.cie_pointer, &cie_desc);
String8 cie_data = str8_substr(debug_frame, cie_desc.entry_range);
+718
View File
@@ -0,0 +1,718 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Helpers
internal String8List
dw_dump_list_from_expr(Arena *arena, String8 data, Arch arch, DW2_ParseCtx *ctx)
{
String8List result = {0};
Temp scratch = scratch_begin(&arena, 1);
ARCH_Info *arch_info = arch_info_from_arch(arch);
for(U64 off = 0; off < data.size;)
{
U64 start_off = 0;
// rjf: read next op
U8 op = 0;
off += str8_deserial_read_struct(data, off, &op);
// rjf: unpack op / operands
String8 operand_string = {0};
U64 size_param = 0;
B32 is_signed = 0;
switch(op)
{
case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2:
case DW_ExprOp_Lit3: case DW_ExprOp_Lit4: case DW_ExprOp_Lit5:
case DW_ExprOp_Lit6: case DW_ExprOp_Lit7: case DW_ExprOp_Lit8:
case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11:
case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14:
case DW_ExprOp_Lit15: case DW_ExprOp_Lit16: case DW_ExprOp_Lit17:
case DW_ExprOp_Lit18: case DW_ExprOp_Lit19: case DW_ExprOp_Lit20:
case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23:
case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26:
case DW_ExprOp_Lit27: case DW_ExprOp_Lit28: case DW_ExprOp_Lit29:
case DW_ExprOp_Lit30: case DW_ExprOp_Lit31:
{
U64 x = op - DW_ExprOp_Lit0;
operand_string = str8f(scratch.arena, "%I64u", x);
}break;
case DW_ExprOp_Const1U:size_param = 1; goto const_n;
case DW_ExprOp_Const2U:size_param = 2; goto const_n;
case DW_ExprOp_Const4U:size_param = 4; goto const_n;
case DW_ExprOp_Const8U:size_param = 8; goto const_n;
case DW_ExprOp_Const1S:size_param = 1; is_signed = 1; goto const_n;
case DW_ExprOp_Const2S:size_param = 2; is_signed = 1; goto const_n;
case DW_ExprOp_Const4S:size_param = 4; is_signed = 1; goto const_n;
case DW_ExprOp_Const8S:size_param = 8; is_signed = 1; goto const_n;
const_n:
{
if(is_signed)
{
S64 x = 0;
off += str8_deserial_read(data, off, &x, size_param, 1);
x = extend_sign64(x, size_param);
operand_string = str8f(scratch.arena, "%I64d", x);
}
else
{
U64 x = 0;
off += str8_deserial_read(data, off, &x, size_param, 1);
operand_string = str8f(scratch.arena, "%I64u", x);
}
}break;
case DW_ExprOp_Addr:
{
U64 addr = 0;
off += str8_deserial_read(data, off, &addr, ctx->addr_size, 1);
operand_string = str8f(scratch.arena, "0x%I64x", addr);
}break;
case DW_ExprOp_ConstU:
{
U64 x = 0;
off += str8_deserial_read_uleb128(data, off, &x);
operand_string = str8f(scratch.arena, "%I64u", x);
}break;
case DW_ExprOp_ConstS:
{
S64 x = 0;
off += str8_deserial_read_sleb128(data, off, &x);
operand_string = str8f(scratch.arena, "%I64d", x);
}break;
case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2:
case DW_ExprOp_Reg3: case DW_ExprOp_Reg4: case DW_ExprOp_Reg5:
case DW_ExprOp_Reg6: case DW_ExprOp_Reg7: case DW_ExprOp_Reg8:
case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11:
case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14:
case DW_ExprOp_Reg15: case DW_ExprOp_Reg16: case DW_ExprOp_Reg17:
case DW_ExprOp_Reg18: case DW_ExprOp_Reg19: case DW_ExprOp_Reg20:
case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23:
case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26:
case DW_ExprOp_Reg27: case DW_ExprOp_Reg28: case DW_ExprOp_Reg29:
case DW_ExprOp_Reg30: case DW_ExprOp_Reg31:
{
U64 reg_idx = op - DW_ExprOp_Reg0;
ARCH_RegCode regcode = arch_reg_code_from_dw(arch, (DW_RegCode)reg_idx);
operand_string = arch_info->reg_code_name_table[regcode];
}break;
case DW_ExprOp_RegX:
{
U64 reg_idx = 0;
off += str8_deserial_read_uleb128(data, off, &reg_idx);
ARCH_RegCode regcode = arch_reg_code_from_dw(arch, (DW_RegCode)reg_idx);
operand_string = arch_info->reg_code_name_table[regcode];
}break;
case DW_ExprOp_ImplicitValue:
{
U64 value_size = 0;
off += str8_deserial_read_uleb128(data, off, &value_size);
Rng1U64 value_range = rng_1u64(off, off + value_size);
String8 value_data = str8_substr(data, value_range);
off += value_size;
String8List value_strings = numeric_str8_list_from_data(scratch.arena, 16, value_data, 1);
operand_string = str8_list_join(scratch.arena, &value_strings, &(StringJoin){.pre = str8_lit("{ "), .sep = str8_lit(", "), .post = str8_lit(" }")});
}break;
case DW_ExprOp_Piece:
{
U64 size = 0;
off += str8_deserial_read_uleb128(data, off, &size);
operand_string = str8f(scratch.arena, "%u", size);
}break;
case DW_ExprOp_BitPiece:
{
U64 bit_size = 0, bit_off = 0;
off += str8_deserial_read_uleb128(data, off, &bit_size);
off += str8_deserial_read_uleb128(data, off, &bit_off);
operand_string = str8f(scratch.arena, "bit size %I64u, bit offset %I64u", bit_size, bit_off);
}break;
case DW_ExprOp_Pick:
{
U8 stack_idx = 0;
off += str8_deserial_read_struct(data, off, &stack_idx);
operand_string = str8f(scratch.arena, "stack index %u", stack_idx);
}break;
case DW_ExprOp_PlusUConst:
{
U64 addend = 0;
off += str8_deserial_read_uleb128(data, off, &addend);
operand_string = str8f(arena, "addend %I64u", addend);
}break;
case DW_ExprOp_Skip:
{
S16 x = 0;
off += str8_deserial_read_struct(data, off, &x);
operand_string = str8f(scratch.arena, "%+d bytes", x);
}break;
case DW_ExprOp_Bra:
{
S16 x = 0;
off += str8_deserial_read_struct(data, off, &x);
operand_string = str8f(scratch.arena, "%+d", x);
}break;
case DW_ExprOp_BReg0: case DW_ExprOp_BReg1: case DW_ExprOp_BReg2:
case DW_ExprOp_BReg3: case DW_ExprOp_BReg4: case DW_ExprOp_BReg5:
case DW_ExprOp_BReg6: case DW_ExprOp_BReg7: case DW_ExprOp_BReg8:
case DW_ExprOp_BReg9: case DW_ExprOp_BReg10: case DW_ExprOp_BReg11:
case DW_ExprOp_BReg12: case DW_ExprOp_BReg13: case DW_ExprOp_BReg14:
case DW_ExprOp_BReg15: case DW_ExprOp_BReg16: case DW_ExprOp_BReg17:
case DW_ExprOp_BReg18: case DW_ExprOp_BReg19: case DW_ExprOp_BReg20:
case DW_ExprOp_BReg21: case DW_ExprOp_BReg22: case DW_ExprOp_BReg23:
case DW_ExprOp_BReg24: case DW_ExprOp_BReg25: case DW_ExprOp_BReg26:
case DW_ExprOp_BReg27: case DW_ExprOp_BReg28: case DW_ExprOp_BReg29:
case DW_ExprOp_BReg30: case DW_ExprOp_BReg31:
{
U64 reg_idx = op - DW_ExprOp_BReg0;
S64 reg_off = 0;
off += str8_deserial_read_sleb128(data, off, &reg_off);
ARCH_RegCode regcode = arch_reg_code_from_dw(arch, (DW_RegCode)reg_idx);
operand_string = str8f(scratch.arena, "%S+%I64d", arch_info->reg_code_name_table[regcode], reg_off);
}break;
case DW_ExprOp_FBReg:
{
S64 reg_off = 0;
off += str8_deserial_read_sleb128(data, off, &reg_off);
operand_string = str8f(scratch.arena, "frame base offset %I64d", reg_off);
}break;
case DW_ExprOp_BRegX:
{
U64 reg_idx = 0;
S64 reg_off = 0;
off += str8_deserial_read_uleb128(data, off, &reg_idx);
off += str8_deserial_read_sleb128(data, off, &reg_off);
ARCH_RegCode regcode = arch_reg_code_from_dw(arch, (DW_RegCode)reg_idx);
operand_string = str8f(scratch.arena, "%S+%I64d", arch_info->reg_code_name_table[regcode], reg_off);
}break;
case DW_ExprOp_XDerefSize:
case DW_ExprOp_DerefSize:
{
U8 x = 0;
off += str8_deserial_read_struct(data, off, &x);
operand_string = str8f(scratch.arena, "%u", x);
}break;
case DW_ExprOp_Call2:
{
U16 x = 0;
off += str8_deserial_read_struct(data, off, &x);
operand_string = str8f(scratch.arena, "%u", x);
}break;
case DW_ExprOp_Call4:
{
U32 x = 0;
off += str8_deserial_read_struct(data, off, &x);
operand_string = str8f(arena, "%u", x);
}break;
case DW_ExprOp_CallRef:
{
U64 x = 0;
off += dw2_read_fmt_u64(data, off, ctx->format, &x);
operand_string = str8f(scratch.arena, "%I64u", x);
}break;
case DW_ExprOp_ImplicitPointer:
case DW_GNU_ExprOp_ImplicitPointer:
{
U64 info_off = 0;
off += dw2_read_fmt_u64(data, off, ctx->format, &info_off);
S64 ptr = 0;
off += str8_deserial_read_sleb128(data, off, &ptr);
operand_string = str8f(scratch.arena, ".debug_info@0x%I64x, ptr %I64x", info_off, ptr);
} break;
case DW_ExprOp_Convert:
case DW_GNU_ExprOp_Convert:
{
U64 type_cu_off = 0;
off += str8_deserial_read_uleb128(data, off, &type_cu_off);
operand_string = str8f(scratch.arena, "TypeCuOff %#I64x", ctx->unit_base_info_off + type_cu_off);
}break;
case DW_GNU_ExprOp_ParameterRef:
{
// TODO: always 4 bytes?
U32 cu_off = 0;
off += str8_deserial_read_struct(data, off, &cu_off);
operand_string = str8f(scratch.arena, "CuOff %#x", ctx->unit_base_info_off + cu_off);
}break;
case DW_ExprOp_DerefType:
case DW_GNU_ExprOp_DerefType:
{
U8 deref_size = 0;
U64 type_cu_off = 0;
off += str8_deserial_read_struct(data, off, &deref_size);
off += str8_deserial_read_uleb128(data, off, &type_cu_off);
operand_string = str8f(scratch.arena, "%#x, TypeCuOff %#I64x", deref_size, ctx->unit_base_info_off + type_cu_off);
}break;
case DW_ExprOp_ConstType:
case DW_GNU_ExprOp_ConstType:
{
U64 type_cu_off = 0;
U8 const_value_size = 0;
off += str8_deserial_read_uleb128(data, off, &type_cu_off);
off += str8_deserial_read_struct(data, off, &const_value_size);
Rng1U64 const_value_range = rng_1u64(off, off + const_value_size);
String8 const_value_data = str8_substr(data, const_value_range);
String8List const_value_strings = numeric_str8_list_from_data(scratch.arena, 16, const_value_data, 1);
String8 const_value_str = str8_list_join(scratch.arena, &const_value_strings, &(StringJoin){.sep = str8_lit(", ")});
operand_string = str8f(scratch.arena, "TypeCuOff %#I64x, Const Value { %S }", ctx->unit_base_info_off + type_cu_off, const_value_str);
off += const_value_size;
}break;
case DW_ExprOp_RegvalType:
case DW_GNU_ExprOp_RegvalType:
{
U64 reg_idx = 0, type_cu_off = 0;
off += str8_deserial_read_uleb128(data, off, &reg_idx);
off += str8_deserial_read_uleb128(data, off, &type_cu_off);
ARCH_RegCode regcode = arch_reg_code_from_dw(arch, (DW_RegCode)reg_idx);
operand_string = str8f(scratch.arena, "%S, TypeCuOff %#I64x", arch_info->reg_code_name_table[regcode], ctx->unit_base_info_off + type_cu_off);
}break;
case DW_ExprOp_EntryValue:
case DW_GNU_ExprOp_EntryValue:
{
U64 block_size = 0;
off += str8_deserial_read_uleb128(data, off, &block_size);
Rng1U64 block_range = rng_1u64(off, off + block_size);
String8 block_data = str8_substr(data, block_range);
// TODO(rjf): WTF, recursive? let's not... - if we want this, let's just do the stack inside of this function
// String8List block_expr = dw_string_list_from_expression(scratch.arena, block_data, cu_base, addr_size, arch, ver, ext, format);
// operand_string = str8_list_join(scratch.arena, &block_expr, &(StringJoin){.pre = str8_lit("{ "), .sep = str8_lit(","), .post = str8_lit(" }")});
operand_string = s("{...}");
off += block_size;
} break;
case DW_ExprOp_Addrx:
{
U64 addr = 0;
off += str8_deserial_read_uleb128(data, off, &addr);
operand_string = str8f(scratch.arena, "%#I64x", addr);
}break;
}
// rjf: push opcode info
String8 opcode_string = dw_string_from_expr_op(ctx->version, ctx->exts, op);
if(operand_string.size == 0)
{
str8_list_pushf(arena, &result, "%S", opcode_string);
}
else
{
str8_list_pushf(arena, &result, "%S(%S)", opcode_string, operand_string);
}
}
scratch_end(scratch);
return result;
}
////////////////////////////////
//~ rjf: Main Dump Entry Point
internal String8List
dw_dump_list_from_raw(Arena *arena, DW_Raw *raw, Arch arch, DW_SectionFlags subset_flags)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
String8 indent = str8_lit(" ");
//////////////////////////////
//- rjf: set up
//
typedef struct DumpSubsetOutputNode DumpSubsetOutputNode;
struct DumpSubsetOutputNode
{
DumpSubsetOutputNode *next;
DW_SectionKind section_kind;
String8List *lane_strings;
};
DumpSubsetOutputNode *first_output_node = 0;
DumpSubsetOutputNode *last_output_node = 0;
String8List *strings = 0;
#define dump(str) str8_list_push(arena, strings, (str))
#define dumpf(...) str8_list_pushf(arena, strings, __VA_ARGS__)
#define DumpSubsetKind(kind) \
if(lane_idx() == 0)\
{\
DumpSubsetOutputNode *n = push_array(scratch.arena, DumpSubsetOutputNode, 1);\
SLLQueuePush(first_output_node, last_output_node, n);\
n->section_kind = (kind);\
n->lane_strings = push_array(scratch.arena, String8List, lane_count());\
}\
lane_sync_u64(&first_output_node, 0);\
lane_sync_u64(&last_output_node, 0);\
strings = &last_output_node->lane_strings[lane_idx()];\
lane_sync(); if(subset_flags & (1ull<<(kind))) ProfScope((char *)dw_regular_name_from_section_kind(kind).str)
#define DumpSubset(name) DumpSubsetKind(DW_SectionKind_##name)
//////////////////////////////
//- rjf: .debug_abbrev
//
DumpSubset(Abbrev)
{
if(lane_idx() == 0)
{
String8 data = raw->sec[DW_SectionKind_Abbrev].data;
U64 idx = 0;
dumpf("\nabbrevs:\n{\n");
for(U64 off = 0; off < data.size; idx += 1)
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
U64 start_off = off;
// rjf: read abbrev info
DW2_AbbrevHeader abbrev_header = {0};
DW2_AbbrevAttribList abbrev_attribs = {0};
off += dw2_read_abbrev(scratch2.arena, data, off, &abbrev_header, &abbrev_attribs);
// rjf: log
dumpf(" abbrev: // abbrev[%I64u], .debug_abbrev@0x%I64x\n", idx, start_off);
dumpf(" {\n");
dumpf(" id: %I64u\n", abbrev_header.id);
dumpf(" tag_kind: %S (0x%I64x)\n", dw_string_from_tag_kind(DW_Version_Last, DW_ExtFlag_All, abbrev_header.tag_kind), abbrev_header.tag_kind);
dumpf(" has_children: %s\n", abbrev_header.has_children ? "yes" : "no");
if(abbrev_attribs.count != 0)
{
dumpf(" attribs:\n {\n");
for EachNode(n, DW2_AbbrevAttribNode, abbrev_attribs.first)
{
DW2_AbbrevAttrib *attrib = &n->v;
dumpf(" {%-24S %-16S%S}\n",
dw_string_from_attrib_kind(DW_Version_Last, DW_ExtFlag_All, attrib->attrib_kind),
dw_string_from_form_kind(DW_Version_Last, DW_ExtFlag_All, attrib->form_kind),
attrib->form_kind == DW_FormKind_ImplicitConst ? str8f(scratch2.arena, " (0x%I64x, %I64u)", attrib->implicit_const, attrib->implicit_const) : s(""));
}
dumpf(" }\n");
}
dumpf(" }\n");
if(off == start_off)
{
break;
}
}
dumpf("}\n");
}
}
//////////////////////////////
//- rjf: .debug_info
//
DumpSubset(Info)
{
//- rjf: gather unit ranges from .debug_info
Rng1U64Array *unit_info_ranges = 0;
if(lane_idx() == 0)
{
unit_info_ranges = push_array(scratch.arena, Rng1U64Array, 1);
*unit_info_ranges = dw2_unit_ranges_from_data(scratch.arena, raw->sec[DW_SectionKind_Info].data);
}
lane_sync_u64(&unit_info_ranges, 0);
U64 unit_count = unit_info_ranges->count;
//- rjf: parse all .debug_info unit headers
DW2_UnitHeader *unit_headers = 0;
Rng1U64 *unit_info_tag_ranges = 0;
{
// rjf: set up
if(lane_idx() == 0)
{
unit_headers = push_array(scratch.arena, DW2_UnitHeader, unit_count);
unit_info_tag_ranges = push_array(scratch.arena, Rng1U64, unit_count);
}
lane_sync_u64(&unit_headers, 0);
lane_sync_u64(&unit_info_tag_ranges, 0);
// rjf: parse all unit headers
String8 data = raw->sec[DW_SectionKind_Info].data;
Rng1U64 range = lane_range(unit_count);
for EachInRange(idx, range)
{
Rng1U64 unit_info_range = unit_info_ranges->v[idx];
U64 bytes_read = dw2_read_unit_header(str8_substr(data, unit_info_range), 0, &unit_headers[idx]);
unit_info_tag_ranges[idx] = r1u64(unit_info_range.min + bytes_read, unit_info_range.max);
}
}
lane_sync();
Rng1U64Array unit_info_tag_ranges_array = {unit_info_tag_ranges, unit_count};
//- rjf: build all abbreviation maps, build (unit -> abbrev map)
DW2_UnitAbbrevMapMap unit_abbrev_map_map = dw2_unit_abbrev_map_map_from_data(scratch.arena, raw->sec[DW_SectionKind_Abbrev].data, unit_headers, unit_count);
//- rjf: parse all unit offsets tables
DW2_OffsetTableSet *offset_tables = 0;
if(lane_idx() == 0)
{
offset_tables = push_array(scratch.arena, DW2_OffsetTableSet, 1);
offset_tables[0] = dw2_offset_table_set_from_raw(scratch.arena, raw);
}
lane_sync_u64(&offset_tables, 0);
//- rjf: build per-unit parsing contexts
DW2_ParseCtx *unit_parse_ctxs = 0;
{
if(lane_idx() == 0)
{
unit_parse_ctxs = push_array(scratch.arena, DW2_ParseCtx, unit_count);
}
lane_sync_u64(&unit_parse_ctxs, 0);
Rng1U64 range = lane_range(unit_count);
for EachInRange(unit_idx, range)
{
DW2_UnitHeader *hdr = &unit_headers[unit_idx];
DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx];
// rjf: equip initial bootstrapping info
dw2_parse_ctx_equip_unit_header(ctx, hdr, unit_info_ranges->v[unit_idx].min);
dw2_parse_ctx_equip_unit_abbrev_map(ctx, &unit_abbrev_map_map, unit_idx);
// rjf: do bootstrapping parse of root tag
DW2_Tag root_tag_bootstrap = {0};
dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &root_tag_bootstrap);
// rjf: equip info extracted from bootstrapping parse
dw2_parse_ctx_equip_unit_root_tag(ctx, &root_tag_bootstrap, offset_tables);
// rjf: do non-bootstrapping parse
DW2_Tag root_tag = {0};
dw2_read_tag(scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, unit_info_tag_ranges[unit_idx].min, &root_tag);
// rjf: equip info extracted from non-bootstrapping parse
dw2_parse_ctx_equip_unit_root_tag(ctx, &root_tag, offset_tables);
}
}
lane_sync();
//- rjf: dump all unit tag trees
String8List *unit_dumps = 0;
if(lane_idx() == 0)
{
unit_dumps = push_array(scratch.arena, String8List, unit_count);
}
lane_sync_u64(&unit_dumps, 0);
U64 unit_take_idx_ = 0;
U64 *unit_take_idx_ptr = &unit_take_idx_;
lane_sync_u64(&unit_take_idx_ptr, 0);
String8List *strings_restore = strings;
for(;;)
{
//- rjf: take unit
U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1;
if(unit_idx >= unit_count)
{
break;
}
//- rjf: unpack unit
strings = &unit_dumps[unit_idx];
DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx];
//- rjf: dump
dumpf(" unit: // unit[%I64u], .debug_info@0x%I64x\n", unit_idx, ctx->unit_base_info_off);
dumpf(" {\n");
dumpf(" version: %u\n", ctx->version);
dumpf(" format: %S\n", dw_string_from_format(ctx->format));
dumpf(" abbrev_off: 0x%I64x\n", unit_headers[unit_idx].abbrev_off);
dumpf(" addr_size: %I64u\n", ctx->addr_size);
dumpf(" kind: %S\n", dw_string_from_comp_unit_kind(unit_headers[unit_idx].kind));
dumpf(" dwo_id: 0x%I64x\n", unit_headers[unit_idx].dwo_id);
dumpf(" tags:\n {\n");
{
U64 depth = 0;
for(U64 off = unit_info_tag_ranges[unit_idx].min; off < unit_info_tag_ranges[unit_idx].max;)
{
U64 start_off = off;
Temp tag_scratch = scratch_begin(&scratch.arena, 1);
// rjf: parse next tag
DW2_Tag tag = {0};
off += dw2_read_tag(tag_scratch.arena, raw, ctx, raw->sec[DW_SectionKind_Info].data, off, &tag);
// rjf: non-zero tag -> dump
if(tag.kind != DW_TagKind_Null)
{
dumpf(" %.*s%S: // .debug_info@0x%I64x\n", depth, indent.str, dw_string_from_tag_kind(ctx->version, ctx->exts, tag.kind), start_off);
dumpf(" %.*s{\n", depth, indent.str);
for EachNode(n, DW2_AttribNode, tag.attribs.first)
{
DW2_Attrib *attrib = &n->v;
String8 kind_string = dw_string_from_attrib_kind(ctx->version, ctx->exts, attrib->attrib_kind);
String8 form_kind_string = dw_string_from_form_kind(ctx->version, ctx->exts, attrib->val.kind);
String8 value_string = {0};
DW_AttribClass value_class = dw_attrib_class_from_form_kind(ctx->version, ctx->exts, attrib->val.kind);
switch(value_class)
{
default:
{
value_string = str8f(tag_scratch.arena, "0x%I64x", attrib->val.u128.u64[0]);
}break;
case DW_AttribClass_Address:
{
value_string = str8f(tag_scratch.arena, "0x%I64x", attrib->val.addr);
}break;
case DW_AttribClass_String:
{
value_string = str8f(tag_scratch.arena, "\"%S\"", attrib->val.string);
}break;
case DW_AttribClass_Flag:
{
value_string = attrib->val.u128.u64[0] ? s("true") : s("false");
}break;
case DW_AttribClass_Block:
{
String8 block = attrib->val.string;
String8List block_strs = numeric_str8_list_from_data(tag_scratch.arena, 16, block, 1);
value_string = str8_list_join(tag_scratch.arena, &block_strs, &(StringJoin){.pre = s("{ "), .sep = s(", "), .post = s(" }")});
}break;
case DW_AttribClass_ExprLoc:
{
String8 expr = attrib->val.string;
String8List expr_strs = dw_dump_list_from_expr(tag_scratch.arena, expr, arch, ctx);
value_string = str8_list_join(tag_scratch.arena, &expr_strs, &(StringJoin){.pre = s("{ "), .sep = s(", "), .post = s(" }")});
}break;
}
dumpf(" %.*s %S: %S // %S\n", depth, indent.str, kind_string, value_string, form_kind_string);
}
}
// rjf: push/pop
if(tag.has_children)
{
depth += 1;
}
else if(tag.kind == DW_TagKind_Null)
{
depth -= 1;
}
// rjf: if tag does not have children -> end info
if(!tag.has_children)
{
dumpf(" %.*s}\n", depth, indent.str);
}
scratch_end(tag_scratch);
if(off == start_off)
{
break;
}
}
}
dumpf(" }\n");
dumpf(" }\n");
}
strings = strings_restore;
//- rjf: join unit dumps in order
if(lane_idx() == 0)
{
dumpf("\nunits:\n{\n");
for EachIndex(unit_idx, unit_count)
{
str8_list_concat_in_place(strings, &unit_dumps[unit_idx]);
}
dumpf("}\n");
}
lane_sync();
}
//////////////////////////////
//- rjf: .debug_line
//
DumpSubset(Line)
{
// TODO(rjf)
}
//////////////////////////////
//- rjf: .debug_str
//
DumpSubset(Str)
{
if(lane_idx() == 0)
{
dumpf("\nstrings:\n{\n");
String8 data = raw->sec[DW_SectionKind_Str].data;
U64 idx = 0;
for(U64 off = 0; off < data.size; idx += 1)
{
U64 start_off = off;
String8 string = {0};
off += str8_deserial_read_cstr(data, off, &string);
dumpf(" {\"%S\"} // string[%I64u], .debug_str@0x%I64x\n", string, idx, start_off);
if(off == start_off)
{
break;
}
}
dumpf("}\n");
}
}
//////////////////////////////
//- rjf: .debug_frame
//
#if 0
DumpSubset(Frame)
{
// TODO(rjf)
}
#endif
//////////////////////////////
//- rjf: .debug_loc
//
#if 0
DumpSubset(Loc)
{
// TODO(rjf)
}
#endif
//////////////////////////////
//- rjf: join results
//
String8List *result_strings_ptr = push_array(scratch.arena, String8List, 1);
lane_sync();
if(lane_idx() == 0)
{
for EachNode(n, DumpSubsetOutputNode, first_output_node)
{
String8List subset_strings = {0};
for EachIndex(idx, lane_count())
{
str8_list_concat_in_place(&subset_strings, &n->lane_strings[idx]);
}
if(subset_strings.total_size != 0)
{
str8_list_pushf(arena, result_strings_ptr, "////////////////////////////////\n//~ %S\n", dw_regular_name_from_section_kind(n->section_kind));
str8_list_concat_in_place(result_strings_ptr, &subset_strings);
str8_list_pushf(arena, result_strings_ptr, "\n");
}
}
}
lane_sync();
lane_sync_u64(&result_strings_ptr, 0);
String8List result_strings = *result_strings_ptr;
lane_sync();
#undef DumpSubsetKind
#undef DumpSubset
#undef dump
#undef dumpf
scratch_end(scratch);
ProfEnd();
return result_strings;
}
+17
View File
@@ -0,0 +1,17 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DWARF_DUMP_2_H
#define DWARF_DUMP_2_H
////////////////////////////////
//~ rjf: Helpers
internal String8List dw_dump_list_from_expr(Arena *arena, String8 data, Arch arch, DW2_ParseCtx *ctx);
////////////////////////////////
//~ rjf: Main Dump Entry Point
internal String8List dw_dump_list_from_raw(Arena *arena, DW_Raw *raw, Arch arch, DW_SectionFlags subset_flags);
#endif //DWARF_DUMP_2_H
-108
View File
@@ -1578,111 +1578,3 @@ dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 frame_base, U64 cfa,
scratch_end(scratch);
return result;
}
internal String8
dw_encode_expr(Arena *arena, Arch arch, DW_Format format, DW_ExprEnc *encs, U64 encs_count)
{
Temp scratch = scratch_begin(&arena, 1);
String8List *srl = push_array(scratch.arena, String8List, 1);
str8_serial_begin(scratch.arena, srl);
HashTable *label_map = hash_table_init(scratch.arena, 128);
struct Fixup { U64 offset; String8 label; struct Fixup *next; };
struct Fixup *first_fixup = 0, *last_fixup = 0;
for EachIndex(i, encs_count) {
DW_ExprEnc *e = &encs[i];
switch (e->type) {
case DW_ExprEncType_Null: {} break;
case DW_ExprEncType_Op: {
str8_serial_push_struct(scratch.arena, srl, &e->op);
} break;
case DW_ExprEncType_U8: {
str8_serial_push_struct(scratch.arena, srl, &e->u8);
} break;
case DW_ExprEncType_U16: {
str8_serial_push_struct(scratch.arena, srl, &e->u16);
} break;
case DW_ExprEncType_U32: {
str8_serial_push_struct(scratch.arena, srl, &e->u32);
} break;
case DW_ExprEncType_U64: {
str8_serial_push_struct(scratch.arena, srl, &e->u64);
} break;
case DW_ExprEncType_S8: {
str8_serial_push_struct(scratch.arena, srl, &e->s8);
} break;
case DW_ExprEncType_S16: {
str8_serial_push_struct(scratch.arena, srl, &e->s16);
} break;
case DW_ExprEncType_S32: {
str8_serial_push_struct(scratch.arena, srl, &e->s32);
} break;
case DW_ExprEncType_S64: {
str8_serial_push_struct(scratch.arena, srl, &e->s64);
} break;
case DW_ExprEncType_ULEB128: {
str8_serial_push_string(scratch.arena, srl, dw_write_uleb128(scratch.arena, e->u64));
} break;
case DW_ExprEncType_SLEB128: {
str8_serial_push_string(scratch.arena, srl, dw_write_sleb128(scratch.arena, e->s64));
} break;
case DW_ExprEncType_Addr: {
U64 addr_size = byte_size_from_arch(arch);
Assert(addr_size <= sizeof(e->addr));
str8_serial_push_string(scratch.arena, srl, str8((U8 *)&e->addr, addr_size));
} break;
case DW_ExprEncType_Block: {
Assert(e->block.size <= max_U8);
str8_serial_push_u8(scratch.arena, srl, (U8)e->block.size);
str8_serial_push_string(scratch.arena, srl, e->block);
} break;
case DW_ExprEncType_DwarfUInt: {
switch (format) {
case DW_Format_Null: {} break;
case DW_Format_32Bit: { str8_serial_push_u32(scratch.arena, srl, e->u32); } break;
case DW_Format_64Bit: { str8_serial_push_u64(scratch.arena, srl, e->u64); } break;
default: { InvalidPath; } break;
}
} break;
case DW_ExprEncType_Label: {
struct Fixup *fixup = push_array(scratch.arena, struct Fixup, 1);
fixup->offset = srl->total_size;
fixup->label = e->label;
SLLQueuePush(first_fixup, last_fixup, fixup);
S16 delta_placeholder = 0;
str8_serial_push_struct(scratch.arena, srl, &delta_placeholder);
} break;
case DW_ExprEncType_DeclLabel: {
hash_table_push_string_u64(scratch.arena, label_map, e->label, srl->total_size);
} break;
default: { InvalidPath; } break;
}
}
// finalize expression
String8 expr = str8_serial_end(arena, srl);
// apply fixups
for EachNode(fixup, struct Fixup, first_fixup) {
U64 label_offset;
if (!hash_table_search_string_u64(label_map, fixup->label, &label_offset)) {
Assert(0 && "undefined label");
continue;
}
S64 delta = (S64)label_offset - (S64)fixup->offset;
AssertAlways(min_S16 <= delta && delta <= max_S16);
S16 *delta_ptr = (S16 *)(expr.str + fixup->offset);
*delta_ptr = delta;
}
scratch_end(scratch);
return expr;
}
-3
View File
@@ -229,8 +229,5 @@ internal String8 dw_string_from_expr_value(Arena *arena, U64 addr_size, DW_ExprV
// evaluator
internal DW_ExprEvalResult dw_eval_expr(Arena *arena, Arch arch, DW_Format format, U64 frame_base, U64 cfa, U64 tls_base, U64 op_limit, DW_Expr expr, void *reg_block, MachineOp_MemRead *mem_read, void *mem_read_ud, DW_ExprValue *value_out);
// encoder
internal String8 dw_encode_expr(Arena *arena, Arch arch, DW_Format format, DW_ExprEnc *encs, U64 encs_count);
#endif //DWARF_EXPR_H
+393
View File
@@ -0,0 +1,393 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Expression Evaluation Functions
internal DW_Eval
dw_eval(Arena *arena, DW_Format fmt, Arch arch, MemoryMap *memory_map, void *regs, U64 frame_base, U64 cfa, U64 tls, DW_EvalState *state, String8 expr, U64 op_idx_cap)
{
DW_Eval result = {0};
ARCH_Info *arch_info = arch_info_from_arch(arch);
B32 need_a_break = 0;
B32 done = 0;
for(;state->off < expr.size && !need_a_break && !done;)
{
U64 start_off = state->off;
U64 off = state->off;
//- rjf: too many ops? -> error
if(state->op_idx >= op_idx_cap)
{
result.status = DW_EvalStatus_ExecOpLimitReached;
break;
}
//- rjf: read next opcode
DW_ExprOp opcode = 0;
off += str8_deserial_read_struct(expr, off, &opcode);
//- rjf: unpack opcode
U8 push_count = 0;
U8 pop_count = 0;
U8 operand_count = 0;
DW_ExprOperandKind operand_kinds[2] = {DW_ExprOperandKind_Null};
{
DW_ExprOpInfo *op_info = dw_info_from_expr_op(DW_Version_5, DW_ExtFlag_All, opcode);
push_count = op_info->push_count;
pop_count = op_info->pop_count;
operand_count = op_info->operand_count;
MemoryCopy(operand_kinds, op_info->operand_kinds, sizeof(operand_kinds));
}
//- rjf: read operands
DW_EvalVal operands[2] = {0};
for(U8 operand_idx = 0; operand_idx < operand_count; operand_idx += 1)
{
U64 fixed_bytes_to_read = 0;
switch(operand_kinds[operand_idx])
{
default:{}break;
case DW_ExprOperandKind_U8: {fixed_bytes_to_read = 1;}goto fixed_read;
case DW_ExprOperandKind_U16: {fixed_bytes_to_read = 2;}goto fixed_read;
case DW_ExprOperandKind_U32: {fixed_bytes_to_read = 4;}goto fixed_read;
case DW_ExprOperandKind_U64: {fixed_bytes_to_read = 8;}goto fixed_read;
case DW_ExprOperandKind_S8: {fixed_bytes_to_read = 1;}goto fixed_read;
case DW_ExprOperandKind_S16: {fixed_bytes_to_read = 2;}goto fixed_read;
case DW_ExprOperandKind_S32: {fixed_bytes_to_read = 4;}goto fixed_read;
case DW_ExprOperandKind_S64: {fixed_bytes_to_read = 8;}goto fixed_read;
case DW_ExprOperandKind_Addr: {fixed_bytes_to_read = byte_size_from_arch(arch);}goto fixed_read;
case DW_ExprOperandKind_DwarfUInt:{fixed_bytes_to_read = dw_addr_size_from_format(fmt);}goto fixed_read;
fixed_read:;
{
off += str8_deserial_read(expr, off, &operands[operand_idx].u512.u64[0], fixed_bytes_to_read, fixed_bytes_to_read);
}break;
case DW_ExprOperandKind_ULEB128:
{
off += str8_deserial_read_uleb128(expr, off, &operands[operand_idx].u512.u64[0]);
}break;
case DW_ExprOperandKind_SLEB128:
{
off += str8_deserial_read_sleb128(expr, off, &operands[operand_idx].s64);
}break;
case DW_ExprOperandKind_Block:
{
U8 block_size = 0;
off += str8_deserial_read_struct(expr, off, &block_size);
operands[operand_idx].data = str8_prefix(str8_skip(expr, off), block_size);
}break;
}
}
//- rjf: do pops
DW_EvalVal popped_vals[3] = {0};
{
pop_count = Min(pop_count, ArrayCount(popped_vals));
for(U8 pop_idx = 0; pop_idx < pop_count; pop_idx += 1)
{
if(state->top_val != 0)
{
DW_EvalValNode *popped = state->top_val;
SLLStackPop(state->top_val);
popped_vals[pop_idx] = popped->v;
SLLStackPush(state->free_val, popped);
}
}
}
//- rjf: apply opcode
DW_EvalVal push_vals[3] = {0};
U64 pick_target_idx = 0;
U64 deref_read_size = 0;
DW_RegCode reg_code_dw = 0;
switch(opcode)
{
//- rjf: unsuported ops
case DW_ExprOp_Call2: // DWARF function calls
case DW_ExprOp_Call4: // DWARF function calls
case DW_ExprOp_CallRef: // DWARF function calls
case DW_ExprOp_Addrx: // address in .debug_addr; not currently present for us, need to find how to preserve if we care
case DW_ExprOp_Constx: // address in .debug_addr; not currently present for us, need to find how to preserve if we care
case DW_ExprOp_EntryValue: // encodes a sub-expression as a data block - need to recursively evaluate, lol
case DW_ExprOp_ConstType: // pushes a constant - but refers into DWARF debug info to specify the type. we don't have that, need to figure out how to preserve mapping from DW type ID -> RDI type, if we care
case DW_ExprOp_DerefType: // same as above - refers to DWARF debug info
case DW_ExprOp_RegvalType: // same as above - refers to DWARF debug info
case DW_ExprOp_Piece: // pieces - used to stitch multiple sub-values together
case DW_ExprOp_BitPiece: // pieces - used to stitch multiple sub-values together
case DW_ExprOp_XDeref: // two entries popped from stack - second one is 'address space identifier'. not possible in this layer right now
case DW_ExprOp_XDerefSize: // does multiple address spaces
case DW_ExprOp_XDerefType: // refers to DWARF debug info, *and* does multiple address spaces
case DW_ExprOp_PushObjectAddress: // pushes "the address of the object currently being evaluated" ???
case DW_ExprOp_ImplicitPointer: // refers to DWARF debug info
default:
{
done = 1;
result.status = DW_EvalStatus_UnsupportedOp;
}break;
//- rjf: address ops
case DW_ExprOp_Deref:{deref_read_size = byte_size_from_arch(arch);}goto deref;
case DW_ExprOp_DerefSize:{deref_read_size = operands[0].u512.u64[0];}goto deref;
deref:;
{
U64 vaddr = popped_vals[0].u512.u64[0];
U64 read_size = Min(deref_read_size, sizeof(push_vals[0].u512));
Rng1U64 read_vaddr_range = r1u64(vaddr, vaddr+read_size);
if(!memory_map_read(memory_map, read_vaddr_range, &push_vals[0].u512))
{
need_a_break = 1;
result.status = DW_EvalStatus_FailedMemoryRead;
result.missed_read_vaddr_range = read_vaddr_range;
}
}break;
//- rjf: generic constants
case DW_ExprOp_Const1U: case DW_ExprOp_Const1S:
case DW_ExprOp_Const2U: case DW_ExprOp_Const2S:
case DW_ExprOp_Const4U: case DW_ExprOp_Const4S:
case DW_ExprOp_Const8U: case DW_ExprOp_Const8S:
case DW_ExprOp_ConstU: case DW_ExprOp_ConstS:
case DW_ExprOp_Addr:
{
push_vals[0] = operands[0];
}break;
//- rjf: stack ops
case DW_ExprOp_Dup:{push_vals[0] = popped_vals[0];}break;
case DW_ExprOp_Drop:{/* NOTE(rjf): nothing to do here - just pops */}break;
case DW_ExprOp_Swap:{push_vals[0] = popped_vals[1]; push_vals[1] = popped_vals[0];}break;
case DW_ExprOp_Rot: {push_vals[0] = popped_vals[0]; push_vals[1] = popped_vals[1]; push_vals[2] = popped_vals[2];}break;
case DW_ExprOp_Over:{pick_target_idx = 1;}break;
case DW_ExprOp_Pick:{pick_target_idx = operands[0].u512.u64[0];}goto pick_stack_val;
pick_stack_val:;
{
U64 idx = 0;
for(DW_EvalValNode *n = state->top_val; n != 0; n = n->next, idx += 1)
{
if(idx == pick_target_idx)
{
push_vals[0] = n->v;
break;
}
}
}break;
//- rjf: arithmetic ops
#define UniOpU(k, op) case k:{push_vals[0].u512.u64[0] = op(popped_vals[0].u512.u64[0]);}break
#define UniOpS(k, op) case k:{push_vals[0].s64 = op(popped_vals[0].s64);}break
#define BinOpS(k, op) case k:{push_vals[0].s64 = popped_vals[1].s64 op popped_vals[0].s64;}break
#define BinOpU(k, op) case k:{push_vals[0].u512.u64[0] = popped_vals[1].u512.u64[0] op popped_vals[0].u512.u64[0];}break
#define BinOpDiv(k, op) case k:if(popped_vals[0].u512.u64[0] != 0){push_vals[0].u512.u64[0] = popped_vals[1].u512.u64[0] op popped_vals[0].u512.u64[0];}break
UniOpS(DW_ExprOp_Abs, abs_s64);
UniOpS(DW_ExprOp_Neg, -);
UniOpU(DW_ExprOp_Not, !);
BinOpU(DW_ExprOp_Minus, -);
BinOpU(DW_ExprOp_Mul, *);
BinOpU(DW_ExprOp_Plus, +);
BinOpU(DW_ExprOp_Shl, <<);
BinOpU(DW_ExprOp_Shr, >>);
BinOpU(DW_ExprOp_And, &);
BinOpU(DW_ExprOp_Or, |);
BinOpU(DW_ExprOp_Xor, ^);
BinOpS(DW_ExprOp_Shra, >>);
BinOpDiv(DW_ExprOp_Div, /);
BinOpDiv(DW_ExprOp_Mod, %);
BinOpU(DW_ExprOp_Eq, ==);
BinOpU(DW_ExprOp_Ne, !=);
BinOpS(DW_ExprOp_Ge, >=);
BinOpS(DW_ExprOp_Gt, >);
BinOpS(DW_ExprOp_Le, <=);
BinOpS(DW_ExprOp_Lt, <);
#undef UniOpU
#undef UniOpS
#undef BinOpS
#undef BinOpU
#undef BinOpDiv
case DW_ExprOp_PlusUConst:
{
push_vals[0].u512.u64[0] = popped_vals[0].u512.u64[0] + operands[0].u512.u64[0];
}break;
//- rjf: jumps/branches
case DW_ExprOp_Bra:
{
if(popped_vals[0].u512.u64[0] == 0)
{
break;
}
} // fallthrough
case DW_ExprOp_Skip:
{
S64 jump_delta = operands[0].s64;
if((jump_delta < 0 && -jump_delta > off) || (off + jump_delta >= expr.size))
{
done = 1;
result.status = DW_EvalStatus_Error;
}
else
{
off += jump_delta;
}
}break;
//- rjf: literals
case DW_ExprOp_Lit0: case DW_ExprOp_Lit1: case DW_ExprOp_Lit2: case DW_ExprOp_Lit3:
case DW_ExprOp_Lit4: case DW_ExprOp_Lit5: case DW_ExprOp_Lit6: case DW_ExprOp_Lit7:
case DW_ExprOp_Lit8: case DW_ExprOp_Lit9: case DW_ExprOp_Lit10: case DW_ExprOp_Lit11:
case DW_ExprOp_Lit12: case DW_ExprOp_Lit13: case DW_ExprOp_Lit14: case DW_ExprOp_Lit15:
case DW_ExprOp_Lit16: case DW_ExprOp_Lit17: case DW_ExprOp_Lit18: case DW_ExprOp_Lit19:
case DW_ExprOp_Lit20: case DW_ExprOp_Lit21: case DW_ExprOp_Lit22: case DW_ExprOp_Lit23:
case DW_ExprOp_Lit24: case DW_ExprOp_Lit25: case DW_ExprOp_Lit26: case DW_ExprOp_Lit27:
case DW_ExprOp_Lit28: case DW_ExprOp_Lit29: case DW_ExprOp_Lit30: case DW_ExprOp_Lit31:
{
U8 val = (opcode - DW_ExprOp_Lit0);
push_vals[0].u512.u8[0] = val;
}break;
//- rjf: register reads (opcode-encoded reg)
case DW_ExprOp_Reg0: case DW_ExprOp_Reg1: case DW_ExprOp_Reg2: case DW_ExprOp_Reg3:
case DW_ExprOp_Reg4: case DW_ExprOp_Reg5: case DW_ExprOp_Reg6: case DW_ExprOp_Reg7:
case DW_ExprOp_Reg8: case DW_ExprOp_Reg9: case DW_ExprOp_Reg10: case DW_ExprOp_Reg11:
case DW_ExprOp_Reg12: case DW_ExprOp_Reg13: case DW_ExprOp_Reg14: case DW_ExprOp_Reg15:
case DW_ExprOp_Reg16: case DW_ExprOp_Reg17: case DW_ExprOp_Reg18: case DW_ExprOp_Reg19:
case DW_ExprOp_Reg20: case DW_ExprOp_Reg21: case DW_ExprOp_Reg22: case DW_ExprOp_Reg23:
case DW_ExprOp_Reg24: case DW_ExprOp_Reg25: case DW_ExprOp_Reg26: case DW_ExprOp_Reg27:
case DW_ExprOp_Reg28: case DW_ExprOp_Reg29: case DW_ExprOp_Reg30: case DW_ExprOp_Reg31:
{reg_code_dw = (opcode - DW_ExprOp_Reg0);}goto reg_read;
case DW_ExprOp_RegX:
{reg_code_dw = operands[0].u512.u64[0];}goto reg_read;
reg_read:;
{
ARCH_RegCode reg_code = arch_reg_code_from_dw(arch, reg_code_dw);
Rng1U16 reg_rng = arch_info->reg_code_rng_table[reg_code];
U64 read_size = dim_1u16(reg_rng);
read_size = Min(read_size, sizeof(push_vals[0].u512));
reg_rng.max = reg_rng.min + read_size;
arch_reg_block_read_range(arch_info, regs, reg_rng, &push_vals[0].u512);
}break;
//- rjf: register reads (opcode-encoded reg + offset)
case DW_ExprOp_BReg0: case DW_ExprOp_BReg1: case DW_ExprOp_BReg2: case DW_ExprOp_BReg3:
case DW_ExprOp_BReg4: case DW_ExprOp_BReg5: case DW_ExprOp_BReg6: case DW_ExprOp_BReg7:
case DW_ExprOp_BReg8: case DW_ExprOp_BReg9: case DW_ExprOp_BReg10: case DW_ExprOp_BReg11:
case DW_ExprOp_BReg12: case DW_ExprOp_BReg13: case DW_ExprOp_BReg14: case DW_ExprOp_BReg15:
case DW_ExprOp_BReg16: case DW_ExprOp_BReg17: case DW_ExprOp_BReg18: case DW_ExprOp_BReg19:
case DW_ExprOp_BReg20: case DW_ExprOp_BReg21: case DW_ExprOp_BReg22: case DW_ExprOp_BReg23:
case DW_ExprOp_BReg24: case DW_ExprOp_BReg25: case DW_ExprOp_BReg26: case DW_ExprOp_BReg27:
case DW_ExprOp_BReg28: case DW_ExprOp_BReg29: case DW_ExprOp_BReg30: case DW_ExprOp_BReg31:
{reg_code_dw = (opcode - DW_ExprOp_BReg0);}goto based_reg_read;
case DW_ExprOp_BRegX:
{reg_code_dw = operands[0].u512.u64[0];}goto based_reg_read;
based_reg_read:;
{
ARCH_RegCode reg_code = arch_reg_code_from_dw(arch, reg_code_dw);
Rng1U16 reg_rng = arch_info->reg_code_rng_table[reg_code];
U64 read_size = dim_1u16(reg_rng);
read_size = Min(read_size, sizeof(push_vals[0].u512));
reg_rng.max = reg_rng.min + read_size;
arch_reg_block_read_range(arch_info, regs, reg_rng, &push_vals[0].u512);
push_vals[0].u512.u64[0] += operands[0].s64;
}break;
//- rjf: frame base register read
case DW_ExprOp_FBReg:
{
push_vals[0].u512.u64[0] = (U64)(frame_base + operands[0].s64);
}break;
//- rjf: TLS
case DW_ExprOp_FormTlsAddress:
{
U64 tls_off = popped_vals[0].u512.u64[0];
U64 tls_addr = tls + tls_off;
push_vals[0].u512.u64[0] = tls_addr;
}break;
//- rjf: call frame CFA
case DW_ExprOp_CallFrameCfa:
{
push_vals[0].u512.u64[0] = cfa;
}break;
//- rjf: implicit location descriptions (value is known, location is not)
case DW_ExprOp_ImplicitValue:
{
push_vals[0] = operands[0];
}break;
case DW_ExprOp_StackValue:
{
// NOTE(rjf): nothing to do here - program is over, value (but not location) has
// been evaluated and exists as the top entry on the stack.
}break;
//- rjf: conversions
case DW_ExprOp_Convert:
case DW_ExprOp_Reinterpret:
{
if(operands[0].u512.u64[0] == 0)
{
push_vals[0] = popped_vals[0];
}
else
{
// NOTE(rjf): with a nonzero operand, it is expecting us to read into the
// DWARF debug info and push as that base type. we do not have access to that
// in the general case here, and we need to figure out how to preserve that info
// if we care.
done = 1;
result.status = DW_EvalStatus_UnsupportedOp;
}
}break;
}
//- rjf: do pushes
if(!done && !need_a_break)
{
push_count = Min(push_count, ArrayCount(push_vals));
for(U8 push_idx = 0; push_idx < push_count; push_idx += 1)
{
DW_EvalValNode *n = state->free_val;
if(n != 0)
{
SLLStackPop(state->free_val);
}
else
{
n = push_array(arena, DW_EvalValNode, 1);
}
n->v = push_vals[push_idx];
SLLStackPush(state->top_val, n);
}
}
//- rjf: do not need a break? advance state.
//
// (if we *do* need a break, this means we need to ask the caller for
// more info, like memory reads, and so we want to resume from the
// offset just before this opcode)
//
if(!need_a_break)
{
state->op_idx += 1;
state->off = off;
}
//- rjf: at the end of the expression? -> set success, fill result
if(off == expr.size)
{
result.status = DW_EvalStatus_Good;
if(state->top_val != 0)
{
result.val = state->top_val->v;
}
}
//- rjf: did not advance? -> error
if(off == start_off)
{
result.status = DW_EvalStatus_Error;
break;
}
}
return result;
}
+66
View File
@@ -0,0 +1,66 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DWARF_EXPR_2_H
#define DWARF_EXPR_2_H
////////////////////////////////
//~ rjf: Evaluation Values
typedef struct DW_EvalVal DW_EvalVal;
struct DW_EvalVal
{
union
{
U512 u512;
S64 s64;
};
String8 data;
};
typedef struct DW_EvalValNode DW_EvalValNode;
struct DW_EvalValNode
{
DW_EvalValNode *next;
DW_EvalVal v;
};
////////////////////////////////
//~ rjf: Evaluation Results
typedef enum DW_EvalStatus
{
DW_EvalStatus_Error,
DW_EvalStatus_FailedMemoryRead,
DW_EvalStatus_ExecOpLimitReached,
DW_EvalStatus_UnsupportedOp,
DW_EvalStatus_Good,
}
DW_EvalStatus;
typedef struct DW_Eval DW_Eval;
struct DW_Eval
{
DW_EvalVal val;
DW_EvalStatus status;
Rng1U64 missed_read_vaddr_range;
};
////////////////////////////////
//~ rjf: Evaluation State Machine
typedef struct DW_EvalState DW_EvalState;
struct DW_EvalState
{
U64 off;
U64 op_idx;
DW_EvalValNode *top_val;
DW_EvalValNode *free_val;
};
////////////////////////////////
//~ rjf: Expression Evaluation Functions
internal DW_Eval dw_eval(Arena *arena, DW_Format fmt, Arch arch, MemoryMap *memory_map, void *regs, U64 frame_base, U64 cfa, U64 tls, DW_EvalState *state, String8 expr, U64 op_idx_cap);
#endif // DWARF_EXPR_2_H
-66
View File
@@ -1,66 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal DW_CallFrameInfo
dw_call_frame_info_from_data(Arena *arena, Arch arch, U64 rebase, String8 debug_frame)
{
Temp scratch = scratch_begin(&arena, 1);
// count CIE and FDE entries
U64 cie_count = 0, fde_count = 0;
for (U64 cursor = 0, desc_size; cursor < debug_frame.size; cursor += desc_size) {
DW_DescriptorEntry desc = {0};
desc_size = dw_parse_descriptor_entry_header(debug_frame, cursor, &desc);
if (desc_size == 0) { break; }
if (desc.type == DW_DescriptorEntryType_CIE) {
cie_count += 1;
} else if (desc.type == DW_DescriptorEntryType_FDE) {
fde_count += 1;
}
}
// parse CIEs and build (offset -> CIE) hash table
HashTable *cie_ht = hash_table_init(scratch.arena, (U64)(cie_count * 1.3));
DW_CIE *cie = push_array(arena, DW_CIE, cie_count);
U64 parse_cie_count = 0;
for (U64 cursor = 0, desc_size; cursor < debug_frame.size; cursor += desc_size) {
DW_DescriptorEntry desc = {0};
desc_size = dw_parse_descriptor_entry_header(debug_frame, cursor, &desc);
if (desc_size == 0) { break; }
if (desc.type == DW_DescriptorEntryType_CIE) {
if (dw_parse_cie(str8_skip(debug_frame, cursor), desc.format, arch, &cie[parse_cie_count])) {
hash_table_push_u64_raw(scratch.arena, cie_ht, cursor, &cie[parse_cie_count]);
parse_cie_count += 1;
}
}
}
// parse FDEs
DW_FDE *fde = push_array(arena, DW_FDE, fde_count);
U64 parse_fde_count = 0;
for (U64 cursor = 0, desc_size; cursor < debug_frame.size; cursor += desc_size) {
DW_DescriptorEntry desc = {0};
desc_size = dw_parse_descriptor_entry_header(debug_frame, cursor, &desc);
if (desc_size == 0) { break; }
if (desc.type == DW_DescriptorEntryType_FDE) {
DW_CIE *cie = hash_table_search_u64_raw(cie_ht, desc.cie_pointer);
if (dw_parse_fde(str8_skip(debug_frame, cursor), desc.format, cie, &fde[parse_fde_count])) {
fde[parse_fde_count].pc_range.min += rebase;
fde[parse_fde_count].pc_range.max += rebase;
parse_fde_count += 1;
}
}
}
// fill out result
DW_CallFrameInfo cfi = {0};
cfi.cie_count = parse_cie_count;
cfi.fde_count = parse_fde_count;
cfi.cie = cie;
cfi.fde = fde;
scratch_end(scratch);
return cfi;
}
-18
View File
@@ -1,18 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DWARF_HELP_H
#define DWARF_HELP_H
typedef struct DW_CallFrameInfo
{
U64 cie_count;
U64 fde_count;
DW_CIE *cie;
DW_FDE *fde;
} DW_CallFrameInfo;
internal DW_CallFrameInfo dw_call_frame_info_from_data(Arena *arena, Arch arch, U64 rebase, String8 debug_frame);
#endif // DWARF_HELP_H
+16 -12
View File
@@ -2,17 +2,21 @@
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "dwarf/dwarf.c"
#include "dwarf/dwarf_parse.c"
#include "dwarf/dwarf_expr.c"
#include "dwarf/dwarf_coff.c"
#include "dwarf/dwarf_elf.c"
#include "dwarf/dwarf_unwind.c"
#include "dwarf/dwarf_dump.c"
#include "dwarf/dwarf_help.c"
#include "dwarf/dwarf_writer.c"
// #include "dwarf/dwarf_parse.c"
// #include "dwarf/dwarf_expr.c"
#include "dwarf/dwarf_expr_2.c"
// #include "dwarf/dwarf_unwind.c"
// #include "dwarf/dwarf_dump.c"
#include "dwarf/dwarf_parse_2.c"
#include "dwarf/eh_frame.c"
#include "dwarf/eh_dump.c"
#if defined(X64_H)
# include "dwarf/x64/dwarf_x64.c"
#include "dwarf/dwarf_unwind_2.c"
#include "dwarf/dwarf_dump_2.c"
#if defined(COFF_H)
# include "dwarf/dwarf_parse_coff.c"
#endif
#if defined(ELF_H)
# include "dwarf/dwarf_parse_elf.c"
#endif
#if 0 // TODO(rjf): this uses concepts from the old parser
# include "dwarf/dwarf_writer.c"
#endif
+16 -12
View File
@@ -5,19 +5,23 @@
#define DWARF_INC_H
#include "dwarf/dwarf.h"
#include "dwarf/dwarf_parse.h"
#include "dwarf/dwarf_expr.h"
#include "dwarf/dwarf_coff.h"
#include "dwarf/dwarf_elf.h"
#include "dwarf/dwarf_unwind.h"
#include "dwarf/dwarf_dump.h"
#include "dwarf/dwarf_help.h"
#include "dwarf/dwarf_writer.h"
// #include "dwarf/dwarf_parse.h"
// #include "dwarf/dwarf_expr.h"
#include "dwarf/dwarf_expr_2.h"
// #include "dwarf/dwarf_unwind.h"
// #include "dwarf/dwarf_dump.h"
#include "dwarf/dwarf_parse_2.h"
#include "dwarf/eh_frame.h"
#include "dwarf/eh_dump.h"
#if defined(X64_H)
# include "dwarf/x64/dwarf_x64.h"
#include "dwarf/dwarf_unwind_2.h"
#include "dwarf/dwarf_dump_2.h"
#if defined(COFF_H)
# include "dwarf/dwarf_parse_coff.h"
#endif
#if defined(ELF_H)
# include "dwarf/dwarf_parse_elf.h"
#endif
#if 0 // TODO(rjf): this uses concepts from the old parser
# include "dwarf/dwarf_writer.h"
#endif
#endif // DWARF_INC_H
-42
View File
@@ -1,42 +0,0 @@
--- DWARF NOTES ---------------------------------------------------------------
DWARF V4 Spec: http://www.dwarfstd.org/doc/DWARF4.pdf
DWARF V5 Spec: http://www.dwarfstd.org/doc/DWARF5.pdf
-------------------------------------------------------------------------------
$ (2021/04/30) On .debug_pubtypes, .debug_pubnames, and .debug_names:
.debug_pubtypes and .debug_pubnames are tables that map from a string (the name
of a type or function respectively) to an offset into .debug_info, which is the
offset of the Debug Information Entry (DIE, in DWARF terminology) of the info
associated with the string. THESE TWO SECTIONS ARE OPTIONAL. They don't show up
in every DWARF-holding file, and so they cannot be relied upon as acceleration
structures. But we're going to support parsing them, to make things a bit nicer
in cases where they are present. DWARF doesn't have much in the way of acceler-
ation structures built in, so our rationale is that we should take anything we
can get to make the format a bit more in some subset of the possible cases.
.debug_names is a DWARF V5 section that is intended to replace .debug_pubtypes
and .debug_pubnames. However, even in cases when DWARF V5 is produced at the
time of writing this, we have not found .debug_names sections being produced.
We did not exhaustively test all compilers and configurations, but it seems
that it is not well-supported at all by major compilers, and there's a very low
probability that a user will have that section, so our current thinking is
that there's no point in supporting it right now.
-------------------------------------------------------------------------------
$ (2021/04/30) On producing DWARF V5 with Clang:
https://lists.llvm.org/pipermail/llvm-dev/2018-August/125068.html
By default it looks like (at the time of writing this) that Clang, by default,
will produce DWARF V4. To produce DWARF V5, however, you can use the -gdwarf-5
option. Even when that option is used, it seems that some features of V5 are
not used (for example, .debug_names).
The above link also says that this will produce .debug_names, but it doesn't as
of Clang 10.
-------------------------------------------------------------------------------
+3 -11
View File
@@ -53,18 +53,14 @@ str8_deserial_read_uleb128(String8 string, U64 off, U64 *value_out)
{
U8 byte = 0;
U64 bytes_read = str8_deserial_read_struct(string, cursor, &byte);
if(bytes_read != sizeof(byte))
{
break;
}
U8 val = byte & 0x7fu;
value |= ((U64)val) << shift;
cursor += bytes_read;
shift += 7u;
if((byte & 0x80u) == 0)
{
break;
@@ -92,13 +88,10 @@ str8_deserial_read_sleb128(String8 string, U64 off, S64 *value_out)
{
break;
}
U8 val = byte & 0x7fu;
value |= ((U64)val) << shift;
cursor += bytes_read;
shift += 7u;
if((byte & 0x80u) == 0)
{
if(shift < sizeof(value) * 8 && (byte & 0x40u) != 0)
@@ -2912,7 +2905,7 @@ dw_parse_descriptor_entry_header(String8 data, U64 off, DW_DescriptorEntry *desc
U64 id_type = format == DW_Format_32Bit ? max_U32 : max_U64;
desc_out->format = format;
desc_out->type = (id == id_type) ? DW_DescriptorEntryType_CIE : DW_DescriptorEntryType_FDE;
desc_out->type = (id == id_type) ? DW_DescriptorEntryKind_CIE : DW_DescriptorEntryKind_FDE;
desc_out->entry_range = entry_range;
desc_out->cie_pointer = id;
desc_out->cie_pointer_off = length_size;
@@ -2952,7 +2945,6 @@ dw_parse_cie(String8 data, DW_Format format, Arch arch, DW_CIE *cie_out)
TryRead(str8_deserial_read_sleb128(data, cursor, &data_align_factor), cursor, exit);
U64 ret_addr_reg = 0;
U64 ret_addr_reg_size = 0;
if (version == DW_Version_1) { TryRead(str8_deserial_read(data, cursor, &ret_addr_reg, sizeof(U8), sizeof(U8)), cursor, exit); }
else { TryRead(str8_deserial_read_uleb128(data, cursor, &ret_addr_reg), cursor, exit); }
@@ -3012,14 +3004,14 @@ dw_parse_cfi(String8 data, U64 fde_offset, Arch arch, DW_CIE *cie_out, DW_FDE *f
B32 is_parsed = 0;
DW_DescriptorEntry fde_desc = {0};
dw_parse_descriptor_entry_header(data, fde_offset, &fde_desc);
if (fde_desc.type == DW_DescriptorEntryType_FDE) {
if (fde_desc.type == DW_DescriptorEntryKind_FDE) {
U64 cie_pointer_off = fde_desc.format == DW_Format_32Bit ? 4 : 12;
U64 cie_pointer = 0;
U64 cie_pointer_size = str8_deserial_read_dwarf_uint(data, fde_offset + cie_pointer_off, fde_desc.format, &cie_pointer);
if (cie_pointer_size) {
DW_DescriptorEntry cie_desc = {0};
dw_parse_descriptor_entry_header(data, cie_pointer, &cie_desc);
if (cie_desc.type == DW_DescriptorEntryType_CIE) {
if (cie_desc.type == DW_DescriptorEntryKind_CIE) {
if (dw_parse_cie(str8_substr(data, cie_desc.entry_range), cie_desc.format, arch, cie_out)) {
if (dw_parse_fde(str8_substr(data, fde_desc.entry_range), fde_desc.format, cie_out, fde_out)) {
is_parsed = 1;
-18
View File
@@ -335,22 +335,6 @@ typedef struct DW_Expr
////////////////////////////////
// .debug_frame
typedef enum
{
DW_DescriptorEntryType_Null,
DW_DescriptorEntryType_CIE,
DW_DescriptorEntryType_FDE
} DW_DescriptorEntryType;
typedef struct DW_DescriptorEntry
{
DW_DescriptorEntryType type;
DW_Format format;
Rng1U64 entry_range;
U64 cie_pointer_off;
U64 cie_pointer;
} DW_DescriptorEntry;
typedef struct DW_CIE
{
String8 insts;
@@ -418,8 +402,6 @@ internal U64 dw_hash_from_string(String8 string);
internal U64 str8_deserial_read_dwarf_packed_size(String8 string, U64 off, U64 *size_out);
internal U64 str8_deserial_read_dwarf_uint (String8 string, U64 off, DW_Format format, U64 *uint_out);
internal U64 str8_deserial_read_uleb128 (String8 string, U64 off, U64 *value_out);
internal U64 str8_deserial_read_sleb128 (String8 string, U64 off, S64 *value_out);
internal U64 str8_deserial_read_uleb128_array(Arena *arena, String8 string, U64 off, U64 count, U64 **arr_out);
internal U64 str8_deserial_read_sleb128_array(Arena *arena, String8 string, U64 off, U64 count, S64 **arr_out);
+611 -122
View File
File diff suppressed because it is too large. Load diff
+172 -7
View File
@@ -4,6 +4,23 @@
#ifndef DWARF_PARSE_2_H
#define DWARF_PARSE_2_H
////////////////////////////////
//~ rjf: Raw Section Data
typedef struct DW_Section DW_Section;
struct DW_Section
{
String8 name;
String8 data;
};
typedef struct DW_Raw DW_Raw;
struct DW_Raw
{
DW_Section sec[DW_SectionKind_COUNT];
DW_Section sup[DW_SectionKind_COUNT];
};
////////////////////////////////
//~ rjf: Unit Headers
@@ -21,6 +38,37 @@ struct DW2_UnitHeader
////////////////////////////////
//~ rjf: Abbreviation Map (ID -> .debug_abbrev Offset)
typedef struct DW2_AbbrevAttrib DW2_AbbrevAttrib;
struct DW2_AbbrevAttrib
{
DW_AttribKind attrib_kind;
DW_FormKind form_kind;
U64 implicit_const;
};
typedef struct DW2_AbbrevAttribNode DW2_AbbrevAttribNode;
struct DW2_AbbrevAttribNode
{
DW2_AbbrevAttribNode *next;
DW2_AbbrevAttrib v;
};
typedef struct DW2_AbbrevAttribList DW2_AbbrevAttribList;
struct DW2_AbbrevAttribList
{
DW2_AbbrevAttribNode *first;
DW2_AbbrevAttribNode *last;
U64 count;
};
typedef struct DW2_AbbrevHeader DW2_AbbrevHeader;
struct DW2_AbbrevHeader
{
U64 id;
DW_TagKind tag_kind;
B8 has_children;
};
typedef struct DW2_AbbrevMapNode DW2_AbbrevMapNode;
struct DW2_AbbrevMapNode
{
@@ -36,6 +84,14 @@ struct DW2_AbbrevMap
U64 slots_count;
};
typedef struct DW2_UnitAbbrevMapMap DW2_UnitAbbrevMapMap;
struct DW2_UnitAbbrevMapMap
{
U64 map_count;
DW2_AbbrevMap *map_from_idx_table;
DW2_AbbrevMap **map_from_unit_idx_table;
};
////////////////////////////////
//~ rjf: Offset Tables (.debug_str_offsets, .debug_rnglists)
@@ -51,18 +107,55 @@ struct DW2_OffsetTable
void *entries;
};
typedef struct DW2_OffsetTableNode DW2_OffsetTableNode;
struct DW2_OffsetTableNode
{
DW2_OffsetTableNode *next;
DW2_OffsetTable v;
Rng1U64 range;
};
typedef struct DW2_OffsetTableList DW2_OffsetTableList;
struct DW2_OffsetTableList
{
DW2_OffsetTableNode *first;
DW2_OffsetTableNode *last;
U64 count;
};
typedef struct DW2_OffsetTableSet DW2_OffsetTableSet;
struct DW2_OffsetTableSet
{
// rjf: .debug_str_offsets
U64 str_offsets_tables_count;
DW2_OffsetTable *str_offsets_tables;
Rng1U64 *str_offsets_tables_ranges;
// rjf: .debug_rnglists
U64 rnglists_tables_count;
DW2_OffsetTable *rnglists_tables;
Rng1U64 *rnglists_tables_ranges;
// rjf: .debug_addr
U64 addr_tables_count;
DW2_OffsetTable *addr_tables;
Rng1U64 *addr_tables_ranges;
// rjf: .debug_loclists
U64 loclists_tables_count;
DW2_OffsetTable *loclists_tables;
Rng1U64 *loclists_tables_ranges;
};
////////////////////////////////
//~ rjf: Parsing Context Bundle
typedef struct DW2_ParseCtx DW2_ParseCtx;
struct DW2_ParseCtx
{
DW_Raw *raw;
// NOTE(rjf): all subsequent fields optional - top-level code fills out whatever
// it can from context.
DW_Version version;
DW_Format format;
DW_Ext ext;
DW_ExtFlags exts;
U64 addr_size;
U64 unit_base_addr;
U64 unit_base_info_off;
@@ -73,6 +166,7 @@ struct DW2_ParseCtx
DW2_OffsetTable *loclists_table;
String8 unit_dir;
String8 unit_file;
DW_Language language;
};
////////////////////////////////
@@ -229,6 +323,57 @@ struct DW2_LocList
U64 count;
};
////////////////////////////////
//~ rjf: Unwind Info
typedef enum DW_CFIKind
{
DW_CFIKind_Null,
DW_CFIKind_CIE, // Common Information Entry
DW_CFIKind_FDE, // Frame Description Entry
DW_CFIKind_COUNT
}
DW_CFIKind;
typedef struct DW_CFIHeader DW_CFIHeader;
struct DW_CFIHeader
{
DW_CFIKind kind;
DW_Format fmt;
Rng1U64 entry_range;
U64 cie_pointer_off;
U64 cie_pointer;
};
typedef struct DW_CIE DW_CIE;
struct DW_CIE
{
Rng1U64 aug_string_range;
Rng1U64 aug_data_range;
U64 code_align_factor;
S64 data_align_factor;
U64 ret_addr_reg;
U64 ext[4];
DW_Format format;
U8 version;
U8 address_size;
U8 segment_selector_size;
};
typedef struct DW_FDE DW_FDE;
struct DW_FDE
{
U64 cie_pointer;
Rng1U64 pc_range;
};
typedef struct DW_CFI DW_CFI;
struct DW_CFI
{
DW_CIE cie;
DW_FDE fde;
};
////////////////////////////////
//~ rjf: Globals
@@ -239,6 +384,7 @@ global read_only DW2_Attrib dw2_attrib_nil = {0};
internal U64 dw2_read_initial_length(String8 data, U64 off, U64 *out, DW_Format *fmt_out);
internal U64 dw2_read_fmt_u64(String8 data, U64 off, DW_Format format, U64 *out);
internal DW_SectionKind dw_section_kind_from_string(String8 string);
////////////////////////////////
//~ rjf: Unit Range Set Parsing (Top-Level .debug_info, .debug_aranges)
@@ -253,30 +399,41 @@ internal U64 dw2_read_unit_header(String8 data, U64 off, DW2_UnitHeader *out);
////////////////////////////////
//~ rjf: Abbreviation Map Parsing
internal U64 dw2_read_abbrev(Arena *arena, String8 data, U64 off, DW2_AbbrevHeader *header_out, DW2_AbbrevAttribList *attribs_out);
internal DW2_AbbrevMap dw2_abbrev_map_from_data(Arena *arena, String8 data, U64 off);
internal DW2_UnitAbbrevMapMap dw2_unit_abbrev_map_map_from_data(Arena *arena, String8 data, DW2_UnitHeader *unit_headers, U64 unit_headers_count);
////////////////////////////////
//~ rjf: Form Value Parsing
internal U64 dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kind, U64 implicit_const, DW2_FormVal *out);
internal U64 dw2_read_form_val(DW_Raw *raw, DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kind, U64 implicit_const, DW2_FormVal *out);
////////////////////////////////
//~ rjf: Parse Context Construction
internal void dw2_parse_ctx_equip_unit_header(DW2_ParseCtx *ctx_out, DW2_UnitHeader *hdr, U64 unit_base_info_off);
internal void dw2_parse_ctx_equip_unit_abbrev_map(DW2_ParseCtx *ctx_out, DW2_UnitAbbrevMapMap *map, U64 unit_idx);
internal void dw2_parse_ctx_equip_unit_root_tag(DW2_ParseCtx *ctx_out, DW2_Tag *tag, DW2_OffsetTableSet *offset_tables);
////////////////////////////////
//~ rjf: Tag Parsing
internal U64 dw2_read_tag(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *tag_out);
internal U64 dw2_read_tag(Arena *arena, DW_Raw *raw, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *tag_out);
internal DW2_Attrib *dw2_attrib_from_kind(DW2_Tag *tag, DW_AttribKind kind);
internal U64 dw2_reference_info_off_from_form_val(DW2_ParseCtx *ctx, DW2_FormVal *v);
////////////////////////////////
//~ rjf: Line Table Parsing
internal U64 dw2_read_line_table_header(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_LineTableHeader *out);
internal U64 dw2_read_line_table_header(Arena *arena, DW_Raw *raw, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_LineTableHeader *out);
////////////////////////////////
//~ rjf: Offset Table Parsing (.debug_str_offsets, .debug_rnglists)
internal U64 dw2_read_offset_table(String8 data, U64 off, DW2_OffsetTable *out);
internal DW2_OffsetTableList dw2_offset_table_list_from_data(Arena *arena, String8 data);
internal B32 dw2_try_offset_from_table_idx(DW2_OffsetTable *tbl, U64 idx, U64 *out);
internal DW2_OffsetTableSet dw2_offset_table_set_from_raw(Arena *arena, DW_Raw *raw);
////////////////////////////////
//~ rjf: Range List Parsing (.debug_rnglists)
@@ -288,4 +445,12 @@ internal Rng1U64List dw2_rnglist_from_form_val(Arena *arena, DW2_ParseCtx *ctx,
internal DW2_LocList dw2_loclist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, DW_Raw *raw, DW2_FormVal form_val);
////////////////////////////////
//~ rjf: Unwind Info Parsing (.debug_frame)
internal U64 dw2_read_cfi_header(String8 data, U64 off, DW_CFIHeader *cfi_header_out);
internal U64 dw2_read_cie(String8 data, U64 off, DW_Format fmt, Arch arch, DW_CIE *cie_out);
internal U64 dw2_read_fde(String8 data, U64 off, DW_Format fmt, Arch arch, DW_CIE *cie, DW_FDE *fde_out);
internal B32 dw2_try_cfi_from_data(String8 data, U64 off, Arch arch, DW_CIE *cie_out, DW_FDE *fde_out);
#endif // DWARF_PARSE_2_H
+45
View File
@@ -0,0 +1,45 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal B32
dw_is_dwarf_present_coff_section_table(String8 string_table, U64 section_count, COFF_SectionHeader *section_table)
{
B32 is_dwarf_present = 0;
for EachIndex(idx, section_count)
{
COFF_SectionHeader *header = &section_table[idx];
String8 name = coff_name_from_section_header(string_table, header);
DW_SectionKind s = dw_section_kind_from_string(name);
is_dwarf_present = s != DW_SectionKind_Null;
if(is_dwarf_present)
{
break;
}
}
return is_dwarf_present;
}
internal DW_Raw
dw_input_from_coff_section_table(Arena *arena, String8 raw_image, String8 string_table, U64 section_count, COFF_SectionHeader *section_table)
{
DW_Raw input = {0};
B32 sect_status[ArrayCount(input.sec)] = {0};
for EachIndex(idx, section_count)
{
COFF_SectionHeader *header = &section_table[idx];
Rng1U64 raw_data_range = rng_1u64(header->foff, header->vsize ? header->foff + header->vsize : header->foff + header->fsize);
String8 name = coff_name_from_section_header(string_table, header);
DW_SectionKind s = dw_section_kind_from_string(name);
if(s != DW_SectionKind_Null)
{
if(!sect_status[s])
{
sect_status[s] = 1;
DW_Section *d = &input.sec[s];
d->name = push_str8_copy(arena, name);
d->data = str8_substr(raw_image, raw_data_range);
}
}
}
return input;
}
@@ -1,11 +1,11 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DWARF_COFF_H
#define DWARF_COFF_H
#ifndef DWARF_PARSE_COFF_H
#define DWARF_PARSE_COFF_H
internal B32 dw_is_dwarf_present_coff_section_table(String8 string_table, U64 section_count, COFF_SectionHeader *section_table);
internal DW_Raw dw_input_from_coff_section_table(Arena *arena, String8 raw_image, String8 string_table, U64 section_count, COFF_SectionHeader *section_table);
#endif // DWARF_COFF_H
#endif // DWARF_PARSE_COFF_H
@@ -11,11 +11,7 @@ dw_is_dwarf_present_from_elf_bin(String8 data, ELF_Bin *bin)
if(shdr->sh_type != ELF_ShType_ProgBits) { continue; }
String8 name = elf_name_from_shdr64(data, bin, shdr);
DW_SectionKind s = dw_section_kind_from_string(name);
if(s == DW_Section_Null)
{
s = dw_section_dwo_kind_from_string(name);
}
is_dwarf_present = (s != DW_Section_Null);
is_dwarf_present = (s != DW_SectionKind_Null);
if(is_dwarf_present)
{
break;
@@ -28,7 +24,7 @@ dw_is_dwarf_present_from_elf_bin(String8 data, ELF_Bin *bin)
#include "third_party/sinfl/sinfl.h"
internal DW_Raw
dw_input_from_elf_bin(Arena *arena, String8 data, ELF_Bin *bin)
dw_raw_from_elf_bin(Arena *arena, String8 data, ELF_Bin *bin)
{
DW_Raw result = {0};
B32 is_section_present[ArrayCount(result.sec)] = {0};
@@ -41,14 +37,8 @@ dw_input_from_elf_bin(Arena *arena, String8 data, ELF_Bin *bin)
String8 section_name = elf_name_from_shdr64(data, bin, shdr);
DW_SectionKind section_kind = dw_section_kind_from_string(section_name);
String8 section_data__maybe_compressed = str8_substr(data, r1u64(shdr->sh_offset, shdr->sh_offset + shdr->sh_size));
B32 is_dwo = 0;
if(section_kind == DW_Section_Null)
{
section_kind = dw_section_dwo_kind_from_string(section_name);
is_dwo = (section_kind != DW_Section_Null);
}
if(section_kind == DW_Section_Null) { continue; } // skip unknown sections
if(section_kind == DW_SectionKind_Null) { continue; } // skip unknown sections
if(is_section_present[section_kind]) { continue; } // skip duplicate sections
//- rjf: decompress section data if needed
@@ -108,7 +98,6 @@ dw_input_from_elf_bin(Arena *arena, String8 data, ELF_Bin *bin)
DW_Section *d = &result.sec[section_kind];
d->name = push_str8_copy(arena, section_name);
d->data = section_data__uncompressed;
d->is_dwo = is_dwo;
}
return result;
}
@@ -1,10 +1,10 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DWARF_ELF_H
#define DWARF_ELF_H
#ifndef DWARF_PARSE_ELF_H
#define DWARF_PARSE_ELF_H
internal B32 dw_is_dwarf_present_from_elf_bin(String8 raw_image, ELF_Bin *bin);
internal DW_Raw dw_input_from_elf_bin(Arena *arena, String8 raw_image, ELF_Bin *bin);
internal DW_Raw dw_raw_from_elf_bin(Arena *arena, String8 raw_image, ELF_Bin *bin);
#endif // DWARF_ELF_H
#endif // DWARF_PARSE_ELF_H
+2
View File
@@ -0,0 +1,2 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
+44
View File
@@ -0,0 +1,44 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DWARF_UNWIND_2_H
#define DWARF_UNWIND_2_H
typedef enum DW_UnwindRuleCode
{
DW_UnwindRuleCode_SameVal,
DW_UnwindRuleCode_Undefined,
DW_UnwindRuleCode_Off,
DW_UnwindRuleCode_ValOff,
DW_UnwindRuleCode_Reg,
DW_UnwindRuleCode_Expr,
DW_UnwindRuleCode_ValExpr,
DW_UnwindRuleCode_Architectural,
}
DW_UnwindRuleCode;
typedef struct DW_UnwindRule DW_UnwindRule;
struct DW_UnwindRule
{
DW_UnwindRuleCode code;
DW_RegCode reg_code;
S64 s64;
String8 expr;
};
typedef struct DW_CFIRow DW_CFIRow;
struct DW_CFIRow
{
U64 base_pc_off;
DW_UnwindRule cfa_rule;
DW_UnwindRule *reg_rules;
};
typedef struct DW_CFIRowNode DW_CFIRowNode;
struct DW_CFIRowNode
{
DW_CFIRowNode *next;
DW_CFIRow v;
};
#endif // DWARF_UNWIND_2_H
+231 -1
View File
@@ -97,6 +97,119 @@ dw_writer_tag_chunk_list_push(Arena *arena, DW_WriterTagChunkList *list, U64 cap
return result;
}
internal U64
dw_write_to_buffer_uleb128(U8 buffer[10], U64 v)
{
U64 buffer_size = 0;
U64 value = v;
do {
U8 byte = value & 0x7f;
value >>= 7;
if (value != 0) {
byte |= 0x80;
}
Assert(buffer_size < 10);
buffer[buffer_size++] = byte;
} while (value > 0);
return buffer_size;
}
internal U64
dw_write_to_buffer_sleb128(U8 buffer[10], U64 v)
{
U64 buffer_size = 0;
for (S64 value = v, more = 1; more != 0; ) {
U8 byte = value & 0x7f;
value >>= 7;
U8 sign_bit = byte & 0x40;
if ((value == 0 && sign_bit == 0) || (value == -1 && sign_bit != 0)) {
more = 0;
} else {
byte |= 0x80;
}
Assert(buffer_size < 10);
buffer[buffer_size++] = byte;
}
return buffer_size;
}
internal String8
dw_write_uleb128(Arena *arena, U64 v)
{
U8 buffer[10];
U64 buffer_size = dw_write_to_buffer_uleb128(buffer, v);
return str8_copy(arena, str8(buffer, buffer_size));
}
internal String8
dw_write_sleb128(Arena *arena, S64 v)
{
U8 buffer[10];
U64 buffer_size = dw_write_to_buffer_sleb128(buffer, v);
return str8_copy(arena, str8(buffer, buffer_size));
}
internal U64
dw_size_from_uleb128(U64 v)
{
U8 buffer[10];
return dw_write_to_buffer_uleb128(buffer, v);
}
internal U64
dw_size_from_sleb128(S64 v)
{
U8 buffer[10];
return dw_write_to_buffer_sleb128(buffer, v);
}
internal void *
dw_serial_push_length(Arena *arena, String8List *srl, DW_Format format, U64 length)
{
void *result;
if (format == DW_Format_64Bit) {
U8 buffer[sizeof(U32) + sizeof(U64)];
MemorySet(&buffer[0], 0xff, sizeof(U32));
MemoryCopy(buffer + sizeof(U32), &length, sizeof(length));
result = str8_serial_push_string(arena, srl, str8_array_fixed(buffer));
} else {
result = str8_serial_push_string(arena, srl, str8((U8 *)&length, sizeof(U32)));
}
return result;
}
internal void *
dw_serial_push_uint(Arena *arena, String8List *srl, DW_Format format, U64 v)
{
String8 uint;
if (format == DW_Format_64Bit) {
U64 *ptr = push_array(arena, U64, 1);
*ptr = v;
uint = str8((U8 *)ptr, sizeof(*ptr));
} else {
U32 *ptr = push_array(arena, U32, 1);
*ptr = safe_cast_u32(v);
uint = str8((U8 *)ptr, sizeof(*ptr));
}
return str8_serial_push_string(arena, srl, uint);
}
internal void *
dw_serial_push_uleb128(Arena *arena, String8List *srl, U64 v)
{
U8 buffer[10];
U64 buffer_size = dw_write_to_buffer_uleb128(buffer, v);
return str8_serial_push_string(arena, srl, str8(buffer, buffer_size));
}
internal void *
dw_serial_push_sleb128(Arena *arena, String8List *srl, S64 v)
{
U8 buffer[10];
U64 buffer_size = dw_write_to_buffer_sleb128(buffer, v);
return str8_serial_push_string(arena, srl, str8(buffer, buffer_size));
}
internal DW_Writer *
dw_writer_begin(DW_Format format, DW_Version version, DW_CompUnitKind cu_kind, Arch arch)
{
@@ -1195,7 +1308,18 @@ dw_writer_emit(DW_Writer *writer)
str8_serial_push_struct(writer->arena, srl, &writer->line.opcode_base);
// (12) standard_opcode_lengths
U8 *std_op_lens = push_array(writer->arena, U8, writer->line.opcode_base - 1);
for (U64 i = 1; i < writer->line.opcode_base; i += 1) { std_op_lens[i-1] = dw_length_from_std_opcode(i); }
for (U64 i = 1; i < writer->line.opcode_base; i += 1)
{
U64 len = 0;
switch(i)
{
default:{}break;
#define X(name, code, len_) case DW_StdOpcode_##name:{len = (len_);}break;
DW_StdOpcode_XList
#undef X
}
std_op_lens[i-1] = len;
}
str8_serial_push_string(writer->arena, srl, str8(std_op_lens, writer->line.opcode_base - 1));
// directory table
str8_list_concat_in_place(srl, &dir_table_srl);
@@ -1277,3 +1401,109 @@ dw_writer_emit_to_obj(DW_Writer *writer, OBJ *obj)
}
#endif
internal String8
dw_encode_expr(Arena *arena, Arch arch, DW_Format format, DW_ExprEnc *encs, U64 encs_count)
{
Temp scratch = scratch_begin(&arena, 1);
String8List *srl = push_array(scratch.arena, String8List, 1);
str8_serial_begin(scratch.arena, srl);
HashTable *label_map = hash_table_init(scratch.arena, 128);
struct Fixup { U64 offset; String8 label; struct Fixup *next; };
struct Fixup *first_fixup = 0, *last_fixup = 0;
for EachIndex(i, encs_count) {
DW_ExprEnc *e = &encs[i];
switch (e->type) {
case DW_ExprEncType_Null: {} break;
case DW_ExprEncType_Op: {
str8_serial_push_struct(scratch.arena, srl, &e->op);
} break;
case DW_ExprEncType_U8: {
str8_serial_push_struct(scratch.arena, srl, &e->u8);
} break;
case DW_ExprEncType_U16: {
str8_serial_push_struct(scratch.arena, srl, &e->u16);
} break;
case DW_ExprEncType_U32: {
str8_serial_push_struct(scratch.arena, srl, &e->u32);
} break;
case DW_ExprEncType_U64: {
str8_serial_push_struct(scratch.arena, srl, &e->u64);
} break;
case DW_ExprEncType_S8: {
str8_serial_push_struct(scratch.arena, srl, &e->s8);
} break;
case DW_ExprEncType_S16: {
str8_serial_push_struct(scratch.arena, srl, &e->s16);
} break;
case DW_ExprEncType_S32: {
str8_serial_push_struct(scratch.arena, srl, &e->s32);
} break;
case DW_ExprEncType_S64: {
str8_serial_push_struct(scratch.arena, srl, &e->s64);
} break;
case DW_ExprEncType_ULEB128: {
str8_serial_push_string(scratch.arena, srl, dw_write_uleb128(scratch.arena, e->u64));
} break;
case DW_ExprEncType_SLEB128: {
str8_serial_push_string(scratch.arena, srl, dw_write_sleb128(scratch.arena, e->s64));
} break;
case DW_ExprEncType_Addr: {
U64 addr_size = byte_size_from_arch(arch);
Assert(addr_size <= sizeof(e->addr));
str8_serial_push_string(scratch.arena, srl, str8((U8 *)&e->addr, addr_size));
} break;
case DW_ExprEncType_Block: {
Assert(e->block.size <= max_U8);
str8_serial_push_u8(scratch.arena, srl, (U8)e->block.size);
str8_serial_push_string(scratch.arena, srl, e->block);
} break;
case DW_ExprEncType_DwarfUInt: {
switch (format) {
case DW_Format_Null: {} break;
case DW_Format_32Bit: { str8_serial_push_u32(scratch.arena, srl, e->u32); } break;
case DW_Format_64Bit: { str8_serial_push_u64(scratch.arena, srl, e->u64); } break;
default: { InvalidPath; } break;
}
} break;
case DW_ExprEncType_Label: {
struct Fixup *fixup = push_array(scratch.arena, struct Fixup, 1);
fixup->offset = srl->total_size;
fixup->label = e->label;
SLLQueuePush(first_fixup, last_fixup, fixup);
S16 delta_placeholder = 0;
str8_serial_push_struct(scratch.arena, srl, &delta_placeholder);
} break;
case DW_ExprEncType_DeclLabel: {
hash_table_push_string_u64(scratch.arena, label_map, e->label, srl->total_size);
} break;
default: { InvalidPath; } break;
}
}
// finalize expression
String8 expr = str8_serial_end(arena, srl);
// apply fixups
for EachNode(fixup, struct Fixup, first_fixup) {
U64 label_offset;
if (!hash_table_search_string_u64(label_map, fixup->label, &label_offset)) {
Assert(0 && "undefined label");
continue;
}
S64 delta = (S64)label_offset - (S64)fixup->offset;
AssertAlways(min_S16 <= delta && delta <= max_S16);
S16 *delta_ptr = (S16 *)(expr.str + fixup->offset);
*delta_ptr = delta;
}
scratch_end(scratch);
return expr;
}
+20
View File
@@ -245,6 +245,23 @@ typedef struct DW_Writer
DW_WriterTagChunkList tag_chunk_list;
} DW_Writer;
////////////////////////////////
// Serializers
internal U64 dw_write_to_buffer_uleb128(U8 buffer[10], U64 v);
internal U64 dw_write_to_buffer_sleb128(U8 buffer[10], U64 v);
internal String8 dw_write_uleb128(Arena *arena, U64 v);
internal String8 dw_write_sleb128(Arena *arena, S64 v);
internal U64 dw_size_from_uleb128(U64 v);
internal U64 dw_size_from_sleb128(S64 v);
internal void * dw_serial_push_length(Arena *arena, String8List *srl, DW_Format format, U64 length);
internal void * dw_serial_push_uint(Arena *arena, String8List *srl, DW_Format format, U64 v);
internal void * dw_serial_push_uleb128(Arena *arena, String8List *srl, U64 v);
internal void * dw_serial_push_sleb128(Arena *arena, String8List *srl, S64 v);
////////////////////////////////
// Writer
@@ -334,5 +351,8 @@ internal DW_IntEnc dw_int_enc_from_uint(U64 v);
internal U64 dw_size_from_sint(S64 v);
internal U64 dw_size_from_uint(U64 v);
// encoder
internal String8 dw_encode_expr(Arena *arena, Arch arch, DW_Format format, DW_ExprEnc *encs, U64 encs_count);
#endif // DWARF_WRITER_H
-141
View File
@@ -1,141 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal String8List
eh_dump_list_from_data(Arena *arena, Arch arch, U64 eh_frame_hdr_vaddr, U64 eh_frame_vaddr, String8 eh_frame_hdr, String8 eh_frame, EH_DumpSubsetFlags subset_flags)
{
Temp scratch = scratch_begin(&arena, 1);
String8List strings = {0};
#define dumpf(...) str8_list_pushf(arena, &strings, __VA_ARGS__)
EH_PtrCtx ptr_ctx = { .data_vaddr = eh_frame_hdr_vaddr };
EH_FrameHdr hdr = eh_parse_frame_hdr(eh_frame_hdr, byte_size_from_arch(arch), &ptr_ctx);
DW_Ext ext = DW_Ext_All;
if (subset_flags & EH_DumpSubsetFlag_EhFrameHdr) {
dumpf("eh_frame_hdr:\n");
dumpf("{\n");
dumpf(" version: %u\n", hdr.version);
dumpf(" eh_frame_ptr_enc: %S\n", eh_string_from_ptr_enc(scratch.arena, hdr.eh_frame_ptr_enc));
dumpf(" table_enc: %S\n", eh_string_from_ptr_enc(scratch.arena, hdr.table_enc));
dumpf(" fde_count: %llu\n", hdr.fde_count);
if (hdr.eh_frame_ptr_enc != EH_PtrEnc_Omit) {
dumpf(" eh_frame_ptr: 0x%I64x\n", hdr.eh_frame_ptr);
}
dumpf(" Entries:\n");
dumpf(" {\n");
for (U64 cursor = 0; cursor < hdr.table.size; ) {
U64 entry_off = cursor;
U64 pc = 0;
U64 pc_size = eh_parse_ptr(hdr.table, cursor, cursor, &ptr_ctx, hdr.table_enc, &pc);
if (pc_size == 0) { break; }
cursor += pc_size;
U64 fde_addr = 0;
U64 fde_addr_size = eh_parse_ptr(hdr.table, cursor, cursor, &ptr_ctx, hdr.table_enc, &fde_addr);
if (fde_addr_size == 0) { break; }
cursor += fde_addr_size;
U64 fde_offset = fde_addr - eh_frame_vaddr;
dumpf(" { off=0x%04I64x, pc=0x%I64x, fde=0x%I64x }\n", entry_off, pc, fde_offset);
}
dumpf(" }\n");
dumpf("}\n");
}
if (subset_flags & EH_DumpSubsetFlag_EhFrame) {
dumpf(".eh_frame:\n");
dumpf("{\n");
for (U64 cursor = 0; cursor < eh_frame.size; ) {
DW_DescriptorEntry desc = {0};
U64 desc_size = eh_parse_descriptor_entry_header(eh_frame, cursor, &desc);
if (desc_size == 0) { break; }
switch (desc.type) {
case DW_DescriptorEntryType_Null: break;
case DW_DescriptorEntryType_CIE: {
String8 cie_data = str8_substr(eh_frame, desc.entry_range);
DW_CIE cie = {0};
if (eh_parse_cie(cie_data, desc.format, arch, eh_frame_vaddr + cursor, &ptr_ctx, &cie)) {
String8List init_insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, DW_Version_5, ext, cie.format, 0, &cie, eh_decode_ptr, &ptr_ctx, cie.insts);
dumpf(" CIE: // entry range: %r\n", desc.entry_range);
dumpf(" {\n");
dumpf(" Format: %S\n", dw_string_from_format(desc.format));
dumpf(" Version: %u\n", cie.version);
dumpf(" Aug string: \"%S\"\n", cie.aug_string);
dumpf(" Code align: %I64u\n", cie.code_align_factor);
dumpf(" Data align: %I64d\n", cie.data_align_factor);
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);
}
dumpf(" Initial Insturction:\n");
dumpf(" {\n");
for EachNode(n, String8Node, init_insts_str_list.first) { dumpf(" %S\n", n->string); }
dumpf(" }\n");
dumpf(" }\n");
} else {
dumpf("ERROR: unable to parse CIE @ %I64x\n", desc.entry_range.min);
}
} break;
case DW_DescriptorEntryType_FDE: {
U64 cie_offset = desc.cie_pointer_off - desc.cie_pointer;
DW_CIE cie = {0};
{
DW_DescriptorEntry cie_desc = {0};
eh_parse_descriptor_entry_header(eh_frame, cie_offset, &cie_desc);
if (cie_desc.type == DW_DescriptorEntryType_CIE) {
String8 cie_data = str8_substr(eh_frame, cie_desc.entry_range);
eh_parse_cie(cie_data, cie_desc.format, arch, eh_frame_vaddr + cie_offset, &ptr_ctx, &cie);
}
}
String8 fde_raw = str8_substr(eh_frame, desc.entry_range);
DW_FDE fde = {0};
if (eh_parse_fde(fde_raw, desc.format, eh_frame_vaddr + cursor, &cie, &ptr_ctx, &fde)) {
String8List insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, hdr.version, ext, fde.format, 0, &cie, eh_decode_ptr, &ptr_ctx, fde.insts);
dumpf(" FDE: // entry range: %r\n", desc.entry_range);
dumpf(" {\n");
{
dumpf(" Format: %S\n", dw_string_from_format(fde.format));
dumpf(" CIE: 0x%I64x\n", cie_offset);
dumpf(" PC range: %r\n", fde.pc_range);
dumpf(" Instructions:\n");
dumpf(" {\n");
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, eh_decode_ptr, &ptr_ctx);
do {
String8 cfa_str = dw_string_from_cfa(scratch.arena, arch, cie.address_size, hdr.version, ext, fde.format, cfi_unwind->row->cfa);
String8 cfi_row_str = dw_string_from_cfi_row(scratch.arena, arch, cie.address_size, hdr.version, ext, fde.format, cfi_unwind->row);
dumpf(" { PC: 0x%I64x, CFA: %-7S, Rules: { %S }\n", cfi_unwind->pc, cfa_str, cfi_row_str);
} while (dw_cfi_next_row(scratch.arena, cfi_unwind));
dumpf(" }\n");
}
} else {
dumpf("ERROR: unable to parse FDE @ %I64x\n", desc.entry_range.min);
}
dumpf(" }\n");
} break;
}
cursor += desc_size;
}
dumpf("}\n");
}
#undef dumpf
scratch_end(scratch);
return strings;
}
-48
View File
@@ -1,48 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef EH_FRAME_DUMP_H
#define EH_FRAME_DUMP_H
////////////////////////////////
//~ Dump Subset Types
#define EH_DumpSubset_XList \
X(EhFrameHdr, eh_frame_hdr, ".eh_frame_hdr") \
X(EhFrame, eh_frame, ".eh_frame")
typedef enum EH_DumpSubset {
#define X(name, name_lower, title) EH_DumpSubset_##name,
EH_DumpSubset_XList
#undef X
} EH_DumpSubset;
typedef U32 EH_DumpSubsetFlags;
enum {
#define X(name, name_lower, title) EH_DumpSubsetFlag_##name = (1<<EH_DumpSubset_##name),
EH_DumpSubset_XList
#undef X
EH_DumpSubsetFlag_All = 0xffffffff,
};
read_only global String8 eh_name_lowercase_from_dump_subset_table[] =
{
#define X(name, name_lower, title) str8_lit_comp(#name_lower),
EH_DumpSubset_XList
#undef X
};
read_only global String8 eh_name_title_from_dump_subset_table[] =
{
#define X(name, name_lower, title) str8_lit_comp(title),
EH_DumpSubset_XList
#undef X
};
////////////////////////////////
//~ Dump Entry Point
internal String8List eh_dump_list_from_data(Arena *arena, Arch arch, U64 eh_frame_hdr_vaddr, U64 eh_frame_vaddr, String8 eh_frame_hdr, String8 eh_frame, EH_DumpSubsetFlags subset_flags);
#endif // EH_FRAME_DUMP_H
-470
View File
@@ -1,470 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal U64
eh_parse_ptr(String8 frame_base, U64 off, U64 pc, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_out)
{
U64 ptr_off = off;
if (encoding == EH_PtrEnc_Omit) {
return 0;
}
// align read offset as needed
if (encoding == EH_PtrEnc_Aligned) {
ptr_off = AlignPow2(ptr_off, ptr_ctx->ptr_align);
encoding = EH_PtrEnc_Ptr;
}
// decode pointer value
U64 decode_size = 0;
U64 raw_ptr_size = 0;
U64 raw_ptr = 0;
switch (encoding & EH_PtrEnc_TypeMask) {
default: { InvalidPath; } break;
case EH_PtrEnc_Ptr : { raw_ptr_size = 8; } goto ufixed;
case EH_PtrEnc_UData2: { raw_ptr_size = 2; } goto ufixed;
case EH_PtrEnc_UData4: { raw_ptr_size = 4; } goto ufixed;
case EH_PtrEnc_UData8: { raw_ptr_size = 8; } goto ufixed;
ufixed: {
decode_size += str8_deserial_read(frame_base, ptr_off, &raw_ptr, raw_ptr_size, raw_ptr_size);
} break;
// TODO: Signed is actually just a flag that indicates this int is negavite.
// There shouldn't be a read for Signed.
// For instance, (EH_PtrEnc_UData2 | EH_PtrEnc_Signed) == EH_PtrEnc_SData etc.
case EH_PtrEnc_Signed: { raw_ptr_size = 8; } goto sfixed;
case EH_PtrEnc_SData2: { raw_ptr_size = 2; } goto sfixed;
case EH_PtrEnc_SData4: { raw_ptr_size = 4; } goto sfixed;
case EH_PtrEnc_SData8: { raw_ptr_size = 8; } goto sfixed;
sfixed: {
decode_size += str8_deserial_read(frame_base, ptr_off, &raw_ptr, raw_ptr_size, raw_ptr_size);
raw_ptr = extend_sign64(raw_ptr, raw_ptr_size);
} break;
case EH_PtrEnc_ULEB128: { decode_size += str8_deserial_read_uleb128(frame_base, ptr_off, &raw_ptr); } break;
case EH_PtrEnc_SLEB128: { decode_size += str8_deserial_read_sleb128(frame_base, ptr_off, (S64*)&raw_ptr); } break;
}
// apply relative bases
if (decode_size > 0) {
U64 ptr = raw_ptr;
switch (encoding & EH_PtrEnc_ModifierMask) {
case 0: break;
case EH_PtrEnc_PcRel: { ptr = pc + raw_ptr; } break;
case EH_PtrEnc_TextRel: { ptr = ptr_ctx->text_vaddr + raw_ptr; } break;
case EH_PtrEnc_DataRel: { ptr = ptr_ctx->data_vaddr + raw_ptr; } break;
case EH_PtrEnc_FuncRel: {
Assert(!"TODO: need a sample to verify implementation");
ptr = ptr_ctx->func_vaddr + raw_ptr;
} break;
default: { InvalidPath; } break;
}
if (ptr_out) {
*ptr_out = ptr;
}
}
return decode_size;
}
internal EH_FrameHdr
eh_parse_frame_hdr(String8 data, U64 address_size, EH_PtrCtx *ptr_ctx)
{
EH_FrameHdr header = {0};
U64 cursor = 0;
U64 version_size = str8_deserial_read_struct(data, cursor, &header.version);
if (version_size == 0) { goto exit; }
cursor += version_size;
if (header.version == 1) {
U64 eh_frame_ptr_enc_size = str8_deserial_read_struct(data, cursor, &header.eh_frame_ptr_enc);
if (eh_frame_ptr_enc_size == 0) { goto exit; }
cursor += eh_frame_ptr_enc_size;
U64 fde_count_enc_size = str8_deserial_read_struct(data, cursor, &header.fde_count_enc);
if (fde_count_enc_size == 0) { goto exit; }
cursor += fde_count_enc_size;
U64 table_enc_size = str8_deserial_read_struct(data, cursor, &header.table_enc);
if (table_enc_size == 0) { goto exit; }
cursor += table_enc_size;
cursor += eh_parse_ptr(data, cursor, cursor, ptr_ctx, header.eh_frame_ptr_enc, &header.eh_frame_ptr);
cursor += eh_parse_ptr(data, cursor, cursor, ptr_ctx, header.fde_count_enc, &header.fde_count);
switch (header.table_enc & EH_PtrEnc_TypeMask) {
case EH_PtrEnc_Ptr: { header.field_byte_size = address_size; } break;
case EH_PtrEnc_ULEB128: { InvalidPath; } break; // TODO: when loading module convert these to UData8
case EH_PtrEnc_UData2: { header.field_byte_size = 2; } break;
case EH_PtrEnc_UData4: { header.field_byte_size = 4; } break;
case EH_PtrEnc_UData8: { header.field_byte_size = 8; } break;
case EH_PtrEnc_Signed: { header.field_byte_size = address_size; } break;
case EH_PtrEnc_SLEB128: { InvalidPath; } break; // TODO: when loading module convert these to UData8
case EH_PtrEnc_SData2: { header.field_byte_size = 2; } break;
case EH_PtrEnc_SData4: { header.field_byte_size = 4; } break;
case EH_PtrEnc_SData8: { header.field_byte_size = 8; } break;
default: { InvalidPath; } break;
}
header.entry_byte_size = header.field_byte_size * 2;
header.table = str8_skip(data, cursor);
AssertAlways(header.table.size == header.entry_byte_size * header.fde_count);
} else {
Assert(0 && "unknown version");
}
exit:;
return header;
}
internal U64
eh_parse_aug_data(String8 aug_string, String8 aug_data, U64 pc, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out)
{
// 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
U64 cursor = 0;
U64 aug_data_size = 0;
EH_AugFlags aug_flags = 0;
EH_PtrEnc lsda_encoding = EH_PtrEnc_Omit;
EH_PtrEnc addr_encoding = EH_PtrEnc_UData8;
EH_PtrEnc handler_encoding = EH_PtrEnc_Omit;
U64 handler_ip = 0;
if (str8_match(str8_prefix(aug_string, 1), str8_lit("z"), 0)) {
cursor = str8_deserial_read_uleb128(aug_data, cursor, &aug_data_size);
for (U8 *ptr = aug_string.str+1; ptr < (aug_string.str+aug_string.size); ptr += 1) {
switch (*ptr) {
case 'L': {
cursor += str8_deserial_read_struct(aug_data, cursor, &lsda_encoding);
aug_flags |= EH_AugFlag_HasLSDA;
} break;
case 'P': {
cursor += str8_deserial_read_struct(aug_data, cursor, &handler_encoding);
cursor += eh_parse_ptr(aug_data, cursor, pc + cursor, ptr_ctx, handler_encoding, &handler_ip);
aug_flags |= EH_AugFlag_HasHandler;
} break;
case 'R': {
cursor += str8_deserial_read_struct(aug_data, cursor, &addr_encoding);
aug_flags |= EH_AugFlag_HasAddrEnc;
} break;
case 'S': {
aug_flags |= EH_AugFlag_SignalFrame;
} break;
default: { Assert(!"failed to parse augmentation string"); goto exit; } break;
}
}
}
if (aug_out) {
aug_out->handler_ip = handler_ip;
aug_out->handler_encoding = handler_encoding;
aug_out->lsda_encoding = handler_encoding;
aug_out->addr_encoding = addr_encoding;
aug_out->flags = aug_flags;
aug_out->size = aug_data_size;
}
exit:;
U64 parse_size = cursor;
return parse_size;
}
internal U64
eh_parse_descriptor_entry_header(String8 data, U64 off, DW_DescriptorEntry *desc_out)
{
U32 first_four_bytes = 0;
str8_deserial_read_struct(data, off, &first_four_bytes);
DW_Format format = first_four_bytes == max_U32 ? DW_Format_64Bit : DW_Format_32Bit;
U64 length = 0;
U64 length_size = str8_deserial_read_dwarf_packed_size(data, off, &length);
if (length_size == 0) { goto exit; }
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, length_size, format, &id);
if (id_size == 0) { goto exit; }
desc_out->format = format;
desc_out->type = (id == 0) ? DW_DescriptorEntryType_CIE : DW_DescriptorEntryType_FDE;
desc_out->entry_range = entry_range;
desc_out->cie_pointer = id;
desc_out->cie_pointer_off = off + length_size;
exit:;
return length + length_size;
}
internal B32
eh_parse_cie(String8 data, DW_Format format, Arch arch, U64 pc, EH_PtrCtx *ptr_ctx, DW_CIE *cie_out)
{
B32 is_parsed = 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);
if (cie_id_size == 0) { goto exit; }
cursor += cie_id_size;
U8 version = 0;
U64 version_size = str8_deserial_read_struct(data, cursor, &version);
if (version_size == 0) { goto exit; }
cursor += version_size;
String8 aug_string = {0};
String8 aug_data = {0};
EH_Augmentation aug = {0};
U64 code_align_factor = 0;
S64 data_align_factor = 0;
U64 ret_addr_reg = max_U64;
if (version == 1) {
U64 aug_string_size = str8_deserial_read_cstr(data, cursor, &aug_string);
if (aug_string_size == 0) { goto exit; }
cursor += aug_string_size;
U64 eh_data = 0;
if (str8_match(aug_string, str8_lit("eh"), 0)) {
U64 arch_byte_size = byte_size_from_arch(arch);
cursor += str8_deserial_read(data, cursor, &eh_data, arch_byte_size, arch_byte_size);
}
U64 code_align_factor_size = str8_deserial_read_uleb128(data, cursor, &code_align_factor);
if (code_align_factor_size == 0) { goto exit; }
cursor += code_align_factor_size;
U64 data_align_factor_size = str8_deserial_read_sleb128(data, cursor, &data_align_factor);
if (data_align_factor_size == 0) { goto exit; }
cursor += data_align_factor_size;
ret_addr_reg = 0;
U64 ret_addr_reg_size = str8_deserial_read(data, cursor, &ret_addr_reg, sizeof(U8), sizeof(U8));
if (ret_addr_reg_size == 0) { goto exit; }
cursor += ret_addr_reg_size;
U64 aug_data_size = eh_parse_aug_data(aug_string, str8_skip(data, cursor), pc + cursor, ptr_ctx, &aug);
aug_data = str8_substr(data, r1u64(cursor, cursor + aug.size));
cursor += aug_data_size;
} else {
Assert(0 && "unexpected version");
}
cie_out->insts = str8_skip(data, cursor);
cie_out->aug_string = aug_string;
cie_out->aug_data = aug_data;
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 = byte_size_from_arch(arch);
cie_out->segment_selector_size = 0;
cie_out->ext[EH_CIE_Ext_LSDAEnc] = EH_PtrEnc_Omit;
cie_out->ext[EH_CIE_Ext_AddrEnc] = EH_PtrEnc_Omit;
cie_out->ext[EH_CIE_Ext_HandlerEnc] = EH_PtrEnc_Omit;
if (aug.flags & EH_AugFlag_HasLSDA) {
cie_out->ext[EH_CIE_Ext_LSDAEnc] = aug.lsda_encoding;
}
if (aug.flags & EH_AugFlag_HasAddrEnc) {
cie_out->ext[EH_CIE_Ext_AddrEnc] = aug.addr_encoding;
}
if (aug.flags & EH_AugFlag_HasHandler) {
cie_out->ext[EH_CIE_Ext_HandlerEnc] = aug.handler_encoding;
cie_out->ext[EH_CIE_Ext_HandlerIp ] = aug.handler_ip;
}
is_parsed = 1;
exit:;
return is_parsed;
}
internal B32
eh_parse_fde(String8 data, DW_Format format, U64 pc, DW_CIE *cie, EH_PtrCtx *ptr_ctx, DW_FDE *fde_out)
{
B32 is_parsed = 0;
U64 cursor = format == DW_Format_32Bit ? 8 : 20;
U64 pc_begin = 0;
U64 pc_delta = 0;
EH_PtrEnc addr_enc = cie->ext[EH_CIE_Ext_AddrEnc];
if (addr_enc != EH_PtrEnc_Omit) {
U64 pc_begin_size = eh_parse_ptr(data, cursor, pc + cursor, ptr_ctx, addr_enc, &pc_begin);
if (pc_begin_size == 0) { goto exit; }
cursor += pc_begin_size;
U64 pc_delta_size = eh_parse_ptr(data, cursor, pc + cursor, ptr_ctx, addr_enc & EH_PtrEnc_TypeMask, &pc_delta);
if (pc_delta_size == 0) { goto exit; }
cursor += pc_delta_size;
}
if (cursor + cie->aug_data.size > data.size) { goto exit; }
cursor += cie->aug_data.size;
fde_out->format = format;
fde_out->pc_range = rng_1u64(pc_begin, pc_begin + pc_delta);
fde_out->insts = str8_skip(data, cursor);
is_parsed = 1;
exit:;
return is_parsed;
}
internal U64
eh_find_nearest_fde(EH_FrameHdr header, EH_PtrCtx *ptr_ctx, U64 pc)
{
U64 fde_addr = max_U64;
U64 fde_idx = max_U64;
if (header.version == 1) {
if (header.fde_count > 0) {
U64 first = 0;
U64 first_size = eh_parse_ptr(header.table, 0, ptr_ctx->pc_vaddr, ptr_ctx, header.table_enc, &first);
AssertAlways(first_size);
if (first == pc) {
fde_idx = 0;
goto exit;
}
if (first > pc) {
goto exit;
}
U64 last_off = header.table.size - header.entry_byte_size;
U64 last = 0;
U64 last_size = eh_parse_ptr(header.table, last_off, ptr_ctx->pc_vaddr + last_off, ptr_ctx, header.table_enc, &last);
AssertAlways(last_size);
if (last <= pc) {
fde_idx = header.fde_count - 1;
goto exit;
}
U64 l = 0;
U64 r = header.fde_count - 1;
while (l <= r) {
U64 m = l + (r - l) / 2;
U64 m_pc_off = m * header.entry_byte_size;
U64 m_pc = 0;
U64 m_pc_size = eh_parse_ptr(header.table, m_pc_off, ptr_ctx->pc_vaddr + m_pc_off, ptr_ctx, header.table_enc, &m_pc);
Assert(m_pc_size);
if (m_pc > pc) {
r = m - 1;
} else if (m_pc < pc) {
l = m + 1;
} else {
fde_idx = m;
goto exit;
}
}
fde_idx = l > 0 ? l-1 : 0;
}
}
exit:;
if (fde_idx < header.fde_count) {
U64 fde_addr_off = (fde_idx * header.entry_byte_size) + header.field_byte_size;
U64 fde_addr_size = eh_parse_ptr(header.table, fde_addr_off, ptr_ctx->pc_vaddr + fde_addr_off, ptr_ctx, header.table_enc, &fde_addr);
Assert(fde_addr_size);
}
return fde_addr;
}
internal int
eh_frame_hdr_entry_sort(void *raw_a, void *raw_b)
{
return ((EH_FrameHdrEntry *)raw_a)->addr < ((EH_FrameHdrEntry *)raw_b)->addr;
}
internal String8
eh_frame_hdr_from_call_frame_info(Arena *arena, U64 fde_count, U64 *fde_offsets, DW_FDE *fde)
{
Temp scratch = scratch_begin(&arena, 1);
// make .eh_frame_hdr
String8List srl = {0};
str8_serial_begin(scratch.arena, &srl);
str8_serial_push_u8(scratch.arena, &srl, 1); // version
str8_serial_push_u8(scratch.arena, &srl, EH_PtrEnc_Omit); // omit eh_frame_ptr field
str8_serial_push_u8(scratch.arena, &srl, EH_PtrEnc_UData8); // fde_count encoding
str8_serial_push_u8(scratch.arena, &srl, EH_PtrEnc_UData8); // table encoding
str8_serial_push_u64(scratch.arena, &srl, fde_count); // fde_count
String8 header = str8_serial_end(scratch.arena, &srl);
// alloc buffer for output
U64 buffer_size = header.size + sizeof(EH_FrameHdrEntry) * fde_count;
U8 *buffer = push_array(arena, U8, buffer_size);
// copy header
MemoryCopyStr8(buffer, header);
// write the table
EH_FrameHdrEntry *table = (EH_FrameHdrEntry *)(buffer + header.size);
for EachIndex(fde_idx, fde_count) {
table[fde_idx].addr = fde[fde_idx].pc_range.min;
table[fde_idx].fde_offset = fde_offsets[fde_idx];
}
radsort(table, fde_count, eh_frame_hdr_entry_sort);
String8 eh_frame_hdr = str8(buffer, buffer_size);
scratch_end(scratch);
return eh_frame_hdr;
}
internal
DW_DECODE_PTR(eh_decode_ptr)
{
EH_DecodePtrCtx *ctx = ud;
return eh_parse_ptr(data, 0, ctx->ptr_ctx->pc_vaddr, ctx->ptr_ctx, ctx->addr_enc, ptr_out);
}
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;
}
-126
View File
@@ -1,126 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef EH_FRAME_H
#define EH_FRAME_H
////////////////////////////////
//~ Format
typedef U8 EH_PtrEnc;
enum
{
EH_PtrEnc_Ptr = 0x00, // Pointer sized unsigned value
EH_PtrEnc_ULEB128 = 0x01, // Unsigned LE base-128 value
EH_PtrEnc_UData2 = 0x02, // Unsigned 16-bit value
EH_PtrEnc_UData4 = 0x03, // Unsigned 32-bit value
EH_PtrEnc_UData8 = 0x04, // Unsigned 64-bit value
EH_PtrEnc_Signed = 0x08, // Signed pointer
EH_PtrEnc_SLEB128 = 0x09, // Signed LE base-128 value
EH_PtrEnc_SData2 = 0x0A, // Signed 16-bit value
EH_PtrEnc_SData4 = 0x0B, // Signed 32-bit value
EH_PtrEnc_SData8 = 0x0C, // Signed 64-bit value
EH_PtrEnc_TypeMask = 0x0F,
};
enum
{
EH_PtrEnc_PcRel = 0x10, // Value is relative to the current program counter.
EH_PtrEnc_TextRel = 0x20, // Value is relative to the .text section.
EH_PtrEnc_DataRel = 0x30, // Value is relative to the .got or .eh_frame_hdr section.
EH_PtrEnc_FuncRel = 0x40, // Value is relative to the function.
EH_PtrEnc_Aligned = 0x50, // Value is aligned to an address unit sized boundary.
EH_PtrEnc_ModifierMask = 0x70,
};
enum
{
EH_PtrEnc_Indirect = 0x80, // Value is stored in virtual memory.
EH_PtrEnc_Omit = 0xFF,
};
typedef struct EH_PtrCtx
{
U64 pc_vaddr; // address where pointer is being read
U64 text_vaddr; // base address of section with instructions (used for encoding pointer on SH and IA64)
U64 data_vaddr; // base address of data section (used for encoding pointer on x86-64)
U64 func_vaddr; // base address of function where IP is located
U64 ptr_align;
} EH_PtrCtx;
typedef U8 EH_AugFlags;
enum
{
EH_AugFlag_HasLSDA = (1 << 0),
EH_AugFlag_HasHandler = (1 << 1),
EH_AugFlag_HasAddrEnc = (1 << 2),
EH_AugFlag_SignalFrame = (1 << 3),
};
typedef struct EH_Augmentation
{
EH_AugFlags flags;
U64 handler_ip;
EH_PtrEnc handler_encoding;
EH_PtrEnc lsda_encoding;
EH_PtrEnc addr_encoding;
U64 size;
} EH_Augmentation;
////////////////////////////////
//~ Parser
enum
{
EH_CIE_Ext_AddrEnc,
EH_CIE_Ext_LSDAEnc,
EH_CIE_Ext_HandlerEnc,
EH_CIE_Ext_HandlerIp,
} EH_CIE_Ext;
typedef struct EH_FrameHdrEntry
{
U64 addr;
U64 fde_offset;
} EH_FrameHdrEntry;
typedef struct EH_FrameHdr
{
U8 version;
EH_PtrEnc eh_frame_ptr_enc;
EH_PtrEnc fde_count_enc;
EH_PtrEnc table_enc;
U64 field_byte_size;
U64 entry_byte_size;
U64 fde_count;
U64 eh_frame_ptr;
String8 table;
} EH_FrameHdr;
typedef struct EH_DecodePtrCtx
{
EH_PtrEnc addr_enc;
EH_PtrCtx *ptr_ctx;
} EH_DecodePtrCtx;
////////////////////////////////
internal U64 eh_parse_ptr(String8 frame_base, U64 off, U64 pc, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_out);
internal EH_FrameHdr eh_parse_frame_hdr(String8 data, U64 address_size, EH_PtrCtx *ptr_ctx);
internal U64 eh_parse_aug_data(String8 aug_string, String8 aug_data, U64 pc, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out);
internal B32 eh_parse_cie(String8 data, DW_Format format, Arch arch, U64 pc, EH_PtrCtx *ptr_ctx, DW_CIE *cie_out);
internal B32 eh_parse_fde(String8 data, DW_Format format, U64 pc, DW_CIE *cie, EH_PtrCtx *ptr_ctx, DW_FDE *fde_out);
internal U64 eh_find_nearest_fde(EH_FrameHdr header, EH_PtrCtx *ptr_ctx, U64 pc);
internal String8 eh_frame_hdr_from_call_frame_info(Arena *arena, U64 fde_count, U64 *fde_offsets, struct DW_FDE *fde);
////////////////////////////////
//~ 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
+69
View File
@@ -3,3 +3,72 @@
//- GENERATED CODE
C_LINKAGE_BEGIN
String8 dw_regular_name_from_section_kind_table[18] =
{
str8_lit_comp(""),
str8_lit_comp(".debug_abbrev"),
str8_lit_comp(".debug_aranges"),
str8_lit_comp(".debug_frame"),
str8_lit_comp(".debug_info"),
str8_lit_comp(".debug_line"),
str8_lit_comp(".debug_loc"),
str8_lit_comp(".debug_macinfo"),
str8_lit_comp(".debug_pubnames"),
str8_lit_comp(".debug_pubtypes"),
str8_lit_comp(".debug_ranges"),
str8_lit_comp(".debug_str"),
str8_lit_comp(".debug_addr"),
str8_lit_comp(".debug_loclists"),
str8_lit_comp(".debug_rnglists"),
str8_lit_comp(".debug_str_offsets"),
str8_lit_comp(".debug_line_str"),
str8_lit_comp(".debug_names"),
};
String8 dw_macho_name_from_section_kind_table[18] =
{
str8_lit_comp(""),
str8_lit_comp("__debug_abbrev"),
str8_lit_comp("__debug_aranges"),
str8_lit_comp("__debug_frame"),
str8_lit_comp("__debug_info"),
str8_lit_comp("__debug_line"),
str8_lit_comp("__debug_loc"),
str8_lit_comp("__debug_macinfo"),
str8_lit_comp("__debug_pubnames"),
str8_lit_comp("__debug_pubtypes"),
str8_lit_comp("__debug_ranges"),
str8_lit_comp("__debug_str"),
str8_lit_comp("__debug_addr"),
str8_lit_comp("__debug_loclists"),
str8_lit_comp("__debug_rnglists"),
str8_lit_comp("__debug_str_offsets"),
str8_lit_comp("__debug_line_str"),
str8_lit_comp("__debug_names"),
};
String8 dw_dwo_name_from_section_kind_table[18] =
{
str8_lit_comp(""),
str8_lit_comp(".debug_abbrev.dwo"),
str8_lit_comp(".debug_aranges.dwo"),
str8_lit_comp(".debug_frame.dwo"),
str8_lit_comp(".debug_info.dwo"),
str8_lit_comp(".debug_line.dwo"),
str8_lit_comp(".debug_loc.dwo"),
str8_lit_comp(".debug_macinfo.dwo"),
str8_lit_comp(".debug_pubnames.dwo"),
str8_lit_comp(".debug_pubtypes.dwo"),
str8_lit_comp(".debug_ranges.dwo"),
str8_lit_comp(".debug_str.dwo"),
str8_lit_comp(".debug_addr.dwo"),
str8_lit_comp(".debug_loclists.dwo"),
str8_lit_comp(".debug_rnglists.dwo"),
str8_lit_comp(".debug_str_offsets.dwo"),
str8_lit_comp(".debug_line_str.dwo"),
str8_lit_comp(".debug_names.dwo"),
};
C_LINKAGE_END
File diff suppressed because it is too large. Load diff
+82 -2
View File
@@ -1,6 +1,8 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#if 0
internal U64
t_dw_test_uleb128(U64 v, U64 expected_length)
{
@@ -63,6 +65,85 @@ Test(test_leb128)
}
}
internal B32
dw_test__form_match(DW_Form a, DW_Form b)
{
B32 is_match = 0;
if (a.kind == b.kind) {
switch (a.kind) {
case DW_Form_Null: {} break;
case DW_Form_Addr: { is_match = str8_match(a.addr, b.addr, 0); } break;
case DW_Form_String: { is_match = str8_match(a.string, b.string, 0); } break;
case DW_Form_ExprLoc: { is_match = str8_match(a.exprloc, b.exprloc, 0); } break;
case DW_Form_Block:
case DW_Form_Block1:
case DW_Form_Block2:
case DW_Form_Block4: {
is_match = str8_match(a.block, b.block, 0);
} break;
case DW_Form_Data1:
case DW_Form_Data2:
case DW_Form_Data4:
case DW_Form_Data8:
case DW_Form_Data16: {
is_match = str8_match(a.data, b.data, 0);
} break;
case DW_Form_Flag: { is_match = a.flag == b.flag; } break;
case DW_Form_SData: { is_match = a.sdata == b.sdata; } break;
case DW_Form_UData: { is_match = a.udata == b.udata; } break;
case DW_Form_RefAddr:
case DW_Form_Ref1:
case DW_Form_Ref2:
case DW_Form_Ref4:
case DW_Form_Ref8:
case DW_Form_RefUData:
case DW_Form_GNU_RefAlt: {
is_match = a.ref == b.ref;
} break;
case DW_Form_Indirect: { NotImplemented; } break;
case DW_Form_SecOffset:
case DW_Form_LineStrp:
case DW_Form_GNU_StrpAlt: {
is_match = a.sec_offset == b.sec_offset;
} break;
case DW_Form_ImplicitConst: { is_match = a.implicit_const, b.implicit_const; } break;
case DW_Form_Strx:
case DW_Form_Strx1:
case DW_Form_Strx2:
case DW_Form_Strx3:
case DW_Form_Strx4:
case DW_Form_Addrx:
case DW_Form_Addrx1:
case DW_Form_Addrx2:
case DW_Form_Addrx3:
case DW_Form_Addrx4:
case DW_Form_RngListx:
case DW_Form_LocListx: {
is_match = a.xval == b.xval;
} break;
case DW_Form_StrpSup: { is_match = a.strp_sup == b.strp_sup; } break;
case DW_Form_RefSup4: { NotImplemented; } break;
case DW_Form_RefSup8: { NotImplemented; } break;
default: { InvalidPath; } break;
}
}
return is_match;
}
internal B32
dwt_tags_must_match(DW_WriterTag *writer_tag, DW_TagNode *reader_tag)
{
@@ -82,7 +163,7 @@ dwt_tags_must_match(DW_WriterTag *writer_tag, DW_TagNode *reader_tag)
while (writer_attrib) {
if (writer_attrib->kind != reader_attrib->v.attrib_kind) { goto exit; }
if (writer_attrib->form.reader.kind != reader_attrib->v.form.kind) { goto exit; }
if ( ! dw_form_match(writer_attrib->form.reader, reader_attrib->v.form)) { goto exit; }
if ( ! dw_test__form_match(writer_attrib->form.reader, reader_attrib->v.form)) { goto exit; }
writer_attrib = writer_attrib->next;
reader_attrib = reader_attrib->next;
}
@@ -1011,7 +1092,6 @@ Test(plus_uconst)
TestCheck(expr_value.addr == 0x123 + 4);
}
#if 0
Test(reg_split_spill)
{
// setup register context