Files
raddebugger/src/dwarf/dwarf_unwind.h
T
Nikita Smith b27b783e6f Dwarf parser WIP
Copied the parser from internal repo and integrated with the base layer
(not tested)

Parser uses based/range pattern for which we have an alternative in
string layer (str8_deserial_*)
2025-01-08 03:26:04 -08:00

225 lines
6.7 KiB
C

// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DWARF_UNWIND_H
#define DWARF_UNWIND_H
typedef struct DW_UnwindResult
{
B32 is_invalid;
B32 missed_read;
U64 missed_read_addr;
U64 stack_pointer;
} DW_UnwindResult;
// EH: Exception Frames
typedef U8 DW_EhPtrEnc;
enum
{
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;
U64 offset;
} DW_CIEUnpackedNode;
// FDE: Frame Description Entry
typedef struct DW_FDEUnpacked
{
Rng1U64 ip_voff_range;
U64 lsda_ip;
Rng1U64 cfi_range;
} DW_FDEUnpacked;
// CFI: Call Frame Information
typedef struct DW_CFIRecords
{
B32 valid;
DW_CIEUnpacked cie;
DW_FDEUnpacked fde;
} DW_CFIRecords;
typedef enum DW_CFICFARule{
DW_CFI_CFA_Rule_RegOff,
DW_CFI_CFA_Rule_Expr,
} DW_CFICFARule;
typedef struct DW_CFICFACell
{
DW_CFICFARule rule;
union {
struct {
U64 reg_idx;
S64 offset;
};
Rng1U64 expr;
};
} DW_CFICFACell;
typedef enum DW_CFIRegisterRule
{
DW_CFIRegisterRule_SameValue,
DW_CFIRegisterRule_Undefined,
DW_CFIRegisterRule_Offset,
DW_CFIRegisterRule_ValOffset,
DW_CFIRegisterRule_Register,
DW_CFIRegisterRule_Expression,
DW_CFIRegisterRule_ValExpression,
} DW_CFIRegisterRule;
typedef struct DW_CFICell
{
DW_CFIRegisterRule rule;
union {
S64 n;
Rng1U64 expr;
};
} DW_CFICell;
typedef struct DW_CFIRow
{
struct DW_CFIRow *next;
DW_CFICell *cells;
DW_CFICFACell cfa_cell;
} DW_CFIRow;
typedef struct DW_CFIMachine
{
U64 cells_per_row;
DW_CIEUnpacked *cie;
DW_EhPtrCtx *ptr_ctx;
DW_CFIRow *initial_row;
U64 fde_ip;
} DW_CFIMachine;
typedef U8 DW_CFADecode;
enum
{
DW_CFADecode_Nop = 0x0,
// 1,2,4,8 reserved for literal byte sizes
DW_CFADecode_Address = 0x9,
DW_CFADecode_ULEB128 = 0xA,
DW_CFADecode_SLEB128 = 0xB,
};
typedef U16 DW_CFAControlBits;
enum
{
DW_CFAControlBits_Dec1Mask = 0x00F,
DW_CFAControlBits_Dec2Mask = 0x0F0,
DW_CFAControlBits_IsReg0 = 0x100,
DW_CFAControlBits_IsReg1 = 0x200,
DW_CFAControlBits_IsReg2 = 0x400,
DW_CFAControlBits_NewRow = 0x800,
};
global read_only DW_CFAControlBits dw_unwind__cfa_control_bits_kind1[DW_CFA_OplKind1 + 1];
global read_only DW_CFAControlBits dw_unwind__cfa_control_bits_kind2[DW_CFA_OplKind2 + 1];
// register codes for unwinding match the DW_RegX64 register codes
#define DW_UNWIND_X64__REG_SLOT_COUNT 17
////////////////////////////////
// x64 Unwind Function
internal DW_UnwindResult
dw_unwind_x64(String8 raw_text,
String8 raw_eh_frame,
String8 raw_eh_frame_header,
Rng1U64 text_vrange,
Rng1U64 eh_frame_vrange,
Rng1U64 eh_frame_header_vrange,
U64 default_image_base,
U64 image_base,
U64 stack_pointer,
DW_RegsX64 *regs,
DW_ReadMemorySig *read_memory,
void *read_memory_ud);
internal DW_UnwindResult dw_unwind_x64__apply_frame_rules(String8 raw_eh_frame, DW_CFIRow *row, U64 text_base_vaddr, DW_ReadMemorySig *read_memory, void *read_memory_ud, U64 stack_pointer, DW_RegsX64 *regs);
////////////////////////////////
// x64 Unwind Helper Functions
internal void dw_unwind_init_x64(void);
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);
//- cfi machine
internal DW_CFIMachine dw_unwind_make_machine_x64(U64 cells_per_row, DW_CIEUnpacked *cie, DW_EhPtrCtx *ptr_ctx);
internal void dw_unwind_machine_equip_initial_row_x64(DW_CFIMachine *machine, DW_CFIRow *initial_row);
internal void dw_unwind_machine_equip_fde_ip_x64(DW_CFIMachine *machine, U64 fde_ip);
internal DW_CFIRow* dw_unwind_row_alloc_x64(Arena *arena, U64 cells_per_row);
internal void dw_unwind_row_zero_x64(DW_CFIRow *row, U64 cells_per_row);
internal void dw_unwind_row_copy_x64(DW_CFIRow *dst, DW_CFIRow *src, U64 cells_per_row);
internal B32 dw_unwind_machine_run_to_ip_x64(void *base, Rng1U64 range, DW_CFIMachine *machine, U64 target_ip, DW_CFIRow *row_out);
#endif // DWARF_UNWIND_H