d2r2: .debug_aranges parsing / unit conversion; .debug_ranges parsing

This commit is contained in:
Ryan Fleury
2026-04-28 08:57:59 -07:00
parent f1a9fc5e3a
commit f5ec462438
10 changed files with 362 additions and 51 deletions
+1 -1
View File
@@ -339,9 +339,9 @@ struct Rng1U64Node
typedef struct Rng1U64List Rng1U64List;
struct Rng1U64List
{
U64 count;
Rng1U64Node *first;
Rng1U64Node *last;
U64 count;
};
typedef struct Rng1U64Array Rng1U64Array;
+1 -1
View File
@@ -8,7 +8,7 @@ typedef struct DW_Section
{
String8 name;
String8 data;
B32 is_dwo;
B32 is_dwo;
} DW_Section;
typedef struct DW_Raw
+62
View File
@@ -900,3 +900,65 @@ dw2_read_str_offsets_table(String8 data, U64 off, DW2_StrOffsetsTable *out)
U64 bytes_read = (off - start_off);
return bytes_read;
}
////////////////////////////////
//~ 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:
{
// TODO(rjf)
}break;
}
return result;
}
+6
View File
@@ -62,6 +62,7 @@ struct DW2_ParseCtx
DW_Format format;
DW_Ext ext;
U64 addr_size;
U64 unit_base_addr;
DW2_AbbrevMap *abbrev_map;
DW2_StrOffsetsTable *str_offsets_table;
String8 unit_dir;
@@ -237,4 +238,9 @@ internal U64 dw2_read_line_table_header(Arena *arena, DW2_ParseCtx *ctx, String8
internal U64 dw2_read_str_offsets_table(String8 data, U64 off, DW2_StrOffsetsTable *out);
////////////////////////////////
//~ 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);
#endif // DWARF_PARSE_2_H
+9 -9
View File
@@ -10,22 +10,22 @@
typedef struct ELF_Shdr64Array ELF_Shdr64Array;
struct ELF_Shdr64Array
{
U64 count;
U64 count;
ELF_Shdr64 *v;
};
typedef struct ELF_Phdr64Array ELF_Phdr64Array;
struct ELF_Phdr64Array
{
U64 count;
U64 count;
ELF_Phdr64 *v;
};
typedef struct ELF_Bin ELF_Bin;
struct ELF_Bin
{
ELF_Hdr64 hdr;
Rng1U64 sh_name_range;
ELF_Hdr64 hdr;
Rng1U64 sh_name_range;
ELF_Shdr64Array shdrs;
ELF_Phdr64Array phdrs;
};
@@ -34,30 +34,30 @@ typedef struct ELF_GnuDebugLink ELF_GnuDebugLink;
struct ELF_GnuDebugLink
{
String8 path;
U32 checksum;
U32 checksum;
};
typedef struct ELF_Note ELF_Note;
struct ELF_Note
{
String8 owner;
String8 owner;
ELF_NoteType type;
String8 desc;
String8 desc;
};
typedef struct ELF_NoteNode ELF_NoteNode;
struct ELF_NoteNode
{
ELF_Note v;
ELF_NoteNode *next;
ELF_Note v;
};
typedef struct ELF_NoteList ELF_NoteList;
struct ELF_NoteList
{
U64 count;
ELF_NoteNode *first;
ELF_NoteNode *last;
U64 count;
};
////////////////////////////////
+5 -5
View File
@@ -872,9 +872,9 @@ rb_thread_entry_point(void *p)
convert_params_2.base_vaddr = elf_base_addr_from_bin(&bin);
convert_params_2.raw = dw_input_from_elf_bin(scratch.arena, dbg_data, &bin);
convert_params_2.path_style = PathStyle_UnixAbsolute;
convert_params_2.binary_sections = e2r_rdi_binary_sections_from_elf_section_table(arena, bin.shdrs);
convert_params_2.binary_sections = e2r_rdi_binary_sections_from_elf_section_table(arena, dbg_data, &bin, &bin.shdrs);
scratch_end(scratch);
} break;
}break;
}
convert_params_2.exe_name = exe_name;
convert_params_2.exe_data = exe_data;
@@ -956,8 +956,8 @@ rb_thread_entry_point(void *p)
{
bake_params = push_array(arena, RDIM_BakeParams, 1);
rdim_bake_params_concat_in_place(bake_params, &pdb_bake_params);
rdim_bake_params_concat_in_place(bake_params, &dwarf_bake_params);
// rdim_bake_params_concat_in_place(bake_params, &dwarf_bake_params_2);
// rdim_bake_params_concat_in_place(bake_params, &dwarf_bake_params);
rdim_bake_params_concat_in_place(bake_params, &dwarf_bake_params_2);
for EachNode(n, RDIM_BakeParamsNode, first_rdi_bake_params)
{
rdim_bake_params_concat_in_place(bake_params, &n->v);
@@ -1404,7 +1404,7 @@ rb_thread_entry_point(void *p)
PE_BinInfo pe = {0};
ELF_Bin elf = {0};
COFF_FileHeaderInfo coff_obj = {0};
DW_Raw dw = {0};
DW_Raw dw = {0};
U64 eh_frame_hdr_vaddr = 0;
U64 eh_frame_vaddr = 0;
String8 eh_frame_hdr = {0};
+1 -1
View File
@@ -2753,7 +2753,7 @@ d2r_convert(Arena *arena, D2R_ConvertParams *params)
input = dw_input_from_elf_bin(scratch.arena, params->dbg_data, &bin);
path_style = PathStyle_UnixAbsolute;
g_d2r_shared.binary_sections = e2r_rdi_binary_sections_from_elf_section_table(arena, bin.shdrs);
g_d2r_shared.binary_sections = e2r_rdi_binary_sections_from_elf_section_table(arena, params->dbg_data, &bin, &bin.shdrs);
} break;
default: { InvalidPath; } break;
}
+248 -29
View File
@@ -78,47 +78,62 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
}
////////////////////////////
//- rjf: gather unit ranges from .debug_info
//- rjf: gather unit ranges from .debug_info, .debug_aranges
//
Rng1U64Array *unit_info_ranges = 0;
ProfScope("gather unit ranges from .debug_info") if(lane_idx() == 0)
Rng1U64Array *unit_arange_ranges = 0;
ProfScope("gather unit ranges from .debug_info, .debug_aranges") if(lane_idx() == 0)
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
String8 data = raw->sec[DW_Section_Info].data;
Rng1U64List unit_info_ranges_list = {0};
for(U64 off = 0; off < data.size;)
struct
{
U64 start_off = off;
String8 data;
Rng1U64Array **dst_array;
}
tasks[] =
{
{raw->sec[DW_Section_Info].data, &unit_info_ranges},
{raw->sec[DW_Section_ARanges].data, &unit_arange_ranges},
};
for EachElement(task_idx, tasks)
{
Temp scratch2 = scratch_begin(&scratch.arena, 1);
String8 data = tasks[task_idx].data;
Rng1U64List unit_range_list = {0};
for(U64 off = 0; off < data.size;)
{
U64 start_off = off;
// rjf: read next unit info size
U64 unit_info_size = 0;
U64 unit_info_size_size = dw2_read_initial_length(data, off, &unit_info_size, 0);
// rjf: read next unit size
U64 unit_size = 0;
U64 unit_size_size = dw2_read_initial_length(data, off, &unit_size, 0);
// rjf: push
if(unit_info_size > 0)
{
rng1u64_list_push(scratch2.arena, &unit_info_ranges_list, r1u64(off, off + unit_info_size_size + unit_info_size));
}
// rjf: push
if(unit_size > 0)
{
rng1u64_list_push(scratch2.arena, &unit_range_list, r1u64(off, off + unit_size_size + unit_size));
}
// rjf: advance
off += unit_info_size_size;
off += unit_info_size;
// rjf: advance
off += unit_size_size;
off += unit_size;
// rjf: break if no movement
if(off == start_off)
{
break;
// rjf: break if no movement
if(off == start_off)
{
break;
}
}
tasks[task_idx].dst_array[0] = push_array(scratch.arena, Rng1U64Array, 1);
tasks[task_idx].dst_array[0][0] = rng1u64_array_from_list(scratch.arena, &unit_range_list);
scratch_end(scratch2);
}
unit_info_ranges = push_array(scratch.arena, Rng1U64Array, 1);
unit_info_ranges[0] = rng1u64_array_from_list(scratch.arena, &unit_info_ranges_list);
scratch_end(scratch2);
}
lane_sync_u64(&unit_info_ranges, 0);
lane_sync_u64(&unit_arange_ranges, 0);
U64 unit_count = unit_info_ranges->count;
////////////////////////////
//- rjf: parse all unit headers
//- rjf: parse all .debug_info unit headers
//
DW2_UnitHeader *unit_headers = 0;
Rng1U64 *unit_info_tag_ranges = 0;
@@ -145,6 +160,148 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
}
lane_sync();
////////////////////////////
//- rjf: parse all units from .debug_aranges
//
U64 *arange_info_offs = 0;
RDIM_Rng1U64ChunkList *arange_voff_ranges = 0;
{
if(lane_idx() == 0)
{
arange_info_offs = push_array(scratch.arena, U64, unit_arange_ranges->count);
arange_voff_ranges = push_array(scratch.arena, RDIM_Rng1U64ChunkList, unit_arange_ranges->count);
}
lane_sync_u64(&arange_info_offs, 0);
lane_sync_u64(&arange_voff_ranges, 0);
U64 unit_take_idx_ = 0;
U64 *unit_take_idx_ptr = &unit_take_idx_;
lane_sync_u64(&unit_take_idx_ptr, 0);
String8 data = raw->sec[DW_Section_ARanges].data;
for(;;)
{
// rjf: take unit
U64 unit_idx = ins_atomic_u64_inc_eval(unit_take_idx_ptr)-1;
if(unit_idx >= unit_arange_ranges->count)
{
break;
}
// rjf: unpack
Rng1U64 arange_range = unit_arange_ranges->v[unit_idx];
U64 off = arange_range.min;
// rjf: read unit data size / format
U64 unit_size = 0;
DW_Format fmt = 0;
off += dw2_read_initial_length(data, off, &unit_size, &fmt);
U64 unit_opl = off + unit_size;
// rjf: read version
DW_Version version = 0;
U64 version_off = off;
off += str8_deserial_read_struct(data, off, &version);
// rjf: warn on non-version 2
if(version != DW_Version_2)
{
log_infof("[.debug_aranges@0x%I64x] DWARF version for unit #%I64d was expected to be 2, but it was read as %i.\n", version_off, unit_idx, (S32)version);
}
// rjf: read .debug_info off for this unit
U64 info_off = 0;
off += dw2_read_fmt_u64(data, off, fmt, &info_off);
// rjf: read address / segment selector size
U8 addr_size = 0;
U8 segment_selector_size = 0;
off += str8_deserial_read_struct(data, off, &addr_size);
off += str8_deserial_read_struct(data, off, &segment_selector_size);
// rjf: round up past padding
{
U64 tuple_size = addr_size*2 + segment_selector_size;
off += tuple_size - (off%tuple_size);
}
// rjf: parse ranges
RDIM_Rng1U64ChunkList voff_ranges = {0};
if(segment_selector_size != 0)
{
log_infof("[.debug_aranges@0x%I64x] Non-zero (%i) segment selector size parsed; this form of addressing is not currently supported in DWARF info.\n", off, (S32)segment_selector_size);
}
else for(;off < unit_opl;)
{
U64 start_off = off;
U64 base_addr = 0;
U64 range_size = 0;
off += str8_deserial_read(data, off, &base_addr, addr_size, addr_size);
off += str8_deserial_read(data, off, &range_size, addr_size, addr_size);
if(base_addr == 0 && range_size == 0)
{
break;
}
if(base_addr < base_vaddr)
{
log_infof("[.debug_aranges@0x%I64x] Address (0x%I64x) parsed which was less than the image base address (0x%I64x). Skipping.\n", start_off, base_addr, base_vaddr);
}
else
{
U64 voff_first = (base_addr - base_vaddr);
U64 voff_opl = voff_first + range_size;
RDIM_Rng1U64 range = {voff_first, voff_opl};
rdim_rng1u64_chunk_list_push(scratch.arena, &voff_ranges, 256, range);
}
if(off == start_off)
{
break;
}
}
// rjf: store
arange_info_offs[unit_idx] = info_off;
arange_voff_ranges[unit_idx] = voff_ranges;
}
lane_sync();
}
////////////////////////////
//- rjf: produce info_off -> list(voff_range) map from aranges units;
// we must do this because technically we can't guarantee that unit_idxs
// inside of .debug_aranges are the same as unit_idxs inside of .debug_info,
// nor can we guarantee that they'd be in the same order, so we need to
// correllate via the encoded .debug_info offset from .debug_aranges.
//
// more excellence.
//
typedef struct D2R2_ARangeUnitNode D2R2_ARangeUnitNode;
struct D2R2_ARangeUnitNode
{
D2R2_ARangeUnitNode *next;
U64 info_off;
RDIM_Rng1U64ChunkList *ranges;
};
U64 arange_unit_from_info_off_map_slots_count = unit_arange_ranges->count;
D2R2_ARangeUnitNode **arange_unit_from_info_off_map_slots = 0;
{
if(lane_idx() == 0)
{
arange_unit_from_info_off_map_slots = push_array(scratch.arena, D2R2_ARangeUnitNode *, arange_unit_from_info_off_map_slots_count);
}
lane_sync_u64(&arange_unit_from_info_off_map_slots, 0);
for EachIndex(arange_unit_idx, unit_arange_ranges->count)
{
U64 info_off = arange_info_offs[arange_unit_idx];
RDIM_Rng1U64ChunkList *ranges = &arange_voff_ranges[arange_unit_idx];
U64 hash = u64_hash_from_str8(str8_struct(&info_off));
U64 slot_idx = hash%arange_unit_from_info_off_map_slots_count;
D2R2_ARangeUnitNode *n = push_array(scratch.arena, D2R2_ARangeUnitNode, 1);
SLLStackPush(arange_unit_from_info_off_map_slots[slot_idx], n);
n->info_off = info_off;
n->ranges = ranges;
}
}
lane_sync();
////////////////////////////
//- rjf: build all abbreviation maps, build (unit -> abbrev map)
//
@@ -389,6 +546,21 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
}
lane_sync();
////////////////////////////
//- rjf: look up base addresses for each unit - equip to per-unit parsing contexts
//
{
Rng1U64 range = lane_range(unit_count);
for EachInRange(unit_idx, range)
{
DW2_Tag *unit_root_tag = &unit_root_tags__pre_str_offsets[unit_idx];
DW2_Attrib *low_pc_attrib = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_LowPc);
U64 low_pc = low_pc_attrib->val.u128.u64[0];
unit_parse_ctxs[unit_idx].unit_base_addr = low_pc;
}
}
lane_sync();
////////////////////////////
//- rjf: look up string offset tables for each unit (.debug_str_offsets);
// equip to per-unit parsing contexts
@@ -1836,7 +2008,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
{
if(dw_is_form_kind_ref(unit_parse_ctx->version, unit_parse_ctx->ext, lower_bound_attrib->val.kind))
{
log_infof("[.debug_info@%I64x] Array type lower bound is a variable; this is not currently supported.\n", start_off);
log_infof("[.debug_info@0x%I64x] Array type lower bound is a variable; this is not currently supported.\n", start_off);
}
else
{
@@ -1855,7 +2027,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
{
if(dw_is_form_kind_ref(unit_parse_ctx->version, unit_parse_ctx->ext, upper_bound_attrib->val.kind))
{
log_infof("[.debug_info@%I64x] Array type upper bound is a variable; this is not currently supported.\n", start_off);
log_infof("[.debug_info@0x%I64x] Array type upper bound is a variable; this is not currently supported.\n", start_off);
}
else
{
@@ -1993,6 +2165,9 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
DW2_Attrib *comp_dir_attrib = &dw2_attrib_nil;
DW2_Attrib *producer_attrib = &dw2_attrib_nil;
DW2_Attrib *lang_attrib = &dw2_attrib_nil;
DW2_Attrib *ranges_attrib = &dw2_attrib_nil;
DW2_Attrib *lopc_attrib = &dw2_attrib_nil;
DW2_Attrib *hipc_attrib = &dw2_attrib_nil;
for EachNode(n, DW2_AttribNode, unit_root_tag->attribs.first)
{
switch(n->v.attrib_kind)
@@ -2003,6 +2178,9 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
Case(comp_dir, CompDir);
Case(producer, Producer);
Case(lang, Language);
Case(ranges, Ranges);
Case(lopc, LowPc);
Case(hipc, HighPc);
#undef Case
}
}
@@ -2034,6 +2212,46 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
}
}
//- rjf: get unit's ranges from .debug_aranges parse artifacts, if we have them
RDIM_Rng1U64ChunkList unit_voff_ranges = {0};
if(arange_unit_from_info_off_map_slots_count != 0)
{
U64 unit_info_off = unit_info_ranges->v[unit_idx].min;
U64 hash = u64_hash_from_str8(str8_struct(&unit_info_off));
U64 slot_idx = hash%arange_unit_from_info_off_map_slots_count;
for(D2R2_ARangeUnitNode *n = arange_unit_from_info_off_map_slots[slot_idx]; n != 0; n = n->next)
{
if(n->info_off == unit_info_off)
{
unit_voff_ranges = n->ranges[0];
break;
}
}
}
//- rjf: if we have no voff ranges from .debug_aranges, then we need to extract
// this info from the unit root rag instead (via ranges & low-pc/high-pc tags)
if(unit_voff_ranges.total_count == 0)
{
// rjf: gather ranges from a ranges attribute
if(ranges_attrib != &dw2_attrib_nil)
{
Rng1U64List ranges = dw2_rnglist_from_form_val(scratch.arena, unit_parse_ctx, raw, ranges_attrib->val);
for EachNode(n, Rng1U64Node, ranges.first)
{
rdim_rng1u64_chunk_list_push(arena, &unit_voff_ranges, 256, (RDIM_Rng1U64){n->v.min - base_vaddr, n->v.max - base_vaddr});
}
}
// rjf: gather contiguous range from low-pc / high-pc attribute
if(lopc_attrib != &dw2_attrib_nil && hipc_attrib != &dw2_attrib_nil)
{
U64 voff_base = lopc_attrib->val.u128.u64[0] - base_vaddr;
U64 voff_opl = hipc_attrib->val.u128.u64[0] - base_vaddr;
rdim_rng1u64_chunk_list_push(arena, &unit_voff_ranges, 256, (RDIM_Rng1U64){voff_base, voff_opl});
}
}
//- rjf: fill top-level unit info
{
dst_unit->unit_name = unit_name;
@@ -2044,6 +2262,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
dst_unit->build_path = unit_comp_dir;
dst_unit->language = unit_lang;
dst_unit->line_table = unit_line_tables[unit_idx];
dst_unit->voff_ranges = unit_voff_ranges;
}
//- rjf: produce all unit symbols
@@ -2119,7 +2338,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params)
RDIM_BakeParams result = {0};
{
result.subset_flags = params->subset_flags;
// TODO(rjf): result.top_level_info = *top_level_info;
result.top_level_info = top_level_info;
// TODO(rjf): result.binary_sections = *binary_sections;
result.units = *all_units;
result.types = *all_types;
+25 -1
View File
@@ -2,8 +2,32 @@
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal RDIM_BinarySectionList
e2r_rdi_binary_sections_from_elf_section_table(Arena *arena, ELF_Shdr64Array shdrs)
e2r_rdi_binary_sections_from_elf_section_table(Arena *arena, String8 data, ELF_Bin *bin, ELF_Shdr64Array *shdrs)
{
RDIM_BinarySectionList result = {0};
for EachIndex(idx, shdrs->count)
{
// rjf: unpack section
ELF_Shdr64 *src_section = &shdrs->v[idx];
String8 name = elf_name_from_shdr64(data, bin, src_section);
U64 voff_first = src_section->sh_addr;
U64 voff_opl = voff_first + src_section->sh_size;
U64 foff_first = src_section->sh_offset;
U64 foff_opl = foff_first + src_section->sh_size;
// rjf: map flags -> rdi
RDI_BinarySectionFlags flags = RDI_BinarySectionFlag_Read;
if(src_section->sh_flags & ELF_Shf_Write) { flags |= RDI_BinarySectionFlag_Write; }
if(src_section->sh_flags & ELF_Shf_ExecInstr) { flags |= RDI_BinarySectionFlag_Execute; }
// rjf: make rdi section
RDIM_BinarySection *dst_section = rdim_binary_section_list_push(arena, &result);
dst_section->name = name;
dst_section->flags = flags;
dst_section->voff_first = voff_first;
dst_section->voff_opl = voff_opl;
dst_section->foff_first = foff_first;
dst_section->foff_opl = foff_opl;
}
return result;
}
+1 -1
View File
@@ -4,6 +4,6 @@
#ifndef RDI_FROM_ELF_H
#define RDI_FROM_ELF_H
internal RDIM_BinarySectionList e2r_rdi_binary_sections_from_elf_section_table(Arena *arena, ELF_Shdr64Array shdrs);
internal RDIM_BinarySectionList e2r_rdi_binary_sections_from_elf_section_table(Arena *arena, String8 data, ELF_Bin *bin, ELF_Shdr64Array *shdrs);
#endif // RDI_FROM_ELF_H