formalize codebase's library exports; document in README

This commit is contained in:
Ryan Fleury
2024-02-13 09:27:52 -08:00
parent 8c70b5efc9
commit 017116aee9
14 changed files with 44 additions and 36 deletions
+97
View File
@@ -0,0 +1,97 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
// Functions
RADDBGI_PROC RADDBGI_U64
raddbgi_hash(RADDBGI_U8 *ptr, RADDBGI_U64 size){
RADDBGI_U64 result = 5381;
RADDBGI_U8 *opl = ptr + size;
for (; ptr < opl; ptr += 1){
result = ((result << 5) + result) + *ptr;
}
return(result);
}
RADDBGI_PROC RADDBGI_U32
raddbgi_size_from_basic_type_kind(RADDBGI_TypeKind kind){
RADDBGI_U32 result = 0;
switch (kind){
#define X(N,C)
#define XZ(N,C,Z) case C: result = Z; break;
#define Y(A,N)
RADDBGI_TypeKindXList(X,XZ,Y)
#undef X
#undef XZ
#undef Y
}
return(result);
}
RADDBGI_PROC RADDBGI_U32
raddbgi_addr_size_from_arch(RADDBGI_Arch arch){
RADDBGI_U32 result = 0;
switch (arch){
#define X(N,C,Z) case C: result = Z; break;
RADDBGI_ArchXList(X)
#undef X
}
return(result);
}
//- eval helpers
RADDBGI_PROC RADDBGI_EvalConversionKind
raddbgi_eval_conversion_rule(RADDBGI_EvalTypeGroup in, RADDBGI_EvalTypeGroup out){
RADDBGI_EvalConversionKind result = 0;
switch (in + (out << 8)){
#define Y(i,o) case ((RADDBGI_EvalTypeGroup_##i) + ((RADDBGI_EvalTypeGroup_##o) << 8)):
#define Xb(c)
#define Xe(c) result = RADDBGI_EvalConversionKind_##c; break;
RADDBGI_EvalConversionKindFromTypeGroupPairMap(Y,Xb,Xe)
#undef Xe
#undef Xb
#undef Y
}
return(result);
}
RADDBGI_PROC RADDBGI_U8*
raddbgi_eval_conversion_message(RADDBGI_EvalConversionKind conversion_kind, RADDBGI_U64 *lenout){
RADDBGI_U8 *result = 0;
switch (conversion_kind){
#define X(N,msg) \
case RADDBGI_EvalConversionKind_##N: result = (RADDBGI_U8*)msg; *lenout = sizeof(msg) - 1; break;
RADDBGI_EvalConversionKindXList(X)
#undef X
}
return(result);
}
RADDBGI_PROC RADDBGI_S32
raddbgi_eval_opcode_type_compatible(RADDBGI_EvalOp op, RADDBGI_EvalTypeGroup group){
RADDBGI_S32 result = 0;
switch (op){
case RADDBGI_EvalOp_Neg: case RADDBGI_EvalOp_Add: case RADDBGI_EvalOp_Sub:
case RADDBGI_EvalOp_Mul: case RADDBGI_EvalOp_Div:
case RADDBGI_EvalOp_EqEq:case RADDBGI_EvalOp_NtEq:
case RADDBGI_EvalOp_LsEq:case RADDBGI_EvalOp_GrEq:
case RADDBGI_EvalOp_Less:case RADDBGI_EvalOp_Grtr:
{
if (group != RADDBGI_EvalTypeGroup_Other){
result = 1;
}
}break;
case RADDBGI_EvalOp_Mod:case RADDBGI_EvalOp_LShift:case RADDBGI_EvalOp_RShift:
case RADDBGI_EvalOp_BitNot:case RADDBGI_EvalOp_BitAnd:case RADDBGI_EvalOp_BitXor:
case RADDBGI_EvalOp_BitOr:case RADDBGI_EvalOp_LogNot:case RADDBGI_EvalOp_LogAnd:
case RADDBGI_EvalOp_LogOr:
{
if (group == RADDBGI_EvalTypeGroup_S || group == RADDBGI_EvalTypeGroup_U){
result = 1;
}
}break;
}
return(result);
}
+919
View File
@@ -0,0 +1,919 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBGI_FORMAT_H
#define RADDBGI_FORMAT_H
////////////////////////////////////////////////////////////////
// Overridable procedure decoration
#if !defined(RADDBGI_PROC)
# define RADDBGI_PROC static
#endif
////////////////////////////////////////////////////////////////
// Overridable integer types
#if !defined(RADDBGI_U8)
# define RADDBGI_U8 RADDBGI_U8
# define RADDBGI_U16 RADDBGI_U16
# define RADDBGI_U32 RADDBGI_U32
# define RADDBGI_U64 RADDBGI_U64
# define RADDBGI_S8 RADDBGI_S8
# define RADDBGI_S16 RADDBGI_S16
# define RADDBGI_S32 RADDBGI_S32
# define RADDBGI_S64 RADDBGI_S64
#include <stdint.h>
typedef uint8_t RADDBGI_U8;
typedef uint16_t RADDBGI_U16;
typedef uint32_t RADDBGI_U32;
typedef uint64_t RADDBGI_U64;
typedef int8_t RADDBGI_S8;
typedef int16_t RADDBGI_S16;
typedef int32_t RADDBGI_S32;
typedef int64_t RADDBGI_S64;
#endif
////////////////////////////////////////////////////////////////
// Architecture Constants
#define RADDBGI_ArchXList(X)\
X(NULL, 0, 0)\
X(X86, 1, 4)\
X(X64, 2, 8)
typedef RADDBGI_U32 RADDBGI_Arch;
typedef enum RADDBGI_ArchEnum{
#define X(N,C,Z) RADDBGI_Arch_##N = C,
RADDBGI_ArchXList(X)
#undef X
} RADDBGI_ArchEnum;
typedef RADDBGI_U8 RADDBGI_RegisterCode;
// x86 registers
#define RADDBGI_RegisterCode_X86_XList(X) \
X(nil, 0) \
X(eax, 1) \
X(ecx, 2) \
X(edx, 3) \
X(ebx, 4) \
X(esp, 5) \
X(ebp, 6) \
X(esi, 7) \
X(edi, 8) \
X(fsbase, 9) \
X(gsbase, 10) \
X(eflags, 11) \
X(eip, 12) \
X(dr0, 13) \
X(dr1, 14) \
X(dr2, 15) \
X(dr3, 16) \
X(dr4, 17) \
X(dr5, 18) \
X(dr6, 19) \
X(dr7, 20) \
X(fpr0, 21) \
X(fpr1, 22) \
X(fpr2, 23) \
X(fpr3, 24) \
X(fpr4, 25) \
X(fpr5, 26) \
X(fpr6, 27) \
X(fpr7, 28) \
X(st0, 29) \
X(st1, 30) \
X(st2, 31) \
X(st3, 32) \
X(st4, 33) \
X(st5, 34) \
X(st6, 35) \
X(st7, 36) \
X(fcw, 37) \
X(fsw, 38) \
X(ftw, 39) \
X(fop, 40) \
X(fcs, 41) \
X(fds, 42) \
X(fip, 43) \
X(fdp, 44) \
X(mxcsr, 45) \
X(mxcsr_mask, 46) \
X(ss, 47) \
X(cs, 48) \
X(ds, 49) \
X(es, 50) \
X(fs, 51) \
X(gs, 52) \
X(ymm0, 53) \
X(ymm1, 54) \
X(ymm2, 55) \
X(ymm3, 56) \
X(ymm4, 57) \
X(ymm5, 58) \
X(ymm6, 59) \
X(ymm7, 60) \
X(COUNT, 61)
typedef enum RADDBGI_RegisterCode_X86_Enum{
#define X(N,C) RADDBGI_RegisterCode_X86_##N = C,
RADDBGI_RegisterCode_X86_XList(X)
#undef X
} RADDBGI_RegisterCode_X86_Enum;
// x64 registers
#define RADDBGI_RegisterCode_X64_XList(X) \
X(nil, 0) \
X(rax, 1) \
X(rcx, 2) \
X(rdx, 3) \
X(rbx, 4) \
X(rsp, 5) \
X(rbp, 6) \
X(rsi, 7) \
X(rdi, 8) \
X(r8, 9) \
X(r9, 10) \
X(r10, 11) \
X(r11, 12) \
X(r12, 13) \
X(r13, 14) \
X(r14, 15) \
X(r15, 16) \
X(es, 17) \
X(cs, 18) \
X(ss, 19) \
X(ds, 20) \
X(fs, 21) \
X(gs, 22) \
X(rip, 23) \
X(rflags, 24) \
X(dr0, 25) \
X(dr1, 26) \
X(dr2, 27) \
X(dr3, 28) \
X(dr4, 29) \
X(dr5, 30) \
X(dr6, 31) \
X(dr7, 32) \
X(st0, 33) \
X(st1, 34) \
X(st2, 35) \
X(st3, 36) \
X(st4, 37) \
X(st5, 38) \
X(st6, 39) \
X(st7, 40) \
X(fpr0, 41) \
X(fpr1, 42) \
X(fpr2, 43) \
X(fpr3, 44) \
X(fpr4, 45) \
X(fpr5, 46) \
X(fpr6, 47) \
X(fpr7, 48) \
X(ymm0, 49) \
X(ymm1, 50) \
X(ymm2, 51) \
X(ymm3, 52) \
X(ymm4, 53) \
X(ymm5, 54) \
X(ymm6, 55) \
X(ymm7, 56) \
X(ymm8, 57) \
X(ymm9, 58) \
X(ymm10, 59) \
X(ymm11, 60) \
X(ymm12, 61) \
X(ymm13, 62) \
X(ymm14, 63) \
X(ymm15, 64) \
X(mxcsr, 65) \
X(fsbase, 66) \
X(gsbase, 67) \
X(fcw, 68) \
X(fsw, 69) \
X(ftw, 70) \
X(fop, 71) \
X(fcs, 72) \
X(fds, 73) \
X(fip, 74) \
X(fdp, 75) \
X(mxcsr_mask, 76) \
X(COUNT, 77)
typedef enum RADDBGI_RegisterCode_X64_Enum{
#define X(N,C) RADDBGI_RegisterCode_X64_##N = C,
RADDBGI_RegisterCode_X64_XList(X)
#undef X
} RADDBGI_RegisterCode_X64_Enum;
////////////////////////////////////////////////////////////////
// Format types
// "raddbg\0\0"
#define RADDBGI_MAGIC_CONSTANT 0x0000676264646172
#define RADDBGI_ENCODING_VERSION 1
#define RADDBGI_LanguageXList(X) \
X(NULL, 0) \
X(C, 1) \
X(CPlusPlus, 2)
typedef RADDBGI_U32 RADDBGI_Language;
typedef enum RADDBGI_LanguageEnum{
#define X(N,C) RADDBGI_Language_##N = C,
RADDBGI_LanguageXList(X)
#undef X
} RADDBGI_LanguageEnum;
typedef struct RADDBGI_Header{
// identification
RADDBGI_U64 magic;
RADDBGI_U32 encoding_version;
// data sections
RADDBGI_U32 data_section_off;
RADDBGI_U32 data_section_count;
} RADDBGI_Header;
//- data sections
#define RADDBGI_DataSectionTag_SECONDARY 0x80000000
#define RADDBGI_DataSectionTagXList(X,Y) \
X(NULL, 0x0000)\
X(TopLevelInfo, 0x0001)\
X(StringData, 0x0002)\
X(StringTable, 0x0003)\
X(IndexRuns, 0x0004)\
X(BinarySections, 0x0005)\
X(FilePathNodes, 0x0006)\
X(SourceFiles, 0x0007)\
X(Units, 0x0008)\
X(UnitVmap, 0x0009)\
X(TypeNodes, 0x000A)\
X(UDTs, 0x000B)\
X(Members, 0x000C)\
X(EnumMembers, 0x000D)\
X(GlobalVariables, 0x000E)\
X(GlobalVmap, 0x000F)\
X(ThreadVariables, 0x0010)\
X(Procedures, 0x0011)\
X(Scopes, 0x0012)\
X(ScopeVoffData, 0x0013)\
X(ScopeVmap, 0x0014)\
X(Locals, 0x0015)\
X(LocationBlocks, 0x0016)\
X(LocationData, 0x0017)\
X(NameMaps, 0x0018)\
Y(PRIMARY_COUNT)\
X(SKIP, RADDBGI_DataSectionTag_SECONDARY|0x0000)\
X(LineInfoVoffs, RADDBGI_DataSectionTag_SECONDARY|0x0001)\
X(LineInfoData, RADDBGI_DataSectionTag_SECONDARY|0x0002)\
X(LineInfoColumns, RADDBGI_DataSectionTag_SECONDARY|0x0003)\
X(LineMapNumbers, RADDBGI_DataSectionTag_SECONDARY|0x0004)\
X(LineMapRanges, RADDBGI_DataSectionTag_SECONDARY|0x0005)\
X(LineMapVoffs, RADDBGI_DataSectionTag_SECONDARY|0x0006)\
X(NameMapBuckets, RADDBGI_DataSectionTag_SECONDARY|0x0007)\
X(NameMapNodes, RADDBGI_DataSectionTag_SECONDARY|0x0008)
typedef RADDBGI_U32 RADDBGI_DataSectionTag;
typedef enum RADDBGI_DataSectionTagEnum{
#define X(N,C) RADDBGI_DataSectionTag_##N = C,
#define Y(N) RADDBGI_DataSectionTag_##N,
RADDBGI_DataSectionTagXList(X,Y)
#undef X
#undef Y
} RADDBGI_DataSectionTagEnum;
#define RADDBGI_DataSectionEncodingXList(X) \
X(Unpacked, 0)
typedef RADDBGI_U32 RADDBGI_DataSectionEncoding;
typedef enum RADDBGI_DataSectionEncodingEnum{
#define X(N,C) RADDBGI_DataSectionEncoding_##N = C,
RADDBGI_DataSectionEncodingXList(X)
#undef X
} RADDBGI_DataSectionEncodingEnum;
typedef struct RADDBGI_DataSection{
RADDBGI_DataSectionTag tag;
RADDBGI_DataSectionEncoding encoding;
RADDBGI_U64 off;
RADDBGI_U64 encoded_size;
RADDBGI_U64 unpacked_size;
} RADDBGI_DataSection;
//- common types
typedef struct RADDBGI_VMapEntry{
RADDBGI_U64 voff;
RADDBGI_U64 idx;
} RADDBGI_VMapEntry;
//- top level info
typedef struct RADDBGI_TopLevelInfo{
RADDBGI_Arch architecture;
RADDBGI_U32 exe_name_string_idx;
RADDBGI_U64 exe_hash;
RADDBGI_U64 voff_max;
} RADDBGI_TopLevelInfo;
//- binary sections
typedef RADDBGI_U32 RADDBGI_BinarySectionFlags;
typedef enum RADDBGI_BinarySectionFlagsEnum{
RADDBGI_BinarySectionFlag_Read = (1 << 0),
RADDBGI_BinarySectionFlag_Write = (1 << 1),
RADDBGI_BinarySectionFlag_Execute = (1 << 2)
} RADDBGI_BinarySectionFlagsEnum;
typedef struct RADDBGI_BinarySection{
RADDBGI_U32 name_string_idx;
RADDBGI_BinarySectionFlags flags;
RADDBGI_U64 voff_first;
RADDBGI_U64 voff_opl;
RADDBGI_U64 foff_first;
RADDBGI_U64 foff_opl;
} RADDBGI_BinarySection;
//- file & file system info
typedef struct RADDBGI_FilePathNode{
RADDBGI_U32 name_string_idx;
RADDBGI_U32 parent_path_node;
RADDBGI_U32 first_child;
RADDBGI_U32 next_sibling;
RADDBGI_U32 source_file_idx;
} RADDBGI_FilePathNode;
typedef struct RADDBGI_SourceFile{
RADDBGI_U32 file_path_node_idx;
RADDBGI_U32 normal_full_path_string_idx;
// usage of line map to go from a line number to an array of voffs
// (line_map_nums * line_number) -> (nil | index)
// (line_map_data * index) -> (range)
// (line_map_voff_data * range) -> (array(voff))
RADDBGI_U32 line_map_count;
RADDBGI_U32 line_map_nums_data_idx; // U32[line_map_count] (sorted - not closed ranges)
RADDBGI_U32 line_map_range_data_idx; // U32[line_map_count + 1] (pairs form ranges)
RADDBGI_U32 line_map_voff_data_idx; // U64[...] (idx by line_map_range_data)
} RADDBGI_SourceFile;
//- units & line info
typedef struct RADDBGI_Unit{
RADDBGI_U32 unit_name_string_idx;
RADDBGI_U32 compiler_name_string_idx;
RADDBGI_U32 source_file_path_node;
RADDBGI_U32 object_file_path_node;
RADDBGI_U32 archive_file_path_node;
RADDBGI_U32 build_path_node;
RADDBGI_Language language;
// usage of line info to go from voff to file & line number:
// (line_info_voffs * voff) -> (nil + index)
// (line_info_data * index) -> (RADDBGI_Line = (file_idx * line_number))
RADDBGI_U32 line_info_voffs_data_idx; // U64[line_info_count + 1] (sorted ranges)
RADDBGI_U32 line_info_data_idx; // RADDBGI_Line[line_info_count]
RADDBGI_U32 line_info_col_data_idx; // RADDBGI_Col[line_info_count]
RADDBGI_U32 line_info_count;
} RADDBGI_Unit;
typedef struct RADDBGI_Line{
RADDBGI_U32 file_idx;
RADDBGI_U32 line_num;
} RADDBGI_Line;
typedef struct RADDBGI_Column{
RADDBGI_U16 col_first;
RADDBGI_U16 col_opl;
} RADDBGI_Column;
//- type info
// X(name,code) - defines a primary code
// XZ(name,code size) - defines a primary code & associates a size
// Y(alias_name,name) - defines an alias for bookends
#define RADDBGI_TypeKindXList(X,XZ,Y)\
X(NULL, 0x0000) \
\
XZ(Void, 0x0001, 0) Y(FirstBuiltIn, Void) \
XZ(Handle, 0x0002, 0xFFFFFFFF) \
XZ(Char8, 0x0003, 1)\
XZ(Char16, 0x0004, 2) \
XZ(Char32, 0x0005, 4) \
XZ(UChar8, 0x0006, 1) \
XZ(UChar16, 0x0007, 2) \
XZ(UChar32, 0x0008, 4) \
XZ(U8, 0x0009, 1) \
XZ(U16, 0x000A, 2) \
XZ(U32, 0x000B, 4) \
XZ(U64, 0x000C, 8) \
XZ(U128, 0x000D, 16) \
XZ(U256, 0x000E, 32) \
XZ(U512, 0x000F, 64) \
XZ(S8, 0x0010, 1) \
XZ(S16, 0x0011, 2) \
XZ(S32, 0x0012, 4) \
XZ(S64, 0x0013, 8) \
XZ(S128, 0x0014, 16) \
XZ(S256, 0x0015, 32) \
XZ(S512, 0x0016, 64) \
XZ(Bool, 0x0017, 1) \
XZ(F16, 0x0018, 2) \
XZ(F32, 0x0019, 4) \
XZ(F32PP, 0x001A, 4) \
XZ(F48, 0x001B, 6) \
XZ(F64, 0x001C, 8) \
XZ(F80, 0x001D, 10) \
XZ(F128, 0x001E, 16) \
XZ(ComplexF32, 0x001F, 8) \
XZ(ComplexF64, 0x0020, 16) \
XZ(ComplexF80, 0x0021, 20) \
XZ(ComplexF128, 0x0022, 32) Y(LastBuiltIn, ComplexF128) \
\
X(Modifier, 0x1000) Y(FirstConstructed, Modifier) \
X(Ptr, 0x1001) \
X(LRef, 0x1002) \
X(RRef, 0x1003) \
X(Array, 0x1004) \
X(Function, 0x1005) \
X(Method, 0x1006) \
X(MemberPtr, 0x1007) Y(LastConstructed, MemberPtr) \
\
X(Struct, 0x2000) Y(FirstUserDefined, Struct) Y(FirstRecord, Struct) \
X(Class, 0x2001) \
X(Union, 0x2002) Y(LastRecord, Union) \
X(Enum, 0x2003) \
X(Alias, 0x2004) \
X(IncompleteStruct, 0x2005) Y(FirstIncomplete, IncompleteStruct) \
X(IncompleteUnion, 0x2006) \
X(IncompleteClass, 0x2007) \
X(IncompleteEnum, 0x2008) Y(LastIncomplete, IncompleteEnum) \
Y(LastUserDefined, IncompleteEnum) \
\
X(Bitfield, 0xF000) \
X(Variadic, 0xF001)
typedef RADDBGI_U16 RADDBGI_TypeKind;
typedef enum RADDBGI_TypeKindEnum{
#define X(name,code) RADDBGI_TypeKind_##name = code,
#define XZ(name,code,size) X(name,code)
#define Y(alias_name,name) RADDBGI_TypeKind_##alias_name = RADDBGI_TypeKind_##name,
RADDBGI_TypeKindXList(X,XZ,Y)
#undef X
#undef XZ
#undef Y
} RADDBGI_TypeKindEnum;
typedef RADDBGI_U16 RADDBGI_TypeModifierFlags;
enum{
RADDBGI_TypeModifierFlag_Const = (1 << 0),
RADDBGI_TypeModifierFlag_Volatile = (1 << 1),
};
// IMPORTANT NOTE: All type nodes in a valid raddbg are *topologically sorted*.
// That means any time a type node refers to another type node, the type node
// it refers to has an index less than or equal to the index of the node that
// is doing the referring. It is never the case that a type node depends on a
// node that comes later in the type node array.
// This restriction does not apply to the members of a type that are
// attached through a "UDT" though.
typedef struct RADDBGI_TypeNode{
RADDBGI_TypeKind kind;
// when kind is 'Modifier' -> RADDBGI_TypeModifierFlags
RADDBGI_U16 flags;
RADDBGI_U32 byte_size;
union{
// kind is 'built-in'
struct{
RADDBGI_U32 name_string_idx;
} built_in;
// kind is 'constructed'
struct{
RADDBGI_U32 direct_type_idx;
RADDBGI_U32 count;
union{
// when kind is 'Function' or 'Method'
RADDBGI_U32 param_idx_run_first;
// when kind is 'MemberPtr'
RADDBGI_U32 owner_type_idx;
};
} constructed;
// kind is 'user defined'
struct{
RADDBGI_U32 name_string_idx;
RADDBGI_U32 direct_type_idx;
RADDBGI_U32 udt_idx;
} user_defined;
// (kind = Bitfield)
struct{
RADDBGI_U32 off;
RADDBGI_U32 size;
} bitfield;
};
} RADDBGI_TypeNode;
typedef RADDBGI_U32 RADDBGI_UserDefinedTypeFlags;
enum{
RADDBGI_UserDefinedTypeFlag_EnumMembers = (1 << 0),
};
typedef struct RADDBGI_UDT{
RADDBGI_U32 self_type_idx;
RADDBGI_UserDefinedTypeFlags flags;
// when EnumMembers flag is set, indexes into enum "enum_members" instead of "members"
RADDBGI_U32 member_first;
RADDBGI_U32 member_count;
RADDBGI_U32 file_idx;
RADDBGI_U32 line;
RADDBGI_U32 col;
} RADDBGI_UDT;
#define RADDBGI_MemberKindXList(X) \
X(NULL, 0x0000) \
X(DataField, 0x0001) \
X(StaticData, 0x0002) \
X(Method, 0x0100) \
X(StaticMethod, 0x0101) \
X(VirtualMethod, 0x0102) \
X(VTablePtr, 0x0200) \
X(Base, 0x0201) \
X(VirtualBase, 0x0202) \
X(NestedType, 0x0300)
typedef RADDBGI_U16 RADDBGI_MemberKind;
typedef enum RADDBGI_MemberKindEnum{
#define X(N,C) RADDBGI_MemberKind_##N = C,
RADDBGI_MemberKindXList(X)
#undef X
} RADDBGI_MemberKindEnum;
// TODO(allen): need a way to equip methods and some virtual methods
// with procedure symbol information. I'm thinking a seperate data
// array of (MemberIdx,ProcSymbolIdx) sorted by MemberIdx. Or just a
// parallel array. Putting them right into this struct looks like it
// would complicate the converters because we tend to want an API
// like 'associate_method_to_proc' that can be used *after* both the
// method and proc are known, rather than one that forces us to know
// the association when constructing the member data.
typedef struct RADDBGI_Member{
RADDBGI_MemberKind kind;
RADDBGI_U16 __unused__;
RADDBGI_U32 name_string_idx;
RADDBGI_U32 type_idx;
RADDBGI_U32 off;
} RADDBGI_Member;
typedef struct RADDBGI_EnumMember{
RADDBGI_U32 name_string_idx;
RADDBGI_U32 __unused__;
RADDBGI_U64 val;
} RADDBGI_EnumMember;
//- symbol info
typedef RADDBGI_U32 RADDBGI_LinkFlags;
enum{
RADDBGI_LinkFlag_External = (1 << 0),
// NOTE: Scope flags are mutually exclusive.
// A symbol is either global scoped, type scoped, or procedure scoped.
RADDBGI_LinkFlag_TypeScoped = (1 << 1),
RADDBGI_LinkFlag_ProcScoped = (1 << 2),
};
typedef struct RADDBGI_GlobalVariable{
RADDBGI_U32 name_string_idx;
// NOTE: "global variables" can be scoped in *any* way. The scope flags here refer to
// *namespace* scoping. "global variables" are all in the data section of the
// final exe/dll type file, so they are "global" in the life-time sense of the
// word. In the namespace sense of the word, they can be scoped globally, by type,
// or by procedure.
RADDBGI_LinkFlags link_flags;
RADDBGI_U64 voff;
RADDBGI_U32 type_idx;
// container_idx: UDT for "TypeScoped", Procedure for "ProcScoped"
RADDBGI_U32 container_idx;
} RADDBGI_GlobalVariable;
typedef struct RADDBGI_ThreadVariable{
RADDBGI_U32 name_string_idx;
// NOTE: See the note in GlobalVariable regarding scoping. The same concept applies here.
RADDBGI_LinkFlags link_flags;
RADDBGI_U32 tls_off;
RADDBGI_U32 type_idx;
// container_idx: UDT for "TypeScoped", Procedure for "ProcScoped"
RADDBGI_U32 container_idx;
} RADDBGI_ThreadVariable;
typedef struct RADDBGI_Procedure{
RADDBGI_U32 name_string_idx;
RADDBGI_U32 link_name_string_idx;
// NOTE: See the note in GlobalVariable regarding scoping. The same concept applies here.
RADDBGI_LinkFlags link_flags;
RADDBGI_U32 type_idx;
RADDBGI_U32 root_scope_idx;
// container_idx: UDT for "TypeScoped", Procedure for "ProcScoped"
RADDBGI_U32 container_idx;
} RADDBGI_Procedure;
typedef struct RADDBGI_Scope{
RADDBGI_U32 proc_idx;
RADDBGI_U32 parent_scope_idx;
RADDBGI_U32 first_child_scope_idx;
RADDBGI_U32 next_sibling_scope_idx;
RADDBGI_U32 voff_range_first;
RADDBGI_U32 voff_range_opl;
// indexes into "locals"
RADDBGI_U32 local_first;
RADDBGI_U32 local_count;
RADDBGI_U32 static_local_idx_run_first;
RADDBGI_U32 static_local_count;
// TODO(allen): attach less common scope-relevant info
} RADDBGI_Scope;
typedef RADDBGI_U32 RADDBGI_LocalKind;
typedef enum{
RADDBGI_LocalKind_NULL,
RADDBGI_LocalKind_Parameter,
RADDBGI_LocalKind_Variable,
RADDBGI_LocalKind_COUNT
} RADDBGI_LocalKindEnum;
typedef struct RADDBGI_Local{
RADDBGI_LocalKind kind;
RADDBGI_U32 name_string_idx;
RADDBGI_U64 type_idx;
// indexes into "location_blocks"
RADDBGI_U32 location_first;
RADDBGI_U32 location_opl;
} RADDBGI_Local;
typedef struct RADDBGI_LocationBlock{
RADDBGI_U32 scope_off_first;
RADDBGI_U32 scope_off_opl;
RADDBGI_U32 location_data_off;
} RADDBGI_LocationBlock;
typedef RADDBGI_U8 RADDBGI_LocationKind;
typedef enum{
RADDBGI_LocationKind_NULL,
RADDBGI_LocationKind_AddrBytecodeStream,
RADDBGI_LocationKind_ValBytecodeStream,
RADDBGI_LocationKind_AddrRegisterPlusU16,
RADDBGI_LocationKind_AddrAddrRegisterPlusU16,
RADDBGI_LocationKind_ValRegister,
RADDBGI_LocationKind_COUNT
} RADDBGI_LocationKindEnum;
typedef struct RADDBGI_LocationBytecodeStream{
RADDBGI_LocationKind kind;
// [... 0] null terminated byte sequence RADDBGI_EvalBytecodeStream
} RADDBGI_LocationBytecodeStream;
typedef struct RADDBGI_LocationRegisterPlusU16{
RADDBGI_LocationKind kind;
RADDBGI_RegisterCode register_code;
RADDBGI_U16 offset;
} RADDBGI_LocationRegisterPlusU16;
typedef struct RADDBGI_LocationRegister{
RADDBGI_LocationKind kind;
RADDBGI_RegisterCode register_code;
} RADDBGI_LocationRegister;
//- name map types
#define RADDBGI_NameMapXList(X)\
X(NULL, 0)\
X(GlobalVariables, 1)\
X(ThreadVariables, 2)\
X(Procedures, 3)\
X(Types, 4)\
X(LinkNameProcedures, 5)\
X(NormalSourcePaths, 6)
typedef RADDBGI_U32 RADDBGI_NameMapKind;
typedef enum RADDBGI_NameMapKindEnum{
#define X(N,C) RADDBGI_NameMapKind_##N = C,
RADDBGI_NameMapXList(X)
#undef X
RADDBGI_NameMapKind_COUNT
} RADDBGI_NameMapKindEnum;
// TODO(allen): documentation here for the hashing and probing strategy for this table
typedef struct RADDBGI_NameMap{
RADDBGI_NameMapKind kind;
RADDBGI_U32 bucket_data_idx;
RADDBGI_U32 node_data_idx;
} RADDBGI_NameMap;
typedef struct RADDBGI_NameMapBucket{
RADDBGI_U32 first_node;
RADDBGI_U32 node_count;
} RADDBGI_NameMapBucket;
typedef struct RADDBGI_NameMapNode{
RADDBGI_U32 string_idx;
RADDBGI_U32 match_count;
// NOTE: if (match_count == 1) then this is the index of the matching item
// if (match_count > 1) then this is the first for an index run of all the matches
RADDBGI_U32 match_idx_or_idx_run_first;
} RADDBGI_NameMapNode;
////////////////////////////////
// Eval Bytecode
// (Name, decodeN, popN, pushN)
#define RADDBGI_EvalOpXList(X)\
X(Stop, 0, 0, 0)\
X(Noop, 0, 0, 0)\
X(Cond, 1, 1, 0)\
X(Skip, 1, 0, 0)\
X(MemRead, 1, 1, 1)\
X(RegRead, 4, 0, 1)\
X(RegReadDyn, 0, 1, 1)\
X(FrameOff, 1, 0, 1)\
X(ModuleOff, 4, 0, 1)\
X(TLSOff, 4, 0, 1)\
X(ObjectOff, 0, 0, 0)\
X(CFA, 0, 0, 0)\
X(ConstU8, 1, 0, 1)\
X(ConstU16, 2, 0, 1)\
X(ConstU32, 4, 0, 1)\
X(ConstU64, 8, 0, 1)\
X(Abs, 1, 1, 1)\
X(Neg, 1, 1, 1)\
X(Add, 1, 2, 1)\
X(Sub, 1, 2, 1)\
X(Mul, 1, 2, 1)\
X(Div, 1, 2, 1)\
X(Mod, 1, 2, 1)\
X(LShift, 1, 2, 1)\
X(RShift, 1, 2, 1)\
X(BitAnd, 1, 2, 1)\
X(BitOr, 1, 2, 1)\
X(BitXor, 1, 2, 1)\
X(BitNot, 1, 1, 1)\
X(LogAnd, 1, 2, 1)\
X(LogOr, 1, 2, 1)\
X(LogNot, 1, 1, 1)\
X(EqEq, 1, 2, 1)\
X(NtEq, 1, 2, 1)\
X(LsEq, 1, 2, 1)\
X(GrEq, 1, 2, 1)\
X(Less, 1, 2, 1)\
X(Grtr, 1, 2, 1)\
X(Trunc, 1, 1, 1)\
X(TruncSigned, 1, 1, 1)\
X(Convert, 2, 1, 1)\
X(Pick, 1, 0, 1)\
X(Pop, 0, 1, 0)\
X(Insert, 1, 0, 0)
// (Name)
#define RADDBGI_EvalTypeGroupXList(X)\
X(Other)\
X(U)\
X(S)\
X(F32)\
X(F64)
// (Name, error-message)
#define RADDBGI_EvalConversionKindXList(X)\
X(Noop, "")\
X(Legal, "")\
X(OtherToOther, "cannot convert between these types")\
X(ToOther, "cannot convert to this type")\
X(FromOther, "cannot convert this type")
// Xb(EvalTypeGroup) Y(TypeKind) Xe(EvalTypeGroup)
#define RADDBGI_EvalTypeGroupFromKindMap(Y,Xb,Xe)\
\
Xb(U) Y(U8) Y(U16) Y(U32) Y(U64) Y(Bool) Y(Ptr) Y(Enum)\
Xe(U)\
\
Xb(S) Y(S8) Y(S16) Y(S32) Y(S64)\
Xe(S)\
\
Xb(F32) Y(F32)\
Xe(F32)\
\
Xb(F64) Y(F64)\
Xe(F64)
// Xb(EvalConversionKind) Y(EvalTypeGroup, EvalTypeGroup) Xe(EvalConversionKind)
#define RADDBGI_EvalConversionKindFromTypeGroupPairMap(Y,Xb,Xe)\
\
Xb(Noop) Y(U, U) Y(S, S) Y(F32, F32) Y(F64, F64) Y(U, S) Y(S, U)\
Xe(Noop)\
\
Xb(Legal)\
Y(U, F32) Y(S, F32) Y(F32, U) Y(F32, S)\
Y(U, F64) Y(S, F64) Y(F64, U) Y(F64, S)\
Y(F32, F64) Y(F64, F32)\
Xe(Legal)\
\
Xb(OtherToOther) Y(Other, Other)\
Xe(OtherToOther)\
\
Xb(ToOther) Y(U, Other) Y(S, Other) Y(F32, Other) Y(F64, Other)\
Xe(ToOther)\
\
Xb(FromOther) Y(Other, U) Y(Other, S) Y(Other, F32) Y(Other, F64)\
Xe(FromOther)
// eval interpretation macros
#define RADDBGI_EncodeRegReadParam(reg,bytesize,bytepos) ((reg)|((bytesize)<<8)|((bytepos)<<16))
// eval enums
typedef RADDBGI_U8 RADDBGI_EvalOp;
typedef enum RADDBGI_EvalOpEnum{
#define X(N,dec,pop,push) RADDBGI_EvalOp_##N,
RADDBGI_EvalOpXList(X)
#undef X
RADDBGI_EvalOp_COUNT
} RADDBGI_EvalOpEnum;
typedef RADDBGI_U8 RADDBGI_EvalTypeGroup;
typedef enum RADDBGI_EvalTypeGroupEnum{
#define X(N) RADDBGI_EvalTypeGroup_##N,
RADDBGI_EvalTypeGroupXList(X)
#undef X
RADDBGI_EvalTypeGroup_COUNT,
} RADDBGI_EvalTypeGroupEnum;
typedef RADDBGI_U8 RADDBGI_EvalConversionKind;
typedef enum RADDBGI_EvalConversionKindEnum{
#define X(N,msg) RADDBGI_EvalConversionKind_##N,
RADDBGI_EvalConversionKindXList(X)
#undef X
RADDBGI_EvalConversionKind_COUNT,
} RADDBGI_EvalConversionKindEnum;
//- eval data tables
#define RADDBGI_EVAL_CTRLBITS(decodeN,popN,pushN) ((decodeN) | ((popN) << 4) | ((pushN) << 6))
#define RADDBGI_DECODEN_FROM_CTRLBITS(ctrlbits) ((ctrlbits) & 0xf)
#define RADDBGI_POPN_FROM_CTRLBITS(ctrlbits) (((ctrlbits) >> 4) & 0x3)
#define RADDBGI_PUSHN_FROM_CTRLBITS(ctrlbits) (((ctrlbits) >> 6) & 0x3)
static RADDBGI_U8 raddbgi_eval_opcode_ctrlbits[] = {
#define X(Name, decodeN, popN, pushN) RADDBGI_EVAL_CTRLBITS(decodeN,popN,pushN),
RADDBGI_EvalOpXList(X)
#undef X
};
////////////////////////////////
// Functions
RADDBGI_PROC RADDBGI_U64 raddbgi_hash(RADDBGI_U8 *ptr, RADDBGI_U64 size);
RADDBGI_PROC RADDBGI_U32 raddbgi_size_from_basic_type_kind(RADDBGI_TypeKind kind);
RADDBGI_PROC RADDBGI_U32 raddbgi_addr_size_from_arch(RADDBGI_Arch arch);
//- eval helpers
RADDBGI_PROC RADDBGI_EvalConversionKind raddbgi_eval_conversion_rule(RADDBGI_EvalTypeGroup in, RADDBGI_EvalTypeGroup out);
RADDBGI_PROC RADDBGI_U8* raddbgi_eval_conversion_message(RADDBGI_EvalConversionKind conversion_kind, RADDBGI_U64 *lennout);
RADDBGI_PROC RADDBGI_S32 raddbgi_eval_opcode_type_compatible(RADDBGI_EvalOp op, RADDBGI_EvalTypeGroup group);
#endif // RADDBGI_FORMAT_H
@@ -0,0 +1,558 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ RADDBG Parse API
RADDBGI_PROC RADDBGI_ParseStatus
raddbgi_parse(RADDBGI_U8 *data, RADDBGI_U64 size, RADDBGI_Parsed *out){
RADDBGI_ParseStatus result = RADDBGI_ParseStatus_Good;
// out header
RADDBGI_Header *hdr = 0;
{
if (sizeof(*hdr) <= size){
hdr = (RADDBGI_Header*)data;
}
// (errors)
if (hdr == 0 || hdr->magic != RADDBGI_MAGIC_CONSTANT){
hdr = 0;
result = RADDBGI_ParseStatus_HeaderDoesNotMatch;
}
if (hdr != 0 && hdr->encoding_version != 1){
hdr = 0;
result = RADDBGI_ParseStatus_UnsupportedVersionNumber;
}
}
// out data sections
RADDBGI_DataSection *dsecs = 0;
RADDBGI_U32 dsec_count = 0;
if (hdr != 0){
RADDBGI_U64 opl = (RADDBGI_U64)hdr->data_section_off + (RADDBGI_U64)hdr->data_section_count*sizeof(*dsecs);
if (opl <= size){
dsecs = (RADDBGI_DataSection*)(data + hdr->data_section_off);
dsec_count = hdr->data_section_count;
}
// (errors)
if (dsecs == 0){
result = RADDBGI_ParseStatus_InvalidDataSecionLayout;
}
}
// extract primary data section indexes
RADDBGI_U32 dsec_idx[RADDBGI_DataSectionTag_PRIMARY_COUNT] = {0};
if (result == RADDBGI_ParseStatus_Good){
RADDBGI_DataSection *sec_ptr = dsecs;
for (RADDBGI_U32 i = 0; i < dsec_count; i += 1, sec_ptr += 1){
if (sec_ptr->tag < RADDBGI_DataSectionTag_PRIMARY_COUNT){
dsec_idx[sec_ptr->tag] = i;
}
}
}
// fill out data block (part 1)
if (result == RADDBGI_ParseStatus_Good){
out->raw_data = data;
out->raw_data_size = size;
out->dsecs = dsecs;
out->dsec_count = dsec_count;
for (RADDBGI_U32 i = 0; i < RADDBGI_DataSectionTag_PRIMARY_COUNT; i += 1){
out->dsec_idx[i] = dsec_idx[i];
}
}
// out string table
RADDBGI_U8 *string_data = 0;
RADDBGI_U64 string_opl = 0;
RADDBGI_U32 *string_offs = 0;
RADDBGI_U64 string_count = 0;
if (result == RADDBGI_ParseStatus_Good){
raddbgi_parse__extract_primary(out, string_data, &string_opl,
RADDBGI_DataSectionTag_StringData);
RADDBGI_U64 table_entry_count = 0;
raddbgi_parse__extract_primary(out, string_offs, &table_entry_count,
RADDBGI_DataSectionTag_StringTable);
if (table_entry_count > 0){
string_count = table_entry_count - 1;
}
// (errors)
if (string_data == 0){
result = RADDBGI_ParseStatus_MissingStringDataSection;
}
else if (string_offs == 0){
result = RADDBGI_ParseStatus_MissingStringTableSection;
}
}
// out index runs
RADDBGI_U32 *idx_run_data = 0;
RADDBGI_U64 idx_run_count = 0;
if (result == RADDBGI_ParseStatus_Good){
raddbgi_parse__extract_primary(out, idx_run_data, &idx_run_count,
RADDBGI_DataSectionTag_IndexRuns);
// (errors)
if (idx_run_data == 0){
result = RADDBGI_ParseStatus_MissingIndexRunSection;
}
}
if (result == RADDBGI_ParseStatus_Good){
// fill out primary data structures (part 2)
out->string_data = string_data;
out->string_offs = string_offs;
out->string_data_size = string_opl;
out->string_count = string_count;
out->idx_run_data = idx_run_data;
out->idx_run_count = idx_run_count;
{
RADDBGI_TopLevelInfo *tli = 0;
RADDBGI_U64 dummy = 0;
raddbgi_parse__extract_primary(out, tli, &dummy, RADDBGI_DataSectionTag_TopLevelInfo);
if (dummy != 1){
tli = 0;
}
out->top_level_info = tli;
}
raddbgi_parse__extract_primary(out, out->binary_sections, &out->binary_sections_count,
RADDBGI_DataSectionTag_BinarySections);
raddbgi_parse__extract_primary(out, out->file_paths, &out->file_paths_count,
RADDBGI_DataSectionTag_FilePathNodes);
raddbgi_parse__extract_primary(out, out->source_files, &out->source_files_count,
RADDBGI_DataSectionTag_SourceFiles);
raddbgi_parse__extract_primary(out, out->units, &out->units_count,
RADDBGI_DataSectionTag_Units);
raddbgi_parse__extract_primary(out, out->unit_vmap, &out->unit_vmap_count,
RADDBGI_DataSectionTag_UnitVmap);
raddbgi_parse__extract_primary(out, out->unit_vmap, &out->unit_vmap_count,
RADDBGI_DataSectionTag_UnitVmap);
raddbgi_parse__extract_primary(out, out->type_nodes, &out->type_nodes_count,
RADDBGI_DataSectionTag_TypeNodes);
raddbgi_parse__extract_primary(out, out->udts, &out->udts_count,
RADDBGI_DataSectionTag_UDTs);
raddbgi_parse__extract_primary(out, out->members, &out->members_count,
RADDBGI_DataSectionTag_Members);
raddbgi_parse__extract_primary(out, out->enum_members, &out->enum_members_count,
RADDBGI_DataSectionTag_EnumMembers);
raddbgi_parse__extract_primary(out, out->global_variables, &out->global_variables_count,
RADDBGI_DataSectionTag_GlobalVariables);
raddbgi_parse__extract_primary(out, out->global_vmap, &out->global_vmap_count,
RADDBGI_DataSectionTag_GlobalVmap);
raddbgi_parse__extract_primary(out, out->thread_variables, &out->thread_variables_count,
RADDBGI_DataSectionTag_ThreadVariables);
raddbgi_parse__extract_primary(out, out->procedures, &out->procedures_count,
RADDBGI_DataSectionTag_Procedures);
raddbgi_parse__extract_primary(out, out->scopes, &out->scopes_count,
RADDBGI_DataSectionTag_Scopes);
raddbgi_parse__extract_primary(out, out->scope_voffs, &out->scope_voffs_count,
RADDBGI_DataSectionTag_ScopeVoffData);
raddbgi_parse__extract_primary(out, out->scope_vmap, &out->scope_vmap_count,
RADDBGI_DataSectionTag_ScopeVmap);
raddbgi_parse__extract_primary(out, out->locals, &out->locals_count,
RADDBGI_DataSectionTag_Locals);
raddbgi_parse__extract_primary(out, out->location_blocks, &out->location_blocks_count,
RADDBGI_DataSectionTag_LocationBlocks);
raddbgi_parse__extract_primary(out, out->location_data, &out->location_data_size,
RADDBGI_DataSectionTag_LocationData);
{
raddbgi_parse__extract_primary(out, out->name_maps, &out->name_maps_count,
RADDBGI_DataSectionTag_NameMaps);
RADDBGI_NameMap *name_map_ptr = out->name_maps;
RADDBGI_NameMap *name_map_opl = out->name_maps + out->name_maps_count;
for (; name_map_ptr < name_map_opl; name_map_ptr += 1){
if (out->name_maps_by_kind[name_map_ptr->kind] == 0){
out->name_maps_by_kind[name_map_ptr->kind] = name_map_ptr;
}
}
}
#if !defined(RADDBGI_DISABLE_NILS)
if(out->binary_sections == 0) { out->binary_sections = &raddbgi_binary_section_nil; out->binary_sections_count = 1; }
if(out->file_paths == 0) { out->file_paths = &raddbgi_file_path_node_nil; out->file_paths_count = 1; }
if(out->source_files == 0) { out->source_files = &raddbgi_source_file_nil; out->source_files_count = 1; }
if(out->units == 0) { out->units = &raddbgi_unit_nil; out->units_count = 1; }
if(out->unit_vmap == 0) { out->unit_vmap = &raddbgi_vmap_entry_nil; out->unit_vmap_count = 1; }
if(out->type_nodes == 0) { out->type_nodes = &raddbgi_type_node_nil; out->type_nodes_count = 1; }
if(out->udts == 0) { out->udts = &raddbgi_udt_nil; out->udts_count = 1; }
if(out->members == 0) { out->members = &raddbgi_member_nil; out->members_count = 1; }
if(out->enum_members == 0) { out->enum_members = &raddbgi_enum_member_nil; out->enum_members_count = 1; }
if(out->global_variables == 0) { out->global_variables = &raddbgi_global_variable_nil; out->global_variables_count = 1; }
if(out->global_vmap == 0) { out->global_vmap = &raddbgi_vmap_entry_nil; out->global_vmap_count = 1; }
if(out->thread_variables == 0) { out->thread_variables = &raddbgi_thread_variable_nil; out->thread_variables_count = 1; }
if(out->procedures == 0) { out->procedures = &raddbgi_procedure_nil; out->procedures_count = 1; }
if(out->scopes == 0) { out->scopes = &raddbgi_scope_nil; out->scopes_count = 1; }
if(out->scope_voffs == 0) { out->scope_voffs = &raddbgi_voff_nil; out->scope_voffs_count = 1; }
if(out->scope_vmap == 0) { out->scope_vmap = &raddbgi_vmap_entry_nil; out->scope_vmap_count = 1; }
if(out->locals == 0) { out->locals = &raddbgi_local_nil; out->locals_count = 1; }
if(out->location_blocks == 0) { out->location_blocks = &raddbgi_location_block_nil; out->location_blocks_count = 1; }
#endif
}
return(result);
}
RADDBGI_PROC RADDBGI_U8*
raddbgi_string_from_idx(RADDBGI_Parsed *parsed, RADDBGI_U32 idx, RADDBGI_U64 *len_out){
RADDBGI_U8 *result = 0;
RADDBGI_U64 len_result = 0;
if (idx < parsed->string_count){
RADDBGI_U32 off_raw = parsed->string_offs[idx];
RADDBGI_U32 opl_raw = parsed->string_offs[idx + 1];
RADDBGI_U32 opl = raddbgi_parse__min(opl_raw, parsed->string_data_size);
RADDBGI_U32 off = raddbgi_parse__min(off_raw, opl);
result = parsed->string_data + off;
len_result = opl - off;
}
*len_out = len_result;
return(result);
}
RADDBGI_PROC RADDBGI_U32*
raddbgi_idx_run_from_first_count(RADDBGI_Parsed *parsed,
RADDBGI_U32 raw_first, RADDBGI_U32 raw_count,
RADDBGI_U32 *n_out){
RADDBGI_U32 raw_opl = raw_first + raw_count;
RADDBGI_U32 opl = raddbgi_parse__min(raw_opl, parsed->idx_run_count);
RADDBGI_U32 first = raddbgi_parse__min(raw_first, opl);
RADDBGI_U32 *result = 0;
if (first < parsed->idx_run_count){
result = parsed->idx_run_data + first;
}
*n_out = opl - first;
return(result);
}
//- line info
RADDBGI_PROC void
raddbgi_line_info_from_unit(RADDBGI_Parsed *p, RADDBGI_Unit *unit, RADDBGI_ParsedLineInfo *out){
RADDBGI_U64 line_info_voff_count = 0;
RADDBGI_U64 *voffs = (RADDBGI_U64*)
raddbgi_data_from_dsec(p, unit->line_info_voffs_data_idx, sizeof(RADDBGI_U64),
RADDBGI_DataSectionTag_LineInfoVoffs,
&line_info_voff_count);
RADDBGI_U64 line_info_count_raw = 0;
RADDBGI_Line *lines = (RADDBGI_Line*)
raddbgi_data_from_dsec(p, unit->line_info_data_idx, sizeof(RADDBGI_Line),
RADDBGI_DataSectionTag_LineInfoData,
&line_info_count_raw);
RADDBGI_U64 column_info_count_raw = 0;
RADDBGI_Column *cols = (RADDBGI_Column*)
raddbgi_data_from_dsec(p, unit->line_info_col_data_idx, sizeof(RADDBGI_Column),
RADDBGI_DataSectionTag_LineInfoColumns,
&column_info_count_raw);
RADDBGI_U32 line_info_count_a = (line_info_voff_count > 0)?line_info_voff_count - 1:0;
RADDBGI_U32 line_info_count = raddbgi_parse__min(line_info_count_a, line_info_count_raw);
RADDBGI_U32 column_info_count = raddbgi_parse__min(column_info_count_raw, line_info_count);
out->voffs = voffs;
out->lines = lines;
out->cols = cols;
out->count = line_info_count;
out->col_count = column_info_count;
}
RADDBGI_PROC RADDBGI_U64
raddbgi_line_info_idx_from_voff(RADDBGI_ParsedLineInfo *line_info, RADDBGI_U64 voff)
{
RADDBGI_U64 result = 0;
if (line_info->count > 0 && line_info->voffs[0] <= voff && voff < line_info->voffs[line_info->count - 1]){
// assuming: (i < j) -> (vmap[i].voff < vmap[j].voff)
// find i such that: (vmap[i].voff <= voff) && (voff < vmap[i + 1].voff)
RADDBGI_U32 first = 0;
RADDBGI_U32 opl = line_info->count;
for (;;){
RADDBGI_U32 mid = (first + opl)/2;
if (line_info->voffs[mid] < voff){
first = mid;
}
else if (line_info->voffs[mid] > voff){
opl = mid;
}
else{
first = mid;
break;
}
if (opl - first <= 1){
break;
}
}
result = (RADDBGI_U64)first;
}
return(result);
}
RADDBGI_PROC void
raddbgi_line_map_from_source_file(RADDBGI_Parsed *p, RADDBGI_SourceFile *srcfile,
RADDBGI_ParsedLineMap *out){
RADDBGI_U64 num_count = 0;
RADDBGI_U32 *nums = (RADDBGI_U32*)
raddbgi_data_from_dsec(p, srcfile->line_map_nums_data_idx, sizeof(RADDBGI_U32),
RADDBGI_DataSectionTag_LineMapNumbers,
&num_count);
RADDBGI_U64 range_count = 0;
RADDBGI_U32 *ranges = (RADDBGI_U32*)
raddbgi_data_from_dsec(p, srcfile->line_map_range_data_idx, sizeof(RADDBGI_U32),
RADDBGI_DataSectionTag_LineMapRanges,
&range_count);
RADDBGI_U64 voff_count = 0;
RADDBGI_U64 *voffs = (RADDBGI_U64*)
raddbgi_data_from_dsec(p, srcfile->line_map_voff_data_idx, sizeof(RADDBGI_U64),
RADDBGI_DataSectionTag_LineMapVoffs,
&voff_count);
RADDBGI_U32 count_a = (range_count > 0)?(range_count - 1):0;
RADDBGI_U32 count_b = raddbgi_parse__min(count_a, num_count);
RADDBGI_U32 count = raddbgi_parse__min(count_b, srcfile->line_map_count);
out->nums = nums;
out->ranges = ranges;
out->voffs = voffs;
out->count = count;
out->voff_count = voff_count;
}
RADDBGI_PROC RADDBGI_U64*
raddbgi_line_voffs_from_num(RADDBGI_ParsedLineMap *map, RADDBGI_U32 linenum, RADDBGI_U32 *n_out){
RADDBGI_U64 *result = 0;
*n_out = 0;
RADDBGI_U32 closest_i = 0;
if (map->count > 0 && map->nums[0] <= linenum){
// assuming: (i < j) -> (nums[i] < nums[j])
// find i such that: (nums[i] <= linenum) && (linenum < nums[i + 1])
RADDBGI_U32 *nums = map->nums;
RADDBGI_U32 first = 0;
RADDBGI_U32 opl = map->count;
for (;;){
RADDBGI_U32 mid = (first + opl)/2;
if (nums[mid] < linenum){
first = mid;
}
else if (nums[mid] > linenum){
opl = mid;
}
else{
first = mid;
break;
}
if (opl - first <= 1){
break;
}
}
closest_i = first;
}
// round up instead of down if possible
if (closest_i + 1 < map->count &&
map->nums[closest_i] < linenum){
closest_i += 1;
}
// set result if possible
if (closest_i < map->count){
RADDBGI_U32 first = map->ranges[closest_i];
RADDBGI_U32 opl = map->ranges[closest_i + 1];
if (opl < map->voff_count){
result = map->voffs + first;
*n_out = opl - first;
}
}
return(result);
}
//- vmaps
RADDBGI_PROC RADDBGI_U64
raddbgi_vmap_idx_from_voff(RADDBGI_VMapEntry *vmap, RADDBGI_U32 vmap_count, RADDBGI_U64 voff){
RADDBGI_U64 result = 0;
if (vmap_count > 0 && vmap[0].voff <= voff && voff < vmap[vmap_count - 1].voff){
// assuming: (i < j) -> (vmap[i].voff < vmap[j].voff)
// find i such that: (vmap[i].voff <= voff) && (voff < vmap[i + 1].voff)
RADDBGI_U32 first = 0;
RADDBGI_U32 opl = vmap_count;
for (;;){
RADDBGI_U32 mid = (first + opl)/2;
if (vmap[mid].voff < voff){
first = mid;
}
else if (vmap[mid].voff > voff){
opl = mid;
}
else{
first = mid;
break;
}
if (opl - first <= 1){
break;
}
}
result = (RADDBGI_U64)vmap[first].idx;
}
return(result);
}
//- name maps
RADDBGI_PROC RADDBGI_NameMap*
raddbgi_name_map_from_kind(RADDBGI_Parsed *p, RADDBGI_NameMapKind kind){
RADDBGI_NameMap *result = 0;
if (0 < kind && kind < RADDBGI_NameMapKind_COUNT){
result = p->name_maps_by_kind[kind];
}
return(result);
}
RADDBGI_PROC void
raddbgi_name_map_parse(RADDBGI_Parsed *p, RADDBGI_NameMap *mapptr, RADDBGI_ParsedNameMap *out){
out->buckets = 0;
out->bucket_count = 0;
if (mapptr != 0){
out->buckets = (RADDBGI_NameMapBucket*)
raddbgi_data_from_dsec(p, mapptr->bucket_data_idx, sizeof(RADDBGI_NameMapBucket),
RADDBGI_DataSectionTag_NameMapBuckets, &out->bucket_count);
out->nodes = (RADDBGI_NameMapNode*)
raddbgi_data_from_dsec(p, mapptr->node_data_idx, sizeof(RADDBGI_NameMapNode),
RADDBGI_DataSectionTag_NameMapNodes, &out->node_count);
}
}
RADDBGI_PROC RADDBGI_NameMapNode*
raddbgi_name_map_lookup(RADDBGI_Parsed *p, RADDBGI_ParsedNameMap *map,
RADDBGI_U8 *str, RADDBGI_U64 len){
RADDBGI_NameMapNode *result = 0;
if (map->bucket_count > 0){
RADDBGI_NameMapBucket *buckets = map->buckets;
RADDBGI_U64 bucket_count = map->bucket_count;
RADDBGI_U64 hash = raddbgi_hash(str, len);
RADDBGI_U64 bucket_index = hash%bucket_count;
RADDBGI_NameMapBucket *bucket = map->buckets + bucket_index;
RADDBGI_NameMapNode *node = map->nodes + bucket->first_node;
RADDBGI_NameMapNode *node_opl = node + bucket->node_count;
for (;node < node_opl; node += 1){
// extract a string from this node
RADDBGI_U64 nlen = 0;
RADDBGI_U8 *nstr = raddbgi_string_from_idx(p, node->string_idx, &nlen);
// compare this to the needle string
RADDBGI_S32 match = 0;
if (nlen == len){
RADDBGI_U8 *a = str;
RADDBGI_U8 *aopl = str + len;
RADDBGI_U8 *b = nstr;
for (;a < aopl && *a == *b; a += 1, b += 1);
match = (a == aopl);
}
// stop with a matching node in result
if (match){
result = node;
break;
}
}
}
return(result);
}
RADDBGI_PROC RADDBGI_U32*
raddbgi_matches_from_map_node(RADDBGI_Parsed *p, RADDBGI_NameMapNode *node,
RADDBGI_U32 *n_out){
RADDBGI_U32 *result = 0;
*n_out = 0;
if (node != 0){
if (node->match_count == 1){
result = &node->match_idx_or_idx_run_first;
*n_out = 1;
}
else{
result = raddbgi_idx_run_from_first_count(p, node->match_idx_or_idx_run_first,
node->match_count, n_out);
}
}
return(result);
}
//- common helpers
RADDBGI_PROC RADDBGI_U64
raddbgi_first_voff_from_proc(RADDBGI_Parsed *p, RADDBGI_U32 proc_id){
RADDBGI_U64 result = 0;
if (0 < proc_id && proc_id < p->procedures_count){
RADDBGI_Procedure *proc = p->procedures + proc_id;
RADDBGI_U32 scope_id = proc->root_scope_idx;
if (0 < scope_id && scope_id < p->scopes_count){
RADDBGI_Scope *scope = p->scopes + scope_id;
if (scope->voff_range_first < scope->voff_range_opl &&
scope->voff_range_first < p->scope_voffs_count){
result = p->scope_voffs[scope->voff_range_first];
}
}
}
return(result);
}
////////////////////////////////
//~ RADDBG Parsing Helpers
RADDBGI_PROC void*
raddbgi_data_from_dsec(RADDBGI_Parsed *parsed, RADDBGI_U32 idx, RADDBGI_U32 item_size,
RADDBGI_DataSectionTag expected_tag,
RADDBGI_U64 *count_out){
void *result = 0;
RADDBGI_U32 count_result = 0;
// TODO(allen): need a version of this that works with encodings other than "Unpacked"
if (0 < idx && idx < parsed->dsec_count){
RADDBGI_DataSection *ds = parsed->dsecs + idx;
if (ds->tag == expected_tag){
RADDBGI_U64 opl = ds->off + ds->encoded_size;
if (opl <= parsed->raw_data_size){
count_result = ds->encoded_size/item_size;
result = (parsed->raw_data + ds->off);
}
}
}
*count_out = count_result;
return(result);
}
@@ -0,0 +1,219 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RADDBGI_PARSE_H
#define RADDBGI_PARSE_H
////////////////////////////////
//~ RADDBG Parsing Helpers
typedef struct RADDBGI_Parsed{
// raw data & data sections (part 1)
RADDBGI_U8 *raw_data;
RADDBGI_U64 raw_data_size;
RADDBGI_DataSection *dsecs;
RADDBGI_U64 dsec_count;
RADDBGI_U32 dsec_idx[RADDBGI_DataSectionTag_PRIMARY_COUNT];
// primary data structures (part 2)
// handled by helper APIs
RADDBGI_U8* string_data;
RADDBGI_U64 string_data_size;
RADDBGI_U32* string_offs;
RADDBGI_U64 string_count;
RADDBGI_U32* idx_run_data;
RADDBGI_U32 idx_run_count;
// directly readable by users
// (any of these may be empty and null even in a successful parse)
RADDBGI_TopLevelInfo* top_level_info;
RADDBGI_BinarySection* binary_sections;
RADDBGI_U64 binary_sections_count;
RADDBGI_FilePathNode* file_paths;
RADDBGI_U64 file_paths_count;
RADDBGI_SourceFile* source_files;
RADDBGI_U64 source_files_count;
RADDBGI_Unit* units;
RADDBGI_U64 units_count;
RADDBGI_VMapEntry* unit_vmap;
RADDBGI_U64 unit_vmap_count;
RADDBGI_TypeNode* type_nodes;
RADDBGI_U64 type_nodes_count;
RADDBGI_UDT* udts;
RADDBGI_U64 udts_count;
RADDBGI_Member* members;
RADDBGI_U64 members_count;
RADDBGI_EnumMember* enum_members;
RADDBGI_U64 enum_members_count;
RADDBGI_GlobalVariable* global_variables;
RADDBGI_U64 global_variables_count;
RADDBGI_VMapEntry* global_vmap;
RADDBGI_U64 global_vmap_count;
RADDBGI_ThreadVariable* thread_variables;
RADDBGI_U64 thread_variables_count;
RADDBGI_Procedure* procedures;
RADDBGI_U64 procedures_count;
RADDBGI_Scope* scopes;
RADDBGI_U64 scopes_count;
RADDBGI_U64* scope_voffs;
RADDBGI_U64 scope_voffs_count;
RADDBGI_VMapEntry* scope_vmap;
RADDBGI_U64 scope_vmap_count;
RADDBGI_Local* locals;
RADDBGI_U64 locals_count;
RADDBGI_LocationBlock* location_blocks;
RADDBGI_U64 location_blocks_count;
RADDBGI_U8* location_data;
RADDBGI_U64 location_data_size;
RADDBGI_NameMap* name_maps;
RADDBGI_U64 name_maps_count;
// other helpers
RADDBGI_NameMap* name_maps_by_kind[RADDBGI_NameMapKind_COUNT];
} RADDBGI_Parsed;
typedef enum{
RADDBGI_ParseStatus_Good = 0,
RADDBGI_ParseStatus_HeaderDoesNotMatch = 1,
RADDBGI_ParseStatus_UnsupportedVersionNumber = 2,
RADDBGI_ParseStatus_InvalidDataSecionLayout = 3,
RADDBGI_ParseStatus_MissingStringDataSection = 4,
RADDBGI_ParseStatus_MissingStringTableSection = 5,
RADDBGI_ParseStatus_MissingIndexRunSection = 6,
} RADDBGI_ParseStatus;
typedef struct RADDBGI_ParsedLineInfo{
// NOTE: Mapping VOFF -> LINE_INFO
//
// * [ voff[i], voff[i + 1] ) forms the voff range
// * for the line info at lines[i] (and cols[i] if i < col_count)
RADDBGI_U64* voffs; // [count + 1] sorted
RADDBGI_Line* lines; // [count]
RADDBGI_Column* cols; // [col_count]
RADDBGI_U64 count;
RADDBGI_U64 col_count;
} RADDBGI_ParsedLineInfo;
typedef struct RADDBGI_ParsedLineMap{
// NOTE: Mapping LINE_NUMBER -> VOFFs
//
// * nums[i] gives a line number
// * that line number has one or more associated voffs
//
// * to find all associated voffs for the line number nums[i] :
// * let k span over the range [ ranges[i], ranges[i + 1] )
// * voffs[k] gives the associated voffs
RADDBGI_U32* nums; // [count] sorted
RADDBGI_U32* ranges; // [count + 1]
RADDBGI_U64* voffs; // [voff_count]
RADDBGI_U64 count;
RADDBGI_U64 voff_count;
} RADDBGI_ParsedLineMap;
typedef struct RADDBGI_ParsedNameMap{
RADDBGI_NameMapBucket *buckets;
RADDBGI_NameMapNode *nodes;
RADDBGI_U64 bucket_count;
RADDBGI_U64 node_count;
} RADDBGI_ParsedNameMap;
////////////////////////////////
//~ Global Nils
#if !defined(RADDBGI_DISABLE_NILS)
static RADDBGI_BinarySection raddbgi_binary_section_nil = {0};
static RADDBGI_FilePathNode raddbgi_file_path_node_nil = {0};
static RADDBGI_SourceFile raddbgi_source_file_nil = {0};
static RADDBGI_Unit raddbgi_unit_nil = {0};
static RADDBGI_VMapEntry raddbgi_vmap_entry_nil = {0};
static RADDBGI_TypeNode raddbgi_type_node_nil = {0};
static RADDBGI_UDT raddbgi_udt_nil = {0};
static RADDBGI_Member raddbgi_member_nil = {0};
static RADDBGI_EnumMember raddbgi_enum_member_nil = {0};
static RADDBGI_GlobalVariable raddbgi_global_variable_nil = {0};
static RADDBGI_ThreadVariable raddbgi_thread_variable_nil = {0};
static RADDBGI_Procedure raddbgi_procedure_nil = {0};
static RADDBGI_Scope raddbgi_scope_nil = {0};
static RADDBGI_U64 raddbgi_voff_nil = 0;
static RADDBGI_LocationBlock raddbgi_location_block_nil = {0};
static RADDBGI_Local raddbgi_local_nil = {0};
#endif
////////////////////////////////
//~ RADDBG Parse API
RADDBGI_PROC RADDBGI_ParseStatus
raddbgi_parse(RADDBGI_U8 *data, RADDBGI_U64 size, RADDBGI_Parsed *out);
RADDBGI_PROC RADDBGI_U8*
raddbgi_string_from_idx(RADDBGI_Parsed *parsed, RADDBGI_U32 idx, RADDBGI_U64 *len_out);
RADDBGI_PROC RADDBGI_U32*
raddbgi_idx_run_from_first_count(RADDBGI_Parsed *parsed, RADDBGI_U32 first, RADDBGI_U32 raw_count,
RADDBGI_U32 *n_out);
//- table lookups
#define raddbgi_element_from_idx(parsed, name, idx) ((0 <= (idx) && (idx) < (parsed)->name##_count) ? &(parsed)->name[idx] : (parsed)->name ? &(parsed)->name[0] : 0)
//- line info
RADDBGI_PROC void
raddbgi_line_info_from_unit(RADDBGI_Parsed *p, RADDBGI_Unit *unit, RADDBGI_ParsedLineInfo *out);
RADDBGI_PROC RADDBGI_U64
raddbgi_line_info_idx_from_voff(RADDBGI_ParsedLineInfo *line_info, RADDBGI_U64 voff);
RADDBGI_PROC void
raddbgi_line_map_from_source_file(RADDBGI_Parsed *p, RADDBGI_SourceFile *srcfile,
RADDBGI_ParsedLineMap *out);
RADDBGI_PROC RADDBGI_U64*
raddbgi_line_voffs_from_num(RADDBGI_ParsedLineMap *map, RADDBGI_U32 linenum, RADDBGI_U32 *n_out);
//- vmaps
RADDBGI_PROC RADDBGI_U64
raddbgi_vmap_idx_from_voff(RADDBGI_VMapEntry *vmap, RADDBGI_U32 vmap_count, RADDBGI_U64 voff);
//- name maps
RADDBGI_PROC RADDBGI_NameMap*
raddbgi_name_map_from_kind(RADDBGI_Parsed *p, RADDBGI_NameMapKind kind);
RADDBGI_PROC void
raddbgi_name_map_parse(RADDBGI_Parsed* p, RADDBGI_NameMap *mapptr, RADDBGI_ParsedNameMap *out);
RADDBGI_PROC RADDBGI_NameMapNode*
raddbgi_name_map_lookup(RADDBGI_Parsed *p, RADDBGI_ParsedNameMap *map,
RADDBGI_U8 *str, RADDBGI_U64 len);
RADDBGI_PROC RADDBGI_U32*
raddbgi_matches_from_map_node(RADDBGI_Parsed *p, RADDBGI_NameMapNode *node, RADDBGI_U32 *n_out);
//- common helpers
RADDBGI_PROC RADDBGI_U64
raddbgi_first_voff_from_proc(RADDBGI_Parsed *p, RADDBGI_U32 proc_id);
////////////////////////////////
//~ RADDBG Parsing Helpers
#define raddbgi_parse__extract_primary(p,outptr,outn,pritag) \
( (*(void**)&(outptr)) = \
raddbgi_data_from_dsec((p),(p)->dsec_idx[pritag],sizeof(*(outptr)),(pritag),(outn)) )
RADDBGI_PROC void*
raddbgi_data_from_dsec(RADDBGI_Parsed *p, RADDBGI_U32 idx, RADDBGI_U32 item_size,
RADDBGI_DataSectionTag expected_tag, RADDBGI_U64 *n_out);
#define raddbgi_parse__min(a,b) (((a)<(b))?(a):(b))
#endif // RADDBGI_PARSE_H