mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-12 23:31:38 -07:00
factor out C++ ABI exception handling
This commit is contained in:
+78
-173
@@ -55,7 +55,7 @@ dw_unwind_x64(String8 raw_text,
|
||||
//- find cfi records
|
||||
DW_CFIRecords cfi_recs = {0};
|
||||
if (has_needed_sections) {
|
||||
DW_EhPtrCtx ptr_ctx = {0};
|
||||
EH_PtrCtx ptr_ctx = {0};
|
||||
ptr_ctx.raw_base_vaddr = frame_base_voff;
|
||||
ptr_ctx.text_vaddr = text_base_vaddr;
|
||||
ptr_ctx.data_vaddr = data_base_vaddr;
|
||||
@@ -75,11 +75,11 @@ dw_unwind_x64(String8 raw_text,
|
||||
//- cfi machine setup
|
||||
DW_CFIMachine machine = {0};
|
||||
if (cfi_recs.valid) {
|
||||
DW_EhPtrCtx ptr_ctx = {0};
|
||||
EH_PtrCtx ptr_ctx = {0};
|
||||
ptr_ctx.raw_base_vaddr = frame_base_voff;
|
||||
ptr_ctx.text_vaddr = text_base_vaddr;
|
||||
ptr_ctx.data_vaddr = data_base_vaddr;
|
||||
ptr_ctx.func_vaddr = cfi_recs.fde.ip_voff_range.min + rebase_voff_to_vaddr; // TODO: it's not super clear how to set up this member, need more test cases
|
||||
ptr_ctx.func_vaddr = cfi_recs.fde.pc_range.min + rebase_voff_to_vaddr; // TODO: it's not super clear how to set up this member, need more test cases
|
||||
machine = dw_unwind_make_machine_x64(DW_UNWIND_X64__REG_SLOT_COUNT, &cfi_recs.cie, &ptr_ctx);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ dw_unwind_x64(String8 raw_text,
|
||||
if (init_row != 0) {
|
||||
// upgrade machine with new equipment
|
||||
dw_unwind_machine_equip_initial_row_x64(&machine, init_row);
|
||||
dw_unwind_machine_equip_fde_ip_x64(&machine, cfi_recs.fde.ip_voff_range.min);
|
||||
dw_unwind_machine_equip_fde_ip_x64(&machine, cfi_recs.fde.pc_range.min);
|
||||
|
||||
// decode main row
|
||||
Rng1U64 main_cfi_range = cfi_recs.fde.cfi_range;
|
||||
@@ -320,100 +320,10 @@ dw_unwind_init_x64(void)
|
||||
}
|
||||
}
|
||||
|
||||
internal U64
|
||||
dw_unwind_parse_pointer_x64(void *frame_base, Rng1U64 frame_range, DW_EhPtrCtx *ptr_ctx, DW_EhPtrEnc encoding, U64 off, U64 *ptr_out)
|
||||
{
|
||||
// aligned offset
|
||||
U64 pointer_off = off;
|
||||
if (encoding == DW_EhPtrEnc_Aligned) {
|
||||
pointer_off = AlignPow2(off, 8); // TODO: align to 4 bytes when we parse x86 ELF binary
|
||||
encoding = DW_EhPtrEnc_Ptr;
|
||||
}
|
||||
|
||||
// decode pointer value
|
||||
U64 size_param = 0;
|
||||
U64 after_pointer_off = 0;
|
||||
U64 raw_pointer = 0;
|
||||
switch (encoding & DW_EhPtrEnc_TypeMask) {
|
||||
default:break;
|
||||
|
||||
case DW_EhPtrEnc_Ptr : size_param = 8; goto ufixed;
|
||||
case DW_EhPtrEnc_UData2: size_param = 2; goto ufixed;
|
||||
case DW_EhPtrEnc_UData4: size_param = 4; goto ufixed;
|
||||
case DW_EhPtrEnc_UData8: size_param = 8; goto ufixed;
|
||||
ufixed:
|
||||
{
|
||||
dw_based_range_read(frame_base, frame_range, pointer_off, size_param, &raw_pointer);
|
||||
after_pointer_off = pointer_off + size_param;
|
||||
} break;
|
||||
|
||||
// TODO: Signed is actually just a flag that indicates this int is negavite.
|
||||
// There shouldn't be a read for Signed.
|
||||
// For instance, (DW_EhPtrEnc_UData2 | DW_EhPtrEnc_Signed) == DW_EhPtrEnc_SData etc.
|
||||
case DW_EhPtrEnc_Signed:size_param = 8; goto sfixed;
|
||||
|
||||
case DW_EhPtrEnc_SData2:size_param = 2; goto sfixed;
|
||||
case DW_EhPtrEnc_SData4:size_param = 4; goto sfixed;
|
||||
case DW_EhPtrEnc_SData8:size_param = 8; goto sfixed;
|
||||
sfixed:
|
||||
{
|
||||
dw_based_range_read(frame_base, frame_range, pointer_off, size_param, &raw_pointer);
|
||||
after_pointer_off = pointer_off + size_param;
|
||||
// sign extension
|
||||
U64 sign_bit = size_param*8 - 1;
|
||||
if ((raw_pointer >> sign_bit) != 0) {
|
||||
raw_pointer |= (~(1 << sign_bit)) + 1;
|
||||
}
|
||||
} break;
|
||||
|
||||
case DW_EhPtrEnc_ULEB128:
|
||||
{
|
||||
U64 size = dw_based_range_read_uleb128(frame_base, frame_range, pointer_off, &raw_pointer);
|
||||
after_pointer_off = pointer_off + size;
|
||||
} break;
|
||||
|
||||
case DW_EhPtrEnc_SLEB128:
|
||||
{
|
||||
U64 size = dw_based_range_read_sleb128(frame_base, frame_range, pointer_off,
|
||||
(S64*)&raw_pointer);
|
||||
after_pointer_off = pointer_off + size;
|
||||
} break;
|
||||
}
|
||||
|
||||
// apply relative bases
|
||||
U64 pointer = raw_pointer;
|
||||
if (pointer != 0) {
|
||||
switch (encoding & DW_EhPtrEnc_ModifyMask) {
|
||||
case DW_EhPtrEnc_PcRel:
|
||||
{
|
||||
pointer = ptr_ctx->raw_base_vaddr + frame_range.min + off + raw_pointer;
|
||||
} break;
|
||||
case DW_EhPtrEnc_TextRel:
|
||||
{
|
||||
pointer = ptr_ctx->text_vaddr + raw_pointer;
|
||||
} break;
|
||||
case DW_EhPtrEnc_DataRel:
|
||||
{
|
||||
pointer = ptr_ctx->data_vaddr + raw_pointer;
|
||||
} break;
|
||||
case DW_EhPtrEnc_FuncRel:
|
||||
{
|
||||
Assert(!"TODO: need a sample to verify implementation");
|
||||
pointer = ptr_ctx->func_vaddr + raw_pointer;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// return
|
||||
*ptr_out = pointer;
|
||||
U64 result = after_pointer_off - off;
|
||||
return(result);
|
||||
}
|
||||
|
||||
//- eh_frame parsing
|
||||
|
||||
internal void
|
||||
dw_unwind_parse_cie_x64(void *base, Rng1U64 range, DW_EhPtrCtx *ptr_ctx, U64 off, DW_CIEUnpacked *cie_out)
|
||||
dw_unwind_parse_cie_x64(void *base, Rng1U64 range, EH_PtrCtx *ptr_ctx, U64 off, DW_UnpackedCIE *cie_out)
|
||||
{
|
||||
NotImplemented;
|
||||
#if 0
|
||||
@@ -474,9 +384,9 @@ dw_unwind_parse_cie_x64(void *base, Rng1U64 range, DW_EhPtrCtx *ptr_ctx, U64 off
|
||||
U64 aug_data_off = after_aug_size_off;
|
||||
U64 after_aug_data_off = after_aug_size_off;
|
||||
|
||||
DW_EhPtrEnc lsda_encoding = DW_EhPtrEnc_Omit;
|
||||
EH_PtrEnc lsda_encoding = EH_PtrEnc_Omit;
|
||||
U64 handler_ip = 0;
|
||||
DW_EhPtrEnc addr_encoding = DW_EhPtrEnc_UData8;
|
||||
EH_PtrEnc addr_encoding = EH_PtrEnc_UData8;
|
||||
|
||||
if (has_augmentation_size > 0) {
|
||||
U64 aug_data_cursor = aug_data_off;
|
||||
@@ -487,7 +397,7 @@ dw_unwind_parse_cie_x64(void *base, Rng1U64 range, DW_EhPtrCtx *ptr_ctx, U64 off
|
||||
aug_data_cursor += sizeof(lsda_encoding);
|
||||
} break;
|
||||
case 'P': {
|
||||
DW_EhPtrEnc handler_encoding = DW_EhPtrEnc_Omit;
|
||||
EH_PtrEnc handler_encoding = EH_PtrEnc_Omit;
|
||||
dw_based_range_read_struct(base, range, aug_data_cursor, &handler_encoding);
|
||||
|
||||
U64 ptr_off = aug_data_cursor + sizeof(handler_encoding);
|
||||
@@ -516,6 +426,7 @@ dw_unwind_parse_cie_x64(void *base, Rng1U64 range, DW_EhPtrCtx *ptr_ctx, U64 off
|
||||
|
||||
// commit values to out
|
||||
cie_out->version = version;
|
||||
|
||||
cie_out->lsda_encoding = lsda_encoding;
|
||||
cie_out->addr_encoding = addr_encoding;
|
||||
cie_out->has_augmentation_size = has_augmentation_size;
|
||||
@@ -531,68 +442,15 @@ dw_unwind_parse_cie_x64(void *base, Rng1U64 range, DW_EhPtrCtx *ptr_ctx, U64 off
|
||||
#endif
|
||||
}
|
||||
|
||||
internal void
|
||||
dw_unwind_parse_fde_x64(void *base, Rng1U64 range, DW_EhPtrCtx *ptr_ctx, DW_CIEUnpacked *cie, U64 off, DW_FDEUnpacked *fde_out)
|
||||
{
|
||||
// pull out pointer encoding field
|
||||
DW_EhPtrEnc ptr_enc = cie->addr_encoding;
|
||||
|
||||
// ip first
|
||||
U64 ip_first_off = off;
|
||||
U64 ip_first = 0;
|
||||
U64 ip_first_size = dw_unwind_parse_pointer_x64(base, range, ptr_ctx, ptr_enc, ip_first_off, &ip_first);
|
||||
|
||||
// ip range size
|
||||
U64 ip_range_size_off = ip_first_off + ip_first_size;
|
||||
U64 ip_range_size = 0;
|
||||
U64 ip_range_size_size = dw_unwind_parse_pointer_x64(base, range, ptr_ctx, ptr_enc & DW_EhPtrEnc_TypeMask, ip_range_size_off, &ip_range_size);
|
||||
|
||||
// augmentation data
|
||||
U64 aug_data_off = ip_range_size_off + ip_range_size_size;
|
||||
U64 after_aug_data_off = aug_data_off;
|
||||
U64 lsda_ip = 0;
|
||||
|
||||
if (cie->has_augmentation_size) {
|
||||
// augmentation size
|
||||
U64 augmentation_size = 0;
|
||||
U64 aug_size_size = dw_based_range_read_uleb128(base, range, aug_data_off, &augmentation_size);
|
||||
U64 after_aug_size_off = aug_data_off + aug_size_size;
|
||||
|
||||
// extract lsda (only thing that can actually be in FDE's augmentation data as far as we know)
|
||||
DW_EhPtrEnc lsda_encoding = cie->lsda_encoding;
|
||||
if (lsda_encoding != DW_EhPtrEnc_Omit) {
|
||||
U64 lsda_off = after_aug_size_off;
|
||||
dw_unwind_parse_pointer_x64(base, range, ptr_ctx, lsda_encoding, lsda_off, &lsda_ip);
|
||||
}
|
||||
|
||||
// set offset at end of augmentation data
|
||||
after_aug_data_off = after_aug_size_off + augmentation_size;
|
||||
}
|
||||
|
||||
// cfi range
|
||||
U64 cfi_off = range.min + after_aug_data_off;
|
||||
U64 cfi_size = 0;
|
||||
if (range.max > cfi_off) {
|
||||
cfi_size = range.max - cfi_off;
|
||||
}
|
||||
|
||||
// commit values to out
|
||||
fde_out->ip_voff_range.min = ip_first;
|
||||
fde_out->ip_voff_range.max = ip_first + ip_range_size;
|
||||
fde_out->lsda_ip = lsda_ip;
|
||||
fde_out->cfi_range.min = cfi_off;
|
||||
fde_out->cfi_range.max = cfi_off + cfi_size;
|
||||
}
|
||||
|
||||
internal DW_CFIRecords
|
||||
dw_unwind_eh_frame_cfi_from_ip_slow_x64(String8 raw_eh_frame, DW_EhPtrCtx *ptr_ctx, U64 ip_voff)
|
||||
dw_unwind_eh_frame_cfi_from_ip_slow_x64(String8 raw_eh_frame, EH_PtrCtx *ptr_ctx, U64 ip_voff)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
DW_CFIRecords result = {0};
|
||||
|
||||
DW_CIEUnpackedNode *cie_first = 0;
|
||||
DW_CIEUnpackedNode *cie_last = 0;
|
||||
DW_UnpackedCIENode *cie_first = 0;
|
||||
DW_UnpackedCIENode *cie_last = 0;
|
||||
|
||||
U64 cursor = 0;
|
||||
for (;;) {
|
||||
@@ -632,10 +490,10 @@ dw_unwind_eh_frame_cfi_from_ip_slow_x64(String8 raw_eh_frame, DW_EhPtrCtx *ptr_c
|
||||
|
||||
// CIE
|
||||
if (discrim == 0) {
|
||||
DW_CIEUnpacked cie = {0};
|
||||
DW_UnpackedCIE cie = {0};
|
||||
dw_unwind_parse_cie_x64(raw_rec.str, rng_1u64(0, raw_rec.size), ptr_ctx, after_discrim_off, &cie);
|
||||
if (cie.version != 0) {
|
||||
DW_CIEUnpackedNode *node = push_array(scratch.arena, DW_CIEUnpackedNode, 1);
|
||||
DW_UnpackedCIENode *node = push_array(scratch.arena, DW_UnpackedCIENode, 1);
|
||||
node->cie = cie;
|
||||
node->offset = rec_off;
|
||||
SLLQueuePush(cie_first, cie_last, node);
|
||||
@@ -647,8 +505,8 @@ dw_unwind_eh_frame_cfi_from_ip_slow_x64(String8 raw_eh_frame, DW_EhPtrCtx *ptr_c
|
||||
U64 cie_offset = rec_range.min + discrim_off - discrim;
|
||||
|
||||
// get cie node
|
||||
DW_CIEUnpackedNode *cie_node = 0;
|
||||
for (DW_CIEUnpackedNode *node = cie_first; node != 0; node = node->next) {
|
||||
DW_UnpackedCIENode *cie_node = 0;
|
||||
for (DW_UnpackedCIENode *node = cie_first; node != 0; node = node->next) {
|
||||
if (node->offset == cie_offset) {
|
||||
cie_node = node;
|
||||
break;
|
||||
@@ -656,12 +514,13 @@ dw_unwind_eh_frame_cfi_from_ip_slow_x64(String8 raw_eh_frame, DW_EhPtrCtx *ptr_c
|
||||
}
|
||||
|
||||
// parse fde
|
||||
DW_FDEUnpacked fde = {0};
|
||||
DW_UnpackedFDE fde = {0};
|
||||
if (cie_node != 0) {
|
||||
dw_unwind_parse_fde_x64(raw_rec.str, rng_1u64(0,raw_rec.size), ptr_ctx, &cie_node->cie, after_discrim_off, &fde);
|
||||
NotImplemented;
|
||||
//dw_unwind_parse_fde_x64(raw_rec.str, rng_1u64(0,raw_rec.size), ptr_ctx, &cie_node->cie, after_discrim_off, &fde);
|
||||
}
|
||||
|
||||
if (contains_1u64(fde.ip_voff_range, ip_voff)) {
|
||||
if (contains_1u64(fde.pc_range, ip_voff)) {
|
||||
result.valid = 1;
|
||||
result.cie = cie_node->cie;
|
||||
result.fde = fde;
|
||||
@@ -679,7 +538,7 @@ dw_unwind_eh_frame_cfi_from_ip_slow_x64(String8 raw_eh_frame, DW_EhPtrCtx *ptr_c
|
||||
}
|
||||
|
||||
internal U64
|
||||
dw_search_eh_frame_hdr_linear_x64(String8 raw_eh_frame_hdr, DW_EhPtrCtx *ptr_ctx, U64 location)
|
||||
dw_search_eh_frame_hdr_linear_x64(String8 raw_eh_frame_hdr, EH_PtrCtx *ptr_ctx, U64 location)
|
||||
{
|
||||
// Table contains only addresses for first instruction in a function and we cannot
|
||||
// guarantee that result is FDE that corresponds to the input location.
|
||||
@@ -695,7 +554,7 @@ dw_search_eh_frame_hdr_linear_x64(String8 raw_eh_frame_hdr, DW_EhPtrCtx *ptr_ctx
|
||||
|
||||
if (version == 1) {
|
||||
#if 0
|
||||
DW_EhPtrCtx ptr_ctx = {0};
|
||||
EH_PtrCtx ptr_ctx = {0};
|
||||
// Set this to base address of .eh_frame_hdr. Entries are relative
|
||||
// to this section for some reason.
|
||||
ptr_ctx.data_vaddr = range.min;
|
||||
@@ -704,7 +563,7 @@ dw_search_eh_frame_hdr_linear_x64(String8 raw_eh_frame_hdr, DW_EhPtrCtx *ptr_ctx
|
||||
ptr_ctx.text_vaddr = 0;
|
||||
#endif
|
||||
|
||||
DW_EhPtrEnc eh_frame_ptr_enc = 0, fde_count_enc = 0, table_enc = 0;
|
||||
EH_PtrEnc eh_frame_ptr_enc = 0, fde_count_enc = 0, table_enc = 0;
|
||||
cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &eh_frame_ptr_enc);
|
||||
cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &fde_count_enc);
|
||||
cursor += str8_deserial_read_struct(raw_eh_frame_hdr, cursor, &table_enc);
|
||||
@@ -733,7 +592,7 @@ dw_search_eh_frame_hdr_linear_x64(String8 raw_eh_frame_hdr, DW_EhPtrCtx *ptr_ctx
|
||||
}
|
||||
|
||||
internal DW_CFIRecords
|
||||
dw_unwind_eh_frame_hdr_from_ip_fast_x64(String8 raw_eh_frame, String8 raw_eh_frame_hdr, DW_EhPtrCtx *ptr_ctx, U64 ip_voff)
|
||||
dw_unwind_eh_frame_hdr_from_ip_fast_x64(String8 raw_eh_frame, String8 raw_eh_frame_hdr, EH_PtrCtx *ptr_ctx, U64 ip_voff)
|
||||
{
|
||||
DW_CFIRecords result = {0};
|
||||
|
||||
@@ -771,15 +630,16 @@ dw_unwind_eh_frame_hdr_from_ip_fast_x64(String8 raw_eh_frame, String8 raw_eh_fra
|
||||
Rng1U64 fde_range = rng_1u64(0, fde_read_offset + (fde_size - sizeof(fde_discrim)));
|
||||
|
||||
// parse CIE
|
||||
DW_CIEUnpacked cie = {0};
|
||||
DW_UnpackedCIE cie = {0};
|
||||
dw_unwind_parse_cie_x64(raw_eh_frame.str, cie_range, ptr_ctx, cie_read_offset, &cie);
|
||||
|
||||
// parse FDE
|
||||
DW_FDEUnpacked fde = {0};
|
||||
dw_unwind_parse_fde_x64(raw_eh_frame.str, fde_range, ptr_ctx, &cie, fde_read_offset, &fde);
|
||||
DW_UnpackedFDE fde = {0};
|
||||
NotImplemented;
|
||||
//dw_unwind_parse_fde_x64(raw_eh_frame.str, fde_range, ptr_ctx, &cie, fde_read_offset, &fde);
|
||||
|
||||
// range check instruction pointer
|
||||
if (contains_1u64(fde.ip_voff_range, ip_voff)) {
|
||||
if (contains_1u64(fde.pc_range, ip_voff)) {
|
||||
result.valid = 1;
|
||||
result.cie = cie;
|
||||
result.fde = fde;
|
||||
@@ -793,7 +653,7 @@ dw_unwind_eh_frame_hdr_from_ip_fast_x64(String8 raw_eh_frame, String8 raw_eh_fra
|
||||
//- cfi machine
|
||||
|
||||
internal DW_CFIMachine
|
||||
dw_unwind_make_machine_x64(U64 cells_per_row, DW_CIEUnpacked *cie, DW_EhPtrCtx *ptr_ctx)
|
||||
dw_unwind_make_machine_x64(U64 cells_per_row, DW_UnpackedCIE *cie, EH_PtrCtx *ptr_ctx)
|
||||
{
|
||||
DW_CFIMachine result = {0};
|
||||
result.cells_per_row = cells_per_row;
|
||||
@@ -843,8 +703,8 @@ dw_unwind_machine_run_to_ip_x64(void *base, Rng1U64 range, DW_CFIMachine *machin
|
||||
B32 result = 0;
|
||||
|
||||
// pull out machine's equipment
|
||||
DW_CIEUnpacked *cie = machine->cie;
|
||||
DW_EhPtrCtx *ptr_ctx = machine->ptr_ctx;
|
||||
DW_UnpackedCIE *cie = machine->cie;
|
||||
EH_PtrCtx *ptr_ctx = machine->ptr_ctx;
|
||||
U64 cells_per_row = machine->cells_per_row;
|
||||
DW_CFIRow *initial_row = machine->initial_row;
|
||||
|
||||
@@ -910,7 +770,9 @@ dw_unwind_machine_run_to_ip_x64(void *base, Rng1U64 range, DW_CFIMachine *machin
|
||||
}
|
||||
} break;
|
||||
case DW_CFADecode_Address: {
|
||||
o_size = dw_unwind_parse_pointer_x64(base, range, ptr_ctx, cie->addr_encoding, decode_cursor, out);
|
||||
// TODO:
|
||||
NotImplemented;
|
||||
//o_size = dw_unwind_parse_pointer_x64(base, range, ptr_ctx, cie->addr_encoding, decode_cursor, out);
|
||||
} break;
|
||||
case DW_CFADecode_ULEB128: {
|
||||
o_size = dw_based_range_read_uleb128(base, range, decode_cursor, out);
|
||||
@@ -1163,3 +1025,46 @@ dw_unwind_machine_run_to_ip_x64(void *base, Rng1U64 range, DW_CFIMachine *machin
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
internal String8
|
||||
dw_string_from_eh_ptr_enc_type(EH_PtrEnc type)
|
||||
{
|
||||
switch (type) {
|
||||
case EH_PtrEnc_Ptr: return str8_lit("Ptr");
|
||||
case EH_PtrEnc_ULEB128: return str8_lit("ULEB128");
|
||||
case EH_PtrEnc_UData2: return str8_lit("UData2");
|
||||
case EH_PtrEnc_UData4: return str8_lit("UData4");
|
||||
case EH_PtrEnc_UData8: return str8_lit("UData8");
|
||||
case EH_PtrEnc_Signed: return str8_lit("Signed");
|
||||
case EH_PtrEnc_SLEB128: return str8_lit("SLEB128");
|
||||
case EH_PtrEnc_SData2: return str8_lit("SData2");
|
||||
case EH_PtrEnc_SData4: return str8_lit("SData4");
|
||||
case EH_PtrEnc_SData8: return str8_lit("SData8");
|
||||
}
|
||||
return str8_zero();
|
||||
}
|
||||
|
||||
internal String8
|
||||
dw_string_from_eh_ptr_enc_modifier(EH_PtrEnc modifier)
|
||||
{
|
||||
switch (modifier) {
|
||||
case EH_PtrEnc_PcRel: return str8_lit("PcRel");
|
||||
case EH_PtrEnc_TextRel: return str8_lit("TextRel");
|
||||
case EH_PtrEnc_DataRel: return str8_lit("DataRel");
|
||||
case EH_PtrEnc_FuncRel: return str8_lit("FuncRel");
|
||||
case EH_PtrEnc_Aligned: return str8_lit("Aligned");
|
||||
}
|
||||
return str8_zero();
|
||||
}
|
||||
|
||||
internal String8
|
||||
dw_string_from_eh_ptr_enc(Arena *arena, EH_PtrEnc enc)
|
||||
{
|
||||
String8 type_str = dw_string_from_eh_ptr_enc_type(enc & EH_PtrEnc_TypeMask);
|
||||
String8 modifer_str = dw_string_from_eh_ptr_enc_modifier(enc & EH_PtrEnc_ModifierMask);
|
||||
String8 indir_str = enc & EH_PtrEnc_Indirect ? str8_lit("Indirect") : str8_zero();
|
||||
String8 result = str8f(arena, "Type: %S, Modifier %S (%S)", type_str, modifer_str, indir_str);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+11
-82
@@ -14,87 +14,19 @@ typedef struct DW_UnwindResult
|
||||
|
||||
// EH: Exception Frames
|
||||
|
||||
typedef U8 DW_EhPtrEnc;
|
||||
enum
|
||||
typedef struct DW_UnpackedCIENode
|
||||
{
|
||||
DW_EhPtrEnc_TypeMask = 0x0F,
|
||||
DW_EhPtrEnc_Ptr = 0x00, // Pointer sized unsigned value
|
||||
DW_EhPtrEnc_ULEB128 = 0x01, // Unsigned LE base-128 value
|
||||
DW_EhPtrEnc_UData2 = 0x02, // Unsigned 16-bit value
|
||||
DW_EhPtrEnc_UData4 = 0x03, // Unsigned 32-bit value
|
||||
DW_EhPtrEnc_UData8 = 0x04, // Unsigned 64-bit value
|
||||
DW_EhPtrEnc_Signed = 0x08, // Signed pointer
|
||||
DW_EhPtrEnc_SLEB128 = 0x09, // Signed LE base-128 value
|
||||
DW_EhPtrEnc_SData2 = 0x0A, // Signed 16-bit value
|
||||
DW_EhPtrEnc_SData4 = 0x0B, // Signed 32-bit value
|
||||
DW_EhPtrEnc_SData8 = 0x0C, // Signed 64-bit value
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DW_EhPtrEnc_ModifyMask = 0x70,
|
||||
DW_EhPtrEnc_PcRel = 0x10, // Value is relative to the current program counter.
|
||||
DW_EhPtrEnc_TextRel = 0x20, // Value is relative to the .text section.
|
||||
DW_EhPtrEnc_DataRel = 0x30, // Value is relative to the .got or .eh_frame_hdr section.
|
||||
DW_EhPtrEnc_FuncRel = 0x40, // Value is relative to the function.
|
||||
DW_EhPtrEnc_Aligned = 0x50, // Value is aligned to an address unit sized boundary.
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DW_EhPtrEnc_Indirect = 0x80, // This flag indicates that value is stored in virtual memory.
|
||||
DW_EhPtrEnc_Omit = 0xFF,
|
||||
};
|
||||
|
||||
typedef struct DW_EhPtrCtx
|
||||
{
|
||||
U64 raw_base_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
|
||||
} DW_EhPtrCtx;
|
||||
|
||||
// CIE: Common Information Entry
|
||||
typedef struct DW_CIEUnpacked
|
||||
{
|
||||
U8 version;
|
||||
DW_EhPtrEnc lsda_encoding;
|
||||
DW_EhPtrEnc addr_encoding;
|
||||
|
||||
B32 has_augmentation_size;
|
||||
U64 augmentation_size;
|
||||
String8 augmentation;
|
||||
|
||||
U64 code_align_factor;
|
||||
S64 data_align_factor;
|
||||
U64 ret_addr_reg;
|
||||
|
||||
U64 handler_ip;
|
||||
|
||||
Rng1U64 cfi_range;
|
||||
} DW_CIEUnpacked;
|
||||
|
||||
typedef struct DW_CIEUnpackedNode
|
||||
{
|
||||
struct DW_CIEUnpackedNode *next;
|
||||
DW_CIEUnpacked cie;
|
||||
struct DW_UnpackedCIENode *next;
|
||||
DW_UnpackedCIE cie;
|
||||
U64 offset;
|
||||
} DW_CIEUnpackedNode;
|
||||
|
||||
// FDE: Frame Description Entry
|
||||
typedef struct DW_FDEUnpacked
|
||||
{
|
||||
Rng1U64 ip_voff_range;
|
||||
U64 lsda_ip;
|
||||
Rng1U64 cfi_range;
|
||||
} DW_FDEUnpacked;
|
||||
} DW_UnpackedCIENode;
|
||||
|
||||
// CFI: Call Frame Information
|
||||
typedef struct DW_CFIRecords
|
||||
{
|
||||
B32 valid;
|
||||
DW_CIEUnpacked cie;
|
||||
DW_FDEUnpacked fde;
|
||||
DW_UnpackedCIE cie;
|
||||
DW_UnpackedFDE fde;
|
||||
} DW_CFIRecords;
|
||||
|
||||
typedef enum DW_CFICFARule{
|
||||
@@ -144,10 +76,10 @@ typedef struct DW_CFIRow
|
||||
typedef struct DW_CFIMachine
|
||||
{
|
||||
U64 cells_per_row;
|
||||
DW_CIEUnpacked *cie;
|
||||
DW_EhPtrCtx *ptr_ctx;
|
||||
DW_UnpackedCIE *cie;
|
||||
DW_CFIRow *initial_row;
|
||||
U64 fde_ip;
|
||||
EH_PtrCtx *ptr_ctx;
|
||||
} DW_CFIMachine;
|
||||
|
||||
typedef U8 DW_CFADecode;
|
||||
@@ -200,17 +132,14 @@ internal DW_UnwindResult dw_unwind_x64__apply_frame_rules(String8 raw_eh_frame,
|
||||
// x64 Unwind Helper Functions
|
||||
|
||||
internal void dw_unwind_init_x64(void);
|
||||
internal U64 dw_unwind_parse_pointer_x64(void *base, Rng1U64 range, DW_EhPtrCtx *ptr_ctx, DW_EhPtrEnc ptr_enc, U64 off, U64 *ptr_out);
|
||||
|
||||
//- eh_frame parsing
|
||||
internal void dw_unwind_parse_cie_x64(void *base,Rng1U64 range,DW_EhPtrCtx *ptr_ctx, U64 off, DW_CIEUnpacked *cie_out);
|
||||
internal void dw_unwind_parse_fde_x64(void *base,Rng1U64 range,DW_EhPtrCtx *ptr_ctx, DW_CIEUnpacked *parent_cie, U64 off, DW_FDEUnpacked *fde_out);
|
||||
internal DW_CFIRecords dw_unwind_eh_frame_cfi_from_ip_slow_x64(String8 raw_eh_frame, DW_EhPtrCtx *ptr_ctx, U64 ip_voff);
|
||||
internal DW_CFIRecords dw_unwind_eh_frame_hdr_from_ip_fast_x64(String8 raw_eh_frame, String8 raw_eh_frame_hdr, DW_EhPtrCtx *ptr_ctx, U64 ip_voff);
|
||||
internal DW_CFIRecords dw_unwind_eh_frame_cfi_from_ip_slow_x64(String8 raw_eh_frame, EH_PtrCtx *ptr_ctx, U64 ip_voff);
|
||||
internal DW_CFIRecords dw_unwind_eh_frame_hdr_from_ip_fast_x64(String8 raw_eh_frame, String8 raw_eh_frame_hdr, EH_PtrCtx *ptr_ctx, U64 ip_voff);
|
||||
|
||||
//- cfi machine
|
||||
|
||||
internal DW_CFIMachine dw_unwind_make_machine_x64(U64 cells_per_row, DW_CIEUnpacked *cie, DW_EhPtrCtx *ptr_ctx);
|
||||
internal DW_CFIMachine dw_unwind_make_machine_x64(U64 cells_per_row, DW_UnpackedCIE *cie, EH_PtrCtx *ptr_ctx);
|
||||
internal void dw_unwind_machine_equip_initial_row_x64(DW_CFIMachine *machine, DW_CFIRow *initial_row);
|
||||
internal void dw_unwind_machine_equip_fde_ip_x64(DW_CFIMachine *machine, U64 fde_ip);
|
||||
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
internal U64
|
||||
eh_read_ptr(String8 frame_base, U64 off, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_out)
|
||||
{
|
||||
U64 ptr_off = off;
|
||||
|
||||
// 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 EH_PtrEnc_PcRel: { ptr = ptr_ctx->raw_base_vaddr + off + 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;
|
||||
}
|
||||
|
||||
if (ptr_out) {
|
||||
*ptr_out = raw_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return decode_size;
|
||||
}
|
||||
|
||||
internal U64
|
||||
eh_parse_aug_data(String8 aug_string, String8 aug_data, 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;
|
||||
|
||||
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)) {
|
||||
U64 aug_data_off = cursor;
|
||||
U64 aug_data_size = 0;
|
||||
cursor += str8_deserial_read_uleb128(aug_string, cursor, &aug_data_size);
|
||||
cursor += aug_data_size;
|
||||
|
||||
String8 aug_data = str8_substr(aug_data, rng_1u64(aug_data_off, aug_data_off + aug_data_size));
|
||||
U64 aug_cursor = 0;
|
||||
for (U8 *ptr = aug_string.str; ptr < (aug_string.str+aug_string.size); ptr += 1) {
|
||||
switch (*ptr) {
|
||||
case 'L': {
|
||||
aug_cursor += str8_deserial_read_struct(aug_data, aug_cursor, &lsda_encoding);
|
||||
aug_flags |= EH_AugFlag_HasLSDA;
|
||||
} break;
|
||||
case 'P': {
|
||||
aug_cursor += str8_deserial_read_struct(aug_data, aug_cursor, &handler_encoding);
|
||||
aug_cursor += str8_deserial_read_dwarf_ptr(aug_data, aug_cursor, ptr_ctx, handler_encoding, &handler_ip);
|
||||
aug_flags |= EH_AugFlag_HasHandler;
|
||||
} break;
|
||||
case 'R': {
|
||||
aug_cursor += str8_deserial_read_struct(aug_data, aug_cursor, &addr_encoding);
|
||||
aug_flags |= EH_AugFlag_HasAddrEnc;
|
||||
} 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;
|
||||
}
|
||||
|
||||
exit:;
|
||||
U64 parse_size = cursor;
|
||||
return parse_size;
|
||||
}
|
||||
|
||||
internal U64
|
||||
eh_size_from_aug_data(String8 aug_string, String8 data, EH_PtrCtx *ptr_ctx)
|
||||
{
|
||||
return eh_parse_aug_data(aug_string, data, ptr_ctx, 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef EH_FRAME_H
|
||||
#define EH_FRAME_H
|
||||
|
||||
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 raw_base_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),
|
||||
};
|
||||
|
||||
typedef struct EH_Augmentation
|
||||
{
|
||||
EH_AugFlags flags;
|
||||
U64 handler_ip;
|
||||
EH_PtrEnc handler_encoding;
|
||||
EH_PtrEnc lsda_encoding;
|
||||
EH_PtrEnc addr_encoding;
|
||||
} EH_Augmentation;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
internal U64 eh_read_ptr(String8 frame_base, U64 off, EH_PtrCtx *ptr_ctx, EH_PtrEnc encoding, U64 *ptr_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);
|
||||
|
||||
#endif // EH_FRAME_H
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "elf/elf_parse.h"
|
||||
#include "codeview/codeview.h"
|
||||
#include "codeview/codeview_parse.h"
|
||||
#include "eh/eh_frame.h"
|
||||
#include "dwarf/dwarf_inc.h"
|
||||
#include "msf/msf.h"
|
||||
#include "msf/msf_parse.h"
|
||||
@@ -46,6 +47,7 @@
|
||||
#include "elf/elf_parse.c"
|
||||
#include "codeview/codeview.c"
|
||||
#include "codeview/codeview_parse.c"
|
||||
#include "eh/eh_frame.c"
|
||||
#include "dwarf/dwarf_inc.c"
|
||||
#include "msf/msf.c"
|
||||
#include "msf/msf_parse.c"
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
#include "pdb/pdb.h"
|
||||
#include "pdb/pdb_parse.h"
|
||||
#include "pdb/pdb_stringize.h"
|
||||
#include "eh/eh_frame.h"
|
||||
#include "dwarf/dwarf_inc.h"
|
||||
#include "rdi_from_coff/rdi_from_coff.h"
|
||||
#include "rdi_from_elf/rdi_from_elf.h"
|
||||
@@ -285,6 +286,7 @@
|
||||
#include "pdb/pdb.c"
|
||||
#include "pdb/pdb_parse.c"
|
||||
#include "pdb/pdb_stringize.c"
|
||||
#include "eh/eh_frame.c"
|
||||
#include "dwarf/dwarf_inc.c"
|
||||
#include "rdi_from_coff/rdi_from_coff.c"
|
||||
#include "rdi_from_elf/rdi_from_elf.c"
|
||||
|
||||
Reference in New Issue
Block a user