mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-14 17:28:07 +00:00
parser for .eh_frame versions of CIE and FDE and new helper for
mapping PC -> CFI Row
This commit is contained in:
committed by
Ryan Fleury
parent
ecbce1efdf
commit
da38f266d9
@@ -2237,7 +2237,8 @@ dw_dump_list_from_sections(Arena *arena,
|
|||||||
String8 cfi_regs_str = {0};
|
String8 cfi_regs_str = {0};
|
||||||
{
|
{
|
||||||
String8List cfi_regs_list = {0};
|
String8List cfi_regs_list = {0};
|
||||||
for EachIndex(reg_idx, cfi_unwind->reg_count) {
|
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];
|
DW_CFI_Register *cfi_reg = &cfi_unwind->row->regs[reg_idx];
|
||||||
String8 rule_str = str8_lit("???");
|
String8 rule_str = str8_lit("???");
|
||||||
switch (cfi_reg->rule) {
|
switch (cfi_reg->rule) {
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ read_only global String8 dw_name_title_from_dump_subset_table[] =
|
|||||||
internal String8 dw_string_from_reg_off(Arena *arena, Arch arch, DW_Reg reg_idx, S64 reg_off);
|
internal String8 dw_string_from_reg_off(Arena *arena, Arch arch, DW_Reg reg_idx, S64 reg_off);
|
||||||
internal String8List dw_string_list_from_expression (Arena *arena, String8 raw_data, U64 cu_base, U64 address_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format);
|
internal 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_single_line_string_from_expression(Arena *arena, String8 raw_data, U64 cu_base, U64 address_size, Arch arch, DW_Version ver, DW_Ext ext, DW_Format format);
|
||||||
internal String8 dw_string_from_eh_ptr_enc (Arena *arena, EH_PtrEnc enc);
|
|
||||||
|
|
||||||
#if 0
|
#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_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);
|
||||||
|
|||||||
@@ -8,3 +8,4 @@
|
|||||||
#include "dwarf/dwarf_elf.c"
|
#include "dwarf/dwarf_elf.c"
|
||||||
#include "dwarf/dwarf_unwind.c"
|
#include "dwarf/dwarf_unwind.c"
|
||||||
#include "dwarf/dwarf_dump.c"
|
#include "dwarf/dwarf_dump.c"
|
||||||
|
#include "dwarf/dwarf_help.c"
|
||||||
|
|||||||
@@ -11,5 +11,6 @@
|
|||||||
#include "dwarf/dwarf_elf.h"
|
#include "dwarf/dwarf_elf.h"
|
||||||
#include "dwarf/dwarf_unwind.h"
|
#include "dwarf/dwarf_unwind.h"
|
||||||
#include "dwarf/dwarf_dump.h"
|
#include "dwarf/dwarf_dump.h"
|
||||||
|
#include "dwarf/dwarf_help.h"
|
||||||
|
|
||||||
#endif // DWARF_INC_H
|
#endif // DWARF_INC_H
|
||||||
|
|||||||
@@ -3485,6 +3485,7 @@ dw_parse_fde(String8 data,
|
|||||||
// commit values to out
|
// commit values to out
|
||||||
fde_out->format = format;
|
fde_out->format = format;
|
||||||
fde_out->cie_pointer = cie_pointer;
|
fde_out->cie_pointer = cie_pointer;
|
||||||
|
fde_out->cie = cie;
|
||||||
fde_out->pc_range = rng_1u64(pc_begin, pc_begin + pc_range);
|
fde_out->pc_range = rng_1u64(pc_begin, pc_begin + pc_range);
|
||||||
fde_out->insts = str8_skip(data, cursor);
|
fde_out->insts = str8_skip(data, cursor);
|
||||||
|
|
||||||
@@ -3493,6 +3494,38 @@ exit:;
|
|||||||
return is_parsed;
|
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)
|
||||||
|
{
|
||||||
|
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) {
|
||||||
|
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 (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)) {
|
||||||
|
is_parsed = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
internal DW_CFA_ParseErrorCode
|
internal DW_CFA_ParseErrorCode
|
||||||
dw_parse_cfa_inst(String8 data,
|
dw_parse_cfa_inst(String8 data,
|
||||||
U64 code_align_factor,
|
U64 code_align_factor,
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ typedef struct DW_CIE
|
|||||||
U64 code_align_factor;
|
U64 code_align_factor;
|
||||||
S64 data_align_factor;
|
S64 data_align_factor;
|
||||||
U64 ret_addr_reg;
|
U64 ret_addr_reg;
|
||||||
|
U64 ext[4];
|
||||||
DW_Format format;
|
DW_Format format;
|
||||||
U8 version;
|
U8 version;
|
||||||
U8 address_size;
|
U8 address_size;
|
||||||
@@ -388,6 +389,7 @@ typedef struct DW_FDE
|
|||||||
{
|
{
|
||||||
DW_Format format;
|
DW_Format format;
|
||||||
U64 cie_pointer;
|
U64 cie_pointer;
|
||||||
|
DW_CIE *cie;
|
||||||
Rng1U64 pc_range;
|
Rng1U64 pc_range;
|
||||||
String8 insts;
|
String8 insts;
|
||||||
} DW_FDE;
|
} DW_FDE;
|
||||||
@@ -558,6 +560,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 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_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_CIEFromOffsetFunc *cie_from_offset_func, void *cie_from_offset_ud, 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);
|
internal DW_CFA_ParseErrorCode dw_parse_cfa_inst(String8 data, U64 code_align_factor, S64 data_align_factor, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud, U64 *bytes_read_out, DW_CFA_Inst *inst_out);
|
||||||
internal DW_CFA_InstList dw_parse_cfa_inst_list(Arena *arena, String8 data, U64 code_align_factor, S64 data_align_factor, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud);
|
internal DW_CFA_InstList dw_parse_cfa_inst_list(Arena *arena, String8 data, U64 code_align_factor, S64 data_align_factor, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud);
|
||||||
|
|||||||
+72
-24
@@ -1,10 +1,10 @@
|
|||||||
// Copyright (c) Epic Games Tools
|
// Copyright (c) Epic Games Tools
|
||||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||||
|
|
||||||
internal DW_CFA_Row *
|
internal DW_CFI_Row *
|
||||||
dw_make_cfa_row(Arena *arena, U64 reg_count)
|
dw_make_cfi_row(Arena *arena, U64 reg_count)
|
||||||
{
|
{
|
||||||
DW_CFA_Row *row = push_array(arena, DW_CFA_Row, 1);
|
DW_CFI_Row *row = push_array(arena, DW_CFI_Row, 1);
|
||||||
row->regs = push_array(arena, DW_CFI_Register, reg_count);
|
row->regs = push_array(arena, DW_CFI_Register, reg_count);
|
||||||
for EachIndex(reg_idx, reg_count) {
|
for EachIndex(reg_idx, reg_count) {
|
||||||
row->regs[reg_idx].rule = DW_CFI_RegisterRule_SameValue;
|
row->regs[reg_idx].rule = DW_CFI_RegisterRule_SameValue;
|
||||||
@@ -12,12 +12,18 @@ dw_make_cfa_row(Arena *arena, U64 reg_count)
|
|||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DW_CFA_Row *
|
internal void
|
||||||
dw_copy_cfa_row(Arena *arena, U64 reg_count, DW_CFA_Row *row)
|
dw_memcpy_cfi_row(DW_CFI_Row *dst, DW_CFI_Row *src, U64 reg_count)
|
||||||
{
|
{
|
||||||
DW_CFA_Row *new_row = dw_make_cfa_row(arena, reg_count);
|
dst->cfa = src->cfa;
|
||||||
new_row->cfa = row->cfa;
|
MemoryCopyTyped(dst->regs, src->regs, reg_count);
|
||||||
MemoryCopyTyped(new_row->regs, row->regs, reg_count);
|
}
|
||||||
|
|
||||||
|
internal DW_CFI_Row *
|
||||||
|
dw_copy_cfi_row(Arena *arena, DW_CFI_Row *row, U64 reg_count)
|
||||||
|
{
|
||||||
|
DW_CFI_Row *new_row = dw_make_cfi_row(arena, reg_count);
|
||||||
|
dw_memcpy_cfi_row(new_row, row, reg_count);
|
||||||
return new_row;
|
return new_row;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,19 +41,21 @@ dw_cfi_unwind_init(Arena *arena,
|
|||||||
uw->insts = dw_parse_cfa_inst_list(arena, fde->insts, cie->code_align_factor, cie->data_align_factor, decode_ptr_func, decode_ptr_ud);
|
uw->insts = dw_parse_cfa_inst_list(arena, fde->insts, cie->code_align_factor, cie->data_align_factor, decode_ptr_func, decode_ptr_ud);
|
||||||
uw->cie = cie;
|
uw->cie = cie;
|
||||||
uw->fde = fde;
|
uw->fde = fde;
|
||||||
uw->reg_count = dw_reg_count_from_arch(arch);
|
|
||||||
uw->pc = fde->pc_range.min;
|
uw->pc = fde->pc_range.min;
|
||||||
uw->arch = arch;
|
uw->arch = arch;
|
||||||
|
uw->reg_count = dw_reg_count_from_arch(arch);
|
||||||
|
|
||||||
// setup initial register rules
|
// setup initial register rules
|
||||||
DW_CFA_InstList initial_insts = dw_parse_cfa_inst_list(scratch.arena, cie->insts, cie->code_align_factor, cie->data_align_factor, decode_ptr_func, decode_ptr_ud);
|
DW_CFA_InstList initial_insts = dw_parse_cfa_inst_list(scratch.arena, cie->insts, cie->code_align_factor, cie->data_align_factor, decode_ptr_func, decode_ptr_ud);
|
||||||
uw->row = dw_make_cfa_row(arena, uw->reg_count);
|
uw->row = dw_make_cfi_row(arena, uw->reg_count);
|
||||||
uw->curr_inst = initial_insts.first;
|
uw->curr_inst = initial_insts.first;
|
||||||
dw_cfi_next_row(arena, uw);
|
if (!dw_cfi_next_row(arena, uw)) {
|
||||||
|
AssertAlways(0 && "unable to interpret initial row instructions");
|
||||||
|
}
|
||||||
|
|
||||||
// make first row from initial rules
|
// make first row from initial rules
|
||||||
uw->initial_row = uw->row;
|
uw->initial_row = uw->row;
|
||||||
uw->row = dw_copy_cfa_row(arena, uw->reg_count, uw->initial_row);
|
uw->row = dw_copy_cfi_row(arena, uw->initial_row, uw->reg_count);
|
||||||
uw->curr_inst = uw->insts.first;
|
uw->curr_inst = uw->insts.first;
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
@@ -156,12 +164,12 @@ dw_cfi_next_row(Arena *arena, DW_CFI_Unwind *uw)
|
|||||||
|
|
||||||
// Row State Instructions
|
// Row State Instructions
|
||||||
case DW_CFA_RememberState: {
|
case DW_CFA_RememberState: {
|
||||||
DW_CFA_Row *new_row = dw_copy_cfa_row(arena, uw->reg_count, uw->row);
|
DW_CFI_Row *new_row = dw_copy_cfi_row(arena, uw->row, uw->reg_count);
|
||||||
SLLStackPush(uw->row, new_row);
|
SLLStackPush(uw->row, new_row);
|
||||||
} break;
|
} break;
|
||||||
case DW_CFA_RestoreState: {
|
case DW_CFA_RestoreState: {
|
||||||
if (uw->row == 0) { goto exit; } // TODO: report error: unbalanced number of pushes and pops
|
if (uw->row == 0) { goto exit; } // TODO: report error: unbalanced number of pushes and pops
|
||||||
DW_CFA_Row *free_row = uw->row;
|
DW_CFI_Row *free_row = uw->row;
|
||||||
SLLStackPop(uw->row);
|
SLLStackPop(uw->row);
|
||||||
SLLStackPush(uw->free_rows, free_row);
|
SLLStackPush(uw->free_rows, free_row);
|
||||||
} break;
|
} break;
|
||||||
@@ -183,8 +191,47 @@ exit:;
|
|||||||
return is_row_valid;
|
return is_row_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal DW_CFI_Row *
|
||||||
|
dw_cfi_row_from_pc(Arena *arena, Arch arch, DW_CIE *cie, DW_FDE *fde, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ctx, U64 pc)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
|
B32 is_row_found = 0;
|
||||||
|
U64 reg_count = dw_reg_count_from_arch(arch);
|
||||||
|
DW_CFI_Unwind *uw = dw_cfi_unwind_init(scratch.arena, arch, cie, fde, decode_ptr_func, decode_ptr_ctx);
|
||||||
|
DW_CFI_Row *row = dw_copy_cfi_row(scratch.arena, uw->row, uw->reg_count);
|
||||||
|
U64 prev_pc = uw->pc;
|
||||||
|
while (dw_cfi_next_row(scratch.arena, uw)) {
|
||||||
|
if (prev_pc <= pc && pc < uw->pc) {
|
||||||
|
is_row_found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dw_memcpy_cfi_row(row, uw->row, uw->reg_count);
|
||||||
|
prev_pc = uw->pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle last row
|
||||||
|
if (!is_row_found) {
|
||||||
|
if (prev_pc <= pc && pc < uw->pc && contains_1u64(fde->pc_range, pc)) {
|
||||||
|
row = uw->row;
|
||||||
|
is_row_found = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy out final row
|
||||||
|
DW_CFI_Row *result = 0;
|
||||||
|
if (is_row_found) {
|
||||||
|
result = dw_copy_cfi_row(arena, row, reg_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch_end(scratch);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
internal DW_UnwindStatus
|
internal DW_UnwindStatus
|
||||||
dw_cfi_apply_register_rules(DW_CFI_Unwind *uw,
|
dw_cfi_apply_register_rules(Arch arch,
|
||||||
|
DW_CIE *cie,
|
||||||
|
DW_CFI_Row *row,
|
||||||
DW_MemRead *mem_read_func, void *mem_read_ud,
|
DW_MemRead *mem_read_func, void *mem_read_ud,
|
||||||
DW_RegRead *reg_read_func, void *reg_read_ud,
|
DW_RegRead *reg_read_func, void *reg_read_ud,
|
||||||
DW_RegWrite *reg_write_func, void *reg_write_ud)
|
DW_RegWrite *reg_write_func, void *reg_write_ud)
|
||||||
@@ -192,8 +239,6 @@ dw_cfi_apply_register_rules(DW_CFI_Unwind *uw,
|
|||||||
Temp scratch = scratch_begin(0,0);
|
Temp scratch = scratch_begin(0,0);
|
||||||
DW_UnwindStatus unwind_status = DW_UnwindStatus_Ok;
|
DW_UnwindStatus unwind_status = DW_UnwindStatus_Ok;
|
||||||
|
|
||||||
DW_CFA_Row *row = uw->row;
|
|
||||||
|
|
||||||
// establish CFA
|
// establish CFA
|
||||||
U64 cfa = 0;
|
U64 cfa = 0;
|
||||||
switch (row->cfa.rule) {
|
switch (row->cfa.rule) {
|
||||||
@@ -201,7 +246,7 @@ dw_cfi_apply_register_rules(DW_CFI_Unwind *uw,
|
|||||||
case DW_CFA_Rule_RegOff: {
|
case DW_CFA_Rule_RegOff: {
|
||||||
// TODO: report error (invalid register read)
|
// TODO: report error (invalid register read)
|
||||||
U64 cfa_reg_value = 0;
|
U64 cfa_reg_value = 0;
|
||||||
U64 reg_size = dw_reg_size_from_code(uw->arch, row->cfa.reg);
|
U64 reg_size = dw_reg_size_from_code(arch, row->cfa.reg);
|
||||||
AssertAlways(reg_size <= sizeof(cfa_reg_value));
|
AssertAlways(reg_size <= sizeof(cfa_reg_value));
|
||||||
unwind_status = reg_read_func(row->cfa.reg, &cfa_reg_value, reg_size, reg_read_ud);
|
unwind_status = reg_read_func(row->cfa.reg, &cfa_reg_value, reg_size, reg_read_ud);
|
||||||
if (unwind_status != DW_UnwindStatus_Ok) { goto exit; }
|
if (unwind_status != DW_UnwindStatus_Ok) { goto exit; }
|
||||||
@@ -212,11 +257,12 @@ dw_cfi_apply_register_rules(DW_CFI_Unwind *uw,
|
|||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
U64 max_reg_size = dw_reg_max_size_from_arch(uw->arch);
|
U64 max_reg_size = dw_reg_max_size_from_arch(arch);
|
||||||
void *reg_buffer = push_array(scratch.arena, U8, max_reg_size);
|
void *reg_buffer = push_array(scratch.arena, U8, max_reg_size);
|
||||||
|
|
||||||
for EachIndex(reg_idx, uw->reg_count) {
|
U64 reg_count = dw_reg_count_from_arch(arch);
|
||||||
DW_CFI_Register *reg = &uw->row->regs[reg_idx];
|
for EachIndex(reg_idx, reg_count) {
|
||||||
|
DW_CFI_Register *reg = &row->regs[reg_idx];
|
||||||
switch (reg->rule) {
|
switch (reg->rule) {
|
||||||
case DW_CFI_RegisterRule_Undefined: {
|
case DW_CFI_RegisterRule_Undefined: {
|
||||||
// TODO: ???
|
// TODO: ???
|
||||||
@@ -226,7 +272,7 @@ dw_cfi_apply_register_rules(DW_CFI_Unwind *uw,
|
|||||||
case DW_CFI_RegisterRule_Offset: {
|
case DW_CFI_RegisterRule_Offset: {
|
||||||
// read register value from memory
|
// read register value from memory
|
||||||
U64 addr = cfa + reg->n;
|
U64 addr = cfa + reg->n;
|
||||||
U64 reg_size = dw_reg_size_from_code(uw->arch, reg_idx);
|
U64 reg_size = dw_reg_size_from_code(arch, reg_idx);
|
||||||
unwind_status = mem_read_func(addr, reg_size, reg_buffer, mem_read_ud);
|
unwind_status = mem_read_func(addr, reg_size, reg_buffer, mem_read_ud);
|
||||||
if (unwind_status != DW_UnwindStatus_Ok) { goto exit; }
|
if (unwind_status != DW_UnwindStatus_Ok) { goto exit; }
|
||||||
|
|
||||||
@@ -239,13 +285,13 @@ dw_cfi_apply_register_rules(DW_CFI_Unwind *uw,
|
|||||||
U64 reg_value = cfa + reg->n;
|
U64 reg_value = cfa + reg->n;
|
||||||
|
|
||||||
// write register value to the thread context
|
// write register value to the thread context
|
||||||
U64 reg_size = dw_reg_size_from_code(uw->arch, reg_idx);
|
U64 reg_size = dw_reg_size_from_code(arch, reg_idx);
|
||||||
unwind_status = reg_write_func(reg_idx, ®_value, reg_size, reg_write_ud);
|
unwind_status = reg_write_func(reg_idx, ®_value, reg_size, reg_write_ud);
|
||||||
if (unwind_status != DW_UnwindStatus_Ok) { goto exit; }
|
if (unwind_status != DW_UnwindStatus_Ok) { goto exit; }
|
||||||
} break;
|
} break;
|
||||||
case DW_CFI_RegisterRule_Register: {
|
case DW_CFI_RegisterRule_Register: {
|
||||||
// read register value from another register
|
// read register value from another register
|
||||||
U64 reg_size = dw_reg_size_from_code(uw->arch, reg_idx);
|
U64 reg_size = dw_reg_size_from_code(arch, reg_idx);
|
||||||
unwind_status = reg_read_func(reg->n, reg_buffer, reg_size, reg_read_ud);
|
unwind_status = reg_read_func(reg->n, reg_buffer, reg_size, reg_read_ud);
|
||||||
if (unwind_status != DW_UnwindStatus_Ok) { goto exit; }
|
if (unwind_status != DW_UnwindStatus_Ok) { goto exit; }
|
||||||
|
|
||||||
@@ -264,9 +310,11 @@ dw_cfi_apply_register_rules(DW_CFI_Unwind *uw,
|
|||||||
case DW_CFI_RegisterRule_Architectural: {
|
case DW_CFI_RegisterRule_Architectural: {
|
||||||
NotImplemented;
|
NotImplemented;
|
||||||
} break;
|
} break;
|
||||||
|
default: { InvalidPath; } break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exit:;
|
exit:;
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
return unwind_status;
|
return unwind_status;
|
||||||
|
|||||||
+12
-10
@@ -44,25 +44,25 @@ typedef struct DW_CFI_Register
|
|||||||
};
|
};
|
||||||
} DW_CFI_Register;
|
} DW_CFI_Register;
|
||||||
|
|
||||||
typedef struct DW_CFA_Row
|
typedef struct DW_CFI_Row
|
||||||
{
|
{
|
||||||
DW_CFA cfa;
|
DW_CFA cfa;
|
||||||
DW_CFI_Register *regs;
|
DW_CFI_Register *regs;
|
||||||
struct DW_CFA_Row *next;
|
struct DW_CFI_Row *next;
|
||||||
} DW_CFA_Row;
|
} DW_CFI_Row;
|
||||||
|
|
||||||
typedef struct DW_CFI_Unwind
|
typedef struct DW_CFI_Unwind
|
||||||
{
|
{
|
||||||
DW_CFA_InstList insts;
|
DW_CFA_InstList insts;
|
||||||
DW_CIE *cie;
|
DW_CIE *cie;
|
||||||
DW_FDE *fde;
|
DW_FDE *fde;
|
||||||
DW_CFA_Row *initial_row;
|
DW_CFI_Row *initial_row;
|
||||||
DW_CFA_Row *row;
|
DW_CFI_Row *row;
|
||||||
DW_CFA_InstNode *curr_inst;
|
DW_CFA_InstNode *curr_inst;
|
||||||
DW_CFA_Row *free_rows;
|
DW_CFI_Row *free_rows;
|
||||||
DW_Reg reg_count;
|
|
||||||
U64 pc;
|
U64 pc;
|
||||||
Arch arch;
|
Arch arch;
|
||||||
|
U64 reg_count;
|
||||||
} DW_CFI_Unwind;
|
} DW_CFI_Unwind;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -82,12 +82,14 @@ typedef DW_MEM_READ(DW_MemRead);
|
|||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
internal DW_CFA_Row * dw_make_cfa_row(Arena *arena, U64 reg_count);
|
internal DW_CFI_Row * dw_make_cfi_row(Arena *arena, U64 reg_count);
|
||||||
internal DW_CFA_Row * dw_copy_cfa_row(Arena *arena, U64 reg_count, DW_CFA_Row *row);
|
internal DW_CFI_Row * dw_copy_cfi_row(Arena *arena, DW_CFI_Row *row, U64 reg_count);
|
||||||
|
|
||||||
internal DW_CFI_Unwind * dw_cfi_unwind_init(Arena *arena, Arch arch, DW_CIE *cie, DW_FDE *fde, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud);
|
internal DW_CFI_Unwind * dw_cfi_unwind_init(Arena *arena, Arch arch, DW_CIE *cie, DW_FDE *fde, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ud);
|
||||||
internal B32 dw_cfi_next_row(Arena *arena, DW_CFI_Unwind *uw);
|
internal B32 dw_cfi_next_row(Arena *arena, DW_CFI_Unwind *uw);
|
||||||
internal DW_UnwindStatus dw_cfi_apply_register_rules(DW_CFI_Unwind *uw, DW_MemRead *mem_read_func, void *mem_read_ud, DW_RegRead *reg_read_func, void *reg_read_ud, DW_RegWrite *reg_write_func, void *reg_write_ud);
|
|
||||||
|
internal DW_CFI_Row * dw_cfi_row_from_pc(Arena *arena, Arch arch, struct DW_CIE *cie, struct DW_FDE *fde, DW_DecodePtr *decode_ptr_func, void *decode_ptr_ctx, U64 pc);
|
||||||
|
internal DW_UnwindStatus dw_cfi_apply_register_rules(Arch arch, DW_CIE *cie, DW_CFI_Row *row, DW_MemRead *mem_read_func, void *mem_read_ud, DW_RegRead *reg_read_func, void *reg_read_ud, DW_RegWrite *reg_write_func, void *reg_write_ud);
|
||||||
|
|
||||||
#endif // DWARF_UNWIND_H
|
#endif // DWARF_UNWIND_H
|
||||||
|
|
||||||
|
|||||||
+287
-95
@@ -2,10 +2,14 @@
|
|||||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||||
|
|
||||||
internal U64
|
internal U64
|
||||||
eh_read_ptr(String8 frame_base, U64 off, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_out)
|
eh_parse_ptr(String8 frame_base, U64 off, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_out)
|
||||||
{
|
{
|
||||||
U64 ptr_off = off;
|
U64 ptr_off = off;
|
||||||
|
|
||||||
|
if (encoding == EH_PtrEnc_Omit) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// align read offset as needed
|
// align read offset as needed
|
||||||
if (encoding == EH_PtrEnc_Aligned) {
|
if (encoding == EH_PtrEnc_Aligned) {
|
||||||
ptr_off = AlignPow2(ptr_off, ptr_ctx->ptr_align);
|
ptr_off = AlignPow2(ptr_off, ptr_ctx->ptr_align);
|
||||||
@@ -65,6 +69,60 @@ eh_read_ptr(String8 frame_base, U64 off, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding,
|
|||||||
return decode_size;
|
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;
|
||||||
|
|
||||||
|
EH_PtrEnc fde_count_enc = 0;
|
||||||
|
U64 fde_count_enc_size = str8_deserial_read_struct(data, cursor, &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;
|
||||||
|
|
||||||
|
U64 eh_frame_ptr = 0;
|
||||||
|
cursor += eh_parse_ptr(data, cursor, ptr_ctx, header.eh_frame_ptr_enc, &eh_frame_ptr);
|
||||||
|
|
||||||
|
cursor += eh_parse_ptr(data, cursor, ptr_ctx, header.fde_count_enc, &header.fde_count);
|
||||||
|
|
||||||
|
switch (header.table_enc) {
|
||||||
|
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
|
internal U64
|
||||||
eh_parse_aug_data(String8 aug_string, String8 aug_data, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out)
|
eh_parse_aug_data(String8 aug_string, String8 aug_data, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out)
|
||||||
{
|
{
|
||||||
@@ -91,7 +149,7 @@ eh_parse_aug_data(String8 aug_string, String8 aug_data, EH_PtrCtx *ptr_ctx, EH_A
|
|||||||
} break;
|
} break;
|
||||||
case 'P': {
|
case 'P': {
|
||||||
aug_cursor += str8_deserial_read_struct(aug_data, aug_cursor, &handler_encoding);
|
aug_cursor += str8_deserial_read_struct(aug_data, aug_cursor, &handler_encoding);
|
||||||
aug_cursor += eh_read_ptr(aug_data, aug_cursor, ptr_ctx, handler_encoding, &handler_ip);
|
aug_cursor += eh_parse_ptr(aug_data, aug_cursor, ptr_ctx, handler_encoding, &handler_ip);
|
||||||
aug_flags |= EH_AugFlag_HasHandler;
|
aug_flags |= EH_AugFlag_HasHandler;
|
||||||
} break;
|
} break;
|
||||||
case 'R': {
|
case 'R': {
|
||||||
@@ -116,128 +174,262 @@ exit:;
|
|||||||
return parse_size;
|
return parse_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal U64
|
internal B32
|
||||||
eh_size_from_aug_data(String8 aug_string, String8 data, EH_PtrCtx *ptr_ctx)
|
eh_parse_cie(String8 data, DW_Format format, Arch arch, EH_PtrCtx *ptr_ctx, DW_CIE *cie_out)
|
||||||
{
|
{
|
||||||
return eh_parse_aug_data(aug_string, data, ptr_ctx, 0);
|
B32 is_parsed = 0;
|
||||||
}
|
U64 cursor = format == DW_Format_32Bit ? 4 : 12;
|
||||||
|
|
||||||
internal U64
|
U64 cie_id = 0;
|
||||||
eh_frame_hdr_search_linear_x64(String8 raw_eh_frame_hdr, EH_PtrCtx *ptr_ctx, U64 location)
|
U64 cie_id_size = str8_deserial_read_dwarf_uint(data, cursor, format, &cie_id);
|
||||||
{
|
if (cie_id_size == 0) { goto exit; }
|
||||||
// Table contains only addresses for first instruction in a function and we cannot
|
cursor += cie_id_size;
|
||||||
// guarantee that result is FDE that corresponds to the input location.
|
|
||||||
// So input location must be cheked against range from FDE header again.
|
|
||||||
|
|
||||||
U64 closest_location = max_U64;
|
|
||||||
U64 closest_address = max_U64;
|
|
||||||
|
|
||||||
U64 cursor = 0;
|
|
||||||
|
|
||||||
U8 version = 0;
|
U8 version = 0;
|
||||||
cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &version);
|
U64 version_size = str8_deserial_read_struct(data, cursor, &version);
|
||||||
|
if (version_size == 0) { goto exit; }
|
||||||
|
cursor += version_size;
|
||||||
|
|
||||||
|
String8 aug_string = {0};
|
||||||
|
EH_Augmentation aug = {0};
|
||||||
|
U64 code_align_factor = 0;
|
||||||
|
S64 data_align_factor = 0;
|
||||||
|
U64 ret_addr_reg = max_U64;
|
||||||
if (version == 1) {
|
if (version == 1) {
|
||||||
#if 0
|
U64 aug_string_size = str8_deserial_read_cstr(data, cursor, &aug_string);
|
||||||
EH_PtrCtx ptr_ctx = {0};
|
if (aug_string_size == 0) { goto exit; }
|
||||||
// Set this to base address of .eh_frame_hdr. Entries are relative
|
cursor += aug_string_size;
|
||||||
// to this section for some reason.
|
|
||||||
ptr_ctx.data_vaddr = range.min;
|
|
||||||
// If input location is VMA then set this to address of .text.
|
|
||||||
// Pointer parsing function will adjust "init_location" to correct VMA.
|
|
||||||
ptr_ctx.text_vaddr = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EH_PtrEnc eh_frame_ptr_enc = 0, fde_count_enc = 0, table_enc = 0;
|
U64 aug_data_size = eh_parse_aug_data(aug_string, str8_skip(data, cursor), ptr_ctx, &aug);
|
||||||
cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &eh_frame_ptr_enc);
|
cursor += aug_data_size;
|
||||||
cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &fde_count_enc);
|
|
||||||
cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &table_enc);
|
|
||||||
|
|
||||||
U64 eh_frame_ptr = 0, fde_count = 0;
|
U64 code_align_factor_size = str8_deserial_read_uleb128(data, cursor, &code_align_factor);
|
||||||
NotImplemented;
|
if (code_align_factor_size == 0) { goto exit; }
|
||||||
//cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, eh_frame_ptr_enc, cursor, &eh_frame_ptr);
|
cursor += code_align_factor_size;
|
||||||
//cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, fde_count_enc, cursor, &fde_count);
|
|
||||||
|
|
||||||
for (U64 fde_idx = 0; fde_idx < fde_count; ++fde_idx) {
|
U64 data_align_factor_size = str8_deserial_read_sleb128(data, cursor, &data_align_factor);
|
||||||
U64 init_location = 0, address = 0;
|
if (data_align_factor_size == 0) { goto exit; }
|
||||||
NotImplemented;
|
cursor += data_align_factor_size;
|
||||||
//cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, table_enc, cursor, &init_location);
|
|
||||||
//cursor += dw_unwind_parse_pointer_x64(raw_eh_frame_hdr.str, rng_1u64(0, raw_eh_frame_hdr.size), ptr_ctx, table_enc, cursor, &address);
|
|
||||||
|
|
||||||
S64 current_delta = (S64)(location - init_location);
|
ret_addr_reg = 0;
|
||||||
S64 closest_delta = (S64)(location - closest_location);
|
U64 ret_addr_reg_size = str8_deserial_read(data, cursor, &ret_addr_reg, sizeof(U8), sizeof(U8));
|
||||||
if (0 <= current_delta && current_delta < closest_delta) {
|
if (ret_addr_reg_size == 0) { goto exit; }
|
||||||
closest_location = init_location;
|
cursor += ret_addr_reg_size;
|
||||||
closest_address = address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// address where to find corresponding FDE, this is an absolute offset
|
cie_out->insts = str8_skip(data, cursor);
|
||||||
// into the image file.
|
cie_out->aug_string = aug_string;
|
||||||
return closest_address;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
is_parsed = 1;
|
||||||
internal DW_CFIRecords
|
exit:;
|
||||||
dw_unwind_eh_frame_hdr_from_ip_fast_x64(String8 raw_eh_frame, String8 raw_eh_frame_hdr, EH_PtrCtx *ptr_ctx, U64 ip_voff)
|
return is_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
eh_parse_fde(String8 data,
|
||||||
|
DW_Format format,
|
||||||
|
DW_CIEFromOffsetFunc *cie_from_offset_func,
|
||||||
|
void *cie_from_offset_ud,
|
||||||
|
EH_PtrCtx *ptr_ctx,
|
||||||
|
DW_FDE *fde_out)
|
||||||
{
|
{
|
||||||
DW_CFIRecords result = {0};
|
B32 is_parsed = 0;
|
||||||
|
U64 cursor = format == DW_Format_32Bit ? 4 : 12;
|
||||||
|
|
||||||
// find FDE offset
|
U64 cie_pointer = 0;
|
||||||
void *eh_frame_hdr = raw_eh_frame.str;
|
U64 cie_pointer_size = str8_deserial_read_dwarf_uint(data, cursor, format, &cie_pointer);
|
||||||
U64 fde_offset = dw_search_eh_frame_hdr_linear_x64(raw_eh_frame_hdr, ptr_ctx, ip_voff);
|
if (cie_pointer_size == 0) { goto exit; }
|
||||||
|
|
||||||
B32 is_fde_offset_valid = (fde_offset != max_U64);
|
DW_CIE *cie = cie_from_offset_func(cie_from_offset_ud, cie_pointer);
|
||||||
if (is_fde_offset_valid) {
|
if (cie == 0) { goto exit; }
|
||||||
U64 fde_read_offset = (fde_offset - ptr_ctx->raw_base_vaddr);
|
|
||||||
|
|
||||||
// read FDE size
|
EH_PtrEnc addr_enc = cie->ext[EH_CIE_Ext_AddrEnc];
|
||||||
U64 fde_size = 0;
|
|
||||||
fde_read_offset += str8_deserial_read_dwarf_packed_size(raw_eh_frame, fde_read_offset, &fde_size);
|
|
||||||
|
|
||||||
// read FDE discriminator
|
U64 pc_begin = 0;
|
||||||
U32 fde_discrim = 0;
|
U64 pc_range = 0;
|
||||||
fde_read_offset += str8_deserial_read_struct(raw_eh_frame, fde_read_offset, &fde_discrim);
|
if (addr_enc != EH_PtrEnc_Omit) {
|
||||||
|
U64 pc_begin_size = eh_parse_ptr(data, cursor, ptr_ctx, addr_enc, &pc_begin);
|
||||||
|
if (pc_begin_size == 0) { goto exit; }
|
||||||
|
cursor += pc_begin_size;
|
||||||
|
|
||||||
// compute parent CIE offset
|
U64 pc_range_size = eh_parse_ptr(data, cursor, ptr_ctx, addr_enc, &pc_range_size);
|
||||||
U64 cie_read_offset = fde_read_offset - (fde_discrim + sizeof(fde_discrim));
|
if (pc_range_size == 0) { goto exit; }
|
||||||
|
cursor += pc_range_size;
|
||||||
|
}
|
||||||
|
|
||||||
// read CIE size
|
if (cursor + cie->aug_data.size > data.size) { goto exit; }
|
||||||
U64 cie_size = 0;
|
cursor += cie->aug_data.size;
|
||||||
cie_read_offset += str8_deserial_read_dwarf_packed_size(raw_eh_frame, cie_read_offset, &cie_size);
|
|
||||||
|
|
||||||
// read CIE discriminator
|
fde_out->format = format;
|
||||||
U32 cie_discrim = max_U32;
|
fde_out->cie_pointer = cie_pointer;
|
||||||
cie_read_offset += str8_deserial_read_struct(raw_eh_frame, cie_read_offset, &cie_discrim);
|
fde_out->cie = cie;
|
||||||
|
fde_out->pc_range = rng_1u64(pc_begin, pc_begin + pc_range);
|
||||||
|
fde_out->insts = str8_skip(data, cursor);
|
||||||
|
|
||||||
B32 is_fde = (fde_discrim != 0);
|
is_parsed = 1;
|
||||||
B32 is_cie = (cie_discrim == 0);
|
exit:;
|
||||||
if (is_fde && is_cie) {
|
return is_parsed;
|
||||||
Rng1U64 cie_range = rng_1u64(0, cie_read_offset + (cie_size - sizeof(cie_discrim)));
|
}
|
||||||
Rng1U64 fde_range = rng_1u64(0, fde_read_offset + (fde_size - sizeof(fde_discrim)));
|
|
||||||
|
|
||||||
// parse CIE
|
internal
|
||||||
DW_CIE cie = {0};
|
DW_CIE_FROM_OFFSET_FUNC(eh_parse_cfi_cie_from_offset)
|
||||||
dw_unwind_parse_cie_x64(raw_eh_frame.str, cie_range, ptr_ctx, cie_read_offset, &cie);
|
{
|
||||||
|
return ud;
|
||||||
|
}
|
||||||
|
|
||||||
// parse FDE
|
internal B32
|
||||||
DW_FDE fde = {0};
|
eh_parse_cfi(String8 eh_frame, U64 fde_offset, Arch arch, EH_PtrCtx *ptr_ctx, DW_CIE *cie_out, DW_FDE *fde_out)
|
||||||
NotImplemented;
|
{
|
||||||
//dw_unwind_parse_fde_x64(raw_eh_frame.str, fde_range, ptr_ctx, &cie, fde_read_offset, &fde);
|
B32 is_parsed = 0;
|
||||||
|
DW_DescriptorEntry fde_desc = {0};
|
||||||
// range check instruction pointer
|
dw_parse_descriptor_entry_header(eh_frame, fde_offset, &fde_desc);
|
||||||
if (contains_1u64(fde.pc_range, ip_voff)) {
|
if (fde_desc.type == DW_DescriptorEntryType_FDE) {
|
||||||
result.valid = 1;
|
U64 cie_pointer_off = fde_desc.format == DW_Format_32Bit ? 4 : 12;
|
||||||
result.cie = cie;
|
U64 cie_pointer = 0;
|
||||||
result.fde = fde;
|
U64 cie_pointer_size = str8_deserial_read_dwarf_uint(eh_frame, fde_offset + cie_pointer_off, fde_desc.format, &cie_pointer);
|
||||||
|
if (cie_pointer_size) {
|
||||||
|
DW_DescriptorEntry cie_desc = {0};
|
||||||
|
dw_parse_descriptor_entry_header(eh_frame, cie_pointer, &cie_desc);
|
||||||
|
if (cie_desc.type == DW_DescriptorEntryType_CIE) {
|
||||||
|
if (eh_parse_cie(str8_substr(eh_frame, cie_desc.entry_range), cie_desc.format, arch, ptr_ctx, cie_out)) {
|
||||||
|
if (eh_parse_fde(str8_substr(eh_frame, fde_desc.entry_range), fde_desc.format, eh_parse_cfi_cie_from_offset, cie_out, ptr_ctx, fde_out)) {
|
||||||
|
return is_parsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
|
return is_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal U64
|
||||||
|
eh_find_nearest_fde(EH_FrameHdr header, EH_PtrCtx *ptr_ctx, U64 pc)
|
||||||
|
{
|
||||||
|
U64 fde_offset = 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, header.table_enc, &first);
|
||||||
|
AssertAlways(first_size);
|
||||||
|
if (first == pc) {
|
||||||
|
fde_idx = 0;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (first > pc) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
U64 last = 0;
|
||||||
|
U64 last_size = eh_parse_ptr(header.table, header.table.size - header.entry_byte_size, ptr_ctx, header.table_enc, &last);
|
||||||
|
AssertAlways(last_size);
|
||||||
|
if (last <= pc) {
|
||||||
|
fde_idx = header.fde_count - 1;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first <= pc && pc < last) {
|
||||||
|
U64 l = 0;
|
||||||
|
U64 r = header.fde_count - 1;
|
||||||
|
while (l <= r) {
|
||||||
|
U64 m = l + (r - l) / 2;
|
||||||
|
U64 m_pc = 0;
|
||||||
|
U64 m_pc_size = eh_parse_ptr(header.table, m * header.entry_byte_size, ptr_ctx, header.table_enc, &m_pc);
|
||||||
|
Assert(m_pc_size);
|
||||||
|
|
||||||
|
if (m_pc == pc) {
|
||||||
|
fde_idx = m;
|
||||||
|
goto exit;
|
||||||
|
} else if (m_pc < pc) {
|
||||||
|
l = m + 1;
|
||||||
|
} else {
|
||||||
|
r = m - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fde_idx = l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:;
|
||||||
|
if (fde_idx < header.fde_count) {
|
||||||
|
U64 fde_offset_size = eh_parse_ptr(header.table, (fde_idx * header.entry_byte_size) + header.field_byte_size, ptr_ctx, header.table_enc, &fde_offset);
|
||||||
|
Assert(fde_offset_size);
|
||||||
|
}
|
||||||
|
return fde_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, ctx->addr_enc, ptr_out);
|
||||||
|
}
|
||||||
|
|
||||||
internal String8
|
internal String8
|
||||||
eh_string_from_ptr_enc_type(EH_PtrEnc type)
|
eh_string_from_ptr_enc_type(EH_PtrEnc type)
|
||||||
|
|||||||
+43
-2
@@ -4,6 +4,9 @@
|
|||||||
#ifndef EH_FRAME_H
|
#ifndef EH_FRAME_H
|
||||||
#define EH_FRAME_H
|
#define EH_FRAME_H
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
//~ Format
|
||||||
|
|
||||||
typedef U8 EH_PtrEnc;
|
typedef U8 EH_PtrEnc;
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
@@ -65,10 +68,48 @@ typedef struct EH_Augmentation
|
|||||||
} EH_Augmentation;
|
} EH_Augmentation;
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
//~ Parser
|
||||||
|
|
||||||
internal U64 eh_read_ptr(String8 frame_base, U64 off, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_out);
|
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;
|
||||||
|
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, 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, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out);
|
internal U64 eh_parse_aug_data(String8 aug_string, String8 aug_data, EH_PtrCtx *ptr_ctx, EH_Augmentation *aug_out);
|
||||||
internal U64 eh_size_from_aug_data(String8 aug_string, String8 data, EH_PtrCtx *ptr_ctx);
|
internal B32 eh_parse_cfi(String8 eh_frame, U64 fde_offset, Arch arch, EH_PtrCtx *ptr_ctx, struct DW_CIE *cie_out, struct 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
|
//~ Enum -> String
|
||||||
|
|||||||
@@ -217,8 +217,8 @@
|
|||||||
#include "pdb/pdb.h"
|
#include "pdb/pdb.h"
|
||||||
#include "pdb/pdb_parse.h"
|
#include "pdb/pdb_parse.h"
|
||||||
#include "pdb/pdb_stringize.h"
|
#include "pdb/pdb_stringize.h"
|
||||||
#include "eh/eh_frame.h"
|
|
||||||
#include "dwarf/dwarf_inc.h"
|
#include "dwarf/dwarf_inc.h"
|
||||||
|
#include "eh/eh_frame.h"
|
||||||
#include "rdi_from_coff/rdi_from_coff.h"
|
#include "rdi_from_coff/rdi_from_coff.h"
|
||||||
#include "rdi_from_elf/rdi_from_elf.h"
|
#include "rdi_from_elf/rdi_from_elf.h"
|
||||||
#include "rdi_from_pdb/rdi_from_pdb.h"
|
#include "rdi_from_pdb/rdi_from_pdb.h"
|
||||||
@@ -265,8 +265,8 @@
|
|||||||
#include "pdb/pdb.c"
|
#include "pdb/pdb.c"
|
||||||
#include "pdb/pdb_parse.c"
|
#include "pdb/pdb_parse.c"
|
||||||
#include "pdb/pdb_stringize.c"
|
#include "pdb/pdb_stringize.c"
|
||||||
#include "eh/eh_frame.c"
|
|
||||||
#include "dwarf/dwarf_inc.c"
|
#include "dwarf/dwarf_inc.c"
|
||||||
|
#include "eh/eh_frame.c"
|
||||||
#include "rdi_from_coff/rdi_from_coff.c"
|
#include "rdi_from_coff/rdi_from_coff.c"
|
||||||
#include "rdi_from_elf/rdi_from_elf.c"
|
#include "rdi_from_elf/rdi_from_elf.c"
|
||||||
#include "rdi_from_pdb/rdi_from_pdb.c"
|
#include "rdi_from_pdb/rdi_from_pdb.c"
|
||||||
|
|||||||
Reference in New Issue
Block a user