dumper for .eh_frame_hdr and .eh_frame

This commit is contained in:
Nikita Smith
2025-11-07 14:39:45 -08:00
committed by Ryan Fleury
parent 327645436a
commit dad92610de
13 changed files with 161 additions and 731 deletions
+81 -73
View File
@@ -343,7 +343,7 @@ dw_string_list_from_cfi_program(Arena *arena,
DW_Ext ext,
DW_Format format,
U64 pc_begin,
DW_CIE *cie,
DW_CIE *cie,
DW_DecodePtr *decode_ptr_func,
void *deocde_ptr_ud,
String8 program)
@@ -494,6 +494,64 @@ exit:;
return list;
}
internal String8
dw_string_from_cfa(Arena *arena, Arch arch, U64 address_size, DW_Version version, DW_Ext ext, DW_Format format, DW_CFA cfa)
{
String8 cfa_str = str8_lit("???");
switch (cfa.rule) {
case DW_CFA_Rule_Null: {} break;
case DW_CFA_Rule_RegOff: { cfa_str = dw_string_from_reg_off(arena, arch, cfa.reg, cfa.off); } break;
case DW_CFA_Rule_Expression: { cfa_str = dw_string_from_expression(arena, cfa.expr, max_U64, address_size, arch, version, ext, format); } break;
default: { InvalidPath; } break;
}
return cfa_str;
}
internal String8
dw_string_from_cfi_row(Arena *arena, Arch arch, U64 address_size, DW_Version version, DW_Ext ext, DW_Format format, DW_CFI_Row *row)
{
Temp scratch = scratch_begin(&arena, 1);
String8List cfi_regs_list = {0};
U64 reg_count = dw_reg_count_from_arch(arch);
for EachIndex(reg_idx, reg_count) {
DW_CFI_Register *cfi_reg = &row->regs[reg_idx];
String8 rule_str = str8_lit("???");
switch (cfi_reg->rule) {
case DW_CFI_RegisterRule_Undefined: {
rule_str = str8f(scratch.arena, "Undefined(%S)", dw_string_from_reg(scratch.arena, arch, cfi_reg->n));
} break;
case DW_CFI_RegisterRule_SameValue: {
rule_str = str8_zero();
} break;
case DW_CFI_RegisterRule_Offset: {
rule_str = str8f(scratch.arena, "[CFA%+I64d]", cfi_reg->n);
} break;
case DW_CFI_RegisterRule_ValOffset: {
rule_str = str8f(scratch.arena, "Val(CFA%+I64d)", cfi_reg->n);
} break;
case DW_CFI_RegisterRule_Expression: {
rule_str = str8f(scratch.arena, "Expression(%S)", dw_string_from_expression(scratch.arena, cfi_reg->expr, max_U64, address_size, arch, version, ext, format));
} break;
case DW_CFI_RegisterRule_ValExpression: {
rule_str = str8f(scratch.arena, "ValExpression(%S)", dw_string_from_expression(scratch.arena, cfi_reg->expr, max_U64, address_size, arch, version, ext, format));
} break;
case DW_CFI_RegisterRule_Architectural: {
rule_str = str8_lit("???");
} break;
default: { InvalidPath; } break;
}
if (rule_str.size) {
str8_list_pushf(scratch.arena, &cfi_regs_list, "%S: %S", dw_string_from_reg(scratch.arena, arch, reg_idx), rule_str);
}
}
String8 string = str8_list_join(arena, &cfi_regs_list, &(StringJoin){.sep=str8_lit(", ")});
scratch_end(scratch);
return string;
}
internal String8
dw_single_line_string_from_expression(Arena *arena, String8 raw_data, U64 cu_base, U64 address_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format)
{
@@ -1674,12 +1732,6 @@ dw_string_from_attrib_value(Arena *arena, DW_Input *input, Arch arch, DW_CompUni
return result;
}
internal
DW_CIE_FROM_OFFSET_FUNC(dwarf_dump_cie_from_offset)
{
return hash_table_search_u64_raw(ud, offset);
}
internal String8List
dw_dump_list_from_sections(Arena *arena,
DW_Input *input,
@@ -1712,7 +1764,8 @@ dw_dump_list_from_sections(Arena *arena,
//- rjf: dump .debug_info
//
DumpSubset(DebugInfo)
{ Rng1U64List unit_ranges_list = dw_unit_ranges_from_data(scratch.arena, input->sec[DW_Section_Info].data);
{
Rng1U64List unit_ranges_list = dw_unit_ranges_from_data(scratch.arena, input->sec[DW_Section_Info].data);
Rng1U64Array unit_ranges = rng1u64_array_from_list(scratch.arena, &unit_ranges_list);
for EachIndex(unit_idx, unit_ranges.count)
{
@@ -2181,12 +2234,12 @@ dw_dump_list_from_sections(Arena *arena,
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.size ? cie.aug_string : str8_lit("None"));
dumpf(" Code align: %I64u\n", cie.code_align_factor);
dumpf(" Data align: %I64d\n", cie.data_align_factor);
dumpf(" Return addr reg: %u\n", cie.ret_addr_reg);
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);
@@ -2201,15 +2254,18 @@ dw_dump_list_from_sections(Arena *arena,
}
} break;
case DW_DescriptorEntryType_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);
DW_CIE cie = {0};
dw_parse_cie(cie_data, cie_desc.format, arch, &cie);
DW_FDE fde = {0};
if (dw_parse_fde(raw_desc, desc.format, dwarf_dump_cie_from_offset, cie_ht, &fde)) {
DW_Version version = DW_Version_5;
DW_Ext ext = DW_Ext_All;
DW_CIE *cie = hash_table_search_u64_raw(cie_ht, fde.cie_pointer);
String8List insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, version, ext, fde.format, fde.pc_range.min, cie, dw_decode_ptr_debug_frame, cie, fde.insts);
dumpf("FDE: // entry range: %r\n", desc.entry_range, dw_string_from_format(fde.format), fde.cie_pointer, fde.pc_range);
if (dw_parse_fde(raw_desc, desc.format, &cie, &fde)) {
DW_Version version = DW_Version_5;
DW_Ext ext = DW_Ext_All;
String8List insts_str_list = dw_string_list_from_cfi_program(scratch.arena, 0, arch, version, ext, fde.format, fde.pc_range.min, &cie, dw_decode_ptr_debug_frame, &cie, fde.insts);
dumpf("FDE: // entry range: %r\n", desc.entry_range);
dumpf("{\n");
{
dumpf(" Format: %S\n", dw_string_from_format(fde.format));
@@ -2220,60 +2276,12 @@ dw_dump_list_from_sections(Arena *arena,
for EachNode(n, String8Node, insts_str_list.first) { dumpf(" %S\n", n->string); }
dumpf(" }\n");
}
dumpf(" Unwind:\n");
dumpf(" {\n");
DW_CFI_Unwind *cfi_unwind = dw_cfi_unwind_init(scratch.arena, arch, cie, &fde, dw_decode_ptr_debug_frame, cie);
DW_CFI_Unwind *cfi_unwind = dw_cfi_unwind_init(scratch.arena, arch, &cie, &fde, dw_decode_ptr_debug_frame, &cie);
do {
String8 cfa_str = str8_lit("???");
DW_CFA cfa = cfi_unwind->row->cfa;
switch (cfa.rule) {
case DW_CFA_Rule_Null: {} break;
case DW_CFA_Rule_RegOff: { cfa_str = dw_string_from_reg_off(scratch.arena, arch, cfa.reg, cfa.off); } break;
case DW_CFA_Rule_Expression: { cfa_str = dw_string_from_expression(scratch.arena, cfa.expr, max_U64, cie->address_size, arch, version, ext, fde.format); } break;
default: { InvalidPath; } break;
}
String8 cfi_regs_str = {0};
{
String8List cfi_regs_list = {0};
U64 reg_count = dw_reg_count_from_arch(arch);
for EachIndex(reg_idx, reg_count) {
DW_CFI_Register *cfi_reg = &cfi_unwind->row->regs[reg_idx];
String8 rule_str = str8_lit("???");
switch (cfi_reg->rule) {
case DW_CFI_RegisterRule_Undefined: {
rule_str = str8f(scratch.arena, "Undefined(%S)", dw_string_from_reg(scratch.arena, arch, cfi_reg->n));
} break;
case DW_CFI_RegisterRule_SameValue: {
rule_str = str8_zero();
} break;
case DW_CFI_RegisterRule_Offset: {
rule_str = str8f(scratch.arena, "[CFA%+I64d]", cfi_reg->n);
} break;
case DW_CFI_RegisterRule_ValOffset: {
rule_str = str8f(scratch.arena, "Val(CFA%+I64d)", cfi_reg->n);
} break;
case DW_CFI_RegisterRule_Expression: {
rule_str = str8f(scratch.arena, "Expression(%S)", dw_string_from_expression(scratch.arena, cfi_reg->expr, max_U64, cie->address_size, arch, version, ext, fde.format));
} break;
case DW_CFI_RegisterRule_ValExpression: {
rule_str = str8f(scratch.arena, "ValExpression(%S)", dw_string_from_expression(scratch.arena, cfi_reg->expr, max_U64, cie->address_size, arch, version, ext, fde.format));
} break;
case DW_CFI_RegisterRule_Architectural: {
rule_str = str8_lit("???");
} break;
default: { InvalidPath; } break;
}
if (rule_str.size) {
str8_list_pushf(scratch.arena, &cfi_regs_list, "%S: %S", dw_string_from_reg(scratch.arena, arch, reg_idx), rule_str);
}
}
cfi_regs_str = str8_list_join(scratch.arena, &cfi_regs_list, &(StringJoin){.sep=str8_lit(", ")});
}
String8 cfa_str = dw_string_from_cfa(scratch.arena, arch, cie.address_size, version, ext, fde.format, cfi_unwind->row->cfa);
String8 cfi_regs_str = dw_string_from_cfi_row(scratch.arena, arch, cie.address_size, version, ext, fde.format, cfi_unwind->row);
dumpf(" { PC: 0x%I64x, CFA: %-7S, Rules: { %S }\n", cfi_unwind->pc, cfa_str, cfi_regs_str);
} while (dw_cfi_next_row(scratch.arena, cfi_unwind));
dumpf(" }\n");
+8 -4
View File
@@ -59,12 +59,16 @@ read_only global String8 dw_name_title_from_dump_subset_table[] =
//~ rjf: Stringification Helpers
internal String8 dw_string_from_reg_off(Arena *arena, Arch arch, DW_Reg reg_idx, S64 reg_off);
internal String8List dw_string_list_from_expression (Arena *arena, String8 raw_data, U64 cu_base, U64 address_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format);
internal String8 dw_single_line_string_from_expression(Arena *arena, String8 raw_data, U64 cu_base, U64 address_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format);
internal String8 dw_string_from_reg(Arena *arena, Arch arch, DW_Reg reg_idx);
internal String8List dw_string_list_from_expression(Arena *arena, String8 raw_data, U64 cu_base, U64 addr_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format);
internal String8 dw_string_from_expression(Arena *arena, String8 expr, U64 cu_base, U64 addr_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format);
internal String8List dw_string_list_from_cfi_program(Arena *arena, U64 cu_base, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format, U64 pc_begin, DW_CIE *cie, DW_DecodePtr *decode_ptr_func, void *deocde_ptr_ud, String8 program);
internal String8 dw_string_from_cfa(Arena *arena, Arch arch, U64 address_size, DW_Version version, DW_Ext ext, DW_Format format, DW_CFA cfa);
internal String8 dw_string_from_cfi_row(Arena *arena, Arch arch, U64 address_size, DW_Version version, DW_Ext ext, DW_Format format, DW_CFI_Row *row);
#if 0
internal void dw_string_from_cfi_program (Arena *arena, String8List *out, String8 indent, String8 raw_data, DW_CIE *cie, EH_PtrCtx *ptr_ctx, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format);
internal void dw_print_eh_frame (Arena *arena, String8List *out, String8 indent, String8 raw_eh_frame, Arch arch, DW_Version ver, DW_Ext ext, EH_PtrCtx *ptr_ctx);
internal void dw_print_debug_loc (Arena *arena, String8List *out, String8 indent, DW_Input *input, Arch arch, ExecutableImageKind image_type, B32 relaxed);
internal void dw_print_debug_ranges (Arena *arena, String8List *out, String8 indent, DW_Input *input, Arch arch, ExecutableImageKind image_type, B32 relaxed);
+2 -7
View File
@@ -1,12 +1,6 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal
DW_CIE_FROM_OFFSET_FUNC(dw_call_frame_info_cie_from_offset)
{
return hash_table_search_u64_raw(ud, offset);
}
internal DW_CallFrameInfo
dw_call_frame_info_from_data(Arena *arena, Arch arch, U64 rebase, String8 debug_frame)
{
@@ -49,7 +43,8 @@ dw_call_frame_info_from_data(Arena *arena, Arch arch, U64 rebase, String8 debug_
desc_size = dw_parse_descriptor_entry_header(debug_frame, cursor, &desc);
if (desc_size == 0) { break; }
if (desc.type == DW_DescriptorEntryType_FDE) {
if (dw_parse_fde(str8_skip(debug_frame, cursor), desc.format, dw_call_frame_info_cie_from_offset, cie_ht, &fde[parse_fde_count])) {
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;
+2
View File
@@ -9,3 +9,5 @@
#include "dwarf/dwarf_unwind.c"
#include "dwarf/dwarf_dump.c"
#include "dwarf/dwarf_help.c"
#include "dwarf/eh_frame.c"
#include "dwarf/eh_dump.c"
+2
View File
@@ -12,5 +12,7 @@
#include "dwarf/dwarf_unwind.h"
#include "dwarf/dwarf_dump.h"
#include "dwarf/dwarf_help.h"
#include "dwarf/eh_frame.h"
#include "dwarf/eh_dump.h"
#endif // DWARF_INC_H
+7 -20
View File
@@ -3369,9 +3369,11 @@ dw_parse_descriptor_entry_header(String8 data, U64 off, DW_DescriptorEntry *desc
if (id_size == 0) { goto exit; }
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->entry_range = entry_range;
desc_out->format = format;
desc_out->type = (id == id_type) ? DW_DescriptorEntryType_CIE : DW_DescriptorEntryType_FDE;
desc_out->entry_range = entry_range;
desc_out->cie_pointer = id;
desc_out->cie_pointer_off = length_size;
exit:;
return length + length_size;
@@ -3447,11 +3449,7 @@ exit:;
}
internal B32
dw_parse_fde(String8 data,
DW_Format format,
DW_CIEFromOffsetFunc *cie_from_offset_func,
void *cie_from_offset_ud,
DW_FDE *fde_out)
dw_parse_fde(String8 data, DW_Format format, DW_CIE *cie, DW_FDE *fde_out)
{
B32 is_parsed = 0;
U64 cursor = format == DW_Format_32Bit ? 4 : 12;
@@ -3462,10 +3460,6 @@ dw_parse_fde(String8 data,
if (cie_pointer_size == 0) { goto exit; }
cursor += cie_pointer_size;
// map offset -> CIE
DW_CIE *cie = cie_from_offset_func(cie_from_offset_ud, cie_pointer);
if (cie == 0) { goto exit; }
// extract address of first instruction
U64 pc_begin = 0;
U64 pc_begin_size = dw_read_debug_frame_ptr(str8_skip(data, cursor), cie, &pc_begin);
@@ -3485,7 +3479,6 @@ dw_parse_fde(String8 data,
// commit values to out
fde_out->format = format;
fde_out->cie_pointer = cie_pointer;
fde_out->cie = cie;
fde_out->pc_range = rng_1u64(pc_begin, pc_begin + pc_range);
fde_out->insts = str8_skip(data, cursor);
@@ -3494,12 +3487,6 @@ exit:;
return is_parsed;
}
internal
DW_CIE_FROM_OFFSET_FUNC(dw_parse_cfi_cie_from_offset)
{
return ud;
}
internal B32
dw_parse_cfi(String8 data, U64 fde_offset, Arch arch, DW_CIE *cie_out, DW_FDE *fde_out)
{
@@ -3515,7 +3502,7 @@ dw_parse_cfi(String8 data, U64 fde_offset, Arch arch, DW_CIE *cie_out, DW_FDE *f
dw_parse_descriptor_entry_header(data, cie_pointer, &cie_desc);
if (cie_desc.type == DW_DescriptorEntryType_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, dw_parse_cfi_cie_from_offset, cie_out, fde_out)) {
if (dw_parse_fde(str8_substr(data, fde_desc.entry_range), fde_desc.format, cie_out, fde_out)) {
is_parsed = 1;
}
}
+3 -5
View File
@@ -368,6 +368,8 @@ 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
@@ -389,7 +391,6 @@ typedef struct DW_FDE
{
DW_Format format;
U64 cie_pointer;
DW_CIE *cie;
Rng1U64 pc_range;
String8 insts;
} DW_FDE;
@@ -430,9 +431,6 @@ typedef struct DW_CFA_InstList
#define DW_DECODE_PTR(name) U64 name(String8 data, void *ud, U64 *ptr_out)
typedef DW_DECODE_PTR(DW_DecodePtr);
#define DW_CIE_FROM_OFFSET_FUNC(name) DW_CIE * name(void *ud, U64 offset)
typedef DW_CIE_FROM_OFFSET_FUNC(DW_CIEFromOffsetFunc);
// hasher
internal U64 dw_hash_from_string(String8 string);
@@ -559,7 +557,7 @@ internal U64 dw_read_debug_frame_ptr(String8 data, DW_CIE *cie, U64 *ptr_out);
internal U64 dw_parse_descriptor_entry_header(String8 data, U64 off, DW_DescriptorEntry *desc_out);
internal B32 dw_parse_cie(String8 data, DW_Format format, Arch arch, DW_CIE *cie_out);
internal B32 dw_parse_fde(String8 data, DW_Format format, DW_CIEFromOffsetFunc *cie_from_offset_func, void *cie_from_offset_ud, DW_FDE *fde_out);
internal B32 dw_parse_fde(String8 data, DW_Format format, DW_CIE *cie, DW_FDE *fde_out);
internal B32 dw_parse_cfi(String8 data, U64 fde_offset, Arch arch, DW_CIE *cie_out, DW_FDE *fde_out);
internal DW_CFA_ParseErrorCode dw_parse_cfa_inst(String8 data, U64 code_align_factor, S64 data_align_factor, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud, U64 *bytes_read_out, DW_CFA_Inst *inst_out);