simplified dwarf parsing checkpoint: pull out form value reading, line table header parsing

This commit is contained in:
Ryan Fleury
2025-11-11 16:27:16 -08:00
parent b8a8ec31c7
commit faee02f763
3 changed files with 536 additions and 160 deletions
+375 -71
View File
@@ -240,83 +240,16 @@ dw2_abbrev_map_from_data(Arena *arena, String8 data, U64 off)
} }
//////////////////////////////// ////////////////////////////////
//~ rjf: Tag Parsing //~ rjf: Form Value Parsing
internal U64 internal U64
dw2_read_tag(Arena *arena, DW2_TagParseCtx *ctx, String8 data, U64 off, DW2_Tag *tag_out) 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; U64 start_off = off;
DW2_FormVal val = {0};
// 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->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->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_AttribVal val = {0};
if(attrib_kind != 0)
{ {
U64 bytes_to_read = 0; U64 bytes_to_read = 0;
switch(attrib_form_kind) switch(form_kind)
{ {
//- rjf: no-ops //- rjf: no-ops
default: default:
@@ -457,6 +390,98 @@ dw2_read_tag(Arena *arena, DW2_TagParseCtx *ctx, String8 data, U64 off, DW2_Tag
}break; }break;
} }
} }
*out = val;
U64 bytes_read = (off - start_off);
return bytes_read;
}
internal String8
dw2_string_from_form_val(DW2_ParseCtx *ctx, DW2_FormVal val)
{
String8 section_data = ctx->raw->sec[val.section_kind].data;
Rng1U64 range = r1u64(val.u128.u64[0], val.u128.u64[1]);
String8 string = str8_substr(section_data, range);
return string;
}
////////////////////////////////
//~ 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 // rjf: push
if(attrib_kind != 0) if(attrib_kind != 0)
@@ -485,3 +510,282 @@ dw2_read_tag(Arena *arena, DW2_TagParseCtx *ctx, String8 data, U64 off, DW2_Tag
U64 bytes_read = (off - start_off); U64 bytes_read = (off - start_off);
return bytes_read; return bytes_read;
} }
////////////////////////////////
//~ 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 files = 0, dirs = 1; files <= 1; files = 1, 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]);
}
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 = dw2_string_from_form_val(ctx, form_val);
}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];
}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;
}break;
case DW_LNCT_LLVM_Source:
{
entry_out->source = dw2_string_from_form_val(ctx, form_val);
}break;
}
}
}
}
}
}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;
}
+80 -8
View File
@@ -37,23 +37,27 @@ struct DW2_AbbrevMap
}; };
//////////////////////////////// ////////////////////////////////
//~ rjf: Tag Parsing Context //~ rjf: Parsing Context Bundle
typedef struct DW2_TagParseCtx DW2_TagParseCtx; typedef struct DW2_ParseCtx DW2_ParseCtx;
struct DW2_TagParseCtx struct DW2_ParseCtx
{ {
DW_Raw *raw;
// NOTE(rjf): all subsequent fields optional - top-level code fills out whatever
// it can from context.
DW_Version version; DW_Version version;
DW_Format format; DW_Format format;
U64 addr_size; U64 addr_size;
String8 abbrev_data;
DW2_AbbrevMap *abbrev_map; DW2_AbbrevMap *abbrev_map;
String8 unit_dir;
String8 unit_file;
}; };
//////////////////////////////// ////////////////////////////////
//~ rjf: Tag Attributes //~ rjf: Tag Attributes
typedef struct DW2_AttribVal DW2_AttribVal; typedef struct DW2_FormVal DW2_FormVal;
struct DW2_AttribVal struct DW2_FormVal
{ {
DW_SectionKind section_kind; DW_SectionKind section_kind;
U128 u128; U128 u128;
@@ -64,7 +68,7 @@ struct DW2_Attrib
{ {
DW_AttribKind attrib_kind; DW_AttribKind attrib_kind;
DW_FormKind form_kind; DW_FormKind form_kind;
DW2_AttribVal val; DW2_FormVal val;
}; };
typedef struct DW2_AttribNode DW2_AttribNode; typedef struct DW2_AttribNode DW2_AttribNode;
@@ -93,6 +97,63 @@ struct DW2_Tag
DW2_AttribList attribs; DW2_AttribList attribs;
}; };
////////////////////////////////
//~ rjf: Line Info
typedef struct DW2_LineTableFile DW2_LineTableFile;
struct DW2_LineTableFile
{
String8 file_name;
U64 dir_idx;
U64 modify_time;
U64 file_size;
MD5 md5;
String8 source;
};
typedef struct DW2_LineTableFileNode DW2_LineTableFileNode;
struct DW2_LineTableFileNode
{
DW2_LineTableFileNode *next;
DW2_LineTableFile v;
};
typedef struct DW2_LineTableFileList DW2_LineTableFileList;
struct DW2_LineTableFileList
{
DW2_LineTableFileNode *first;
DW2_LineTableFileNode *last;
U64 count;
};
typedef struct DW2_LineTableFileArray DW2_LineTableFileArray;
struct DW2_LineTableFileArray
{
DW2_LineTableFile *v;
U64 count;
};
typedef struct DW2_LineTableHeader DW2_LineTableHeader;
struct DW2_LineTableHeader
{
U64 unit_length;
DW_Format format;
DW_Version version;
U8 addr_size;
U8 segment_selector_size;
U64 header_length;
U8 min_inst_length;
U8 max_ops_per_inst;
U8 default_is_stmt;
S8 line_base;
U8 line_range;
U8 opcode_base;
U64 opcode_lengths_count;
U8 *opcode_lengths;
DW2_LineTableFileArray dirs;
DW2_LineTableFileArray files;
};
//////////////////////////////// ////////////////////////////////
//~ rjf: Basic Parsing Helpers //~ rjf: Basic Parsing Helpers
@@ -109,9 +170,20 @@ internal U64 dw2_read_unit_header(String8 data, U64 off, DW2_UnitHeader *out);
internal DW2_AbbrevMap dw2_abbrev_map_from_data(Arena *arena, String8 data, U64 off); internal DW2_AbbrevMap dw2_abbrev_map_from_data(Arena *arena, String8 data, U64 off);
////////////////////////////////
//~ 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);
internal String8 dw2_string_from_form_val(DW2_ParseCtx *ctx, DW2_FormVal val);
//////////////////////////////// ////////////////////////////////
//~ rjf: Tag Parsing //~ rjf: Tag Parsing
internal U64 dw2_read_tag(Arena *arena, DW2_TagParseCtx *ctx, String8 data, U64 off, DW2_Tag *tag_out); internal U64 dw2_read_tag(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_Tag *tag_out);
////////////////////////////////
//~ rjf: Line Table Parsing
internal U64 dw2_read_line_table_header(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_LineTableHeader *out);
#endif // DWARF_PARSE_2_H #endif // DWARF_PARSE_2_H
+7 -7
View File
@@ -257,24 +257,24 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
lane_sync(); lane_sync();
//////////////////////////// ////////////////////////////
//- rjf: build per-unit tag parsing contexts //- rjf: build per-unit parsing contexts
// //
DW2_TagParseCtx *unit_tag_parse_ctxs = 0; DW2_ParseCtx *unit_parse_ctxs = 0;
{ {
if(lane_idx() == 0) if(lane_idx() == 0)
{ {
unit_tag_parse_ctxs = push_array(scratch.arena, DW2_TagParseCtx, unit_count); unit_parse_ctxs = push_array(scratch.arena, DW2_ParseCtx, unit_count);
} }
lane_sync_u64(&unit_tag_parse_ctxs, 0); lane_sync_u64(&unit_parse_ctxs, 0);
Rng1U64 range = lane_range(unit_count); Rng1U64 range = lane_range(unit_count);
for EachInRange(unit_idx, range) for EachInRange(unit_idx, range)
{ {
DW2_UnitHeader *hdr = &unit_headers[unit_idx]; DW2_UnitHeader *hdr = &unit_headers[unit_idx];
DW2_TagParseCtx *ctx = &unit_tag_parse_ctxs[unit_idx]; DW2_ParseCtx *ctx = &unit_parse_ctxs[unit_idx];
ctx->raw = raw;
ctx->version = hdr->version; ctx->version = hdr->version;
ctx->format = hdr->format; ctx->format = hdr->format;
ctx->addr_size = hdr->addr_size; ctx->addr_size = hdr->addr_size;
ctx->abbrev_data = raw->sec[DW_Section_Abbrev].data;
ctx->abbrev_map = abbrev_map_from_unit_idx_table[unit_idx]; ctx->abbrev_map = abbrev_map_from_unit_idx_table[unit_idx];
} }
} }
@@ -293,7 +293,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
Rng1U64 range = lane_range(unit_count); Rng1U64 range = lane_range(unit_count);
for EachInRange(unit_idx, range) for EachInRange(unit_idx, range)
{ {
dw2_read_tag(scratch.arena, &unit_tag_parse_ctxs[unit_idx], raw->sec[DW_Section_Info].data, unit_info_tag_ranges[unit_idx].min, &unit_root_tags[unit_idx]); dw2_read_tag(scratch.arena, &unit_parse_ctxs[unit_idx], raw->sec[DW_Section_Info].data, unit_info_tag_ranges[unit_idx].min, &unit_root_tags[unit_idx]);
} }
} }
lane_sync(); lane_sync();