diff --git a/src/dwarf/dwarf_parse_2.c b/src/dwarf/dwarf_parse_2.c index ea5572ef..0f7d0068 100644 --- a/src/dwarf/dwarf_parse_2.c +++ b/src/dwarf/dwarf_parse_2.c @@ -402,12 +402,9 @@ dw2_read_form_val(DW2_ParseCtx *ctx, String8 data, U64 off, DW_FormKind form_kin if(ctx->str_offsets_table != 0) { U64 entry_idx = val.u128.u64[0]; - if(entry_idx < ctx->str_offsets_table->entries_count) + U64 string_data_off = 0; + if(dw2_try_offset_from_table_idx(ctx->str_offsets_table, entry_idx, &string_data_off)) { - U64 entry_size = ctx->str_offsets_table->entry_size; - U64 entry_off = entry_idx * entry_size; - U64 string_data_off = 0; - MemoryCopy(&string_data_off, (U8 *)ctx->str_offsets_table->entries + entry_off, entry_size); 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); @@ -866,7 +863,7 @@ dw2_read_line_table_header(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 of //~ rjf: String Offset Table Parsing (.debug_str_offsets) internal U64 -dw2_read_str_offsets_table(String8 data, U64 off, DW2_StrOffsetsTable *out) +dw2_read_offset_table(String8 data, U64 off, DW2_OffsetTable *out) { U64 start_off = off; { @@ -901,6 +898,20 @@ dw2_read_str_offsets_table(String8 data, U64 off, DW2_StrOffsetsTable *out) 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) @@ -957,7 +968,120 @@ dw2_rnglist_from_form_val(Arena *arena, DW2_ParseCtx *ctx, DW_Raw *raw, DW2_Form //- rjf: @dwarf5 case DW_Version_5: { - // TODO(rjf) + 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; diff --git a/src/dwarf/dwarf_parse_2.h b/src/dwarf/dwarf_parse_2.h index 8b86ee68..be200e96 100644 --- a/src/dwarf/dwarf_parse_2.h +++ b/src/dwarf/dwarf_parse_2.h @@ -37,10 +37,10 @@ struct DW2_AbbrevMap }; //////////////////////////////// -//~ rjf: String Offset Table (.debug_str_offsets) +//~ rjf: Offset Tables (.debug_str_offsets, .debug_rnglists) -typedef struct DW2_StrOffsetsTable DW2_StrOffsetsTable; -struct DW2_StrOffsetsTable +typedef struct DW2_OffsetTable DW2_OffsetTable; +struct DW2_OffsetTable { DW_Format format; DW_Version version; @@ -64,7 +64,9 @@ struct DW2_ParseCtx U64 addr_size; U64 unit_base_addr; DW2_AbbrevMap *abbrev_map; - DW2_StrOffsetsTable *str_offsets_table; + DW2_OffsetTable *rnglists_table; + DW2_OffsetTable *str_offsets_table; + DW2_OffsetTable *addr_table; String8 unit_dir; String8 unit_file; }; @@ -234,9 +236,10 @@ internal U64 dw2_reference_info_off_from_form_val(DW2_ParseCtx *ctx, DW2_FormVal internal U64 dw2_read_line_table_header(Arena *arena, DW2_ParseCtx *ctx, String8 data, U64 off, DW2_LineTableHeader *out); //////////////////////////////// -//~ rjf: String Offset Table Parsing (.debug_str_offsets) +//~ rjf: Offset Table Parsing (.debug_str_offsets, .debug_rnglists) -internal U64 dw2_read_str_offsets_table(String8 data, U64 off, DW2_StrOffsetsTable *out); +internal U64 dw2_read_offset_table(String8 data, U64 off, DW2_OffsetTable *out); +internal B32 dw2_try_offset_from_table_idx(DW2_OffsetTable *tbl, U64 idx, U64 *out); //////////////////////////////// //~ rjf: Range List Parsing (.debug_rnglists) diff --git a/src/rdi_from_dwarf/rdi_from_dwarf_2.c b/src/rdi_from_dwarf/rdi_from_dwarf_2.c index 20e890db..a426454f 100644 --- a/src/rdi_from_dwarf/rdi_from_dwarf_2.c +++ b/src/rdi_from_dwarf/rdi_from_dwarf_2.c @@ -416,7 +416,9 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) lane_sync(); //////////////////////////// - //- rjf: parse all string offsets tables + //- rjf: parse all unit offsets tables + // + // on .debug_str_offsets, as one example: // // in an incredible twist of fate, DWARF decided to decouple these from // compilation units. compilation units *do* contain a @@ -439,61 +441,90 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // thank you, again, DWARF. // U64 str_offsets_tables_count = 0; - DW2_StrOffsetsTable *str_offsets_tables = 0; + DW2_OffsetTable *str_offsets_tables = 0; Rng1U64 *str_offsets_tables_ranges = 0; - ProfScope("parse all string offsets tables") if(lane_idx() == 0) + U64 rnglists_tables_count = 0; + DW2_OffsetTable *rnglists_tables = 0; + Rng1U64 *rnglists_tables_ranges = 0; + U64 addr_tables_count = 0; + DW2_OffsetTable *addr_tables = 0; + Rng1U64 *addr_tables_ranges = 0; + ProfScope("parse all offset tables (.debug_rnglists, .debug_str_offsets, .debug_addr)") if(lane_idx() == 0) { - Temp scratch2 = scratch_begin(&scratch.arena, 1); - - //- rjf: gather all tables (loose) - typedef struct TableNode TableNode; - struct TableNode + struct { - TableNode *next; - DW2_StrOffsetsTable v; - Rng1U64 range; + String8 data; + U64 *tables_count_out; + DW2_OffsetTable **tables_out; + Rng1U64 **tables_ranges_out; + } + tasks[] = + { + {raw->sec[DW_Section_StrOffsets].data, &str_offsets_tables_count, &str_offsets_tables, &str_offsets_tables_ranges}, + {raw->sec[DW_Section_RngLists].data, &rnglists_tables_count, &rnglists_tables, &rnglists_tables_ranges}, + {raw->sec[DW_Section_Addr].data, &addr_tables_count, &addr_tables, &addr_tables_ranges}, }; - TableNode *first_table = 0; - TableNode *last_table = 0; - U64 table_count = 0; - for(U64 off = 0; off < raw->sec[DW_Section_StrOffsets].data.size;) + for EachElement(task_idx, tasks) { - U64 start_off = off; - DW2_StrOffsetsTable table = {0}; - off += dw2_read_str_offsets_table(raw->sec[DW_Section_StrOffsets].data, off, &table); - if(table.entries != 0) + Temp scratch2 = scratch_begin(&scratch.arena, 1); + String8 data = tasks[task_idx].data; + + //- rjf: gather all tables (loose) + typedef struct TableNode TableNode; + struct TableNode { - TableNode *n = push_array(scratch2.arena, TableNode, 1); - SLLQueuePush(first_table, last_table, n); - n->v = table; - n->range = r1u64(start_off, off); - table_count += 1; - } - if(off == start_off) + TableNode *next; + DW2_OffsetTable v; + Rng1U64 range; + }; + TableNode *first_table = 0; + TableNode *last_table = 0; + U64 table_count = 0; + for(U64 off = 0; off < data.size;) { - break; + U64 start_off = off; + DW2_OffsetTable table = {0}; + off += dw2_read_offset_table(data, off, &table); + if(table.entries != 0) + { + TableNode *n = push_array(scratch2.arena, TableNode, 1); + SLLQueuePush(first_table, last_table, n); + n->v = table; + n->range = r1u64(start_off, off); + table_count += 1; + } + if(off == start_off) + { + break; + } } + + //- rjf: tighten + tasks[task_idx].tables_count_out[0] = table_count; + tasks[task_idx].tables_out[0] = push_array(scratch.arena, DW2_OffsetTable, table_count); + tasks[task_idx].tables_ranges_out[0] = push_array(scratch.arena, Rng1U64, table_count); + { + U64 idx = 0; + for EachNode(n, TableNode, first_table) + { + tasks[task_idx].tables_out[0][idx] = n->v; + tasks[task_idx].tables_ranges_out[0][idx] = n->range; + idx += 1; + } + } + + scratch_end(scratch2); } - - //- rjf: tighten - str_offsets_tables_count = table_count; - str_offsets_tables = push_array(scratch.arena, DW2_StrOffsetsTable, str_offsets_tables_count); - str_offsets_tables_ranges = push_array(scratch.arena, Rng1U64, str_offsets_tables_count); - { - U64 idx = 0; - for EachNode(n, TableNode, first_table) - { - str_offsets_tables[idx] = n->v; - str_offsets_tables_ranges[idx] = n->range; - idx += 1; - } - } - - scratch_end(scratch2); } lane_sync_u64(&str_offsets_tables_count, 0); lane_sync_u64(&str_offsets_tables, 0); lane_sync_u64(&str_offsets_tables_ranges, 0); + lane_sync_u64(&rnglists_tables_count, 0); + lane_sync_u64(&rnglists_tables, 0); + lane_sync_u64(&rnglists_tables_ranges, 0); + lane_sync_u64(&addr_tables_count, 0); + lane_sync_u64(&addr_tables, 0); + lane_sync_u64(&addr_tables_ranges, 0); //////////////////////////// //- rjf: build per-unit parsing contexts @@ -531,17 +562,19 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) // so, in another incredible twist of fate, we actually must do this *twice*. first, // to find *just the StrOffsetsBase*, and then, to actually fully resolve everything. // - DW2_Tag *unit_root_tags__pre_str_offsets = 0; + // (all of the above is also true for RngListsBase) + // + DW2_Tag *unit_root_tags__pre_offset_tables = 0; { if(lane_idx() == 0) { - unit_root_tags__pre_str_offsets = push_array(scratch.arena, DW2_Tag, unit_count); + unit_root_tags__pre_offset_tables = push_array(scratch.arena, DW2_Tag, unit_count); } - lane_sync_u64(&unit_root_tags__pre_str_offsets, 0); + lane_sync_u64(&unit_root_tags__pre_offset_tables, 0); Rng1U64 range = lane_range(unit_count); for EachInRange(unit_idx, range) { - 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__pre_str_offsets[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__pre_offset_tables[unit_idx]); } } lane_sync(); @@ -553,7 +586,7 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) 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_Tag *unit_root_tag = &unit_root_tags__pre_offset_tables[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; @@ -562,22 +595,52 @@ d2r2_convert(Arena *arena, D2R2_ConvertParams *params) lane_sync(); //////////////////////////// - //- rjf: look up string offset tables for each unit (.debug_str_offsets); + //- rjf: look up offset tables for each unit (.debug_str_offsets, .debug_rnglists, etc.); // equip to per-unit parsing contexts // { + Rng1U64Array rnglists_tables_ranges_array = {rnglists_tables_ranges, rnglists_tables_count}; Rng1U64Array str_offsets_tables_ranges_array = {str_offsets_tables_ranges, str_offsets_tables_count}; + Rng1U64Array addr_tables_ranges_array = {addr_tables_ranges, addr_tables_count}; 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 *str_offsets_base_off_attrib = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_StrOffsetsBase); - U64 str_offsets_base_off = str_offsets_base_off_attrib->val.u128.u64[0]; - U64 str_offsets_table_num = rng1u64_array_num_from_value__binary_search(&str_offsets_tables_ranges_array, str_offsets_base_off); - if(0 < str_offsets_table_num && str_offsets_table_num <= str_offsets_tables_ranges_array.count) + DW2_Tag *unit_root_tag = &unit_root_tags__pre_offset_tables[unit_idx]; + + // rjf: find rnglists table { - DW2_StrOffsetsTable *table = &str_offsets_tables[str_offsets_table_num-1]; - unit_parse_ctxs[unit_idx].str_offsets_table = table; + DW2_Attrib *rnglists_base_off_attrib = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_RngListsBase); + U64 rnglists_base_off = rnglists_base_off_attrib->val.u128.u64[0]; + U64 rnglists_table_num = rng1u64_array_num_from_value__binary_search(&rnglists_tables_ranges_array, rnglists_base_off); + if(0 < rnglists_table_num && rnglists_table_num <= rnglists_tables_ranges_array.count) + { + DW2_OffsetTable *table = &rnglists_tables[rnglists_table_num-1]; + unit_parse_ctxs[unit_idx].rnglists_table = table; + } + } + + // rjf: find str offsets table + { + DW2_Attrib *str_offsets_base_off_attrib = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_StrOffsetsBase); + U64 str_offsets_base_off = str_offsets_base_off_attrib->val.u128.u64[0]; + U64 str_offsets_table_num = rng1u64_array_num_from_value__binary_search(&str_offsets_tables_ranges_array, str_offsets_base_off); + if(0 < str_offsets_table_num && str_offsets_table_num <= str_offsets_tables_ranges_array.count) + { + DW2_OffsetTable *table = &str_offsets_tables[str_offsets_table_num-1]; + unit_parse_ctxs[unit_idx].str_offsets_table = table; + } + } + + // rjf: find addr table + { + DW2_Attrib *addr_base_off_attrib = dw2_attrib_from_kind(unit_root_tag, DW_AttribKind_AddrBase); + U64 addr_base_off = addr_base_off_attrib->val.u128.u64[0]; + U64 addr_table_num = rng1u64_array_num_from_value__binary_search(&addr_tables_ranges_array, addr_base_off); + if(0 < addr_table_num && addr_table_num <= addr_tables_ranges_array.count) + { + DW2_OffsetTable *table = &addr_tables[addr_table_num-1]; + unit_parse_ctxs[unit_idx].addr_table = table; + } } } }