From cfe3cde396757a952c3eb0d92eef7e0dfbfcd0a5 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Fri, 20 Feb 2026 11:17:12 -0800 Subject: [PATCH] checkpoint: basic line emission --- src/dwarf/dwarf.h | 1 + src/dwarf/dwarf_writer.c | 733 ++++++++++++++++++++++++++++++------ src/dwarf/dwarf_writer.h | 177 +++++++-- src/linker/hash_table.c | 9 + src/linker/hash_table.h | 6 +- src/natvis/base.natvis | 1 + src/torture/torture_dwarf.c | 136 ++++--- 7 files changed, 853 insertions(+), 210 deletions(-) diff --git a/src/dwarf/dwarf.h b/src/dwarf/dwarf.h index 960202a3..5de439be 100644 --- a/src/dwarf/dwarf.h +++ b/src/dwarf/dwarf.h @@ -166,6 +166,7 @@ typedef enum DW_StdOpcodeEnum #define X(_N,_ID) DW_StdOpcode_##_N = _ID, DW_StdOpcode_XList #undef X + DW_StdOpcode_Count, } DW_StdOpcodeEnum; #define DW_ExtOpcode_XList \ diff --git a/src/dwarf/dwarf_writer.c b/src/dwarf/dwarf_writer.c index 420704f5..c76d0c2e 100644 --- a/src/dwarf/dwarf_writer.c +++ b/src/dwarf/dwarf_writer.c @@ -49,14 +49,6 @@ dw_size_from_uint(U64 v) return 0; } -internal void -dw_lower_form(DW_WriterFormKind form_kind, - DW_WriterForm form, - DW_FormKind *form_kind_out, - DW_Form *form_out) -{ -} - internal DW_WriterFixupNode * dw_writer_fixup_list_push(Arena *arena, DW_WriterFixupList *list, DW_WriterFixup v) { @@ -110,19 +102,22 @@ 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->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; for EachElement(i, writer->sections) { str8_serial_begin(arena, &writer->sections[i].srl); } - // write .debug_info header + // write info header { String8List *srl = &writer->sections[DW_Section_Info].srl; @@ -158,6 +153,137 @@ dw_writer_end(DW_Writer **writer_ptr) *writer_ptr = 0; } +internal U64 +dw_serial_push_form(Arena *arena, String8List *srl, DW_Version version, DW_Format format, U8 address_size, DW_WriterFixupList *fixups, DW_WriterXForm form) +{ + U64 start_off = srl->total_size; + switch (form.reader.kind) { + case DW_Form_Addr: { + str8_serial_push_string(arena, srl, form.reader.addr); + } break; + case DW_Form_Block1: + case DW_Form_Block2: + case DW_Form_Block4: { + str8_serial_push_string(arena, srl, form.reader.block); + } break; + case DW_Form_Data1: + case DW_Form_Data2: + case DW_Form_Data4: + case DW_Form_Data8: { + str8_serial_push_string(arena, srl, form.reader.data); + } break; + case DW_Form_String: { + str8_serial_push_cstr(arena, srl, form.reader.string); + } break; + case DW_Form_Flag: { + str8_serial_push_u8(arena, srl, form.reader.flag); + } break; + case DW_Form_SData: { + dw_serial_push_sleb128(arena, srl, form.reader.sdata); + } break; + case DW_Form_Strp: { + dw_serial_push_uint(arena, srl, format, form.reader.sec_offset); + } break; + case DW_Form_UData: { + dw_serial_push_uleb128(arena, srl, form.reader.udata); + } break; + case DW_Form_RefAddr: { + if (version < DW_Version_3) { + Assert(address_size <= sizeof(form.reader.ref)); + str8_serial_push_string(arena, srl, str8((U8 *)&form.reader.ref, address_size)); + } else { + dw_serial_push_uint(arena, srl, format, form.reader.ref); + } + } break; + case DW_Form_Ref1: { + str8_serial_push_u8(arena, srl, (U8)form.reader.ref); + } break; + case DW_Form_Ref2: { + str8_serial_push_u16(arena, srl, (U16)form.reader.ref); + } break; + case DW_Form_Ref4: { + str8_serial_push_u32(arena, srl, (U32)form.reader.ref); + } break; + case DW_Form_Ref8: { + if (form.writer.ref->info_off == 0) { + // reserve 8 bytes + void *value = str8_serial_push_u64(arena, srl, 0); + + // push fixup + dw_writer_fixup_list_push(arena, fixups, (DW_WriterFixup){ .ptr = value, .tag = form.writer.ref }); + } + } break; + case DW_Form_RefUData: { + dw_serial_push_uleb128(arena, srl, form.reader.ref); + } break; + case DW_Form_Indirect: { + NotImplemented; + } break; + case DW_Form_SecOffset: { + dw_serial_push_uint(arena, srl, format, form.reader.sec_offset); + } break; + case DW_Form_ExprLoc: { + dw_serial_push_uleb128(arena, srl, form.reader.exprloc.size); + str8_serial_push_string(arena, srl, form.reader.exprloc); + } break; + case DW_Form_FlagPresent: { + } break; + case DW_Form_RefSig8: { + NotImplemented; + } break; + case DW_Form_Strx: + case DW_Form_Addrx: + case DW_Form_RngListx: + case DW_Form_LocListx: { + dw_serial_push_uleb128(arena, srl, form.reader.xval); + } break; + case DW_Form_RefSup4: { + NotImplemented; + } break; + case DW_Form_StrpSup: { + dw_serial_push_uint(arena, srl, format, form.reader.strp_sup); + } break; + case DW_Form_Data16: { + str8_serial_push_string(arena, srl, form.reader.data); + } break; + case DW_Form_LineStrp: { + dw_serial_push_uint(arena, srl, format, form.reader.sec_offset); + } break; + case DW_Form_ImplicitConst: { + // value is stored in the abbrev entry + } break; + case DW_Form_RefSup8: { + NotImplemented; + } break; + case DW_Form_Strx1: + case DW_Form_Addrx1: { + str8_serial_push_u8(arena, srl, (U8)form.reader.xval); + } break; + case DW_Form_Strx2: + case DW_Form_Addrx2: { + str8_serial_push_u16(arena, srl, (U16)form.reader.xval); + } break; + case DW_Form_Strx3: + case DW_Form_Addrx3: { + str8_serial_push_string(arena, srl, str8((U8 *)&form.reader.xval, 3)); + } break; + case DW_Form_Strx4: + case DW_Form_Addrx4: { + str8_serial_push_u32(arena, srl, (U32)form.reader.xval); + } break; + + case DW_Form_GNU_StrpAlt: { + dw_serial_push_uint(arena, srl, format, form.reader.sec_offset); + } break; + case DW_Form_GNU_RefAlt: { + dw_serial_push_uint(arena, srl, format, form.reader.ref); + } break; + + default: InvalidPath; break; + } + return srl->total_size - start_off; +} + internal String8 dw_make_abbrev_entry(Arena *arena, DW_WriterTag *tag) { @@ -174,9 +300,9 @@ dw_make_abbrev_entry(Arena *arena, DW_WriterTag *tag) // attrib abbrev for EachNode(attrib, DW_WriterAttrib, tag->first_attrib) { dw_serial_push_uleb128(scratch.arena, &srl, attrib->kind); - dw_serial_push_uleb128(scratch.arena, &srl, attrib->reader.form.kind); - if (attrib->reader.form.kind == DW_Form_ImplicitConst) { - dw_serial_push_sleb128(scratch.arena, &srl, attrib->reader.form.implicit_const); + dw_serial_push_uleb128(scratch.arena, &srl, attrib->form.reader.kind); + if (attrib->form.reader.kind == DW_Form_ImplicitConst) { + dw_serial_push_sleb128(scratch.arena, &srl, attrib->form.reader.implicit_const); } } @@ -251,7 +377,7 @@ dw_writer_push_attrib(DW_Writer *writer, DW_AttribKind kind, DW_WriterForm form) DW_WriterAttrib *attrib = dw_writer_attrib_chunk_list_push(writer->arena, &writer->attrib_chunk_list, 512); attrib->kind = kind; - attrib->writer.form = form; + attrib->form.writer = form; SLLQueuePush(writer->current->first_attrib, writer->current->last_attrib, attrib); writer->current->attrib_count += 1; return attrib; @@ -358,68 +484,230 @@ dw_writer_push_attrib_implicit(DW_Writer *writer, DW_AttribKind kind, S64 implic return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_Implicit, .implicit = implicit }); } +internal void +dw_line_inst_list_push_node(DW_LineInstList *list, DW_LineInstNode *node) +{ + SLLQueuePush(list->first, list->last, node); + list->count += 1; +} + +internal DW_LineInstNode * +dw_line_inst_list_push(Arena *arena, DW_LineInstList *list, DW_LineInst v) +{ + DW_LineInstNode *node = push_array(arena, DW_LineInstNode, 1); + node->v = v; + dw_line_inst_list_push_node(list, node); + return node; +} + +internal String8 +dw_data_from_line_insts(Arena *arena, U8 address_size, DW_LineInstList insts) +{ + Temp scratch = scratch_begin(&arena, 1); + String8List srl = {0}; + str8_serial_begin(scratch.arena, &srl); + + for EachNode(inst_n, DW_LineInstNode, insts.first) { + DW_LineInst *inst = &inst_n->v; + + str8_serial_push_struct(scratch.arena, &srl, &inst->opcode); + switch (inst->opcode) { + case DW_StdOpcode_Copy: {} break; + case DW_StdOpcode_AdvancePc: { + dw_serial_push_uleb128(arena, &srl, inst->advance_pc); + } break; + case DW_StdOpcode_AdvanceLine: { + dw_serial_push_sleb128(arena, &srl, inst->advance_line); + } break; + case DW_StdOpcode_SetFile: { + dw_serial_push_uleb128(arena, &srl, inst->set_file->file_idx); + } break; + case DW_StdOpcode_SetColumn: { + dw_serial_push_uleb128(arena, &srl, inst->set_column); + } break; + case DW_StdOpcode_NegateStmt: {} break; + case DW_StdOpcode_SetBasicBlock: {} break; + case DW_StdOpcode_ConstAddPc: {} break; + case DW_StdOpcode_FixedAdvancePc: { + str8_serial_push_u16(arena, &srl, inst->fixed_advance_pc); + } break; + case DW_StdOpcode_SetPrologueEnd: {} break; + case DW_StdOpcode_SetEpilogueBegin: {} break; + case DW_StdOpcode_SetIsa: {} break; + case DW_StdOpcode_ExtendedOpcode: { + // get ext size + U64 ext_size = 0; + switch (inst->ext) { + case DW_ExtOpcode_EndSequence: { ext_size = 0; } break; + case DW_ExtOpcode_SetAddress: { ext_size = address_size; } break; + case DW_ExtOpcode_DefineFile: { + NotImplemented; + } break; + case DW_ExtOpcode_SetDiscriminator: { + ext_size = dw_size_from_uleb128(inst->set_discriminator); + } break; + default: { InvalidPath; } break; + } + + // write ext header + dw_serial_push_uleb128(arena, &srl, ext_size); + str8_serial_push_struct(arena, &srl, &inst->ext); + + // write ext operands + switch (inst->ext) { + case DW_ExtOpcode_EndSequence: {} break; + case DW_ExtOpcode_SetAddress: { + dw_serial_push_uleb128(arena, &srl, inst->set_address); + } break; + case DW_ExtOpcode_DefineFile: { + NotImplemented; + } break; + case DW_ExtOpcode_SetDiscriminator: { + dw_serial_push_uleb128(arena, &srl, inst->set_discriminator); + } break; + default: { InvalidPath; } break; + } + } break; + // special opcode + default: {} break; + } + } + + String8 data = str8_serial_end(arena, &srl); + scratch_end(scratch); + return data; +} + +internal void +dw_writer_line_emit(DW_Writer *writer, DW_WriterFile *file, U64 ln, U64 col, U64 addr) +{ + if (writer->line.last_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; + if (ln_delta != 0) { + dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_advance_line(ln_delta)); + } + + 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; + writer->line.addr = addr; +} + +internal void +dw_writer_line_set_address(DW_Writer *writer, U64 address) +{ + dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNE_set_address(address)); +} + +internal void +dw_writer_line_set_prologue_end(DW_Writer *writer) +{ + dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_set_prologue_end()); +} + +internal void +dw_writer_line_epilogue_begin(DW_Writer *writer) +{ + dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_set_epilogue_begin()); +} + +internal void +dw_writer_line_set_isa(DW_Writer *writer, U64 isa) +{ + dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNS_set_isa(isa)); +} + +internal DW_WriterFile * +dw_writer_new_file(DW_Writer *writer, String8 path) +{ + DW_WriterFile *file = push_array(writer->arena, DW_WriterFile, 1); + file->path = path; + file->file_idx = writer->line.file_count; + + SLLQueuePush(writer->line.first_file, writer->line.last_file, file); + writer->line.file_count += 1; + return file; +} + internal void dw_lower_attrib_forms(DW_Writer *writer, DW_WriterTag *tag) { for EachNode(attrib, DW_WriterAttrib, tag->first_attrib) { - switch (attrib->writer.form.kind) { + switch (attrib->form.writer.kind) { case DW_WriterFormKind_Null: {} break; case DW_WriterFormKind_Flag: { - attrib->reader.form.kind = DW_Form_Flag; - attrib->reader.form.flag = attrib->writer.form.flag; + attrib->form.reader.kind = DW_Form_Flag; + attrib->form.reader.flag = attrib->form.writer.flag; } break; case DW_WriterFormKind_SInt: { - switch (dw_int_enc_from_sint(attrib->writer.form.sint)) { - case DW_IntEnc_Null: attrib->reader.form.kind = 0; break; + switch (dw_int_enc_from_sint(attrib->form.writer.sint)) { + case DW_IntEnc_Null: attrib->form.reader.kind = 0; break; case DW_IntEnc_1Byte: { - attrib->reader.form.kind = DW_Form_Data1; - attrib->reader.form.data = str8((U8 *)&attrib->writer.form.sint, sizeof(S8)); + attrib->form.reader.kind = DW_Form_Data1; + attrib->form.reader.data = str8((U8 *)&attrib->form.writer.sint, sizeof(S8)); } break; case DW_IntEnc_2Byte: { - attrib->reader.form.kind = DW_Form_Data2; - attrib->reader.form.data = str8((U8 *)&attrib->writer.form.sint, sizeof(S16)); + attrib->form.reader.kind = DW_Form_Data2; + attrib->form.reader.data = str8((U8 *)&attrib->form.writer.sint, sizeof(S16)); } break; case DW_IntEnc_4Byte: { - attrib->reader.form.kind = DW_Form_Data4; - attrib->reader.form.data = str8((U8 *)&attrib->writer.form.sint, sizeof(U32)); + attrib->form.reader.kind = DW_Form_Data4; + attrib->form.reader.data = str8((U8 *)&attrib->form.writer.sint, sizeof(U32)); } break; case DW_IntEnc_LEB128: { - attrib->reader.form.kind = DW_Form_SData; - attrib->reader.form.sdata = attrib->writer.form.sint; + attrib->form.reader.kind = DW_Form_SData; + attrib->form.reader.sdata = attrib->form.writer.sint; } break; default: InvalidPath; break; } } break; case DW_WriterFormKind_UInt: { - attrib->reader.form.kind = DW_Form_UData; - attrib->reader.form.udata = attrib->writer.form.uint; + attrib->form.reader.kind = DW_Form_UData; + attrib->form.reader.udata = attrib->form.writer.uint; } break; case DW_WriterFormKind_Address: { - Assert(writer->address_size <= sizeof(attrib->writer.form.address)); - attrib->reader.form.kind = DW_Form_Addr; - attrib->reader.form.addr = push_str8_copy(writer->arena, str8((U8 *)&attrib->writer.form.address, writer->address_size)); + Assert(writer->address_size <= sizeof(attrib->form.writer.address)); + attrib->form.reader.kind = DW_Form_Addr; + attrib->form.reader.addr = push_str8_copy(writer->arena, str8((U8 *)&attrib->form.writer.address, writer->address_size)); } break; case DW_WriterFormKind_Ref: { - if (attrib->writer.form.ref->info_off == 0) { - attrib->reader.form.kind = DW_Form_Ref8; + if (attrib->form.writer.ref->info_off == 0) { + attrib->form.reader.kind = DW_Form_Ref8; } else { - switch (dw_int_enc_from_uint(attrib->writer.form.ref->info_off)) { + switch (dw_int_enc_from_uint(attrib->form.writer.ref->info_off)) { case DW_IntEnc_Null: break; case DW_IntEnc_1Byte: { - attrib->reader.form.kind = DW_Form_Ref1; - attrib->reader.form.ref = (U8)attrib->writer.form.ref->info_off; + attrib->form.reader.kind = DW_Form_Ref1; + attrib->form.reader.ref = (U8)attrib->form.writer.ref->info_off; } break; case DW_IntEnc_2Byte: { - attrib->reader.form.kind = DW_Form_Ref2; - attrib->reader.form.ref = (U16)attrib->writer.form.ref->info_off; + attrib->form.reader.kind = DW_Form_Ref2; + attrib->form.reader.ref = (U16)attrib->form.writer.ref->info_off; } break; case DW_IntEnc_4Byte: { - attrib->reader.form.kind = DW_Form_Ref4; - attrib->reader.form.ref = (U32)attrib->writer.form.ref->info_off; + attrib->form.reader.kind = DW_Form_Ref4; + attrib->form.reader.ref = (U32)attrib->form.writer.ref->info_off; } break; case DW_IntEnc_LEB128: { - attrib->reader.form.kind = DW_Form_RefUData; - attrib->reader.form.ref = attrib->writer.form.ref->info_off; + attrib->form.reader.kind = DW_Form_RefUData; + attrib->form.reader.ref = attrib->form.writer.ref->info_off; } break; default: InvalidPath; break; } @@ -438,26 +726,26 @@ dw_lower_attrib_forms(DW_Writer *writer, DW_WriterTag *tag) NotImplemented; } break; case DW_WriterFormKind_Block: { - attrib->reader.form.block = attrib->writer.form.block; - switch (attrib->writer.form.block.size) { + attrib->form.reader.block = attrib->form.writer.block; + switch (attrib->form.writer.block.size) { case 0 : break; - case 1 : attrib->reader.form.kind = DW_Form_Block1; break; - case 2 : attrib->reader.form.kind = DW_Form_Block2; break; - case 4 : attrib->reader.form.kind = DW_Form_Block4; break; - default: attrib->reader.form.kind = DW_Form_Block; break; + case 1 : attrib->form.reader.kind = DW_Form_Block1; break; + case 2 : attrib->form.reader.kind = DW_Form_Block2; break; + case 4 : attrib->form.reader.kind = DW_Form_Block4; break; + default: attrib->form.reader.kind = DW_Form_Block; break; } } break; case DW_WriterFormKind_String: { - attrib->reader.form.kind = DW_Form_String; - attrib->reader.form.string = attrib->writer.form.string; + attrib->form.reader.kind = DW_Form_String; + attrib->form.reader.string = attrib->form.writer.string; } break; case DW_WriterFormKind_ExprLoc: { - attrib->reader.form.kind = DW_Form_ExprLoc; - attrib->reader.form.exprloc = attrib->writer.form.exprloc; + attrib->form.reader.kind = DW_Form_ExprLoc; + attrib->form.reader.exprloc = attrib->form.writer.exprloc; } break; case DW_WriterFormKind_Implicit: { - attrib->reader.form.kind = DW_Form_ImplicitConst; - attrib->reader.form.implicit_const = attrib->writer.form.implicit; + attrib->form.reader.kind = DW_Form_ImplicitConst; + attrib->form.reader.implicit_const = attrib->form.writer.implicit; } break; default: { InvalidPath; } break; } @@ -500,74 +788,74 @@ dw_writer_emit_tag(DW_Writer *writer, DW_WriterTag *tag) { String8List *debug_info = &writer->sections[DW_Section_Info].srl; for EachNode(attrib, DW_WriterAttrib, tag->first_attrib) { - switch (attrib->reader.form.kind) { + switch (attrib->form.reader.kind) { case DW_Form_Addr: { - str8_serial_push_string(writer->arena, debug_info, attrib->reader.form.addr); + str8_serial_push_string(writer->arena, debug_info, attrib->form.reader.addr); } break; case DW_Form_Block1: case DW_Form_Block2: case DW_Form_Block4: { - str8_serial_push_string(writer->arena, debug_info, attrib->reader.form.block); + str8_serial_push_string(writer->arena, debug_info, attrib->form.reader.block); } break; case DW_Form_Data1: case DW_Form_Data2: case DW_Form_Data4: case DW_Form_Data8: { - str8_serial_push_string(writer->arena, debug_info, attrib->reader.form.data); + str8_serial_push_string(writer->arena, debug_info, attrib->form.reader.data); } break; case DW_Form_String: { - str8_serial_push_cstr(writer->arena, debug_info, attrib->reader.form.string); + str8_serial_push_cstr(writer->arena, debug_info, attrib->form.reader.string); } break; case DW_Form_Flag: { - str8_serial_push_u8(writer->arena, debug_info, attrib->reader.form.flag); + str8_serial_push_u8(writer->arena, debug_info, attrib->form.reader.flag); } break; case DW_Form_SData: { - dw_serial_push_sleb128(writer->arena, debug_info, attrib->reader.form.sdata); + dw_serial_push_sleb128(writer->arena, debug_info, attrib->form.reader.sdata); } break; case DW_Form_Strp: { - dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.sec_offset); + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->form.reader.sec_offset); } break; case DW_Form_UData: { - dw_serial_push_uleb128(writer->arena, debug_info, attrib->reader.form.udata); + dw_serial_push_uleb128(writer->arena, debug_info, attrib->form.reader.udata); } break; case DW_Form_RefAddr: { if (writer->version < DW_Version_3) { - Assert(writer->address_size <= sizeof(attrib->reader.form.ref)); - str8_serial_push_string(writer->arena, debug_info, str8((U8 *)&attrib->reader.form.ref, writer->address_size)); + Assert(writer->address_size <= sizeof(attrib->form.reader.ref)); + str8_serial_push_string(writer->arena, debug_info, str8((U8 *)&attrib->form.reader.ref, writer->address_size)); } else { - dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.ref); + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->form.reader.ref); } } break; case DW_Form_Ref1: { - str8_serial_push_u8(writer->arena, debug_info, (U8)attrib->reader.form.ref); + str8_serial_push_u8(writer->arena, debug_info, (U8)attrib->form.reader.ref); } break; case DW_Form_Ref2: { - str8_serial_push_u16(writer->arena, debug_info, (U16)attrib->reader.form.ref); + str8_serial_push_u16(writer->arena, debug_info, (U16)attrib->form.reader.ref); } break; case DW_Form_Ref4: { - str8_serial_push_u32(writer->arena, debug_info, (U32)attrib->reader.form.ref); + str8_serial_push_u32(writer->arena, debug_info, (U32)attrib->form.reader.ref); } break; case DW_Form_Ref8: { - if (attrib->writer.form.ref->info_off == 0) { + if (attrib->form.writer.ref->info_off == 0) { // reserve 8 bytes void *value = str8_serial_push_u64(writer->arena, debug_info, 0); // push fixup - dw_writer_fixup_list_push(writer->arena, &writer->fixups, (DW_WriterFixup){ .value = value, .attrib = attrib }); + dw_writer_fixup_list_push(writer->arena, &writer->fixups, (DW_WriterFixup){ .tag = attrib->form.writer.ref, .ptr = value }); } } break; case DW_Form_RefUData: { - dw_serial_push_uleb128(writer->arena, debug_info, attrib->reader.form.ref); + dw_serial_push_uleb128(writer->arena, debug_info, attrib->form.reader.ref); } break; case DW_Form_Indirect: { NotImplemented; } break; case DW_Form_SecOffset: { - dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.sec_offset); + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->form.reader.sec_offset); } break; case DW_Form_ExprLoc: { - dw_serial_push_uleb128(writer->arena, debug_info, attrib->reader.form.exprloc.size); - str8_serial_push_string(writer->arena, debug_info, attrib->reader.form.exprloc); + dw_serial_push_uleb128(writer->arena, debug_info, attrib->form.reader.exprloc.size); + str8_serial_push_string(writer->arena, debug_info, attrib->form.reader.exprloc); } break; case DW_Form_FlagPresent: { } break; @@ -578,19 +866,19 @@ dw_writer_emit_tag(DW_Writer *writer, DW_WriterTag *tag) case DW_Form_Addrx: case DW_Form_RngListx: case DW_Form_LocListx: { - dw_serial_push_uleb128(writer->arena, debug_info, attrib->reader.form.xval); + dw_serial_push_uleb128(writer->arena, debug_info, attrib->form.reader.xval); } break; case DW_Form_RefSup4: { NotImplemented; } break; case DW_Form_StrpSup: { - dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.strp_sup); + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->form.reader.strp_sup); } break; case DW_Form_Data16: { - str8_serial_push_string(writer->arena, debug_info, attrib->reader.form.data); + str8_serial_push_string(writer->arena, debug_info, attrib->form.reader.data); } break; case DW_Form_LineStrp: { - dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.sec_offset); + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->form.reader.sec_offset); } break; case DW_Form_ImplicitConst: { // value is stored in the abbrev entry @@ -600,26 +888,26 @@ dw_writer_emit_tag(DW_Writer *writer, DW_WriterTag *tag) } break; case DW_Form_Strx1: case DW_Form_Addrx1: { - str8_serial_push_u8(writer->arena, debug_info, (U8)attrib->reader.form.xval); + str8_serial_push_u8(writer->arena, debug_info, (U8)attrib->form.reader.xval); } break; case DW_Form_Strx2: case DW_Form_Addrx2: { - str8_serial_push_u16(writer->arena, debug_info, (U16)attrib->reader.form.xval); + str8_serial_push_u16(writer->arena, debug_info, (U16)attrib->form.reader.xval); } break; case DW_Form_Strx3: case DW_Form_Addrx3: { - str8_serial_push_string(writer->arena, debug_info, str8((U8 *)&attrib->reader.form.xval, 3)); + str8_serial_push_string(writer->arena, debug_info, str8((U8 *)&attrib->form.reader.xval, 3)); } break; case DW_Form_Strx4: case DW_Form_Addrx4: { - str8_serial_push_u32(writer->arena, debug_info, (U32)attrib->reader.form.xval); + str8_serial_push_u32(writer->arena, debug_info, (U32)attrib->form.reader.xval); } break; case DW_Form_GNU_StrpAlt: { - dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.sec_offset); + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->form.reader.sec_offset); } break; case DW_Form_GNU_RefAlt: { - dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.ref); + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->form.reader.ref); } break; default: InvalidPath; break; @@ -641,36 +929,245 @@ dw_writer_emit_tag(DW_Writer *writer, DW_WriterTag *tag) internal void dw_writer_emit(DW_Writer *writer) { - dw_writer_emit_tag(writer, writer->root); + // line + { + // push line table terminator + dw_line_inst_list_push(writer->arena, &writer->line.line_insts, DW_LNE_end_sequence()); - // null terminate .debug_abbrev - dw_serial_push_uleb128(writer->arena, &writer->sections[DW_Section_Abbrev].srl, 0); - dw_serial_push_uleb128(writer->arena, &writer->sections[DW_Section_Abbrev].srl, 0); - - // fixup units lengths - for EachElement(i, writer->sections) { - if (writer->sections[i].length) { - if (writer->format == DW_Format_64Bit) { - U64 *length = writer->sections[i].length; - U64 length_size = sizeof(U64) + sizeof(U32); - Assert(writer->sections[i].srl.total_size >= length_size); - *length = writer->sections[i].srl.total_size - length_size; - } else { - U32 *length = writer->sections[i].length; - U32 length_size = sizeof(U32); - Assert(writer->sections[i].srl.total_size >= length_size); - *length = safe_cast_u32(writer->sections[i].srl.total_size - length_size); + // find comp dir in the compile unit tag + String8 comp_dir = {0}; + String8 comp_name = {0}; + Assert(writer->root->kind == DW_TagKind_CompileUnit); + for EachNode(attrib, DW_WriterAttrib, writer->root->first_attrib) { + if (attrib->kind == DW_AttribKind_CompDir) { + AssertAlways(attrib->form.writer.kind == DW_WriterFormKind_String); + comp_dir = attrib->form.writer.string; + } else if (attrib->kind == DW_AttribKind_Name) { + AssertAlways(attrib->form.writer.kind == DW_WriterFormKind_String); + comp_name = attrib->form.writer.string; } } + + String8List dir_table_srl = {0}; + str8_serial_begin(writer->arena, &dir_table_srl); + { + Temp scratch = scratch_begin(0, 0); + String8List *srl = &dir_table_srl; + + // (13) directory_entry_format_count + str8_serial_push_u8(writer->arena, srl, 1); + // (14) directory_entry_format + 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); + // first entry must be compile unit directory + hash_table_push_string_u64(scratch.arena, dir_ht, comp_dir, dir_ht->count); + 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; + } + } + 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); + + // (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) { + 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 } }); + } + + scratch_end(scratch); + } + + String8List file_table_srl = {0}; + { + Temp scratch = scratch_begin(0, 0); + String8List *srl = &file_table_srl; + + struct { DW_LNCT lnct; DW_FormKind form_kind; } encs[] = { + { DW_LNCT_Path, DW_Form_String }, + { DW_LNCT_DirectoryIndex, DW_Form_UData }, + { DW_LNCT_TimeStamp, DW_Form_Null }, // DW_Form_UData + { DW_LNCT_Size, DW_Form_Null }, // DW_Form_UData + { DW_LNCT_MD5, DW_Form_Null }, // DW_Form_Block + { DW_LNCT_LLVM_Source, DW_Form_Null } // DW_Form_String + }; + + HashTable *encs_ht = hash_table_init(scratch.arena, ArrayCount(encs) * 2); + for EachElement(i, encs) { hash_table_push_u64_raw(scratch.arena, encs_ht, encs[i].lnct, &encs[i].form_kind); } + + // enable encodings in use + for EachNode(file, DW_WriterFile, writer->line.first_file) { + if (file->time_stamp != 0) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_TimeStamp ) = DW_Form_UData; } + if (file->size != 0) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_Size ) = DW_Form_UData; } + if (file->md5.size != 0) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_MD5 ) = DW_Form_Block; } + if (file->source.size != 0) { *(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_LLVM_Source) = DW_Form_String; } + } + + // count needed encodings + U64 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); + + // (18) file_name_entry_format + for EachElement(i, encs) { + if (encs[i].form_kind == DW_Form_Null) { continue; } + dw_serial_push_uleb128(writer->arena, srl, encs[i].lnct ); + dw_serial_push_uleb128(writer->arena, srl, encs[i].form_kind); + } + + // (19) file_names_count + dw_serial_push_uleb128(writer->arena, srl, writer->line.file_count); + + // (20) file_names + if (comp_name.size) { + for (DW_WriterFile *n = writer->line.first_file, *p = 0; n != 0; p = n, n = n->next) { + String8 file_name = str8_skip_last_slash(n->path); + if (str8_match(file_name, comp_name, 0)) { + if (p == 0) { + // file is already first + } else { + // move compile unit file to be first entry in the table + p->next = n->next; + n->next = writer->line.first_file; + writer->line.first_file = n; + } + break; + } + } + } + for EachNode(file, DW_WriterFile, writer->line.first_file) { + static DW_WriterXForm null_form_udata = { .reader = { .kind = DW_Form_UData } }; + static DW_WriterXForm null_form_string = { .reader = { .kind = DW_Form_String } }; + static DW_WriterXForm null_form_block = { .reader = { .kind = DW_Form_Block } }; + + // 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 } }; + 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_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, dir_idx_form); + + // time stamp + if (*(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_TimeStamp) == DW_Form_UData) { + if (file->time_stamp) { + DW_WriterXForm time_stamp_form = { .reader = { .kind = DW_Form_UData, .udata = file->time_stamp } }; + dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, time_stamp_form); + } else { + dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, null_form_udata); + } + } + + // size + if (*(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_Size) == DW_Form_UData) { + if (file->size) { + DW_WriterXForm size_form = { .reader = { .kind = DW_Form_UData, .udata = file->size } }; + dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, size_form); + } else { + dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, null_form_udata); + } + } + + // MD5 + if (*(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_MD5) == DW_Form_Block) { + if (file->md5.size) { + DW_WriterXForm md5_form = { .reader = { .kind = DW_Form_Block, .block = file->md5 } }; + dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, md5_form); + } else { + dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, null_form_block); + } + } + + // LLVM Source + if (*(DW_FormKind *)hash_table_search_u64_raw(encs_ht, DW_LNCT_LLVM_Source) == DW_Form_String) { + if (file->source.size) { + DW_WriterXForm source_form = { .reader = { .kind = DW_Form_String, .string = file->source } }; + dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, source_form); + } else { + dw_serial_push_form(writer->arena, srl, writer->version, writer->format, writer->address_size, 0, null_form_string); + } + } + } + + scratch_end(scratch); + } + + { + String8List *srl = &writer->sections[DW_Section_Line].srl; + // (1) unit_length + writer->sections[DW_Section_Line].length = dw_serial_push_length(writer->arena, srl, writer->format, 0); + // (2) version + str8_serial_push_struct(writer->arena, srl, &writer->version); + // (3) address_size + str8_serial_push_struct(writer->arena, srl, &writer->address_size); + // (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); + // (6) minimum_instruction_length + str8_serial_push_struct(writer->arena, srl, &writer->line.min_inst_len); + // (7) maximum_operations_per_instruction + str8_serial_push_struct(writer->arena, srl, &writer->line.max_ops_per_inst); + // (8) default_is_stmt + str8_serial_push_struct(writer->arena, srl, &writer->line.default_is_stmt); + // (9) line_base + str8_serial_push_struct(writer->arena, srl, &writer->line.line_base); + // (10) line_range + str8_serial_push_struct(writer->arena, srl, &writer->line.line_range); + // (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); + // directory table + str8_list_concat_in_place(srl, &dir_table_srl); + // file table + str8_list_concat_in_place(srl, &file_table_srl); + // 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); + } } - // fixup forward references in .debug_info - for EachNode(fixup_n, DW_WriterFixupNode, writer->fixups.first) { - DW_WriterFixup *fixup = &fixup_n->v; - if (fixup->attrib->reader.form.kind == DW_Form_Ref8) { - Assert(fixup->attrib->writer.form.ref->info_off != 0); - MemoryCopy(fixup->value, &fixup->attrib->writer.form.ref->info_off, sizeof(U64)); - fixup->attrib->reader.form.ref = fixup->attrib->writer.form.ref->info_off; + // info + { + dw_writer_emit_tag(writer, writer->root); + + // null terminate .debug_abbrev + dw_serial_push_uleb128(writer->arena, &writer->sections[DW_Section_Abbrev].srl, 0); + + // fixup units lengths + for EachElement(i, writer->sections) { + if (writer->sections[i].length) { + if (writer->format == DW_Format_64Bit) { + U64 *length = writer->sections[i].length; + U64 length_size = sizeof(U64) + sizeof(U32); + Assert(writer->sections[i].srl.total_size >= length_size); + *length = writer->sections[i].srl.total_size - length_size; + } else { + U32 *length = writer->sections[i].length; + U32 length_size = sizeof(U32); + Assert(writer->sections[i].srl.total_size >= length_size); + *length = safe_cast_u32(writer->sections[i].srl.total_size - length_size); + } + } + } + + // fixup forward references in .debug_info + for EachNode(fixup_n, DW_WriterFixupNode, writer->fixups.first) { + DW_WriterFixup *fixup = &fixup_n->v; + Assert(fixup->tag->info_off != 0); + MemoryCopy(fixup->ptr, &fixup->tag->info_off, sizeof(U64)); } } } diff --git a/src/dwarf/dwarf_writer.h b/src/dwarf/dwarf_writer.h index 821c98fd..95b2f913 100644 --- a/src/dwarf/dwarf_writer.h +++ b/src/dwarf/dwarf_writer.h @@ -65,15 +65,16 @@ typedef struct DW_WriterForm }; } DW_WriterForm; +typedef struct DW_WriterXForm +{ + DW_WriterForm writer; + DW_Form reader; +} DW_WriterXForm; + typedef struct DW_WriterAttrib { DW_AttribKindEnum kind; - struct { - DW_WriterForm form; - } writer; - struct { - DW_Form form; - } reader; + DW_WriterXForm form; struct DW_WriterAttrib *next; } DW_WriterAttrib; @@ -123,10 +124,58 @@ typedef struct DW_WriterAttribChunkList DW_WriterAttribChunk *last; } DW_WriterAttribChunkList; +typedef struct DW_WriterLine +{ + struct DW_WriterLine *next; +} DW_WriterLine; + +typedef struct DW_WriterFile +{ + struct DW_WriterFile *next; + String8 path; + U64 time_stamp; + U64 size; + String8 md5; + String8 source; + U64 file_idx; + U64 dir_idx; +} DW_WriterFile; + +typedef struct DW_LineInst +{ + DW_StdOpcode opcode; + union { + U64 advance_pc; + S64 advance_line; + DW_WriterFile *set_file; + U64 set_column; + U16 fixed_advance_pc; + U64 set_isa; + struct { + DW_ExtOpcode ext; + U64 set_address; + U64 set_discriminator; + }; + }; +} DW_LineInst; + +typedef struct DW_LineInstNode +{ + DW_LineInst v; + struct DW_LineInstNode *next; +} DW_LineInstNode; + +typedef struct DW_LineInstList +{ + U64 count; + DW_LineInstNode *first; + DW_LineInstNode *last; +} DW_LineInstList; + typedef struct DW_WriterFixup { - struct DW_WriterAttrib *attrib; - U8 *value; + DW_WriterTag *tag; + U8 *ptr; } DW_WriterFixup; typedef struct DW_WriterFixupNode @@ -151,38 +200,67 @@ typedef struct DW_WriterSection typedef struct DW_Writer { Arena *arena; + + // Compile Unit + Arch arch; + DW_Version version; + DW_Format format; + DW_CompUnitKind cu_kind; + U8 address_size; + U8 segsel_size; + + // Abbrev + U64 abbrev_base_info_off; + HashTable *abbrev_id_map; + + // Info DW_WriterTag *root; DW_WriterTag *current; - Arch arch; - DW_Format format; - DW_Version version; - DW_CompUnitKind cu_kind; - U64 address_size; - U64 abbrev_base_info_off; + + // Line + struct { + U8 min_inst_len; + U8 max_ops_per_inst; + U8 default_is_stmt; + S8 line_base; + U8 line_range; + U8 opcode_base; + U8 *std_op_lens; + DW_LineInstList line_insts; + + DW_WriterFile *file; + U64 ln; + U64 col; + U64 addr; + + U64 file_count; + DW_WriterFile *first_file; + DW_WriterFile *last_file; + } line; + + // Emit DW_WriterFixupList fixups; - HashTable *abbrev_id_map; DW_WriterSection sections[DW_Section_Count]; DW_WriterAttribChunkList attrib_chunk_list; DW_WriterTagChunkList tag_chunk_list; } DW_Writer; //////////////////////////////// - -internal DW_IntEnc dw_int_enc_from_sint(S64 v); -internal DW_IntEnc dw_int_enc_from_uint(U64 v); - -internal U64 dw_size_from_sint(S64 v); -internal U64 dw_size_from_uint(U64 v); - -internal void dw_serial_push_form(Arena *arena, String8List *srl, DW_Format format, DW_Version version, U8 address_size, DW_FormKind form_kind, DW_Form form); - -//////////////////////////////// +// Writer internal DW_Writer * dw_writer_begin(DW_Format format, DW_Version version, DW_CompUnitKind cu_kind, Arch arch); -internal void dw_writer_end(DW_Writer **writer_ptr); +internal void dw_writer_end (DW_Writer **writer_ptr); + +//////////////////////////////// +// Form + +internal U64 dw_serial_push_form(Arena *arena, String8List *srl, DW_Version version, DW_Format format, U8 address_size, DW_WriterFixupList *fixups, DW_WriterXForm form); + +//////////////////////////////// +// Info internal DW_WriterTag * dw_writer_tag_begin(DW_Writer *writer, DW_TagKind kind); -internal void dw_writer_tag_end(DW_Writer *writer); +internal void dw_writer_tag_end (DW_Writer *writer); internal DW_WriterAttrib * dw_writer_push_attrib (DW_Writer *writer, DW_AttribKind kind, DW_WriterForm form ); internal DW_WriterAttrib * dw_writer_push_attrib_address (DW_Writer *writer, DW_AttribKind kind, U64 address ); @@ -201,13 +279,56 @@ internal DW_WriterAttrib * dw_writer_push_attrib_line_ptr (DW_Writer *writer, 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 ); - #define dw_writer_push_attrib_expressionv(w, k, ...) dw_writer_push_attrib_expression(w, k, (DW_ExprEnc[]){ __VA_ARGS__ }, ArrayCount(((DW_ExprEnc[]){ __VA_ARGS__ })) ) +//////////////////////////////// +// Line + +internal void dw_line_inst_list_push_node(DW_LineInstList *list, DW_LineInstNode *node); +internal DW_LineInstNode * dw_line_inst_list_push (Arena *arena, DW_LineInstList *list, DW_LineInst op); + +// std opcodes +#define DW_LNS_copy() (DW_LineInst){ .opcode = DW_StdOpcode_Copy } +#define DW_LNS_advance_pc(d) (DW_LineInst){ .opcode = DW_StdOpcode_AdvancePc, .advance_pc = d } +#define DW_LNS_advance_line(s) (DW_LineInst){ .opcode = DW_StdOpcode_AdvanceLine, .advance_line = s } +#define DW_LNS_set_file(f) (DW_LineInst){ .opcode = DW_StdOpcode_SetFile, .set_file = f } +#define DW_LNS_set_column(c) (DW_LineInst){ .opcode = DW_StdOpcode_SetColumn, .set_column = c } +#define DW_LNS_negate_stmt() (DW_LineInst){ .opcode = DW_StdOpcode_NegateStmt } +#define DW_LNS_set_basic_block() (DW_LineInst){ .opcode = DW_StdOpcode_SetBasicBlock } +#define DW_LNS_const_add_pc(a) (DW_LineInst){ .opcode = DW_StdOpcode_ConstAddPc } +#define DW_LNS_fixed_advance_pc(a) (DW_LineInst){ .opcode = DW_StdOpcode_FixedAdvancePc, .fixed_advance_pc = a } +#define DW_LNS_set_prologue_end() (DW_LineInst){ .opcode = DW_StdOpcode_SetPrologueEnd } +#define DW_LNS_set_epilogue_begin() (DW_LineInst){ .opcode = DW_StdOpcode_SetEpilogueBegin } +#define DW_LNS_set_isa(i) (DW_LineInst){ .opcode = DW_StdOpcode_SetIsa, .set_isa = i } + +// ext opcodes +#define DW_LNE_end_sequence() (DW_LineInst){ .opcode = DW_StdOpcode_ExtendedOpcode, .ext = DW_ExtOpcode_EndSequence } +#define DW_LNE_set_address(a) (DW_LineInst){ .opcode = DW_StdOpcode_ExtendedOpcode, .ext = DW_ExtOpcode_SetAddress, .set_address = a } +#define DW_LNE_set_discriminator(d) (DW_LineInst){ .opcode = DW_StdOpcode_ExtendedOpcode, .ext = DW_ExtOpcode_SetDiscriminator, .set_discriminator = d } + +internal void dw_writer_line_emit (DW_Writer *writer, DW_WriterFile *file, U64 ln, U64 col, U64 addr); +internal void dw_writer_line_set_address (DW_Writer *writer, U64 address); +internal void dw_writer_line_set_prologue_end(DW_Writer *writer); +internal void dw_writer_line_epilogue_begin (DW_Writer *writer); +internal void dw_writer_line_set_isa (DW_Writer *writer, U64 isa); +internal DW_WriterFile * dw_writer_new_file (DW_Writer *writer, String8 path); + +//////////////////////////////// +// Emit + internal void dw_writer_emit(DW_Writer *writer); #ifdef OBJ_H internal void dw_writer_emit_to_obj(DW_Writer *writer, OBJ *obj); #endif +//////////////////////////////// +// Format Helpers + +internal DW_IntEnc dw_int_enc_from_sint(S64 v); +internal DW_IntEnc dw_int_enc_from_uint(U64 v); + +internal U64 dw_size_from_sint(S64 v); +internal U64 dw_size_from_uint(U64 v); + #endif // DWARF_WRITER_H diff --git a/src/linker/hash_table.c b/src/linker/hash_table.c index 25cc5c9f..7cdbbcb7 100644 --- a/src/linker/hash_table.c +++ b/src/linker/hash_table.c @@ -519,3 +519,12 @@ remove_duplicates_str8_list(Arena *arena, String8List list) return result; } +internal String8 ** +str8_from_key_value_pairs(Arena *arena, KeyValuePair *v, U64 count) +{ + String8 **result = push_array(arena, String8 *, count); + for EachIndex(i, count) { + result[i] = &v[i].value_string; + } + return result; +} diff --git a/src/linker/hash_table.h b/src/linker/hash_table.h index 80f0ea30..326c0b1b 100644 --- a/src/linker/hash_table.h +++ b/src/linker/hash_table.h @@ -94,14 +94,16 @@ internal U32 * keys_from_hash_table_u32 (Arena *arena, HashTable internal U64 * keys_from_hash_table_u64 (Arena *arena, HashTable *ht); internal String8 keys_from_hash_table_str8 (Arena *arena, HashTable *ht); internal KeyValuePair * key_value_pairs_from_hash_table(Arena *arena, HashTable *ht); +internal void * keys_from_hash_table_raw (Arena *arena, HashTable *ht); -internal void * keys_from_hash_table_raw (Arena *arena, HashTable *ht); -internal void * values_from_hash_table_raw(Arena *arena, HashTable *ht); +internal U64 * values_from_hash_table_u64(Arena *arena, HashTable *ht); internal void sort_key_value_pairs_as_u32 (KeyValuePair *pairs, U64 count); internal void sort_key_value_pairs_as_u64 (KeyValuePair *pairs, U64 count); internal void sort_key_value_pairs_as_string_sensitive(KeyValuePair *pairs, U64 count); +internal String8 ** str8_from_key_value_pairs(Arena *arena, KeyValuePair *v, U64 count); + // --- Misc -------------------------------------------------------------------- internal U64Array remove_duplicates_u64_array(Arena *arena, U64Array arr); diff --git a/src/natvis/base.natvis b/src/natvis/base.natvis index 59a43987..4db4ce56 100644 --- a/src/natvis/base.natvis +++ b/src/natvis/base.natvis @@ -154,6 +154,7 @@ + {{ count={count} first={first} }} count diff --git a/src/torture/torture_dwarf.c b/src/torture/torture_dwarf.c index 567364e4..095b76b9 100644 --- a/src/torture/torture_dwarf.c +++ b/src/torture/torture_dwarf.c @@ -82,8 +82,8 @@ dwt_tags_must_match(DW_WriterTag *writer_tag, DW_TagNode *reader_tag) DW_AttribNode *reader_attrib = reader_tag->tag.attribs.first; while (writer_attrib) { if (writer_attrib->kind != reader_attrib->v.attrib_kind) { goto exit; } - if (writer_attrib->reader.form.kind != reader_attrib->v.form.kind) { goto exit; } - if ( ! dw_form_match(writer_attrib->reader.form, reader_attrib->v.form)) { goto exit; } + if (writer_attrib->form.reader.kind != reader_attrib->v.form.kind) { goto exit; } + if ( ! dw_form_match(writer_attrib->form.reader, reader_attrib->v.form)) { goto exit; } writer_attrib = writer_attrib->next; reader_attrib = reader_attrib->next; } @@ -107,11 +107,23 @@ dwt_make_writer(void) { Temp scratch = scratch_begin(0, 0); - // define debug info layout 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_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_enum(writer, DW_AttribKind_Language, DW_Language_C99); dw_writer_push_attrib_flag(writer, DW_AttribKind_UseUtf8, 1); { @@ -223,19 +235,19 @@ T_BeginTest(dwarf_writer) DW_WriterAttrib *producer_attrib = comp_unit_tag->first_attrib; T_Ok(producer_attrib->kind == DW_AttribKind_Producer); - T_Ok(producer_attrib->reader.form.kind == DW_Form_String); - T_Ok(str8_match(producer_attrib->writer.form.string, str8_lit("RAD DWARF WRITER"), 0)); + T_Ok(producer_attrib->form.reader.kind == DW_Form_String); + T_Ok(str8_match(producer_attrib->form.writer.string, str8_lit("RAD DWARF WRITER"), 0)); DW_WriterAttrib *language_attrib = producer_attrib->next; T_Ok(language_attrib->kind == DW_AttribKind_Language); - T_Ok(language_attrib->reader.form.kind == DW_Form_Data1); - T_Ok(language_attrib->reader.form.data.size == 1); - T_Ok(*(U8 *)language_attrib->reader.form.data.str == DW_Language_C99); + T_Ok(language_attrib->form.reader.kind == DW_Form_Data1); + T_Ok(language_attrib->form.reader.data.size == 1); + T_Ok(*(U8 *)language_attrib->form.reader.data.str == DW_Language_C99); DW_WriterAttrib *use_utf8_attrib = language_attrib->next; T_Ok(use_utf8_attrib->kind == DW_AttribKind_UseUtf8); - T_Ok(use_utf8_attrib->reader.form.kind == DW_Form_Flag); - T_Ok(use_utf8_attrib->reader.form.flag == 1); + T_Ok(use_utf8_attrib->form.reader.kind == DW_Form_Flag); + T_Ok(use_utf8_attrib->form.reader.flag == 1); } DW_WriterTag *char_type_tag = comp_unit_tag->first_child; @@ -250,20 +262,20 @@ T_BeginTest(dwarf_writer) DW_WriterAttrib *byte_size_attrib = char_type_tag->first_attrib; T_Ok(byte_size_attrib->kind == DW_AttribKind_ByteSize); - T_Ok(byte_size_attrib->reader.form.kind == DW_Form_Data1); - T_Ok(byte_size_attrib->reader.form.data.size == 1); - T_Ok(*(U8 *)byte_size_attrib->reader.form.data.str == 1); + T_Ok(byte_size_attrib->form.reader.kind == DW_Form_Data1); + T_Ok(byte_size_attrib->form.reader.data.size == 1); + T_Ok(*(U8 *)byte_size_attrib->form.reader.data.str == 1); DW_WriterAttrib *encoding_attrib = byte_size_attrib->next; T_Ok(encoding_attrib->kind == DW_AttribKind_Encoding); - T_Ok(encoding_attrib->reader.form.kind == DW_Form_Data1); - T_Ok(encoding_attrib->reader.form.data.size == 1); - T_Ok(*(U8 *)encoding_attrib->reader.form.data.str == DW_ATE_SignedChar); + T_Ok(encoding_attrib->form.reader.kind == DW_Form_Data1); + T_Ok(encoding_attrib->form.reader.data.size == 1); + T_Ok(*(U8 *)encoding_attrib->form.reader.data.str == DW_ATE_SignedChar); DW_WriterAttrib *name_attrib = encoding_attrib->next; T_Ok(name_attrib->kind == DW_AttribKind_Name); - T_Ok(name_attrib->reader.form.kind == DW_Form_String); - T_Ok(str8_match(name_attrib->reader.form.string, str8_lit("char"), 0)); + T_Ok(name_attrib->form.reader.kind == DW_Form_String); + T_Ok(str8_match(name_attrib->form.reader.string, str8_lit("char"), 0)); } DW_WriterTag *const_type_tag = char_type_tag->next; @@ -278,8 +290,8 @@ T_BeginTest(dwarf_writer) DW_WriterAttrib *type_attrib = const_type_tag->first_attrib; T_Ok(type_attrib->kind == DW_AttribKind_Type); - T_Ok(type_attrib->writer.form.kind == DW_WriterFormKind_Ref); - T_Ok(type_attrib->writer.form.ref == char_type_tag); + T_Ok(type_attrib->form.writer.kind == DW_WriterFormKind_Ref); + T_Ok(type_attrib->form.writer.ref == char_type_tag); } DW_WriterTag *dup_char_type_tag = const_type_tag->next; @@ -294,20 +306,20 @@ T_BeginTest(dwarf_writer) DW_WriterAttrib *byte_size_attrib = dup_char_type_tag->first_attrib; T_Ok(byte_size_attrib->kind == DW_AttribKind_ByteSize); - T_Ok(byte_size_attrib->reader.form.kind == DW_Form_Data1); - T_Ok(byte_size_attrib->reader.form.data.size == 1); - T_Ok(*(U8 *)byte_size_attrib->reader.form.data.str == 1); + T_Ok(byte_size_attrib->form.reader.kind == DW_Form_Data1); + T_Ok(byte_size_attrib->form.reader.data.size == 1); + T_Ok(*(U8 *)byte_size_attrib->form.reader.data.str == 1); DW_WriterAttrib *encoding_attrib = byte_size_attrib->next; T_Ok(encoding_attrib->kind == DW_AttribKind_Encoding); - T_Ok(encoding_attrib->reader.form.kind == DW_Form_Data1); - T_Ok(encoding_attrib->reader.form.data.size == 1); - T_Ok(*(U8 *)encoding_attrib->reader.form.data.str == DW_ATE_SignedChar); + T_Ok(encoding_attrib->form.reader.kind == DW_Form_Data1); + T_Ok(encoding_attrib->form.reader.data.size == 1); + T_Ok(*(U8 *)encoding_attrib->form.reader.data.str == DW_ATE_SignedChar); DW_WriterAttrib *name_attrib = encoding_attrib->next; T_Ok(name_attrib->kind == DW_AttribKind_Name); - T_Ok(name_attrib->reader.form.kind == DW_Form_String); - T_Ok(str8_match(name_attrib->reader.form.string, str8_lit("char"), 0)); + T_Ok(name_attrib->form.reader.kind == DW_Form_String); + T_Ok(str8_match(name_attrib->form.reader.string, str8_lit("char"), 0)); } DW_WriterTag *simple_struct_tag = dup_char_type_tag->next; @@ -321,8 +333,8 @@ T_BeginTest(dwarf_writer) DW_WriterAttrib *simple_struct_name = simple_struct_tag->first_attrib; T_Ok(simple_struct_name->kind == DW_AttribKind_Name); - T_Ok(simple_struct_name->reader.form.kind == DW_Form_String); - T_Ok(str8_match(simple_struct_name->reader.form.string, str8_lit("FooBar"), 0)); + T_Ok(simple_struct_name->form.reader.kind == DW_Form_String); + T_Ok(str8_match(simple_struct_name->form.reader.string, str8_lit("FooBar"), 0)); DW_WriterTag *m0_tag = simple_struct_tag->first_child; { @@ -334,19 +346,19 @@ T_BeginTest(dwarf_writer) DW_WriterAttrib *name = m0_tag->first_attrib; T_Ok(name->kind == DW_AttribKind_Name); - T_Ok(name->reader.form.kind == DW_Form_String); - T_Ok(str8_match(name->reader.form.string, str8_lit("m0"), 0)); + T_Ok(name->form.reader.kind == DW_Form_String); + T_Ok(str8_match(name->form.reader.string, str8_lit("m0"), 0)); DW_WriterAttrib *type = name->next; T_Ok(type->kind == DW_AttribKind_Type); - T_Ok(type->reader.form.kind == DW_Form_Ref1); - T_Ok(type->reader.form.ref == 0x20); + T_Ok(type->form.reader.kind == DW_Form_Ref1); + T_Ok(type->form.reader.ref == 0x20); DW_WriterAttrib *data_loc = type->next; T_Ok(data_loc->kind == DW_AttribKind_DataMemberLocation); - T_Ok(data_loc->reader.form.kind == DW_Form_Data1); - T_Ok(data_loc->reader.form.data.size == 1); - T_Ok(*(U8 *)data_loc->reader.form.data.str == 0); + T_Ok(data_loc->form.reader.kind == DW_Form_Data1); + T_Ok(data_loc->form.reader.data.size == 1); + T_Ok(*(U8 *)data_loc->form.reader.data.str == 0); } DW_WriterTag *m1_tag = m0_tag->next; @@ -359,24 +371,24 @@ T_BeginTest(dwarf_writer) DW_WriterAttrib *name = m1_tag->first_attrib; T_Ok(name->kind == DW_AttribKind_Name); - T_Ok(name->reader.form.kind == DW_Form_String); - T_Ok(str8_match(name->reader.form.string, str8_lit("m1"), 0)); + T_Ok(name->form.reader.kind == DW_Form_String); + T_Ok(str8_match(name->form.reader.string, str8_lit("m1"), 0)); DW_WriterAttrib *type = name->next; T_Ok(type->kind == DW_AttribKind_Type); - T_Ok(type->reader.form.kind == DW_Form_Ref1); - T_Ok(type->reader.form.ref == 0x20); + T_Ok(type->form.reader.kind == DW_Form_Ref1); + T_Ok(type->form.reader.ref == 0x20); DW_WriterAttrib *data_loc = type->next; T_Ok(data_loc->kind == DW_AttribKind_DataMemberLocation); - T_Ok(data_loc->reader.form.kind == DW_Form_Data1); - T_Ok(data_loc->reader.form.data.size == 1); - T_Ok(*(U8 *)data_loc->reader.form.data.str == 10); + T_Ok(data_loc->form.reader.kind == DW_Form_Data1); + T_Ok(data_loc->form.reader.data.size == 1); + T_Ok(*(U8 *)data_loc->form.reader.data.str == 10); DW_WriterAttrib *byte_size = data_loc->next; T_Ok(byte_size->kind == DW_AttribKind_ByteSize); - T_Ok(byte_size->reader.form.kind == DW_Form_ImplicitConst); - T_Ok(byte_size->reader.form.implicit_const == 123); + T_Ok(byte_size->form.reader.kind == DW_Form_ImplicitConst); + T_Ok(byte_size->form.reader.implicit_const == 123); } } @@ -388,40 +400,40 @@ T_BeginTest(dwarf_writer) { DW_WriterAttrib *external = main_tag->first_attrib; T_Ok(external->kind == DW_AttribKind_External); - T_Ok(external->reader.form.kind == DW_Form_Flag); - T_Ok(external->reader.form.flag); + T_Ok(external->form.reader.kind == DW_Form_Flag); + T_Ok(external->form.reader.flag); DW_WriterAttrib *prototyped = external->next; T_Ok(prototyped->kind == DW_AttribKind_Prototyped); - T_Ok(prototyped->reader.form.kind == DW_Form_Flag); - T_Ok(prototyped->reader.form.flag); + T_Ok(prototyped->form.reader.kind == DW_Form_Flag); + T_Ok(prototyped->form.reader.flag); DW_WriterAttrib *low_pc = prototyped->next; T_Ok(low_pc->kind == DW_AttribKind_LowPc); - T_Ok(low_pc->reader.form.kind == DW_Form_Addr); - T_Ok(low_pc->reader.form.addr.size == sizeof(U64)); - T_Ok(*(U64 *)low_pc->reader.form.addr.str == 0x14012f2f0); + T_Ok(low_pc->form.reader.kind == DW_Form_Addr); + T_Ok(low_pc->form.reader.addr.size == sizeof(U64)); + T_Ok(*(U64 *)low_pc->form.reader.addr.str == 0x14012f2f0); DW_WriterAttrib *high_pc = low_pc->next; T_Ok(high_pc->kind == DW_AttribKind_HighPc); - T_Ok(high_pc->reader.form.kind == DW_Form_Addr); - T_Ok(high_pc->reader.form.addr.size == sizeof(U64)); - T_Ok(*(U64 *)high_pc->reader.form.addr.str == 0x14012f405); + T_Ok(high_pc->form.reader.kind == DW_Form_Addr); + T_Ok(high_pc->form.reader.addr.size == sizeof(U64)); + T_Ok(*(U64 *)high_pc->form.reader.addr.str == 0x14012f405); DW_WriterAttrib *name = high_pc->next; T_Ok(name->kind == DW_AttribKind_Name); - T_Ok(name->reader.form.kind == DW_Form_String); - T_Ok(str8_match(name->reader.form.string, str8_lit("main"), 0)); + T_Ok(name->form.reader.kind == DW_Form_String); + T_Ok(str8_match(name->form.reader.string, str8_lit("main"), 0)); DW_WriterAttrib *type = name->next; T_Ok(type->kind == DW_AttribKind_Type); - T_Ok(type->reader.form.kind == DW_Form_Ref1); - T_Ok(type->reader.form.ref == simple_struct_tag->info_off); + T_Ok(type->form.reader.kind == DW_Form_Ref1); + T_Ok(type->form.reader.ref == simple_struct_tag->info_off); DW_WriterAttrib *frame_base = type->next; T_Ok(frame_base->kind == DW_AttribKind_FrameBase); - T_Ok(frame_base->reader.form.kind == DW_Form_ExprLoc); - DW_Expr frame_base_expr = dw_expr_from_data(scratch.arena, writer->format, writer->address_size, frame_base->reader.form.exprloc); + T_Ok(frame_base->form.reader.kind == DW_Form_ExprLoc); + DW_Expr frame_base_expr = dw_expr_from_data(scratch.arena, writer->format, writer->address_size, frame_base->form.reader.exprloc); T_Ok(frame_base_expr.count == 1); T_Ok(frame_base_expr.first->opcode == DW_ExprOp_Reg7); T_Ok(frame_base_expr.first->operands == 0);