| author | |
| committer | |
| log | ba17552b4eb8def495053013eebbe39fc324c8ae |
| tree | 33f871441f432bfdee7f6f555b95062be565ded8 |
| parent | 38c161afabe1dc7d0df7ad981d70b962ef87120a |
Hook up Elf and MachO linkers to the new solution.10 files changed, 1786 insertions(+), 2485 deletions(-)
src/Module.zig+4-4| ... | @@ -4439,8 +4439,8 @@ pub fn clearDecl( | ... | @@ -4439,8 +4439,8 @@ pub fn clearDecl( |
| 4439 | }; | 4439 | }; |
| 4440 | decl.fn_link = switch (mod.comp.bin_file.tag) { | 4440 | decl.fn_link = switch (mod.comp.bin_file.tag) { |
| 4441 | .coff => .{ .coff = {} }, | 4441 | .coff => .{ .coff = {} }, |
| 4442 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, | 4442 | .elf => .{ .elf = link.File.Dwarf.SrcFn.empty }, |
| 4443 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, | 4443 | .macho => .{ .macho = link.File.Dwarf.SrcFn.empty }, |
| 4444 | .plan9 => .{ .plan9 = {} }, | 4444 | .plan9 => .{ .plan9 = {} }, |
| 4445 | .c => .{ .c = {} }, | 4445 | .c => .{ .c = {} }, |
| 4446 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, | 4446 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, |
| ... | @@ -4776,8 +4776,8 @@ pub fn allocateNewDecl( | ... | @@ -4776,8 +4776,8 @@ pub fn allocateNewDecl( |
| 4776 | }, | 4776 | }, |
| 4777 | .fn_link = switch (mod.comp.bin_file.tag) { | 4777 | .fn_link = switch (mod.comp.bin_file.tag) { |
| 4778 | .coff => .{ .coff = {} }, | 4778 | .coff => .{ .coff = {} }, |
| 4779 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, | 4779 | .elf => .{ .elf = link.File.Dwarf.SrcFn.empty }, |
| 4780 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, | 4780 | .macho => .{ .macho = link.File.Dwarf.SrcFn.empty }, |
| 4781 | .plan9 => .{ .plan9 = {} }, | 4781 | .plan9 => .{ .plan9 = {} }, |
| 4782 | .c => .{ .c = {} }, | 4782 | .c => .{ .c = {} }, |
| 4783 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, | 4783 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, |
src/arch/arm/Emit.zig+2-2| ... | @@ -409,7 +409,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { | ... | @@ -409,7 +409,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { |
| 409 | switch (self.debug_output) { | 409 | switch (self.debug_output) { |
| 410 | .dwarf => |dbg_out| { | 410 | .dwarf => |dbg_out| { |
| 411 | try dbg_out.dbg_info.ensureUnusedCapacity(3); | 411 | try dbg_out.dbg_info.ensureUnusedCapacity(3); |
| 412 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter); | 412 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); |
| 413 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc | 413 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 414 | 1, // ULEB128 dwarf expression length | 414 | 1, // ULEB128 dwarf expression length |
| 415 | reg.dwarfLocOp(), | 415 | reg.dwarfLocOp(), |
| ... | @@ -440,7 +440,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { | ... | @@ -440,7 +440,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { |
| 440 | else => unreachable, | 440 | else => unreachable, |
| 441 | }; | 441 | }; |
| 442 | 442 | ||
| 443 | try dbg_out.dbg_info.append(link.File.Elf.abbrev_parameter); | 443 | try dbg_out.dbg_info.append(link.File.Dwarf.abbrev_parameter); |
| 444 | 444 | ||
| 445 | // Get length of the LEB128 stack offset | 445 | // Get length of the LEB128 stack offset |
| 446 | var counting_writer = std.io.countingWriter(std.io.null_writer); | 446 | var counting_writer = std.io.countingWriter(std.io.null_writer); |
src/arch/riscv64/CodeGen.zig+1-1| ... | @@ -1384,7 +1384,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32 | ... | @@ -1384,7 +1384,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32 |
| 1384 | switch (self.debug_output) { | 1384 | switch (self.debug_output) { |
| 1385 | .dwarf => |dbg_out| { | 1385 | .dwarf => |dbg_out| { |
| 1386 | try dbg_out.dbg_info.ensureUnusedCapacity(3); | 1386 | try dbg_out.dbg_info.ensureUnusedCapacity(3); |
| 1387 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter); | 1387 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); |
| 1388 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc | 1388 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 1389 | 1, // ULEB128 dwarf expression length | 1389 | 1, // ULEB128 dwarf expression length |
| 1390 | reg.dwarfLocOp(), | 1390 | reg.dwarfLocOp(), |
src/arch/x86_64/Emit.zig+2-16| ... | @@ -1019,14 +1019,7 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 | ... | @@ -1019,14 +1019,7 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 |
| 1019 | switch (emit.debug_output) { | 1019 | switch (emit.debug_output) { |
| 1020 | .dwarf => |dbg_out| { | 1020 | .dwarf => |dbg_out| { |
| 1021 | try dbg_out.dbg_info.ensureUnusedCapacity(3); | 1021 | try dbg_out.dbg_info.ensureUnusedCapacity(3); |
| 1022 | 1022 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); | |
| 1023 | // TODO this will go away once we pull DWARF into a cross-platform module. | ||
| 1024 | if (emit.bin_file.cast(link.File.MachO)) |_| { | ||
| 1025 | dbg_out.dbg_info.appendAssumeCapacity(link.File.MachO.DebugSymbols.abbrev_parameter); | ||
| 1026 | } else if (emit.bin_file.cast(link.File.Elf)) |_| { | ||
| 1027 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter); | ||
| 1028 | } else return emit.fail("TODO DWARF in non-MachO and non-ELF backend", .{}); | ||
| 1029 | |||
| 1030 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc | 1023 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 1031 | 1, // ULEB128 dwarf expression length | 1024 | 1, // ULEB128 dwarf expression length |
| 1032 | reg.dwarfLocOp(), | 1025 | reg.dwarfLocOp(), |
| ... | @@ -1049,14 +1042,7 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 | ... | @@ -1049,14 +1042,7 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 |
| 1049 | // for example when -fomit-frame-pointer is set. | 1042 | // for example when -fomit-frame-pointer is set. |
| 1050 | const disp = @intCast(i32, max_stack) - off + 16; | 1043 | const disp = @intCast(i32, max_stack) - off + 16; |
| 1051 | try dbg_out.dbg_info.ensureUnusedCapacity(8); | 1044 | try dbg_out.dbg_info.ensureUnusedCapacity(8); |
| 1052 | 1045 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); | |
| 1053 | // TODO this will go away once we pull DWARF into a cross-platform module. | ||
| 1054 | if (emit.bin_file.cast(link.File.MachO)) |_| { | ||
| 1055 | dbg_out.dbg_info.appendAssumeCapacity(link.File.MachO.DebugSymbols.abbrev_parameter); | ||
| 1056 | } else if (emit.bin_file.cast(link.File.Elf)) |_| { | ||
| 1057 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter); | ||
| 1058 | } else return emit.fail("TODO DWARF in non-MachO and non-ELF backend", .{}); | ||
| 1059 | |||
| 1060 | const fixup = dbg_out.dbg_info.items.len; | 1046 | const fixup = dbg_out.dbg_info.items.len; |
| 1061 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc | 1047 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 1062 | 1, // we will backpatch it after we encode the displacement in LEB128 | 1048 | 1, // we will backpatch it after we encode the displacement in LEB128 |
src/link.zig+3-2| ... | @@ -221,9 +221,9 @@ pub const File = struct { | ... | @@ -221,9 +221,9 @@ pub const File = struct { |
| 221 | }; | 221 | }; |
| 222 | 222 | ||
| 223 | pub const LinkFn = union { | 223 | pub const LinkFn = union { |
| 224 | elf: Elf.SrcFn, | 224 | elf: Dwarf.SrcFn, |
| 225 | coff: Coff.SrcFn, | 225 | coff: Coff.SrcFn, |
| 226 | macho: MachO.SrcFn, | 226 | macho: Dwarf.SrcFn, |
| 227 | plan9: void, | 227 | plan9: void, |
| 228 | c: void, | 228 | c: void, |
| 229 | wasm: Wasm.FnData, | 229 | wasm: Wasm.FnData, |
| ... | @@ -915,6 +915,7 @@ pub const File = struct { | ... | @@ -915,6 +915,7 @@ pub const File = struct { |
| 915 | pub const SpirV = @import("link/SpirV.zig"); | 915 | pub const SpirV = @import("link/SpirV.zig"); |
| 916 | pub const Wasm = @import("link/Wasm.zig"); | 916 | pub const Wasm = @import("link/Wasm.zig"); |
| 917 | pub const NvPtx = @import("link/NvPtx.zig"); | 917 | pub const NvPtx = @import("link/NvPtx.zig"); |
| 918 | pub const Dwarf = @import("link/Dwarf.zig"); | ||
| 918 | }; | 919 | }; |
| 919 | 920 | ||
| 920 | pub fn determineMode(options: Options) fs.File.Mode { | 921 | pub fn determineMode(options: Options) fs.File.Mode { |
src/link/Dwarf.zig created+1625| ... | @@ -0,0 +1,1625 @@ | ||
| 1 | const Dwarf = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const builtin = @import("builtin"); | ||
| 5 | const assert = std.debug.assert; | ||
| 6 | const fs = std.fs; | ||
| 7 | const leb128 = std.leb; | ||
| 8 | const log = std.log.scoped(.dwarf); | ||
| 9 | const mem = std.mem; | ||
| 10 | |||
| 11 | const link = @import("../link.zig"); | ||
| 12 | const trace = @import("../tracy.zig").trace; | ||
| 13 | |||
| 14 | const Allocator = mem.Allocator; | ||
| 15 | const DW = std.dwarf; | ||
| 16 | const File = link.File; | ||
| 17 | const LinkBlock = File.LinkBlock; | ||
| 18 | const LinkFn = File.LinkFn; | ||
| 19 | const Module = @import("../Module.zig"); | ||
| 20 | const Value = @import("../value.zig").Value; | ||
| 21 | const Type = @import("../type.zig").Type; | ||
| 22 | |||
| 23 | allocator: Allocator, | ||
| 24 | tag: File.Tag, | ||
| 25 | ptr_width: PtrWidth, | ||
| 26 | target: std.Target, | ||
| 27 | |||
| 28 | /// A list of `File.LinkFn` whose Line Number Programs have surplus capacity. | ||
| 29 | /// This is the same concept as `text_block_free_list`; see those doc comments. | ||
| 30 | dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{}, | ||
| 31 | dbg_line_fn_first: ?*SrcFn = null, | ||
| 32 | dbg_line_fn_last: ?*SrcFn = null, | ||
| 33 | |||
| 34 | /// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity. /// This is the same concept as `text_block_free_list`; see those doc comments. | ||
| 35 | dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*DebugInfoAtom, void) = .{}, | ||
| 36 | dbg_info_decl_first: ?*DebugInfoAtom = null, | ||
| 37 | dbg_info_decl_last: ?*DebugInfoAtom = null, | ||
| 38 | |||
| 39 | abbrev_table_offset: ?u64 = null, | ||
| 40 | |||
| 41 | /// Table of debug symbol names. | ||
| 42 | strtab: std.ArrayListUnmanaged(u8) = .{}, | ||
| 43 | |||
| 44 | pub const DebugInfoAtom = struct { | ||
| 45 | /// Previous/next linked list pointers. | ||
| 46 | /// This is the linked list node for this Decl's corresponding .debug_info tag. | ||
| 47 | prev: ?*DebugInfoAtom, | ||
| 48 | next: ?*DebugInfoAtom, | ||
| 49 | /// Offset into .debug_info pointing to the tag for this Decl. | ||
| 50 | off: u32, | ||
| 51 | /// Size of the .debug_info tag for this Decl, not including padding. | ||
| 52 | len: u32, | ||
| 53 | }; | ||
| 54 | |||
| 55 | pub const SrcFn = struct { | ||
| 56 | /// Offset from the beginning of the Debug Line Program header that contains this function. | ||
| 57 | off: u32, | ||
| 58 | /// Size of the line number program component belonging to this function, not | ||
| 59 | /// including padding. | ||
| 60 | len: u32, | ||
| 61 | |||
| 62 | /// Points to the previous and next neighbors, based on the offset from .debug_line. | ||
| 63 | /// This can be used to find, for example, the capacity of this `SrcFn`. | ||
| 64 | prev: ?*SrcFn, | ||
| 65 | next: ?*SrcFn, | ||
| 66 | |||
| 67 | pub const empty: SrcFn = .{ | ||
| 68 | .off = 0, | ||
| 69 | .len = 0, | ||
| 70 | .prev = null, | ||
| 71 | .next = null, | ||
| 72 | }; | ||
| 73 | }; | ||
| 74 | |||
| 75 | pub const PtrWidth = enum { p32, p64 }; | ||
| 76 | |||
| 77 | pub const abbrev_compile_unit = 1; | ||
| 78 | pub const abbrev_subprogram = 2; | ||
| 79 | pub const abbrev_subprogram_retvoid = 3; | ||
| 80 | pub const abbrev_base_type = 4; | ||
| 81 | pub const abbrev_ptr_type = 5; | ||
| 82 | pub const abbrev_struct_type = 6; | ||
| 83 | pub const abbrev_struct_member = 7; | ||
| 84 | pub const abbrev_pad1 = 8; | ||
| 85 | pub const abbrev_parameter = 9; | ||
| 86 | |||
| 87 | /// The reloc offset for the virtual address of a function in its Line Number Program. | ||
| 88 | /// Size is a virtual address integer. | ||
| 89 | const dbg_line_vaddr_reloc_index = 3; | ||
| 90 | /// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram. | ||
| 91 | /// Size is a virtual address integer. | ||
| 92 | const dbg_info_low_pc_reloc_index = 1; | ||
| 93 | |||
| 94 | const min_nop_size = 2; | ||
| 95 | |||
| 96 | /// When allocating, the ideal_capacity is calculated by | ||
| 97 | /// actual_capacity + (actual_capacity / ideal_factor) | ||
| 98 | const ideal_factor = 3; | ||
| 99 | |||
| 100 | pub fn init(allocator: Allocator, tag: File.Tag, target: std.Target) Dwarf { | ||
| 101 | const ptr_width: PtrWidth = switch (target.cpu.arch.ptrBitWidth()) { | ||
| 102 | 0...32 => .p32, | ||
| 103 | 33...64 => .p64, | ||
| 104 | else => unreachable, | ||
| 105 | }; | ||
| 106 | return Dwarf{ | ||
| 107 | .allocator = allocator, | ||
| 108 | .tag = tag, | ||
| 109 | .ptr_width = ptr_width, | ||
| 110 | .target = target, | ||
| 111 | }; | ||
| 112 | } | ||
| 113 | |||
| 114 | pub fn deinit(self: *Dwarf) void { | ||
| 115 | const gpa = self.allocator; | ||
| 116 | self.dbg_line_fn_free_list.deinit(gpa); | ||
| 117 | self.dbg_info_decl_free_list.deinit(gpa); | ||
| 118 | self.strtab.deinit(gpa); | ||
| 119 | } | ||
| 120 | |||
| 121 | pub const DeclDebugBuffers = struct { | ||
| 122 | dbg_line_buffer: std.ArrayList(u8), | ||
| 123 | dbg_info_buffer: std.ArrayList(u8), | ||
| 124 | dbg_info_type_relocs: File.DbgInfoTypeRelocsTable, | ||
| 125 | }; | ||
| 126 | |||
| 127 | pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { | ||
| 128 | const tracy = trace(@src()); | ||
| 129 | defer tracy.end(); | ||
| 130 | |||
| 131 | const decl_name = try decl.getFullyQualifiedName(self.allocator); | ||
| 132 | defer self.allocator.free(decl_name); | ||
| 133 | |||
| 134 | log.debug("initDeclDebugInfo {s}{*}", .{ decl_name, decl }); | ||
| 135 | |||
| 136 | const gpa = self.allocator; | ||
| 137 | var dbg_line_buffer = std.ArrayList(u8).init(gpa); | ||
| 138 | var dbg_info_buffer = std.ArrayList(u8).init(gpa); | ||
| 139 | var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{}; | ||
| 140 | |||
| 141 | assert(decl.has_tv); | ||
| 142 | |||
| 143 | switch (decl.ty.zigTypeTag()) { | ||
| 144 | .Fn => { | ||
| 145 | // For functions we need to add a prologue to the debug line program. | ||
| 146 | try dbg_line_buffer.ensureTotalCapacity(26); | ||
| 147 | |||
| 148 | const func = decl.val.castTag(.function).?.data; | ||
| 149 | log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{ | ||
| 150 | decl.src_line, | ||
| 151 | func.lbrace_line, | ||
| 152 | func.rbrace_line, | ||
| 153 | }); | ||
| 154 | const line = @intCast(u28, decl.src_line + func.lbrace_line); | ||
| 155 | |||
| 156 | const ptr_width_bytes = self.ptrWidthBytes(); | ||
| 157 | dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{ | ||
| 158 | DW.LNS.extended_op, | ||
| 159 | ptr_width_bytes + 1, | ||
| 160 | DW.LNE.set_address, | ||
| 161 | }); | ||
| 162 | // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`. | ||
| 163 | assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len); | ||
| 164 | dbg_line_buffer.items.len += ptr_width_bytes; | ||
| 165 | |||
| 166 | dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line); | ||
| 167 | // This is the "relocatable" relative line offset from the previous function's end curly | ||
| 168 | // to this function's begin curly. | ||
| 169 | assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len); | ||
| 170 | // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later. | ||
| 171 | leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line); | ||
| 172 | |||
| 173 | dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file); | ||
| 174 | assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len); | ||
| 175 | // Once we support more than one source file, this will have the ability to be more | ||
| 176 | // than one possible value. | ||
| 177 | const file_index = 1; | ||
| 178 | leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index); | ||
| 179 | |||
| 180 | // Emit a line for the begin curly with prologue_end=false. The codegen will | ||
| 181 | // do the work of setting prologue_end=true and epilogue_begin=true. | ||
| 182 | dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy); | ||
| 183 | |||
| 184 | // .debug_info subprogram | ||
| 185 | const decl_name_with_null = decl_name[0 .. decl_name.len + 1]; | ||
| 186 | try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len); | ||
| 187 | |||
| 188 | const fn_ret_type = decl.ty.fnReturnType(); | ||
| 189 | const fn_ret_has_bits = fn_ret_type.hasRuntimeBits(); | ||
| 190 | if (fn_ret_has_bits) { | ||
| 191 | dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram); | ||
| 192 | } else { | ||
| 193 | dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid); | ||
| 194 | } | ||
| 195 | // These get overwritten after generating the machine code. These values are | ||
| 196 | // "relocations" and have to be in this fixed place so that functions can be | ||
| 197 | // moved in virtual address space. | ||
| 198 | assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len); | ||
| 199 | dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT.low_pc, DW.FORM.addr | ||
| 200 | assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len); | ||
| 201 | dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 | ||
| 202 | if (fn_ret_has_bits) { | ||
| 203 | const gop = try dbg_info_type_relocs.getOrPut(gpa, fn_ret_type); | ||
| 204 | if (!gop.found_existing) { | ||
| 205 | gop.value_ptr.* = .{ | ||
| 206 | .off = undefined, | ||
| 207 | .relocs = .{}, | ||
| 208 | }; | ||
| 209 | } | ||
| 210 | try gop.value_ptr.relocs.append(gpa, @intCast(u32, dbg_info_buffer.items.len)); | ||
| 211 | dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 | ||
| 212 | } | ||
| 213 | dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string | ||
| 214 | |||
| 215 | }, | ||
| 216 | else => { | ||
| 217 | // TODO implement .debug_info for global variables | ||
| 218 | }, | ||
| 219 | } | ||
| 220 | |||
| 221 | return DeclDebugBuffers{ | ||
| 222 | .dbg_info_buffer = dbg_info_buffer, | ||
| 223 | .dbg_line_buffer = dbg_line_buffer, | ||
| 224 | .dbg_info_type_relocs = dbg_info_type_relocs, | ||
| 225 | }; | ||
| 226 | } | ||
| 227 | |||
| 228 | pub fn commitDeclDebugInfo( | ||
| 229 | self: *Dwarf, | ||
| 230 | file: *File, | ||
| 231 | module: *Module, | ||
| 232 | decl: *Module.Decl, | ||
| 233 | sym_addr: u64, | ||
| 234 | sym_size: u64, | ||
| 235 | debug_buffers: *DeclDebugBuffers, | ||
| 236 | ) !void { | ||
| 237 | const tracy = trace(@src()); | ||
| 238 | defer tracy.end(); | ||
| 239 | |||
| 240 | const gpa = self.allocator; | ||
| 241 | var dbg_line_buffer = &debug_buffers.dbg_line_buffer; | ||
| 242 | var dbg_info_buffer = &debug_buffers.dbg_info_buffer; | ||
| 243 | var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs; | ||
| 244 | |||
| 245 | const target_endian = self.target.cpu.arch.endian(); | ||
| 246 | |||
| 247 | assert(decl.has_tv); | ||
| 248 | switch (decl.ty.zigTypeTag()) { | ||
| 249 | .Fn => { | ||
| 250 | // Since the Decl is a function, we need to update the .debug_line program. | ||
| 251 | // Perform the relocations based on vaddr. | ||
| 252 | switch (self.ptr_width) { | ||
| 253 | .p32 => { | ||
| 254 | { | ||
| 255 | const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4]; | ||
| 256 | mem.writeInt(u32, ptr, @intCast(u32, sym_addr), target_endian); | ||
| 257 | } | ||
| 258 | { | ||
| 259 | const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4]; | ||
| 260 | mem.writeInt(u32, ptr, @intCast(u32, sym_addr), target_endian); | ||
| 261 | } | ||
| 262 | }, | ||
| 263 | .p64 => { | ||
| 264 | { | ||
| 265 | const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8]; | ||
| 266 | mem.writeInt(u64, ptr, sym_addr, target_endian); | ||
| 267 | } | ||
| 268 | { | ||
| 269 | const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8]; | ||
| 270 | mem.writeInt(u64, ptr, sym_addr, target_endian); | ||
| 271 | } | ||
| 272 | }, | ||
| 273 | } | ||
| 274 | { | ||
| 275 | const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4]; | ||
| 276 | mem.writeInt(u32, ptr, @intCast(u32, sym_size), target_endian); | ||
| 277 | } | ||
| 278 | |||
| 279 | try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence }); | ||
| 280 | |||
| 281 | // Now we have the full contents and may allocate a region to store it. | ||
| 282 | |||
| 283 | // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for | ||
| 284 | // `TextBlock` and the .debug_info. If you are editing this logic, you | ||
| 285 | // probably need to edit that logic too. | ||
| 286 | const src_fn = switch (self.tag) { | ||
| 287 | .elf => &decl.fn_link.elf, | ||
| 288 | .macho => &decl.fn_link.macho, | ||
| 289 | else => unreachable, // TODO | ||
| 290 | }; | ||
| 291 | src_fn.len = @intCast(u32, dbg_line_buffer.items.len); | ||
| 292 | |||
| 293 | if (self.dbg_line_fn_last) |last| blk: { | ||
| 294 | if (src_fn == last) break :blk; | ||
| 295 | if (src_fn.next) |next| { | ||
| 296 | // Update existing function - non-last item. | ||
| 297 | if (src_fn.off + src_fn.len + min_nop_size > next.off) { | ||
| 298 | // It grew too big, so we move it to a new location. | ||
| 299 | if (src_fn.prev) |prev| { | ||
| 300 | self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {}; | ||
| 301 | prev.next = src_fn.next; | ||
| 302 | } | ||
| 303 | next.prev = src_fn.prev; | ||
| 304 | src_fn.next = null; | ||
| 305 | // Populate where it used to be with NOPs. | ||
| 306 | switch (self.tag) { | ||
| 307 | .elf => { | ||
| 308 | const elf_file = file.cast(File.Elf).?; | ||
| 309 | const debug_line_sect = &elf_file.sections.items[elf_file.debug_line_section_index.?]; | ||
| 310 | const file_pos = debug_line_sect.sh_offset + src_fn.off; | ||
| 311 | try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len); | ||
| 312 | }, | ||
| 313 | .macho => { | ||
| 314 | const macho_file = file.cast(File.MachO).?; | ||
| 315 | const d_sym = &macho_file.d_sym.?; | ||
| 316 | const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; | ||
| 317 | const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?]; | ||
| 318 | const file_pos = debug_line_sect.offset + src_fn.off; | ||
| 319 | try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len); | ||
| 320 | }, | ||
| 321 | else => unreachable, | ||
| 322 | } | ||
| 323 | // TODO Look at the free list before appending at the end. | ||
| 324 | src_fn.prev = last; | ||
| 325 | last.next = src_fn; | ||
| 326 | self.dbg_line_fn_last = src_fn; | ||
| 327 | |||
| 328 | src_fn.off = last.off + padToIdeal(last.len); | ||
| 329 | } | ||
| 330 | } else if (src_fn.prev == null) { | ||
| 331 | // Append new function. | ||
| 332 | // TODO Look at the free list before appending at the end. | ||
| 333 | src_fn.prev = last; | ||
| 334 | last.next = src_fn; | ||
| 335 | self.dbg_line_fn_last = src_fn; | ||
| 336 | |||
| 337 | src_fn.off = last.off + padToIdeal(last.len); | ||
| 338 | } | ||
| 339 | } else { | ||
| 340 | // This is the first function of the Line Number Program. | ||
| 341 | self.dbg_line_fn_first = src_fn; | ||
| 342 | self.dbg_line_fn_last = src_fn; | ||
| 343 | |||
| 344 | src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(module)); | ||
| 345 | } | ||
| 346 | |||
| 347 | const last_src_fn = self.dbg_line_fn_last.?; | ||
| 348 | const needed_size = last_src_fn.off + last_src_fn.len; | ||
| 349 | const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0; | ||
| 350 | const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0; | ||
| 351 | |||
| 352 | // We only have support for one compilation unit so far, so the offsets are directly | ||
| 353 | // from the .debug_line section. | ||
| 354 | switch (self.tag) { | ||
| 355 | .elf => { | ||
| 356 | const elf_file = file.cast(File.Elf).?; | ||
| 357 | const debug_line_sect = &elf_file.sections.items[elf_file.debug_line_section_index.?]; | ||
| 358 | if (needed_size != debug_line_sect.sh_size) { | ||
| 359 | if (needed_size > elf_file.allocatedSize(debug_line_sect.sh_offset)) { | ||
| 360 | const new_offset = elf_file.findFreeSpace(needed_size, 1); | ||
| 361 | const existing_size = last_src_fn.off; | ||
| 362 | log.debug("moving .debug_line section: {d} bytes from 0x{x} to 0x{x}", .{ | ||
| 363 | existing_size, | ||
| 364 | debug_line_sect.sh_offset, | ||
| 365 | new_offset, | ||
| 366 | }); | ||
| 367 | const amt = try elf_file.base.file.?.copyRangeAll( | ||
| 368 | debug_line_sect.sh_offset, | ||
| 369 | elf_file.base.file.?, | ||
| 370 | new_offset, | ||
| 371 | existing_size, | ||
| 372 | ); | ||
| 373 | if (amt != existing_size) return error.InputOutput; | ||
| 374 | debug_line_sect.sh_offset = new_offset; | ||
| 375 | } | ||
| 376 | debug_line_sect.sh_size = needed_size; | ||
| 377 | elf_file.shdr_table_dirty = true; // TODO look into making only the one section dirty | ||
| 378 | elf_file.debug_line_header_dirty = true; | ||
| 379 | } | ||
| 380 | const file_pos = debug_line_sect.sh_offset + src_fn.off; | ||
| 381 | try pwriteDbgLineNops( | ||
| 382 | elf_file.base.file.?, | ||
| 383 | file_pos, | ||
| 384 | prev_padding_size, | ||
| 385 | dbg_line_buffer.items, | ||
| 386 | next_padding_size, | ||
| 387 | ); | ||
| 388 | }, | ||
| 389 | .macho => { | ||
| 390 | const macho_file = file.cast(File.MachO).?; | ||
| 391 | const d_sym = &macho_file.d_sym.?; | ||
| 392 | const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; | ||
| 393 | const debug_line_sect = &dwarf_segment.sections.items[d_sym.debug_line_section_index.?]; | ||
| 394 | if (needed_size != debug_line_sect.size) { | ||
| 395 | if (needed_size > d_sym.allocatedSize(debug_line_sect.offset)) { | ||
| 396 | const new_offset = d_sym.findFreeSpace(needed_size, 1); | ||
| 397 | const existing_size = last_src_fn.off; | ||
| 398 | |||
| 399 | log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{ | ||
| 400 | existing_size, | ||
| 401 | debug_line_sect.offset, | ||
| 402 | new_offset, | ||
| 403 | }); | ||
| 404 | |||
| 405 | try File.MachO.copyRangeAllOverlappingAlloc( | ||
| 406 | gpa, | ||
| 407 | d_sym.file, | ||
| 408 | debug_line_sect.offset, | ||
| 409 | new_offset, | ||
| 410 | existing_size, | ||
| 411 | ); | ||
| 412 | |||
| 413 | debug_line_sect.offset = @intCast(u32, new_offset); | ||
| 414 | debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff; | ||
| 415 | } | ||
| 416 | debug_line_sect.size = needed_size; | ||
| 417 | d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty | ||
| 418 | d_sym.debug_line_header_dirty = true; | ||
| 419 | } | ||
| 420 | const file_pos = debug_line_sect.offset + src_fn.off; | ||
| 421 | try pwriteDbgLineNops( | ||
| 422 | d_sym.file, | ||
| 423 | file_pos, | ||
| 424 | prev_padding_size, | ||
| 425 | dbg_line_buffer.items, | ||
| 426 | next_padding_size, | ||
| 427 | ); | ||
| 428 | }, | ||
| 429 | else => unreachable, | ||
| 430 | } | ||
| 431 | |||
| 432 | // .debug_info - End the TAG.subprogram children. | ||
| 433 | try dbg_info_buffer.append(0); | ||
| 434 | }, | ||
| 435 | else => {}, | ||
| 436 | } | ||
| 437 | |||
| 438 | if (dbg_info_buffer.items.len == 0) | ||
| 439 | return; | ||
| 440 | |||
| 441 | // We need this for the duration of this function only so that for composite | ||
| 442 | // types such as []const u32, if the type *u32 is non-existent, we create | ||
| 443 | // it synthetically and store the backing bytes in this arena. After we are | ||
| 444 | // done with the relocations, we can safely deinit the entire memory slab. | ||
| 445 | // TODO currently, we do not store the relocations for future use, however, | ||
| 446 | // if that is the case, we should move memory management to a higher scope, | ||
| 447 | // such as linker scope, or whatnot. | ||
| 448 | var dbg_type_arena = std.heap.ArenaAllocator.init(gpa); | ||
| 449 | defer dbg_type_arena.deinit(); | ||
| 450 | |||
| 451 | { | ||
| 452 | // Now we emit the .debug_info types of the Decl. These will count towards the size of | ||
| 453 | // the buffer, so we have to do it before computing the offset, and we can't perform the actual | ||
| 454 | // relocations yet. | ||
| 455 | var it: usize = 0; | ||
| 456 | while (it < dbg_info_type_relocs.count()) : (it += 1) { | ||
| 457 | const ty = dbg_info_type_relocs.keys()[it]; | ||
| 458 | const value_ptr = dbg_info_type_relocs.getPtr(ty).?; | ||
| 459 | value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); | ||
| 460 | try self.addDbgInfoType(dbg_type_arena.allocator(), ty, dbg_info_buffer, dbg_info_type_relocs); | ||
| 461 | } | ||
| 462 | } | ||
| 463 | |||
| 464 | const atom = switch (self.tag) { | ||
| 465 | .elf => &decl.link.elf.dbg_info_atom, | ||
| 466 | .macho => &decl.link.macho.dbg_info_atom, | ||
| 467 | else => unreachable, | ||
| 468 | }; | ||
| 469 | try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); | ||
| 470 | |||
| 471 | { | ||
| 472 | // Now that we have the offset assigned we can finally perform type relocations. | ||
| 473 | for (dbg_info_type_relocs.values()) |value| { | ||
| 474 | for (value.relocs.items) |off| { | ||
| 475 | mem.writeIntLittle( | ||
| 476 | u32, | ||
| 477 | dbg_info_buffer.items[off..][0..4], | ||
| 478 | atom.off + value.off, | ||
| 479 | ); | ||
| 480 | } | ||
| 481 | } | ||
| 482 | } | ||
| 483 | |||
| 484 | try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); | ||
| 485 | } | ||
| 486 | |||
| 487 | fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom, len: u32) !void { | ||
| 488 | const tracy = trace(@src()); | ||
| 489 | defer tracy.end(); | ||
| 490 | |||
| 491 | // This logic is nearly identical to the logic above in `updateDecl` for | ||
| 492 | // `SrcFn` and the line number programs. If you are editing this logic, you | ||
| 493 | // probably need to edit that logic too. | ||
| 494 | const gpa = self.allocator; | ||
| 495 | |||
| 496 | atom.len = len; | ||
| 497 | if (self.dbg_info_decl_last) |last| blk: { | ||
| 498 | if (atom == last) break :blk; | ||
| 499 | if (atom.next) |next| { | ||
| 500 | // Update existing Decl - non-last item. | ||
| 501 | if (atom.off + atom.len + min_nop_size > next.off) { | ||
| 502 | // It grew too big, so we move it to a new location. | ||
| 503 | if (atom.prev) |prev| { | ||
| 504 | self.dbg_info_decl_free_list.put(gpa, prev, {}) catch {}; | ||
| 505 | prev.next = atom.next; | ||
| 506 | } | ||
| 507 | next.prev = atom.prev; | ||
| 508 | atom.next = null; | ||
| 509 | // Populate where it used to be with NOPs. | ||
| 510 | switch (self.tag) { | ||
| 511 | .elf => { | ||
| 512 | const elf_file = file.cast(File.Elf).?; | ||
| 513 | const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?]; | ||
| 514 | const file_pos = debug_info_sect.sh_offset + atom.off; | ||
| 515 | try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false); | ||
| 516 | }, | ||
| 517 | .macho => { | ||
| 518 | const macho_file = file.cast(File.MachO).?; | ||
| 519 | const d_sym = &macho_file.d_sym.?; | ||
| 520 | const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; | ||
| 521 | const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?]; | ||
| 522 | const file_pos = debug_info_sect.offset + atom.off; | ||
| 523 | try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false); | ||
| 524 | }, | ||
| 525 | else => unreachable, | ||
| 526 | } | ||
| 527 | // TODO Look at the free list before appending at the end. | ||
| 528 | atom.prev = last; | ||
| 529 | last.next = atom; | ||
| 530 | self.dbg_info_decl_last = atom; | ||
| 531 | |||
| 532 | atom.off = last.off + padToIdeal(last.len); | ||
| 533 | } | ||
| 534 | } else if (atom.prev == null) { | ||
| 535 | // Append new Decl. | ||
| 536 | // TODO Look at the free list before appending at the end. | ||
| 537 | atom.prev = last; | ||
| 538 | last.next = atom; | ||
| 539 | self.dbg_info_decl_last = atom; | ||
| 540 | |||
| 541 | atom.off = last.off + padToIdeal(last.len); | ||
| 542 | } | ||
| 543 | } else { | ||
| 544 | // This is the first Decl of the .debug_info | ||
| 545 | self.dbg_info_decl_first = atom; | ||
| 546 | self.dbg_info_decl_last = atom; | ||
| 547 | |||
| 548 | atom.off = @intCast(u32, padToIdeal(self.dbgInfoHeaderBytes())); | ||
| 549 | } | ||
| 550 | } | ||
| 551 | |||
| 552 | fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *DebugInfoAtom, dbg_info_buf: []const u8) !void { | ||
| 553 | const tracy = trace(@src()); | ||
| 554 | defer tracy.end(); | ||
| 555 | |||
| 556 | // This logic is nearly identical to the logic above in `updateDecl` for | ||
| 557 | // `SrcFn` and the line number programs. If you are editing this logic, you | ||
| 558 | // probably need to edit that logic too. | ||
| 559 | const gpa = self.allocator; | ||
| 560 | |||
| 561 | const last_decl = self.dbg_info_decl_last.?; | ||
| 562 | // +1 for a trailing zero to end the children of the decl tag. | ||
| 563 | const needed_size = last_decl.off + last_decl.len + 1; | ||
| 564 | const prev_padding_size: u32 = if (atom.prev) |prev| atom.off - (prev.off + prev.len) else 0; | ||
| 565 | const next_padding_size: u32 = if (atom.next) |next| next.off - (atom.off + atom.len) else 0; | ||
| 566 | |||
| 567 | // To end the children of the decl tag. | ||
| 568 | const trailing_zero = atom.next == null; | ||
| 569 | |||
| 570 | // We only have support for one compilation unit so far, so the offsets are directly | ||
| 571 | // from the .debug_info section. | ||
| 572 | switch (self.tag) { | ||
| 573 | .elf => { | ||
| 574 | const elf_file = file.cast(File.Elf).?; | ||
| 575 | const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?]; | ||
| 576 | if (needed_size != debug_info_sect.sh_size) { | ||
| 577 | if (needed_size > elf_file.allocatedSize(debug_info_sect.sh_offset)) { | ||
| 578 | const new_offset = elf_file.findFreeSpace(needed_size, 1); | ||
| 579 | const existing_size = last_decl.off; | ||
| 580 | log.debug("moving .debug_info section: {d} bytes from 0x{x} to 0x{x}", .{ | ||
| 581 | existing_size, | ||
| 582 | debug_info_sect.sh_offset, | ||
| 583 | new_offset, | ||
| 584 | }); | ||
| 585 | const amt = try elf_file.base.file.?.copyRangeAll( | ||
| 586 | debug_info_sect.sh_offset, | ||
| 587 | elf_file.base.file.?, | ||
| 588 | new_offset, | ||
| 589 | existing_size, | ||
| 590 | ); | ||
| 591 | if (amt != existing_size) return error.InputOutput; | ||
| 592 | debug_info_sect.sh_offset = new_offset; | ||
| 593 | } | ||
| 594 | debug_info_sect.sh_size = needed_size; | ||
| 595 | elf_file.shdr_table_dirty = true; // TODO look into making only the one section dirty | ||
| 596 | elf_file.debug_info_header_dirty = true; | ||
| 597 | } | ||
| 598 | const file_pos = debug_info_sect.sh_offset + atom.off; | ||
| 599 | try pwriteDbgInfoNops( | ||
| 600 | elf_file.base.file.?, | ||
| 601 | file_pos, | ||
| 602 | prev_padding_size, | ||
| 603 | dbg_info_buf, | ||
| 604 | next_padding_size, | ||
| 605 | trailing_zero, | ||
| 606 | ); | ||
| 607 | }, | ||
| 608 | .macho => { | ||
| 609 | const macho_file = file.cast(File.MachO).?; | ||
| 610 | const d_sym = &macho_file.d_sym.?; | ||
| 611 | const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; | ||
| 612 | const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?]; | ||
| 613 | if (needed_size != debug_info_sect.size) { | ||
| 614 | if (needed_size > d_sym.allocatedSize(debug_info_sect.offset)) { | ||
| 615 | const new_offset = d_sym.findFreeSpace(needed_size, 1); | ||
| 616 | const existing_size = last_decl.off; | ||
| 617 | |||
| 618 | log.debug("moving __debug_info section: {} bytes from 0x{x} to 0x{x}", .{ | ||
| 619 | existing_size, | ||
| 620 | debug_info_sect.offset, | ||
| 621 | new_offset, | ||
| 622 | }); | ||
| 623 | |||
| 624 | try File.MachO.copyRangeAllOverlappingAlloc( | ||
| 625 | gpa, | ||
| 626 | d_sym.file, | ||
| 627 | debug_info_sect.offset, | ||
| 628 | new_offset, | ||
| 629 | existing_size, | ||
| 630 | ); | ||
| 631 | |||
| 632 | debug_info_sect.offset = @intCast(u32, new_offset); | ||
| 633 | debug_info_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff; | ||
| 634 | } | ||
| 635 | debug_info_sect.size = needed_size; | ||
| 636 | d_sym.load_commands_dirty = true; // TODO look into making only the one section dirty | ||
| 637 | d_sym.debug_line_header_dirty = true; | ||
| 638 | } | ||
| 639 | const file_pos = debug_info_sect.offset + atom.off; | ||
| 640 | try pwriteDbgInfoNops( | ||
| 641 | d_sym.file, | ||
| 642 | file_pos, | ||
| 643 | prev_padding_size, | ||
| 644 | dbg_info_buf, | ||
| 645 | next_padding_size, | ||
| 646 | trailing_zero, | ||
| 647 | ); | ||
| 648 | }, | ||
| 649 | else => unreachable, | ||
| 650 | } | ||
| 651 | } | ||
| 652 | |||
| 653 | pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) !void { | ||
| 654 | const tracy = trace(@src()); | ||
| 655 | defer tracy.end(); | ||
| 656 | |||
| 657 | const func = decl.val.castTag(.function).?.data; | ||
| 658 | log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{ | ||
| 659 | decl.src_line, | ||
| 660 | func.lbrace_line, | ||
| 661 | func.rbrace_line, | ||
| 662 | }); | ||
| 663 | const line = @intCast(u28, decl.src_line + func.lbrace_line); | ||
| 664 | var data: [4]u8 = undefined; | ||
| 665 | leb128.writeUnsignedFixed(4, &data, line); | ||
| 666 | |||
| 667 | switch (self.tag) { | ||
| 668 | .elf => { | ||
| 669 | const elf_file = file.cast(File.Elf).?; | ||
| 670 | const shdr = elf_file.sections.items[elf_file.debug_line_section_index.?]; | ||
| 671 | const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff(); | ||
| 672 | try elf_file.base.file.?.pwriteAll(&data, file_pos); | ||
| 673 | }, | ||
| 674 | .macho => { | ||
| 675 | const macho_file = file.cast(File.MachO).?; | ||
| 676 | const d_sym = macho_file.d_sym.?; | ||
| 677 | const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; | ||
| 678 | const sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?]; | ||
| 679 | const file_pos = sect.offset + decl.fn_link.macho.off + self.getRelocDbgLineOff(); | ||
| 680 | try d_sym.file.pwriteAll(&data, file_pos); | ||
| 681 | }, | ||
| 682 | else => unreachable, | ||
| 683 | } | ||
| 684 | } | ||
| 685 | |||
| 686 | pub fn freeAtom(self: *Dwarf, atom: *DebugInfoAtom) void { | ||
| 687 | if (self.dbg_info_decl_first == atom) { | ||
| 688 | self.dbg_info_decl_first = atom.next; | ||
| 689 | } | ||
| 690 | if (self.dbg_info_decl_last == atom) { | ||
| 691 | // TODO shrink the .debug_info section size here | ||
| 692 | self.dbg_info_decl_last = atom.prev; | ||
| 693 | } | ||
| 694 | |||
| 695 | if (atom.prev) |prev| { | ||
| 696 | prev.next = atom.next; | ||
| 697 | |||
| 698 | // TODO the free list logic like we do for text blocks above | ||
| 699 | } else { | ||
| 700 | atom.prev = null; | ||
| 701 | } | ||
| 702 | |||
| 703 | if (atom.next) |next| { | ||
| 704 | next.prev = atom.prev; | ||
| 705 | } else { | ||
| 706 | atom.next = null; | ||
| 707 | } | ||
| 708 | } | ||
| 709 | |||
| 710 | pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void { | ||
| 711 | // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing | ||
| 712 | // is desired for both. | ||
| 713 | const gpa = self.allocator; | ||
| 714 | const fn_link = switch (self.tag) { | ||
| 715 | .elf => &decl.fn_link.elf, | ||
| 716 | .macho => &decl.fn_link.macho, | ||
| 717 | else => unreachable, | ||
| 718 | }; | ||
| 719 | _ = self.dbg_line_fn_free_list.remove(fn_link); | ||
| 720 | |||
| 721 | if (fn_link.prev) |prev| { | ||
| 722 | self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {}; | ||
| 723 | prev.next = fn_link.next; | ||
| 724 | if (fn_link.next) |next| { | ||
| 725 | next.prev = prev; | ||
| 726 | } else { | ||
| 727 | self.dbg_line_fn_last = prev; | ||
| 728 | } | ||
| 729 | } else if (fn_link.next) |next| { | ||
| 730 | self.dbg_line_fn_first = next; | ||
| 731 | next.prev = null; | ||
| 732 | } | ||
| 733 | if (self.dbg_line_fn_first == fn_link) { | ||
| 734 | self.dbg_line_fn_first = fn_link.next; | ||
| 735 | } | ||
| 736 | if (self.dbg_line_fn_last == fn_link) { | ||
| 737 | self.dbg_line_fn_last = fn_link.prev; | ||
| 738 | } | ||
| 739 | } | ||
| 740 | |||
| 741 | /// Asserts the type has codegen bits. | ||
| 742 | fn addDbgInfoType( | ||
| 743 | self: *Dwarf, | ||
| 744 | arena: Allocator, | ||
| 745 | ty: Type, | ||
| 746 | dbg_info_buffer: *std.ArrayList(u8), | ||
| 747 | dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable, | ||
| 748 | ) error{OutOfMemory}!void { | ||
| 749 | const target = self.target; | ||
| 750 | var relocs = std.ArrayList(struct { ty: Type, reloc: u32 }).init(arena); | ||
| 751 | |||
| 752 | switch (ty.zigTypeTag()) { | ||
| 753 | .NoReturn => unreachable, | ||
| 754 | .Void => { | ||
| 755 | try dbg_info_buffer.append(abbrev_pad1); | ||
| 756 | }, | ||
| 757 | .Bool => { | ||
| 758 | try dbg_info_buffer.appendSlice(&[_]u8{ | ||
| 759 | abbrev_base_type, | ||
| 760 | DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 | ||
| 761 | 1, // DW.AT.byte_size, DW.FORM.data1 | ||
| 762 | 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string | ||
| 763 | }); | ||
| 764 | }, | ||
| 765 | .Int => { | ||
| 766 | const info = ty.intInfo(target); | ||
| 767 | try dbg_info_buffer.ensureUnusedCapacity(12); | ||
| 768 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); | ||
| 769 | // DW.AT.encoding, DW.FORM.data1 | ||
| 770 | dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { | ||
| 771 | .signed => DW.ATE.signed, | ||
| 772 | .unsigned => DW.ATE.unsigned, | ||
| 773 | }); | ||
| 774 | // DW.AT.byte_size, DW.FORM.data1 | ||
| 775 | dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); | ||
| 776 | // DW.AT.name, DW.FORM.string | ||
| 777 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 778 | }, | ||
| 779 | .Optional => { | ||
| 780 | if (ty.isPtrLikeOptional()) { | ||
| 781 | try dbg_info_buffer.ensureUnusedCapacity(12); | ||
| 782 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); | ||
| 783 | // DW.AT.encoding, DW.FORM.data1 | ||
| 784 | dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); | ||
| 785 | // DW.AT.byte_size, DW.FORM.data1 | ||
| 786 | dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); | ||
| 787 | // DW.AT.name, DW.FORM.string | ||
| 788 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 789 | } else { | ||
| 790 | // Non-pointer optionals are structs: struct { .maybe = *, .val = * } | ||
| 791 | var buf = try arena.create(Type.Payload.ElemType); | ||
| 792 | const payload_ty = ty.optionalChild(buf); | ||
| 793 | // DW.AT.structure_type | ||
| 794 | try dbg_info_buffer.append(abbrev_struct_type); | ||
| 795 | // DW.AT.byte_size, DW.FORM.sdata | ||
| 796 | const abi_size = ty.abiSize(target); | ||
| 797 | try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); | ||
| 798 | // DW.AT.name, DW.FORM.string | ||
| 799 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 800 | // DW.AT.member | ||
| 801 | try dbg_info_buffer.ensureUnusedCapacity(7); | ||
| 802 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 803 | // DW.AT.name, DW.FORM.string | ||
| 804 | dbg_info_buffer.appendSliceAssumeCapacity("maybe"); | ||
| 805 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 806 | // DW.AT.type, DW.FORM.ref4 | ||
| 807 | var index = dbg_info_buffer.items.len; | ||
| 808 | try dbg_info_buffer.resize(index + 4); | ||
| 809 | try relocs.append(.{ .ty = Type.bool, .reloc = @intCast(u32, index) }); | ||
| 810 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 811 | try dbg_info_buffer.ensureUnusedCapacity(6); | ||
| 812 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 813 | // DW.AT.member | ||
| 814 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 815 | // DW.AT.name, DW.FORM.string | ||
| 816 | dbg_info_buffer.appendSliceAssumeCapacity("val"); | ||
| 817 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 818 | // DW.AT.type, DW.FORM.ref4 | ||
| 819 | index = dbg_info_buffer.items.len; | ||
| 820 | try dbg_info_buffer.resize(index + 4); | ||
| 821 | try relocs.append(.{ .ty = payload_ty, .reloc = @intCast(u32, index) }); | ||
| 822 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 823 | const offset = abi_size - payload_ty.abiSize(target); | ||
| 824 | try leb128.writeULEB128(dbg_info_buffer.writer(), offset); | ||
| 825 | // DW.AT.structure_type delimit children | ||
| 826 | try dbg_info_buffer.append(0); | ||
| 827 | } | ||
| 828 | }, | ||
| 829 | .Pointer => { | ||
| 830 | if (ty.isSlice()) { | ||
| 831 | // Slices are structs: struct { .ptr = *, .len = N } | ||
| 832 | // DW.AT.structure_type | ||
| 833 | try dbg_info_buffer.ensureUnusedCapacity(2); | ||
| 834 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_type); | ||
| 835 | // DW.AT.byte_size, DW.FORM.sdata | ||
| 836 | dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2); | ||
| 837 | // DW.AT.name, DW.FORM.string | ||
| 838 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 839 | // DW.AT.member | ||
| 840 | try dbg_info_buffer.ensureUnusedCapacity(5); | ||
| 841 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 842 | // DW.AT.name, DW.FORM.string | ||
| 843 | dbg_info_buffer.appendSliceAssumeCapacity("ptr"); | ||
| 844 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 845 | // DW.AT.type, DW.FORM.ref4 | ||
| 846 | var index = dbg_info_buffer.items.len; | ||
| 847 | try dbg_info_buffer.resize(index + 4); | ||
| 848 | var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); | ||
| 849 | const ptr_ty = ty.slicePtrFieldType(buf); | ||
| 850 | try relocs.append(.{ .ty = ptr_ty, .reloc = @intCast(u32, index) }); | ||
| 851 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 852 | try dbg_info_buffer.ensureUnusedCapacity(6); | ||
| 853 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 854 | // DW.AT.member | ||
| 855 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 856 | // DW.AT.name, DW.FORM.string | ||
| 857 | dbg_info_buffer.appendSliceAssumeCapacity("len"); | ||
| 858 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 859 | // DW.AT.type, DW.FORM.ref4 | ||
| 860 | index = dbg_info_buffer.items.len; | ||
| 861 | try dbg_info_buffer.resize(index + 4); | ||
| 862 | try relocs.append(.{ .ty = Type.initTag(.usize), .reloc = @intCast(u32, index) }); | ||
| 863 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 864 | try dbg_info_buffer.ensureUnusedCapacity(2); | ||
| 865 | dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize)); | ||
| 866 | // DW.AT.structure_type delimit children | ||
| 867 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 868 | } else { | ||
| 869 | try dbg_info_buffer.ensureUnusedCapacity(5); | ||
| 870 | dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type); | ||
| 871 | // DW.AT.type, DW.FORM.ref4 | ||
| 872 | const index = dbg_info_buffer.items.len; | ||
| 873 | try dbg_info_buffer.resize(index + 4); | ||
| 874 | try relocs.append(.{ .ty = ty.childType(), .reloc = @intCast(u32, index) }); | ||
| 875 | } | ||
| 876 | }, | ||
| 877 | .Struct => blk: { | ||
| 878 | // try dbg_info_buffer.ensureUnusedCapacity(23); | ||
| 879 | // DW.AT.structure_type | ||
| 880 | try dbg_info_buffer.append(abbrev_struct_type); | ||
| 881 | // DW.AT.byte_size, DW.FORM.sdata | ||
| 882 | const abi_size = ty.abiSize(target); | ||
| 883 | try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); | ||
| 884 | // DW.AT.name, DW.FORM.string | ||
| 885 | const struct_name = try ty.nameAlloc(arena); | ||
| 886 | try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); | ||
| 887 | dbg_info_buffer.appendSliceAssumeCapacity(struct_name); | ||
| 888 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 889 | |||
| 890 | const struct_obj = ty.castTag(.@"struct").?.data; | ||
| 891 | if (struct_obj.layout == .Packed) { | ||
| 892 | log.debug("TODO implement .debug_info for packed structs", .{}); | ||
| 893 | break :blk; | ||
| 894 | } | ||
| 895 | |||
| 896 | const fields = ty.structFields(); | ||
| 897 | for (fields.keys()) |field_name, field_index| { | ||
| 898 | const field = fields.get(field_name).?; | ||
| 899 | // DW.AT.member | ||
| 900 | try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); | ||
| 901 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 902 | // DW.AT.name, DW.FORM.string | ||
| 903 | dbg_info_buffer.appendSliceAssumeCapacity(field_name); | ||
| 904 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 905 | // DW.AT.type, DW.FORM.ref4 | ||
| 906 | var index = dbg_info_buffer.items.len; | ||
| 907 | try dbg_info_buffer.resize(index + 4); | ||
| 908 | try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); | ||
| 909 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 910 | const field_off = ty.structFieldOffset(field_index, target); | ||
| 911 | try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); | ||
| 912 | } | ||
| 913 | |||
| 914 | // DW.AT.structure_type delimit children | ||
| 915 | try dbg_info_buffer.append(0); | ||
| 916 | }, | ||
| 917 | else => { | ||
| 918 | log.debug("TODO implement .debug_info for type '{}'", .{ty}); | ||
| 919 | try dbg_info_buffer.append(abbrev_pad1); | ||
| 920 | }, | ||
| 921 | } | ||
| 922 | |||
| 923 | for (relocs.items) |rel| { | ||
| 924 | const gop = try dbg_info_type_relocs.getOrPut(self.allocator, rel.ty); | ||
| 925 | if (!gop.found_existing) { | ||
| 926 | gop.value_ptr.* = .{ | ||
| 927 | .off = undefined, | ||
| 928 | .relocs = .{}, | ||
| 929 | }; | ||
| 930 | } | ||
| 931 | try gop.value_ptr.relocs.append(self.allocator, rel.reloc); | ||
| 932 | } | ||
| 933 | } | ||
| 934 | |||
| 935 | pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { | ||
| 936 | // These are LEB encoded but since the values are all less than 127 | ||
| 937 | // we can simply append these bytes. | ||
| 938 | const abbrev_buf = [_]u8{ | ||
| 939 | abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header | ||
| 940 | DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, | ||
| 941 | DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, | ||
| 942 | DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, | ||
| 943 | DW.FORM.strp, DW.AT.producer, DW.FORM.strp, | ||
| 944 | DW.AT.language, DW.FORM.data2, 0, | ||
| 945 | 0, // table sentinel | ||
| 946 | abbrev_subprogram, | ||
| 947 | DW.TAG.subprogram, | ||
| 948 | DW.CHILDREN.yes, // header | ||
| 949 | DW.AT.low_pc, | ||
| 950 | DW.FORM.addr, | ||
| 951 | DW.AT.high_pc, | ||
| 952 | DW.FORM.data4, | ||
| 953 | DW.AT.type, | ||
| 954 | DW.FORM.ref4, | ||
| 955 | DW.AT.name, | ||
| 956 | DW.FORM.string, | ||
| 957 | 0, 0, // table sentinel | ||
| 958 | abbrev_subprogram_retvoid, | ||
| 959 | DW.TAG.subprogram, DW.CHILDREN.yes, // header | ||
| 960 | DW.AT.low_pc, DW.FORM.addr, | ||
| 961 | DW.AT.high_pc, DW.FORM.data4, | ||
| 962 | DW.AT.name, DW.FORM.string, | ||
| 963 | 0, | ||
| 964 | 0, // table sentinel | ||
| 965 | abbrev_base_type, | ||
| 966 | DW.TAG.base_type, | ||
| 967 | DW.CHILDREN.no, // header | ||
| 968 | DW.AT.encoding, | ||
| 969 | DW.FORM.data1, | ||
| 970 | DW.AT.byte_size, | ||
| 971 | DW.FORM.data1, | ||
| 972 | DW.AT.name, | ||
| 973 | DW.FORM.string, | ||
| 974 | 0, | ||
| 975 | 0, // table sentinel | ||
| 976 | abbrev_ptr_type, | ||
| 977 | DW.TAG.pointer_type, | ||
| 978 | DW.CHILDREN.no, // header | ||
| 979 | DW.AT.type, | ||
| 980 | DW.FORM.ref4, | ||
| 981 | 0, | ||
| 982 | 0, // table sentinel | ||
| 983 | abbrev_struct_type, | ||
| 984 | DW.TAG.structure_type, | ||
| 985 | DW.CHILDREN.yes, // header | ||
| 986 | DW.AT.byte_size, | ||
| 987 | DW.FORM.sdata, | ||
| 988 | DW.AT.name, | ||
| 989 | DW.FORM.string, | ||
| 990 | 0, | ||
| 991 | 0, // table sentinel | ||
| 992 | abbrev_struct_member, | ||
| 993 | DW.TAG.member, | ||
| 994 | DW.CHILDREN.no, // header | ||
| 995 | DW.AT.name, | ||
| 996 | DW.FORM.string, | ||
| 997 | DW.AT.type, | ||
| 998 | DW.FORM.ref4, | ||
| 999 | DW.AT.data_member_location, | ||
| 1000 | DW.FORM.sdata, | ||
| 1001 | 0, | ||
| 1002 | 0, // table sentinel | ||
| 1003 | abbrev_pad1, | ||
| 1004 | DW.TAG.unspecified_type, | ||
| 1005 | DW.CHILDREN.no, // header | ||
| 1006 | 0, | ||
| 1007 | 0, // table sentinel | ||
| 1008 | abbrev_parameter, | ||
| 1009 | DW.TAG.formal_parameter, DW.CHILDREN.no, // header | ||
| 1010 | DW.AT.location, DW.FORM.exprloc, | ||
| 1011 | DW.AT.type, DW.FORM.ref4, | ||
| 1012 | DW.AT.name, DW.FORM.string, | ||
| 1013 | 0, | ||
| 1014 | 0, // table sentinel | ||
| 1015 | 0, | ||
| 1016 | 0, | ||
| 1017 | 0, // section sentinel | ||
| 1018 | }; | ||
| 1019 | const abbrev_offset = 0; | ||
| 1020 | self.abbrev_table_offset = abbrev_offset; | ||
| 1021 | |||
| 1022 | const needed_size = abbrev_buf.len; | ||
| 1023 | switch (self.tag) { | ||
| 1024 | .elf => { | ||
| 1025 | const elf_file = file.cast(File.Elf).?; | ||
| 1026 | const debug_abbrev_sect = &elf_file.sections.items[elf_file.debug_abbrev_section_index.?]; | ||
| 1027 | const allocated_size = elf_file.allocatedSize(debug_abbrev_sect.sh_offset); | ||
| 1028 | if (needed_size > allocated_size) { | ||
| 1029 | debug_abbrev_sect.sh_size = 0; // free the space | ||
| 1030 | debug_abbrev_sect.sh_offset = elf_file.findFreeSpace(needed_size, 1); | ||
| 1031 | } | ||
| 1032 | debug_abbrev_sect.sh_size = needed_size; | ||
| 1033 | log.debug(".debug_abbrev start=0x{x} end=0x{x}", .{ | ||
| 1034 | debug_abbrev_sect.sh_offset, | ||
| 1035 | debug_abbrev_sect.sh_offset + needed_size, | ||
| 1036 | }); | ||
| 1037 | |||
| 1038 | const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset; | ||
| 1039 | try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos); | ||
| 1040 | }, | ||
| 1041 | .macho => { | ||
| 1042 | const macho_file = file.cast(File.MachO).?; | ||
| 1043 | const d_sym = &macho_file.d_sym.?; | ||
| 1044 | const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; | ||
| 1045 | const debug_abbrev_sect = &dwarf_segment.sections.items[d_sym.debug_abbrev_section_index.?]; | ||
| 1046 | const allocated_size = d_sym.allocatedSize(debug_abbrev_sect.offset); | ||
| 1047 | if (needed_size > allocated_size) { | ||
| 1048 | debug_abbrev_sect.size = 0; // free the space | ||
| 1049 | const offset = d_sym.findFreeSpace(needed_size, 1); | ||
| 1050 | debug_abbrev_sect.offset = @intCast(u32, offset); | ||
| 1051 | debug_abbrev_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff; | ||
| 1052 | } | ||
| 1053 | debug_abbrev_sect.size = needed_size; | ||
| 1054 | log.debug("__debug_abbrev start=0x{x} end=0x{x}", .{ | ||
| 1055 | debug_abbrev_sect.offset, | ||
| 1056 | debug_abbrev_sect.offset + needed_size, | ||
| 1057 | }); | ||
| 1058 | const file_pos = debug_abbrev_sect.offset + abbrev_offset; | ||
| 1059 | try d_sym.file.pwriteAll(&abbrev_buf, file_pos); | ||
| 1060 | }, | ||
| 1061 | else => unreachable, | ||
| 1062 | } | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | fn dbgInfoHeaderBytes(self: *Dwarf) usize { | ||
| 1066 | _ = self; | ||
| 1067 | return 120; | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u64, high_pc: u64) !void { | ||
| 1071 | // If this value is null it means there is an error in the module; | ||
| 1072 | // leave debug_info_header_dirty=true. | ||
| 1073 | const first_dbg_info_off = self.getDebugInfoOff() orelse return; | ||
| 1074 | |||
| 1075 | // We have a function to compute the upper bound size, because it's needed | ||
| 1076 | // for determining where to put the offset of the first `LinkBlock`. | ||
| 1077 | const needed_bytes = self.dbgInfoHeaderBytes(); | ||
| 1078 | var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, needed_bytes); | ||
| 1079 | defer di_buf.deinit(); | ||
| 1080 | |||
| 1081 | const target_endian = self.target.cpu.arch.endian(); | ||
| 1082 | const init_len_size: usize = if (self.tag == .macho) | ||
| 1083 | 4 | ||
| 1084 | else switch (self.ptr_width) { | ||
| 1085 | .p32 => @as(usize, 4), | ||
| 1086 | .p64 => 12, | ||
| 1087 | }; | ||
| 1088 | |||
| 1089 | // initial length - length of the .debug_info contribution for this compilation unit, | ||
| 1090 | // not including the initial length itself. | ||
| 1091 | // We have to come back and write it later after we know the size. | ||
| 1092 | const after_init_len = di_buf.items.len + init_len_size; | ||
| 1093 | // +1 for the final 0 that ends the compilation unit children. | ||
| 1094 | const dbg_info_end = self.getDebugInfoEnd().? + 1; | ||
| 1095 | const init_len = dbg_info_end - after_init_len; | ||
| 1096 | if (self.tag == .macho) { | ||
| 1097 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len)); | ||
| 1098 | } else switch (self.ptr_width) { | ||
| 1099 | .p32 => { | ||
| 1100 | mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian); | ||
| 1101 | }, | ||
| 1102 | .p64 => { | ||
| 1103 | di_buf.appendNTimesAssumeCapacity(0xff, 4); | ||
| 1104 | mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian); | ||
| 1105 | }, | ||
| 1106 | } | ||
| 1107 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version | ||
| 1108 | const abbrev_offset = self.abbrev_table_offset.?; | ||
| 1109 | if (self.tag == .macho) { | ||
| 1110 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset)); | ||
| 1111 | di_buf.appendAssumeCapacity(8); // address size | ||
| 1112 | } else switch (self.ptr_width) { | ||
| 1113 | .p32 => { | ||
| 1114 | mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian); | ||
| 1115 | di_buf.appendAssumeCapacity(4); // address size | ||
| 1116 | }, | ||
| 1117 | .p64 => { | ||
| 1118 | mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian); | ||
| 1119 | di_buf.appendAssumeCapacity(8); // address size | ||
| 1120 | }, | ||
| 1121 | } | ||
| 1122 | // Write the form for the compile unit, which must match the abbrev table above. | ||
| 1123 | const name_strp = try self.makeString(module.root_pkg.root_src_path); | ||
| 1124 | const comp_dir_strp = try self.makeString(module.root_pkg.root_src_directory.path orelse "."); | ||
| 1125 | const producer_strp = try self.makeString(link.producer_string); | ||
| 1126 | |||
| 1127 | di_buf.appendAssumeCapacity(abbrev_compile_unit); | ||
| 1128 | if (self.tag == .macho) { | ||
| 1129 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // DW.AT.stmt_list, DW.FORM.sec_offset | ||
| 1130 | mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), low_pc); | ||
| 1131 | mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), high_pc); | ||
| 1132 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, name_strp)); | ||
| 1133 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, comp_dir_strp)); | ||
| 1134 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, producer_strp)); | ||
| 1135 | } else { | ||
| 1136 | self.writeAddrAssumeCapacity(&di_buf, 0); // DW.AT.stmt_list, DW.FORM.sec_offset | ||
| 1137 | self.writeAddrAssumeCapacity(&di_buf, low_pc); | ||
| 1138 | self.writeAddrAssumeCapacity(&di_buf, high_pc); | ||
| 1139 | self.writeAddrAssumeCapacity(&di_buf, name_strp); | ||
| 1140 | self.writeAddrAssumeCapacity(&di_buf, comp_dir_strp); | ||
| 1141 | self.writeAddrAssumeCapacity(&di_buf, producer_strp); | ||
| 1142 | } | ||
| 1143 | // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number: | ||
| 1144 | // http://dwarfstd.org/ShowIssue.php?issue=171115.1 | ||
| 1145 | // Until then we say it is C99. | ||
| 1146 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99, target_endian); | ||
| 1147 | |||
| 1148 | if (di_buf.items.len > first_dbg_info_off) { | ||
| 1149 | // Move the first N decls to the end to make more padding for the header. | ||
| 1150 | @panic("TODO: handle .debug_info header exceeding its padding"); | ||
| 1151 | } | ||
| 1152 | const jmp_amt = first_dbg_info_off - di_buf.items.len; | ||
| 1153 | switch (self.tag) { | ||
| 1154 | .elf => { | ||
| 1155 | const elf_file = file.cast(File.Elf).?; | ||
| 1156 | const debug_info_sect = elf_file.sections.items[elf_file.debug_info_section_index.?]; | ||
| 1157 | const file_pos = debug_info_sect.sh_offset; | ||
| 1158 | try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false); | ||
| 1159 | }, | ||
| 1160 | .macho => { | ||
| 1161 | const macho_file = file.cast(File.MachO).?; | ||
| 1162 | const d_sym = &macho_file.d_sym.?; | ||
| 1163 | const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; | ||
| 1164 | const debug_info_sect = dwarf_seg.sections.items[d_sym.debug_info_section_index.?]; | ||
| 1165 | const file_pos = debug_info_sect.offset; | ||
| 1166 | try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false); | ||
| 1167 | }, | ||
| 1168 | else => unreachable, | ||
| 1169 | } | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void { | ||
| 1173 | const target_endian = self.target.cpu.arch.endian(); | ||
| 1174 | switch (self.ptr_width) { | ||
| 1175 | .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, addr), target_endian), | ||
| 1176 | .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian), | ||
| 1177 | } | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | /// Writes to the file a buffer, prefixed and suffixed by the specified number of | ||
| 1181 | /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes | ||
| 1182 | /// are less than 1044480 bytes (if this limit is ever reached, this function can be | ||
| 1183 | /// improved to make more than one pwritev call, or the limit can be raised by a fixed | ||
| 1184 | /// amount by increasing the length of `vecs`). | ||
| 1185 | fn pwriteDbgLineNops( | ||
| 1186 | file: fs.File, | ||
| 1187 | offset: u64, | ||
| 1188 | prev_padding_size: usize, | ||
| 1189 | buf: []const u8, | ||
| 1190 | next_padding_size: usize, | ||
| 1191 | ) !void { | ||
| 1192 | const tracy = trace(@src()); | ||
| 1193 | defer tracy.end(); | ||
| 1194 | |||
| 1195 | const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096; | ||
| 1196 | const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 }; | ||
| 1197 | var vecs: [512]std.os.iovec_const = undefined; | ||
| 1198 | var vec_index: usize = 0; | ||
| 1199 | { | ||
| 1200 | var padding_left = prev_padding_size; | ||
| 1201 | if (padding_left % 2 != 0) { | ||
| 1202 | vecs[vec_index] = .{ | ||
| 1203 | .iov_base = &three_byte_nop, | ||
| 1204 | .iov_len = three_byte_nop.len, | ||
| 1205 | }; | ||
| 1206 | vec_index += 1; | ||
| 1207 | padding_left -= three_byte_nop.len; | ||
| 1208 | } | ||
| 1209 | while (padding_left > page_of_nops.len) { | ||
| 1210 | vecs[vec_index] = .{ | ||
| 1211 | .iov_base = &page_of_nops, | ||
| 1212 | .iov_len = page_of_nops.len, | ||
| 1213 | }; | ||
| 1214 | vec_index += 1; | ||
| 1215 | padding_left -= page_of_nops.len; | ||
| 1216 | } | ||
| 1217 | if (padding_left > 0) { | ||
| 1218 | vecs[vec_index] = .{ | ||
| 1219 | .iov_base = &page_of_nops, | ||
| 1220 | .iov_len = padding_left, | ||
| 1221 | }; | ||
| 1222 | vec_index += 1; | ||
| 1223 | } | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | vecs[vec_index] = .{ | ||
| 1227 | .iov_base = buf.ptr, | ||
| 1228 | .iov_len = buf.len, | ||
| 1229 | }; | ||
| 1230 | vec_index += 1; | ||
| 1231 | |||
| 1232 | { | ||
| 1233 | var padding_left = next_padding_size; | ||
| 1234 | if (padding_left % 2 != 0) { | ||
| 1235 | vecs[vec_index] = .{ | ||
| 1236 | .iov_base = &three_byte_nop, | ||
| 1237 | .iov_len = three_byte_nop.len, | ||
| 1238 | }; | ||
| 1239 | vec_index += 1; | ||
| 1240 | padding_left -= three_byte_nop.len; | ||
| 1241 | } | ||
| 1242 | while (padding_left > page_of_nops.len) { | ||
| 1243 | vecs[vec_index] = .{ | ||
| 1244 | .iov_base = &page_of_nops, | ||
| 1245 | .iov_len = page_of_nops.len, | ||
| 1246 | }; | ||
| 1247 | vec_index += 1; | ||
| 1248 | padding_left -= page_of_nops.len; | ||
| 1249 | } | ||
| 1250 | if (padding_left > 0) { | ||
| 1251 | vecs[vec_index] = .{ | ||
| 1252 | .iov_base = &page_of_nops, | ||
| 1253 | .iov_len = padding_left, | ||
| 1254 | }; | ||
| 1255 | vec_index += 1; | ||
| 1256 | } | ||
| 1257 | } | ||
| 1258 | try file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | /// Writes to the file a buffer, prefixed and suffixed by the specified number of | ||
| 1262 | /// bytes of padding. | ||
| 1263 | fn pwriteDbgInfoNops( | ||
| 1264 | file: fs.File, | ||
| 1265 | offset: u64, | ||
| 1266 | prev_padding_size: usize, | ||
| 1267 | buf: []const u8, | ||
| 1268 | next_padding_size: usize, | ||
| 1269 | trailing_zero: bool, | ||
| 1270 | ) !void { | ||
| 1271 | const tracy = trace(@src()); | ||
| 1272 | defer tracy.end(); | ||
| 1273 | |||
| 1274 | const page_of_nops = [1]u8{abbrev_pad1} ** 4096; | ||
| 1275 | var vecs: [32]std.os.iovec_const = undefined; | ||
| 1276 | var vec_index: usize = 0; | ||
| 1277 | { | ||
| 1278 | var padding_left = prev_padding_size; | ||
| 1279 | while (padding_left > page_of_nops.len) { | ||
| 1280 | vecs[vec_index] = .{ | ||
| 1281 | .iov_base = &page_of_nops, | ||
| 1282 | .iov_len = page_of_nops.len, | ||
| 1283 | }; | ||
| 1284 | vec_index += 1; | ||
| 1285 | padding_left -= page_of_nops.len; | ||
| 1286 | } | ||
| 1287 | if (padding_left > 0) { | ||
| 1288 | vecs[vec_index] = .{ | ||
| 1289 | .iov_base = &page_of_nops, | ||
| 1290 | .iov_len = padding_left, | ||
| 1291 | }; | ||
| 1292 | vec_index += 1; | ||
| 1293 | } | ||
| 1294 | } | ||
| 1295 | |||
| 1296 | vecs[vec_index] = .{ | ||
| 1297 | .iov_base = buf.ptr, | ||
| 1298 | .iov_len = buf.len, | ||
| 1299 | }; | ||
| 1300 | vec_index += 1; | ||
| 1301 | |||
| 1302 | { | ||
| 1303 | var padding_left = next_padding_size; | ||
| 1304 | while (padding_left > page_of_nops.len) { | ||
| 1305 | vecs[vec_index] = .{ | ||
| 1306 | .iov_base = &page_of_nops, | ||
| 1307 | .iov_len = page_of_nops.len, | ||
| 1308 | }; | ||
| 1309 | vec_index += 1; | ||
| 1310 | padding_left -= page_of_nops.len; | ||
| 1311 | } | ||
| 1312 | if (padding_left > 0) { | ||
| 1313 | vecs[vec_index] = .{ | ||
| 1314 | .iov_base = &page_of_nops, | ||
| 1315 | .iov_len = padding_left, | ||
| 1316 | }; | ||
| 1317 | vec_index += 1; | ||
| 1318 | } | ||
| 1319 | } | ||
| 1320 | |||
| 1321 | if (trailing_zero) { | ||
| 1322 | var zbuf = [1]u8{0}; | ||
| 1323 | vecs[vec_index] = .{ | ||
| 1324 | .iov_base = &zbuf, | ||
| 1325 | .iov_len = zbuf.len, | ||
| 1326 | }; | ||
| 1327 | vec_index += 1; | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | try file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { | ||
| 1334 | const target_endian = self.target.cpu.arch.endian(); | ||
| 1335 | const init_len_size: usize = if (self.tag == .macho) | ||
| 1336 | 4 | ||
| 1337 | else switch (self.ptr_width) { | ||
| 1338 | .p32 => @as(usize, 4), | ||
| 1339 | .p64 => 12, | ||
| 1340 | }; | ||
| 1341 | const ptr_width_bytes: u8 = self.ptrWidthBytes(); | ||
| 1342 | |||
| 1343 | // Enough for all the data without resizing. When support for more compilation units | ||
| 1344 | // is added, the size of this section will become more variable. | ||
| 1345 | var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, 100); | ||
| 1346 | defer di_buf.deinit(); | ||
| 1347 | |||
| 1348 | // initial length - length of the .debug_aranges contribution for this compilation unit, | ||
| 1349 | // not including the initial length itself. | ||
| 1350 | // We have to come back and write it later after we know the size. | ||
| 1351 | const init_len_index = di_buf.items.len; | ||
| 1352 | di_buf.items.len += init_len_size; | ||
| 1353 | const after_init_len = di_buf.items.len; | ||
| 1354 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version | ||
| 1355 | // When more than one compilation unit is supported, this will be the offset to it. | ||
| 1356 | // For now it is always at offset 0 in .debug_info. | ||
| 1357 | if (self.tag == .macho) { | ||
| 1358 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // __debug_info offset | ||
| 1359 | } else { | ||
| 1360 | self.writeAddrAssumeCapacity(&di_buf, 0); // .debug_info offset | ||
| 1361 | } | ||
| 1362 | di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size | ||
| 1363 | di_buf.appendAssumeCapacity(0); // segment_selector_size | ||
| 1364 | |||
| 1365 | const end_header_offset = di_buf.items.len; | ||
| 1366 | const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2); | ||
| 1367 | di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset); | ||
| 1368 | |||
| 1369 | // Currently only one compilation unit is supported, so the address range is simply | ||
| 1370 | // identical to the main program header virtual address and memory size. | ||
| 1371 | self.writeAddrAssumeCapacity(&di_buf, addr); | ||
| 1372 | self.writeAddrAssumeCapacity(&di_buf, size); | ||
| 1373 | |||
| 1374 | // Sentinel. | ||
| 1375 | self.writeAddrAssumeCapacity(&di_buf, 0); | ||
| 1376 | self.writeAddrAssumeCapacity(&di_buf, 0); | ||
| 1377 | |||
| 1378 | // Go back and populate the initial length. | ||
| 1379 | const init_len = di_buf.items.len - after_init_len; | ||
| 1380 | if (self.tag == .macho) { | ||
| 1381 | mem.writeIntLittle(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len)); | ||
| 1382 | } else switch (self.ptr_width) { | ||
| 1383 | .p32 => { | ||
| 1384 | mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian); | ||
| 1385 | }, | ||
| 1386 | .p64 => { | ||
| 1387 | // initial length - length of the .debug_aranges contribution for this compilation unit, | ||
| 1388 | // not including the initial length itself. | ||
| 1389 | di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff }; | ||
| 1390 | mem.writeInt(u64, di_buf.items[init_len_index + 4 ..][0..8], init_len, target_endian); | ||
| 1391 | }, | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | const needed_size = di_buf.items.len; | ||
| 1395 | switch (self.tag) { | ||
| 1396 | .elf => { | ||
| 1397 | const elf_file = file.cast(File.Elf).?; | ||
| 1398 | const debug_aranges_sect = &elf_file.sections.items[elf_file.debug_aranges_section_index.?]; | ||
| 1399 | const allocated_size = elf_file.allocatedSize(debug_aranges_sect.sh_offset); | ||
| 1400 | if (needed_size > allocated_size) { | ||
| 1401 | debug_aranges_sect.sh_size = 0; // free the space | ||
| 1402 | debug_aranges_sect.sh_offset = elf_file.findFreeSpace(needed_size, 16); | ||
| 1403 | } | ||
| 1404 | debug_aranges_sect.sh_size = needed_size; | ||
| 1405 | log.debug(".debug_aranges start=0x{x} end=0x{x}", .{ | ||
| 1406 | debug_aranges_sect.sh_offset, | ||
| 1407 | debug_aranges_sect.sh_offset + needed_size, | ||
| 1408 | }); | ||
| 1409 | const file_pos = debug_aranges_sect.sh_offset; | ||
| 1410 | try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos); | ||
| 1411 | }, | ||
| 1412 | .macho => { | ||
| 1413 | const macho_file = file.cast(File.MachO).?; | ||
| 1414 | const d_sym = &macho_file.d_sym.?; | ||
| 1415 | const dwarf_seg = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; | ||
| 1416 | const debug_aranges_sect = &dwarf_seg.sections.items[d_sym.debug_aranges_section_index.?]; | ||
| 1417 | const allocated_size = d_sym.allocatedSize(debug_aranges_sect.offset); | ||
| 1418 | if (needed_size > allocated_size) { | ||
| 1419 | debug_aranges_sect.size = 0; // free the space | ||
| 1420 | const new_offset = d_sym.findFreeSpace(needed_size, 16); | ||
| 1421 | debug_aranges_sect.addr = dwarf_seg.inner.vmaddr + new_offset - dwarf_seg.inner.fileoff; | ||
| 1422 | debug_aranges_sect.offset = @intCast(u32, new_offset); | ||
| 1423 | } | ||
| 1424 | debug_aranges_sect.size = needed_size; | ||
| 1425 | log.debug("__debug_aranges start=0x{x} end=0x{x}", .{ | ||
| 1426 | debug_aranges_sect.offset, | ||
| 1427 | debug_aranges_sect.offset + needed_size, | ||
| 1428 | }); | ||
| 1429 | const file_pos = debug_aranges_sect.offset; | ||
| 1430 | try d_sym.file.pwriteAll(di_buf.items, file_pos); | ||
| 1431 | }, | ||
| 1432 | else => unreachable, | ||
| 1433 | } | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void { | ||
| 1437 | const ptr_width_bytes: u8 = self.ptrWidthBytes(); | ||
| 1438 | const target_endian = self.target.cpu.arch.endian(); | ||
| 1439 | const init_len_size: usize = if (self.tag == .macho) | ||
| 1440 | 4 | ||
| 1441 | else switch (self.ptr_width) { | ||
| 1442 | .p32 => @as(usize, 4), | ||
| 1443 | .p64 => 12, | ||
| 1444 | }; | ||
| 1445 | |||
| 1446 | const dbg_line_prg_off = self.getDebugLineProgramOff() orelse return; | ||
| 1447 | const dbg_line_prg_end = self.getDebugLineProgramEnd().?; | ||
| 1448 | assert(dbg_line_prg_end != 0); | ||
| 1449 | |||
| 1450 | // The size of this header is variable, depending on the number of directories, | ||
| 1451 | // files, and padding. We have a function to compute the upper bound size, however, | ||
| 1452 | // because it's needed for determining where to put the offset of the first `SrcFn`. | ||
| 1453 | const needed_bytes = self.dbgLineNeededHeaderBytes(module); | ||
| 1454 | var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, needed_bytes); | ||
| 1455 | defer di_buf.deinit(); | ||
| 1456 | |||
| 1457 | // initial length - length of the .debug_line contribution for this compilation unit, | ||
| 1458 | // not including the initial length itself. | ||
| 1459 | const after_init_len = di_buf.items.len + init_len_size; | ||
| 1460 | const init_len = dbg_line_prg_end - after_init_len; | ||
| 1461 | if (self.tag == .macho) { | ||
| 1462 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len)); | ||
| 1463 | } else switch (self.ptr_width) { | ||
| 1464 | .p32 => { | ||
| 1465 | mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian); | ||
| 1466 | }, | ||
| 1467 | .p64 => { | ||
| 1468 | di_buf.appendNTimesAssumeCapacity(0xff, 4); | ||
| 1469 | mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian); | ||
| 1470 | }, | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version | ||
| 1474 | |||
| 1475 | // Empirically, debug info consumers do not respect this field, or otherwise | ||
| 1476 | // consider it to be an error when it does not point exactly to the end of the header. | ||
| 1477 | // Therefore we rely on the NOP jump at the beginning of the Line Number Program for | ||
| 1478 | // padding rather than this field. | ||
| 1479 | const before_header_len = di_buf.items.len; | ||
| 1480 | di_buf.items.len += if (self.tag == .macho) @sizeOf(u32) else ptr_width_bytes; // We will come back and write this. | ||
| 1481 | const after_header_len = di_buf.items.len; | ||
| 1482 | |||
| 1483 | const opcode_base = DW.LNS.set_isa + 1; | ||
| 1484 | di_buf.appendSliceAssumeCapacity(&[_]u8{ | ||
| 1485 | 1, // minimum_instruction_length | ||
| 1486 | 1, // maximum_operations_per_instruction | ||
| 1487 | 1, // default_is_stmt | ||
| 1488 | 1, // line_base (signed) | ||
| 1489 | 1, // line_range | ||
| 1490 | opcode_base, | ||
| 1491 | |||
| 1492 | // Standard opcode lengths. The number of items here is based on `opcode_base`. | ||
| 1493 | // The value is the number of LEB128 operands the instruction takes. | ||
| 1494 | 0, // `DW.LNS.copy` | ||
| 1495 | 1, // `DW.LNS.advance_pc` | ||
| 1496 | 1, // `DW.LNS.advance_line` | ||
| 1497 | 1, // `DW.LNS.set_file` | ||
| 1498 | 1, // `DW.LNS.set_column` | ||
| 1499 | 0, // `DW.LNS.negate_stmt` | ||
| 1500 | 0, // `DW.LNS.set_basic_block` | ||
| 1501 | 0, // `DW.LNS.const_add_pc` | ||
| 1502 | 1, // `DW.LNS.fixed_advance_pc` | ||
| 1503 | 0, // `DW.LNS.set_prologue_end` | ||
| 1504 | 0, // `DW.LNS.set_epilogue_begin` | ||
| 1505 | 1, // `DW.LNS.set_isa` | ||
| 1506 | 0, // include_directories (none except the compilation unit cwd) | ||
| 1507 | }); | ||
| 1508 | // file_names[0] | ||
| 1509 | di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name | ||
| 1510 | di_buf.appendSliceAssumeCapacity(&[_]u8{ | ||
| 1511 | 0, // null byte for the relative path name | ||
| 1512 | 0, // directory_index | ||
| 1513 | 0, // mtime (TODO supply this) | ||
| 1514 | 0, // file size bytes (TODO supply this) | ||
| 1515 | 0, // file_names sentinel | ||
| 1516 | }); | ||
| 1517 | |||
| 1518 | const header_len = di_buf.items.len - after_header_len; | ||
| 1519 | if (self.tag == .macho) { | ||
| 1520 | mem.writeIntLittle(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len)); | ||
| 1521 | } else switch (self.ptr_width) { | ||
| 1522 | .p32 => { | ||
| 1523 | mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian); | ||
| 1524 | }, | ||
| 1525 | .p64 => { | ||
| 1526 | mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian); | ||
| 1527 | }, | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | // We use NOPs because consumers empirically do not respect the header length field. | ||
| 1531 | if (di_buf.items.len > dbg_line_prg_off) { | ||
| 1532 | // Move the first N files to the end to make more padding for the header. | ||
| 1533 | @panic("TODO: handle .debug_line header exceeding its padding"); | ||
| 1534 | } | ||
| 1535 | const jmp_amt = dbg_line_prg_off - di_buf.items.len; | ||
| 1536 | switch (self.tag) { | ||
| 1537 | .elf => { | ||
| 1538 | const elf_file = file.cast(File.Elf).?; | ||
| 1539 | const debug_line_sect = elf_file.sections.items[elf_file.debug_line_section_index.?]; | ||
| 1540 | const file_pos = debug_line_sect.sh_offset; | ||
| 1541 | try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt); | ||
| 1542 | }, | ||
| 1543 | .macho => { | ||
| 1544 | const macho_file = file.cast(File.MachO).?; | ||
| 1545 | const d_sym = &macho_file.d_sym.?; | ||
| 1546 | const dwarf_seg = d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; | ||
| 1547 | const debug_line_sect = dwarf_seg.sections.items[d_sym.debug_line_section_index.?]; | ||
| 1548 | const file_pos = debug_line_sect.offset; | ||
| 1549 | try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt); | ||
| 1550 | }, | ||
| 1551 | else => unreachable, | ||
| 1552 | } | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | fn getDebugInfoOff(self: Dwarf) ?u32 { | ||
| 1556 | const first = self.dbg_info_decl_first orelse return null; | ||
| 1557 | return first.off; | ||
| 1558 | } | ||
| 1559 | |||
| 1560 | fn getDebugInfoEnd(self: Dwarf) ?u32 { | ||
| 1561 | const last = self.dbg_info_decl_last orelse return null; | ||
| 1562 | return last.off + last.len; | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | fn getDebugLineProgramOff(self: Dwarf) ?u32 { | ||
| 1566 | const first = self.dbg_line_fn_first orelse return null; | ||
| 1567 | return first.off; | ||
| 1568 | } | ||
| 1569 | |||
| 1570 | fn getDebugLineProgramEnd(self: Dwarf) ?u32 { | ||
| 1571 | const last = self.dbg_line_fn_last orelse return null; | ||
| 1572 | return last.off + last.len; | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | /// Always 4 or 8 depending on whether this is 32-bit or 64-bit format. | ||
| 1576 | fn ptrWidthBytes(self: Dwarf) u8 { | ||
| 1577 | return switch (self.ptr_width) { | ||
| 1578 | .p32 => 4, | ||
| 1579 | .p64 => 8, | ||
| 1580 | }; | ||
| 1581 | } | ||
| 1582 | |||
| 1583 | fn dbgLineNeededHeaderBytes(self: Dwarf, module: *Module) u32 { | ||
| 1584 | _ = self; | ||
| 1585 | const directory_entry_format_count = 1; | ||
| 1586 | const file_name_entry_format_count = 1; | ||
| 1587 | const directory_count = 1; | ||
| 1588 | const file_name_count = 1; | ||
| 1589 | const root_src_dir_path_len = if (module.root_pkg.root_src_directory.path) |p| p.len else 1; // "." | ||
| 1590 | return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + | ||
| 1591 | directory_count * 8 + file_name_count * 8 + | ||
| 1592 | // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like | ||
| 1593 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. | ||
| 1594 | root_src_dir_path_len + | ||
| 1595 | module.root_pkg.root_src_path.len); | ||
| 1596 | } | ||
| 1597 | |||
| 1598 | /// The reloc offset for the line offset of a function from the previous function's line. | ||
| 1599 | /// It's a fixed-size 4-byte ULEB128. | ||
| 1600 | fn getRelocDbgLineOff(self: Dwarf) usize { | ||
| 1601 | return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1; | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | fn getRelocDbgFileIndex(self: Dwarf) usize { | ||
| 1605 | return self.getRelocDbgLineOff() + 5; | ||
| 1606 | } | ||
| 1607 | |||
| 1608 | fn getRelocDbgInfoSubprogramHighPC(self: Dwarf) u32 { | ||
| 1609 | return dbg_info_low_pc_reloc_index + self.ptrWidthBytes(); | ||
| 1610 | } | ||
| 1611 | |||
| 1612 | /// TODO Improve this to use a table. | ||
| 1613 | fn makeString(self: *Dwarf, bytes: []const u8) !u32 { | ||
| 1614 | try self.strtab.ensureUnusedCapacity(self.allocator, bytes.len + 1); | ||
| 1615 | const result = self.strtab.items.len; | ||
| 1616 | self.strtab.appendSliceAssumeCapacity(bytes); | ||
| 1617 | self.strtab.appendAssumeCapacity(0); | ||
| 1618 | return @intCast(u32, result); | ||
| 1619 | } | ||
| 1620 | |||
| 1621 | fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { | ||
| 1622 | // TODO https://github.com/ziglang/zig/issues/1284 | ||
| 1623 | return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch | ||
| 1624 | std.math.maxInt(@TypeOf(actual_size)); | ||
| 1625 | } | ||
src/link/Elf.zig+105-1237| ... | @@ -9,11 +9,10 @@ const Allocator = std.mem.Allocator; | ... | @@ -9,11 +9,10 @@ const Allocator = std.mem.Allocator; |
| 9 | const fs = std.fs; | 9 | const fs = std.fs; |
| 10 | const elf = std.elf; | 10 | const elf = std.elf; |
| 11 | const log = std.log.scoped(.link); | 11 | const log = std.log.scoped(.link); |
| 12 | const DW = std.dwarf; | ||
| 13 | const leb128 = std.leb; | ||
| 14 | 12 | ||
| 15 | const Module = @import("../Module.zig"); | 13 | const Module = @import("../Module.zig"); |
| 16 | const Compilation = @import("../Compilation.zig"); | 14 | const Compilation = @import("../Compilation.zig"); |
| 15 | const Dwarf = @import("Dwarf.zig"); | ||
| 17 | const codegen = @import("../codegen.zig"); | 16 | const codegen = @import("../codegen.zig"); |
| 18 | const lldMain = @import("../main.zig").lldMain; | 17 | const lldMain = @import("../main.zig").lldMain; |
| 19 | const trace = @import("../tracy.zig").trace; | 18 | const trace = @import("../tracy.zig").trace; |
| ... | @@ -37,6 +36,7 @@ const default_entry_addr = 0x8000000; | ... | @@ -37,6 +36,7 @@ const default_entry_addr = 0x8000000; |
| 37 | pub const base_tag: File.Tag = .elf; | 36 | pub const base_tag: File.Tag = .elf; |
| 38 | 37 | ||
| 39 | base: File, | 38 | base: File, |
| 39 | dwarf: ?Dwarf = null, | ||
| 40 | 40 | ||
| 41 | ptr_width: PtrWidth, | 41 | ptr_width: PtrWidth, |
| 42 | 42 | ||
| ... | @@ -67,7 +67,6 @@ phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, | ... | @@ -67,7 +67,6 @@ phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, |
| 67 | entry_addr: ?u64 = null, | 67 | entry_addr: ?u64 = null, |
| 68 | page_size: u16, | 68 | page_size: u16, |
| 69 | 69 | ||
| 70 | debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, | ||
| 71 | shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, | 70 | shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, |
| 72 | shstrtab_index: ?u16 = null, | 71 | shstrtab_index: ?u16 = null, |
| 73 | 72 | ||
| ... | @@ -82,8 +81,6 @@ debug_str_section_index: ?u16 = null, | ... | @@ -82,8 +81,6 @@ debug_str_section_index: ?u16 = null, |
| 82 | debug_aranges_section_index: ?u16 = null, | 81 | debug_aranges_section_index: ?u16 = null, |
| 83 | debug_line_section_index: ?u16 = null, | 82 | debug_line_section_index: ?u16 = null, |
| 84 | 83 | ||
| 85 | debug_abbrev_table_offset: ?u64 = null, | ||
| 86 | |||
| 87 | /// The same order as in the file. ELF requires global symbols to all be after the | 84 | /// The same order as in the file. ELF requires global symbols to all be after the |
| 88 | /// local symbols, they cannot be mixed. So we must buffer all the global symbols and | 85 | /// local symbols, they cannot be mixed. So we must buffer all the global symbols and |
| 89 | /// write them at the end. These are only the local symbols. The length of this array | 86 | /// write them at the end. These are only the local symbols. The length of this array |
| ... | @@ -168,18 +165,6 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{}, | ... | @@ -168,18 +165,6 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{}, |
| 168 | /// with `Decl` `main`, and lives as long as that `Decl`. | 165 | /// with `Decl` `main`, and lives as long as that `Decl`. |
| 169 | unnamed_const_atoms: UnnamedConstTable = .{}, | 166 | unnamed_const_atoms: UnnamedConstTable = .{}, |
| 170 | 167 | ||
| 171 | /// A list of `SrcFn` whose Line Number Programs have surplus capacity. | ||
| 172 | /// This is the same concept as `text_block_free_list`; see those doc comments. | ||
| 173 | dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{}, | ||
| 174 | dbg_line_fn_first: ?*SrcFn = null, | ||
| 175 | dbg_line_fn_last: ?*SrcFn = null, | ||
| 176 | |||
| 177 | /// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity. | ||
| 178 | /// This is the same concept as `text_block_free_list`; see those doc comments. | ||
| 179 | dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{}, | ||
| 180 | dbg_info_decl_first: ?*TextBlock = null, | ||
| 181 | dbg_info_decl_last: ?*TextBlock = null, | ||
| 182 | |||
| 183 | /// A table of relocations indexed by the owning them `TextBlock`. | 168 | /// A table of relocations indexed by the owning them `TextBlock`. |
| 184 | /// Note that once we refactor `TextBlock`'s lifetime and ownership rules, | 169 | /// Note that once we refactor `TextBlock`'s lifetime and ownership rules, |
| 185 | /// this will be a table indexed by index into the list of Atoms. | 170 | /// this will be a table indexed by index into the list of Atoms. |
| ... | @@ -222,24 +207,14 @@ pub const TextBlock = struct { | ... | @@ -222,24 +207,14 @@ pub const TextBlock = struct { |
| 222 | prev: ?*TextBlock, | 207 | prev: ?*TextBlock, |
| 223 | next: ?*TextBlock, | 208 | next: ?*TextBlock, |
| 224 | 209 | ||
| 225 | /// Previous/next linked list pointers. | 210 | dbg_info_atom: Dwarf.DebugInfoAtom, |
| 226 | /// This is the linked list node for this Decl's corresponding .debug_info tag. | ||
| 227 | dbg_info_prev: ?*TextBlock, | ||
| 228 | dbg_info_next: ?*TextBlock, | ||
| 229 | /// Offset into .debug_info pointing to the tag for this Decl. | ||
| 230 | dbg_info_off: u32, | ||
| 231 | /// Size of the .debug_info tag for this Decl, not including padding. | ||
| 232 | dbg_info_len: u32, | ||
| 233 | 211 | ||
| 234 | pub const empty = TextBlock{ | 212 | pub const empty = TextBlock{ |
| 235 | .local_sym_index = 0, | 213 | .local_sym_index = 0, |
| 236 | .offset_table_index = undefined, | 214 | .offset_table_index = undefined, |
| 237 | .prev = null, | 215 | .prev = null, |
| 238 | .next = null, | 216 | .next = null, |
| 239 | .dbg_info_prev = null, | 217 | .dbg_info_atom = undefined, |
| 240 | .dbg_info_next = null, | ||
| 241 | .dbg_info_off = undefined, | ||
| 242 | .dbg_info_len = undefined, | ||
| 243 | }; | 218 | }; |
| 244 | 219 | ||
| 245 | /// Returns how much room there is to grow in virtual address space. | 220 | /// Returns how much room there is to grow in virtual address space. |
| ... | @@ -273,26 +248,6 @@ pub const Export = struct { | ... | @@ -273,26 +248,6 @@ pub const Export = struct { |
| 273 | sym_index: ?u32 = null, | 248 | sym_index: ?u32 = null, |
| 274 | }; | 249 | }; |
| 275 | 250 | ||
| 276 | pub const SrcFn = struct { | ||
| 277 | /// Offset from the beginning of the Debug Line Program header that contains this function. | ||
| 278 | off: u32, | ||
| 279 | /// Size of the line number program component belonging to this function, not | ||
| 280 | /// including padding. | ||
| 281 | len: u32, | ||
| 282 | |||
| 283 | /// Points to the previous and next neighbors, based on the offset from .debug_line. | ||
| 284 | /// This can be used to find, for example, the capacity of this `SrcFn`. | ||
| 285 | prev: ?*SrcFn, | ||
| 286 | next: ?*SrcFn, | ||
| 287 | |||
| 288 | pub const empty: SrcFn = .{ | ||
| 289 | .off = 0, | ||
| 290 | .len = 0, | ||
| 291 | .prev = null, | ||
| 292 | .next = null, | ||
| 293 | }; | ||
| 294 | }; | ||
| 295 | |||
| 296 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf { | 251 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf { |
| 297 | assert(options.object_format == .elf); | 252 | assert(options.object_format == .elf); |
| 298 | 253 | ||
| ... | @@ -351,6 +306,11 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { | ... | @@ -351,6 +306,11 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { |
| 351 | errdefer gpa.destroy(self); | 306 | errdefer gpa.destroy(self); |
| 352 | const page_size: u16 = 0x1000; // TODO ppc64le requires 64KB | 307 | const page_size: u16 = 0x1000; // TODO ppc64le requires 64KB |
| 353 | 308 | ||
| 309 | var dwarf: ?Dwarf = if (!options.strip and options.module != null) | ||
| 310 | Dwarf.init(gpa, .elf, options.target) | ||
| 311 | else | ||
| 312 | null; | ||
| 313 | |||
| 354 | self.* = .{ | 314 | self.* = .{ |
| 355 | .base = .{ | 315 | .base = .{ |
| 356 | .tag = .elf, | 316 | .tag = .elf, |
| ... | @@ -358,6 +318,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { | ... | @@ -358,6 +318,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { |
| 358 | .allocator = gpa, | 318 | .allocator = gpa, |
| 359 | .file = null, | 319 | .file = null, |
| 360 | }, | 320 | }, |
| 321 | .dwarf = dwarf, | ||
| 361 | .ptr_width = ptr_width, | 322 | .ptr_width = ptr_width, |
| 362 | .page_size = page_size, | 323 | .page_size = page_size, |
| 363 | }; | 324 | }; |
| ... | @@ -377,14 +338,11 @@ pub fn deinit(self: *Elf) void { | ... | @@ -377,14 +338,11 @@ pub fn deinit(self: *Elf) void { |
| 377 | self.sections.deinit(self.base.allocator); | 338 | self.sections.deinit(self.base.allocator); |
| 378 | self.program_headers.deinit(self.base.allocator); | 339 | self.program_headers.deinit(self.base.allocator); |
| 379 | self.shstrtab.deinit(self.base.allocator); | 340 | self.shstrtab.deinit(self.base.allocator); |
| 380 | self.debug_strtab.deinit(self.base.allocator); | ||
| 381 | self.local_symbols.deinit(self.base.allocator); | 341 | self.local_symbols.deinit(self.base.allocator); |
| 382 | self.global_symbols.deinit(self.base.allocator); | 342 | self.global_symbols.deinit(self.base.allocator); |
| 383 | self.global_symbol_free_list.deinit(self.base.allocator); | 343 | self.global_symbol_free_list.deinit(self.base.allocator); |
| 384 | self.local_symbol_free_list.deinit(self.base.allocator); | 344 | self.local_symbol_free_list.deinit(self.base.allocator); |
| 385 | self.offset_table_free_list.deinit(self.base.allocator); | 345 | self.offset_table_free_list.deinit(self.base.allocator); |
| 386 | self.dbg_line_fn_free_list.deinit(self.base.allocator); | ||
| 387 | self.dbg_info_decl_free_list.deinit(self.base.allocator); | ||
| 388 | self.offset_table.deinit(self.base.allocator); | 346 | self.offset_table.deinit(self.base.allocator); |
| 389 | self.phdr_shdr_table.deinit(self.base.allocator); | 347 | self.phdr_shdr_table.deinit(self.base.allocator); |
| 390 | self.decls.deinit(self.base.allocator); | 348 | self.decls.deinit(self.base.allocator); |
| ... | @@ -420,6 +378,10 @@ pub fn deinit(self: *Elf) void { | ... | @@ -420,6 +378,10 @@ pub fn deinit(self: *Elf) void { |
| 420 | } | 378 | } |
| 421 | 379 | ||
| 422 | self.atom_by_index_table.deinit(self.base.allocator); | 380 | self.atom_by_index_table.deinit(self.base.allocator); |
| 381 | |||
| 382 | if (self.dwarf) |*dw| { | ||
| 383 | dw.deinit(); | ||
| 384 | } | ||
| 423 | } | 385 | } |
| 424 | 386 | ||
| 425 | pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.RelocInfo) !u64 { | 387 | pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.RelocInfo) !u64 { |
| ... | @@ -443,14 +405,6 @@ pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.Reloc | ... | @@ -443,14 +405,6 @@ pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.Reloc |
| 443 | return vaddr; | 405 | return vaddr; |
| 444 | } | 406 | } |
| 445 | 407 | ||
| 446 | fn getDebugLineProgramOff(self: Elf) u32 { | ||
| 447 | return self.dbg_line_fn_first.?.off; | ||
| 448 | } | ||
| 449 | |||
| 450 | fn getDebugLineProgramEnd(self: Elf) u32 { | ||
| 451 | return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len; | ||
| 452 | } | ||
| 453 | |||
| 454 | /// Returns end pos of collision, if any. | 408 | /// Returns end pos of collision, if any. |
| 455 | fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { | 409 | fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 456 | const small_ptr = self.ptr_width == .p32; | 410 | const small_ptr = self.ptr_width == .p32; |
| ... | @@ -497,7 +451,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { | ... | @@ -497,7 +451,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 497 | return null; | 451 | return null; |
| 498 | } | 452 | } |
| 499 | 453 | ||
| 500 | fn allocatedSize(self: *Elf, start: u64) u64 { | 454 | pub fn allocatedSize(self: *Elf, start: u64) u64 { |
| 501 | if (start == 0) | 455 | if (start == 0) |
| 502 | return 0; | 456 | return 0; |
| 503 | var min_pos: u64 = std.math.maxInt(u64); | 457 | var min_pos: u64 = std.math.maxInt(u64); |
| ... | @@ -518,7 +472,7 @@ fn allocatedSize(self: *Elf, start: u64) u64 { | ... | @@ -518,7 +472,7 @@ fn allocatedSize(self: *Elf, start: u64) u64 { |
| 518 | return min_pos - start; | 472 | return min_pos - start; |
| 519 | } | 473 | } |
| 520 | 474 | ||
| 521 | fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 { | 475 | pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 { |
| 522 | var start: u64 = 0; | 476 | var start: u64 = 0; |
| 523 | while (self.detectAllocCollision(start, object_size)) |item_end| { | 477 | while (self.detectAllocCollision(start, object_size)) |item_end| { |
| 524 | start = mem.alignForwardGeneric(u64, item_end, min_alignment); | 478 | start = mem.alignForwardGeneric(u64, item_end, min_alignment); |
| ... | @@ -535,15 +489,6 @@ fn makeString(self: *Elf, bytes: []const u8) !u32 { | ... | @@ -535,15 +489,6 @@ fn makeString(self: *Elf, bytes: []const u8) !u32 { |
| 535 | return @intCast(u32, result); | 489 | return @intCast(u32, result); |
| 536 | } | 490 | } |
| 537 | 491 | ||
| 538 | /// TODO Improve this to use a table. | ||
| 539 | fn makeDebugString(self: *Elf, bytes: []const u8) !u32 { | ||
| 540 | try self.debug_strtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1); | ||
| 541 | const result = self.debug_strtab.items.len; | ||
| 542 | self.debug_strtab.appendSliceAssumeCapacity(bytes); | ||
| 543 | self.debug_strtab.appendAssumeCapacity(0); | ||
| 544 | return @intCast(u32, result); | ||
| 545 | } | ||
| 546 | |||
| 547 | fn getString(self: *Elf, str_off: u32) []const u8 { | 492 | fn getString(self: *Elf, str_off: u32) []const u8 { |
| 548 | assert(str_off < self.shstrtab.items.len); | 493 | assert(str_off < self.shstrtab.items.len); |
| 549 | return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0); | 494 | return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0); |
| ... | @@ -806,14 +751,14 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -806,14 +751,14 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 806 | 751 | ||
| 807 | if (self.debug_str_section_index == null) { | 752 | if (self.debug_str_section_index == null) { |
| 808 | self.debug_str_section_index = @intCast(u16, self.sections.items.len); | 753 | self.debug_str_section_index = @intCast(u16, self.sections.items.len); |
| 809 | assert(self.debug_strtab.items.len == 0); | 754 | assert(self.dwarf.?.strtab.items.len == 0); |
| 810 | try self.sections.append(self.base.allocator, .{ | 755 | try self.sections.append(self.base.allocator, .{ |
| 811 | .sh_name = try self.makeString(".debug_str"), | 756 | .sh_name = try self.makeString(".debug_str"), |
| 812 | .sh_type = elf.SHT_PROGBITS, | 757 | .sh_type = elf.SHT_PROGBITS, |
| 813 | .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, | 758 | .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, |
| 814 | .sh_addr = 0, | 759 | .sh_addr = 0, |
| 815 | .sh_offset = 0, | 760 | .sh_offset = 0, |
| 816 | .sh_size = self.debug_strtab.items.len, | 761 | .sh_size = 0, |
| 817 | .sh_link = 0, | 762 | .sh_link = 0, |
| 818 | .sh_info = 0, | 763 | .sh_info = 0, |
| 819 | .sh_addralign = 1, | 764 | .sh_addralign = 1, |
| ... | @@ -977,16 +922,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -977,16 +922,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 977 | } | 922 | } |
| 978 | } | 923 | } |
| 979 | 924 | ||
| 980 | pub const abbrev_compile_unit = 1; | ||
| 981 | pub const abbrev_subprogram = 2; | ||
| 982 | pub const abbrev_subprogram_retvoid = 3; | ||
| 983 | pub const abbrev_base_type = 4; | ||
| 984 | pub const abbrev_ptr_type = 5; | ||
| 985 | pub const abbrev_struct_type = 6; | ||
| 986 | pub const abbrev_struct_member = 7; | ||
| 987 | pub const abbrev_pad1 = 8; | ||
| 988 | pub const abbrev_parameter = 9; | ||
| 989 | |||
| 990 | pub fn flush(self: *Elf, comp: *Compilation) !void { | 925 | pub fn flush(self: *Elf, comp: *Compilation) !void { |
| 991 | if (self.base.options.emit == null) { | 926 | if (self.base.options.emit == null) { |
| 992 | if (build_options.have_llvm) { | 927 | if (build_options.have_llvm) { |
| ... | @@ -1022,11 +957,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { | ... | @@ -1022,11 +957,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1022 | 957 | ||
| 1023 | const target_endian = self.base.options.target.cpu.arch.endian(); | 958 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 1024 | const foreign_endian = target_endian != builtin.cpu.arch.endian(); | 959 | const foreign_endian = target_endian != builtin.cpu.arch.endian(); |
| 1025 | const ptr_width_bytes: u8 = self.ptrWidthBytes(); | ||
| 1026 | const init_len_size: usize = switch (self.ptr_width) { | ||
| 1027 | .p32 => 4, | ||
| 1028 | .p64 => 12, | ||
| 1029 | }; | ||
| 1030 | 960 | ||
| 1031 | { | 961 | { |
| 1032 | var it = self.relocs.iterator(); | 962 | var it = self.relocs.iterator(); |
| ... | @@ -1068,349 +998,38 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { | ... | @@ -1068,349 +998,38 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1068 | try self.writeAllGlobalSymbols(); | 998 | try self.writeAllGlobalSymbols(); |
| 1069 | 999 | ||
| 1070 | if (self.debug_abbrev_section_dirty) { | 1000 | if (self.debug_abbrev_section_dirty) { |
| 1071 | const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?]; | 1001 | try self.dwarf.?.writeDbgAbbrev(&self.base); |
| 1072 | |||
| 1073 | // These are LEB encoded but since the values are all less than 127 | ||
| 1074 | // we can simply append these bytes. | ||
| 1075 | const abbrev_buf = [_]u8{ | ||
| 1076 | abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header | ||
| 1077 | DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, | ||
| 1078 | DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, | ||
| 1079 | DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, | ||
| 1080 | DW.FORM.strp, DW.AT.producer, DW.FORM.strp, | ||
| 1081 | DW.AT.language, DW.FORM.data2, 0, | ||
| 1082 | 0, // table sentinel | ||
| 1083 | abbrev_subprogram, | ||
| 1084 | DW.TAG.subprogram, | ||
| 1085 | DW.CHILDREN.yes, // header | ||
| 1086 | DW.AT.low_pc, | ||
| 1087 | DW.FORM.addr, | ||
| 1088 | DW.AT.high_pc, | ||
| 1089 | DW.FORM.data4, | ||
| 1090 | DW.AT.type, | ||
| 1091 | DW.FORM.ref4, | ||
| 1092 | DW.AT.name, | ||
| 1093 | DW.FORM.string, | ||
| 1094 | 0, 0, // table sentinel | ||
| 1095 | abbrev_subprogram_retvoid, | ||
| 1096 | DW.TAG.subprogram, DW.CHILDREN.yes, // header | ||
| 1097 | DW.AT.low_pc, DW.FORM.addr, | ||
| 1098 | DW.AT.high_pc, DW.FORM.data4, | ||
| 1099 | DW.AT.name, DW.FORM.string, | ||
| 1100 | 0, | ||
| 1101 | 0, // table sentinel | ||
| 1102 | abbrev_base_type, | ||
| 1103 | DW.TAG.base_type, | ||
| 1104 | DW.CHILDREN.no, // header | ||
| 1105 | DW.AT.encoding, | ||
| 1106 | DW.FORM.data1, | ||
| 1107 | DW.AT.byte_size, | ||
| 1108 | DW.FORM.data1, | ||
| 1109 | DW.AT.name, | ||
| 1110 | DW.FORM.string, | ||
| 1111 | 0, | ||
| 1112 | 0, // table sentinel | ||
| 1113 | abbrev_ptr_type, | ||
| 1114 | DW.TAG.pointer_type, | ||
| 1115 | DW.CHILDREN.no, // header | ||
| 1116 | DW.AT.type, | ||
| 1117 | DW.FORM.ref4, | ||
| 1118 | 0, | ||
| 1119 | 0, // table sentinel | ||
| 1120 | abbrev_struct_type, | ||
| 1121 | DW.TAG.structure_type, | ||
| 1122 | DW.CHILDREN.yes, // header | ||
| 1123 | DW.AT.byte_size, | ||
| 1124 | DW.FORM.sdata, | ||
| 1125 | DW.AT.name, | ||
| 1126 | DW.FORM.string, | ||
| 1127 | 0, | ||
| 1128 | 0, // table sentinel | ||
| 1129 | abbrev_struct_member, | ||
| 1130 | DW.TAG.member, | ||
| 1131 | DW.CHILDREN.no, // header | ||
| 1132 | DW.AT.name, | ||
| 1133 | DW.FORM.string, | ||
| 1134 | DW.AT.type, | ||
| 1135 | DW.FORM.ref4, | ||
| 1136 | DW.AT.data_member_location, | ||
| 1137 | DW.FORM.sdata, | ||
| 1138 | 0, | ||
| 1139 | 0, // table sentinel | ||
| 1140 | abbrev_pad1, | ||
| 1141 | DW.TAG.unspecified_type, | ||
| 1142 | DW.CHILDREN.no, // header | ||
| 1143 | 0, | ||
| 1144 | 0, // table sentinel | ||
| 1145 | abbrev_parameter, | ||
| 1146 | DW.TAG.formal_parameter, DW.CHILDREN.no, // header | ||
| 1147 | DW.AT.location, DW.FORM.exprloc, | ||
| 1148 | DW.AT.type, DW.FORM.ref4, | ||
| 1149 | DW.AT.name, DW.FORM.string, | ||
| 1150 | 0, | ||
| 1151 | 0, // table sentinel | ||
| 1152 | 0, | ||
| 1153 | 0, | ||
| 1154 | 0, // section sentinel | ||
| 1155 | }; | ||
| 1156 | |||
| 1157 | const needed_size = abbrev_buf.len; | ||
| 1158 | const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset); | ||
| 1159 | if (needed_size > allocated_size) { | ||
| 1160 | debug_abbrev_sect.sh_size = 0; // free the space | ||
| 1161 | debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1); | ||
| 1162 | } | ||
| 1163 | debug_abbrev_sect.sh_size = needed_size; | ||
| 1164 | log.debug(".debug_abbrev start=0x{x} end=0x{x}", .{ | ||
| 1165 | debug_abbrev_sect.sh_offset, | ||
| 1166 | debug_abbrev_sect.sh_offset + needed_size, | ||
| 1167 | }); | ||
| 1168 | |||
| 1169 | const abbrev_offset = 0; | ||
| 1170 | self.debug_abbrev_table_offset = abbrev_offset; | ||
| 1171 | try self.base.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset); | ||
| 1172 | if (!self.shdr_table_dirty) { | 1002 | if (!self.shdr_table_dirty) { |
| 1173 | // Then it won't get written with the others and we need to do it. | 1003 | // Then it won't get written with the others and we need to do it. |
| 1174 | try self.writeSectHeader(self.debug_abbrev_section_index.?); | 1004 | try self.writeSectHeader(self.debug_abbrev_section_index.?); |
| 1175 | } | 1005 | } |
| 1176 | |||
| 1177 | self.debug_abbrev_section_dirty = false; | 1006 | self.debug_abbrev_section_dirty = false; |
| 1178 | } | 1007 | } |
| 1179 | 1008 | ||
| 1180 | if (self.debug_info_header_dirty) debug_info: { | 1009 | if (self.debug_info_header_dirty) { |
| 1181 | // If this value is null it means there is an error in the module; | ||
| 1182 | // leave debug_info_header_dirty=true. | ||
| 1183 | const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info; | ||
| 1184 | const last_dbg_info_decl = self.dbg_info_decl_last.?; | ||
| 1185 | const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; | ||
| 1186 | |||
| 1187 | // We have a function to compute the upper bound size, because it's needed | ||
| 1188 | // for determining where to put the offset of the first `LinkBlock`. | ||
| 1189 | const needed_bytes = self.dbgInfoNeededHeaderBytes(); | ||
| 1190 | var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, needed_bytes); | ||
| 1191 | defer di_buf.deinit(); | ||
| 1192 | |||
| 1193 | // initial length - length of the .debug_info contribution for this compilation unit, | ||
| 1194 | // not including the initial length itself. | ||
| 1195 | // We have to come back and write it later after we know the size. | ||
| 1196 | const after_init_len = di_buf.items.len + init_len_size; | ||
| 1197 | // +1 for the final 0 that ends the compilation unit children. | ||
| 1198 | const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1; | ||
| 1199 | const init_len = dbg_info_end - after_init_len; | ||
| 1200 | switch (self.ptr_width) { | ||
| 1201 | .p32 => { | ||
| 1202 | mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian); | ||
| 1203 | }, | ||
| 1204 | .p64 => { | ||
| 1205 | di_buf.appendNTimesAssumeCapacity(0xff, 4); | ||
| 1206 | mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian); | ||
| 1207 | }, | ||
| 1208 | } | ||
| 1209 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version | ||
| 1210 | const abbrev_offset = self.debug_abbrev_table_offset.?; | ||
| 1211 | switch (self.ptr_width) { | ||
| 1212 | .p32 => { | ||
| 1213 | mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian); | ||
| 1214 | di_buf.appendAssumeCapacity(4); // address size | ||
| 1215 | }, | ||
| 1216 | .p64 => { | ||
| 1217 | mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian); | ||
| 1218 | di_buf.appendAssumeCapacity(8); // address size | ||
| 1219 | }, | ||
| 1220 | } | ||
| 1221 | // Write the form for the compile unit, which must match the abbrev table above. | ||
| 1222 | const name_strp = try self.makeDebugString(module.root_pkg.root_src_path); | ||
| 1223 | const comp_dir_strp = try self.makeDebugString(module.root_pkg.root_src_directory.path orelse "."); | ||
| 1224 | const producer_strp = try self.makeDebugString(link.producer_string); | ||
| 1225 | // Currently only one compilation unit is supported, so the address range is simply | 1010 | // Currently only one compilation unit is supported, so the address range is simply |
| 1226 | // identical to the main program header virtual address and memory size. | 1011 | // identical to the main program header virtual address and memory size. |
| 1227 | const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | 1012 | const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 1228 | const low_pc = text_phdr.p_vaddr; | 1013 | const low_pc = text_phdr.p_vaddr; |
| 1229 | const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; | 1014 | const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; |
| 1230 | 1015 | try self.dwarf.?.writeDbgInfoHeader(&self.base, module, low_pc, high_pc); | |
| 1231 | di_buf.appendAssumeCapacity(abbrev_compile_unit); | ||
| 1232 | self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT.stmt_list, DW.FORM.sec_offset | ||
| 1233 | self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc); | ||
| 1234 | self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc); | ||
| 1235 | self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp); | ||
| 1236 | self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp); | ||
| 1237 | self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp); | ||
| 1238 | // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number: | ||
| 1239 | // http://dwarfstd.org/ShowIssue.php?issue=171115.1 | ||
| 1240 | // Until then we say it is C99. | ||
| 1241 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99, target_endian); | ||
| 1242 | |||
| 1243 | if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) { | ||
| 1244 | // Move the first N decls to the end to make more padding for the header. | ||
| 1245 | @panic("TODO: handle .debug_info header exceeding its padding"); | ||
| 1246 | } | ||
| 1247 | const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len; | ||
| 1248 | try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.sh_offset); | ||
| 1249 | self.debug_info_header_dirty = false; | 1016 | self.debug_info_header_dirty = false; |
| 1250 | } | 1017 | } |
| 1251 | 1018 | ||
| 1252 | if (self.debug_aranges_section_dirty) { | 1019 | if (self.debug_aranges_section_dirty) { |
| 1253 | const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?]; | ||
| 1254 | |||
| 1255 | // Enough for all the data without resizing. When support for more compilation units | ||
| 1256 | // is added, the size of this section will become more variable. | ||
| 1257 | var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, 100); | ||
| 1258 | defer di_buf.deinit(); | ||
| 1259 | |||
| 1260 | // initial length - length of the .debug_aranges contribution for this compilation unit, | ||
| 1261 | // not including the initial length itself. | ||
| 1262 | // We have to come back and write it later after we know the size. | ||
| 1263 | const init_len_index = di_buf.items.len; | ||
| 1264 | di_buf.items.len += init_len_size; | ||
| 1265 | const after_init_len = di_buf.items.len; | ||
| 1266 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version | ||
| 1267 | // When more than one compilation unit is supported, this will be the offset to it. | ||
| 1268 | // For now it is always at offset 0 in .debug_info. | ||
| 1269 | self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // .debug_info offset | ||
| 1270 | di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size | ||
| 1271 | di_buf.appendAssumeCapacity(0); // segment_selector_size | ||
| 1272 | |||
| 1273 | const end_header_offset = di_buf.items.len; | ||
| 1274 | const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2); | ||
| 1275 | di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset); | ||
| 1276 | |||
| 1277 | // Currently only one compilation unit is supported, so the address range is simply | 1020 | // Currently only one compilation unit is supported, so the address range is simply |
| 1278 | // identical to the main program header virtual address and memory size. | 1021 | // identical to the main program header virtual address and memory size. |
| 1279 | const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | 1022 | const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 1280 | self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr); | 1023 | try self.dwarf.?.writeDbgAranges(&self.base, text_phdr.p_vaddr, text_phdr.p_memsz); |
| 1281 | self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_memsz); | ||
| 1282 | |||
| 1283 | // Sentinel. | ||
| 1284 | self.writeDwarfAddrAssumeCapacity(&di_buf, 0); | ||
| 1285 | self.writeDwarfAddrAssumeCapacity(&di_buf, 0); | ||
| 1286 | |||
| 1287 | // Go back and populate the initial length. | ||
| 1288 | const init_len = di_buf.items.len - after_init_len; | ||
| 1289 | switch (self.ptr_width) { | ||
| 1290 | .p32 => { | ||
| 1291 | mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian); | ||
| 1292 | }, | ||
| 1293 | .p64 => { | ||
| 1294 | // initial length - length of the .debug_aranges contribution for this compilation unit, | ||
| 1295 | // not including the initial length itself. | ||
| 1296 | di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff }; | ||
| 1297 | mem.writeInt(u64, di_buf.items[init_len_index + 4 ..][0..8], init_len, target_endian); | ||
| 1298 | }, | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | const needed_size = di_buf.items.len; | ||
| 1302 | const allocated_size = self.allocatedSize(debug_aranges_sect.sh_offset); | ||
| 1303 | if (needed_size > allocated_size) { | ||
| 1304 | debug_aranges_sect.sh_size = 0; // free the space | ||
| 1305 | debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16); | ||
| 1306 | } | ||
| 1307 | debug_aranges_sect.sh_size = needed_size; | ||
| 1308 | log.debug(".debug_aranges start=0x{x} end=0x{x}", .{ | ||
| 1309 | debug_aranges_sect.sh_offset, | ||
| 1310 | debug_aranges_sect.sh_offset + needed_size, | ||
| 1311 | }); | ||
| 1312 | |||
| 1313 | try self.base.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset); | ||
| 1314 | if (!self.shdr_table_dirty) { | 1024 | if (!self.shdr_table_dirty) { |
| 1315 | // Then it won't get written with the others and we need to do it. | 1025 | // Then it won't get written with the others and we need to do it. |
| 1316 | try self.writeSectHeader(self.debug_aranges_section_index.?); | 1026 | try self.writeSectHeader(self.debug_aranges_section_index.?); |
| 1317 | } | 1027 | } |
| 1318 | |||
| 1319 | self.debug_aranges_section_dirty = false; | 1028 | self.debug_aranges_section_dirty = false; |
| 1320 | } | 1029 | } |
| 1321 | if (self.debug_line_header_dirty) debug_line: { | ||
| 1322 | if (self.dbg_line_fn_first == null) { | ||
| 1323 | break :debug_line; // Error in module; leave debug_line_header_dirty=true. | ||
| 1324 | } | ||
| 1325 | const dbg_line_prg_off = self.getDebugLineProgramOff(); | ||
| 1326 | const dbg_line_prg_end = self.getDebugLineProgramEnd(); | ||
| 1327 | assert(dbg_line_prg_end != 0); | ||
| 1328 | |||
| 1329 | const debug_line_sect = &self.sections.items[self.debug_line_section_index.?]; | ||
| 1330 | |||
| 1331 | // The size of this header is variable, depending on the number of directories, | ||
| 1332 | // files, and padding. We have a function to compute the upper bound size, however, | ||
| 1333 | // because it's needed for determining where to put the offset of the first `SrcFn`. | ||
| 1334 | const needed_bytes = self.dbgLineNeededHeaderBytes(); | ||
| 1335 | var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, needed_bytes); | ||
| 1336 | defer di_buf.deinit(); | ||
| 1337 | |||
| 1338 | // initial length - length of the .debug_line contribution for this compilation unit, | ||
| 1339 | // not including the initial length itself. | ||
| 1340 | const after_init_len = di_buf.items.len + init_len_size; | ||
| 1341 | const init_len = dbg_line_prg_end - after_init_len; | ||
| 1342 | switch (self.ptr_width) { | ||
| 1343 | .p32 => { | ||
| 1344 | mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian); | ||
| 1345 | }, | ||
| 1346 | .p64 => { | ||
| 1347 | di_buf.appendNTimesAssumeCapacity(0xff, 4); | ||
| 1348 | mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian); | ||
| 1349 | }, | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version | ||
| 1353 | |||
| 1354 | // Empirically, debug info consumers do not respect this field, or otherwise | ||
| 1355 | // consider it to be an error when it does not point exactly to the end of the header. | ||
| 1356 | // Therefore we rely on the NOP jump at the beginning of the Line Number Program for | ||
| 1357 | // padding rather than this field. | ||
| 1358 | const before_header_len = di_buf.items.len; | ||
| 1359 | di_buf.items.len += ptr_width_bytes; // We will come back and write this. | ||
| 1360 | const after_header_len = di_buf.items.len; | ||
| 1361 | |||
| 1362 | const opcode_base = DW.LNS.set_isa + 1; | ||
| 1363 | di_buf.appendSliceAssumeCapacity(&[_]u8{ | ||
| 1364 | 1, // minimum_instruction_length | ||
| 1365 | 1, // maximum_operations_per_instruction | ||
| 1366 | 1, // default_is_stmt | ||
| 1367 | 1, // line_base (signed) | ||
| 1368 | 1, // line_range | ||
| 1369 | opcode_base, | ||
| 1370 | |||
| 1371 | // Standard opcode lengths. The number of items here is based on `opcode_base`. | ||
| 1372 | // The value is the number of LEB128 operands the instruction takes. | ||
| 1373 | 0, // `DW.LNS.copy` | ||
| 1374 | 1, // `DW.LNS.advance_pc` | ||
| 1375 | 1, // `DW.LNS.advance_line` | ||
| 1376 | 1, // `DW.LNS.set_file` | ||
| 1377 | 1, // `DW.LNS.set_column` | ||
| 1378 | 0, // `DW.LNS.negate_stmt` | ||
| 1379 | 0, // `DW.LNS.set_basic_block` | ||
| 1380 | 0, // `DW.LNS.const_add_pc` | ||
| 1381 | 1, // `DW.LNS.fixed_advance_pc` | ||
| 1382 | 0, // `DW.LNS.set_prologue_end` | ||
| 1383 | 0, // `DW.LNS.set_epilogue_begin` | ||
| 1384 | 1, // `DW.LNS.set_isa` | ||
| 1385 | 0, // include_directories (none except the compilation unit cwd) | ||
| 1386 | }); | ||
| 1387 | // file_names[0] | ||
| 1388 | di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name | ||
| 1389 | di_buf.appendSliceAssumeCapacity(&[_]u8{ | ||
| 1390 | 0, // null byte for the relative path name | ||
| 1391 | 0, // directory_index | ||
| 1392 | 0, // mtime (TODO supply this) | ||
| 1393 | 0, // file size bytes (TODO supply this) | ||
| 1394 | 0, // file_names sentinel | ||
| 1395 | }); | ||
| 1396 | 1030 | ||
| 1397 | const header_len = di_buf.items.len - after_header_len; | 1031 | if (self.debug_line_header_dirty) { |
| 1398 | switch (self.ptr_width) { | 1032 | try self.dwarf.?.writeDbgLineHeader(&self.base, module); |
| 1399 | .p32 => { | ||
| 1400 | mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian); | ||
| 1401 | }, | ||
| 1402 | .p64 => { | ||
| 1403 | mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian); | ||
| 1404 | }, | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | // We use NOPs because consumers empirically do not respect the header length field. | ||
| 1408 | if (di_buf.items.len > dbg_line_prg_off) { | ||
| 1409 | // Move the first N files to the end to make more padding for the header. | ||
| 1410 | @panic("TODO: handle .debug_line header exceeding its padding"); | ||
| 1411 | } | ||
| 1412 | const jmp_amt = dbg_line_prg_off - di_buf.items.len; | ||
| 1413 | try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset); | ||
| 1414 | self.debug_line_header_dirty = false; | 1033 | self.debug_line_header_dirty = false; |
| 1415 | } | 1034 | } |
| 1416 | 1035 | ||
| ... | @@ -1481,11 +1100,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { | ... | @@ -1481,11 +1100,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1481 | self.shstrtab_dirty = false; | 1100 | self.shstrtab_dirty = false; |
| 1482 | } | 1101 | } |
| 1483 | } | 1102 | } |
| 1103 | |||
| 1484 | { | 1104 | { |
| 1485 | const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?]; | 1105 | const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?]; |
| 1486 | if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) { | 1106 | const dwarf = self.dwarf.?; |
| 1107 | if (self.debug_strtab_dirty or dwarf.strtab.items.len != debug_strtab_sect.sh_size) { | ||
| 1487 | const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset); | 1108 | const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset); |
| 1488 | const needed_size = self.debug_strtab.items.len; | 1109 | const needed_size = dwarf.strtab.items.len; |
| 1489 | 1110 | ||
| 1490 | if (needed_size > allocated_size) { | 1111 | if (needed_size > allocated_size) { |
| 1491 | debug_strtab_sect.sh_size = 0; // free the space | 1112 | debug_strtab_sect.sh_size = 0; // free the space |
| ... | @@ -1494,7 +1115,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { | ... | @@ -1494,7 +1115,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1494 | debug_strtab_sect.sh_size = needed_size; | 1115 | debug_strtab_sect.sh_size = needed_size; |
| 1495 | log.debug("debug_strtab start=0x{x} end=0x{x}", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size }); | 1116 | log.debug("debug_strtab start=0x{x} end=0x{x}", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size }); |
| 1496 | 1117 | ||
| 1497 | try self.base.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset); | 1118 | try self.base.file.?.pwriteAll(dwarf.strtab.items, debug_strtab_sect.sh_offset); |
| 1498 | if (!self.shdr_table_dirty) { | 1119 | if (!self.shdr_table_dirty) { |
| 1499 | // Then it won't get written with the others and we need to do it. | 1120 | // Then it won't get written with the others and we need to do it. |
| 1500 | try self.writeSectHeader(self.debug_str_section_index.?); | 1121 | try self.writeSectHeader(self.debug_str_section_index.?); |
| ... | @@ -1502,6 +1123,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { | ... | @@ -1502,6 +1123,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1502 | self.debug_strtab_dirty = false; | 1123 | self.debug_strtab_dirty = false; |
| 1503 | } | 1124 | } |
| 1504 | } | 1125 | } |
| 1126 | |||
| 1505 | if (self.shdr_table_dirty) { | 1127 | if (self.shdr_table_dirty) { |
| 1506 | const shsize: u64 = switch (self.ptr_width) { | 1128 | const shsize: u64 = switch (self.ptr_width) { |
| 1507 | .p32 => @sizeOf(elf.Elf32_Shdr), | 1129 | .p32 => @sizeOf(elf.Elf32_Shdr), |
| ... | @@ -2340,7 +1962,6 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { | ... | @@ -2340,7 +1962,6 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { |
| 2340 | i += 1; | 1962 | i += 1; |
| 2341 | } | 1963 | } |
| 2342 | } | 1964 | } |
| 2343 | // TODO process free list for dbg info just like we do above for vaddrs | ||
| 2344 | 1965 | ||
| 2345 | if (self.atoms.getPtr(phdr_index)) |last_block| { | 1966 | if (self.atoms.getPtr(phdr_index)) |last_block| { |
| 2346 | if (last_block.* == text_block) { | 1967 | if (last_block.* == text_block) { |
| ... | @@ -2353,14 +1974,6 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { | ... | @@ -2353,14 +1974,6 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { |
| 2353 | } | 1974 | } |
| 2354 | } | 1975 | } |
| 2355 | 1976 | ||
| 2356 | if (self.dbg_info_decl_first == text_block) { | ||
| 2357 | self.dbg_info_decl_first = text_block.dbg_info_next; | ||
| 2358 | } | ||
| 2359 | if (self.dbg_info_decl_last == text_block) { | ||
| 2360 | // TODO shrink the .debug_info section size here | ||
| 2361 | self.dbg_info_decl_last = text_block.dbg_info_prev; | ||
| 2362 | } | ||
| 2363 | |||
| 2364 | if (text_block.prev) |prev| { | 1977 | if (text_block.prev) |prev| { |
| 2365 | prev.next = text_block.next; | 1978 | prev.next = text_block.next; |
| 2366 | 1979 | ||
| ... | @@ -2379,18 +1992,8 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { | ... | @@ -2379,18 +1992,8 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { |
| 2379 | text_block.next = null; | 1992 | text_block.next = null; |
| 2380 | } | 1993 | } |
| 2381 | 1994 | ||
| 2382 | if (text_block.dbg_info_prev) |prev| { | 1995 | if (self.dwarf) |*dw| { |
| 2383 | prev.dbg_info_next = text_block.dbg_info_next; | 1996 | dw.freeAtom(&text_block.dbg_info_atom); |
| 2384 | |||
| 2385 | // TODO the free list logic like we do for text blocks above | ||
| 2386 | } else { | ||
| 2387 | text_block.dbg_info_prev = null; | ||
| 2388 | } | ||
| 2389 | |||
| 2390 | if (text_block.dbg_info_next) |next| { | ||
| 2391 | next.dbg_info_prev = text_block.dbg_info_prev; | ||
| 2392 | } else { | ||
| 2393 | text_block.dbg_info_next = null; | ||
| 2394 | } | 1997 | } |
| 2395 | } | 1998 | } |
| 2396 | 1999 | ||
| ... | @@ -2619,26 +2222,9 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { | ... | @@ -2619,26 +2222,9 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { |
| 2619 | 2222 | ||
| 2620 | self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {}; | 2223 | self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {}; |
| 2621 | } | 2224 | } |
| 2622 | // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing | 2225 | |
| 2623 | // is desired for both. | 2226 | if (self.dwarf) |*dw| { |
| 2624 | _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf); | 2227 | dw.freeDecl(decl); |
| 2625 | if (decl.fn_link.elf.prev) |prev| { | ||
| 2626 | self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {}; | ||
| 2627 | prev.next = decl.fn_link.elf.next; | ||
| 2628 | if (decl.fn_link.elf.next) |next| { | ||
| 2629 | next.prev = prev; | ||
| 2630 | } else { | ||
| 2631 | self.dbg_line_fn_last = prev; | ||
| 2632 | } | ||
| 2633 | } else if (decl.fn_link.elf.next) |next| { | ||
| 2634 | self.dbg_line_fn_first = next; | ||
| 2635 | next.prev = null; | ||
| 2636 | } | ||
| 2637 | if (self.dbg_line_fn_first == &decl.fn_link.elf) { | ||
| 2638 | self.dbg_line_fn_first = decl.fn_link.elf.next; | ||
| 2639 | } | ||
| 2640 | if (self.dbg_line_fn_last == &decl.fn_link.elf) { | ||
| 2641 | self.dbg_line_fn_last = decl.fn_link.elf.prev; | ||
| 2642 | } | 2228 | } |
| 2643 | } | 2229 | } |
| 2644 | 2230 | ||
| ... | @@ -2739,62 +2325,6 @@ fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8 | ... | @@ -2739,62 +2325,6 @@ fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8 |
| 2739 | return local_sym; | 2325 | return local_sym; |
| 2740 | } | 2326 | } |
| 2741 | 2327 | ||
| 2742 | fn finishUpdateDecl( | ||
| 2743 | self: *Elf, | ||
| 2744 | module: *Module, | ||
| 2745 | decl: *Module.Decl, | ||
| 2746 | dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable, | ||
| 2747 | dbg_info_buffer: *std.ArrayList(u8), | ||
| 2748 | ) !void { | ||
| 2749 | // We need this for the duration of this function only so that for composite | ||
| 2750 | // types such as []const u32, if the type *u32 is non-existent, we create | ||
| 2751 | // it synthetically and store the backing bytes in this arena. After we are | ||
| 2752 | // done with the relocations, we can safely deinit the entire memory slab. | ||
| 2753 | // TODO currently, we do not store the relocations for future use, however, | ||
| 2754 | // if that is the case, we should move memory management to a higher scope, | ||
| 2755 | // such as linker scope, or whatnot. | ||
| 2756 | var dbg_type_arena = std.heap.ArenaAllocator.init(self.base.allocator); | ||
| 2757 | defer dbg_type_arena.deinit(); | ||
| 2758 | |||
| 2759 | // Now we emit the .debug_info types of the Decl. These will count towards the size of | ||
| 2760 | // the buffer, so we have to do it before computing the offset, and we can't perform the actual | ||
| 2761 | // relocations yet. | ||
| 2762 | { | ||
| 2763 | var it: usize = 0; | ||
| 2764 | while (it < dbg_info_type_relocs.count()) : (it += 1) { | ||
| 2765 | const ty = dbg_info_type_relocs.keys()[it]; | ||
| 2766 | const value_ptr = dbg_info_type_relocs.getPtr(ty).?; | ||
| 2767 | value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); | ||
| 2768 | try self.addDbgInfoType(dbg_type_arena.allocator(), ty, dbg_info_buffer, dbg_info_type_relocs); | ||
| 2769 | } | ||
| 2770 | } | ||
| 2771 | |||
| 2772 | const text_block = &decl.link.elf; | ||
| 2773 | try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len)); | ||
| 2774 | |||
| 2775 | const target_endian = self.base.options.target.cpu.arch.endian(); | ||
| 2776 | |||
| 2777 | { | ||
| 2778 | // Now that we have the offset assigned we can finally perform type relocations. | ||
| 2779 | for (dbg_info_type_relocs.values()) |value| { | ||
| 2780 | for (value.relocs.items) |off| { | ||
| 2781 | mem.writeInt( | ||
| 2782 | u32, | ||
| 2783 | dbg_info_buffer.items[off..][0..4], | ||
| 2784 | text_block.dbg_info_off + value.off, | ||
| 2785 | target_endian, | ||
| 2786 | ); | ||
| 2787 | } | ||
| 2788 | } | ||
| 2789 | } | ||
| 2790 | |||
| 2791 | try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items); | ||
| 2792 | |||
| 2793 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | ||
| 2794 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | ||
| 2795 | return self.updateDeclExports(module, decl, decl_exports); | ||
| 2796 | } | ||
| 2797 | |||
| 2798 | pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { | 2328 | pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { |
| 2799 | if (build_options.skip_non_native and builtin.object_format != .elf) { | 2329 | if (build_options.skip_non_native and builtin.object_format != .elf) { |
| 2800 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 2330 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| ... | @@ -2809,96 +2339,33 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven | ... | @@ -2809,96 +2339,33 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven |
| 2809 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 2339 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 2810 | defer code_buffer.deinit(); | 2340 | defer code_buffer.deinit(); |
| 2811 | 2341 | ||
| 2812 | // For functions we need to add a prologue to the debug line program. | ||
| 2813 | var dbg_line_buffer = try std.ArrayList(u8).initCapacity(self.base.allocator, 26); | ||
| 2814 | defer dbg_line_buffer.deinit(); | ||
| 2815 | |||
| 2816 | var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator); | ||
| 2817 | defer dbg_info_buffer.deinit(); | ||
| 2818 | |||
| 2819 | var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{}; | ||
| 2820 | defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs); | ||
| 2821 | |||
| 2822 | const decl = func.owner_decl; | 2342 | const decl = func.owner_decl; |
| 2823 | self.freeUnnamedConsts(decl); | 2343 | self.freeUnnamedConsts(decl); |
| 2824 | 2344 | ||
| 2825 | const decl_name = try decl.getFullyQualifiedName(self.base.allocator); | 2345 | var debug_buffers_buf: Dwarf.DeclDebugBuffers = undefined; |
| 2826 | defer self.base.allocator.free(decl_name); | 2346 | const debug_buffers = if (self.dwarf) |*dw| blk: { |
| 2827 | 2347 | debug_buffers_buf = try dw.initDeclDebugInfo(decl); | |
| 2828 | log.debug("updateFunc {s}{*}", .{ decl_name, func.owner_decl }); | 2348 | break :blk &debug_buffers_buf; |
| 2829 | log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ | 2349 | } else null; |
| 2830 | decl.src_line, | 2350 | defer { |
| 2831 | func.lbrace_line, | 2351 | if (debug_buffers) |dbg| { |
| 2832 | func.rbrace_line, | 2352 | dbg.dbg_line_buffer.deinit(); |
| 2833 | }); | 2353 | dbg.dbg_info_buffer.deinit(); |
| 2834 | const line = @intCast(u28, decl.src_line + func.lbrace_line); | 2354 | deinitRelocs(self.base.allocator, &dbg.dbg_info_type_relocs); |
| 2835 | |||
| 2836 | const ptr_width_bytes = self.ptrWidthBytes(); | ||
| 2837 | dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{ | ||
| 2838 | DW.LNS.extended_op, | ||
| 2839 | ptr_width_bytes + 1, | ||
| 2840 | DW.LNE.set_address, | ||
| 2841 | }); | ||
| 2842 | // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`. | ||
| 2843 | assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len); | ||
| 2844 | dbg_line_buffer.items.len += ptr_width_bytes; | ||
| 2845 | |||
| 2846 | dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line); | ||
| 2847 | // This is the "relocatable" relative line offset from the previous function's end curly | ||
| 2848 | // to this function's begin curly. | ||
| 2849 | assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len); | ||
| 2850 | // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later. | ||
| 2851 | leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line); | ||
| 2852 | |||
| 2853 | dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file); | ||
| 2854 | assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len); | ||
| 2855 | // Once we support more than one source file, this will have the ability to be more | ||
| 2856 | // than one possible value. | ||
| 2857 | const file_index = 1; | ||
| 2858 | leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index); | ||
| 2859 | |||
| 2860 | // Emit a line for the begin curly with prologue_end=false. The codegen will | ||
| 2861 | // do the work of setting prologue_end=true and epilogue_begin=true. | ||
| 2862 | dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy); | ||
| 2863 | |||
| 2864 | // .debug_info subprogram | ||
| 2865 | const decl_name_with_null = decl_name[0 .. decl_name.len + 1]; | ||
| 2866 | try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len); | ||
| 2867 | |||
| 2868 | const fn_ret_type = decl.ty.fnReturnType(); | ||
| 2869 | const fn_ret_has_bits = fn_ret_type.hasRuntimeBits(); | ||
| 2870 | if (fn_ret_has_bits) { | ||
| 2871 | dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram); | ||
| 2872 | } else { | ||
| 2873 | dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid); | ||
| 2874 | } | ||
| 2875 | // These get overwritten after generating the machine code. These values are | ||
| 2876 | // "relocations" and have to be in this fixed place so that functions can be | ||
| 2877 | // moved in virtual address space. | ||
| 2878 | assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len); | ||
| 2879 | dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT.low_pc, DW.FORM.addr | ||
| 2880 | assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len); | ||
| 2881 | dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 | ||
| 2882 | if (fn_ret_has_bits) { | ||
| 2883 | const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type); | ||
| 2884 | if (!gop.found_existing) { | ||
| 2885 | gop.value_ptr.* = .{ | ||
| 2886 | .off = undefined, | ||
| 2887 | .relocs = .{}, | ||
| 2888 | }; | ||
| 2889 | } | 2355 | } |
| 2890 | try gop.value_ptr.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len)); | ||
| 2891 | dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 | ||
| 2892 | } | 2356 | } |
| 2893 | dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string | ||
| 2894 | 2357 | ||
| 2895 | const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ | 2358 | const res = if (debug_buffers) |dbg| |
| 2896 | .dwarf = .{ | 2359 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ |
| 2897 | .dbg_line = &dbg_line_buffer, | 2360 | .dwarf = .{ |
| 2898 | .dbg_info = &dbg_info_buffer, | 2361 | .dbg_line = &dbg.dbg_line_buffer, |
| 2899 | .dbg_info_type_relocs = &dbg_info_type_relocs, | 2362 | .dbg_info = &dbg.dbg_info_buffer, |
| 2900 | }, | 2363 | .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, |
| 2901 | }); | 2364 | }, |
| 2365 | }) | ||
| 2366 | else | ||
| 2367 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); | ||
| 2368 | |||
| 2902 | const code = switch (res) { | 2369 | const code = switch (res) { |
| 2903 | .appended => code_buffer.items, | 2370 | .appended => code_buffer.items, |
| 2904 | .fail => |em| { | 2371 | .fail => |em| { |
| ... | @@ -2907,128 +2374,14 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven | ... | @@ -2907,128 +2374,14 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven |
| 2907 | return; | 2374 | return; |
| 2908 | }, | 2375 | }, |
| 2909 | }; | 2376 | }; |
| 2910 | |||
| 2911 | const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC); | 2377 | const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC); |
| 2912 | 2378 | if (debug_buffers) |dbg| { | |
| 2913 | const target_endian = self.base.options.target.cpu.arch.endian(); | 2379 | try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg); |
| 2914 | |||
| 2915 | // Since the Decl is a function, we need to update the .debug_line program. | ||
| 2916 | // Perform the relocations based on vaddr. | ||
| 2917 | switch (self.ptr_width) { | ||
| 2918 | .p32 => { | ||
| 2919 | { | ||
| 2920 | const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4]; | ||
| 2921 | mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian); | ||
| 2922 | } | ||
| 2923 | { | ||
| 2924 | const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4]; | ||
| 2925 | mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian); | ||
| 2926 | } | ||
| 2927 | }, | ||
| 2928 | .p64 => { | ||
| 2929 | { | ||
| 2930 | const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8]; | ||
| 2931 | mem.writeInt(u64, ptr, local_sym.st_value, target_endian); | ||
| 2932 | } | ||
| 2933 | { | ||
| 2934 | const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8]; | ||
| 2935 | mem.writeInt(u64, ptr, local_sym.st_value, target_endian); | ||
| 2936 | } | ||
| 2937 | }, | ||
| 2938 | } | 2380 | } |
| 2939 | { | ||
| 2940 | const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4]; | ||
| 2941 | mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian); | ||
| 2942 | } | ||
| 2943 | |||
| 2944 | try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence }); | ||
| 2945 | |||
| 2946 | // Now we have the full contents and may allocate a region to store it. | ||
| 2947 | |||
| 2948 | // This logic is nearly identical to the logic below in `updateDeclDebugInfoAllocation` for | ||
| 2949 | // `TextBlock` and the .debug_info. If you are editing this logic, you | ||
| 2950 | // probably need to edit that logic too. | ||
| 2951 | |||
| 2952 | const debug_line_sect = &self.sections.items[self.debug_line_section_index.?]; | ||
| 2953 | const src_fn = &decl.fn_link.elf; | ||
| 2954 | src_fn.len = @intCast(u32, dbg_line_buffer.items.len); | ||
| 2955 | if (self.dbg_line_fn_last) |last| not_first: { | ||
| 2956 | if (src_fn.next) |next| { | ||
| 2957 | // Update existing function - non-last item. | ||
| 2958 | if (src_fn.off + src_fn.len + min_nop_size > next.off) { | ||
| 2959 | // It grew too big, so we move it to a new location. | ||
| 2960 | if (src_fn.prev) |prev| { | ||
| 2961 | self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {}; | ||
| 2962 | prev.next = src_fn.next; | ||
| 2963 | } | ||
| 2964 | assert(src_fn.prev != next); | ||
| 2965 | next.prev = src_fn.prev; | ||
| 2966 | src_fn.next = null; | ||
| 2967 | // Populate where it used to be with NOPs. | ||
| 2968 | const file_pos = debug_line_sect.sh_offset + src_fn.off; | ||
| 2969 | try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos); | ||
| 2970 | // TODO Look at the free list before appending at the end. | ||
| 2971 | src_fn.prev = last; | ||
| 2972 | last.next = src_fn; | ||
| 2973 | self.dbg_line_fn_last = src_fn; | ||
| 2974 | |||
| 2975 | src_fn.off = last.off + padToIdeal(last.len); | ||
| 2976 | } | ||
| 2977 | } else if (src_fn.prev == null) { | ||
| 2978 | if (src_fn == last) { | ||
| 2979 | // Special case: there is only 1 function and it is being updated. | ||
| 2980 | // In this case there is nothing to do. The function's length has | ||
| 2981 | // already been updated, and the logic below takes care of | ||
| 2982 | // resizing the .debug_line section. | ||
| 2983 | break :not_first; | ||
| 2984 | } | ||
| 2985 | // Append new function. | ||
| 2986 | // TODO Look at the free list before appending at the end. | ||
| 2987 | src_fn.prev = last; | ||
| 2988 | last.next = src_fn; | ||
| 2989 | self.dbg_line_fn_last = src_fn; | ||
| 2990 | 2381 | ||
| 2991 | src_fn.off = last.off + padToIdeal(last.len); | 2382 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
| 2992 | } | 2383 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; |
| 2993 | } else { | 2384 | return self.updateDeclExports(module, decl, decl_exports); |
| 2994 | // This is the first function of the Line Number Program. | ||
| 2995 | self.dbg_line_fn_first = src_fn; | ||
| 2996 | self.dbg_line_fn_last = src_fn; | ||
| 2997 | |||
| 2998 | src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes()); | ||
| 2999 | } | ||
| 3000 | |||
| 3001 | const last_src_fn = self.dbg_line_fn_last.?; | ||
| 3002 | const needed_size = last_src_fn.off + last_src_fn.len; | ||
| 3003 | if (needed_size != debug_line_sect.sh_size) { | ||
| 3004 | if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) { | ||
| 3005 | const new_offset = self.findFreeSpace(needed_size, 1); | ||
| 3006 | const existing_size = last_src_fn.off; | ||
| 3007 | log.debug("moving .debug_line section: {d} bytes from 0x{x} to 0x{x}", .{ | ||
| 3008 | existing_size, | ||
| 3009 | debug_line_sect.sh_offset, | ||
| 3010 | new_offset, | ||
| 3011 | }); | ||
| 3012 | const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size); | ||
| 3013 | if (amt != existing_size) return error.InputOutput; | ||
| 3014 | debug_line_sect.sh_offset = new_offset; | ||
| 3015 | } | ||
| 3016 | debug_line_sect.sh_size = needed_size; | ||
| 3017 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty | ||
| 3018 | self.debug_line_header_dirty = true; | ||
| 3019 | } | ||
| 3020 | const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0; | ||
| 3021 | const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0; | ||
| 3022 | |||
| 3023 | // We only have support for one compilation unit so far, so the offsets are directly | ||
| 3024 | // from the .debug_line section. | ||
| 3025 | const file_pos = debug_line_sect.sh_offset + src_fn.off; | ||
| 3026 | try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos); | ||
| 3027 | |||
| 3028 | // .debug_info - End the TAG.subprogram children. | ||
| 3029 | try dbg_info_buffer.append(0); | ||
| 3030 | |||
| 3031 | return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer); | ||
| 3032 | } | 2385 | } |
| 3033 | 2386 | ||
| 3034 | pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { | 2387 | pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { |
| ... | @@ -3057,29 +2410,42 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { | ... | @@ -3057,29 +2410,42 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { |
| 3057 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 2410 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 3058 | defer code_buffer.deinit(); | 2411 | defer code_buffer.deinit(); |
| 3059 | 2412 | ||
| 3060 | var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator); | 2413 | var debug_buffers_buf: Dwarf.DeclDebugBuffers = undefined; |
| 3061 | defer dbg_line_buffer.deinit(); | 2414 | const debug_buffers = if (self.dwarf) |*dw| blk: { |
| 3062 | 2415 | debug_buffers_buf = try dw.initDeclDebugInfo(decl); | |
| 3063 | var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator); | 2416 | break :blk &debug_buffers_buf; |
| 3064 | defer dbg_info_buffer.deinit(); | 2417 | } else null; |
| 3065 | 2418 | defer { | |
| 3066 | var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{}; | 2419 | if (debug_buffers) |dbg| { |
| 3067 | defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs); | 2420 | dbg.dbg_line_buffer.deinit(); |
| 2421 | dbg.dbg_info_buffer.deinit(); | ||
| 2422 | deinitRelocs(self.base.allocator, &dbg.dbg_info_type_relocs); | ||
| 2423 | } | ||
| 2424 | } | ||
| 3068 | 2425 | ||
| 3069 | // TODO implement .debug_info for global variables | 2426 | // TODO implement .debug_info for global variables |
| 3070 | const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; | 2427 | const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; |
| 3071 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ | 2428 | const res = if (debug_buffers) |dbg| |
| 3072 | .ty = decl.ty, | 2429 | try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ |
| 3073 | .val = decl_val, | 2430 | .ty = decl.ty, |
| 3074 | }, &code_buffer, .{ | 2431 | .val = decl_val, |
| 3075 | .dwarf = .{ | 2432 | }, &code_buffer, .{ |
| 3076 | .dbg_line = &dbg_line_buffer, | 2433 | .dwarf = .{ |
| 3077 | .dbg_info = &dbg_info_buffer, | 2434 | .dbg_line = &dbg.dbg_line_buffer, |
| 3078 | .dbg_info_type_relocs = &dbg_info_type_relocs, | 2435 | .dbg_info = &dbg.dbg_info_buffer, |
| 3079 | }, | 2436 | .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, |
| 3080 | }, .{ | 2437 | }, |
| 3081 | .parent_atom_index = decl.link.elf.local_sym_index, | 2438 | }, .{ |
| 3082 | }); | 2439 | .parent_atom_index = decl.link.elf.local_sym_index, |
| 2440 | }) | ||
| 2441 | else | ||
| 2442 | try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ | ||
| 2443 | .ty = decl.ty, | ||
| 2444 | .val = decl_val, | ||
| 2445 | }, &code_buffer, .none, .{ | ||
| 2446 | .parent_atom_index = decl.link.elf.local_sym_index, | ||
| 2447 | }); | ||
| 2448 | |||
| 3083 | const code = switch (res) { | 2449 | const code = switch (res) { |
| 3084 | .externally_managed => |x| x, | 2450 | .externally_managed => |x| x, |
| 3085 | .appended => code_buffer.items, | 2451 | .appended => code_buffer.items, |
| ... | @@ -3090,8 +2456,14 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { | ... | @@ -3090,8 +2456,14 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { |
| 3090 | }, | 2456 | }, |
| 3091 | }; | 2457 | }; |
| 3092 | 2458 | ||
| 3093 | _ = try self.updateDeclCode(decl, code, elf.STT_OBJECT); | 2459 | const local_sym = try self.updateDeclCode(decl, code, elf.STT_OBJECT); |
| 3094 | return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer); | 2460 | if (debug_buffers) |dbg| { |
| 2461 | try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg); | ||
| 2462 | } | ||
| 2463 | |||
| 2464 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | ||
| 2465 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | ||
| 2466 | return self.updateDeclExports(module, decl, decl_exports); | ||
| 3095 | } | 2467 | } |
| 3096 | 2468 | ||
| 3097 | pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl) !u32 { | 2469 | pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl) !u32 { |
| ... | @@ -3170,304 +2542,6 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl | ... | @@ -3170,304 +2542,6 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl |
| 3170 | return atom.local_sym_index; | 2542 | return atom.local_sym_index; |
| 3171 | } | 2543 | } |
| 3172 | 2544 | ||
| 3173 | /// Asserts the type has codegen bits. | ||
| 3174 | fn addDbgInfoType( | ||
| 3175 | self: *Elf, | ||
| 3176 | arena: Allocator, | ||
| 3177 | ty: Type, | ||
| 3178 | dbg_info_buffer: *std.ArrayList(u8), | ||
| 3179 | dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable, | ||
| 3180 | ) error{OutOfMemory}!void { | ||
| 3181 | const target = self.base.options.target; | ||
| 3182 | var relocs = std.ArrayList(struct { ty: Type, reloc: u32 }).init(arena); | ||
| 3183 | |||
| 3184 | switch (ty.zigTypeTag()) { | ||
| 3185 | .NoReturn => unreachable, | ||
| 3186 | .Void => { | ||
| 3187 | try dbg_info_buffer.append(abbrev_pad1); | ||
| 3188 | }, | ||
| 3189 | .Bool => { | ||
| 3190 | try dbg_info_buffer.appendSlice(&[_]u8{ | ||
| 3191 | abbrev_base_type, | ||
| 3192 | DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 | ||
| 3193 | 1, // DW.AT.byte_size, DW.FORM.data1 | ||
| 3194 | 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string | ||
| 3195 | }); | ||
| 3196 | }, | ||
| 3197 | .Int => { | ||
| 3198 | const info = ty.intInfo(target); | ||
| 3199 | try dbg_info_buffer.ensureUnusedCapacity(12); | ||
| 3200 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); | ||
| 3201 | // DW.AT.encoding, DW.FORM.data1 | ||
| 3202 | dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { | ||
| 3203 | .signed => DW.ATE.signed, | ||
| 3204 | .unsigned => DW.ATE.unsigned, | ||
| 3205 | }); | ||
| 3206 | // DW.AT.byte_size, DW.FORM.data1 | ||
| 3207 | dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); | ||
| 3208 | // DW.AT.name, DW.FORM.string | ||
| 3209 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 3210 | }, | ||
| 3211 | .Optional => { | ||
| 3212 | if (ty.isPtrLikeOptional()) { | ||
| 3213 | try dbg_info_buffer.ensureUnusedCapacity(12); | ||
| 3214 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); | ||
| 3215 | // DW.AT.encoding, DW.FORM.data1 | ||
| 3216 | dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); | ||
| 3217 | // DW.AT.byte_size, DW.FORM.data1 | ||
| 3218 | dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); | ||
| 3219 | // DW.AT.name, DW.FORM.string | ||
| 3220 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 3221 | } else { | ||
| 3222 | // Non-pointer optionals are structs: struct { .maybe = *, .val = * } | ||
| 3223 | var buf = try arena.create(Type.Payload.ElemType); | ||
| 3224 | const payload_ty = ty.optionalChild(buf); | ||
| 3225 | // DW.AT.structure_type | ||
| 3226 | try dbg_info_buffer.append(abbrev_struct_type); | ||
| 3227 | // DW.AT.byte_size, DW.FORM.sdata | ||
| 3228 | const abi_size = ty.abiSize(target); | ||
| 3229 | try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); | ||
| 3230 | // DW.AT.name, DW.FORM.string | ||
| 3231 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 3232 | // DW.AT.member | ||
| 3233 | try dbg_info_buffer.ensureUnusedCapacity(7); | ||
| 3234 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 3235 | // DW.AT.name, DW.FORM.string | ||
| 3236 | dbg_info_buffer.appendSliceAssumeCapacity("maybe"); | ||
| 3237 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 3238 | // DW.AT.type, DW.FORM.ref4 | ||
| 3239 | var index = dbg_info_buffer.items.len; | ||
| 3240 | try dbg_info_buffer.resize(index + 4); | ||
| 3241 | try relocs.append(.{ .ty = Type.bool, .reloc = @intCast(u32, index) }); | ||
| 3242 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 3243 | try dbg_info_buffer.ensureUnusedCapacity(6); | ||
| 3244 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 3245 | // DW.AT.member | ||
| 3246 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 3247 | // DW.AT.name, DW.FORM.string | ||
| 3248 | dbg_info_buffer.appendSliceAssumeCapacity("val"); | ||
| 3249 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 3250 | // DW.AT.type, DW.FORM.ref4 | ||
| 3251 | index = dbg_info_buffer.items.len; | ||
| 3252 | try dbg_info_buffer.resize(index + 4); | ||
| 3253 | try relocs.append(.{ .ty = payload_ty, .reloc = @intCast(u32, index) }); | ||
| 3254 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 3255 | const offset = abi_size - payload_ty.abiSize(target); | ||
| 3256 | try leb128.writeULEB128(dbg_info_buffer.writer(), offset); | ||
| 3257 | // DW.AT.structure_type delimit children | ||
| 3258 | try dbg_info_buffer.append(0); | ||
| 3259 | } | ||
| 3260 | }, | ||
| 3261 | .Pointer => { | ||
| 3262 | if (ty.isSlice()) { | ||
| 3263 | // Slices are structs: struct { .ptr = *, .len = N } | ||
| 3264 | // DW.AT.structure_type | ||
| 3265 | try dbg_info_buffer.ensureUnusedCapacity(2); | ||
| 3266 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_type); | ||
| 3267 | // DW.AT.byte_size, DW.FORM.sdata | ||
| 3268 | dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2); | ||
| 3269 | // DW.AT.name, DW.FORM.string | ||
| 3270 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 3271 | // DW.AT.member | ||
| 3272 | try dbg_info_buffer.ensureUnusedCapacity(5); | ||
| 3273 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 3274 | // DW.AT.name, DW.FORM.string | ||
| 3275 | dbg_info_buffer.appendSliceAssumeCapacity("ptr"); | ||
| 3276 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 3277 | // DW.AT.type, DW.FORM.ref4 | ||
| 3278 | var index = dbg_info_buffer.items.len; | ||
| 3279 | try dbg_info_buffer.resize(index + 4); | ||
| 3280 | var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); | ||
| 3281 | const ptr_ty = ty.slicePtrFieldType(buf); | ||
| 3282 | try relocs.append(.{ .ty = ptr_ty, .reloc = @intCast(u32, index) }); | ||
| 3283 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 3284 | try dbg_info_buffer.ensureUnusedCapacity(6); | ||
| 3285 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 3286 | // DW.AT.member | ||
| 3287 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 3288 | // DW.AT.name, DW.FORM.string | ||
| 3289 | dbg_info_buffer.appendSliceAssumeCapacity("len"); | ||
| 3290 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 3291 | // DW.AT.type, DW.FORM.ref4 | ||
| 3292 | index = dbg_info_buffer.items.len; | ||
| 3293 | try dbg_info_buffer.resize(index + 4); | ||
| 3294 | try relocs.append(.{ .ty = Type.initTag(.usize), .reloc = @intCast(u32, index) }); | ||
| 3295 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 3296 | try dbg_info_buffer.ensureUnusedCapacity(2); | ||
| 3297 | dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize)); | ||
| 3298 | // DW.AT.structure_type delimit children | ||
| 3299 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 3300 | } else { | ||
| 3301 | try dbg_info_buffer.ensureUnusedCapacity(5); | ||
| 3302 | dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type); | ||
| 3303 | // DW.AT.type, DW.FORM.ref4 | ||
| 3304 | const index = dbg_info_buffer.items.len; | ||
| 3305 | try dbg_info_buffer.resize(index + 4); | ||
| 3306 | try relocs.append(.{ .ty = ty.childType(), .reloc = @intCast(u32, index) }); | ||
| 3307 | } | ||
| 3308 | }, | ||
| 3309 | .Struct => blk: { | ||
| 3310 | // try dbg_info_buffer.ensureUnusedCapacity(23); | ||
| 3311 | // DW.AT.structure_type | ||
| 3312 | try dbg_info_buffer.append(abbrev_struct_type); | ||
| 3313 | // DW.AT.byte_size, DW.FORM.sdata | ||
| 3314 | const abi_size = ty.abiSize(target); | ||
| 3315 | try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); | ||
| 3316 | // DW.AT.name, DW.FORM.string | ||
| 3317 | const struct_name = try ty.nameAlloc(arena); | ||
| 3318 | try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); | ||
| 3319 | dbg_info_buffer.appendSliceAssumeCapacity(struct_name); | ||
| 3320 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 3321 | |||
| 3322 | const struct_obj = ty.castTag(.@"struct").?.data; | ||
| 3323 | if (struct_obj.layout == .Packed) { | ||
| 3324 | log.debug("TODO implement .debug_info for packed structs", .{}); | ||
| 3325 | break :blk; | ||
| 3326 | } | ||
| 3327 | |||
| 3328 | const fields = ty.structFields(); | ||
| 3329 | for (fields.keys()) |field_name, field_index| { | ||
| 3330 | const field = fields.get(field_name).?; | ||
| 3331 | // DW.AT.member | ||
| 3332 | try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); | ||
| 3333 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 3334 | // DW.AT.name, DW.FORM.string | ||
| 3335 | dbg_info_buffer.appendSliceAssumeCapacity(field_name); | ||
| 3336 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 3337 | // DW.AT.type, DW.FORM.ref4 | ||
| 3338 | var index = dbg_info_buffer.items.len; | ||
| 3339 | try dbg_info_buffer.resize(index + 4); | ||
| 3340 | try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); | ||
| 3341 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 3342 | const field_off = ty.structFieldOffset(field_index, target); | ||
| 3343 | try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); | ||
| 3344 | } | ||
| 3345 | |||
| 3346 | // DW.AT.structure_type delimit children | ||
| 3347 | try dbg_info_buffer.append(0); | ||
| 3348 | }, | ||
| 3349 | else => { | ||
| 3350 | log.debug("TODO implement .debug_info for type '{}'", .{ty}); | ||
| 3351 | try dbg_info_buffer.append(abbrev_pad1); | ||
| 3352 | }, | ||
| 3353 | } | ||
| 3354 | |||
| 3355 | for (relocs.items) |rel| { | ||
| 3356 | const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, rel.ty); | ||
| 3357 | if (!gop.found_existing) { | ||
| 3358 | gop.value_ptr.* = .{ | ||
| 3359 | .off = undefined, | ||
| 3360 | .relocs = .{}, | ||
| 3361 | }; | ||
| 3362 | } | ||
| 3363 | try gop.value_ptr.relocs.append(self.base.allocator, rel.reloc); | ||
| 3364 | } | ||
| 3365 | } | ||
| 3366 | |||
| 3367 | fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !void { | ||
| 3368 | const tracy = trace(@src()); | ||
| 3369 | defer tracy.end(); | ||
| 3370 | |||
| 3371 | // This logic is nearly identical to the logic above in `updateDecl` for | ||
| 3372 | // `SrcFn` and the line number programs. If you are editing this logic, you | ||
| 3373 | // probably need to edit that logic too. | ||
| 3374 | |||
| 3375 | const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; | ||
| 3376 | text_block.dbg_info_len = len; | ||
| 3377 | if (self.dbg_info_decl_last) |last| not_first: { | ||
| 3378 | if (text_block.dbg_info_next) |next| { | ||
| 3379 | // Update existing Decl - non-last item. | ||
| 3380 | if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) { | ||
| 3381 | // It grew too big, so we move it to a new location. | ||
| 3382 | if (text_block.dbg_info_prev) |prev| { | ||
| 3383 | self.dbg_info_decl_free_list.put(self.base.allocator, prev, {}) catch {}; | ||
| 3384 | prev.dbg_info_next = text_block.dbg_info_next; | ||
| 3385 | } | ||
| 3386 | next.dbg_info_prev = text_block.dbg_info_prev; | ||
| 3387 | text_block.dbg_info_next = null; | ||
| 3388 | // Populate where it used to be with NOPs. | ||
| 3389 | const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off; | ||
| 3390 | try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos); | ||
| 3391 | // TODO Look at the free list before appending at the end. | ||
| 3392 | text_block.dbg_info_prev = last; | ||
| 3393 | last.dbg_info_next = text_block; | ||
| 3394 | self.dbg_info_decl_last = text_block; | ||
| 3395 | |||
| 3396 | text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len); | ||
| 3397 | } | ||
| 3398 | } else if (text_block.dbg_info_prev == null) { | ||
| 3399 | if (text_block == last) { | ||
| 3400 | // Special case: there is only 1 .debug_info block and it is being updated. | ||
| 3401 | // In this case there is nothing to do. The block's length has | ||
| 3402 | // already been updated, and logic in writeDeclDebugInfo takes care of | ||
| 3403 | // resizing the .debug_info section. | ||
| 3404 | break :not_first; | ||
| 3405 | } | ||
| 3406 | // Append new Decl. | ||
| 3407 | // TODO Look at the free list before appending at the end. | ||
| 3408 | text_block.dbg_info_prev = last; | ||
| 3409 | last.dbg_info_next = text_block; | ||
| 3410 | self.dbg_info_decl_last = text_block; | ||
| 3411 | |||
| 3412 | text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len); | ||
| 3413 | } | ||
| 3414 | } else { | ||
| 3415 | // This is the first Decl of the .debug_info | ||
| 3416 | self.dbg_info_decl_first = text_block; | ||
| 3417 | self.dbg_info_decl_last = text_block; | ||
| 3418 | |||
| 3419 | text_block.dbg_info_off = padToIdeal(self.dbgInfoNeededHeaderBytes()); | ||
| 3420 | } | ||
| 3421 | } | ||
| 3422 | |||
| 3423 | fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const u8) !void { | ||
| 3424 | const tracy = trace(@src()); | ||
| 3425 | defer tracy.end(); | ||
| 3426 | |||
| 3427 | // This logic is nearly identical to the logic above in `updateDecl` for | ||
| 3428 | // `SrcFn` and the line number programs. If you are editing this logic, you | ||
| 3429 | // probably need to edit that logic too. | ||
| 3430 | |||
| 3431 | const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; | ||
| 3432 | |||
| 3433 | const last_decl = self.dbg_info_decl_last.?; | ||
| 3434 | // +1 for a trailing zero to end the children of the decl tag. | ||
| 3435 | const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1; | ||
| 3436 | if (needed_size != debug_info_sect.sh_size) { | ||
| 3437 | if (needed_size > self.allocatedSize(debug_info_sect.sh_offset)) { | ||
| 3438 | const new_offset = self.findFreeSpace(needed_size, 1); | ||
| 3439 | const existing_size = last_decl.dbg_info_off; | ||
| 3440 | log.debug("moving .debug_info section: {} bytes from 0x{x} to 0x{x}", .{ | ||
| 3441 | existing_size, | ||
| 3442 | debug_info_sect.sh_offset, | ||
| 3443 | new_offset, | ||
| 3444 | }); | ||
| 3445 | const amt = try self.base.file.?.copyRangeAll(debug_info_sect.sh_offset, self.base.file.?, new_offset, existing_size); | ||
| 3446 | if (amt != existing_size) return error.InputOutput; | ||
| 3447 | debug_info_sect.sh_offset = new_offset; | ||
| 3448 | } | ||
| 3449 | debug_info_sect.sh_size = needed_size; | ||
| 3450 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty | ||
| 3451 | self.debug_info_header_dirty = true; | ||
| 3452 | } | ||
| 3453 | const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev| | ||
| 3454 | text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len) | ||
| 3455 | else | ||
| 3456 | 0; | ||
| 3457 | const next_padding_size: u32 = if (text_block.dbg_info_next) |next| | ||
| 3458 | next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len) | ||
| 3459 | else | ||
| 3460 | 0; | ||
| 3461 | |||
| 3462 | // To end the children of the decl tag. | ||
| 3463 | const trailing_zero = text_block.dbg_info_next == null; | ||
| 3464 | |||
| 3465 | // We only have support for one compilation unit so far, so the offsets are directly | ||
| 3466 | // from the .debug_info section. | ||
| 3467 | const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off; | ||
| 3468 | try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos); | ||
| 3469 | } | ||
| 3470 | |||
| 3471 | pub fn updateDeclExports( | 2545 | pub fn updateDeclExports( |
| 3472 | self: *Elf, | 2546 | self: *Elf, |
| 3473 | module: *Module, | 2547 | module: *Module, |
| ... | @@ -3568,20 +2642,9 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec | ... | @@ -3568,20 +2642,9 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec |
| 3568 | log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); | 2642 | log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); |
| 3569 | 2643 | ||
| 3570 | if (self.llvm_object) |_| return; | 2644 | if (self.llvm_object) |_| return; |
| 3571 | 2645 | if (self.dwarf) |*dw| { | |
| 3572 | const func = decl.val.castTag(.function).?.data; | 2646 | try dw.updateDeclLineNumber(&self.base, decl); |
| 3573 | log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ | 2647 | } |
| 3574 | decl.src_line, | ||
| 3575 | func.lbrace_line, | ||
| 3576 | func.rbrace_line, | ||
| 3577 | }); | ||
| 3578 | const line = @intCast(u28, decl.src_line + func.lbrace_line); | ||
| 3579 | |||
| 3580 | const shdr = &self.sections.items[self.debug_line_section_index.?]; | ||
| 3581 | const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff(); | ||
| 3582 | var data: [4]u8 = undefined; | ||
| 3583 | leb128.writeUnsignedFixed(4, &data, line); | ||
| 3584 | try self.base.file.?.pwriteAll(&data, file_pos); | ||
| 3585 | } | 2648 | } |
| 3586 | 2649 | ||
| 3587 | pub fn deleteExport(self: *Elf, exp: Export) void { | 2650 | pub fn deleteExport(self: *Elf, exp: Export) void { |
| ... | @@ -3806,201 +2869,6 @@ fn archPtrWidthBytes(self: Elf) u8 { | ... | @@ -3806,201 +2869,6 @@ fn archPtrWidthBytes(self: Elf) u8 { |
| 3806 | return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8); | 2869 | return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8); |
| 3807 | } | 2870 | } |
| 3808 | 2871 | ||
| 3809 | /// The reloc offset for the virtual address of a function in its Line Number Program. | ||
| 3810 | /// Size is a virtual address integer. | ||
| 3811 | const dbg_line_vaddr_reloc_index = 3; | ||
| 3812 | /// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram. | ||
| 3813 | /// Size is a virtual address integer. | ||
| 3814 | const dbg_info_low_pc_reloc_index = 1; | ||
| 3815 | |||
| 3816 | /// The reloc offset for the line offset of a function from the previous function's line. | ||
| 3817 | /// It's a fixed-size 4-byte ULEB128. | ||
| 3818 | fn getRelocDbgLineOff(self: Elf) usize { | ||
| 3819 | return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1; | ||
| 3820 | } | ||
| 3821 | |||
| 3822 | fn getRelocDbgFileIndex(self: Elf) usize { | ||
| 3823 | return self.getRelocDbgLineOff() + 5; | ||
| 3824 | } | ||
| 3825 | |||
| 3826 | fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 { | ||
| 3827 | return dbg_info_low_pc_reloc_index + self.ptrWidthBytes(); | ||
| 3828 | } | ||
| 3829 | |||
| 3830 | fn dbgLineNeededHeaderBytes(self: Elf) u32 { | ||
| 3831 | const directory_entry_format_count = 1; | ||
| 3832 | const file_name_entry_format_count = 1; | ||
| 3833 | const directory_count = 1; | ||
| 3834 | const file_name_count = 1; | ||
| 3835 | const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "." | ||
| 3836 | return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + | ||
| 3837 | directory_count * 8 + file_name_count * 8 + | ||
| 3838 | // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like | ||
| 3839 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. | ||
| 3840 | root_src_dir_path_len + | ||
| 3841 | self.base.options.module.?.root_pkg.root_src_path.len); | ||
| 3842 | } | ||
| 3843 | |||
| 3844 | fn dbgInfoNeededHeaderBytes(self: Elf) u32 { | ||
| 3845 | _ = self; | ||
| 3846 | return 120; | ||
| 3847 | } | ||
| 3848 | |||
| 3849 | const min_nop_size = 2; | ||
| 3850 | |||
| 3851 | /// Writes to the file a buffer, prefixed and suffixed by the specified number of | ||
| 3852 | /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes | ||
| 3853 | /// are less than 1044480 bytes (if this limit is ever reached, this function can be | ||
| 3854 | /// improved to make more than one pwritev call, or the limit can be raised by a fixed | ||
| 3855 | /// amount by increasing the length of `vecs`). | ||
| 3856 | fn pwriteDbgLineNops( | ||
| 3857 | self: *Elf, | ||
| 3858 | prev_padding_size: usize, | ||
| 3859 | buf: []const u8, | ||
| 3860 | next_padding_size: usize, | ||
| 3861 | offset: u64, | ||
| 3862 | ) !void { | ||
| 3863 | const tracy = trace(@src()); | ||
| 3864 | defer tracy.end(); | ||
| 3865 | |||
| 3866 | const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096; | ||
| 3867 | const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 }; | ||
| 3868 | var vecs: [512]std.os.iovec_const = undefined; | ||
| 3869 | var vec_index: usize = 0; | ||
| 3870 | { | ||
| 3871 | var padding_left = prev_padding_size; | ||
| 3872 | if (padding_left % 2 != 0) { | ||
| 3873 | vecs[vec_index] = .{ | ||
| 3874 | .iov_base = &three_byte_nop, | ||
| 3875 | .iov_len = three_byte_nop.len, | ||
| 3876 | }; | ||
| 3877 | vec_index += 1; | ||
| 3878 | padding_left -= three_byte_nop.len; | ||
| 3879 | } | ||
| 3880 | while (padding_left > page_of_nops.len) { | ||
| 3881 | vecs[vec_index] = .{ | ||
| 3882 | .iov_base = &page_of_nops, | ||
| 3883 | .iov_len = page_of_nops.len, | ||
| 3884 | }; | ||
| 3885 | vec_index += 1; | ||
| 3886 | padding_left -= page_of_nops.len; | ||
| 3887 | } | ||
| 3888 | if (padding_left > 0) { | ||
| 3889 | vecs[vec_index] = .{ | ||
| 3890 | .iov_base = &page_of_nops, | ||
| 3891 | .iov_len = padding_left, | ||
| 3892 | }; | ||
| 3893 | vec_index += 1; | ||
| 3894 | } | ||
| 3895 | } | ||
| 3896 | |||
| 3897 | vecs[vec_index] = .{ | ||
| 3898 | .iov_base = buf.ptr, | ||
| 3899 | .iov_len = buf.len, | ||
| 3900 | }; | ||
| 3901 | vec_index += 1; | ||
| 3902 | |||
| 3903 | { | ||
| 3904 | var padding_left = next_padding_size; | ||
| 3905 | if (padding_left % 2 != 0) { | ||
| 3906 | vecs[vec_index] = .{ | ||
| 3907 | .iov_base = &three_byte_nop, | ||
| 3908 | .iov_len = three_byte_nop.len, | ||
| 3909 | }; | ||
| 3910 | vec_index += 1; | ||
| 3911 | padding_left -= three_byte_nop.len; | ||
| 3912 | } | ||
| 3913 | while (padding_left > page_of_nops.len) { | ||
| 3914 | vecs[vec_index] = .{ | ||
| 3915 | .iov_base = &page_of_nops, | ||
| 3916 | .iov_len = page_of_nops.len, | ||
| 3917 | }; | ||
| 3918 | vec_index += 1; | ||
| 3919 | padding_left -= page_of_nops.len; | ||
| 3920 | } | ||
| 3921 | if (padding_left > 0) { | ||
| 3922 | vecs[vec_index] = .{ | ||
| 3923 | .iov_base = &page_of_nops, | ||
| 3924 | .iov_len = padding_left, | ||
| 3925 | }; | ||
| 3926 | vec_index += 1; | ||
| 3927 | } | ||
| 3928 | } | ||
| 3929 | try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); | ||
| 3930 | } | ||
| 3931 | |||
| 3932 | /// Writes to the file a buffer, prefixed and suffixed by the specified number of | ||
| 3933 | /// bytes of padding. | ||
| 3934 | fn pwriteDbgInfoNops( | ||
| 3935 | self: *Elf, | ||
| 3936 | prev_padding_size: usize, | ||
| 3937 | buf: []const u8, | ||
| 3938 | next_padding_size: usize, | ||
| 3939 | trailing_zero: bool, | ||
| 3940 | offset: u64, | ||
| 3941 | ) !void { | ||
| 3942 | const tracy = trace(@src()); | ||
| 3943 | defer tracy.end(); | ||
| 3944 | |||
| 3945 | const page_of_nops = [1]u8{abbrev_pad1} ** 4096; | ||
| 3946 | var vecs: [32]std.os.iovec_const = undefined; | ||
| 3947 | var vec_index: usize = 0; | ||
| 3948 | { | ||
| 3949 | var padding_left = prev_padding_size; | ||
| 3950 | while (padding_left > page_of_nops.len) { | ||
| 3951 | vecs[vec_index] = .{ | ||
| 3952 | .iov_base = &page_of_nops, | ||
| 3953 | .iov_len = page_of_nops.len, | ||
| 3954 | }; | ||
| 3955 | vec_index += 1; | ||
| 3956 | padding_left -= page_of_nops.len; | ||
| 3957 | } | ||
| 3958 | if (padding_left > 0) { | ||
| 3959 | vecs[vec_index] = .{ | ||
| 3960 | .iov_base = &page_of_nops, | ||
| 3961 | .iov_len = padding_left, | ||
| 3962 | }; | ||
| 3963 | vec_index += 1; | ||
| 3964 | } | ||
| 3965 | } | ||
| 3966 | |||
| 3967 | vecs[vec_index] = .{ | ||
| 3968 | .iov_base = buf.ptr, | ||
| 3969 | .iov_len = buf.len, | ||
| 3970 | }; | ||
| 3971 | vec_index += 1; | ||
| 3972 | |||
| 3973 | { | ||
| 3974 | var padding_left = next_padding_size; | ||
| 3975 | while (padding_left > page_of_nops.len) { | ||
| 3976 | vecs[vec_index] = .{ | ||
| 3977 | .iov_base = &page_of_nops, | ||
| 3978 | .iov_len = page_of_nops.len, | ||
| 3979 | }; | ||
| 3980 | vec_index += 1; | ||
| 3981 | padding_left -= page_of_nops.len; | ||
| 3982 | } | ||
| 3983 | if (padding_left > 0) { | ||
| 3984 | vecs[vec_index] = .{ | ||
| 3985 | .iov_base = &page_of_nops, | ||
| 3986 | .iov_len = padding_left, | ||
| 3987 | }; | ||
| 3988 | vec_index += 1; | ||
| 3989 | } | ||
| 3990 | } | ||
| 3991 | |||
| 3992 | if (trailing_zero) { | ||
| 3993 | var zbuf = [1]u8{0}; | ||
| 3994 | vecs[vec_index] = .{ | ||
| 3995 | .iov_base = &zbuf, | ||
| 3996 | .iov_len = zbuf.len, | ||
| 3997 | }; | ||
| 3998 | vec_index += 1; | ||
| 3999 | } | ||
| 4000 | |||
| 4001 | try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); | ||
| 4002 | } | ||
| 4003 | |||
| 4004 | fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { | 2872 | fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { |
| 4005 | return .{ | 2873 | return .{ |
| 4006 | .p_type = phdr.p_type, | 2874 | .p_type = phdr.p_type, |
src/link/MachO.zig+10-72| ... | @@ -295,26 +295,6 @@ pub const Export = struct { | ... | @@ -295,26 +295,6 @@ pub const Export = struct { |
| 295 | sym_index: ?u32 = null, | 295 | sym_index: ?u32 = null, |
| 296 | }; | 296 | }; |
| 297 | 297 | ||
| 298 | pub const SrcFn = struct { | ||
| 299 | /// Offset from the beginning of the Debug Line Program header that contains this function. | ||
| 300 | off: u32, | ||
| 301 | /// Size of the line number program component belonging to this function, not | ||
| 302 | /// including padding. | ||
| 303 | len: u32, | ||
| 304 | |||
| 305 | /// Points to the previous and next neighbors, based on the offset from .debug_line. | ||
| 306 | /// This can be used to find, for example, the capacity of this `SrcFn`. | ||
| 307 | prev: ?*SrcFn, | ||
| 308 | next: ?*SrcFn, | ||
| 309 | |||
| 310 | pub const empty: SrcFn = .{ | ||
| 311 | .off = 0, | ||
| 312 | .len = 0, | ||
| 313 | .prev = null, | ||
| 314 | .next = null, | ||
| 315 | }; | ||
| 316 | }; | ||
| 317 | |||
| 318 | pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { | 298 | pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { |
| 319 | assert(options.object_format == .macho); | 299 | assert(options.object_format == .macho); |
| 320 | 300 | ||
| ... | @@ -376,6 +356,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { | ... | @@ -376,6 +356,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { |
| 376 | 356 | ||
| 377 | self.d_sym = .{ | 357 | self.d_sym = .{ |
| 378 | .base = self, | 358 | .base = self, |
| 359 | .dwarf = link.File.Dwarf.init(allocator, .macho, options.target), | ||
| 379 | .file = d_sym_file, | 360 | .file = d_sym_file, |
| 380 | }; | 361 | }; |
| 381 | } | 362 | } |
| ... | @@ -3523,7 +3504,6 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) | ... | @@ -3523,7 +3504,6 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) |
| 3523 | i += 1; | 3504 | i += 1; |
| 3524 | } | 3505 | } |
| 3525 | } | 3506 | } |
| 3526 | // TODO process free list for dbg info just like we do above for vaddrs | ||
| 3527 | 3507 | ||
| 3528 | if (self.atoms.getPtr(match)) |last_atom| { | 3508 | if (self.atoms.getPtr(match)) |last_atom| { |
| 3529 | if (last_atom.* == atom) { | 3509 | if (last_atom.* == atom) { |
| ... | @@ -3536,16 +3516,6 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) | ... | @@ -3536,16 +3516,6 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) |
| 3536 | } | 3516 | } |
| 3537 | } | 3517 | } |
| 3538 | 3518 | ||
| 3539 | if (self.d_sym) |*d_sym| { | ||
| 3540 | if (d_sym.dbg_info_decl_first == atom) { | ||
| 3541 | d_sym.dbg_info_decl_first = atom.dbg_info_next; | ||
| 3542 | } | ||
| 3543 | if (d_sym.dbg_info_decl_last == atom) { | ||
| 3544 | // TODO shrink the .debug_info section size here | ||
| 3545 | d_sym.dbg_info_decl_last = atom.dbg_info_prev; | ||
| 3546 | } | ||
| 3547 | } | ||
| 3548 | |||
| 3549 | if (atom.prev) |prev| { | 3519 | if (atom.prev) |prev| { |
| 3550 | prev.next = atom.next; | 3520 | prev.next = atom.next; |
| 3551 | 3521 | ||
| ... | @@ -3564,18 +3534,8 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) | ... | @@ -3564,18 +3534,8 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) |
| 3564 | atom.next = null; | 3534 | atom.next = null; |
| 3565 | } | 3535 | } |
| 3566 | 3536 | ||
| 3567 | if (atom.dbg_info_prev) |prev| { | 3537 | if (self.d_sym) |*d_sym| { |
| 3568 | prev.dbg_info_next = atom.dbg_info_next; | 3538 | d_sym.dwarf.freeAtom(&atom.dbg_info_atom); |
| 3569 | |||
| 3570 | // TODO the free list logic like we do for atoms above | ||
| 3571 | } else { | ||
| 3572 | atom.dbg_info_prev = null; | ||
| 3573 | } | ||
| 3574 | |||
| 3575 | if (atom.dbg_info_next) |next| { | ||
| 3576 | next.dbg_info_prev = atom.dbg_info_prev; | ||
| 3577 | } else { | ||
| 3578 | atom.dbg_info_next = null; | ||
| 3579 | } | 3539 | } |
| 3580 | } | 3540 | } |
| 3581 | 3541 | ||
| ... | @@ -3725,9 +3685,9 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv | ... | @@ -3725,9 +3685,9 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 3725 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 3685 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 3726 | defer code_buffer.deinit(); | 3686 | defer code_buffer.deinit(); |
| 3727 | 3687 | ||
| 3728 | var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined; | 3688 | var debug_buffers_buf: link.File.Dwarf.DeclDebugBuffers = undefined; |
| 3729 | const debug_buffers = if (self.d_sym) |*d_sym| blk: { | 3689 | const debug_buffers = if (self.d_sym) |*d_sym| blk: { |
| 3730 | debug_buffers_buf = try d_sym.initDeclDebugBuffers(self.base.allocator, module, decl); | 3690 | debug_buffers_buf = try d_sym.initDeclDebugInfo(module, decl); |
| 3731 | break :blk &debug_buffers_buf; | 3691 | break :blk &debug_buffers_buf; |
| 3732 | } else null; | 3692 | } else null; |
| 3733 | defer { | 3693 | defer { |
| ... | @@ -3766,7 +3726,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv | ... | @@ -3766,7 +3726,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 3766 | 3726 | ||
| 3767 | if (debug_buffers) |db| { | 3727 | if (debug_buffers) |db| { |
| 3768 | if (self.d_sym) |*d_sym| { | 3728 | if (self.d_sym) |*d_sym| { |
| 3769 | try d_sym.commitDeclDebugInfo(self.base.allocator, module, decl, db); | 3729 | try d_sym.commitDeclDebugInfo(module, decl, db); |
| 3770 | } | 3730 | } |
| 3771 | } | 3731 | } |
| 3772 | 3732 | ||
| ... | @@ -3805,9 +3765,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De | ... | @@ -3805,9 +3765,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De |
| 3805 | const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment)); | 3765 | const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment)); |
| 3806 | try self.atom_by_index_table.putNoClobber(self.base.allocator, local_sym_index, atom); | 3766 | try self.atom_by_index_table.putNoClobber(self.base.allocator, local_sym_index, atom); |
| 3807 | 3767 | ||
| 3808 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ | 3768 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{ |
| 3809 | .none = .{}, | ||
| 3810 | }, .{ | ||
| 3811 | .parent_atom_index = local_sym_index, | 3769 | .parent_atom_index = local_sym_index, |
| 3812 | }); | 3770 | }); |
| 3813 | const code = switch (res) { | 3771 | const code = switch (res) { |
| ... | @@ -3869,9 +3827,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -3869,9 +3827,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 3869 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 3827 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 3870 | defer code_buffer.deinit(); | 3828 | defer code_buffer.deinit(); |
| 3871 | 3829 | ||
| 3872 | var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined; | 3830 | var debug_buffers_buf: link.File.Dwarf.DeclDebugBuffers = undefined; |
| 3873 | const debug_buffers = if (self.d_sym) |*d_sym| blk: { | 3831 | const debug_buffers = if (self.d_sym) |*d_sym| blk: { |
| 3874 | debug_buffers_buf = try d_sym.initDeclDebugBuffers(self.base.allocator, module, decl); | 3832 | debug_buffers_buf = try d_sym.initDeclDebugInfo(module, decl); |
| 3875 | break :blk &debug_buffers_buf; | 3833 | break :blk &debug_buffers_buf; |
| 3876 | } else null; | 3834 | } else null; |
| 3877 | defer { | 3835 | defer { |
| ... | @@ -4364,27 +4322,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { | ... | @@ -4364,27 +4322,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| 4364 | decl.link.macho.local_sym_index = 0; | 4322 | decl.link.macho.local_sym_index = 0; |
| 4365 | } | 4323 | } |
| 4366 | if (self.d_sym) |*d_sym| { | 4324 | if (self.d_sym) |*d_sym| { |
| 4367 | // TODO make this logic match freeAtom. Maybe abstract the logic | 4325 | d_sym.dwarf.freeDecl(decl); |
| 4368 | // out since the same thing is desired for both. | ||
| 4369 | _ = d_sym.dbg_line_fn_free_list.remove(&decl.fn_link.macho); | ||
| 4370 | if (decl.fn_link.macho.prev) |prev| { | ||
| 4371 | d_sym.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {}; | ||
| 4372 | prev.next = decl.fn_link.macho.next; | ||
| 4373 | if (decl.fn_link.macho.next) |next| { | ||
| 4374 | next.prev = prev; | ||
| 4375 | } else { | ||
| 4376 | d_sym.dbg_line_fn_last = prev; | ||
| 4377 | } | ||
| 4378 | } else if (decl.fn_link.macho.next) |next| { | ||
| 4379 | d_sym.dbg_line_fn_first = next; | ||
| 4380 | next.prev = null; | ||
| 4381 | } | ||
| 4382 | if (d_sym.dbg_line_fn_first == &decl.fn_link.macho) { | ||
| 4383 | d_sym.dbg_line_fn_first = decl.fn_link.macho.next; | ||
| 4384 | } | ||
| 4385 | if (d_sym.dbg_line_fn_last == &decl.fn_link.macho) { | ||
| 4386 | d_sym.dbg_line_fn_last = decl.fn_link.macho.prev; | ||
| 4387 | } | ||
| 4388 | } | 4326 | } |
| 4389 | } | 4327 | } |
| 4390 | 4328 |
src/link/MachO/Atom.zig+3-12| ... | @@ -13,6 +13,7 @@ const trace = @import("../../tracy.zig").trace; | ... | @@ -13,6 +13,7 @@ const trace = @import("../../tracy.zig").trace; |
| 13 | 13 | ||
| 14 | const Allocator = mem.Allocator; | 14 | const Allocator = mem.Allocator; |
| 15 | const Arch = std.Target.Cpu.Arch; | 15 | const Arch = std.Target.Cpu.Arch; |
| 16 | const Dwarf = @import("../Dwarf.zig"); | ||
| 16 | const MachO = @import("../MachO.zig"); | 17 | const MachO = @import("../MachO.zig"); |
| 17 | const Object = @import("Object.zig"); | 18 | const Object = @import("Object.zig"); |
| 18 | const StringIndexAdapter = std.hash_map.StringIndexAdapter; | 19 | const StringIndexAdapter = std.hash_map.StringIndexAdapter; |
| ... | @@ -71,14 +72,7 @@ stab: ?Stab = null, | ... | @@ -71,14 +72,7 @@ stab: ?Stab = null, |
| 71 | next: ?*Atom, | 72 | next: ?*Atom, |
| 72 | prev: ?*Atom, | 73 | prev: ?*Atom, |
| 73 | 74 | ||
| 74 | /// Previous/next linked list pointers. | 75 | dbg_info_atom: Dwarf.DebugInfoAtom, |
| 75 | /// This is the linked list node for this Decl's corresponding .debug_info tag. | ||
| 76 | dbg_info_prev: ?*Atom, | ||
| 77 | dbg_info_next: ?*Atom, | ||
| 78 | /// Offset into .debug_info pointing to the tag for this Decl. | ||
| 79 | dbg_info_off: u32, | ||
| 80 | /// Size of the .debug_info tag for this Decl, not including padding. | ||
| 81 | dbg_info_len: u32, | ||
| 82 | 76 | ||
| 83 | dirty: bool = true, | 77 | dirty: bool = true, |
| 84 | 78 | ||
| ... | @@ -188,10 +182,7 @@ pub const empty = Atom{ | ... | @@ -188,10 +182,7 @@ pub const empty = Atom{ |
| 188 | .alignment = 0, | 182 | .alignment = 0, |
| 189 | .prev = null, | 183 | .prev = null, |
| 190 | .next = null, | 184 | .next = null, |
| 191 | .dbg_info_prev = null, | 185 | .dbg_info_atom = undefined, |
| 192 | .dbg_info_next = null, | ||
| 193 | .dbg_info_off = undefined, | ||
| 194 | .dbg_info_len = undefined, | ||
| 195 | }; | 186 | }; |
| 196 | 187 | ||
| 197 | pub fn deinit(self: *Atom, allocator: Allocator) void { | 188 | pub fn deinit(self: *Atom, allocator: Allocator) void { |
src/link/MachO/DebugSymbols.zig+31-1139| ... | @@ -1,29 +1,27 @@ | ... | @@ -1,29 +1,27 @@ |
| 1 | const DebugSymbols = @This(); | 1 | const DebugSymbols = @This(); |
| 2 | 2 | ||
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const build_options = @import("build_options"); | ||
| 4 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 5 | const fs = std.fs; | 6 | const fs = std.fs; |
| 7 | const link = @import("../../link.zig"); | ||
| 6 | const log = std.log.scoped(.link); | 8 | const log = std.log.scoped(.link); |
| 7 | const leb128 = std.leb; | ||
| 8 | const macho = std.macho; | 9 | const macho = std.macho; |
| 10 | const makeStaticString = MachO.makeStaticString; | ||
| 9 | const math = std.math; | 11 | const math = std.math; |
| 10 | const mem = std.mem; | 12 | const mem = std.mem; |
| 11 | const DW = std.dwarf; | 13 | const padToIdeal = MachO.padToIdeal; |
| 12 | const leb = std.leb; | ||
| 13 | const Allocator = mem.Allocator; | ||
| 14 | |||
| 15 | const build_options = @import("build_options"); | ||
| 16 | const trace = @import("../../tracy.zig").trace; | 14 | const trace = @import("../../tracy.zig").trace; |
| 17 | const Module = @import("../../Module.zig"); | 15 | |
| 18 | const Type = @import("../../type.zig").Type; | 16 | const Allocator = mem.Allocator; |
| 19 | const link = @import("../../link.zig"); | 17 | const Dwarf = @import("../Dwarf.zig"); |
| 20 | const MachO = @import("../MachO.zig"); | 18 | const MachO = @import("../MachO.zig"); |
| 19 | const Module = @import("../../Module.zig"); | ||
| 21 | const TextBlock = MachO.TextBlock; | 20 | const TextBlock = MachO.TextBlock; |
| 22 | const SrcFn = MachO.SrcFn; | 21 | const Type = @import("../../type.zig").Type; |
| 23 | const makeStaticString = MachO.makeStaticString; | ||
| 24 | const padToIdeal = MachO.padToIdeal; | ||
| 25 | 22 | ||
| 26 | base: *MachO, | 23 | base: *MachO, |
| 24 | dwarf: Dwarf, | ||
| 27 | file: fs.File, | 25 | file: fs.File, |
| 28 | 26 | ||
| 29 | /// Table of all load commands | 27 | /// Table of all load commands |
| ... | @@ -54,23 +52,6 @@ debug_str_section_index: ?u16 = null, | ... | @@ -54,23 +52,6 @@ debug_str_section_index: ?u16 = null, |
| 54 | debug_aranges_section_index: ?u16 = null, | 52 | debug_aranges_section_index: ?u16 = null, |
| 55 | debug_line_section_index: ?u16 = null, | 53 | debug_line_section_index: ?u16 = null, |
| 56 | 54 | ||
| 57 | debug_abbrev_table_offset: ?u64 = null, | ||
| 58 | |||
| 59 | /// A list of `SrcFn` whose Line Number Programs have surplus capacity. | ||
| 60 | /// This is the same concept as `text_block_free_list`; see those doc comments. | ||
| 61 | dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{}, | ||
| 62 | dbg_line_fn_first: ?*SrcFn = null, | ||
| 63 | dbg_line_fn_last: ?*SrcFn = null, | ||
| 64 | |||
| 65 | /// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity. | ||
| 66 | /// This is the same concept as `text_block_free_list`; see those doc comments. | ||
| 67 | dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{}, | ||
| 68 | dbg_info_decl_first: ?*TextBlock = null, | ||
| 69 | dbg_info_decl_last: ?*TextBlock = null, | ||
| 70 | |||
| 71 | /// Table of debug symbol names aka the debug string table. | ||
| 72 | debug_string_table: std.ArrayListUnmanaged(u8) = .{}, | ||
| 73 | |||
| 74 | load_commands_dirty: bool = false, | 55 | load_commands_dirty: bool = false, |
| 75 | debug_string_table_dirty: bool = false, | 56 | debug_string_table_dirty: bool = false, |
| 76 | debug_abbrev_section_dirty: bool = false, | 57 | debug_abbrev_section_dirty: bool = false, |
| ... | @@ -78,25 +59,6 @@ debug_aranges_section_dirty: bool = false, | ... | @@ -78,25 +59,6 @@ debug_aranges_section_dirty: bool = false, |
| 78 | debug_info_header_dirty: bool = false, | 59 | debug_info_header_dirty: bool = false, |
| 79 | debug_line_header_dirty: bool = false, | 60 | debug_line_header_dirty: bool = false, |
| 80 | 61 | ||
| 81 | pub const abbrev_compile_unit = 1; | ||
| 82 | pub const abbrev_subprogram = 2; | ||
| 83 | pub const abbrev_subprogram_retvoid = 3; | ||
| 84 | pub const abbrev_base_type = 4; | ||
| 85 | pub const abbrev_ptr_type = 5; | ||
| 86 | pub const abbrev_struct_type = 6; | ||
| 87 | pub const abbrev_struct_member = 7; | ||
| 88 | pub const abbrev_pad1 = 8; | ||
| 89 | pub const abbrev_parameter = 9; | ||
| 90 | |||
| 91 | /// The reloc offset for the virtual address of a function in its Line Number Program. | ||
| 92 | /// Size is a virtual address integer. | ||
| 93 | const dbg_line_vaddr_reloc_index = 3; | ||
| 94 | /// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram. | ||
| 95 | /// Size is a virtual address integer. | ||
| 96 | const dbg_info_low_pc_reloc_index = 1; | ||
| 97 | |||
| 98 | const min_nop_size = 2; | ||
| 99 | |||
| 100 | /// You must call this function *after* `MachO.populateMissingMetadata()` | 62 | /// You must call this function *after* `MachO.populateMissingMetadata()` |
| 101 | /// has been called to get a viable debug symbols output. | 63 | /// has been called to get a viable debug symbols output. |
| 102 | pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void { | 64 | pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void { |
| ... | @@ -193,10 +155,10 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void | ... | @@ -193,10 +155,10 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void |
| 193 | } | 155 | } |
| 194 | 156 | ||
| 195 | if (self.debug_str_section_index == null) { | 157 | if (self.debug_str_section_index == null) { |
| 196 | assert(self.debug_string_table.items.len == 0); | 158 | assert(self.dwarf.strtab.items.len == 0); |
| 197 | self.debug_str_section_index = try self.allocateSection( | 159 | self.debug_str_section_index = try self.allocateSection( |
| 198 | "__debug_str", | 160 | "__debug_str", |
| 199 | @intCast(u32, self.debug_string_table.items.len), | 161 | @intCast(u32, self.dwarf.strtab.items.len), |
| 200 | 0, | 162 | 0, |
| 201 | ); | 163 | ); |
| 202 | self.debug_string_table_dirty = true; | 164 | self.debug_string_table_dirty = true; |
| ... | @@ -277,7 +239,7 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 { | ... | @@ -277,7 +239,7 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 { |
| 277 | return null; | 239 | return null; |
| 278 | } | 240 | } |
| 279 | 241 | ||
| 280 | fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 { | 242 | pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 { |
| 281 | const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | 243 | const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; |
| 282 | var offset: u64 = seg.inner.fileoff; | 244 | var offset: u64 = seg.inner.fileoff; |
| 283 | while (self.detectAllocCollision(offset, object_size)) |item_end| { | 245 | while (self.detectAllocCollision(offset, object_size)) |item_end| { |
| ... | @@ -290,322 +252,45 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti | ... | @@ -290,322 +252,45 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti |
| 290 | // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the | 252 | // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the |
| 291 | // Zig source code. | 253 | // Zig source code. |
| 292 | const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented; | 254 | const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented; |
| 293 | const init_len_size: usize = 4; | ||
| 294 | 255 | ||
| 295 | if (self.debug_abbrev_section_dirty) { | 256 | if (self.debug_abbrev_section_dirty) { |
| 296 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | 257 | try self.dwarf.writeDbgAbbrev(&self.base.base); |
| 297 | const debug_abbrev_sect = &dwarf_segment.sections.items[self.debug_abbrev_section_index.?]; | ||
| 298 | |||
| 299 | // These are LEB encoded but since the values are all less than 127 | ||
| 300 | // we can simply append these bytes. | ||
| 301 | const abbrev_buf = [_]u8{ | ||
| 302 | abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header | ||
| 303 | DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, | ||
| 304 | DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, | ||
| 305 | DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, | ||
| 306 | DW.FORM.strp, DW.AT.producer, DW.FORM.strp, | ||
| 307 | DW.AT.language, DW.FORM.data2, 0, | ||
| 308 | 0, // table sentinel | ||
| 309 | abbrev_subprogram, | ||
| 310 | DW.TAG.subprogram, | ||
| 311 | DW.CHILDREN.yes, // header | ||
| 312 | DW.AT.low_pc, | ||
| 313 | DW.FORM.addr, | ||
| 314 | DW.AT.high_pc, | ||
| 315 | DW.FORM.data4, | ||
| 316 | DW.AT.type, | ||
| 317 | DW.FORM.ref4, | ||
| 318 | DW.AT.name, | ||
| 319 | DW.FORM.string, | ||
| 320 | 0, 0, // table sentinel | ||
| 321 | abbrev_subprogram_retvoid, | ||
| 322 | DW.TAG.subprogram, DW.CHILDREN.yes, // header | ||
| 323 | DW.AT.low_pc, DW.FORM.addr, | ||
| 324 | DW.AT.high_pc, DW.FORM.data4, | ||
| 325 | DW.AT.name, DW.FORM.string, | ||
| 326 | 0, | ||
| 327 | 0, // table sentinel | ||
| 328 | abbrev_base_type, | ||
| 329 | DW.TAG.base_type, | ||
| 330 | DW.CHILDREN.no, // header | ||
| 331 | DW.AT.encoding, | ||
| 332 | DW.FORM.data1, | ||
| 333 | DW.AT.byte_size, | ||
| 334 | DW.FORM.data1, | ||
| 335 | DW.AT.name, | ||
| 336 | DW.FORM.string, | ||
| 337 | 0, | ||
| 338 | 0, // table sentinel | ||
| 339 | abbrev_ptr_type, | ||
| 340 | DW.TAG.pointer_type, | ||
| 341 | DW.CHILDREN.no, // header | ||
| 342 | DW.AT.type, | ||
| 343 | DW.FORM.ref4, | ||
| 344 | 0, | ||
| 345 | 0, // table sentinel | ||
| 346 | abbrev_struct_type, | ||
| 347 | DW.TAG.structure_type, | ||
| 348 | DW.CHILDREN.yes, // header | ||
| 349 | DW.AT.byte_size, | ||
| 350 | DW.FORM.sdata, | ||
| 351 | DW.AT.name, | ||
| 352 | DW.FORM.string, | ||
| 353 | 0, | ||
| 354 | 0, // table sentinel | ||
| 355 | abbrev_struct_member, | ||
| 356 | DW.TAG.member, | ||
| 357 | DW.CHILDREN.no, // header | ||
| 358 | DW.AT.name, | ||
| 359 | DW.FORM.string, | ||
| 360 | DW.AT.type, | ||
| 361 | DW.FORM.ref4, | ||
| 362 | DW.AT.data_member_location, | ||
| 363 | DW.FORM.sdata, | ||
| 364 | 0, | ||
| 365 | 0, // table sentinel | ||
| 366 | abbrev_pad1, | ||
| 367 | DW.TAG.unspecified_type, | ||
| 368 | DW.CHILDREN.no, // header | ||
| 369 | 0, | ||
| 370 | 0, // table sentinel | ||
| 371 | abbrev_parameter, | ||
| 372 | DW.TAG.formal_parameter, DW.CHILDREN.no, // header | ||
| 373 | DW.AT.location, DW.FORM.exprloc, | ||
| 374 | DW.AT.type, DW.FORM.ref4, | ||
| 375 | DW.AT.name, DW.FORM.string, | ||
| 376 | 0, | ||
| 377 | 0, // table sentinel | ||
| 378 | 0, | ||
| 379 | 0, | ||
| 380 | 0, // section sentinel | ||
| 381 | }; | ||
| 382 | |||
| 383 | const needed_size = abbrev_buf.len; | ||
| 384 | const allocated_size = self.allocatedSize(debug_abbrev_sect.offset); | ||
| 385 | if (needed_size > allocated_size) { | ||
| 386 | debug_abbrev_sect.size = 0; // free the space | ||
| 387 | const offset = self.findFreeSpace(needed_size, 1); | ||
| 388 | debug_abbrev_sect.offset = @intCast(u32, offset); | ||
| 389 | debug_abbrev_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff; | ||
| 390 | } | ||
| 391 | debug_abbrev_sect.size = needed_size; | ||
| 392 | log.debug("__debug_abbrev start=0x{x} end=0x{x}", .{ | ||
| 393 | debug_abbrev_sect.offset, | ||
| 394 | debug_abbrev_sect.offset + needed_size, | ||
| 395 | }); | ||
| 396 | |||
| 397 | const abbrev_offset = 0; | ||
| 398 | self.debug_abbrev_table_offset = abbrev_offset; | ||
| 399 | try self.file.pwriteAll(&abbrev_buf, debug_abbrev_sect.offset + abbrev_offset); | ||
| 400 | self.load_commands_dirty = true; | 258 | self.load_commands_dirty = true; |
| 401 | self.debug_abbrev_section_dirty = false; | 259 | self.debug_abbrev_section_dirty = false; |
| 402 | } | 260 | } |
| 403 | 261 | ||
| 404 | if (self.debug_info_header_dirty) debug_info: { | 262 | if (self.debug_info_header_dirty) { |
| 405 | // If this value is null it means there is an error in the module; | ||
| 406 | // leave debug_info_header_dirty=true. | ||
| 407 | const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info; | ||
| 408 | const last_dbg_info_decl = self.dbg_info_decl_last.?; | ||
| 409 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | ||
| 410 | const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?]; | ||
| 411 | |||
| 412 | // We have a function to compute the upper bound size, because it's needed | ||
| 413 | // for determining where to put the offset of the first `LinkBlock`. | ||
| 414 | const needed_bytes = self.dbgInfoNeededHeaderBytes(); | ||
| 415 | var di_buf = try std.ArrayList(u8).initCapacity(allocator, needed_bytes); | ||
| 416 | defer di_buf.deinit(); | ||
| 417 | |||
| 418 | // initial length - length of the .debug_info contribution for this compilation unit, | ||
| 419 | // not including the initial length itself. | ||
| 420 | // We have to come back and write it later after we know the size. | ||
| 421 | const after_init_len = di_buf.items.len + init_len_size; | ||
| 422 | // +1 for the final 0 that ends the compilation unit children. | ||
| 423 | const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1; | ||
| 424 | const init_len = dbg_info_end - after_init_len; | ||
| 425 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len)); | ||
| 426 | mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4); // DWARF version | ||
| 427 | const abbrev_offset = self.debug_abbrev_table_offset.?; | ||
| 428 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset)); | ||
| 429 | di_buf.appendAssumeCapacity(8); // address size | ||
| 430 | // Write the form for the compile unit, which must match the abbrev table above. | ||
| 431 | const name_strp = try self.makeDebugString(allocator, module.root_pkg.root_src_path); | ||
| 432 | const comp_dir_strp = try self.makeDebugString(allocator, module.root_pkg.root_src_directory.path orelse "."); | ||
| 433 | const producer_strp = try self.makeDebugString(allocator, link.producer_string); | ||
| 434 | // Currently only one compilation unit is supported, so the address range is simply | 263 | // Currently only one compilation unit is supported, so the address range is simply |
| 435 | // identical to the main program header virtual address and memory size. | 264 | // identical to the main program header virtual address and memory size. |
| 436 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment; | 265 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment; |
| 437 | const text_section = text_segment.sections.items[self.text_section_index.?]; | 266 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 438 | const low_pc = text_section.addr; | 267 | const low_pc = text_section.addr; |
| 439 | const high_pc = text_section.addr + text_section.size; | 268 | const high_pc = text_section.addr + text_section.size; |
| 440 | 269 | try self.dwarf.writeDbgInfoHeader(&self.base.base, module, low_pc, high_pc); | |
| 441 | di_buf.appendAssumeCapacity(abbrev_compile_unit); | ||
| 442 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // DW.AT.stmt_list, DW.FORM.sec_offset | ||
| 443 | mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), low_pc); | ||
| 444 | mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), high_pc); | ||
| 445 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, name_strp)); | ||
| 446 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, comp_dir_strp)); | ||
| 447 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, producer_strp)); | ||
| 448 | // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number: | ||
| 449 | // http://dwarfstd.org/ShowIssue.php?issue=171115.1 | ||
| 450 | // Until then we say it is C99. | ||
| 451 | mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99); | ||
| 452 | |||
| 453 | if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) { | ||
| 454 | // Move the first N decls to the end to make more padding for the header. | ||
| 455 | @panic("TODO: handle __debug_info header exceeding its padding"); | ||
| 456 | } | ||
| 457 | const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len; | ||
| 458 | try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.offset); | ||
| 459 | self.debug_info_header_dirty = false; | 270 | self.debug_info_header_dirty = false; |
| 460 | } | 271 | } |
| 461 | 272 | ||
| 462 | if (self.debug_aranges_section_dirty) { | 273 | if (self.debug_aranges_section_dirty) { |
| 463 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | ||
| 464 | const debug_aranges_sect = &dwarf_segment.sections.items[self.debug_aranges_section_index.?]; | ||
| 465 | |||
| 466 | // Enough for all the data without resizing. When support for more compilation units | ||
| 467 | // is added, the size of this section will become more variable. | ||
| 468 | var di_buf = try std.ArrayList(u8).initCapacity(allocator, 100); | ||
| 469 | defer di_buf.deinit(); | ||
| 470 | |||
| 471 | // initial length - length of the .debug_aranges contribution for this compilation unit, | ||
| 472 | // not including the initial length itself. | ||
| 473 | // We have to come back and write it later after we know the size. | ||
| 474 | const init_len_index = di_buf.items.len; | ||
| 475 | di_buf.items.len += init_len_size; | ||
| 476 | const after_init_len = di_buf.items.len; | ||
| 477 | mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2); // version | ||
| 478 | // When more than one compilation unit is supported, this will be the offset to it. | ||
| 479 | // For now it is always at offset 0 in .debug_info. | ||
| 480 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // __debug_info offset | ||
| 481 | di_buf.appendAssumeCapacity(@sizeOf(u64)); // address_size | ||
| 482 | di_buf.appendAssumeCapacity(0); // segment_selector_size | ||
| 483 | |||
| 484 | const end_header_offset = di_buf.items.len; | ||
| 485 | const begin_entries_offset = mem.alignForward(end_header_offset, @sizeOf(u64) * 2); | ||
| 486 | di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset); | ||
| 487 | |||
| 488 | // Currently only one compilation unit is supported, so the address range is simply | 274 | // Currently only one compilation unit is supported, so the address range is simply |
| 489 | // identical to the main program header virtual address and memory size. | 275 | // identical to the main program header virtual address and memory size. |
| 490 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment; | 276 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment; |
| 491 | const text_section = text_segment.sections.items[self.text_section_index.?]; | 277 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 492 | mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.addr); | 278 | try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size); |
| 493 | mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.size); | ||
| 494 | |||
| 495 | // Sentinel. | ||
| 496 | mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), 0); | ||
| 497 | mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), 0); | ||
| 498 | |||
| 499 | // Go back and populate the initial length. | ||
| 500 | const init_len = di_buf.items.len - after_init_len; | ||
| 501 | // initial length - length of the .debug_aranges contribution for this compilation unit, | ||
| 502 | // not including the initial length itself. | ||
| 503 | mem.writeIntLittle(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len)); | ||
| 504 | |||
| 505 | const needed_size = di_buf.items.len; | ||
| 506 | const allocated_size = self.allocatedSize(debug_aranges_sect.offset); | ||
| 507 | if (needed_size > allocated_size) { | ||
| 508 | debug_aranges_sect.size = 0; // free the space | ||
| 509 | const new_offset = self.findFreeSpace(needed_size, 16); | ||
| 510 | debug_aranges_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff; | ||
| 511 | debug_aranges_sect.offset = @intCast(u32, new_offset); | ||
| 512 | } | ||
| 513 | debug_aranges_sect.size = needed_size; | ||
| 514 | log.debug("__debug_aranges start=0x{x} end=0x{x}", .{ | ||
| 515 | debug_aranges_sect.offset, | ||
| 516 | debug_aranges_sect.offset + needed_size, | ||
| 517 | }); | ||
| 518 | |||
| 519 | try self.file.pwriteAll(di_buf.items, debug_aranges_sect.offset); | ||
| 520 | self.load_commands_dirty = true; | 279 | self.load_commands_dirty = true; |
| 521 | self.debug_aranges_section_dirty = false; | 280 | self.debug_aranges_section_dirty = false; |
| 522 | } | 281 | } |
| 523 | if (self.debug_line_header_dirty) debug_line: { | ||
| 524 | if (self.dbg_line_fn_first == null) { | ||
| 525 | break :debug_line; // Error in module; leave debug_line_header_dirty=true. | ||
| 526 | } | ||
| 527 | const dbg_line_prg_off = self.getDebugLineProgramOff(); | ||
| 528 | const dbg_line_prg_end = self.getDebugLineProgramEnd(); | ||
| 529 | assert(dbg_line_prg_end != 0); | ||
| 530 | 282 | ||
| 531 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | 283 | if (self.debug_line_header_dirty) { |
| 532 | const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?]; | 284 | try self.dwarf.writeDbgLineHeader(&self.base.base, module); |
| 533 | |||
| 534 | // The size of this header is variable, depending on the number of directories, | ||
| 535 | // files, and padding. We have a function to compute the upper bound size, however, | ||
| 536 | // because it's needed for determining where to put the offset of the first `SrcFn`. | ||
| 537 | const needed_bytes = self.dbgLineNeededHeaderBytes(module); | ||
| 538 | var di_buf = try std.ArrayList(u8).initCapacity(allocator, needed_bytes); | ||
| 539 | defer di_buf.deinit(); | ||
| 540 | |||
| 541 | // initial length - length of the .debug_line contribution for this compilation unit, | ||
| 542 | // not including the initial length itself. | ||
| 543 | const after_init_len = di_buf.items.len + init_len_size; | ||
| 544 | const init_len = dbg_line_prg_end - after_init_len; | ||
| 545 | mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len)); | ||
| 546 | mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4); // version | ||
| 547 | |||
| 548 | // Empirically, debug info consumers do not respect this field, or otherwise | ||
| 549 | // consider it to be an error when it does not point exactly to the end of the header. | ||
| 550 | // Therefore we rely on the NOP jump at the beginning of the Line Number Program for | ||
| 551 | // padding rather than this field. | ||
| 552 | const before_header_len = di_buf.items.len; | ||
| 553 | di_buf.items.len += @sizeOf(u32); // We will come back and write this. | ||
| 554 | const after_header_len = di_buf.items.len; | ||
| 555 | |||
| 556 | const opcode_base = DW.LNS.set_isa + 1; | ||
| 557 | di_buf.appendSliceAssumeCapacity(&[_]u8{ | ||
| 558 | 1, // minimum_instruction_length | ||
| 559 | 1, // maximum_operations_per_instruction | ||
| 560 | 1, // default_is_stmt | ||
| 561 | 1, // line_base (signed) | ||
| 562 | 1, // line_range | ||
| 563 | opcode_base, | ||
| 564 | |||
| 565 | // Standard opcode lengths. The number of items here is based on `opcode_base`. | ||
| 566 | // The value is the number of LEB128 operands the instruction takes. | ||
| 567 | 0, // `DW.LNS.copy` | ||
| 568 | 1, // `DW.LNS.advance_pc` | ||
| 569 | 1, // `DW.LNS.advance_line` | ||
| 570 | 1, // `DW.LNS.set_file` | ||
| 571 | 1, // `DW.LNS.set_column` | ||
| 572 | 0, // `DW.LNS.negate_stmt` | ||
| 573 | 0, // `DW.LNS.set_basic_block` | ||
| 574 | 0, // `DW.LNS.const_add_pc` | ||
| 575 | 1, // `DW.LNS.fixed_advance_pc` | ||
| 576 | 0, // `DW.LNS.set_prologue_end` | ||
| 577 | 0, // `DW.LNS.set_epilogue_begin` | ||
| 578 | 1, // `DW.LNS.set_isa` | ||
| 579 | 0, // include_directories (none except the compilation unit cwd) | ||
| 580 | }); | ||
| 581 | // file_names[0] | ||
| 582 | di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name | ||
| 583 | di_buf.appendSliceAssumeCapacity(&[_]u8{ | ||
| 584 | 0, // null byte for the relative path name | ||
| 585 | 0, // directory_index | ||
| 586 | 0, // mtime (TODO supply this) | ||
| 587 | 0, // file size bytes (TODO supply this) | ||
| 588 | 0, // file_names sentinel | ||
| 589 | }); | ||
| 590 | |||
| 591 | const header_len = di_buf.items.len - after_header_len; | ||
| 592 | mem.writeIntLittle(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len)); | ||
| 593 | |||
| 594 | // We use NOPs because consumers empirically do not respect the header length field. | ||
| 595 | if (di_buf.items.len > dbg_line_prg_off) { | ||
| 596 | // Move the first N files to the end to make more padding for the header. | ||
| 597 | @panic("TODO: handle __debug_line header exceeding its padding"); | ||
| 598 | } | ||
| 599 | const jmp_amt = dbg_line_prg_off - di_buf.items.len; | ||
| 600 | try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.offset); | ||
| 601 | self.debug_line_header_dirty = false; | 285 | self.debug_line_header_dirty = false; |
| 602 | } | 286 | } |
| 287 | |||
| 603 | { | 288 | { |
| 604 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | 289 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; |
| 605 | const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?]; | 290 | const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?]; |
| 606 | if (self.debug_string_table_dirty or self.debug_string_table.items.len != debug_strtab_sect.size) { | 291 | if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) { |
| 607 | const allocated_size = self.allocatedSize(debug_strtab_sect.offset); | 292 | const allocated_size = self.allocatedSize(debug_strtab_sect.offset); |
| 608 | const needed_size = self.debug_string_table.items.len; | 293 | const needed_size = self.dwarf.strtab.items.len; |
| 609 | 294 | ||
| 610 | if (needed_size > allocated_size) { | 295 | if (needed_size > allocated_size) { |
| 611 | debug_strtab_sect.size = 0; // free the space | 296 | debug_strtab_sect.size = 0; // free the space |
| ... | @@ -620,7 +305,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti | ... | @@ -620,7 +305,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti |
| 620 | debug_strtab_sect.offset + needed_size, | 305 | debug_strtab_sect.offset + needed_size, |
| 621 | }); | 306 | }); |
| 622 | 307 | ||
| 623 | try self.file.pwriteAll(self.debug_string_table.items, debug_strtab_sect.offset); | 308 | try self.file.pwriteAll(self.dwarf.strtab.items, debug_strtab_sect.offset); |
| 624 | self.load_commands_dirty = true; | 309 | self.load_commands_dirty = true; |
| 625 | self.debug_string_table_dirty = false; | 310 | self.debug_string_table_dirty = false; |
| 626 | } | 311 | } |
| ... | @@ -639,13 +324,11 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti | ... | @@ -639,13 +324,11 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti |
| 639 | } | 324 | } |
| 640 | 325 | ||
| 641 | pub fn deinit(self: *DebugSymbols, allocator: Allocator) void { | 326 | pub fn deinit(self: *DebugSymbols, allocator: Allocator) void { |
| 642 | self.dbg_info_decl_free_list.deinit(allocator); | ||
| 643 | self.dbg_line_fn_free_list.deinit(allocator); | ||
| 644 | self.debug_string_table.deinit(allocator); | ||
| 645 | for (self.load_commands.items) |*lc| { | 327 | for (self.load_commands.items) |*lc| { |
| 646 | lc.deinit(allocator); | 328 | lc.deinit(allocator); |
| 647 | } | 329 | } |
| 648 | self.load_commands.deinit(allocator); | 330 | self.load_commands.deinit(allocator); |
| 331 | self.dwarf.deinit(); | ||
| 649 | self.file.close(); | 332 | self.file.close(); |
| 650 | } | 333 | } |
| 651 | 334 | ||
| ... | @@ -777,7 +460,7 @@ fn writeHeader(self: *DebugSymbols) !void { | ... | @@ -777,7 +460,7 @@ fn writeHeader(self: *DebugSymbols) !void { |
| 777 | try self.file.pwriteAll(mem.asBytes(&header), 0); | 460 | try self.file.pwriteAll(mem.asBytes(&header), 0); |
| 778 | } | 461 | } |
| 779 | 462 | ||
| 780 | fn allocatedSize(self: *DebugSymbols, start: u64) u64 { | 463 | pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 { |
| 781 | const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | 464 | const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; |
| 782 | assert(start >= seg.inner.fileoff); | 465 | assert(start >= seg.inner.fileoff); |
| 783 | var min_pos: u64 = std.math.maxInt(u64); | 466 | var min_pos: u64 = std.math.maxInt(u64); |
| ... | @@ -964,813 +647,22 @@ fn writeStringTable(self: *DebugSymbols) !void { | ... | @@ -964,813 +647,22 @@ fn writeStringTable(self: *DebugSymbols) !void { |
| 964 | 647 | ||
| 965 | pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void { | 648 | pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void { |
| 966 | _ = module; | 649 | _ = module; |
| 967 | const tracy = trace(@src()); | 650 | return self.dwarf.updateDeclLineNumber(&self.base.base, decl); |
| 968 | defer tracy.end(); | ||
| 969 | |||
| 970 | log.debug("updateDeclLineNumber {s}{*}", .{ decl.name, decl }); | ||
| 971 | |||
| 972 | const func = decl.val.castTag(.function).?.data; | ||
| 973 | log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ | ||
| 974 | decl.src_line, | ||
| 975 | func.lbrace_line, | ||
| 976 | func.rbrace_line, | ||
| 977 | }); | ||
| 978 | const line = @intCast(u28, decl.src_line + func.lbrace_line); | ||
| 979 | |||
| 980 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | ||
| 981 | const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?]; | ||
| 982 | const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff(); | ||
| 983 | var data: [4]u8 = undefined; | ||
| 984 | leb.writeUnsignedFixed(4, &data, line); | ||
| 985 | try self.file.pwriteAll(&data, file_pos); | ||
| 986 | } | 651 | } |
| 987 | 652 | ||
| 988 | pub const DeclDebugBuffers = struct { | ||
| 989 | dbg_line_buffer: std.ArrayList(u8), | ||
| 990 | dbg_info_buffer: std.ArrayList(u8), | ||
| 991 | dbg_info_type_relocs: link.File.DbgInfoTypeRelocsTable, | ||
| 992 | }; | ||
| 993 | |||
| 994 | /// Caller owns the returned memory. | 653 | /// Caller owns the returned memory. |
| 995 | pub fn initDeclDebugBuffers( | 654 | pub fn initDeclDebugInfo(self: *DebugSymbols, module: *Module, decl: *Module.Decl) !Dwarf.DeclDebugBuffers { |
| 996 | self: *DebugSymbols, | ||
| 997 | allocator: Allocator, | ||
| 998 | module: *Module, | ||
| 999 | decl: *Module.Decl, | ||
| 1000 | ) !DeclDebugBuffers { | ||
| 1001 | _ = self; | ||
| 1002 | _ = module; | 655 | _ = module; |
| 1003 | const tracy = trace(@src()); | 656 | return self.dwarf.initDeclDebugInfo(decl); |
| 1004 | defer tracy.end(); | ||
| 1005 | |||
| 1006 | var dbg_line_buffer = std.ArrayList(u8).init(allocator); | ||
| 1007 | var dbg_info_buffer = std.ArrayList(u8).init(allocator); | ||
| 1008 | var dbg_info_type_relocs: link.File.DbgInfoTypeRelocsTable = .{}; | ||
| 1009 | |||
| 1010 | assert(decl.has_tv); | ||
| 1011 | switch (decl.ty.zigTypeTag()) { | ||
| 1012 | .Fn => { | ||
| 1013 | // For functions we need to add a prologue to the debug line program. | ||
| 1014 | try dbg_line_buffer.ensureTotalCapacity(26); | ||
| 1015 | |||
| 1016 | const func = decl.val.castTag(.function).?.data; | ||
| 1017 | log.debug("updateFunc {s}{*}", .{ decl.name, func.owner_decl }); | ||
| 1018 | log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ | ||
| 1019 | decl.src_line, | ||
| 1020 | func.lbrace_line, | ||
| 1021 | func.rbrace_line, | ||
| 1022 | }); | ||
| 1023 | const line = @intCast(u28, decl.src_line + func.lbrace_line); | ||
| 1024 | |||
| 1025 | dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{ | ||
| 1026 | DW.LNS.extended_op, | ||
| 1027 | @sizeOf(u64) + 1, | ||
| 1028 | DW.LNE.set_address, | ||
| 1029 | }); | ||
| 1030 | // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`. | ||
| 1031 | assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len); | ||
| 1032 | dbg_line_buffer.items.len += @sizeOf(u64); | ||
| 1033 | |||
| 1034 | dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line); | ||
| 1035 | // This is the "relocatable" relative line offset from the previous function's end curly | ||
| 1036 | // to this function's begin curly. | ||
| 1037 | assert(getRelocDbgLineOff() == dbg_line_buffer.items.len); | ||
| 1038 | // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later. | ||
| 1039 | leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line); | ||
| 1040 | |||
| 1041 | dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file); | ||
| 1042 | assert(getRelocDbgFileIndex() == dbg_line_buffer.items.len); | ||
| 1043 | // Once we support more than one source file, this will have the ability to be more | ||
| 1044 | // than one possible value. | ||
| 1045 | const file_index = 1; | ||
| 1046 | leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index); | ||
| 1047 | |||
| 1048 | // Emit a line for the begin curly with prologue_end=false. The codegen will | ||
| 1049 | // do the work of setting prologue_end=true and epilogue_begin=true. | ||
| 1050 | dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy); | ||
| 1051 | |||
| 1052 | // .debug_info subprogram | ||
| 1053 | const decl_name_with_null = decl.name[0 .. mem.sliceTo(decl.name, 0).len + 1]; | ||
| 1054 | try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len); | ||
| 1055 | |||
| 1056 | const fn_ret_type = decl.ty.fnReturnType(); | ||
| 1057 | const fn_ret_has_bits = fn_ret_type.hasRuntimeBits(); | ||
| 1058 | if (fn_ret_has_bits) { | ||
| 1059 | dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram); | ||
| 1060 | } else { | ||
| 1061 | dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid); | ||
| 1062 | } | ||
| 1063 | // These get overwritten after generating the machine code. These values are | ||
| 1064 | // "relocations" and have to be in this fixed place so that functions can be | ||
| 1065 | // moved in virtual address space. | ||
| 1066 | assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len); | ||
| 1067 | dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT.low_pc, DW.FORM.addr | ||
| 1068 | assert(getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len); | ||
| 1069 | dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 | ||
| 1070 | if (fn_ret_has_bits) { | ||
| 1071 | const gop = try dbg_info_type_relocs.getOrPut(allocator, fn_ret_type); | ||
| 1072 | if (!gop.found_existing) { | ||
| 1073 | gop.value_ptr.* = .{ | ||
| 1074 | .off = undefined, | ||
| 1075 | .relocs = .{}, | ||
| 1076 | }; | ||
| 1077 | } | ||
| 1078 | try gop.value_ptr.relocs.append(allocator, @intCast(u32, dbg_info_buffer.items.len)); | ||
| 1079 | dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 | ||
| 1080 | } | ||
| 1081 | dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string | ||
| 1082 | }, | ||
| 1083 | else => { | ||
| 1084 | // TODO implement .debug_info for global variables | ||
| 1085 | }, | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | return DeclDebugBuffers{ | ||
| 1089 | .dbg_info_buffer = dbg_info_buffer, | ||
| 1090 | .dbg_line_buffer = dbg_line_buffer, | ||
| 1091 | .dbg_info_type_relocs = dbg_info_type_relocs, | ||
| 1092 | }; | ||
| 1093 | } | 657 | } |
| 1094 | 658 | ||
| 1095 | pub fn commitDeclDebugInfo( | 659 | pub fn commitDeclDebugInfo( |
| 1096 | self: *DebugSymbols, | 660 | self: *DebugSymbols, |
| 1097 | allocator: Allocator, | ||
| 1098 | module: *Module, | 661 | module: *Module, |
| 1099 | decl: *Module.Decl, | 662 | decl: *Module.Decl, |
| 1100 | debug_buffers: *DeclDebugBuffers, | 663 | debug_buffers: *Dwarf.DeclDebugBuffers, |
| 1101 | ) !void { | 664 | ) !void { |
| 1102 | const tracy = trace(@src()); | ||
| 1103 | defer tracy.end(); | ||
| 1104 | |||
| 1105 | var dbg_line_buffer = &debug_buffers.dbg_line_buffer; | ||
| 1106 | var dbg_info_buffer = &debug_buffers.dbg_info_buffer; | ||
| 1107 | var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs; | ||
| 1108 | |||
| 1109 | const symbol = self.base.locals.items[decl.link.macho.local_sym_index]; | 665 | const symbol = self.base.locals.items[decl.link.macho.local_sym_index]; |
| 1110 | const text_block = &decl.link.macho; | 666 | const atom = &decl.link.macho; |
| 1111 | // If the Decl is a function, we need to update the __debug_line program. | 667 | return self.dwarf.commitDeclDebugInfo(&self.base.base, module, decl, symbol.n_value, atom.size, debug_buffers); |
| 1112 | assert(decl.has_tv); | ||
| 1113 | switch (decl.ty.zigTypeTag()) { | ||
| 1114 | .Fn => { | ||
| 1115 | // Perform the relocations based on vaddr. | ||
| 1116 | { | ||
| 1117 | const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8]; | ||
| 1118 | mem.writeIntLittle(u64, ptr, symbol.n_value); | ||
| 1119 | } | ||
| 1120 | { | ||
| 1121 | const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8]; | ||
| 1122 | mem.writeIntLittle(u64, ptr, symbol.n_value); | ||
| 1123 | } | ||
| 1124 | { | ||
| 1125 | const ptr = dbg_info_buffer.items[getRelocDbgInfoSubprogramHighPC()..][0..4]; | ||
| 1126 | mem.writeIntLittle(u32, ptr, @intCast(u32, text_block.size)); | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence }); | ||
| 1130 | |||
| 1131 | // Now we have the full contents and may allocate a region to store it. | ||
| 1132 | |||
| 1133 | // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for | ||
| 1134 | // `TextBlock` and the .debug_info. If you are editing this logic, you | ||
| 1135 | // probably need to edit that logic too. | ||
| 1136 | |||
| 1137 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | ||
| 1138 | const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?]; | ||
| 1139 | const src_fn = &decl.fn_link.macho; | ||
| 1140 | src_fn.len = @intCast(u32, dbg_line_buffer.items.len); | ||
| 1141 | if (self.dbg_line_fn_last) |last| blk: { | ||
| 1142 | if (src_fn == last) break :blk; | ||
| 1143 | if (src_fn.next) |next| { | ||
| 1144 | // Update existing function - non-last item. | ||
| 1145 | if (src_fn.off + src_fn.len + min_nop_size > next.off) { | ||
| 1146 | // It grew too big, so we move it to a new location. | ||
| 1147 | if (src_fn.prev) |prev| { | ||
| 1148 | self.dbg_line_fn_free_list.put(allocator, prev, {}) catch {}; | ||
| 1149 | prev.next = src_fn.next; | ||
| 1150 | } | ||
| 1151 | next.prev = src_fn.prev; | ||
| 1152 | src_fn.next = null; | ||
| 1153 | // Populate where it used to be with NOPs. | ||
| 1154 | const file_pos = debug_line_sect.offset + src_fn.off; | ||
| 1155 | try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos); | ||
| 1156 | // TODO Look at the free list before appending at the end. | ||
| 1157 | src_fn.prev = last; | ||
| 1158 | last.next = src_fn; | ||
| 1159 | self.dbg_line_fn_last = src_fn; | ||
| 1160 | |||
| 1161 | src_fn.off = last.off + padToIdeal(last.len); | ||
| 1162 | } | ||
| 1163 | } else if (src_fn.prev == null) { | ||
| 1164 | // Append new function. | ||
| 1165 | // TODO Look at the free list before appending at the end. | ||
| 1166 | src_fn.prev = last; | ||
| 1167 | last.next = src_fn; | ||
| 1168 | self.dbg_line_fn_last = src_fn; | ||
| 1169 | |||
| 1170 | src_fn.off = last.off + padToIdeal(last.len); | ||
| 1171 | } | ||
| 1172 | } else { | ||
| 1173 | // This is the first function of the Line Number Program. | ||
| 1174 | self.dbg_line_fn_first = src_fn; | ||
| 1175 | self.dbg_line_fn_last = src_fn; | ||
| 1176 | |||
| 1177 | src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(module)); | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | const last_src_fn = self.dbg_line_fn_last.?; | ||
| 1181 | const needed_size = last_src_fn.off + last_src_fn.len; | ||
| 1182 | if (needed_size != debug_line_sect.size) { | ||
| 1183 | if (needed_size > self.allocatedSize(debug_line_sect.offset)) { | ||
| 1184 | const new_offset = self.findFreeSpace(needed_size, 1); | ||
| 1185 | const existing_size = last_src_fn.off; | ||
| 1186 | |||
| 1187 | log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{ | ||
| 1188 | existing_size, | ||
| 1189 | debug_line_sect.offset, | ||
| 1190 | new_offset, | ||
| 1191 | }); | ||
| 1192 | |||
| 1193 | try MachO.copyRangeAllOverlappingAlloc( | ||
| 1194 | self.base.base.allocator, | ||
| 1195 | self.file, | ||
| 1196 | debug_line_sect.offset, | ||
| 1197 | new_offset, | ||
| 1198 | existing_size, | ||
| 1199 | ); | ||
| 1200 | |||
| 1201 | debug_line_sect.offset = @intCast(u32, new_offset); | ||
| 1202 | debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff; | ||
| 1203 | } | ||
| 1204 | debug_line_sect.size = needed_size; | ||
| 1205 | self.load_commands_dirty = true; // TODO look into making only the one section dirty | ||
| 1206 | self.debug_line_header_dirty = true; | ||
| 1207 | } | ||
| 1208 | const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0; | ||
| 1209 | const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0; | ||
| 1210 | |||
| 1211 | // We only have support for one compilation unit so far, so the offsets are directly | ||
| 1212 | // from the .debug_line section. | ||
| 1213 | const file_pos = debug_line_sect.offset + src_fn.off; | ||
| 1214 | try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos); | ||
| 1215 | |||
| 1216 | // .debug_info - End the TAG.subprogram children. | ||
| 1217 | try dbg_info_buffer.append(0); | ||
| 1218 | }, | ||
| 1219 | else => {}, | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | if (dbg_info_buffer.items.len == 0) | ||
| 1223 | return; | ||
| 1224 | |||
| 1225 | // We need this for the duration of this function only so that for composite | ||
| 1226 | // types such as []const u32, if the type *u32 is non-existent, we create | ||
| 1227 | // it synthetically and store the backing bytes in this arena. After we are | ||
| 1228 | // done with the relocations, we can safely deinit the entire memory slab. | ||
| 1229 | // TODO currently, we do not store the relocations for future use, however, | ||
| 1230 | // if that is the case, we should move memory management to a higher scope, | ||
| 1231 | // such as linker scope, or whatnot. | ||
| 1232 | var dbg_type_arena = std.heap.ArenaAllocator.init(allocator); | ||
| 1233 | defer dbg_type_arena.deinit(); | ||
| 1234 | |||
| 1235 | { | ||
| 1236 | // Now we emit the .debug_info types of the Decl. These will count towards the size of | ||
| 1237 | // the buffer, so we have to do it before computing the offset, and we can't perform the actual | ||
| 1238 | // relocations yet. | ||
| 1239 | var it: usize = 0; | ||
| 1240 | while (it < dbg_info_type_relocs.count()) : (it += 1) { | ||
| 1241 | const ty = dbg_info_type_relocs.keys()[it]; | ||
| 1242 | const value_ptr = dbg_info_type_relocs.getPtr(ty).?; | ||
| 1243 | value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); | ||
| 1244 | try self.addDbgInfoType(dbg_type_arena.allocator(), ty, dbg_info_buffer, dbg_info_type_relocs); | ||
| 1245 | } | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | try self.updateDeclDebugInfoAllocation(allocator, text_block, @intCast(u32, dbg_info_buffer.items.len)); | ||
| 1249 | |||
| 1250 | { | ||
| 1251 | // Now that we have the offset assigned we can finally perform type relocations. | ||
| 1252 | for (dbg_info_type_relocs.values()) |value| { | ||
| 1253 | for (value.relocs.items) |off| { | ||
| 1254 | mem.writeIntLittle( | ||
| 1255 | u32, | ||
| 1256 | dbg_info_buffer.items[off..][0..4], | ||
| 1257 | text_block.dbg_info_off + value.off, | ||
| 1258 | ); | ||
| 1259 | } | ||
| 1260 | } | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items); | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | /// Asserts the type has codegen bits. | ||
| 1267 | fn addDbgInfoType( | ||
| 1268 | self: *DebugSymbols, | ||
| 1269 | arena: Allocator, | ||
| 1270 | ty: Type, | ||
| 1271 | dbg_info_buffer: *std.ArrayList(u8), | ||
| 1272 | dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable, | ||
| 1273 | ) !void { | ||
| 1274 | const target = self.base.base.options.target; | ||
| 1275 | var relocs = std.ArrayList(struct { ty: Type, reloc: u32 }).init(arena); | ||
| 1276 | |||
| 1277 | switch (ty.zigTypeTag()) { | ||
| 1278 | .NoReturn => unreachable, | ||
| 1279 | .Void => { | ||
| 1280 | try dbg_info_buffer.append(abbrev_pad1); | ||
| 1281 | }, | ||
| 1282 | .Bool => { | ||
| 1283 | try dbg_info_buffer.appendSlice(&[_]u8{ | ||
| 1284 | abbrev_base_type, | ||
| 1285 | DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 | ||
| 1286 | 1, // DW.AT.byte_size, DW.FORM.data1 | ||
| 1287 | 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string | ||
| 1288 | }); | ||
| 1289 | }, | ||
| 1290 | .Int => { | ||
| 1291 | const info = ty.intInfo(target); | ||
| 1292 | try dbg_info_buffer.ensureUnusedCapacity(12); | ||
| 1293 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); | ||
| 1294 | // DW.AT.encoding, DW.FORM.data1 | ||
| 1295 | dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { | ||
| 1296 | .signed => DW.ATE.signed, | ||
| 1297 | .unsigned => DW.ATE.unsigned, | ||
| 1298 | }); | ||
| 1299 | // DW.AT.byte_size, DW.FORM.data1 | ||
| 1300 | dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); | ||
| 1301 | // DW.AT.name, DW.FORM.string | ||
| 1302 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 1303 | }, | ||
| 1304 | .Optional => { | ||
| 1305 | if (ty.isPtrLikeOptional()) { | ||
| 1306 | try dbg_info_buffer.ensureUnusedCapacity(12); | ||
| 1307 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); | ||
| 1308 | // DW.AT.encoding, DW.FORM.data1 | ||
| 1309 | dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); | ||
| 1310 | // DW.AT.byte_size, DW.FORM.data1 | ||
| 1311 | dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); | ||
| 1312 | // DW.AT.name, DW.FORM.string | ||
| 1313 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 1314 | } else { | ||
| 1315 | // Non-pointer optionals are structs: struct { .maybe = *, .val = * } | ||
| 1316 | var buf = try arena.create(Type.Payload.ElemType); | ||
| 1317 | const payload_ty = ty.optionalChild(buf); | ||
| 1318 | // DW.AT.structure_type | ||
| 1319 | try dbg_info_buffer.append(abbrev_struct_type); | ||
| 1320 | // DW.AT.byte_size, DW.FORM.sdata | ||
| 1321 | const abi_size = ty.abiSize(target); | ||
| 1322 | try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); | ||
| 1323 | // DW.AT.name, DW.FORM.string | ||
| 1324 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 1325 | // DW.AT.member | ||
| 1326 | try dbg_info_buffer.ensureUnusedCapacity(7); | ||
| 1327 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 1328 | // DW.AT.name, DW.FORM.string | ||
| 1329 | dbg_info_buffer.appendSliceAssumeCapacity("maybe"); | ||
| 1330 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 1331 | // DW.AT.type, DW.FORM.ref4 | ||
| 1332 | var index = dbg_info_buffer.items.len; | ||
| 1333 | try dbg_info_buffer.resize(index + 4); | ||
| 1334 | try relocs.append(.{ .ty = Type.bool, .reloc = @intCast(u32, index) }); | ||
| 1335 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 1336 | try dbg_info_buffer.ensureUnusedCapacity(6); | ||
| 1337 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 1338 | // DW.AT.member | ||
| 1339 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 1340 | // DW.AT.name, DW.FORM.string | ||
| 1341 | dbg_info_buffer.appendSliceAssumeCapacity("val"); | ||
| 1342 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 1343 | // DW.AT.type, DW.FORM.ref4 | ||
| 1344 | index = dbg_info_buffer.items.len; | ||
| 1345 | try dbg_info_buffer.resize(index + 4); | ||
| 1346 | try relocs.append(.{ .ty = payload_ty, .reloc = @intCast(u32, index) }); | ||
| 1347 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 1348 | const offset = abi_size - payload_ty.abiSize(target); | ||
| 1349 | try leb128.writeULEB128(dbg_info_buffer.writer(), offset); | ||
| 1350 | // DW.AT.structure_type delimit children | ||
| 1351 | try dbg_info_buffer.append(0); | ||
| 1352 | } | ||
| 1353 | }, | ||
| 1354 | .Pointer => { | ||
| 1355 | if (ty.isSlice()) { | ||
| 1356 | // Slices are structs: struct { .ptr = *, .len = N } | ||
| 1357 | // DW.AT.structure_type | ||
| 1358 | try dbg_info_buffer.ensureUnusedCapacity(2); | ||
| 1359 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_type); | ||
| 1360 | // DW.AT.byte_size, DW.FORM.sdata | ||
| 1361 | dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2); | ||
| 1362 | // DW.AT.name, DW.FORM.string | ||
| 1363 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 1364 | // DW.AT.member | ||
| 1365 | try dbg_info_buffer.ensureUnusedCapacity(5); | ||
| 1366 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 1367 | // DW.AT.name, DW.FORM.string | ||
| 1368 | dbg_info_buffer.appendSliceAssumeCapacity("ptr"); | ||
| 1369 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 1370 | // DW.AT.type, DW.FORM.ref4 | ||
| 1371 | var index = dbg_info_buffer.items.len; | ||
| 1372 | try dbg_info_buffer.resize(index + 4); | ||
| 1373 | var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); | ||
| 1374 | const ptr_ty = ty.slicePtrFieldType(buf); | ||
| 1375 | try relocs.append(.{ .ty = ptr_ty, .reloc = @intCast(u32, index) }); | ||
| 1376 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 1377 | try dbg_info_buffer.ensureUnusedCapacity(6); | ||
| 1378 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 1379 | // DW.AT.member | ||
| 1380 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 1381 | // DW.AT.name, DW.FORM.string | ||
| 1382 | dbg_info_buffer.appendSliceAssumeCapacity("len"); | ||
| 1383 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 1384 | // DW.AT.type, DW.FORM.ref4 | ||
| 1385 | index = dbg_info_buffer.items.len; | ||
| 1386 | try dbg_info_buffer.resize(index + 4); | ||
| 1387 | try relocs.append(.{ .ty = Type.initTag(.usize), .reloc = @intCast(u32, index) }); | ||
| 1388 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 1389 | try dbg_info_buffer.ensureUnusedCapacity(2); | ||
| 1390 | dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize)); | ||
| 1391 | // DW.AT.structure_type delimit children | ||
| 1392 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 1393 | } else { | ||
| 1394 | try dbg_info_buffer.ensureUnusedCapacity(5); | ||
| 1395 | dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type); | ||
| 1396 | // DW.AT.type, DW.FORM.ref4 | ||
| 1397 | const index = dbg_info_buffer.items.len; | ||
| 1398 | try dbg_info_buffer.resize(index + 4); | ||
| 1399 | try relocs.append(.{ .ty = ty.childType(), .reloc = @intCast(u32, index) }); | ||
| 1400 | } | ||
| 1401 | }, | ||
| 1402 | .Struct => blk: { | ||
| 1403 | // try dbg_info_buffer.ensureUnusedCapacity(23); | ||
| 1404 | // DW.AT.structure_type | ||
| 1405 | try dbg_info_buffer.append(abbrev_struct_type); | ||
| 1406 | // DW.AT.byte_size, DW.FORM.sdata | ||
| 1407 | const abi_size = ty.abiSize(target); | ||
| 1408 | try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); | ||
| 1409 | // DW.AT.name, DW.FORM.string | ||
| 1410 | const struct_name = try ty.nameAlloc(arena); | ||
| 1411 | try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); | ||
| 1412 | dbg_info_buffer.appendSliceAssumeCapacity(struct_name); | ||
| 1413 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 1414 | |||
| 1415 | const struct_obj = ty.castTag(.@"struct").?.data; | ||
| 1416 | if (struct_obj.layout == .Packed) { | ||
| 1417 | log.debug("TODO implement .debug_info for packed structs", .{}); | ||
| 1418 | break :blk; | ||
| 1419 | } | ||
| 1420 | |||
| 1421 | const fields = ty.structFields(); | ||
| 1422 | for (fields.keys()) |field_name, field_index| { | ||
| 1423 | const field = fields.get(field_name).?; | ||
| 1424 | // DW.AT.member | ||
| 1425 | try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); | ||
| 1426 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); | ||
| 1427 | // DW.AT.name, DW.FORM.string | ||
| 1428 | dbg_info_buffer.appendSliceAssumeCapacity(field_name); | ||
| 1429 | dbg_info_buffer.appendAssumeCapacity(0); | ||
| 1430 | // DW.AT.type, DW.FORM.ref4 | ||
| 1431 | var index = dbg_info_buffer.items.len; | ||
| 1432 | try dbg_info_buffer.resize(index + 4); | ||
| 1433 | try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); | ||
| 1434 | // DW.AT.data_member_location, DW.FORM.sdata | ||
| 1435 | const field_off = ty.structFieldOffset(field_index, target); | ||
| 1436 | try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); | ||
| 1437 | } | ||
| 1438 | |||
| 1439 | // DW.AT.structure_type delimit children | ||
| 1440 | try dbg_info_buffer.append(0); | ||
| 1441 | }, | ||
| 1442 | else => { | ||
| 1443 | log.debug("TODO implement .debug_info for type '{}'", .{ty}); | ||
| 1444 | try dbg_info_buffer.append(abbrev_pad1); | ||
| 1445 | }, | ||
| 1446 | } | ||
| 1447 | |||
| 1448 | for (relocs.items) |rel| { | ||
| 1449 | const gop = try dbg_info_type_relocs.getOrPut(self.base.base.allocator, rel.ty); | ||
| 1450 | if (!gop.found_existing) { | ||
| 1451 | gop.value_ptr.* = .{ | ||
| 1452 | .off = undefined, | ||
| 1453 | .relocs = .{}, | ||
| 1454 | }; | ||
| 1455 | } | ||
| 1456 | try gop.value_ptr.relocs.append(self.base.base.allocator, rel.reloc); | ||
| 1457 | } | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | fn updateDeclDebugInfoAllocation( | ||
| 1461 | self: *DebugSymbols, | ||
| 1462 | allocator: Allocator, | ||
| 1463 | text_block: *TextBlock, | ||
| 1464 | len: u32, | ||
| 1465 | ) !void { | ||
| 1466 | const tracy = trace(@src()); | ||
| 1467 | defer tracy.end(); | ||
| 1468 | |||
| 1469 | // This logic is nearly identical to the logic above in `updateDecl` for | ||
| 1470 | // `SrcFn` and the line number programs. If you are editing this logic, you | ||
| 1471 | // probably need to edit that logic too. | ||
| 1472 | |||
| 1473 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | ||
| 1474 | const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?]; | ||
| 1475 | text_block.dbg_info_len = len; | ||
| 1476 | if (self.dbg_info_decl_last) |last| blk: { | ||
| 1477 | if (text_block == last) break :blk; | ||
| 1478 | if (text_block.dbg_info_next) |next| { | ||
| 1479 | // Update existing Decl - non-last item. | ||
| 1480 | if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) { | ||
| 1481 | // It grew too big, so we move it to a new location. | ||
| 1482 | if (text_block.dbg_info_prev) |prev| { | ||
| 1483 | self.dbg_info_decl_free_list.put(allocator, prev, {}) catch {}; | ||
| 1484 | prev.dbg_info_next = text_block.dbg_info_next; | ||
| 1485 | } | ||
| 1486 | next.dbg_info_prev = text_block.dbg_info_prev; | ||
| 1487 | text_block.dbg_info_next = null; | ||
| 1488 | // Populate where it used to be with NOPs. | ||
| 1489 | const file_pos = debug_info_sect.offset + text_block.dbg_info_off; | ||
| 1490 | try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos); | ||
| 1491 | // TODO Look at the free list before appending at the end. | ||
| 1492 | text_block.dbg_info_prev = last; | ||
| 1493 | last.dbg_info_next = text_block; | ||
| 1494 | self.dbg_info_decl_last = text_block; | ||
| 1495 | |||
| 1496 | text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len); | ||
| 1497 | } | ||
| 1498 | } else if (text_block.dbg_info_prev == null) { | ||
| 1499 | // Append new Decl. | ||
| 1500 | // TODO Look at the free list before appending at the end. | ||
| 1501 | text_block.dbg_info_prev = last; | ||
| 1502 | last.dbg_info_next = text_block; | ||
| 1503 | self.dbg_info_decl_last = text_block; | ||
| 1504 | |||
| 1505 | text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len); | ||
| 1506 | } | ||
| 1507 | } else { | ||
| 1508 | // This is the first Decl of the .debug_info | ||
| 1509 | self.dbg_info_decl_first = text_block; | ||
| 1510 | self.dbg_info_decl_last = text_block; | ||
| 1511 | |||
| 1512 | text_block.dbg_info_off = padToIdeal(self.dbgInfoNeededHeaderBytes()); | ||
| 1513 | } | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | fn writeDeclDebugInfo(self: *DebugSymbols, text_block: *TextBlock, dbg_info_buf: []const u8) !void { | ||
| 1517 | const tracy = trace(@src()); | ||
| 1518 | defer tracy.end(); | ||
| 1519 | |||
| 1520 | // This logic is nearly identical to the logic above in `updateDecl` for | ||
| 1521 | // `SrcFn` and the line number programs. If you are editing this logic, you | ||
| 1522 | // probably need to edit that logic too. | ||
| 1523 | |||
| 1524 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | ||
| 1525 | const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?]; | ||
| 1526 | |||
| 1527 | const last_decl = self.dbg_info_decl_last.?; | ||
| 1528 | // +1 for a trailing zero to end the children of the decl tag. | ||
| 1529 | const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1; | ||
| 1530 | if (needed_size != debug_info_sect.size) { | ||
| 1531 | if (needed_size > self.allocatedSize(debug_info_sect.offset)) { | ||
| 1532 | const new_offset = self.findFreeSpace(needed_size, 1); | ||
| 1533 | const existing_size = last_decl.dbg_info_off; | ||
| 1534 | |||
| 1535 | log.debug("moving __debug_info section: {} bytes from 0x{x} to 0x{x}", .{ | ||
| 1536 | existing_size, | ||
| 1537 | debug_info_sect.offset, | ||
| 1538 | new_offset, | ||
| 1539 | }); | ||
| 1540 | |||
| 1541 | try MachO.copyRangeAllOverlappingAlloc( | ||
| 1542 | self.base.base.allocator, | ||
| 1543 | self.file, | ||
| 1544 | debug_info_sect.offset, | ||
| 1545 | new_offset, | ||
| 1546 | existing_size, | ||
| 1547 | ); | ||
| 1548 | |||
| 1549 | debug_info_sect.offset = @intCast(u32, new_offset); | ||
| 1550 | debug_info_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff; | ||
| 1551 | } | ||
| 1552 | debug_info_sect.size = needed_size; | ||
| 1553 | self.load_commands_dirty = true; // TODO look into making only the one section dirty | ||
| 1554 | self.debug_info_header_dirty = true; | ||
| 1555 | } | ||
| 1556 | const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev| | ||
| 1557 | text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len) | ||
| 1558 | else | ||
| 1559 | 0; | ||
| 1560 | const next_padding_size: u32 = if (text_block.dbg_info_next) |next| | ||
| 1561 | next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len) | ||
| 1562 | else | ||
| 1563 | 0; | ||
| 1564 | |||
| 1565 | // To end the children of the decl tag. | ||
| 1566 | const trailing_zero = text_block.dbg_info_next == null; | ||
| 1567 | |||
| 1568 | // We only have support for one compilation unit so far, so the offsets are directly | ||
| 1569 | // from the .debug_info section. | ||
| 1570 | const file_pos = debug_info_sect.offset + text_block.dbg_info_off; | ||
| 1571 | try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos); | ||
| 1572 | } | ||
| 1573 | |||
| 1574 | fn getDebugLineProgramOff(self: DebugSymbols) u32 { | ||
| 1575 | return self.dbg_line_fn_first.?.off; | ||
| 1576 | } | ||
| 1577 | |||
| 1578 | fn getDebugLineProgramEnd(self: DebugSymbols) u32 { | ||
| 1579 | return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len; | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | /// TODO Improve this to use a table. | ||
| 1583 | fn makeDebugString(self: *DebugSymbols, allocator: Allocator, bytes: []const u8) !u32 { | ||
| 1584 | try self.debug_string_table.ensureUnusedCapacity(allocator, bytes.len + 1); | ||
| 1585 | const result = self.debug_string_table.items.len; | ||
| 1586 | self.debug_string_table.appendSliceAssumeCapacity(bytes); | ||
| 1587 | self.debug_string_table.appendAssumeCapacity(0); | ||
| 1588 | return @intCast(u32, result); | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | /// The reloc offset for the line offset of a function from the previous function's line. | ||
| 1592 | /// It's a fixed-size 4-byte ULEB128. | ||
| 1593 | fn getRelocDbgLineOff() usize { | ||
| 1594 | return dbg_line_vaddr_reloc_index + @sizeOf(u64) + 1; | ||
| 1595 | } | ||
| 1596 | |||
| 1597 | fn getRelocDbgFileIndex() usize { | ||
| 1598 | return getRelocDbgLineOff() + 5; | ||
| 1599 | } | ||
| 1600 | |||
| 1601 | fn getRelocDbgInfoSubprogramHighPC() u32 { | ||
| 1602 | return dbg_info_low_pc_reloc_index + @sizeOf(u64); | ||
| 1603 | } | ||
| 1604 | |||
| 1605 | fn dbgLineNeededHeaderBytes(self: DebugSymbols, module: *Module) u32 { | ||
| 1606 | _ = self; | ||
| 1607 | const directory_entry_format_count = 1; | ||
| 1608 | const file_name_entry_format_count = 1; | ||
| 1609 | const directory_count = 1; | ||
| 1610 | const file_name_count = 1; | ||
| 1611 | const root_src_dir_path_len = if (module.root_pkg.root_src_directory.path) |p| p.len else 1; // "." | ||
| 1612 | return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + | ||
| 1613 | directory_count * 8 + file_name_count * 8 + | ||
| 1614 | // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like | ||
| 1615 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. | ||
| 1616 | root_src_dir_path_len + | ||
| 1617 | module.root_pkg.root_src_path.len); | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | fn dbgInfoNeededHeaderBytes(self: DebugSymbols) u32 { | ||
| 1621 | _ = self; | ||
| 1622 | return 120; | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | /// Writes to the file a buffer, prefixed and suffixed by the specified number of | ||
| 1626 | /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes | ||
| 1627 | /// are less than 126,976 bytes (if this limit is ever reached, this function can be | ||
| 1628 | /// improved to make more than one pwritev call, or the limit can be raised by a fixed | ||
| 1629 | /// amount by increasing the length of `vecs`). | ||
| 1630 | fn pwriteDbgLineNops( | ||
| 1631 | self: *DebugSymbols, | ||
| 1632 | prev_padding_size: usize, | ||
| 1633 | buf: []const u8, | ||
| 1634 | next_padding_size: usize, | ||
| 1635 | offset: u64, | ||
| 1636 | ) !void { | ||
| 1637 | const tracy = trace(@src()); | ||
| 1638 | defer tracy.end(); | ||
| 1639 | |||
| 1640 | const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096; | ||
| 1641 | const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 }; | ||
| 1642 | var vecs: [32]std.os.iovec_const = undefined; | ||
| 1643 | var vec_index: usize = 0; | ||
| 1644 | { | ||
| 1645 | var padding_left = prev_padding_size; | ||
| 1646 | if (padding_left % 2 != 0) { | ||
| 1647 | vecs[vec_index] = .{ | ||
| 1648 | .iov_base = &three_byte_nop, | ||
| 1649 | .iov_len = three_byte_nop.len, | ||
| 1650 | }; | ||
| 1651 | vec_index += 1; | ||
| 1652 | padding_left -= three_byte_nop.len; | ||
| 1653 | } | ||
| 1654 | while (padding_left > page_of_nops.len) { | ||
| 1655 | vecs[vec_index] = .{ | ||
| 1656 | .iov_base = &page_of_nops, | ||
| 1657 | .iov_len = page_of_nops.len, | ||
| 1658 | }; | ||
| 1659 | vec_index += 1; | ||
| 1660 | padding_left -= page_of_nops.len; | ||
| 1661 | } | ||
| 1662 | if (padding_left > 0) { | ||
| 1663 | vecs[vec_index] = .{ | ||
| 1664 | .iov_base = &page_of_nops, | ||
| 1665 | .iov_len = padding_left, | ||
| 1666 | }; | ||
| 1667 | vec_index += 1; | ||
| 1668 | } | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | vecs[vec_index] = .{ | ||
| 1672 | .iov_base = buf.ptr, | ||
| 1673 | .iov_len = buf.len, | ||
| 1674 | }; | ||
| 1675 | vec_index += 1; | ||
| 1676 | |||
| 1677 | { | ||
| 1678 | var padding_left = next_padding_size; | ||
| 1679 | if (padding_left % 2 != 0) { | ||
| 1680 | vecs[vec_index] = .{ | ||
| 1681 | .iov_base = &three_byte_nop, | ||
| 1682 | .iov_len = three_byte_nop.len, | ||
| 1683 | }; | ||
| 1684 | vec_index += 1; | ||
| 1685 | padding_left -= three_byte_nop.len; | ||
| 1686 | } | ||
| 1687 | while (padding_left > page_of_nops.len) { | ||
| 1688 | vecs[vec_index] = .{ | ||
| 1689 | .iov_base = &page_of_nops, | ||
| 1690 | .iov_len = page_of_nops.len, | ||
| 1691 | }; | ||
| 1692 | vec_index += 1; | ||
| 1693 | padding_left -= page_of_nops.len; | ||
| 1694 | } | ||
| 1695 | if (padding_left > 0) { | ||
| 1696 | vecs[vec_index] = .{ | ||
| 1697 | .iov_base = &page_of_nops, | ||
| 1698 | .iov_len = padding_left, | ||
| 1699 | }; | ||
| 1700 | vec_index += 1; | ||
| 1701 | } | ||
| 1702 | } | ||
| 1703 | try self.file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | /// Writes to the file a buffer, prefixed and suffixed by the specified number of | ||
| 1707 | /// bytes of padding. | ||
| 1708 | fn pwriteDbgInfoNops( | ||
| 1709 | self: *DebugSymbols, | ||
| 1710 | prev_padding_size: usize, | ||
| 1711 | buf: []const u8, | ||
| 1712 | next_padding_size: usize, | ||
| 1713 | trailing_zero: bool, | ||
| 1714 | offset: u64, | ||
| 1715 | ) !void { | ||
| 1716 | const tracy = trace(@src()); | ||
| 1717 | defer tracy.end(); | ||
| 1718 | |||
| 1719 | const page_of_nops = [1]u8{abbrev_pad1} ** 4096; | ||
| 1720 | var vecs: [32]std.os.iovec_const = undefined; | ||
| 1721 | var vec_index: usize = 0; | ||
| 1722 | { | ||
| 1723 | var padding_left = prev_padding_size; | ||
| 1724 | while (padding_left > page_of_nops.len) { | ||
| 1725 | vecs[vec_index] = .{ | ||
| 1726 | .iov_base = &page_of_nops, | ||
| 1727 | .iov_len = page_of_nops.len, | ||
| 1728 | }; | ||
| 1729 | vec_index += 1; | ||
| 1730 | padding_left -= page_of_nops.len; | ||
| 1731 | } | ||
| 1732 | if (padding_left > 0) { | ||
| 1733 | vecs[vec_index] = .{ | ||
| 1734 | .iov_base = &page_of_nops, | ||
| 1735 | .iov_len = padding_left, | ||
| 1736 | }; | ||
| 1737 | vec_index += 1; | ||
| 1738 | } | ||
| 1739 | } | ||
| 1740 | |||
| 1741 | vecs[vec_index] = .{ | ||
| 1742 | .iov_base = buf.ptr, | ||
| 1743 | .iov_len = buf.len, | ||
| 1744 | }; | ||
| 1745 | vec_index += 1; | ||
| 1746 | |||
| 1747 | { | ||
| 1748 | var padding_left = next_padding_size; | ||
| 1749 | while (padding_left > page_of_nops.len) { | ||
| 1750 | vecs[vec_index] = .{ | ||
| 1751 | .iov_base = &page_of_nops, | ||
| 1752 | .iov_len = page_of_nops.len, | ||
| 1753 | }; | ||
| 1754 | vec_index += 1; | ||
| 1755 | padding_left -= page_of_nops.len; | ||
| 1756 | } | ||
| 1757 | if (padding_left > 0) { | ||
| 1758 | vecs[vec_index] = .{ | ||
| 1759 | .iov_base = &page_of_nops, | ||
| 1760 | .iov_len = padding_left, | ||
| 1761 | }; | ||
| 1762 | vec_index += 1; | ||
| 1763 | } | ||
| 1764 | } | ||
| 1765 | |||
| 1766 | if (trailing_zero) { | ||
| 1767 | var zbuf = [1]u8{0}; | ||
| 1768 | vecs[vec_index] = .{ | ||
| 1769 | .iov_base = &zbuf, | ||
| 1770 | .iov_len = zbuf.len, | ||
| 1771 | }; | ||
| 1772 | vec_index += 1; | ||
| 1773 | } | ||
| 1774 | |||
| 1775 | try self.file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); | ||
| 1776 | } | 668 | } |