From 4b88258a3070fa5bbee64517490cb5d192acc1b7 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Fri, 20 Feb 2026 18:08:45 -0800 Subject: [PATCH] emit simple DWARF line table --- src/dwarf/dwarf.c | 22 ++++++++ src/dwarf/dwarf.h | 5 ++ src/dwarf/dwarf_parse.c | 21 ++++---- src/dwarf/dwarf_writer.c | 101 ++++++++++++++++++++++-------------- src/dwarf/dwarf_writer.h | 6 +-- src/torture/torture_dwarf.c | 41 +++++++++++---- 6 files changed, 133 insertions(+), 63 deletions(-) diff --git a/src/dwarf/dwarf.c b/src/dwarf/dwarf.c index 0ead6dc8..e8179a53 100644 --- a/src/dwarf/dwarf.c +++ b/src/dwarf/dwarf.c @@ -1029,3 +1029,25 @@ dw_form_match(DW_Form a, DW_Form b) return is_match; } +internal U64 +dw_length_from_std_opcode(DW_StdOpcode opcode) +{ + switch (opcode) { + case DW_StdOpcode_ExtendedOpcode: return 0; + case DW_StdOpcode_Copy: return 0; + case DW_StdOpcode_AdvancePc: return 1; + case DW_StdOpcode_AdvanceLine: return 1; + case DW_StdOpcode_SetFile: return 1; + case DW_StdOpcode_SetColumn: return 1; + case DW_StdOpcode_NegateStmt: return 0; + case DW_StdOpcode_SetBasicBlock: return 0; + case DW_StdOpcode_ConstAddPc: return 0; + case DW_StdOpcode_FixedAdvancePc: return 1; + case DW_StdOpcode_SetPrologueEnd: return 0; + case DW_StdOpcode_SetEpilogueBegin: return 0; + case DW_StdOpcode_SetIsa: return 1; + default: InvalidPath; break; + } + return 0; +} + diff --git a/src/dwarf/dwarf.h b/src/dwarf/dwarf.h index 5de439be..272f269c 100644 --- a/src/dwarf/dwarf.h +++ b/src/dwarf/dwarf.h @@ -1829,4 +1829,9 @@ internal void * dw_serial_push_sleb128(Arena *arena, String8List *srl, S64 v); internal B32 dw_form_match(DW_Form a, DW_Form b); +//////////////////////////////// +// Line + +internal U64 dw_length_from_std_opcode(DW_StdOpcode opcode); + #endif // DWARF_H diff --git a/src/dwarf/dwarf_parse.c b/src/dwarf/dwarf_parse.c index bdaae721..c56c2ec4 100644 --- a/src/dwarf/dwarf_parse.c +++ b/src/dwarf/dwarf_parse.c @@ -2586,10 +2586,11 @@ dw_line_vm_step(DW_LineVM *vm) // extended opcodes case DW_StdOpcode_ExtendedOpcode: { TryRead(str8_deserial_read_uleb128(vm->program, vm->cursor, &vm->ext_length), vm->cursor, exit); + String8 ext_data = str8_substr(vm->program, r1u64(vm->cursor, vm->cursor + vm->ext_length)); + vm->cursor += vm->ext_length; - U64 extended_opl = vm->cursor + vm->ext_length; - - TryRead(str8_deserial_read_struct(vm->program, vm->cursor, &vm->ext_opcode), vm->cursor, exit); + U64 ext_cursor = 0; + TryRead(str8_deserial_read_struct(ext_data, ext_cursor, &vm->ext_opcode), vm->cursor, exit); switch (vm->ext_opcode) { case DW_ExtOpcode_EndSequence: { @@ -2599,16 +2600,16 @@ dw_line_vm_step(DW_LineVM *vm) } break; case DW_ExtOpcode_SetAddress: { - TryRead(str8_deserial_read(vm->program, vm->cursor, &vm->operands[0].u64, vm->header.address_size, vm->header.address_size), vm->cursor, exit); + TryRead(str8_deserial_read(ext_data, ext_cursor, &vm->operands[0].u64, vm->header.address_size, vm->header.address_size), vm->cursor, exit); vm->state.address = vm->operands[0].u64; vm->state.op_index = 0; } break; case DW_ExtOpcode_DefineFile: { - TryRead(str8_deserial_read_cstr (vm->program, vm->cursor, &vm->operands[0].string), vm->cursor, exit); // file name - TryRead(str8_deserial_read_uleb128(vm->program, vm->cursor, &vm->operands[1].u64), vm->cursor, exit); // dir index - TryRead(str8_deserial_read_uleb128(vm->program, vm->cursor, &vm->operands[2].u64), vm->cursor, exit); // modify time - TryRead(str8_deserial_read_uleb128(vm->program, vm->cursor, &vm->operands[3].u64), vm->cursor, exit); // file size + TryRead(str8_deserial_read_cstr (ext_data, ext_cursor, &vm->operands[0].string), vm->cursor, exit); // file name + TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[1].u64), vm->cursor, exit); // dir index + TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[2].u64), vm->cursor, exit); // modify time + TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[3].u64), vm->cursor, exit); // file size if (vm->ext_file_ht == 0) { vm->ext_file_ht = hash_table_init(vm->arena, 512); @@ -2629,14 +2630,12 @@ dw_line_vm_step(DW_LineVM *vm) } break; case DW_ExtOpcode_SetDiscriminator: { - TryRead(str8_deserial_read_uleb128(vm->program, vm->cursor, &vm->operands[0].u64), vm->cursor, exit); + TryRead(str8_deserial_read_uleb128(ext_data, ext_cursor, &vm->operands[0].u64), vm->cursor, exit); vm->state.discriminator = vm->operands[0].u64; } break; default: { NotImplemented; } break; } - - vm->cursor = extended_opl; } break; default: { diff --git a/src/dwarf/dwarf_writer.c b/src/dwarf/dwarf_writer.c index c76d0c2e..6fbf357e 100644 --- a/src/dwarf/dwarf_writer.c +++ b/src/dwarf/dwarf_writer.c @@ -102,17 +102,21 @@ dw_writer_begin(DW_Format format, DW_Version version, DW_CompUnitKind cu_kind, A { Arena *arena = arena_alloc(); DW_Writer *writer = push_array(arena, DW_Writer, 1); - writer->arena = arena; - writer->arch = arch; - writer->format = format; - writer->version = version; - writer->cu_kind = cu_kind; - writer->address_size = byte_size_from_arch(arch); - writer->current = 0; - writer->abbrev_id_map = hash_table_init(arena, 0x2000); - writer->line.opcode_base = DW_StdOpcode_Count; - writer->line.line_base = -5; - writer->line.line_range = 14; + writer->arena = arena; + writer->arch = arch; + writer->format = format; + writer->version = version; + writer->cu_kind = cu_kind; + writer->address_size = byte_size_from_arch(arch); + writer->current = 0; + writer->abbrev_id_map = hash_table_init(arena, 0x2000); + writer->line.min_inst_len = min_instruction_size_from_arch(arch); + writer->line.max_ops_per_inst = max_ops_per_instruction_from_arch(arch); + writer->line.default_is_stmt = 1; + writer->line.line_base = -5; + writer->line.line_range = 14; + writer->line.opcode_base = DW_StdOpcode_Count; + writer->line.ln = 1; for EachElement(i, writer->sections) { str8_serial_begin(arena, &writer->sections[i].srl); } @@ -461,7 +465,7 @@ dw_writer_push_attrib_expression(DW_Writer *writer, DW_AttribKind kind, DW_ExprE } internal DW_WriterAttrib * -dw_writer_push_attrib_line_ptr(DW_Writer *writer, DW_AttribKind kind, void *line_ptr) +dw_writer_push_attrib_line_ptr(DW_Writer *writer, DW_AttribKind kind, U64 line_ptr) { return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_LinePtr, .line_ptr = line_ptr }); } @@ -548,6 +552,7 @@ dw_data_from_line_insts(Arena *arena, U8 address_size, DW_LineInstList insts) } break; default: { InvalidPath; } break; } + ext_size += sizeof(DW_ExtOpcode); // write ext header dw_serial_push_uleb128(arena, &srl, ext_size); @@ -569,7 +574,7 @@ dw_data_from_line_insts(Arena *arena, U8 address_size, DW_LineInstList insts) } } break; // special opcode - default: {} break; + default: { } break; } } @@ -581,29 +586,27 @@ dw_data_from_line_insts(Arena *arena, U8 address_size, DW_LineInstList insts) internal void dw_writer_line_emit(DW_Writer *writer, DW_WriterFile *file, U64 ln, U64 col, U64 addr) { - if (writer->line.last_file != file) { + if (writer->line.file != file) { dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_set_file(file)); } Assert(addr <= max_S64); S64 addr_delta = (S64)addr - (S64)writer->line.addr; - if (addr_delta != 0) { - dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_advance_pc(addr_delta)); - } + S64 ln_delta = (S64)ln - (S64)writer->line.ln; + S64 col_delta = (S64)col - (S64)writer->line.col; - S64 ln_delta = (S64)ln - (S64)writer->line.ln; - if (ln_delta != 0) { - dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_advance_line(ln_delta)); + if (col_delta == 0 && (ln_delta > 0 || addr_delta > 0) && + writer->line.line_base <= ln_delta && ln_delta < writer->line.line_range+writer->line.line_base && + addr_delta < max_U8 - writer->line.opcode_base) { + U8 opcode = (ln_delta - writer->line.line_base) + (writer->line.line_range * addr_delta) + writer->line.opcode_base; + dw_line_inst_list_push(writer->arena, &writer->line.line_insts, (DW_LineInst){ .opcode = opcode }); + } else { + if (addr_delta != 0) { dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_advance_pc(addr_delta)); } + if (ln_delta != 0) { dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_advance_line(ln_delta)); } + if (col_delta != 0) { dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_set_column(col_delta)); } + dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_copy()); } - S64 col_delta = (S64)col - (S64)writer->line.col; - if (col_delta != 0) { - dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_set_column(col_delta)); - } - - // append row - dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_copy()); - writer->line.file = file; writer->line.ln = ln; writer->line.col = col; @@ -714,7 +717,8 @@ dw_lower_attrib_forms(DW_Writer *writer, DW_WriterTag *tag) } } break; case DW_WriterFormKind_LinePtr: { - NotImplemented; + attrib->form.reader.kind = DW_Form_SecOffset; + attrib->form.reader.sec_offset = attrib->form.writer.line_ptr; } break; case DW_WriterFormKind_MacPtr: { NotImplemented; @@ -960,23 +964,29 @@ dw_writer_emit(DW_Writer *writer) dw_serial_push_uleb128(writer->arena, srl, DW_LNCT_Path); dw_serial_push_uleb128(writer->arena, srl, DW_Form_String); + + // dedup directories - HashTable *dir_ht = hash_table_init(scratch.arena, writer->line.file_count + 1); + String8List dirs = {0}; + HashTable *dir_ht = hash_table_init(scratch.arena, writer->line.file_count + 1); // first entry must be compile unit directory hash_table_push_string_u64(scratch.arena, dir_ht, comp_dir, dir_ht->count); + str8_list_push(scratch.arena, &dirs, comp_dir); for EachNode(file, DW_WriterFile, writer->line.first_file) { String8 path = str8_chop_last_slash(file->path); if ( ! hash_table_search_string_u64(dir_ht, path, &file->dir_idx)) { file->dir_idx = hash_table_push_string_u64(scratch.arena, dir_ht, path, dir_ht->count)->v.value_u64; + str8_list_push(scratch.arena, &dirs, path); } } KeyValuePair *dirs_kv = key_value_pairs_from_hash_table(scratch.arena, dir_ht); - sort_key_value_pairs_as_string_sensitive(dirs_kv, dir_ht->count); + //sort_key_value_pairs_as_string_sensitive(dirs_kv, dir_ht->count); // (15) directories_count dw_serial_push_uleb128(writer->arena, srl, dir_ht->count); + // (16) directories - for (U64 i = 1; i < dir_ht->count; i += 1) { + for (U64 i = 0; i < dir_ht->count; i += 1) { String8 dir = dirs_kv[i].key_string; dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, (DW_WriterXForm) { .reader = { .kind = DW_Form_String, .string = dir } }); } @@ -985,6 +995,7 @@ dw_writer_emit(DW_Writer *writer) } String8List file_table_srl = {0}; + str8_serial_begin(writer->arena, &file_table_srl); { Temp scratch = scratch_begin(0, 0); String8List *srl = &file_table_srl; @@ -1010,11 +1021,11 @@ dw_writer_emit(DW_Writer *writer) } // count needed encodings - U64 enc_count = 0; + U8 enc_count = 0; for EachElement(i, encs) { if (encs[i].form_kind != DW_Form_Null) { enc_count += 1; } } // (17) file_name_entry_format_count - str8_serial_push_u16(writer->arena, srl, enc_count); + str8_serial_push_u8(writer->arena, srl, enc_count); // (18) file_name_entry_format for EachElement(i, encs) { @@ -1038,6 +1049,9 @@ dw_writer_emit(DW_Writer *writer) p->next = n->next; n->next = writer->line.first_file; writer->line.first_file = n; + if (writer->line.last_file == n) { + writer->line.last_file = p; + } } break; } @@ -1050,12 +1064,13 @@ dw_writer_emit(DW_Writer *writer) // path Assert(*(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_Path) == DW_Form_String); - DW_WriterXForm path_form = { .reader = { .kind = DW_Form_String, .string = file->path } }; + String8 file_name = str8_skip_last_slash(file->path); + DW_WriterXForm path_form = { .reader = { .kind = DW_Form_String, .string = file_name } }; dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, path_form); // directory index Assert(*(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_DirectoryIndex) == DW_Form_UData); - DW_WriterXForm dir_idx_form = { .reader = { .kind = DW_Form_UData, .udata = file->time_stamp } }; + DW_WriterXForm dir_idx_form = { .reader = { .kind = DW_Form_UData, .udata = file->dir_idx } }; dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, dir_idx_form); // time stamp @@ -1113,8 +1128,8 @@ dw_writer_emit(DW_Writer *writer) // (4) segment_selector_size str8_serial_push_struct(writer->arena, srl, &writer->segsel_size); // (5) header_length - U64 header_size = srl->total_size + dir_table_srl.total_size + file_table_srl.total_size; - dw_serial_push_uint(writer->arena, srl, writer->format, header_size); + void *header_length_ptr = dw_serial_push_uint(writer->arena, srl, writer->format, 0); + U64 header_length_offset = srl->total_size; // (6) minimum_instruction_length str8_serial_push_struct(writer->arena, srl, &writer->line.min_inst_len); // (7) maximum_operations_per_instruction @@ -1128,11 +1143,19 @@ dw_writer_emit(DW_Writer *writer) // (11) opcode_base str8_serial_push_struct(writer->arena, srl, &writer->line.opcode_base); // (12) standard_opcode_lengths - str8_serial_push_struct(writer->arena, srl, &writer->line.std_op_lens); + U8 *std_op_lens = push_array(writer->arena, U8, DW_StdOpcode_Count - 1); + for (U64 i = 1; i < DW_StdOpcode_Count; i += 1) { std_op_lens[i-1] = dw_length_from_std_opcode(i); } + str8_serial_push_string(writer->arena, srl, str8(std_op_lens, DW_StdOpcode_Count - 1)); // directory table str8_list_concat_in_place(srl, &dir_table_srl); // file table str8_list_concat_in_place(srl, &file_table_srl); + + // fixup header length + U64 bytes_to_line_program = srl->total_size - header_length_offset; + if (writer->format == DW_Format_32Bit) { *(U32 *)header_length_ptr = bytes_to_line_program; } + else { *(U64 *)header_length_ptr = bytes_to_line_program; } + // line program String8 line_program_data = dw_data_from_line_insts(writer->arena, writer->address_size, writer->line.line_insts); str8_serial_push_string(writer->arena, srl, line_program_data); diff --git a/src/dwarf/dwarf_writer.h b/src/dwarf/dwarf_writer.h index 95b2f913..466ad5d5 100644 --- a/src/dwarf/dwarf_writer.h +++ b/src/dwarf/dwarf_writer.h @@ -47,7 +47,7 @@ typedef struct DW_WriterForm union { struct { struct DW_WriterTag *ref; - void *line_ptr; + U64 line_ptr; void *mac_ptr; void *rng_list_ptr; }; @@ -137,6 +137,7 @@ typedef struct DW_WriterFile U64 size; String8 md5; String8 source; + // computed during line table emit step U64 file_idx; U64 dir_idx; } DW_WriterFile; @@ -225,7 +226,6 @@ typedef struct DW_Writer S8 line_base; U8 line_range; U8 opcode_base; - U8 *std_op_lens; DW_LineInstList line_insts; DW_WriterFile *file; @@ -275,7 +275,7 @@ internal DW_WriterAttrib * dw_writer_push_attrib_enum (DW_Writer *writer, internal DW_WriterAttrib * dw_writer_push_attrib_ref (DW_Writer *writer, DW_AttribKind kind, DW_WriterTag *ref ); internal DW_WriterAttrib * dw_writer_push_attrib_exprloc (DW_Writer *writer, DW_AttribKind kind, String8 exprloc ); internal DW_WriterAttrib * dw_writer_push_attrib_expression (DW_Writer *writer, DW_AttribKind kind, DW_ExprEnc *encs, U64 encs_count); -internal DW_WriterAttrib * dw_writer_push_attrib_line_ptr (DW_Writer *writer, DW_AttribKind kind, void * line_ptr ); +internal DW_WriterAttrib * dw_writer_push_attrib_line_ptr (DW_Writer *writer, DW_AttribKind kind, U64 line_ptr ); internal DW_WriterAttrib * dw_writer_push_attrib_mac_ptr (DW_Writer *writer, DW_AttribKind kind, void * mac_ptr ); internal DW_WriterAttrib * dw_writer_push_attrib_rng_list_ptr(DW_Writer *writer, DW_AttribKind kind, void * rng_list_ptr); internal DW_WriterAttrib * dw_writer_push_attrib_implicit (DW_Writer *writer, DW_AttribKind kind, S64 implicit ); diff --git a/src/torture/torture_dwarf.c b/src/torture/torture_dwarf.c index 095b76b9..2641aefd 100644 --- a/src/torture/torture_dwarf.c +++ b/src/torture/torture_dwarf.c @@ -107,25 +107,40 @@ dwt_make_writer(void) { Temp scratch = scratch_begin(0, 0); + String8 comp_dir = t_make_file_path(scratch.arena, str8_lit("")); + String8 comp_name = str8_lit("test_code.txt"); + String8 test_text = str8_lit("nop\nnop\nret\n"); + String8 test_path = t_make_file_path(scratch.arena, comp_name); + t_write_file(comp_name, test_text); + DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_Last, DW_CompUnitKind_Compile, Arch_x64); // line { - DW_WriterFile *b_file = dw_writer_new_file(writer, str8_lit("~/devel/projects/b/b.c")); - DW_WriterFile *a_file = dw_writer_new_file(writer, str8_lit("~/devel/projects/a.c")); + DW_WriterFile *a_file = dw_writer_new_file(writer, test_path); dw_writer_line_set_prologue_end(writer); - dw_writer_line_emit(writer, b_file, 10, 0, 0x140001000); - dw_writer_line_emit(writer, b_file, 11, 0, 0x140001010); - dw_writer_line_emit(writer, a_file, 1, 0, 0x140001016); - dw_writer_line_emit(writer, a_file, 2, 0, 0x140001020); + dw_writer_line_emit(writer, a_file, 1, 0, 0x140001000); + dw_writer_line_emit(writer, a_file, 2, 0, 0x140001001); + dw_writer_line_emit(writer, a_file, 3, 0, 0x140001002); + + //DW_WriterFile *b_file = dw_writer_new_file(writer, str8_lit("~/devel/projects/b/b.c")); + //DW_WriterFile *a_file = dw_writer_new_file(writer, str8_lit("~/devel/projects/a.c")); + //dw_writer_line_set_prologue_end(writer); + //dw_writer_line_emit(writer, b_file, 10, 0, 0x140001000); + //dw_writer_line_emit(writer, b_file, 11, 0, 0x140001010); + //dw_writer_line_emit(writer, a_file, 1, 0, 0x140001016); + //dw_writer_line_emit(writer, a_file, 2, 0, 0x140001020); } // info { dw_writer_tag_begin(writer, DW_TagKind_CompileUnit); dw_writer_push_attrib_string(writer, DW_AttribKind_Producer, str8_lit("RAD DWARF WRITER")); - dw_writer_push_attrib_string(writer, DW_AttribKind_CompDir, str8_lit("~/devel/projects")); - dw_writer_push_attrib_string(writer, DW_AttribKind_Name, str8_lit("a.c")); + dw_writer_push_attrib_string(writer, DW_AttribKind_CompDir, comp_dir); + dw_writer_push_attrib_string(writer, DW_AttribKind_Name, comp_name); dw_writer_push_attrib_enum(writer, DW_AttribKind_Language, DW_Language_C99); dw_writer_push_attrib_flag(writer, DW_AttribKind_UseUtf8, 1); + dw_writer_push_attrib_line_ptr(writer, DW_AttribKind_StmtList, 0); + dw_writer_push_attrib_address(writer, DW_AttribKind_LowPc, 0x140001000); + dw_writer_push_attrib_address(writer, DW_AttribKind_HighPc, 0x140001003); { DW_WriterTag *char_type = dw_writer_tag_begin(writer, DW_TagKind_BaseType); dw_writer_push_attrib_sint(writer, DW_AttribKind_ByteSize, 1); @@ -167,8 +182,8 @@ dwt_make_writer(void) dw_writer_tag_begin(writer, DW_TagKind_SubProgram); dw_writer_push_attrib_flag(writer, DW_AttribKind_External, 1); dw_writer_push_attrib_flag(writer, DW_AttribKind_Prototyped, 1); - dw_writer_push_attrib_address(writer, DW_AttribKind_LowPc, 0x14012f2f0); - dw_writer_push_attrib_address(writer, DW_AttribKind_HighPc, 0x14012f405); + dw_writer_push_attrib_address(writer, DW_AttribKind_LowPc, 0x140001000); + dw_writer_push_attrib_address(writer, DW_AttribKind_HighPc, 0x140001003); dw_writer_push_attrib_string(writer, DW_AttribKind_Name, str8_lit("main")); dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, simple_struct_tag); dw_writer_push_attrib_expression(writer, DW_AttribKind_FrameBase, &(DW_ExprEnc)DW_ExprEnc_Op(Reg7), 1); @@ -182,6 +197,8 @@ dwt_make_writer(void) { OBJ *obj = obj_alloc(0, Arch_x64); OBJ_Section *text_section = obj_push_section(obj, str8_lit(".text"), OBJ_SectionFlag_Read|OBJ_SectionFlag_Exec|OBJ_SectionFlag_Load); + str8_serial_push_u8(obj->arena, &text_section->data, 0x90); + str8_serial_push_u8(obj->arena, &text_section->data, 0x90); str8_serial_push_u8(obj->arena, &text_section->data, 0xc3); obj_push_symbol(obj, str8_lit("entry"), OBJ_SymbolScope_Global, OBJ_RefKind_Section, text_section); @@ -195,6 +212,10 @@ dwt_make_writer(void) String8 string_table = str8_substr(raw_coff, obj_coff_header.string_table_range); input = dw_input_from_coff_section_table(scratch.arena, raw_coff, string_table, section_count, section_table); + t_write_file(str8_lit("dwarf.obj"), raw_coff); + + t_invoke(str8_lit("radlink"), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:full dwarf.obj"), max_U64); + obj_release(&obj); }