| 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 | 4439 | }; |
| 4440 | 4440 | decl.fn_link = switch (mod.comp.bin_file.tag) { |
| 4441 | 4441 | .coff => .{ .coff = {} }, |
| 4442 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, | |
| 4443 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, | |
| 4442 | .elf => .{ .elf = link.File.Dwarf.SrcFn.empty }, | |
| 4443 | .macho => .{ .macho = link.File.Dwarf.SrcFn.empty }, | |
| 4444 | 4444 | .plan9 => .{ .plan9 = {} }, |
| 4445 | 4445 | .c => .{ .c = {} }, |
| 4446 | 4446 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, |
| ... | ... | @@ -4776,8 +4776,8 @@ pub fn allocateNewDecl( |
| 4776 | 4776 | }, |
| 4777 | 4777 | .fn_link = switch (mod.comp.bin_file.tag) { |
| 4778 | 4778 | .coff => .{ .coff = {} }, |
| 4779 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, | |
| 4780 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, | |
| 4779 | .elf => .{ .elf = link.File.Dwarf.SrcFn.empty }, | |
| 4780 | .macho => .{ .macho = link.File.Dwarf.SrcFn.empty }, | |
| 4781 | 4781 | .plan9 => .{ .plan9 = {} }, |
| 4782 | 4782 | .c => .{ .c = {} }, |
| 4783 | 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 | 409 | switch (self.debug_output) { |
| 410 | 410 | .dwarf => |dbg_out| { |
| 411 | 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 | 413 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 414 | 414 | 1, // ULEB128 dwarf expression length |
| 415 | 415 | reg.dwarfLocOp(), |
| ... | ... | @@ -440,7 +440,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { |
| 440 | 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 | 445 | // Get length of the LEB128 stack offset |
| 446 | 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 | 1384 | switch (self.debug_output) { |
| 1385 | 1385 | .dwarf => |dbg_out| { |
| 1386 | 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 | 1388 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 1389 | 1389 | 1, // ULEB128 dwarf expression length |
| 1390 | 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 | 1019 | switch (emit.debug_output) { |
| 1020 | 1020 | .dwarf => |dbg_out| { |
| 1021 | 1021 | try dbg_out.dbg_info.ensureUnusedCapacity(3); |
| 1022 | ||
| 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 | ||
| 1022 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); | |
| 1030 | 1023 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 1031 | 1024 | 1, // ULEB128 dwarf expression length |
| 1032 | 1025 | reg.dwarfLocOp(), |
| ... | ... | @@ -1049,14 +1042,7 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 |
| 1049 | 1042 | // for example when -fomit-frame-pointer is set. |
| 1050 | 1043 | const disp = @intCast(i32, max_stack) - off + 16; |
| 1051 | 1044 | try dbg_out.dbg_info.ensureUnusedCapacity(8); |
| 1052 | ||
| 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 | ||
| 1045 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); | |
| 1060 | 1046 | const fixup = dbg_out.dbg_info.items.len; |
| 1061 | 1047 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 1062 | 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 | 221 | }; |
| 222 | 222 | |
| 223 | 223 | pub const LinkFn = union { |
| 224 | elf: Elf.SrcFn, | |
| 224 | elf: Dwarf.SrcFn, | |
| 225 | 225 | coff: Coff.SrcFn, |
| 226 | macho: MachO.SrcFn, | |
| 226 | macho: Dwarf.SrcFn, | |
| 227 | 227 | plan9: void, |
| 228 | 228 | c: void, |
| 229 | 229 | wasm: Wasm.FnData, |
| ... | ... | @@ -915,6 +915,7 @@ pub const File = struct { |
| 915 | 915 | pub const SpirV = @import("link/SpirV.zig"); |
| 916 | 916 | pub const Wasm = @import("link/Wasm.zig"); |
| 917 | 917 | pub const NvPtx = @import("link/NvPtx.zig"); |
| 918 | pub const Dwarf = @import("link/Dwarf.zig"); | |
| 918 | 919 | }; |
| 919 | 920 | |
| 920 | 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 | 9 | const fs = std.fs; |
| 10 | 10 | const elf = std.elf; |
| 11 | 11 | const log = std.log.scoped(.link); |
| 12 | const DW = std.dwarf; | |
| 13 | const leb128 = std.leb; | |
| 14 | 12 | |
| 15 | 13 | const Module = @import("../Module.zig"); |
| 16 | 14 | const Compilation = @import("../Compilation.zig"); |
| 15 | const Dwarf = @import("Dwarf.zig"); | |
| 17 | 16 | const codegen = @import("../codegen.zig"); |
| 18 | 17 | const lldMain = @import("../main.zig").lldMain; |
| 19 | 18 | const trace = @import("../tracy.zig").trace; |
| ... | ... | @@ -37,6 +36,7 @@ const default_entry_addr = 0x8000000; |
| 37 | 36 | pub const base_tag: File.Tag = .elf; |
| 38 | 37 | |
| 39 | 38 | base: File, |
| 39 | dwarf: ?Dwarf = null, | |
| 40 | 40 | |
| 41 | 41 | ptr_width: PtrWidth, |
| 42 | 42 | |
| ... | ... | @@ -67,7 +67,6 @@ phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, |
| 67 | 67 | entry_addr: ?u64 = null, |
| 68 | 68 | page_size: u16, |
| 69 | 69 | |
| 70 | debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, | |
| 71 | 70 | shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, |
| 72 | 71 | shstrtab_index: ?u16 = null, |
| 73 | 72 | |
| ... | ... | @@ -82,8 +81,6 @@ debug_str_section_index: ?u16 = null, |
| 82 | 81 | debug_aranges_section_index: ?u16 = null, |
| 83 | 82 | debug_line_section_index: ?u16 = null, |
| 84 | 83 | |
| 85 | debug_abbrev_table_offset: ?u64 = null, | |
| 86 | ||
| 87 | 84 | /// The same order as in the file. ELF requires global symbols to all be after the |
| 88 | 85 | /// local symbols, they cannot be mixed. So we must buffer all the global symbols and |
| 89 | 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 | 165 | /// with `Decl` `main`, and lives as long as that `Decl`. |
| 169 | 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 | 168 | /// A table of relocations indexed by the owning them `TextBlock`. |
| 184 | 169 | /// Note that once we refactor `TextBlock`'s lifetime and ownership rules, |
| 185 | 170 | /// this will be a table indexed by index into the list of Atoms. |
| ... | ... | @@ -222,24 +207,14 @@ pub const TextBlock = struct { |
| 222 | 207 | prev: ?*TextBlock, |
| 223 | 208 | next: ?*TextBlock, |
| 224 | 209 | |
| 225 | /// Previous/next linked list pointers. | |
| 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, | |
| 210 | dbg_info_atom: Dwarf.DebugInfoAtom, | |
| 233 | 211 | |
| 234 | 212 | pub const empty = TextBlock{ |
| 235 | 213 | .local_sym_index = 0, |
| 236 | 214 | .offset_table_index = undefined, |
| 237 | 215 | .prev = null, |
| 238 | 216 | .next = null, |
| 239 | .dbg_info_prev = null, | |
| 240 | .dbg_info_next = null, | |
| 241 | .dbg_info_off = undefined, | |
| 242 | .dbg_info_len = undefined, | |
| 217 | .dbg_info_atom = undefined, | |
| 243 | 218 | }; |
| 244 | 219 | |
| 245 | 220 | /// Returns how much room there is to grow in virtual address space. |
| ... | ... | @@ -273,26 +248,6 @@ pub const Export = struct { |
| 273 | 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 | 251 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf { |
| 297 | 252 | assert(options.object_format == .elf); |
| 298 | 253 | |
| ... | ... | @@ -351,6 +306,11 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { |
| 351 | 306 | errdefer gpa.destroy(self); |
| 352 | 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 | 314 | self.* = .{ |
| 355 | 315 | .base = .{ |
| 356 | 316 | .tag = .elf, |
| ... | ... | @@ -358,6 +318,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { |
| 358 | 318 | .allocator = gpa, |
| 359 | 319 | .file = null, |
| 360 | 320 | }, |
| 321 | .dwarf = dwarf, | |
| 361 | 322 | .ptr_width = ptr_width, |
| 362 | 323 | .page_size = page_size, |
| 363 | 324 | }; |
| ... | ... | @@ -377,14 +338,11 @@ pub fn deinit(self: *Elf) void { |
| 377 | 338 | self.sections.deinit(self.base.allocator); |
| 378 | 339 | self.program_headers.deinit(self.base.allocator); |
| 379 | 340 | self.shstrtab.deinit(self.base.allocator); |
| 380 | self.debug_strtab.deinit(self.base.allocator); | |
| 381 | 341 | self.local_symbols.deinit(self.base.allocator); |
| 382 | 342 | self.global_symbols.deinit(self.base.allocator); |
| 383 | 343 | self.global_symbol_free_list.deinit(self.base.allocator); |
| 384 | 344 | self.local_symbol_free_list.deinit(self.base.allocator); |
| 385 | 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 | 346 | self.offset_table.deinit(self.base.allocator); |
| 389 | 347 | self.phdr_shdr_table.deinit(self.base.allocator); |
| 390 | 348 | self.decls.deinit(self.base.allocator); |
| ... | ... | @@ -420,6 +378,10 @@ pub fn deinit(self: *Elf) void { |
| 420 | 378 | } |
| 421 | 379 | |
| 422 | 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 | 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 | 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 | 408 | /// Returns end pos of collision, if any. |
| 455 | 409 | fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 456 | 410 | const small_ptr = self.ptr_width == .p32; |
| ... | ... | @@ -497,7 +451,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 497 | 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 | 455 | if (start == 0) |
| 502 | 456 | return 0; |
| 503 | 457 | var min_pos: u64 = std.math.maxInt(u64); |
| ... | ... | @@ -518,7 +472,7 @@ fn allocatedSize(self: *Elf, start: u64) u64 { |
| 518 | 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 | 476 | var start: u64 = 0; |
| 523 | 477 | while (self.detectAllocCollision(start, object_size)) |item_end| { |
| 524 | 478 | start = mem.alignForwardGeneric(u64, item_end, min_alignment); |
| ... | ... | @@ -535,15 +489,6 @@ fn makeString(self: *Elf, bytes: []const u8) !u32 { |
| 535 | 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 | 492 | fn getString(self: *Elf, str_off: u32) []const u8 { |
| 548 | 493 | assert(str_off < self.shstrtab.items.len); |
| 549 | 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 | 751 | |
| 807 | 752 | if (self.debug_str_section_index == null) { |
| 808 | 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 | 755 | try self.sections.append(self.base.allocator, .{ |
| 811 | 756 | .sh_name = try self.makeString(".debug_str"), |
| 812 | 757 | .sh_type = elf.SHT_PROGBITS, |
| 813 | 758 | .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, |
| 814 | 759 | .sh_addr = 0, |
| 815 | 760 | .sh_offset = 0, |
| 816 | .sh_size = self.debug_strtab.items.len, | |
| 761 | .sh_size = 0, | |
| 817 | 762 | .sh_link = 0, |
| 818 | 763 | .sh_info = 0, |
| 819 | 764 | .sh_addralign = 1, |
| ... | ... | @@ -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 | 925 | pub fn flush(self: *Elf, comp: *Compilation) !void { |
| 991 | 926 | if (self.base.options.emit == null) { |
| 992 | 927 | if (build_options.have_llvm) { |
| ... | ... | @@ -1022,11 +957,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1022 | 957 | |
| 1023 | 958 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 1024 | 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 | 962 | var it = self.relocs.iterator(); |
| ... | ... | @@ -1068,349 +998,38 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1068 | 998 | try self.writeAllGlobalSymbols(); |
| 1069 | 999 | |
| 1070 | 1000 | if (self.debug_abbrev_section_dirty) { |
| 1071 | const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?]; | |
| 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); | |
| 1001 | try self.dwarf.?.writeDbgAbbrev(&self.base); | |
| 1172 | 1002 | if (!self.shdr_table_dirty) { |
| 1173 | 1003 | // Then it won't get written with the others and we need to do it. |
| 1174 | 1004 | try self.writeSectHeader(self.debug_abbrev_section_index.?); |
| 1175 | 1005 | } |
| 1176 | ||
| 1177 | 1006 | self.debug_abbrev_section_dirty = false; |
| 1178 | 1007 | } |
| 1179 | 1008 | |
| 1180 | if (self.debug_info_header_dirty) debug_info: { | |
| 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); | |
| 1009 | if (self.debug_info_header_dirty) { | |
| 1225 | 1010 | // Currently only one compilation unit is supported, so the address range is simply |
| 1226 | 1011 | // identical to the main program header virtual address and memory size. |
| 1227 | 1012 | const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 1228 | 1013 | const low_pc = text_phdr.p_vaddr; |
| 1229 | 1014 | const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; |
| 1230 | ||
| 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); | |
| 1015 | try self.dwarf.?.writeDbgInfoHeader(&self.base, module, low_pc, high_pc); | |
| 1249 | 1016 | self.debug_info_header_dirty = false; |
| 1250 | 1017 | } |
| 1251 | 1018 | |
| 1252 | 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 | 1020 | // Currently only one compilation unit is supported, so the address range is simply |
| 1278 | 1021 | // identical to the main program header virtual address and memory size. |
| 1279 | 1022 | const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 1280 | self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr); | |
| 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); | |
| 1023 | try self.dwarf.?.writeDbgAranges(&self.base, text_phdr.p_vaddr, text_phdr.p_memsz); | |
| 1314 | 1024 | if (!self.shdr_table_dirty) { |
| 1315 | 1025 | // Then it won't get written with the others and we need to do it. |
| 1316 | 1026 | try self.writeSectHeader(self.debug_aranges_section_index.?); |
| 1317 | 1027 | } |
| 1318 | ||
| 1319 | 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; | |
| 1398 | switch (self.ptr_width) { | |
| 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); | |
| 1031 | if (self.debug_line_header_dirty) { | |
| 1032 | try self.dwarf.?.writeDbgLineHeader(&self.base, module); | |
| 1414 | 1033 | self.debug_line_header_dirty = false; |
| 1415 | 1034 | } |
| 1416 | 1035 | |
| ... | ... | @@ -1481,11 +1100,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1481 | 1100 | self.shstrtab_dirty = false; |
| 1482 | 1101 | } |
| 1483 | 1102 | } |
| 1103 | ||
| 1484 | 1104 | { |
| 1485 | 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 | 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 | 1111 | if (needed_size > allocated_size) { |
| 1491 | 1112 | debug_strtab_sect.sh_size = 0; // free the space |
| ... | ... | @@ -1494,7 +1115,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1494 | 1115 | debug_strtab_sect.sh_size = needed_size; |
| 1495 | 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 | 1119 | if (!self.shdr_table_dirty) { |
| 1499 | 1120 | // Then it won't get written with the others and we need to do it. |
| 1500 | 1121 | try self.writeSectHeader(self.debug_str_section_index.?); |
| ... | ... | @@ -1502,6 +1123,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1502 | 1123 | self.debug_strtab_dirty = false; |
| 1503 | 1124 | } |
| 1504 | 1125 | } |
| 1126 | ||
| 1505 | 1127 | if (self.shdr_table_dirty) { |
| 1506 | 1128 | const shsize: u64 = switch (self.ptr_width) { |
| 1507 | 1129 | .p32 => @sizeOf(elf.Elf32_Shdr), |
| ... | ... | @@ -2340,7 +1962,6 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { |
| 2340 | 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 | 1966 | if (self.atoms.getPtr(phdr_index)) |last_block| { |
| 2346 | 1967 | if (last_block.* == text_block) { |
| ... | ... | @@ -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 | 1977 | if (text_block.prev) |prev| { |
| 2365 | 1978 | prev.next = text_block.next; |
| 2366 | 1979 | |
| ... | ... | @@ -2379,18 +1992,8 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { |
| 2379 | 1992 | text_block.next = null; |
| 2380 | 1993 | } |
| 2381 | 1994 | |
| 2382 | if (text_block.dbg_info_prev) |prev| { | |
| 2383 | prev.dbg_info_next = text_block.dbg_info_next; | |
| 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; | |
| 1995 | if (self.dwarf) |*dw| { | |
| 1996 | dw.freeAtom(&text_block.dbg_info_atom); | |
| 2394 | 1997 | } |
| 2395 | 1998 | } |
| 2396 | 1999 | |
| ... | ... | @@ -2619,26 +2222,9 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { |
| 2619 | 2222 | |
| 2620 | 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 | |
| 2623 | // is desired for both. | |
| 2624 | _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf); | |
| 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; | |
| 2225 | ||
| 2226 | if (self.dwarf) |*dw| { | |
| 2227 | dw.freeDecl(decl); | |
| 2642 | 2228 | } |
| 2643 | 2229 | } |
| 2644 | 2230 | |
| ... | ... | @@ -2739,62 +2325,6 @@ fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8 |
| 2739 | 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 | 2328 | pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { |
| 2799 | 2329 | if (build_options.skip_non_native and builtin.object_format != .elf) { |
| 2800 | 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 | 2339 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 2810 | 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 | 2342 | const decl = func.owner_decl; |
| 2823 | 2343 | self.freeUnnamedConsts(decl); |
| 2824 | 2344 | |
| 2825 | const decl_name = try decl.getFullyQualifiedName(self.base.allocator); | |
| 2826 | defer self.base.allocator.free(decl_name); | |
| 2827 | ||
| 2828 | log.debug("updateFunc {s}{*}", .{ decl_name, func.owner_decl }); | |
| 2829 | log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ | |
| 2830 | decl.src_line, | |
| 2831 | func.lbrace_line, | |
| 2832 | func.rbrace_line, | |
| 2833 | }); | |
| 2834 | const line = @intCast(u28, decl.src_line + func.lbrace_line); | |
| 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 | }; | |
| 2345 | var debug_buffers_buf: Dwarf.DeclDebugBuffers = undefined; | |
| 2346 | const debug_buffers = if (self.dwarf) |*dw| blk: { | |
| 2347 | debug_buffers_buf = try dw.initDeclDebugInfo(decl); | |
| 2348 | break :blk &debug_buffers_buf; | |
| 2349 | } else null; | |
| 2350 | defer { | |
| 2351 | if (debug_buffers) |dbg| { | |
| 2352 | dbg.dbg_line_buffer.deinit(); | |
| 2353 | dbg.dbg_info_buffer.deinit(); | |
| 2354 | deinitRelocs(self.base.allocator, &dbg.dbg_info_type_relocs); | |
| 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, .{ | |
| 2896 | .dwarf = .{ | |
| 2897 | .dbg_line = &dbg_line_buffer, | |
| 2898 | .dbg_info = &dbg_info_buffer, | |
| 2899 | .dbg_info_type_relocs = &dbg_info_type_relocs, | |
| 2900 | }, | |
| 2901 | }); | |
| 2358 | const res = if (debug_buffers) |dbg| | |
| 2359 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ | |
| 2360 | .dwarf = .{ | |
| 2361 | .dbg_line = &dbg.dbg_line_buffer, | |
| 2362 | .dbg_info = &dbg.dbg_info_buffer, | |
| 2363 | .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, | |
| 2364 | }, | |
| 2365 | }) | |
| 2366 | else | |
| 2367 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); | |
| 2368 | ||
| 2902 | 2369 | const code = switch (res) { |
| 2903 | 2370 | .appended => code_buffer.items, |
| 2904 | 2371 | .fail => |em| { |
| ... | ... | @@ -2907,128 +2374,14 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven |
| 2907 | 2374 | return; |
| 2908 | 2375 | }, |
| 2909 | 2376 | }; |
| 2910 | ||
| 2911 | 2377 | const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC); |
| 2912 | ||
| 2913 | const target_endian = self.base.options.target.cpu.arch.endian(); | |
| 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 | }, | |
| 2378 | if (debug_buffers) |dbg| { | |
| 2379 | try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg); | |
| 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); | |
| 2992 | } | |
| 2993 | } else { | |
| 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); | |
| 2382 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | |
| 2383 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | |
| 2384 | return self.updateDeclExports(module, decl, decl_exports); | |
| 3032 | 2385 | } |
| 3033 | 2386 | |
| 3034 | 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 | 2410 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 3058 | 2411 | defer code_buffer.deinit(); |
| 3059 | 2412 | |
| 3060 | var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator); | |
| 3061 | defer dbg_line_buffer.deinit(); | |
| 3062 | ||
| 3063 | var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator); | |
| 3064 | defer dbg_info_buffer.deinit(); | |
| 3065 | ||
| 3066 | var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{}; | |
| 3067 | defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs); | |
| 2413 | var debug_buffers_buf: Dwarf.DeclDebugBuffers = undefined; | |
| 2414 | const debug_buffers = if (self.dwarf) |*dw| blk: { | |
| 2415 | debug_buffers_buf = try dw.initDeclDebugInfo(decl); | |
| 2416 | break :blk &debug_buffers_buf; | |
| 2417 | } else null; | |
| 2418 | defer { | |
| 2419 | if (debug_buffers) |dbg| { | |
| 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 | 2426 | // TODO implement .debug_info for global variables |
| 3070 | 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(), .{ | |
| 3072 | .ty = decl.ty, | |
| 3073 | .val = decl_val, | |
| 3074 | }, &code_buffer, .{ | |
| 3075 | .dwarf = .{ | |
| 3076 | .dbg_line = &dbg_line_buffer, | |
| 3077 | .dbg_info = &dbg_info_buffer, | |
| 3078 | .dbg_info_type_relocs = &dbg_info_type_relocs, | |
| 3079 | }, | |
| 3080 | }, .{ | |
| 3081 | .parent_atom_index = decl.link.elf.local_sym_index, | |
| 3082 | }); | |
| 2428 | const res = if (debug_buffers) |dbg| | |
| 2429 | try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ | |
| 2430 | .ty = decl.ty, | |
| 2431 | .val = decl_val, | |
| 2432 | }, &code_buffer, .{ | |
| 2433 | .dwarf = .{ | |
| 2434 | .dbg_line = &dbg.dbg_line_buffer, | |
| 2435 | .dbg_info = &dbg.dbg_info_buffer, | |
| 2436 | .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, | |
| 2437 | }, | |
| 2438 | }, .{ | |
| 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 | 2449 | const code = switch (res) { |
| 3084 | 2450 | .externally_managed => |x| x, |
| 3085 | 2451 | .appended => code_buffer.items, |
| ... | ... | @@ -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); | |
| 3094 | return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer); | |
| 2459 | const local_sym = try self.updateDeclCode(decl, code, elf.STT_OBJECT); | |
| 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 | 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 | 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 | 2545 | pub fn updateDeclExports( |
| 3472 | 2546 | self: *Elf, |
| 3473 | 2547 | module: *Module, |
| ... | ... | @@ -3568,20 +2642,9 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec |
| 3568 | 2642 | log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); |
| 3569 | 2643 | |
| 3570 | 2644 | if (self.llvm_object) |_| return; |
| 3571 | ||
| 3572 | const func = decl.val.castTag(.function).?.data; | |
| 3573 | log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ | |
| 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); | |
| 2645 | if (self.dwarf) |*dw| { | |
| 2646 | try dw.updateDeclLineNumber(&self.base, decl); | |
| 2647 | } | |
| 3585 | 2648 | } |
| 3586 | 2649 | |
| 3587 | 2650 | pub fn deleteExport(self: *Elf, exp: Export) void { |
| ... | ... | @@ -3806,201 +2869,6 @@ fn archPtrWidthBytes(self: Elf) u8 { |
| 3806 | 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 | 2872 | fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { |
| 4005 | 2873 | return .{ |
| 4006 | 2874 | .p_type = phdr.p_type, |
src/link/MachO.zig+10-72| ... | ... | @@ -295,26 +295,6 @@ pub const Export = struct { |
| 295 | 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 | 298 | pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { |
| 319 | 299 | assert(options.object_format == .macho); |
| 320 | 300 | |
| ... | ... | @@ -376,6 +356,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { |
| 376 | 356 | |
| 377 | 357 | self.d_sym = .{ |
| 378 | 358 | .base = self, |
| 359 | .dwarf = link.File.Dwarf.init(allocator, .macho, options.target), | |
| 379 | 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 | 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 | 3508 | if (self.atoms.getPtr(match)) |last_atom| { |
| 3529 | 3509 | if (last_atom.* == atom) { |
| ... | ... | @@ -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 | 3519 | if (atom.prev) |prev| { |
| 3550 | 3520 | prev.next = atom.next; |
| 3551 | 3521 | |
| ... | ... | @@ -3564,18 +3534,8 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) |
| 3564 | 3534 | atom.next = null; |
| 3565 | 3535 | } |
| 3566 | 3536 | |
| 3567 | if (atom.dbg_info_prev) |prev| { | |
| 3568 | prev.dbg_info_next = atom.dbg_info_next; | |
| 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; | |
| 3537 | if (self.d_sym) |*d_sym| { | |
| 3538 | d_sym.dwarf.freeAtom(&atom.dbg_info_atom); | |
| 3579 | 3539 | } |
| 3580 | 3540 | } |
| 3581 | 3541 | |
| ... | ... | @@ -3725,9 +3685,9 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 3725 | 3685 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 3726 | 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 | 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 | 3691 | break :blk &debug_buffers_buf; |
| 3732 | 3692 | } else null; |
| 3733 | 3693 | defer { |
| ... | ... | @@ -3766,7 +3726,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 3766 | 3726 | |
| 3767 | 3727 | if (debug_buffers) |db| { |
| 3768 | 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 | 3765 | const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment)); |
| 3806 | 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, .{ | |
| 3809 | .none = .{}, | |
| 3810 | }, .{ | |
| 3768 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{ | |
| 3811 | 3769 | .parent_atom_index = local_sym_index, |
| 3812 | 3770 | }); |
| 3813 | 3771 | const code = switch (res) { |
| ... | ... | @@ -3869,9 +3827,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 3869 | 3827 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 3870 | 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 | 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 | 3833 | break :blk &debug_buffers_buf; |
| 3876 | 3834 | } else null; |
| 3877 | 3835 | defer { |
| ... | ... | @@ -4364,27 +4322,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| 4364 | 4322 | decl.link.macho.local_sym_index = 0; |
| 4365 | 4323 | } |
| 4366 | 4324 | if (self.d_sym) |*d_sym| { |
| 4367 | // TODO make this logic match freeAtom. Maybe abstract the logic | |
| 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 | } | |
| 4325 | d_sym.dwarf.freeDecl(decl); | |
| 4388 | 4326 | } |
| 4389 | 4327 | } |
| 4390 | 4328 |
src/link/MachO/Atom.zig+3-12| ... | ... | @@ -13,6 +13,7 @@ const trace = @import("../../tracy.zig").trace; |
| 13 | 13 | |
| 14 | 14 | const Allocator = mem.Allocator; |
| 15 | 15 | const Arch = std.Target.Cpu.Arch; |
| 16 | const Dwarf = @import("../Dwarf.zig"); | |
| 16 | 17 | const MachO = @import("../MachO.zig"); |
| 17 | 18 | const Object = @import("Object.zig"); |
| 18 | 19 | const StringIndexAdapter = std.hash_map.StringIndexAdapter; |
| ... | ... | @@ -71,14 +72,7 @@ stab: ?Stab = null, |
| 71 | 72 | next: ?*Atom, |
| 72 | 73 | prev: ?*Atom, |
| 73 | 74 | |
| 74 | /// Previous/next linked list pointers. | |
| 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, | |
| 75 | dbg_info_atom: Dwarf.DebugInfoAtom, | |
| 82 | 76 | |
| 83 | 77 | dirty: bool = true, |
| 84 | 78 | |
| ... | ... | @@ -188,10 +182,7 @@ pub const empty = Atom{ |
| 188 | 182 | .alignment = 0, |
| 189 | 183 | .prev = null, |
| 190 | 184 | .next = null, |
| 191 | .dbg_info_prev = null, | |
| 192 | .dbg_info_next = null, | |
| 193 | .dbg_info_off = undefined, | |
| 194 | .dbg_info_len = undefined, | |
| 185 | .dbg_info_atom = undefined, | |
| 195 | 186 | }; |
| 196 | 187 | |
| 197 | 188 | pub fn deinit(self: *Atom, allocator: Allocator) void { |
src/link/MachO/DebugSymbols.zig+31-1139| ... | ... | @@ -1,29 +1,27 @@ |
| 1 | 1 | const DebugSymbols = @This(); |
| 2 | 2 | |
| 3 | 3 | const std = @import("std"); |
| 4 | const build_options = @import("build_options"); | |
| 4 | 5 | const assert = std.debug.assert; |
| 5 | 6 | const fs = std.fs; |
| 7 | const link = @import("../../link.zig"); | |
| 6 | 8 | const log = std.log.scoped(.link); |
| 7 | const leb128 = std.leb; | |
| 8 | 9 | const macho = std.macho; |
| 10 | const makeStaticString = MachO.makeStaticString; | |
| 9 | 11 | const math = std.math; |
| 10 | 12 | const mem = std.mem; |
| 11 | const DW = std.dwarf; | |
| 12 | const leb = std.leb; | |
| 13 | const Allocator = mem.Allocator; | |
| 14 | ||
| 15 | const build_options = @import("build_options"); | |
| 13 | const padToIdeal = MachO.padToIdeal; | |
| 16 | 14 | const trace = @import("../../tracy.zig").trace; |
| 17 | const Module = @import("../../Module.zig"); | |
| 18 | const Type = @import("../../type.zig").Type; | |
| 19 | const link = @import("../../link.zig"); | |
| 15 | ||
| 16 | const Allocator = mem.Allocator; | |
| 17 | const Dwarf = @import("../Dwarf.zig"); | |
| 20 | 18 | const MachO = @import("../MachO.zig"); |
| 19 | const Module = @import("../../Module.zig"); | |
| 21 | 20 | const TextBlock = MachO.TextBlock; |
| 22 | const SrcFn = MachO.SrcFn; | |
| 23 | const makeStaticString = MachO.makeStaticString; | |
| 24 | const padToIdeal = MachO.padToIdeal; | |
| 21 | const Type = @import("../../type.zig").Type; | |
| 25 | 22 | |
| 26 | 23 | base: *MachO, |
| 24 | dwarf: Dwarf, | |
| 27 | 25 | file: fs.File, |
| 28 | 26 | |
| 29 | 27 | /// Table of all load commands |
| ... | ... | @@ -54,23 +52,6 @@ debug_str_section_index: ?u16 = null, |
| 54 | 52 | debug_aranges_section_index: ?u16 = null, |
| 55 | 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 | 55 | load_commands_dirty: bool = false, |
| 75 | 56 | debug_string_table_dirty: bool = false, |
| 76 | 57 | debug_abbrev_section_dirty: bool = false, |
| ... | ... | @@ -78,25 +59,6 @@ debug_aranges_section_dirty: bool = false, |
| 78 | 59 | debug_info_header_dirty: bool = false, |
| 79 | 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 | 62 | /// You must call this function *after* `MachO.populateMissingMetadata()` |
| 101 | 63 | /// has been called to get a viable debug symbols output. |
| 102 | 64 | 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 | 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 | 159 | self.debug_str_section_index = try self.allocateSection( |
| 198 | 160 | "__debug_str", |
| 199 | @intCast(u32, self.debug_string_table.items.len), | |
| 161 | @intCast(u32, self.dwarf.strtab.items.len), | |
| 200 | 162 | 0, |
| 201 | 163 | ); |
| 202 | 164 | self.debug_string_table_dirty = true; |
| ... | ... | @@ -277,7 +239,7 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 { |
| 277 | 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 | 243 | const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; |
| 282 | 244 | var offset: u64 = seg.inner.fileoff; |
| 283 | 245 | while (self.detectAllocCollision(offset, object_size)) |item_end| { |
| ... | ... | @@ -290,322 +252,45 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti |
| 290 | 252 | // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the |
| 291 | 253 | // Zig source code. |
| 292 | 254 | const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented; |
| 293 | const init_len_size: usize = 4; | |
| 294 | 255 | |
| 295 | 256 | if (self.debug_abbrev_section_dirty) { |
| 296 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; | |
| 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); | |
| 257 | try self.dwarf.writeDbgAbbrev(&self.base.base); | |
| 400 | 258 | self.load_commands_dirty = true; |
| 401 | 259 | self.debug_abbrev_section_dirty = false; |
| 402 | 260 | } |
| 403 | 261 | |
| 404 | if (self.debug_info_header_dirty) debug_info: { | |
| 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); | |
| 262 | if (self.debug_info_header_dirty) { | |
| 434 | 263 | // Currently only one compilation unit is supported, so the address range is simply |
| 435 | 264 | // identical to the main program header virtual address and memory size. |
| 436 | 265 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment; |
| 437 | 266 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 438 | 267 | const low_pc = text_section.addr; |
| 439 | 268 | const high_pc = text_section.addr + text_section.size; |
| 440 | ||
| 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); | |
| 269 | try self.dwarf.writeDbgInfoHeader(&self.base.base, module, low_pc, high_pc); | |
| 459 | 270 | self.debug_info_header_dirty = false; |
| 460 | 271 | } |
| 461 | 272 | |
| 462 | 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 | 274 | // Currently only one compilation unit is supported, so the address range is simply |
| 489 | 275 | // identical to the main program header virtual address and memory size. |
| 490 | 276 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment; |
| 491 | 277 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 492 | mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.addr); | |
| 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); | |
| 278 | try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size); | |
| 520 | 279 | self.load_commands_dirty = true; |
| 521 | 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; | |
| 532 | const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?]; | |
| 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); | |
| 283 | if (self.debug_line_header_dirty) { | |
| 284 | try self.dwarf.writeDbgLineHeader(&self.base.base, module); | |
| 601 | 285 | self.debug_line_header_dirty = false; |
| 602 | 286 | } |
| 287 | ||
| 603 | 288 | { |
| 604 | 289 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; |
| 605 | 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 | 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 | 295 | if (needed_size > allocated_size) { |
| 611 | 296 | debug_strtab_sect.size = 0; // free the space |
| ... | ... | @@ -620,7 +305,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti |
| 620 | 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 | 309 | self.load_commands_dirty = true; |
| 625 | 310 | self.debug_string_table_dirty = false; |
| 626 | 311 | } |
| ... | ... | @@ -639,13 +324,11 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti |
| 639 | 324 | } |
| 640 | 325 | |
| 641 | 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 | 327 | for (self.load_commands.items) |*lc| { |
| 646 | 328 | lc.deinit(allocator); |
| 647 | 329 | } |
| 648 | 330 | self.load_commands.deinit(allocator); |
| 331 | self.dwarf.deinit(); | |
| 649 | 332 | self.file.close(); |
| 650 | 333 | } |
| 651 | 334 | |
| ... | ... | @@ -777,7 +460,7 @@ fn writeHeader(self: *DebugSymbols) !void { |
| 777 | 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 | 464 | const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; |
| 782 | 465 | assert(start >= seg.inner.fileoff); |
| 783 | 466 | var min_pos: u64 = std.math.maxInt(u64); |
| ... | ... | @@ -964,813 +647,22 @@ fn writeStringTable(self: *DebugSymbols) !void { |
| 964 | 647 | |
| 965 | 648 | pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void { |
| 966 | 649 | _ = module; |
| 967 | const tracy = trace(@src()); | |
| 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); | |
| 650 | return self.dwarf.updateDeclLineNumber(&self.base.base, decl); | |
| 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 | 653 | /// Caller owns the returned memory. |
| 995 | pub fn initDeclDebugBuffers( | |
| 996 | self: *DebugSymbols, | |
| 997 | allocator: Allocator, | |
| 998 | module: *Module, | |
| 999 | decl: *Module.Decl, | |
| 1000 | ) !DeclDebugBuffers { | |
| 1001 | _ = self; | |
| 654 | pub fn initDeclDebugInfo(self: *DebugSymbols, module: *Module, decl: *Module.Decl) !Dwarf.DeclDebugBuffers { | |
| 1002 | 655 | _ = module; |
| 1003 | const tracy = trace(@src()); | |
| 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 | }; | |
| 656 | return self.dwarf.initDeclDebugInfo(decl); | |
| 1093 | 657 | } |
| 1094 | 658 | |
| 1095 | 659 | pub fn commitDeclDebugInfo( |
| 1096 | 660 | self: *DebugSymbols, |
| 1097 | allocator: Allocator, | |
| 1098 | 661 | module: *Module, |
| 1099 | 662 | decl: *Module.Decl, |
| 1100 | debug_buffers: *DeclDebugBuffers, | |
| 663 | debug_buffers: *Dwarf.DeclDebugBuffers, | |
| 1101 | 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 | 665 | const symbol = self.base.locals.items[decl.link.macho.local_sym_index]; |
| 1110 | const text_block = &decl.link.macho; | |
| 1111 | // If the Decl is a function, we need to update the __debug_line program. | |
| 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); | |
| 666 | const atom = &decl.link.macho; | |
| 667 | return self.dwarf.commitDeclDebugInfo(&self.base.base, module, decl, symbol.n_value, atom.size, debug_buffers); | |
| 1776 | 668 | } |