Files
raddebugger/src/dwarf/dwarf_parse_2.c
T

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;
}