mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-14 17:28:07 +00:00
1089 lines
33 KiB
C
1089 lines
33 KiB
C
// Copyright (c) Epic Games Tools
|
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Basic Parsing Helpers
|
|
|
|
internal U64
|
|
dw2_read_initial_length(String8 data, U64 off, U64 *out, DW_Format *fmt_out)
|
|
{
|
|
DW_Format format = DW_Format_32Bit;
|
|
U64 size = 0;
|
|
U64 size_size = 0;
|
|
{
|
|
U32 size_or_header = 0;
|
|
size_size += str8_deserial_read_struct(data, off, &size_or_header);
|
|
if(size_or_header == max_U32)
|
|
{
|
|
size_size += str8_deserial_read_struct(data, off + size_size, &size);
|
|
format = DW_Format_64Bit;
|
|
}
|
|
else
|
|
{
|
|
size = (U64)size_or_header;
|
|
}
|
|
}
|
|
if(out)
|
|
{
|
|
out[0] = size;
|
|
}
|
|
if(fmt_out)
|
|
{
|
|
fmt_out[0] = format;
|
|
}
|
|
return size_size;
|
|
}
|
|
|
|
internal U64
|
|
dw2_read_fmt_u64(String8 data, U64 off, DW_Format format, U64 *out)
|
|
{
|
|
U64 start_off = off;
|
|
switch(format)
|
|
{
|
|
default:
|
|
case DW_Format_32Bit:
|
|
{
|
|
U32 out_32 = 0;
|
|
off += str8_deserial_read_struct(data, off, &out_32);
|
|
out[0] = (U64)out_32;
|
|
}break;
|
|
case DW_Format_64Bit:
|
|
{
|
|
off += str8_deserial_read_struct(data, off, out);
|
|
}break;
|
|
}
|
|
U64 bytes_read = (off - start_off);
|
|
return bytes_read;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Unit Header Parsing
|
|
|
|
internal U64
|
|
dw2_read_unit_header(String8 data, U64 off, DW2_UnitHeader *out)
|
|
{
|
|
U64 start_off = off;
|
|
|
|
// rjf: read unit's .debug_info range size / format
|
|
U64 unit_info_size = 0;
|
|
DW_Format format = DW_Format_32Bit;
|
|
off += dw2_read_initial_length(data, off, &unit_info_size, &format);
|
|
|
|
// rjf: read dwarf version
|
|
DW_Version version = 0;
|
|
off += str8_deserial_read_struct(data, off, &version);
|
|
|
|
// rjf: read header properties
|
|
U64 abbrev_off = max_U64;
|
|
U64 addr_size = 0;
|
|
DW_CompUnitKind kind = DW_CompUnitKind_Reserved;
|
|
U64 dwo_id = 0;
|
|
switch((DW_VersionEnum)version)
|
|
{
|
|
case DW_Version_Null:
|
|
case DW_Version_1:
|
|
{}break;
|
|
|
|
// rjf: @dwarf2
|
|
case DW_Version_2:
|
|
{
|
|
// rjf: read abbrev off
|
|
U32 abbrev_off_32 = 0;
|
|
off += str8_deserial_read_struct(data, off, &abbrev_off_32);
|
|
abbrev_off = (U64)abbrev_off_32;
|
|
|
|
// rjf: read address size
|
|
U8 addr_size_8 = 0;
|
|
off += str8_deserial_read_struct(data, off, &addr_size_8);
|
|
addr_size = (U64)addr_size_8;
|
|
}break;
|
|
|
|
// rjf: @dwarf3 @dwarf4
|
|
case DW_Version_3:
|
|
case DW_Version_4:
|
|
{
|
|
// rjf: read abbrev off
|
|
off += dw2_read_fmt_u64(data, off, format, &abbrev_off);
|
|
|
|
// rjf: read address size
|
|
U8 addr_size_8 = 0;
|
|
off += str8_deserial_read_struct(data, off, &addr_size_8);
|
|
addr_size = (U64)addr_size_8;
|
|
}break;
|
|
|
|
// rjf: @dwarf5
|
|
case DW_Version_5:
|
|
{
|
|
// rjf: read unit kind
|
|
U8 comp_unit_kind_8 = 0;
|
|
off += str8_deserial_read_struct(data, off, &comp_unit_kind_8);
|
|
kind = (DW_CompUnitKind)comp_unit_kind_8;
|
|
|
|
// rjf: read address size
|
|
U8 addr_size_8 = 0;
|
|
off += str8_deserial_read_struct(data, off, &addr_size_8);
|
|
addr_size = (U64)addr_size_8;
|
|
|
|
// rjf: read abbrev off
|
|
off += dw2_read_fmt_u64(data, off, format, &abbrev_off);
|
|
|
|
// rjf: read DWO ID, if applicable
|
|
if(kind == DW_CompUnitKind_Skeleton ||
|
|
kind == DW_CompUnitKind_SplitCompile ||
|
|
kind == DW_CompUnitKind_SplitType)
|
|
{
|
|
off += str8_deserial_read_struct(data, off, &dwo_id);
|
|
}
|
|
}break;
|
|
}
|
|
|
|
// rjf: fill
|
|
out->version = version;
|
|
out->format = format;
|
|
out->abbrev_off = abbrev_off;
|
|
out->addr_size = addr_size;
|
|
out->kind = kind;
|
|
out->dwo_id = dwo_id;
|
|
|
|
// rjf: return # of bytes read
|
|
U64 bytes_read = (off - start_off);
|
|
return bytes_read;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Abbreviation Map Parsing
|
|
|
|
internal DW2_AbbrevMap
|
|
dw2_abbrev_map_from_data(Arena *arena, String8 data, U64 off)
|
|
{
|
|
DW2_AbbrevMap map = {0};
|
|
|
|
// rjf: loop - first to determine the number of tag formats encoded
|
|
// by this abbrev table, second to actually build a hash table for
|
|
// mapping (id -> .debug_abbrev offset)
|
|
for(B32 build = 0; build <= 1; build += 1)
|
|
{
|
|
// rjf: parse whole abbrev table
|
|
U64 tag_count = 0;
|
|
for(U64 read_off = off;;)
|
|
{
|
|
U64 start_off = read_off;
|
|
|
|
// rjf: read tag abbrev
|
|
U64 id = 0;
|
|
U64 tag_kind = 0;
|
|
U8 has_children = 0;
|
|
{
|
|
read_off += str8_deserial_read_uleb128(data, read_off, &id);
|
|
if(id != 0)
|
|
{
|
|
read_off += str8_deserial_read_uleb128(data, read_off, &tag_kind);
|
|
read_off += str8_deserial_read_struct(data, read_off, &has_children);
|
|
}
|
|
}
|
|
|
|
// rjf: count tag
|
|
if(id != 0)
|
|
{
|
|
tag_count += 1;
|
|
}
|
|
|
|
// rjf: read all tag attribute abbreviations
|
|
if(id != 0)
|
|
{
|
|
for(;;)
|
|
{
|
|
U64 attrib_start_off = read_off;
|
|
U64 attrib_kind = 0;
|
|
U64 attrib_form_kind = 0;
|
|
U64 implicit_const = 0;
|
|
read_off += str8_deserial_read_uleb128(data, read_off, &attrib_kind);
|
|
read_off += str8_deserial_read_uleb128(data, read_off, &attrib_form_kind);
|
|
if(attrib_form_kind == DW_Form_ImplicitConst)
|
|
{
|
|
read_off += str8_deserial_read_uleb128(data, read_off, &implicit_const);
|
|
}
|
|
if(read_off == attrib_start_off || attrib_kind == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// rjf: if building (second loop) -> insert into map
|
|
if(build)
|
|
{
|
|
U64 hash = u64_hash_from_str8(str8_struct(&id));
|
|
U64 slot_idx = hash%map.slots_count;
|
|
DW2_AbbrevMapNode *n = push_array(arena, DW2_AbbrevMapNode, 1);
|
|
SLLStackPush(map.slots[slot_idx], n);
|
|
n->id = id;
|
|
n->off = start_off;
|
|
}
|
|
|
|
// rjf: no tag ID, or no movement? -> exit
|
|
if(read_off == start_off || id == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rjf: on first loop iteration, given the tag format count, construct the table
|
|
if(!build)
|
|
{
|
|
map.slots_count = Max(1, tag_count + tag_count/4);
|
|
map.slots = push_array(arena, DW2_AbbrevMapNode *, map.slots_count);
|
|
}
|
|
}
|
|
|
|
return map;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Form Value Parsing
|
|
|
|
internal U64
|
|
dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kind, U64 implicit_const, DW2_FormVal *out)
|
|
{
|
|
U64 start_off = off;
|
|
DW2_FormVal val = {form_kind};
|
|
{
|
|
//- rjf: read value bytes
|
|
U64 bytes_to_read = 0;
|
|
switch(form_kind)
|
|
{
|
|
//- rjf: no-ops
|
|
default:
|
|
case DW_Form_Indirect:
|
|
case DW_Form_Null:
|
|
{}break;
|
|
|
|
//- rjf: 1-byte uint reads
|
|
case DW_Form_Ref1:
|
|
case DW_Form_Data1:
|
|
case DW_Form_Flag:
|
|
case DW_Form_Strx1:
|
|
case DW_Form_Addrx1:
|
|
bytes_to_read = 1; goto read_fixed_uint;
|
|
|
|
//- rjf: 2-byte uint reads
|
|
case DW_Form_Ref2:
|
|
case DW_Form_Data2:
|
|
case DW_Form_Strx2:
|
|
case DW_Form_Addrx2:
|
|
bytes_to_read = 2; goto read_fixed_uint;
|
|
|
|
//- rjf: 3-byte uint reads
|
|
case DW_Form_Strx3:
|
|
case DW_Form_Addrx3:
|
|
bytes_to_read = 3; goto read_fixed_uint;
|
|
|
|
//- rjf: 4-byte uint reads
|
|
case DW_Form_Data4:
|
|
case DW_Form_Ref4:
|
|
case DW_Form_RefSup4:
|
|
case DW_Form_Strx4:
|
|
case DW_Form_Addrx4:
|
|
bytes_to_read = 4; goto read_fixed_uint;
|
|
|
|
//- rjf: 8-byte uint reads
|
|
case DW_Form_Data8:
|
|
case DW_Form_Ref8:
|
|
case DW_Form_RefSig8:
|
|
case DW_Form_RefSup8:
|
|
bytes_to_read = 8; goto read_fixed_uint;
|
|
|
|
//- rjf: 16-byte uint reads
|
|
case DW_Form_Data16:
|
|
bytes_to_read = 16; goto read_fixed_uint;
|
|
|
|
//- rjf: address-sized reads
|
|
case DW_Form_Addr:
|
|
bytes_to_read = ctx->addr_size; goto read_fixed_uint;
|
|
|
|
//- rjf: format-defined-size reads
|
|
case DW_Form_RefAddr:
|
|
case DW_Form_SecOffset:
|
|
case DW_Form_LineStrp:
|
|
case DW_Form_Strp:
|
|
case DW_Form_StrpSup:
|
|
bytes_to_read = dw_size_from_format(ctx->format); goto read_fixed_uint;
|
|
|
|
//- rjf: fixed-size uint reads
|
|
read_fixed_uint:
|
|
{
|
|
off += str8_deserial_read(data, off, &val.u128.u64[0], bytes_to_read, bytes_to_read);
|
|
}break;
|
|
|
|
//- rjf: uleb128 reads
|
|
case DW_Form_UData:
|
|
case DW_Form_RefUData:
|
|
case DW_Form_Strx:
|
|
case DW_Form_Addrx:
|
|
case DW_Form_LocListx:
|
|
case DW_Form_RngListx:
|
|
{
|
|
off += str8_deserial_read_uleb128(data, off, &val.u128.u64[0]);
|
|
}break;
|
|
|
|
//- rjf: sleb128 reads
|
|
case DW_Form_SData:
|
|
{
|
|
off += str8_deserial_read_sleb128(data, off, (S64 *)(&val.u128.u64[0]));
|
|
}break;
|
|
|
|
//- rjf: fixed-size uint reads / skips
|
|
case DW_Form_Block1: bytes_to_read = 1; goto read_fixed_uint_and_skip;
|
|
case DW_Form_Block2: bytes_to_read = 2; goto read_fixed_uint_and_skip;
|
|
case DW_Form_Block4: bytes_to_read = 4; goto read_fixed_uint_and_skip;
|
|
read_fixed_uint_and_skip:
|
|
{
|
|
U64 size = 0;
|
|
U64 og_read_off = off;
|
|
off += str8_deserial_read(data, off, &size, bytes_to_read, bytes_to_read);
|
|
val.u128.u64[0] = size;
|
|
val.u128.u64[1] = og_read_off;
|
|
off += size;
|
|
}break;
|
|
|
|
//- rjf: uleb128 read & skip
|
|
case DW_Form_Block:
|
|
{
|
|
U64 size = 0;
|
|
U64 og_read_off = off;
|
|
off += str8_deserial_read_uleb128(data, off, &size);
|
|
val.string = str8_substr(data, r1u64(off, off+size));
|
|
off += size;
|
|
}break;
|
|
|
|
//- rjf: strings
|
|
case DW_Form_String:
|
|
{
|
|
String8 string = {0};
|
|
U64 og_read_off = off;
|
|
off += str8_deserial_read_cstr(data, off, &string);
|
|
val.string = string;
|
|
}break;
|
|
|
|
//- rjf: implicit constants (no reading in .debug_info; value comes from .debug_abbrev)
|
|
case DW_Form_ImplicitConst:
|
|
{
|
|
val.u128.u64[0] = implicit_const;
|
|
}break;
|
|
|
|
//- rjf: exprloc
|
|
case DW_Form_ExprLoc:
|
|
{
|
|
U64 size = 0;
|
|
U64 og_read_off = off;
|
|
U64 bytes_read = str8_deserial_read_uleb128(data, off, &size);
|
|
val.u128.u64[0] = og_read_off + bytes_read;
|
|
val.u128.u64[1] = size;
|
|
off += size + bytes_read;
|
|
}break;
|
|
|
|
//- rjf: flag present
|
|
case DW_Form_FlagPresent:
|
|
{
|
|
val.u128.u64[0] = 1;
|
|
}break;
|
|
}
|
|
|
|
//- rjf: in some cases, resolve numeric values -> strings
|
|
{
|
|
switch(form_kind)
|
|
{
|
|
default:{}break;
|
|
case DW_Form_Strx1:
|
|
case DW_Form_Strx2:
|
|
case DW_Form_Strx3:
|
|
case DW_Form_Strx4:
|
|
case DW_Form_Strx:
|
|
if(ctx->str_offsets_table != 0)
|
|
{
|
|
U64 entry_idx = val.u128.u64[0];
|
|
U64 string_data_off = 0;
|
|
if(dw2_try_offset_from_table_idx(ctx->str_offsets_table, entry_idx, &string_data_off))
|
|
{
|
|
String8 string_section_data = ctx->raw->sec[DW_Section_Str].data;
|
|
val.string = str8_cstring_capped(string_section_data.str + string_data_off,
|
|
string_section_data.str + string_section_data.size);
|
|
}
|
|
}break;
|
|
case DW_Form_LineStrp:
|
|
{
|
|
String8 string_section_data = ctx->raw->sec[DW_Section_LineStr].data;
|
|
val.string = str8_cstring_capped(string_section_data.str + val.u128.u64[0],
|
|
string_section_data.str + string_section_data.size);
|
|
}break;
|
|
case DW_Form_Strp:
|
|
case DW_Form_StrpSup:
|
|
{
|
|
String8 string_section_data = ctx->raw->sec[DW_Section_Str].data;
|
|
val.string = str8_cstring_capped(string_section_data.str + val.u128.u64[0],
|
|
string_section_data.str + string_section_data.size);
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
*out = val;
|
|
U64 bytes_read = (off - start_off);
|
|
return bytes_read;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Tag Parsing
|
|
|
|
internal U64
|
|
dw2_read_tag(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *tag_out)
|
|
{
|
|
U64 start_off = off;
|
|
|
|
// rjf: read tag's abbrev ID
|
|
U64 abbrev_id = 0;
|
|
off += str8_deserial_read_uleb128(data, off, &abbrev_id);
|
|
|
|
// rjf: map abbrev ID -> abbrev offset
|
|
U64 abbrev_off = 0;
|
|
{
|
|
DW2_AbbrevMap *abbrev_map = ctx->abbrev_map;
|
|
U64 hash = u64_hash_from_str8(str8_struct(&abbrev_id));
|
|
U64 slot_idx = hash%abbrev_map->slots_count;
|
|
for EachNode(n, DW2_AbbrevMapNode, abbrev_map->slots[slot_idx])
|
|
{
|
|
if(n->id == abbrev_id)
|
|
{
|
|
abbrev_off = n->off;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// rjf: read abbrev header
|
|
U64 tag_kind = 0;
|
|
U8 has_children = 0;
|
|
U64 attrib_abbrev_read_off = 0;
|
|
{
|
|
String8 abbrev_data = ctx->raw->sec[DW_Section_Abbrev].data;
|
|
U64 abbrev_read_off = abbrev_off;
|
|
U64 id = 0;
|
|
abbrev_read_off += str8_deserial_read_uleb128(abbrev_data, abbrev_read_off, &id);
|
|
if(id != 0)
|
|
{
|
|
abbrev_read_off += str8_deserial_read_uleb128(abbrev_data, abbrev_read_off, &tag_kind);
|
|
abbrev_read_off += str8_deserial_read_struct(abbrev_data, abbrev_read_off, &has_children);
|
|
}
|
|
attrib_abbrev_read_off = abbrev_read_off;
|
|
}
|
|
|
|
// rjf: walk abbrev / info in lock-step; read all tag attributes
|
|
DW2_AttribList attribs = {0};
|
|
if(abbrev_id != 0)
|
|
{
|
|
String8 abbrev_data = ctx->raw->sec[DW_Section_Abbrev].data;
|
|
U64 abbrev_read_off = attrib_abbrev_read_off;
|
|
for(;;)
|
|
{
|
|
U64 abbrev_start_read_off = abbrev_read_off;
|
|
|
|
// rjf: read next attribute abbreviation from .debug_abbrev
|
|
U64 attrib_kind = 0;
|
|
U64 attrib_form_kind = 0;
|
|
U64 implicit_const = 0;
|
|
abbrev_read_off += str8_deserial_read_uleb128(abbrev_data, abbrev_read_off, &attrib_kind);
|
|
abbrev_read_off += str8_deserial_read_uleb128(abbrev_data, abbrev_read_off, &attrib_form_kind);
|
|
if(attrib_form_kind == DW_Form_ImplicitConst)
|
|
{
|
|
abbrev_read_off += str8_deserial_read_uleb128(abbrev_data, abbrev_read_off, &implicit_const);
|
|
}
|
|
|
|
// rjf: indirect form -> form kind is encoded in .debug_info (because why not)
|
|
if(attrib_form_kind == DW_Form_Indirect)
|
|
{
|
|
off += str8_deserial_read_uleb128(data, off, &attrib_form_kind);
|
|
}
|
|
|
|
// rjf: read attribute's value from .debug_info
|
|
DW2_FormVal val = {0};
|
|
if(attrib_kind != 0)
|
|
{
|
|
off += dw2_read_form_val(ctx, data, off, attrib_form_kind, implicit_const, &val);
|
|
}
|
|
|
|
// rjf: push
|
|
if(attrib_kind != 0)
|
|
{
|
|
DW2_AttribNode *n = push_array(arena, DW2_AttribNode, 1);
|
|
SLLQueuePush(attribs.first, attribs.last, n);
|
|
attribs.count += 1;
|
|
n->v.attrib_kind = attrib_kind;
|
|
n->v.val = val;
|
|
}
|
|
|
|
// rjf: no movement, or no attrib kind -> done
|
|
if(abbrev_read_off == abbrev_start_read_off || attrib_kind == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// rjf: fill output
|
|
tag_out->kind = (DW_TagKind)tag_kind;
|
|
tag_out->has_children = !!has_children;
|
|
tag_out->attribs = attribs;
|
|
|
|
// rjf: return # of bytes read
|
|
U64 bytes_read = (off - start_off);
|
|
return bytes_read;
|
|
}
|
|
|
|
internal DW2_Attrib *
|
|
dw2_attrib_from_kind(DW2_Tag *tag, DW_AttribKind kind)
|
|
{
|
|
DW2_Attrib *result = &dw2_attrib_nil;
|
|
for EachNode(n, DW2_AttribNode, tag->attribs.first)
|
|
{
|
|
if(n->v.attrib_kind == kind)
|
|
{
|
|
result = &n->v;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal U64
|
|
dw2_reference_info_off_from_form_val(DW2_ParseCtx *ctx, DW2_FormVal *v)
|
|
{
|
|
U64 result = 0;
|
|
switch(v->kind)
|
|
{
|
|
default:{}break;
|
|
case DW_Form_Ref1:
|
|
case DW_Form_Ref2:
|
|
case DW_Form_Ref4:
|
|
case DW_Form_Ref8:
|
|
{
|
|
result = v->u128.u64[0];
|
|
}break;
|
|
// TODO(rjf): DW_Form_RefAddr, DW_Form_RefUData, DW_Form_RefSig8, DW_Form_RefSup8, etc.
|
|
}
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Line Table Parsing
|
|
|
|
internal U64
|
|
dw2_read_line_table_header(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_LineTableHeader *out)
|
|
{
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
U64 start_off = 0;
|
|
|
|
//////////////////////////////
|
|
//- rjf: read unit length
|
|
//
|
|
U64 unit_length = 0;
|
|
DW_Format format = DW_Format_32Bit;
|
|
off += dw2_read_initial_length(data, off, &unit_length, &format);
|
|
U64 off_opl = off + unit_length;
|
|
|
|
//////////////////////////////
|
|
//- rjf: read version
|
|
//
|
|
DW_Version version = DW_Version_Null;
|
|
off += str8_deserial_read_struct(data, off, &version);
|
|
|
|
//////////////////////////////
|
|
//- rjf: read address / segment selector sizes
|
|
//
|
|
U8 addr_size = 0;
|
|
U8 segment_selector_size = 0;
|
|
switch(version)
|
|
{
|
|
// NOTE(rjf): pre-dwarf5: assume from context
|
|
default:
|
|
{
|
|
addr_size = ctx->addr_size;
|
|
}break;
|
|
|
|
// NOTE(rjf): @dwarf5 read address / segment selector sizes explicitly
|
|
case DW_Version_5:
|
|
{
|
|
off += str8_deserial_read_struct(data, off, &addr_size);
|
|
off += str8_deserial_read_struct(data, off, &segment_selector_size);
|
|
}break;
|
|
}
|
|
|
|
//////////////////////////////
|
|
//- rjf: read all remaining flat header properties
|
|
//
|
|
U64 header_length = 0;
|
|
U8 min_inst_length = 0;
|
|
U8 max_ops_per_inst = 1;
|
|
U8 default_is_stmt = 0;
|
|
S8 line_base = 0;
|
|
U8 line_range = 0;
|
|
U8 opcode_base = 0;
|
|
{
|
|
off += dw2_read_fmt_u64(data, off, format, &header_length);
|
|
off += str8_deserial_read_struct(data, off, &min_inst_length);
|
|
off += str8_deserial_read_struct(data, off, &max_ops_per_inst);
|
|
off += str8_deserial_read_struct(data, off, &default_is_stmt);
|
|
off += str8_deserial_read_struct(data, off, &line_base);
|
|
off += str8_deserial_read_struct(data, off, &line_range);
|
|
off += str8_deserial_read_struct(data, off, &opcode_base);
|
|
}
|
|
|
|
//////////////////////////////
|
|
//- rjf: read opcode lengths
|
|
//
|
|
U64 opcode_lengths_count = (opcode_base > 0 ? opcode_base - 1 : 0);
|
|
U8 *opcode_lengths = 0;
|
|
if(opcode_lengths_count > 0)
|
|
{
|
|
opcode_lengths = str8_skip(data, off).str;
|
|
off += sizeof(opcode_lengths[0]) * opcode_lengths_count;
|
|
}
|
|
|
|
//////////////////////////////
|
|
//- rjf: read directory / file tables
|
|
//
|
|
DW2_LineTableFileArray dirs = {0};
|
|
DW2_LineTableFileArray files = {0};
|
|
switch(version)
|
|
{
|
|
////////////////////////////
|
|
//- rjf: pre-dwarf5: simple(r) well-defined format... still ulebs everywhere but oh well...
|
|
// just wait until the next case...
|
|
//
|
|
default:
|
|
{
|
|
//- rjf: gather directories
|
|
DW2_LineTableFileList dir_list = {0};
|
|
{
|
|
// rjf: push compile directory as first list element, always
|
|
{
|
|
DW2_LineTableFileNode *n = push_array(scratch.arena, DW2_LineTableFileNode, 1);
|
|
SLLQueuePush(dir_list.first, dir_list.last, n);
|
|
dir_list.count += 1;
|
|
n->v.file_name = ctx->unit_dir;
|
|
}
|
|
|
|
// rjf: gather additional directories
|
|
for(;off < off_opl;)
|
|
{
|
|
String8 dir = {0};
|
|
off += str8_deserial_read_cstr(data, off, &dir);
|
|
if(dir.size != 0)
|
|
{
|
|
DW2_LineTableFileNode *n = push_array(scratch.arena, DW2_LineTableFileNode, 1);
|
|
SLLQueuePush(dir_list.first, dir_list.last, n);
|
|
dir_list.count += 1;
|
|
n->v.file_name = dir;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//- rjf: gather files
|
|
DW2_LineTableFileList file_list = {0};
|
|
{
|
|
// rjf: push main compilation unit file as first list element, always
|
|
{
|
|
DW2_LineTableFileNode *n = push_array(scratch.arena, DW2_LineTableFileNode, 1);
|
|
SLLQueuePush(file_list.first, file_list.last, n);
|
|
file_list.count += 1;
|
|
n->v.file_name = ctx->unit_file;
|
|
}
|
|
|
|
// rjf: gather additional files
|
|
for(;off < off_opl;)
|
|
{
|
|
String8 file_name = {0};
|
|
U64 dir_idx = 0;
|
|
U64 modify_time = 0;
|
|
U64 file_size = 0;
|
|
off += str8_deserial_read_cstr(data, off, &file_name);
|
|
off += str8_deserial_read_uleb128(data, off, &dir_idx);
|
|
off += str8_deserial_read_uleb128(data, off, &modify_time);
|
|
off += str8_deserial_read_uleb128(data, off, &file_size);
|
|
if(file_name.size != 0)
|
|
{
|
|
DW2_LineTableFileNode *n = push_array(scratch.arena, DW2_LineTableFileNode, 1);
|
|
SLLQueuePush(file_list.first, file_list.last, n);
|
|
file_list.count += 1;
|
|
n->v.file_name = file_name;
|
|
n->v.dir_idx = dir_idx;
|
|
n->v.modify_time = modify_time;
|
|
n->v.file_size = file_size;
|
|
}
|
|
}
|
|
}
|
|
|
|
//- rjf: flatten
|
|
{
|
|
dirs.count = dir_list.count;
|
|
dirs.v = push_array(arena, DW2_LineTableFile, dirs.count);
|
|
files.count = file_list.count;
|
|
files.v = push_array(arena, DW2_LineTableFile, files.count);
|
|
{
|
|
U64 idx = 0;
|
|
for EachNode(n, DW2_LineTableFileNode, dir_list.first)
|
|
{
|
|
MemoryCopyStruct(&dirs.v[idx], &n->v);
|
|
idx += 1;
|
|
}
|
|
}
|
|
{
|
|
U64 idx = 0;
|
|
for EachNode(n, DW2_LineTableFileNode, file_list.first)
|
|
{
|
|
MemoryCopyStruct(&files.v[idx], &n->v);
|
|
idx += 1;
|
|
}
|
|
}
|
|
}
|
|
}break;
|
|
|
|
////////////////////////////
|
|
//- rjf: @dwarf5: another VM! d'oh. :(
|
|
//
|
|
case DW_Version_5:
|
|
{
|
|
//- rjf: read directory & file tables
|
|
for(B32 do_files = 0, do_dirs = 1; do_files <= 1; do_files += 1, do_dirs = 0)
|
|
{
|
|
// rjf: read header
|
|
U8 entry_formats_count = 0;
|
|
off += str8_deserial_read_struct(data, off, &entry_formats_count);
|
|
U64 *entry_formats = push_array(scratch.arena, U64, entry_formats_count*2);
|
|
for EachIndex(idx, entry_formats_count)
|
|
{
|
|
off += str8_deserial_read_uleb128(data, off, &entry_formats[idx*2 + 0]);
|
|
off += str8_deserial_read_uleb128(data, off, &entry_formats[idx*2 + 1]);
|
|
}
|
|
U64 table_count = 0;
|
|
off += str8_deserial_read_uleb128(data, off, &table_count);
|
|
|
|
// rjf: read all table entries
|
|
DW2_LineTableFileArray table = {0};
|
|
table.count = table_count;
|
|
table.v = push_array(arena, DW2_LineTableFile, table.count);
|
|
{
|
|
U64 idx = 0;
|
|
for(;off < off_opl && idx < table.count; idx += 1)
|
|
{
|
|
DW2_LineTableFile *entry_out = &table.v[idx];
|
|
for(U64 entry_format_idx = 0; entry_format_idx < entry_formats_count; entry_format_idx += 1)
|
|
{
|
|
DW_LNCT lnct = (DW_LNCT)entry_formats[entry_format_idx*2 + 0];
|
|
DW_FormKind form_kind = (DW_FormKind)entry_formats[entry_format_idx*2 + 1];
|
|
DW2_FormVal form_val = {0};
|
|
off += dw2_read_form_val(ctx, data, off, form_kind, 0, &form_val);
|
|
switch(lnct)
|
|
{
|
|
default:
|
|
{
|
|
log_infof("DWARF LNCT encoding not currently supported (0x%I64x).\n", (U64)lnct);
|
|
}break;
|
|
case DW_LNCT_Path:
|
|
{
|
|
entry_out->file_name = form_val.string;
|
|
}break;
|
|
case DW_LNCT_DirectoryIndex:
|
|
{
|
|
entry_out->dir_idx = form_val.u128.u64[0];
|
|
}break;
|
|
case DW_LNCT_TimeStamp:
|
|
{
|
|
entry_out->modify_time = form_val.u128.u64[0];
|
|
entry_out->flags |= DW2_LineTableFileFlag_HasModifyTime;
|
|
}break;
|
|
case DW_LNCT_Size:
|
|
{
|
|
entry_out->file_size = form_val.u128.u64[0];
|
|
}break;
|
|
case DW_LNCT_MD5:
|
|
{
|
|
entry_out->md5.u128 = form_val.u128;
|
|
entry_out->flags |= DW2_LineTableFileFlag_HasMD5;
|
|
}break;
|
|
case DW_LNCT_LLVM_Source:
|
|
{
|
|
entry_out->source = form_val.string;
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// rjf: store
|
|
if(0){}
|
|
else if(do_files) { files = table; }
|
|
else if(do_dirs) { dirs = table; }
|
|
}
|
|
|
|
}break;
|
|
}
|
|
|
|
//////////////////////////////
|
|
//- rjf: fill output
|
|
//
|
|
if(out)
|
|
{
|
|
out->unit_length = unit_length;
|
|
out->format = format;
|
|
out->version = version;
|
|
out->addr_size = addr_size;
|
|
out->segment_selector_size = segment_selector_size;
|
|
out->header_length = header_length;
|
|
out->min_inst_length = min_inst_length;
|
|
out->max_ops_per_inst = max_ops_per_inst;
|
|
out->default_is_stmt = default_is_stmt;
|
|
out->line_base = line_base;
|
|
out->line_range = line_range;
|
|
out->opcode_base = opcode_base;
|
|
out->opcode_lengths_count = opcode_lengths_count;
|
|
out->opcode_lengths = opcode_lengths;
|
|
out->dirs = dirs;
|
|
out->files = files;
|
|
}
|
|
|
|
U64 bytes_read = (off - start_off);
|
|
scratch_end(scratch);
|
|
return bytes_read;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: String Offset Table Parsing (.debug_str_offsets)
|
|
|
|
internal U64
|
|
dw2_read_offset_table(String8 data, U64 off, DW2_OffsetTable *out)
|
|
{
|
|
U64 start_off = off;
|
|
{
|
|
// rjf: read data length / format
|
|
U64 unit_data_length = 0;
|
|
DW_Format format = DW_Format_Null;
|
|
off += dw2_read_initial_length(data, off, &unit_data_length, &format);
|
|
U64 unit_data_off_opl = off + unit_data_length;
|
|
|
|
// rjf: read version
|
|
DW_Version version = DW_Version_Null;
|
|
off += str8_deserial_read_struct(data, off, &version);
|
|
|
|
// rjf: version 5: read rest (this section only exists in 5+)
|
|
if(version == DW_Version_5)
|
|
{
|
|
// rjf: skip padding
|
|
off += sizeof(U16);
|
|
|
|
// rjf: fill table info
|
|
out->format = format;
|
|
out->version = version;
|
|
out->entry_size = dw_size_from_format(format);
|
|
out->entries_count = (unit_data_off_opl - off) / out->entry_size;
|
|
out->entries = data.str + off;
|
|
|
|
// rjf: skip table
|
|
off = unit_data_off_opl;
|
|
}
|
|
}
|
|
U64 bytes_read = (off - start_off);
|
|
return bytes_read;
|
|
}
|
|
|
|
internal B32
|
|
dw2_try_offset_from_table_idx(DW2_OffsetTable *tbl, U64 idx, U64 *out)
|
|
{
|
|
B32 result = 0;
|
|
if(idx < tbl->entries_count)
|
|
{
|
|
U64 entry_size = tbl->entry_size;
|
|
U64 entry_off = idx * entry_size;
|
|
MemoryCopy(out, (U8 *)tbl->entries + entry_off, entry_size);
|
|
result = 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Range List Parsing (.debug_rnglists)
|
|
|
|
internal Rng1U64List
|
|
dw2_rnglist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, DW_Raw *raw, DW2_FormVal form_val)
|
|
{
|
|
Rng1U64List result = {0};
|
|
switch((DW_VersionEnum)ctx->version)
|
|
{
|
|
case DW_Version_Null:{}break;
|
|
|
|
//- rjf: pre-dwarf5
|
|
case DW_Version_1:
|
|
case DW_Version_2:
|
|
case DW_Version_3:
|
|
case DW_Version_4:
|
|
{
|
|
String8 data = raw->sec[DW_Section_Ranges].data;
|
|
U64 ranges_off = ranges_off = form_val.u128.u64[0];;
|
|
U64 base_addr = ctx->unit_base_addr;
|
|
U64 sentinel = (ctx->addr_size == 4 ? max_U32 : max_U64);
|
|
for(U64 off = ranges_off; off < data.size;)
|
|
{
|
|
U64 start_off = off;
|
|
U64 range_min = 0;
|
|
U64 range_opl = 0;
|
|
off += str8_deserial_read(data, off, &range_min, ctx->addr_size, ctx->addr_size);
|
|
off += str8_deserial_read(data, off, &range_opl, ctx->addr_size, ctx->addr_size);
|
|
//
|
|
// NOTE(rjf): interpreting tuples:
|
|
// [0, 0) -> ending range list
|
|
// [max_U32/U64, depending on addr size, X) -> set new base address to X
|
|
// [N, M) -> new [base + N, base + M) range
|
|
//
|
|
if(range_min == 0 && range_opl == 0)
|
|
{
|
|
break;
|
|
}
|
|
else if(range_min == sentinel)
|
|
{
|
|
base_addr = range_opl;
|
|
}
|
|
else if(off == start_off)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
rng1u64_list_push(arena, &result, r1u64(base_addr + range_min, base_addr + range_opl));
|
|
}
|
|
}
|
|
}break;
|
|
|
|
//- rjf: @dwarf5
|
|
case DW_Version_5:
|
|
{
|
|
String8 data = raw->sec[DW_Section_RngLists].data;
|
|
|
|
// rjf: determine section offset - sometimes this is encoded directly,
|
|
// other times it is relative, based on the form kind - more variability
|
|
// in this awful format
|
|
U64 rnglist_off = 0;
|
|
switch(form_val.kind)
|
|
{
|
|
default:{}break;
|
|
case DW_Form_SecOffset:
|
|
{
|
|
rnglist_off = form_val.u128.u64[0];
|
|
}break;
|
|
case DW_Form_RngListx:
|
|
if(ctx->rnglists_table != 0)
|
|
{
|
|
U64 rnglist_off_idx = form_val.u128.u64[0];
|
|
dw2_try_offset_from_table_idx(ctx->rnglists_table, rnglist_off_idx, &rnglist_off);
|
|
}break;
|
|
}
|
|
|
|
// rjf: in a HIGHLY UNEXPECTED TURN OF EVENTS, we now have to decode ANOTHER
|
|
// OPCODE STREAM to unpack the actual list of ranges!!!
|
|
U64 rle_sentinel = (ctx->addr_size == 4 ? max_U32 : max_U64);
|
|
U64 base_addr = ctx->unit_base_addr;
|
|
for(U64 off = rnglist_off; off < data.size;)
|
|
{
|
|
U64 start_off = off;
|
|
|
|
// rjf: decode op kind
|
|
DW_RLE rle_kind = DW_RLE_EndOfList;
|
|
off += str8_deserial_read_struct(data, off, &rle_kind);
|
|
|
|
// rjf: obtain range from op
|
|
B32 good_range = 1;
|
|
Rng1U64 range = {0};
|
|
switch(rle_kind)
|
|
{
|
|
default:{good_range = 0;}break;
|
|
case DW_RLE_BaseAddressx:
|
|
{
|
|
good_range = 0;
|
|
U64 addr_idx = 0;
|
|
off += str8_deserial_read_uleb128(data, off, &addr_idx);
|
|
if(ctx->addr_table != 0)
|
|
{
|
|
U64 new_base_addr = 0;
|
|
if(dw2_try_offset_from_table_idx(ctx->addr_table, addr_idx, &new_base_addr))
|
|
{
|
|
base_addr = new_base_addr;
|
|
}
|
|
}
|
|
}break;
|
|
case DW_RLE_StartxLength:
|
|
{
|
|
good_range = 0;
|
|
U64 start_idx = 0;
|
|
U64 length = 0;
|
|
off += str8_deserial_read_uleb128(data, off, &start_idx);
|
|
off += str8_deserial_read_uleb128(data, off, &length);
|
|
if(ctx->addr_table != 0)
|
|
{
|
|
U64 start = 0;
|
|
if(dw2_try_offset_from_table_idx(ctx->addr_table, start_idx, &start))
|
|
{
|
|
good_range = 1;
|
|
range = r1u64(start, start+length);
|
|
}
|
|
}
|
|
}break;
|
|
case DW_RLE_OffsetPair:
|
|
{
|
|
U64 range_off_start = 0;
|
|
U64 range_off_end = 0;
|
|
off += str8_deserial_read_uleb128(data, off, &range_off_start);
|
|
off += str8_deserial_read_uleb128(data, off, &range_off_end);
|
|
range = r1u64(base_addr + range_off_start, base_addr + range_off_end);
|
|
}break;
|
|
case DW_RLE_BaseAddress:
|
|
{
|
|
U64 new_base_addr = 0;
|
|
off += str8_deserial_read(data, off, &new_base_addr, ctx->addr_size, ctx->addr_size);
|
|
base_addr = new_base_addr;
|
|
}break;
|
|
case DW_RLE_StartEnd:
|
|
{
|
|
U64 start = 0;
|
|
U64 end = 0;
|
|
off += str8_deserial_read(data, off, &start, ctx->addr_size, ctx->addr_size);
|
|
off += str8_deserial_read(data, off, &end, ctx->addr_size, ctx->addr_size);
|
|
range = r1u64(start, end);
|
|
}break;
|
|
case DW_RLE_StartLength:
|
|
{
|
|
U64 start = 0;
|
|
U64 length = 0;
|
|
off += str8_deserial_read(data, off, &start, ctx->addr_size, ctx->addr_size);
|
|
off += str8_deserial_read_uleb128(data, off, &length);
|
|
range = r1u64(start, start + length);
|
|
}break;
|
|
}
|
|
|
|
// rjf: add range
|
|
if(good_range)
|
|
{
|
|
rng1u64_list_push(arena, &result, range);
|
|
}
|
|
|
|
// rjf: end if no movement or end-of-list code
|
|
if(off == start_off || rle_kind == DW_RLE_EndOfList)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}break;
|
|
}
|
|
return result;
|
|
}
|