diff --git a/src/dwarf/dwarf.c b/src/dwarf/dwarf.c index 344cf956..0ead6dc8 100644 --- a/src/dwarf/dwarf.c +++ b/src/dwarf/dwarf.c @@ -959,8 +959,9 @@ dw_form_match(DW_Form a, DW_Form b) switch (a.kind) { case DW_Form_Null: {} break; - case DW_Form_Addr: { is_match = str8_match(a.addr, b.addr, 0); } break; - case DW_Form_String: { is_match = str8_match(a.string, b.string, 0); } break; + case DW_Form_Addr: { is_match = str8_match(a.addr, b.addr, 0); } break; + case DW_Form_String: { is_match = str8_match(a.string, b.string, 0); } break; + case DW_Form_ExprLoc: { is_match = str8_match(a.exprloc, b.exprloc, 0); } break; case DW_Form_Block: case DW_Form_Block1: @@ -1026,7 +1027,5 @@ dw_form_match(DW_Form a, DW_Form b) } return is_match; - - return is_match; } diff --git a/src/dwarf/dwarf.h b/src/dwarf/dwarf.h index ee37de30..587bedf1 100644 --- a/src/dwarf/dwarf.h +++ b/src/dwarf/dwarf.h @@ -494,7 +494,7 @@ typedef enum DW_FormEnum #undef X } DW_FormEnum; -typedef union DW_Form +typedef struct DW_Form { DW_FormKind kind; union { diff --git a/src/dwarf/dwarf_inc.c b/src/dwarf/dwarf_inc.c index 286b7799..de222b03 100644 --- a/src/dwarf/dwarf_inc.c +++ b/src/dwarf/dwarf_inc.c @@ -9,5 +9,6 @@ #include "dwarf/dwarf_unwind.c" #include "dwarf/dwarf_dump.c" #include "dwarf/dwarf_help.c" +#include "dwarf/dwarf_writer.c" #include "dwarf/eh_frame.c" #include "dwarf/eh_dump.c" diff --git a/src/dwarf/dwarf_inc.h b/src/dwarf/dwarf_inc.h index 71c548bf..aeba7dde 100644 --- a/src/dwarf/dwarf_inc.h +++ b/src/dwarf/dwarf_inc.h @@ -12,6 +12,7 @@ #include "dwarf/dwarf_unwind.h" #include "dwarf/dwarf_dump.h" #include "dwarf/dwarf_help.h" +#include "dwarf/dwarf_writer.h" #include "dwarf/eh_frame.h" #include "dwarf/eh_dump.h" diff --git a/src/dwarf/dwarf_writer.c b/src/dwarf/dwarf_writer.c new file mode 100644 index 00000000..9eaaae69 --- /dev/null +++ b/src/dwarf/dwarf_writer.c @@ -0,0 +1,667 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal DW_IntEnc +dw_int_enc_from_sint(S64 v) +{ + if ((S8)v == v) return DW_IntEnc_1Byte; + if ((S16)v == v) return DW_IntEnc_2Byte; + if ((S32)v == v) return DW_IntEnc_4Byte; + return DW_IntEnc_LEB128; +} + +internal DW_IntEnc +dw_int_enc_from_uint(U64 v) +{ + if ((U8)v == v) return DW_IntEnc_1Byte; + if ((U16)v == v) return DW_IntEnc_2Byte; + if ((U32)v == v) return DW_IntEnc_4Byte; + return DW_IntEnc_LEB128; +} + +internal U64 +dw_size_from_sint(S64 v) +{ + DW_IntEnc enc = dw_int_enc_from_sint(v); + switch (enc) { + case DW_IntEnc_Null: break; + case DW_IntEnc_1Byte: return 1; + case DW_IntEnc_2Byte: return 2; + case DW_IntEnc_4Byte: return 4; + case DW_IntEnc_LEB128: return dw_size_from_sleb128(v); + default: InvalidPath; break; + } + return 0; +} + +internal U64 +dw_size_from_uint(U64 v) +{ + DW_IntEnc enc = dw_int_enc_from_uint(v); + switch (enc) { + case DW_IntEnc_Null: break; + case DW_IntEnc_1Byte: return 1; + case DW_IntEnc_2Byte: return 2; + case DW_IntEnc_4Byte: return 4; + case DW_IntEnc_LEB128: return dw_size_from_uleb128(v); + default: InvalidPath; break; + } + 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) +{ + DW_WriterFixupNode *n = push_array(arena, DW_WriterFixupNode, 1); + n->v = v; + SLLQueuePush(list->first, list->last, n); + list->count += 1; + return n; +} + +internal DW_WriterAttrib * +dw_writer_attrib_chunk_list_push(Arena *arena, DW_WriterAttribChunkList *list, U64 cap) +{ + if (list->last == 0 || list->last->count >= list->last->max) { + DW_WriterAttribChunk *n = push_array(arena, DW_WriterAttribChunk, 1); + n->v = push_array_no_zero(arena, DW_WriterAttrib, cap); + n->max = cap; + SLLQueuePush(list->first, list->last, n); + list->chunk_count += 1; + } + + DW_WriterAttrib *result = &list->last->v[list->last->count++]; + MemoryZeroStruct(result); + + list->total_count += 1; + + return result; +} + +internal DW_WriterTag * +dw_writer_tag_chunk_list_push(Arena *arena, DW_WriterTagChunkList *list, U64 cap) +{ + if (list->last == 0 || list->last->count >= list->last->max) { + DW_WriterTagChunk *n = push_array(arena, DW_WriterTagChunk, 1); + n->v = push_array_no_zero(arena, DW_WriterTag, cap); + n->max = cap; + SLLQueuePush(list->first, list->last, n); + list->chunk_count += 1; + } + + DW_WriterTag *result = &list->last->v[list->last->count++]; + MemoryZeroStruct(result); + + list->total_count += 1; + + return result; +} + +internal DW_Writer * +dw_writer_begin(DW_Format format, DW_Version version, DW_CompUnitKind cu_kind, Arch arch) +{ + 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); + for EachElement(i, writer->sections) { + str8_serial_begin(arena, &writer->sections[i].srl); + } + + // write .debug_info header + { + String8List *srl = &writer->sections[DW_Section_Info].srl; + + // length + writer->sections[DW_Section_Info].length = dw_serial_push_length(writer->arena, srl, writer->format, 0); + + // version + str8_serial_push_struct(writer->arena, srl, &writer->version); + + // compile unit kind + str8_serial_push_struct(writer->arena, srl, &writer->cu_kind); + + // address size + str8_serial_push_u8(writer->arena, srl, (U8)writer->address_size); + + // reserve 'abbrev_base' + writer->abbrev_base_info_off = srl->total_size; + dw_serial_push_uint(writer->arena, srl, writer->format, 0); + + if (writer->cu_kind == DW_CompUnitKind_Skeleton) { + // TODO: write skeleton id + NotImplemented; + } + } + + return writer; +} + +internal void +dw_writer_end(DW_Writer **writer_ptr) +{ + arena_release((*writer_ptr)->arena); + *writer_ptr = 0; +} + +internal String8 +dw_make_abbrev_entry(Arena *arena, DW_WriterTag *tag) +{ + Temp scratch = scratch_begin(&arena, 1); + + String8List srl = {0}; + str8_serial_begin(scratch.arena, &srl); + + // tag abbrev + B8 has_children = tag->first_child != 0; + dw_serial_push_uleb128(scratch.arena, &srl, tag->kind); + str8_serial_push_string(scratch.arena, &srl, str8_struct(&has_children)); + + // 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); + } + } + + // closing entry + dw_serial_push_uleb128(scratch.arena, &srl, 0); + dw_serial_push_uleb128(scratch.arena, &srl, 0); + + String8 tag_abbrev = str8_serial_end(arena, &srl); + + scratch_end(scratch); + return tag_abbrev; +} + +internal DW_WriterTag * +dw_writer_tag_begin(DW_Writer *writer, DW_TagKind kind) +{ + DW_WriterTag *tag = dw_writer_tag_chunk_list_push(writer->arena, &writer->tag_chunk_list, 512); + tag->kind = kind; + tag->parent = writer->current; + + // add tag to the tree + if (writer->current) { + SLLQueuePush(writer->current->first_child, writer->current->last_child, tag); + } else { + writer->root = tag; + } + writer->current = tag; + + return tag; +} + +internal void +dw_writer_tag_end(DW_Writer *writer) +{ + // pop to parent + writer->current = writer->current->parent; +} + +internal DW_WriterAttrib * +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; + SLLQueuePush(writer->current->first_attrib, writer->current->last_attrib, attrib); + writer->current->attrib_count += 1; + return attrib; +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_address(DW_Writer *writer, DW_AttribKind kind, U64 address) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_Address, .address = address }); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_block(DW_Writer *writer, DW_AttribKind kind, String8 block) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_Block, .block = str8_copy(writer->arena, block) }); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_data(DW_Writer *writer, DW_AttribKind kind, String8 data) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_Data, .data = str8_copy(writer->arena, data) }); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_string(DW_Writer *writer, DW_AttribKind kind, String8 string) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_String, .string = string }); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_flag(DW_Writer *writer, DW_AttribKind kind, B8 flag) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_Flag, .flag = flag }); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_sint(DW_Writer *writer, DW_AttribKind kind, S64 sint) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_SInt, .sint = sint}); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_uint(DW_Writer *writer, DW_AttribKind kind, U64 uint) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_UInt, .uint = uint }); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_enum(DW_Writer *writer, DW_AttribKind kind, S64 e) +{ + return dw_writer_push_attrib_sint(writer, kind, e); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_ref(DW_Writer *writer, DW_AttribKind kind, DW_WriterTag *ref) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_Ref, .ref = ref }); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_exprloc(DW_Writer *writer, DW_AttribKind kind, String8 exprloc) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_ExprLoc, .exprloc = str8_copy(writer->arena, exprloc) }); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_expression(DW_Writer *writer, DW_AttribKind kind, DW_ExprEnc *encs, U64 encs_count) +{ + String8 exprloc = dw_encode_expr(writer->arena, writer->arch, writer->format, encs, encs_count); + return dw_writer_push_attrib_exprloc(writer, kind, exprloc); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_line_ptr(DW_Writer *writer, DW_AttribKind kind, void *line_ptr) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_LinePtr, .line_ptr = line_ptr }); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_mac_ptr(DW_Writer *writer, DW_AttribKind kind, void *mac_ptr) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_MacPtr, .mac_ptr = mac_ptr }); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_rng_list_ptr(DW_Writer *writer, DW_AttribKind kind, void *rng_list_ptr) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_RngListPtr, .rng_list_ptr = rng_list_ptr }); +} + +internal DW_WriterAttrib * +dw_writer_push_attrib_implicit(DW_Writer *writer, DW_AttribKind kind, S64 implicit) +{ + return dw_writer_push_attrib(writer, kind, (DW_WriterForm){ .kind = DW_WriterFormKind_Implicit, .implicit = implicit }); +} + +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) { + case DW_WriterFormKind_Null: {} break; + case DW_WriterFormKind_Flag: { + attrib->reader.form.kind = DW_Form_Flag; + attrib->reader.form.flag = attrib->writer.form.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; + case DW_IntEnc_1Byte: { + attrib->reader.form.kind = DW_Form_Data1; + attrib->reader.form.data = str8((U8 *)&attrib->writer.form.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)); + } break; + case DW_IntEnc_4Byte: { + attrib->reader.form.kind = DW_Form_Data4; + attrib->reader.form.data = str8((U8 *)&attrib->writer.form.sint, sizeof(U32)); + } break; + case DW_IntEnc_LEB128: { + attrib->reader.form.kind = DW_Form_SData; + attrib->reader.form.sdata = attrib->writer.form.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; + } 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)); + } break; + case DW_WriterFormKind_Ref: { + if (attrib->writer.form.ref->info_off == 0) { + attrib->reader.form.kind = DW_Form_Ref8; + } else { + switch (dw_int_enc_from_uint(attrib->writer.form.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; + } break; + case DW_IntEnc_2Byte: { + attrib->reader.form.kind = DW_Form_Ref2; + attrib->reader.form.ref = (U16)attrib->writer.form.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; + } break; + case DW_IntEnc_LEB128: { + attrib->reader.form.kind = DW_Form_RefUData; + attrib->reader.form.ref = attrib->writer.form.ref->info_off; + } break; + default: InvalidPath; break; + } + } + } break; + case DW_WriterFormKind_LinePtr: { + NotImplemented; + } break; + case DW_WriterFormKind_MacPtr: { + NotImplemented; + } break; + case DW_WriterFormKind_RngListPtr: { + NotImplemented; + } break; + case DW_WriterFormKind_Data: { + NotImplemented; + } break; + case DW_WriterFormKind_Block: { + attrib->reader.form.block = attrib->writer.form.block; + switch (attrib->writer.form.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; + } + } break; + case DW_WriterFormKind_String: { + attrib->reader.form.kind = DW_Form_String; + attrib->reader.form.string = attrib->writer.form.string; + } break; + case DW_WriterFormKind_ExprLoc: { + attrib->reader.form.kind = DW_Form_ExprLoc; + attrib->reader.form.exprloc = attrib->writer.form.exprloc; + } break; + case DW_WriterFormKind_Implicit: { + attrib->reader.form.kind = DW_Form_ImplicitConst; + attrib->reader.form.implicit_const = attrib->writer.form.implicit; + } break; + default: { InvalidPath; } break; + } + } +} + +internal void +dw_writer_emit_tag(DW_Writer *writer, DW_WriterTag *tag) +{ + Assert(tag->info_off == 0); + + dw_lower_attrib_forms(writer, tag); + + // make abbrev id for the tag + U64 abbrev_id; + { + Temp temp = temp_begin(writer->arena); + String8 abbrev_entry = dw_make_abbrev_entry(temp.arena, tag); + + if ( ! hash_table_search_string_u64(writer->abbrev_id_map, abbrev_entry, &abbrev_id)) { + abbrev_id = writer->abbrev_id_map->count + 1; + + hash_table_push_string_u64(writer->arena, writer->abbrev_id_map, abbrev_entry, abbrev_id); + + // push abbrev entry + dw_serial_push_uleb128(writer->arena, &writer->sections[DW_Section_Abbrev].srl, abbrev_id); + str8_serial_push_string(writer->arena, &writer->sections[DW_Section_Abbrev].srl, abbrev_entry); + } else { + temp_end(temp); + } + } + + tag->abbrev_id = abbrev_id; + tag->info_off = writer->sections[DW_Section_Info].srl.total_size; + + // emit tag abbrev id + dw_serial_push_uleb128(writer->arena, &writer->sections[DW_Section_Info].srl, tag->abbrev_id); + + // emit attribs + { + String8List *debug_info = &writer->sections[DW_Section_Info].srl; + for EachNode(attrib, DW_WriterAttrib, tag->first_attrib) { + switch (attrib->reader.form.kind) { + case DW_Form_Addr: { + str8_serial_push_string(writer->arena, debug_info, attrib->reader.form.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); + } 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); + } break; + case DW_Form_String: { + str8_serial_push_cstr(writer->arena, debug_info, attrib->reader.form.string); + } break; + case DW_Form_Flag: { + str8_serial_push_u8(writer->arena, debug_info, attrib->reader.form.flag); + } break; + case DW_Form_SData: { + dw_serial_push_sleb128(writer->arena, debug_info, attrib->reader.form.sdata); + } break; + case DW_Form_Strp: { + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.sec_offset); + } break; + case DW_Form_UData: { + dw_serial_push_uleb128(writer->arena, debug_info, attrib->reader.form.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)); + } else { + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.ref); + } + } break; + case DW_Form_Ref1: { + str8_serial_push_u8(writer->arena, debug_info, (U8)attrib->reader.form.ref); + } break; + case DW_Form_Ref2: { + str8_serial_push_u16(writer->arena, debug_info, (U16)attrib->reader.form.ref); + } break; + case DW_Form_Ref4: { + str8_serial_push_u32(writer->arena, debug_info, (U32)attrib->reader.form.ref); + } break; + case DW_Form_Ref8: { + if (attrib->writer.form.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 }); + } + } break; + case DW_Form_RefUData: { + dw_serial_push_uleb128(writer->arena, debug_info, attrib->reader.form.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); + } 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); + } 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(writer->arena, debug_info, attrib->reader.form.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); + } break; + case DW_Form_Data16: { + str8_serial_push_string(writer->arena, debug_info, attrib->reader.form.data); + } break; + case DW_Form_LineStrp: { + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.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(writer->arena, debug_info, (U8)attrib->reader.form.xval); + } break; + case DW_Form_Strx2: + case DW_Form_Addrx2: { + str8_serial_push_u16(writer->arena, debug_info, (U16)attrib->reader.form.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)); + } break; + case DW_Form_Strx4: + case DW_Form_Addrx4: { + str8_serial_push_u32(writer->arena, debug_info, (U32)attrib->reader.form.xval); + } break; + + case DW_Form_GNU_StrpAlt: { + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.sec_offset); + } break; + case DW_Form_GNU_RefAlt: { + dw_serial_push_uint(writer->arena, debug_info, writer->format, attrib->reader.form.ref); + } break; + + default: InvalidPath; break; + } + } + } + + // write children + for EachNode(child, DW_WriterTag, tag->first_child) { + dw_writer_emit_tag(writer, child); + } + + // close tag + if (tag->first_child) { + dw_serial_push_uleb128(writer->arena, &writer->sections[DW_Section_Info].srl, 0); + } +} + +internal void +dw_writer_emit(DW_Writer *writer) +{ + dw_writer_emit_tag(writer, writer->root); + + // 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); + } + } + } + + // 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; + } + } +} + +#ifdef OBJ_H +internal void +dw_writer_emit_to_obj(DW_Writer *writer, OBJ *obj) +{ + dw_writer_emit(writer); + + // push obj sections + OBJ_Section *sections[DW_Section_Count] = {0}; + for EachElement(i, writer->sections) { + if (writer->sections[i].srl.total_size) { + sections[i] = obj_push_section(obj, dw_name_string_from_section_kind(i), OBJ_SectionFlag_Read|OBJ_SectionFlag_Write); + } + } + + // push OBJ relocation for 'abbrev base' + OBJ_Section *debug_info = sections[DW_Section_Info]; + obj_push_reloc(obj, debug_info, OBJ_RelocKind_SecRel, writer->abbrev_base_info_off, sections[DW_Section_Abbrev]->symbol); + + // copy section data from writer to obj + for EachElement(i, writer->sections) { + if (writer->sections[i].srl.total_size > 0) { + String8 data = str8_serial_end(obj->arena, &writer->sections[i].srl); + str8_list_push(obj->arena, §ions[i]->data, data); + } + } + + // zero out writer sections + MemoryZeroArray(writer->sections); +} +#endif + diff --git a/src/dwarf/dwarf_writer.h b/src/dwarf/dwarf_writer.h new file mode 100644 index 00000000..f2d8ad76 --- /dev/null +++ b/src/dwarf/dwarf_writer.h @@ -0,0 +1,209 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef DWARF_WRITER_H +#define DWARF_WRITER_H + +//////////////////////////////// + +typedef enum +{ + DW_IntEnc_Null, + DW_IntEnc_1Byte, + DW_IntEnc_2Byte, + DW_IntEnc_4Byte, + DW_IntEnc_LEB128, +} DW_IntEnc; + +//////////////////////////////// + +typedef enum +{ + DW_WriterFormKind_Null, + DW_WriterFormKind_Flag, + DW_WriterFormKind_SInt, + DW_WriterFormKind_UInt, + DW_WriterFormKind_Address, + DW_WriterFormKind_Ref, + DW_WriterFormKind_LinePtr, + DW_WriterFormKind_MacPtr, + DW_WriterFormKind_RngListPtr, + DW_WriterFormKind_Data, + DW_WriterFormKind_Block, + DW_WriterFormKind_String, + DW_WriterFormKind_ExprLoc, + DW_WriterFormKind_Implicit, +} DW_WriterFormKind; + +typedef struct DW_WriterForm +{ + DW_WriterFormKind kind; + union { + B8 flag; + S64 sint; + U64 uint; + U64 address; + S64 implicit; + union { + struct { + struct DW_WriterTag *ref; + void *line_ptr; + void *mac_ptr; + void *rng_list_ptr; + }; + struct { + U64 ref_offset; + U64 line_offset; + U64 mac_ptr_offset; + U64 rng_list_offset; + }; + }; + String8 data; + String8 block; + String8 string; + String8 exprloc; + }; +} DW_WriterForm; + +typedef struct DW_WriterAttrib +{ + DW_AttribKindEnum kind; + struct { + DW_WriterForm form; + } writer; + struct { + DW_Form form; + } reader; + struct DW_WriterAttrib *next; +} DW_WriterAttrib; + +typedef struct DW_WriterTag +{ + DW_TagKindEnum kind; + struct DW_WriterTag *next; + struct DW_WriterTag *parent; + struct DW_WriterTag *first_child; + struct DW_WriterTag *last_child; + U64 attrib_count; + U64 abbrev_id; + U64 info_off; + DW_WriterAttrib *first_attrib; + DW_WriterAttrib *last_attrib; +} DW_WriterTag; + +typedef struct DW_WriterTagChunk +{ + U64 count; + U64 max; + DW_WriterTag *v; + struct DW_WriterTagChunk *next; +} DW_WriterTagChunk; + +typedef struct DW_WriterTagChunkList +{ + U64 chunk_count; + U64 total_count; + DW_WriterTagChunk *first; + DW_WriterTagChunk *last; +} DW_WriterTagChunkList; + +typedef struct DW_WriterAttribChunk +{ + U64 count; + U64 max; + DW_WriterAttrib *v; + struct DW_WriterAttribChunk *next; +} DW_WriterAttribChunk; + +typedef struct DW_WriterAttribChunkList +{ + U64 chunk_count; + U64 total_count; + DW_WriterAttribChunk *first; + DW_WriterAttribChunk *last; +} DW_WriterAttribChunkList; + +typedef struct DW_WriterFixup +{ + struct DW_WriterAttrib *attrib; + U8 *value; +} DW_WriterFixup; + +typedef struct DW_WriterFixupNode +{ + DW_WriterFixup v; + struct DW_WriterFixupNode *next; +} DW_WriterFixupNode; + +typedef struct DW_WriterFixupList +{ + U64 count; + DW_WriterFixupNode *first; + DW_WriterFixupNode *last; +} DW_WriterFixupList; + +typedef struct DW_WriterSection +{ + void *length; + String8List srl; +} DW_WriterSection; + +typedef struct DW_Writer +{ + Arena *arena; + 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; + 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); + +//////////////////////////////// + +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 DW_WriterTag * dw_writer_tag_begin(DW_Writer *writer, DW_TagKind kind); +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 ); +internal DW_WriterAttrib * dw_writer_push_attrib_block (DW_Writer *writer, DW_AttribKind kind, String8 block ); +internal DW_WriterAttrib * dw_writer_push_attrib_data (DW_Writer *writer, DW_AttribKind kind, String8 data ); +internal DW_WriterAttrib * dw_writer_push_attrib_string (DW_Writer *writer, DW_AttribKind kind, String8 string ); +internal DW_WriterAttrib * dw_writer_push_attrib_flag (DW_Writer *writer, DW_AttribKind kind, B8 flag ); +internal DW_WriterAttrib * dw_writer_push_attrib_sint (DW_Writer *writer, DW_AttribKind kind, S64 sint ); +internal DW_WriterAttrib * dw_writer_push_attrib_uint (DW_Writer *writer, DW_AttribKind kind, U64 uint ); +internal DW_WriterAttrib * dw_writer_push_attrib_enum (DW_Writer *writer, DW_AttribKind kind, S64 e ); +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_line_ptr (DW_Writer *writer, DW_AttribKind kind, void * 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 ); + +internal void dw_writer_emit(DW_Writer *writer); +#ifdef OBJ_H +internal void dw_writer_emit_to_obj(DW_Writer *writer, OBJ *obj); +#endif + +#endif // DWARF_WRITER_H + diff --git a/src/obj/obj.c b/src/obj/obj.c new file mode 100644 index 00000000..c33577d8 --- /dev/null +++ b/src/obj/obj.c @@ -0,0 +1,68 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal OBJ * +obj_alloc(U64 time_stamp, Arch arch) +{ + Arena *arena = arena_alloc(); + OBJ *obj = push_array(arena, OBJ, 1); + obj->arena = arena; + obj->time_stamp = time_stamp; + obj->arch = arch; + return obj; +} + +internal void +obj_release(OBJ **obj_ptr) +{ + arena_release((*obj_ptr)->arena); + *obj_ptr = 0; +} + +internal OBJ_Symbol * +obj_push_symbol(OBJ *obj, String8 name, OBJ_SymbolScope scope, OBJ_RefKind ref_kind, void *ref) +{ + OBJ_SymbolNode *n = push_array(obj->arena, OBJ_SymbolNode, 1); + SLLQueuePush(obj->symbols.first, obj->symbols.last, n); + obj->symbols.count += 1; + + OBJ_Symbol *v = &n->v; + v->name = push_str8_copy(obj->arena, name); + v->scope = scope; + v->ref_kind = ref_kind; + v->ref = ref; + + return v; +} + +internal OBJ_Section * +obj_push_section(OBJ *obj, String8 name, OBJ_SectionFlags flags) +{ + OBJ_SectionNode *n = push_array(obj->arena, OBJ_SectionNode, 1); + SLLQueuePush(obj->sections.first, obj->sections.last, n); + obj->sections.count += 1; + + OBJ_Section *v = &n->v; + v->name = push_str8_copy(obj->arena, name); + v->flags = flags; + v->symbol = obj_push_symbol(obj, name, OBJ_SymbolScope_Local, OBJ_RefKind_Section, v); + str8_serial_begin(obj->arena, &v->data); + + return v; +} + +internal OBJ_Reloc * +obj_push_reloc(OBJ *obj, OBJ_Section *sec, OBJ_RelocKind kind, U64 offset, OBJ_Symbol *symbol) +{ + OBJ_RelocNode *n = push_array(obj->arena, OBJ_RelocNode, 1); + SLLQueuePush(sec->relocs.first, sec->relocs.last, n); + sec->relocs.count += 1; + + OBJ_Reloc *v = &n->v; + v->kind = kind; + v->offset = offset; + v->symbol = symbol; + + return v; +} + diff --git a/src/obj/obj.h b/src/obj/obj.h new file mode 100644 index 00000000..a5b285e3 --- /dev/null +++ b/src/obj/obj.h @@ -0,0 +1,115 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef OBJ_H +#define OBJ_H + +typedef enum +{ + OBJ_RefKind_Null, + OBJ_RefKind_Section, +} OBJ_RefKind; + +typedef enum +{ + OBJ_SymbolScope_Null, + OBJ_SymbolScope_Local, + OBJ_SymbolScope_Global, +} OBJ_SymbolScope; + +typedef struct OBJ_Symbol +{ + String8 name; + OBJ_SymbolScope scope; + OBJ_RefKind ref_kind; + void *ref; +} OBJ_Symbol; + +typedef struct OBJ_SymbolNode +{ + OBJ_Symbol v; + struct OBJ_SymbolNode *next; +} OBJ_SymbolNode; + +typedef struct OBJ_SymbolList +{ + U64 count; + OBJ_SymbolNode *first; + OBJ_SymbolNode *last; +} OBJ_SymbolList; + +typedef enum +{ + OBJ_RelocKind_Null, + OBJ_RelocKind_SecRel, +} OBJ_RelocKind; + +typedef struct OBJ_Reloc +{ + OBJ_RelocKind kind; + U64 offset; + OBJ_Symbol *symbol; +} OBJ_Reloc; + +typedef struct OBJ_RelocNode +{ + OBJ_Reloc v; + struct OBJ_RelocNode *next; +} OBJ_RelocNode; + +typedef struct OBJ_RelocList +{ + U64 count; + OBJ_RelocNode *first; + OBJ_RelocNode *last; +} OBJ_RelocList; + +typedef enum OBJ_SectionFlags +{ + OBJ_SectionFlag_Read = (1 << 0), + OBJ_SectionFlag_Write = (1 << 1), + OBJ_SectionFlag_Exec = (1 << 2), + OBJ_SectionFlag_Load = (1 << 3), +} OBJ_SectionFlags; + +typedef struct OBJ_Section +{ + String8 name; + String8List data; + OBJ_SectionFlags flags; + OBJ_RelocList relocs; + OBJ_Symbol *symbol; +} OBJ_Section; + +typedef struct OBJ_SectionNode +{ + OBJ_Section v; + struct OBJ_SectionNode *next; +} OBJ_SectionNode; + +typedef struct OBJ_SectionList +{ + U64 count; + OBJ_SectionNode *first; + OBJ_SectionNode *last; +} OBJ_SectionList; + +typedef struct OBJ +{ + Arena *arena; + U64 time_stamp; + Arch arch; + OBJ_SectionList sections; + OBJ_SymbolList symbols; +} OBJ; + +//////////////////////////////// + +internal OBJ * obj_alloc(U64 time_stamp, Arch arch); +internal void obj_release(OBJ **obj_ptr); +internal OBJ_Symbol * obj_push_symbol(OBJ *obj, String8 name, OBJ_SymbolScope scope, OBJ_RefKind ref_kind, void *ref); +internal OBJ_Section * obj_push_section(OBJ *obj, String8 name, OBJ_SectionFlags flags); +internal OBJ_Reloc * obj_push_reloc(OBJ *obj, OBJ_Section *sec, OBJ_RelocKind kind, U64 offset, OBJ_Symbol *symbol); + +#endif // OBJ_H + diff --git a/src/torture/torture_dwarf.c b/src/torture/torture_dwarf.c index 995a3c82..881b835d 100644 --- a/src/torture/torture_dwarf.c +++ b/src/torture/torture_dwarf.c @@ -64,6 +64,378 @@ T_BeginTest(test_leb128) } T_EndTest; +internal B32 +dwt_tags_must_match(DW_WriterTag *writer_tag, DW_TagNode *reader_tag) +{ + B32 is_match = 0; + + // match headers + B32 writer_has_children = writer_tag->first_child != 0; + if (writer_tag->kind != reader_tag->tag.kind) { goto exit; } + if (writer_tag->abbrev_id != reader_tag->tag.abbrev_id) { goto exit; } + if (writer_tag->attrib_count != reader_tag->tag.attribs.count) { goto exit; } + if (writer_tag->info_off != reader_tag->tag.info_off) { goto exit; } + if (writer_has_children != reader_tag->tag.has_children) { goto exit; } + + // match attribs + DW_WriterAttrib *writer_attrib = writer_tag->first_attrib; + 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; } + writer_attrib = writer_attrib->next; + reader_attrib = reader_attrib->next; + } + + // visit children + DW_WriterTag *writer_child = writer_tag->first_child; + DW_TagNode *reader_child = reader_tag->first_child; + while (writer_child) { + if ( ! dwt_tags_must_match(writer_child, reader_child)) { goto exit; } + writer_child = writer_child->next; + reader_child = reader_child->sibling; + } + + is_match = 1; +exit:; + return is_match; +} + +internal DW_Writer * +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); + { + dw_writer_tag_begin(writer, DW_TagKind_CompileUnit); + dw_writer_push_attrib_string(writer, DW_AttribKind_Producer, str8_lit(BUILD_TITLE_STRING_LITERAL)); + dw_writer_push_attrib_enum(writer, DW_AttribKind_Language, DW_Language_C99); + dw_writer_push_attrib_flag(writer, DW_AttribKind_UseUtf8, 1); + { + DW_WriterTag *char_type = dw_writer_tag_begin(writer, DW_TagKind_BaseType); + dw_writer_push_attrib_sint(writer, DW_AttribKind_ByteSize, 1); + dw_writer_push_attrib_enum(writer, DW_AttribKind_Encoding, DW_ATE_SignedChar); + dw_writer_push_attrib_string(writer, DW_AttribKind_Name, str8_lit("char")); + dw_writer_tag_end(writer); + + dw_writer_tag_begin(writer, DW_TagKind_ConstType); + dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, char_type); + dw_writer_tag_end(writer); + + // test abbrev dedup + DW_WriterTag *dup_char_type = dw_writer_tag_begin(writer, DW_TagKind_BaseType); + dw_writer_push_attrib_sint(writer, DW_AttribKind_ByteSize, 1); + dw_writer_push_attrib_enum(writer, DW_AttribKind_Encoding, DW_ATE_SignedChar); + dw_writer_push_attrib_string(writer, DW_AttribKind_Name, str8_lit("char")); + dw_writer_tag_end(writer); + + // simple struct test + DW_WriterTag *simple_struct_tag = dw_writer_tag_begin(writer, DW_TagKind_StructureType); + dw_writer_push_attrib_string(writer, DW_AttribKind_Name, str8_lit("FooBar")); + dw_writer_push_attrib_uint(writer, DW_AttribKind_ByteSize, 90); + { + dw_writer_tag_begin(writer, DW_TagKind_Member); + dw_writer_push_attrib_string(writer, DW_AttribKind_Name, str8_lit("m0")); + dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, char_type); + dw_writer_push_attrib_sint(writer, DW_AttribKind_DataMemberLocation, 0); + dw_writer_tag_end(writer); + + dw_writer_tag_begin(writer, DW_TagKind_Member); + dw_writer_push_attrib_string(writer, DW_AttribKind_Name, str8_lit("m1")); + dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, char_type); + dw_writer_push_attrib_sint(writer, DW_AttribKind_DataMemberLocation, 10); + dw_writer_push_attrib_implicit(writer, DW_AttribKind_ByteSize, 123); + dw_writer_tag_end(writer); + } + dw_writer_tag_end(writer); + + 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_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); + dw_writer_tag_end(writer); + } + dw_writer_tag_end(writer); + } + + { + DW_Input input; + { + 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, 0xc3); + obj_push_symbol(obj, str8_lit("entry"), OBJ_SymbolScope_Global, OBJ_RefKind_Section, text_section); + + dw_writer_emit_to_obj(writer, obj); + + String8 raw_coff = coff_from_obj(scratch.arena, obj); + COFF_FileHeaderInfo obj_coff_header = coff_file_header_info_from_data(raw_coff); + String8 raw_sections = str8_substr(raw_coff, obj_coff_header.section_table_range); + U64 section_count = raw_sections.size / sizeof(COFF_SectionHeader); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)raw_sections.str; + 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); + + obj_release(&obj); + } + + DW_ListUnitInput lu_input = dw_list_unit_input_from_input(scratch.arena, &input); + DW_CompUnit cu = dw_cu_from_info_off(scratch.arena, &input, lu_input, 0, 1); + DW_TagTree tag_tree = dw_tag_tree_from_cu(scratch.arena, &input, &cu); + AssertAlways(dwt_tags_must_match(writer->root, tag_tree.root)); + + } + + scratch_end(scratch); + return writer; +} + +T_BeginTest(dwarf_writer) +{ + DW_Writer *writer = dwt_make_writer(); + + // validate the writer + + T_Ok(writer->current == 0); + T_Ok(writer->format == DW_Format_32Bit); + T_Ok(writer->cu_kind == DW_CompUnitKind_Compile); + T_Ok(writer->version == DW_Version_5); + T_Ok(writer->address_size == 8); + T_Ok(writer->abbrev_base_info_off == 8); + T_Ok(writer->fixups.count == 0); + T_Ok(writer->abbrev_id_map->count == 7); + + DW_WriterTag *comp_unit_tag = writer->root; + { + T_Ok(comp_unit_tag->kind == DW_TagKind_CompileUnit); + T_Ok(comp_unit_tag->next == 0); + T_Ok(comp_unit_tag->parent == 0); + T_Ok(comp_unit_tag->attrib_count == 3); + T_Ok(comp_unit_tag->abbrev_id == 1); + T_Ok(comp_unit_tag->info_off == 0xc); + + 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(BUILD_TITLE_STRING_LITERAL), 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); + + 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); + } + + DW_WriterTag *char_type_tag = comp_unit_tag->first_child; + { + T_Ok(char_type_tag->kind == DW_TagKind_BaseType); + T_Ok(char_type_tag->next != 0); + T_Ok(char_type_tag->parent == comp_unit_tag); + T_Ok(char_type_tag->attrib_count == 3); + T_Ok(char_type_tag->abbrev_id == 2); + T_Ok(char_type_tag->info_off == 0x4d); + T_Ok(char_type_tag->first_attrib != char_type_tag->last_attrib); + + 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); + + 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); + + 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)); + } + + DW_WriterTag *const_type_tag = char_type_tag->next; + { + T_Ok(const_type_tag->kind == DW_TagKind_ConstType); + T_Ok(const_type_tag->next != 0); + T_Ok(const_type_tag->parent == comp_unit_tag); + T_Ok(const_type_tag->attrib_count == 1); + T_Ok(const_type_tag->abbrev_id == 3); + T_Ok(const_type_tag->info_off == 0x55); + T_Ok(const_type_tag->first_attrib && const_type_tag->first_attrib == const_type_tag->last_attrib); + + 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); + } + + DW_WriterTag *dup_char_type_tag = const_type_tag->next; + { + T_Ok(dup_char_type_tag->kind == DW_TagKind_BaseType); + T_Ok(dup_char_type_tag->next != 0); + T_Ok(dup_char_type_tag->parent == comp_unit_tag); + T_Ok(dup_char_type_tag->attrib_count == 3); + T_Ok(dup_char_type_tag->abbrev_id == 2); + T_Ok(dup_char_type_tag->info_off == 0x57); + T_Ok(dup_char_type_tag->first_attrib != dup_char_type_tag->last_attrib); + + 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); + + 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); + + 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)); + } + + DW_WriterTag *simple_struct_tag = dup_char_type_tag->next; + { + T_Ok(simple_struct_tag->kind == DW_TagKind_StructureType); + T_Ok(simple_struct_tag->next != 0); + T_Ok(simple_struct_tag->parent == comp_unit_tag); + T_Ok(simple_struct_tag->attrib_count == 2); + T_Ok(simple_struct_tag->abbrev_id == 4); + T_Ok(simple_struct_tag->info_off == 0x5f); + + 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)); + + DW_WriterTag *m0_tag = simple_struct_tag->first_child; + { + T_Ok(m0_tag->kind == DW_TagKind_Member); + T_Ok(m0_tag->parent == simple_struct_tag); + T_Ok(m0_tag->attrib_count == 3); + T_Ok(m0_tag->info_off == 0x68); + T_Ok(m0_tag->abbrev_id == 5); + + 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)); + + 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 == 0x4d); + + 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); + } + + DW_WriterTag *m1_tag = m0_tag->next; + { + T_Ok(m1_tag->kind == DW_TagKind_Member); + T_Ok(m1_tag->parent == simple_struct_tag); + T_Ok(m1_tag->attrib_count == 4); + T_Ok(m1_tag->info_off == 0x6e); + T_Ok(m1_tag->abbrev_id == 6); + + 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)); + + 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 == 0x4d); + + 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); + + 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); + } + } + + DW_WriterTag *main_tag = simple_struct_tag->next; + T_Ok(main_tag->next == 0); + T_Ok(main_tag->kind == DW_TagKind_SubProgram); + T_Ok(main_tag->parent == main_tag->parent); + T_Ok(main_tag->abbrev_id == 7); + { + 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); + + 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); + + 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); + + 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); + + 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)); + + 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); + + 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_expr.count == 1); + T_Ok(frame_base_expr.first->opcode == DW_ExprOp_Reg7); + T_Ok(frame_base_expr.first->operands == 0); + T_Ok(frame_base_expr.first->size == 1); + T_Ok(frame_base_expr.first->next == 0); + T_Ok(frame_base_expr.first->prev == 0); + } + + dw_writer_end(&writer); +} +T_EndTest; + T_BeginTest(value_in_register) { // setup register context diff --git a/src/torture/torture_main.c b/src/torture/torture_main.c index 75ecb3ba..b7c2735c 100644 --- a/src/torture/torture_main.c +++ b/src/torture/torture_main.c @@ -11,6 +11,7 @@ #include "base/base_inc.h" #include "os/os_inc.h" +#include "obj/obj.h" #include "linker/base_ext/base_core.h" #include "linker/base_ext/base_arena.h" #include "linker/base_ext/base_arrays.h" @@ -34,6 +35,7 @@ #include "base/base_inc.c" #include "os/os_inc.c" +#include "obj/obj.c" #include "linker/hash_table.c" #include "linker/base_ext/base_core.c" #include "linker/base_ext/base_arena.c"