authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-06 18:35:58+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-08 09:46:27+01:00
logba17552b4eb8def495053013eebbe39fc324c8ae
tree33f871441f432bfdee7f6f555b95062be565ded8
parent38c161afabe1dc7d0df7ad981d70b962ef87120a

dwarf: move all dwarf into standalone module

Hook up Elf and MachO linkers to the new solution.

10 files changed, 1786 insertions(+), 2485 deletions(-)

src/Module.zig+4-4
...@@ -4439,8 +4439,8 @@ pub fn clearDecl(...@@ -4439,8 +4439,8 @@ pub fn clearDecl(
4439 };4439 };
4440 decl.fn_link = switch (mod.comp.bin_file.tag) {4440 decl.fn_link = switch (mod.comp.bin_file.tag) {
4441 .coff => .{ .coff = {} },4441 .coff => .{ .coff = {} },
4442 .elf => .{ .elf = link.File.Elf.SrcFn.empty },4442 .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },
4443 .macho => .{ .macho = link.File.MachO.SrcFn.empty },4443 .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
4444 .plan9 => .{ .plan9 = {} },4444 .plan9 => .{ .plan9 = {} },
4445 .c => .{ .c = {} },4445 .c => .{ .c = {} },
4446 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },4446 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
...@@ -4776,8 +4776,8 @@ pub fn allocateNewDecl(...@@ -4776,8 +4776,8 @@ pub fn allocateNewDecl(
4776 },4776 },
4777 .fn_link = switch (mod.comp.bin_file.tag) {4777 .fn_link = switch (mod.comp.bin_file.tag) {
4778 .coff => .{ .coff = {} },4778 .coff => .{ .coff = {} },
4779 .elf => .{ .elf = link.File.Elf.SrcFn.empty },4779 .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },
4780 .macho => .{ .macho = link.File.MachO.SrcFn.empty },4780 .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
4781 .plan9 => .{ .plan9 = {} },4781 .plan9 => .{ .plan9 = {} },
4782 .c => .{ .c = {} },4782 .c => .{ .c = {} },
4783 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },4783 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
src/arch/arm/Emit.zig+2-2
...@@ -409,7 +409,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void {...@@ -409,7 +409,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void {
409 switch (self.debug_output) {409 switch (self.debug_output) {
410 .dwarf => |dbg_out| {410 .dwarf => |dbg_out| {
411 try dbg_out.dbg_info.ensureUnusedCapacity(3);411 try dbg_out.dbg_info.ensureUnusedCapacity(3);
412 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);412 dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter);
413 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc413 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
414 1, // ULEB128 dwarf expression length414 1, // ULEB128 dwarf expression length
415 reg.dwarfLocOp(),415 reg.dwarfLocOp(),
...@@ -440,7 +440,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void {...@@ -440,7 +440,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void {
440 else => unreachable,440 else => unreachable,
441 };441 };
442442
443 try dbg_out.dbg_info.append(link.File.Elf.abbrev_parameter);443 try dbg_out.dbg_info.append(link.File.Dwarf.abbrev_parameter);
444444
445 // Get length of the LEB128 stack offset445 // Get length of the LEB128 stack offset
446 var counting_writer = std.io.countingWriter(std.io.null_writer);446 var counting_writer = std.io.countingWriter(std.io.null_writer);
src/arch/riscv64/CodeGen.zig+1-1
...@@ -1384,7 +1384,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32...@@ -1384,7 +1384,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32
1384 switch (self.debug_output) {1384 switch (self.debug_output) {
1385 .dwarf => |dbg_out| {1385 .dwarf => |dbg_out| {
1386 try dbg_out.dbg_info.ensureUnusedCapacity(3);1386 try dbg_out.dbg_info.ensureUnusedCapacity(3);
1387 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);1387 dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter);
1388 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc1388 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
1389 1, // ULEB128 dwarf expression length1389 1, // ULEB128 dwarf expression length
1390 reg.dwarfLocOp(),1390 reg.dwarfLocOp(),
src/arch/x86_64/Emit.zig+2-16
...@@ -1019,14 +1019,7 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32...@@ -1019,14 +1019,7 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32
1019 switch (emit.debug_output) {1019 switch (emit.debug_output) {
1020 .dwarf => |dbg_out| {1020 .dwarf => |dbg_out| {
1021 try dbg_out.dbg_info.ensureUnusedCapacity(3);1021 try dbg_out.dbg_info.ensureUnusedCapacity(3);
10221022 dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter);
1023 // TODO this will go away once we pull DWARF into a cross-platform module.
1024 if (emit.bin_file.cast(link.File.MachO)) |_| {
1025 dbg_out.dbg_info.appendAssumeCapacity(link.File.MachO.DebugSymbols.abbrev_parameter);
1026 } else if (emit.bin_file.cast(link.File.Elf)) |_| {
1027 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
1028 } else return emit.fail("TODO DWARF in non-MachO and non-ELF backend", .{});
1029
1030 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc1023 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
1031 1, // ULEB128 dwarf expression length1024 1, // ULEB128 dwarf expression length
1032 reg.dwarfLocOp(),1025 reg.dwarfLocOp(),
...@@ -1049,14 +1042,7 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32...@@ -1049,14 +1042,7 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32
1049 // for example when -fomit-frame-pointer is set.1042 // for example when -fomit-frame-pointer is set.
1050 const disp = @intCast(i32, max_stack) - off + 16;1043 const disp = @intCast(i32, max_stack) - off + 16;
1051 try dbg_out.dbg_info.ensureUnusedCapacity(8);1044 try dbg_out.dbg_info.ensureUnusedCapacity(8);
10521045 dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter);
1053 // TODO this will go away once we pull DWARF into a cross-platform module.
1054 if (emit.bin_file.cast(link.File.MachO)) |_| {
1055 dbg_out.dbg_info.appendAssumeCapacity(link.File.MachO.DebugSymbols.abbrev_parameter);
1056 } else if (emit.bin_file.cast(link.File.Elf)) |_| {
1057 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
1058 } else return emit.fail("TODO DWARF in non-MachO and non-ELF backend", .{});
1059
1060 const fixup = dbg_out.dbg_info.items.len;1046 const fixup = dbg_out.dbg_info.items.len;
1061 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc1047 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
1062 1, // we will backpatch it after we encode the displacement in LEB1281048 1, // we will backpatch it after we encode the displacement in LEB128
src/link.zig+3-2
...@@ -221,9 +221,9 @@ pub const File = struct {...@@ -221,9 +221,9 @@ pub const File = struct {
221 };221 };
222222
223 pub const LinkFn = union {223 pub const LinkFn = union {
224 elf: Elf.SrcFn,224 elf: Dwarf.SrcFn,
225 coff: Coff.SrcFn,225 coff: Coff.SrcFn,
226 macho: MachO.SrcFn,226 macho: Dwarf.SrcFn,
227 plan9: void,227 plan9: void,
228 c: void,228 c: void,
229 wasm: Wasm.FnData,229 wasm: Wasm.FnData,
...@@ -915,6 +915,7 @@ pub const File = struct {...@@ -915,6 +915,7 @@ pub const File = struct {
915 pub const SpirV = @import("link/SpirV.zig");915 pub const SpirV = @import("link/SpirV.zig");
916 pub const Wasm = @import("link/Wasm.zig");916 pub const Wasm = @import("link/Wasm.zig");
917 pub const NvPtx = @import("link/NvPtx.zig");917 pub const NvPtx = @import("link/NvPtx.zig");
918 pub const Dwarf = @import("link/Dwarf.zig");
918};919};
919920
920pub fn determineMode(options: Options) fs.File.Mode {921pub fn determineMode(options: Options) fs.File.Mode {
src/link/Dwarf.zig created+1625
...@@ -0,0 +1,1625 @@
1const Dwarf = @This();
2
3const std = @import("std");
4const builtin = @import("builtin");
5const assert = std.debug.assert;
6const fs = std.fs;
7const leb128 = std.leb;
8const log = std.log.scoped(.dwarf);
9const mem = std.mem;
10
11const link = @import("../link.zig");
12const trace = @import("../tracy.zig").trace;
13
14const Allocator = mem.Allocator;
15const DW = std.dwarf;
16const File = link.File;
17const LinkBlock = File.LinkBlock;
18const LinkFn = File.LinkFn;
19const Module = @import("../Module.zig");
20const Value = @import("../value.zig").Value;
21const Type = @import("../type.zig").Type;
22
23allocator: Allocator,
24tag: File.Tag,
25ptr_width: PtrWidth,
26target: 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.
30dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
31dbg_line_fn_first: ?*SrcFn = null,
32dbg_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.
35dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*DebugInfoAtom, void) = .{},
36dbg_info_decl_first: ?*DebugInfoAtom = null,
37dbg_info_decl_last: ?*DebugInfoAtom = null,
38
39abbrev_table_offset: ?u64 = null,
40
41/// Table of debug symbol names.
42strtab: std.ArrayListUnmanaged(u8) = .{},
43
44pub 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
55pub 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
75pub const PtrWidth = enum { p32, p64 };
76
77pub const abbrev_compile_unit = 1;
78pub const abbrev_subprogram = 2;
79pub const abbrev_subprogram_retvoid = 3;
80pub const abbrev_base_type = 4;
81pub const abbrev_ptr_type = 5;
82pub const abbrev_struct_type = 6;
83pub const abbrev_struct_member = 7;
84pub const abbrev_pad1 = 8;
85pub 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.
89const 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.
92const dbg_info_low_pc_reloc_index = 1;
93
94const min_nop_size = 2;
95
96/// When allocating, the ideal_capacity is calculated by
97/// actual_capacity + (actual_capacity / ideal_factor)
98const ideal_factor = 3;
99
100pub 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
114pub 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
121pub 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
127pub 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
228pub 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
487fn 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
552fn 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
653pub 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
686pub 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
710pub 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.
742fn 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
935pub 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
1065fn dbgInfoHeaderBytes(self: *Dwarf) usize {
1066 _ = self;
1067 return 120;
1068}
1069
1070pub 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
1172fn 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`).
1185fn 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.
1263fn 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
1333pub 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
1436pub 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
1555fn getDebugInfoOff(self: Dwarf) ?u32 {
1556 const first = self.dbg_info_decl_first orelse return null;
1557 return first.off;
1558}
1559
1560fn getDebugInfoEnd(self: Dwarf) ?u32 {
1561 const last = self.dbg_info_decl_last orelse return null;
1562 return last.off + last.len;
1563}
1564
1565fn getDebugLineProgramOff(self: Dwarf) ?u32 {
1566 const first = self.dbg_line_fn_first orelse return null;
1567 return first.off;
1568}
1569
1570fn 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.
1576fn ptrWidthBytes(self: Dwarf) u8 {
1577 return switch (self.ptr_width) {
1578 .p32 => 4,
1579 .p64 => 8,
1580 };
1581}
1582
1583fn 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.
1600fn getRelocDbgLineOff(self: Dwarf) usize {
1601 return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
1602}
1603
1604fn getRelocDbgFileIndex(self: Dwarf) usize {
1605 return self.getRelocDbgLineOff() + 5;
1606}
1607
1608fn getRelocDbgInfoSubprogramHighPC(self: Dwarf) u32 {
1609 return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
1610}
1611
1612/// TODO Improve this to use a table.
1613fn 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
1621fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
1622 // TODO https://github.com/ziglang/zig/issues/1284
1623 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
1624 std.math.maxInt(@TypeOf(actual_size));
1625}
src/link/Elf.zig+105-1237
...@@ -9,11 +9,10 @@ const Allocator = std.mem.Allocator;...@@ -9,11 +9,10 @@ const Allocator = std.mem.Allocator;
9const fs = std.fs;9const fs = std.fs;
10const elf = std.elf;10const elf = std.elf;
11const log = std.log.scoped(.link);11const log = std.log.scoped(.link);
12const DW = std.dwarf;
13const leb128 = std.leb;
1412
15const Module = @import("../Module.zig");13const Module = @import("../Module.zig");
16const Compilation = @import("../Compilation.zig");14const Compilation = @import("../Compilation.zig");
15const Dwarf = @import("Dwarf.zig");
17const codegen = @import("../codegen.zig");16const codegen = @import("../codegen.zig");
18const lldMain = @import("../main.zig").lldMain;17const lldMain = @import("../main.zig").lldMain;
19const trace = @import("../tracy.zig").trace;18const trace = @import("../tracy.zig").trace;
...@@ -37,6 +36,7 @@ const default_entry_addr = 0x8000000;...@@ -37,6 +36,7 @@ const default_entry_addr = 0x8000000;
37pub const base_tag: File.Tag = .elf;36pub const base_tag: File.Tag = .elf;
3837
39base: File,38base: File,
39dwarf: ?Dwarf = null,
4040
41ptr_width: PtrWidth,41ptr_width: PtrWidth,
4242
...@@ -67,7 +67,6 @@ phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},...@@ -67,7 +67,6 @@ phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},
67entry_addr: ?u64 = null,67entry_addr: ?u64 = null,
68page_size: u16,68page_size: u16,
6969
70debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
71shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},70shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
72shstrtab_index: ?u16 = null,71shstrtab_index: ?u16 = null,
7372
...@@ -82,8 +81,6 @@ debug_str_section_index: ?u16 = null,...@@ -82,8 +81,6 @@ debug_str_section_index: ?u16 = null,
82debug_aranges_section_index: ?u16 = null,81debug_aranges_section_index: ?u16 = null,
83debug_line_section_index: ?u16 = null,82debug_line_section_index: ?u16 = null,
8483
85debug_abbrev_table_offset: ?u64 = null,
86
87/// The same order as in the file. ELF requires global symbols to all be after the84/// The same order as in the file. ELF requires global symbols to all be after the
88/// local symbols, they cannot be mixed. So we must buffer all the global symbols and85/// local symbols, they cannot be mixed. So we must buffer all the global symbols and
89/// write them at the end. These are only the local symbols. The length of this array86/// write them at the end. These are only the local symbols. The length of this array
...@@ -168,18 +165,6 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{},...@@ -168,18 +165,6 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{},
168/// with `Decl` `main`, and lives as long as that `Decl`.165/// with `Decl` `main`, and lives as long as that `Decl`.
169unnamed_const_atoms: UnnamedConstTable = .{},166unnamed_const_atoms: UnnamedConstTable = .{},
170167
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.
173dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
174dbg_line_fn_first: ?*SrcFn = null,
175dbg_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.
179dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
180dbg_info_decl_first: ?*TextBlock = null,
181dbg_info_decl_last: ?*TextBlock = null,
182
183/// A table of relocations indexed by the owning them `TextBlock`.168/// A table of relocations indexed by the owning them `TextBlock`.
184/// Note that once we refactor `TextBlock`'s lifetime and ownership rules,169/// Note that once we refactor `TextBlock`'s lifetime and ownership rules,
185/// this will be a table indexed by index into the list of Atoms.170/// this will be a table indexed by index into the list of Atoms.
...@@ -222,24 +207,14 @@ pub const TextBlock = struct {...@@ -222,24 +207,14 @@ pub const TextBlock = struct {
222 prev: ?*TextBlock,207 prev: ?*TextBlock,
223 next: ?*TextBlock,208 next: ?*TextBlock,
224209
225 /// Previous/next linked list pointers.210 dbg_info_atom: Dwarf.DebugInfoAtom,
226 /// This is the linked list node for this Decl's corresponding .debug_info tag.
227 dbg_info_prev: ?*TextBlock,
228 dbg_info_next: ?*TextBlock,
229 /// Offset into .debug_info pointing to the tag for this Decl.
230 dbg_info_off: u32,
231 /// Size of the .debug_info tag for this Decl, not including padding.
232 dbg_info_len: u32,
233211
234 pub const empty = TextBlock{212 pub const empty = TextBlock{
235 .local_sym_index = 0,213 .local_sym_index = 0,
236 .offset_table_index = undefined,214 .offset_table_index = undefined,
237 .prev = null,215 .prev = null,
238 .next = null,216 .next = null,
239 .dbg_info_prev = null,217 .dbg_info_atom = undefined,
240 .dbg_info_next = null,
241 .dbg_info_off = undefined,
242 .dbg_info_len = undefined,
243 };218 };
244219
245 /// Returns how much room there is to grow in virtual address space.220 /// Returns how much room there is to grow in virtual address space.
...@@ -273,26 +248,6 @@ pub const Export = struct {...@@ -273,26 +248,6 @@ pub const Export = struct {
273 sym_index: ?u32 = null,248 sym_index: ?u32 = null,
274};249};
275250
276pub 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
296pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf {251pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf {
297 assert(options.object_format == .elf);252 assert(options.object_format == .elf);
298253
...@@ -351,6 +306,11 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -351,6 +306,11 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
351 errdefer gpa.destroy(self);306 errdefer gpa.destroy(self);
352 const page_size: u16 = 0x1000; // TODO ppc64le requires 64KB307 const page_size: u16 = 0x1000; // TODO ppc64le requires 64KB
353308
309 var dwarf: ?Dwarf = if (!options.strip and options.module != null)
310 Dwarf.init(gpa, .elf, options.target)
311 else
312 null;
313
354 self.* = .{314 self.* = .{
355 .base = .{315 .base = .{
356 .tag = .elf,316 .tag = .elf,
...@@ -358,6 +318,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -358,6 +318,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
358 .allocator = gpa,318 .allocator = gpa,
359 .file = null,319 .file = null,
360 },320 },
321 .dwarf = dwarf,
361 .ptr_width = ptr_width,322 .ptr_width = ptr_width,
362 .page_size = page_size,323 .page_size = page_size,
363 };324 };
...@@ -377,14 +338,11 @@ pub fn deinit(self: *Elf) void {...@@ -377,14 +338,11 @@ pub fn deinit(self: *Elf) void {
377 self.sections.deinit(self.base.allocator);338 self.sections.deinit(self.base.allocator);
378 self.program_headers.deinit(self.base.allocator);339 self.program_headers.deinit(self.base.allocator);
379 self.shstrtab.deinit(self.base.allocator);340 self.shstrtab.deinit(self.base.allocator);
380 self.debug_strtab.deinit(self.base.allocator);
381 self.local_symbols.deinit(self.base.allocator);341 self.local_symbols.deinit(self.base.allocator);
382 self.global_symbols.deinit(self.base.allocator);342 self.global_symbols.deinit(self.base.allocator);
383 self.global_symbol_free_list.deinit(self.base.allocator);343 self.global_symbol_free_list.deinit(self.base.allocator);
384 self.local_symbol_free_list.deinit(self.base.allocator);344 self.local_symbol_free_list.deinit(self.base.allocator);
385 self.offset_table_free_list.deinit(self.base.allocator);345 self.offset_table_free_list.deinit(self.base.allocator);
386 self.dbg_line_fn_free_list.deinit(self.base.allocator);
387 self.dbg_info_decl_free_list.deinit(self.base.allocator);
388 self.offset_table.deinit(self.base.allocator);346 self.offset_table.deinit(self.base.allocator);
389 self.phdr_shdr_table.deinit(self.base.allocator);347 self.phdr_shdr_table.deinit(self.base.allocator);
390 self.decls.deinit(self.base.allocator);348 self.decls.deinit(self.base.allocator);
...@@ -420,6 +378,10 @@ pub fn deinit(self: *Elf) void {...@@ -420,6 +378,10 @@ pub fn deinit(self: *Elf) void {
420 }378 }
421379
422 self.atom_by_index_table.deinit(self.base.allocator);380 self.atom_by_index_table.deinit(self.base.allocator);
381
382 if (self.dwarf) |*dw| {
383 dw.deinit();
384 }
423}385}
424386
425pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.RelocInfo) !u64 {387pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.RelocInfo) !u64 {
...@@ -443,14 +405,6 @@ pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.Reloc...@@ -443,14 +405,6 @@ pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.Reloc
443 return vaddr;405 return vaddr;
444}406}
445407
446fn getDebugLineProgramOff(self: Elf) u32 {
447 return self.dbg_line_fn_first.?.off;
448}
449
450fn getDebugLineProgramEnd(self: Elf) u32 {
451 return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len;
452}
453
454/// Returns end pos of collision, if any.408/// Returns end pos of collision, if any.
455fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {409fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
456 const small_ptr = self.ptr_width == .p32;410 const small_ptr = self.ptr_width == .p32;
...@@ -497,7 +451,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -497,7 +451,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
497 return null;451 return null;
498}452}
499453
500fn allocatedSize(self: *Elf, start: u64) u64 {454pub fn allocatedSize(self: *Elf, start: u64) u64 {
501 if (start == 0)455 if (start == 0)
502 return 0;456 return 0;
503 var min_pos: u64 = std.math.maxInt(u64);457 var min_pos: u64 = std.math.maxInt(u64);
...@@ -518,7 +472,7 @@ fn allocatedSize(self: *Elf, start: u64) u64 {...@@ -518,7 +472,7 @@ fn allocatedSize(self: *Elf, start: u64) u64 {
518 return min_pos - start;472 return min_pos - start;
519}473}
520474
521fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {475pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
522 var start: u64 = 0;476 var start: u64 = 0;
523 while (self.detectAllocCollision(start, object_size)) |item_end| {477 while (self.detectAllocCollision(start, object_size)) |item_end| {
524 start = mem.alignForwardGeneric(u64, item_end, min_alignment);478 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
...@@ -535,15 +489,6 @@ fn makeString(self: *Elf, bytes: []const u8) !u32 {...@@ -535,15 +489,6 @@ fn makeString(self: *Elf, bytes: []const u8) !u32 {
535 return @intCast(u32, result);489 return @intCast(u32, result);
536}490}
537491
538/// TODO Improve this to use a table.
539fn 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
547fn getString(self: *Elf, str_off: u32) []const u8 {492fn getString(self: *Elf, str_off: u32) []const u8 {
548 assert(str_off < self.shstrtab.items.len);493 assert(str_off < self.shstrtab.items.len);
549 return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0);494 return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0);
...@@ -806,14 +751,14 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -806,14 +751,14 @@ pub fn populateMissingMetadata(self: *Elf) !void {
806751
807 if (self.debug_str_section_index == null) {752 if (self.debug_str_section_index == null) {
808 self.debug_str_section_index = @intCast(u16, self.sections.items.len);753 self.debug_str_section_index = @intCast(u16, self.sections.items.len);
809 assert(self.debug_strtab.items.len == 0);754 assert(self.dwarf.?.strtab.items.len == 0);
810 try self.sections.append(self.base.allocator, .{755 try self.sections.append(self.base.allocator, .{
811 .sh_name = try self.makeString(".debug_str"),756 .sh_name = try self.makeString(".debug_str"),
812 .sh_type = elf.SHT_PROGBITS,757 .sh_type = elf.SHT_PROGBITS,
813 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,758 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
814 .sh_addr = 0,759 .sh_addr = 0,
815 .sh_offset = 0,760 .sh_offset = 0,
816 .sh_size = self.debug_strtab.items.len,761 .sh_size = 0,
817 .sh_link = 0,762 .sh_link = 0,
818 .sh_info = 0,763 .sh_info = 0,
819 .sh_addralign = 1,764 .sh_addralign = 1,
...@@ -977,16 +922,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -977,16 +922,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
977 }922 }
978}923}
979924
980pub const abbrev_compile_unit = 1;
981pub const abbrev_subprogram = 2;
982pub const abbrev_subprogram_retvoid = 3;
983pub const abbrev_base_type = 4;
984pub const abbrev_ptr_type = 5;
985pub const abbrev_struct_type = 6;
986pub const abbrev_struct_member = 7;
987pub const abbrev_pad1 = 8;
988pub const abbrev_parameter = 9;
989
990pub fn flush(self: *Elf, comp: *Compilation) !void {925pub fn flush(self: *Elf, comp: *Compilation) !void {
991 if (self.base.options.emit == null) {926 if (self.base.options.emit == null) {
992 if (build_options.have_llvm) {927 if (build_options.have_llvm) {
...@@ -1022,11 +957,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -1022,11 +957,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
1022957
1023 const target_endian = self.base.options.target.cpu.arch.endian();958 const target_endian = self.base.options.target.cpu.arch.endian();
1024 const foreign_endian = target_endian != builtin.cpu.arch.endian();959 const foreign_endian = target_endian != builtin.cpu.arch.endian();
1025 const ptr_width_bytes: u8 = self.ptrWidthBytes();
1026 const init_len_size: usize = switch (self.ptr_width) {
1027 .p32 => 4,
1028 .p64 => 12,
1029 };
1030960
1031 {961 {
1032 var it = self.relocs.iterator();962 var it = self.relocs.iterator();
...@@ -1068,349 +998,38 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -1068,349 +998,38 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
1068 try self.writeAllGlobalSymbols();998 try self.writeAllGlobalSymbols();
1069999
1070 if (self.debug_abbrev_section_dirty) {1000 if (self.debug_abbrev_section_dirty) {
1071 const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?];1001 try self.dwarf.?.writeDbgAbbrev(&self.base);
1072
1073 // These are LEB encoded but since the values are all less than 127
1074 // we can simply append these bytes.
1075 const abbrev_buf = [_]u8{
1076 abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header
1077 DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc,
1078 DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr,
1079 DW.AT.name, DW.FORM.strp, DW.AT.comp_dir,
1080 DW.FORM.strp, DW.AT.producer, DW.FORM.strp,
1081 DW.AT.language, DW.FORM.data2, 0,
1082 0, // table sentinel
1083 abbrev_subprogram,
1084 DW.TAG.subprogram,
1085 DW.CHILDREN.yes, // header
1086 DW.AT.low_pc,
1087 DW.FORM.addr,
1088 DW.AT.high_pc,
1089 DW.FORM.data4,
1090 DW.AT.type,
1091 DW.FORM.ref4,
1092 DW.AT.name,
1093 DW.FORM.string,
1094 0, 0, // table sentinel
1095 abbrev_subprogram_retvoid,
1096 DW.TAG.subprogram, DW.CHILDREN.yes, // header
1097 DW.AT.low_pc, DW.FORM.addr,
1098 DW.AT.high_pc, DW.FORM.data4,
1099 DW.AT.name, DW.FORM.string,
1100 0,
1101 0, // table sentinel
1102 abbrev_base_type,
1103 DW.TAG.base_type,
1104 DW.CHILDREN.no, // header
1105 DW.AT.encoding,
1106 DW.FORM.data1,
1107 DW.AT.byte_size,
1108 DW.FORM.data1,
1109 DW.AT.name,
1110 DW.FORM.string,
1111 0,
1112 0, // table sentinel
1113 abbrev_ptr_type,
1114 DW.TAG.pointer_type,
1115 DW.CHILDREN.no, // header
1116 DW.AT.type,
1117 DW.FORM.ref4,
1118 0,
1119 0, // table sentinel
1120 abbrev_struct_type,
1121 DW.TAG.structure_type,
1122 DW.CHILDREN.yes, // header
1123 DW.AT.byte_size,
1124 DW.FORM.sdata,
1125 DW.AT.name,
1126 DW.FORM.string,
1127 0,
1128 0, // table sentinel
1129 abbrev_struct_member,
1130 DW.TAG.member,
1131 DW.CHILDREN.no, // header
1132 DW.AT.name,
1133 DW.FORM.string,
1134 DW.AT.type,
1135 DW.FORM.ref4,
1136 DW.AT.data_member_location,
1137 DW.FORM.sdata,
1138 0,
1139 0, // table sentinel
1140 abbrev_pad1,
1141 DW.TAG.unspecified_type,
1142 DW.CHILDREN.no, // header
1143 0,
1144 0, // table sentinel
1145 abbrev_parameter,
1146 DW.TAG.formal_parameter, DW.CHILDREN.no, // header
1147 DW.AT.location, DW.FORM.exprloc,
1148 DW.AT.type, DW.FORM.ref4,
1149 DW.AT.name, DW.FORM.string,
1150 0,
1151 0, // table sentinel
1152 0,
1153 0,
1154 0, // section sentinel
1155 };
1156
1157 const needed_size = abbrev_buf.len;
1158 const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset);
1159 if (needed_size > allocated_size) {
1160 debug_abbrev_sect.sh_size = 0; // free the space
1161 debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1162 }
1163 debug_abbrev_sect.sh_size = needed_size;
1164 log.debug(".debug_abbrev start=0x{x} end=0x{x}", .{
1165 debug_abbrev_sect.sh_offset,
1166 debug_abbrev_sect.sh_offset + needed_size,
1167 });
1168
1169 const abbrev_offset = 0;
1170 self.debug_abbrev_table_offset = abbrev_offset;
1171 try self.base.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset);
1172 if (!self.shdr_table_dirty) {1002 if (!self.shdr_table_dirty) {
1173 // Then it won't get written with the others and we need to do it.1003 // Then it won't get written with the others and we need to do it.
1174 try self.writeSectHeader(self.debug_abbrev_section_index.?);1004 try self.writeSectHeader(self.debug_abbrev_section_index.?);
1175 }1005 }
1176
1177 self.debug_abbrev_section_dirty = false;1006 self.debug_abbrev_section_dirty = false;
1178 }1007 }
11791008
1180 if (self.debug_info_header_dirty) debug_info: {1009 if (self.debug_info_header_dirty) {
1181 // If this value is null it means there is an error in the module;
1182 // leave debug_info_header_dirty=true.
1183 const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
1184 const last_dbg_info_decl = self.dbg_info_decl_last.?;
1185 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
1186
1187 // We have a function to compute the upper bound size, because it's needed
1188 // for determining where to put the offset of the first `LinkBlock`.
1189 const needed_bytes = self.dbgInfoNeededHeaderBytes();
1190 var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, needed_bytes);
1191 defer di_buf.deinit();
1192
1193 // initial length - length of the .debug_info contribution for this compilation unit,
1194 // not including the initial length itself.
1195 // We have to come back and write it later after we know the size.
1196 const after_init_len = di_buf.items.len + init_len_size;
1197 // +1 for the final 0 that ends the compilation unit children.
1198 const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1;
1199 const init_len = dbg_info_end - after_init_len;
1200 switch (self.ptr_width) {
1201 .p32 => {
1202 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
1203 },
1204 .p64 => {
1205 di_buf.appendNTimesAssumeCapacity(0xff, 4);
1206 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
1207 },
1208 }
1209 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
1210 const abbrev_offset = self.debug_abbrev_table_offset.?;
1211 switch (self.ptr_width) {
1212 .p32 => {
1213 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);
1214 di_buf.appendAssumeCapacity(4); // address size
1215 },
1216 .p64 => {
1217 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);
1218 di_buf.appendAssumeCapacity(8); // address size
1219 },
1220 }
1221 // Write the form for the compile unit, which must match the abbrev table above.
1222 const name_strp = try self.makeDebugString(module.root_pkg.root_src_path);
1223 const comp_dir_strp = try self.makeDebugString(module.root_pkg.root_src_directory.path orelse ".");
1224 const producer_strp = try self.makeDebugString(link.producer_string);
1225 // Currently only one compilation unit is supported, so the address range is simply1010 // Currently only one compilation unit is supported, so the address range is simply
1226 // identical to the main program header virtual address and memory size.1011 // identical to the main program header virtual address and memory size.
1227 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];1012 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1228 const low_pc = text_phdr.p_vaddr;1013 const low_pc = text_phdr.p_vaddr;
1229 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;1014 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
12301015 try self.dwarf.?.writeDbgInfoHeader(&self.base, module, low_pc, high_pc);
1231 di_buf.appendAssumeCapacity(abbrev_compile_unit);
1232 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT.stmt_list, DW.FORM.sec_offset
1233 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
1234 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
1235 self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
1236 self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp);
1237 self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp);
1238 // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
1239 // http://dwarfstd.org/ShowIssue.php?issue=171115.1
1240 // Until then we say it is C99.
1241 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99, target_endian);
1242
1243 if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
1244 // Move the first N decls to the end to make more padding for the header.
1245 @panic("TODO: handle .debug_info header exceeding its padding");
1246 }
1247 const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len;
1248 try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.sh_offset);
1249 self.debug_info_header_dirty = false;1016 self.debug_info_header_dirty = false;
1250 }1017 }
12511018
1252 if (self.debug_aranges_section_dirty) {1019 if (self.debug_aranges_section_dirty) {
1253 const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?];
1254
1255 // Enough for all the data without resizing. When support for more compilation units
1256 // is added, the size of this section will become more variable.
1257 var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, 100);
1258 defer di_buf.deinit();
1259
1260 // initial length - length of the .debug_aranges contribution for this compilation unit,
1261 // not including the initial length itself.
1262 // We have to come back and write it later after we know the size.
1263 const init_len_index = di_buf.items.len;
1264 di_buf.items.len += init_len_size;
1265 const after_init_len = di_buf.items.len;
1266 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version
1267 // When more than one compilation unit is supported, this will be the offset to it.
1268 // For now it is always at offset 0 in .debug_info.
1269 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // .debug_info offset
1270 di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size
1271 di_buf.appendAssumeCapacity(0); // segment_selector_size
1272
1273 const end_header_offset = di_buf.items.len;
1274 const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2);
1275 di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
1276
1277 // Currently only one compilation unit is supported, so the address range is simply1020 // Currently only one compilation unit is supported, so the address range is simply
1278 // identical to the main program header virtual address and memory size.1021 // identical to the main program header virtual address and memory size.
1279 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];1022 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1280 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr);1023 try self.dwarf.?.writeDbgAranges(&self.base, text_phdr.p_vaddr, text_phdr.p_memsz);
1281 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_memsz);
1282
1283 // Sentinel.
1284 self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
1285 self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
1286
1287 // Go back and populate the initial length.
1288 const init_len = di_buf.items.len - after_init_len;
1289 switch (self.ptr_width) {
1290 .p32 => {
1291 mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
1292 },
1293 .p64 => {
1294 // initial length - length of the .debug_aranges contribution for this compilation unit,
1295 // not including the initial length itself.
1296 di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
1297 mem.writeInt(u64, di_buf.items[init_len_index + 4 ..][0..8], init_len, target_endian);
1298 },
1299 }
1300
1301 const needed_size = di_buf.items.len;
1302 const allocated_size = self.allocatedSize(debug_aranges_sect.sh_offset);
1303 if (needed_size > allocated_size) {
1304 debug_aranges_sect.sh_size = 0; // free the space
1305 debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16);
1306 }
1307 debug_aranges_sect.sh_size = needed_size;
1308 log.debug(".debug_aranges start=0x{x} end=0x{x}", .{
1309 debug_aranges_sect.sh_offset,
1310 debug_aranges_sect.sh_offset + needed_size,
1311 });
1312
1313 try self.base.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset);
1314 if (!self.shdr_table_dirty) {1024 if (!self.shdr_table_dirty) {
1315 // Then it won't get written with the others and we need to do it.1025 // Then it won't get written with the others and we need to do it.
1316 try self.writeSectHeader(self.debug_aranges_section_index.?);1026 try self.writeSectHeader(self.debug_aranges_section_index.?);
1317 }1027 }
1318
1319 self.debug_aranges_section_dirty = false;1028 self.debug_aranges_section_dirty = false;
1320 }1029 }
1321 if (self.debug_line_header_dirty) debug_line: {
1322 if (self.dbg_line_fn_first == null) {
1323 break :debug_line; // Error in module; leave debug_line_header_dirty=true.
1324 }
1325 const dbg_line_prg_off = self.getDebugLineProgramOff();
1326 const dbg_line_prg_end = self.getDebugLineProgramEnd();
1327 assert(dbg_line_prg_end != 0);
1328
1329 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
1330
1331 // The size of this header is variable, depending on the number of directories,
1332 // files, and padding. We have a function to compute the upper bound size, however,
1333 // because it's needed for determining where to put the offset of the first `SrcFn`.
1334 const needed_bytes = self.dbgLineNeededHeaderBytes();
1335 var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, needed_bytes);
1336 defer di_buf.deinit();
1337
1338 // initial length - length of the .debug_line contribution for this compilation unit,
1339 // not including the initial length itself.
1340 const after_init_len = di_buf.items.len + init_len_size;
1341 const init_len = dbg_line_prg_end - after_init_len;
1342 switch (self.ptr_width) {
1343 .p32 => {
1344 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
1345 },
1346 .p64 => {
1347 di_buf.appendNTimesAssumeCapacity(0xff, 4);
1348 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
1349 },
1350 }
1351
1352 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version
1353
1354 // Empirically, debug info consumers do not respect this field, or otherwise
1355 // consider it to be an error when it does not point exactly to the end of the header.
1356 // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
1357 // padding rather than this field.
1358 const before_header_len = di_buf.items.len;
1359 di_buf.items.len += ptr_width_bytes; // We will come back and write this.
1360 const after_header_len = di_buf.items.len;
1361
1362 const opcode_base = DW.LNS.set_isa + 1;
1363 di_buf.appendSliceAssumeCapacity(&[_]u8{
1364 1, // minimum_instruction_length
1365 1, // maximum_operations_per_instruction
1366 1, // default_is_stmt
1367 1, // line_base (signed)
1368 1, // line_range
1369 opcode_base,
1370
1371 // Standard opcode lengths. The number of items here is based on `opcode_base`.
1372 // The value is the number of LEB128 operands the instruction takes.
1373 0, // `DW.LNS.copy`
1374 1, // `DW.LNS.advance_pc`
1375 1, // `DW.LNS.advance_line`
1376 1, // `DW.LNS.set_file`
1377 1, // `DW.LNS.set_column`
1378 0, // `DW.LNS.negate_stmt`
1379 0, // `DW.LNS.set_basic_block`
1380 0, // `DW.LNS.const_add_pc`
1381 1, // `DW.LNS.fixed_advance_pc`
1382 0, // `DW.LNS.set_prologue_end`
1383 0, // `DW.LNS.set_epilogue_begin`
1384 1, // `DW.LNS.set_isa`
1385 0, // include_directories (none except the compilation unit cwd)
1386 });
1387 // file_names[0]
1388 di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name
1389 di_buf.appendSliceAssumeCapacity(&[_]u8{
1390 0, // null byte for the relative path name
1391 0, // directory_index
1392 0, // mtime (TODO supply this)
1393 0, // file size bytes (TODO supply this)
1394 0, // file_names sentinel
1395 });
13961030
1397 const header_len = di_buf.items.len - after_header_len;1031 if (self.debug_line_header_dirty) {
1398 switch (self.ptr_width) {1032 try self.dwarf.?.writeDbgLineHeader(&self.base, module);
1399 .p32 => {
1400 mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian);
1401 },
1402 .p64 => {
1403 mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian);
1404 },
1405 }
1406
1407 // We use NOPs because consumers empirically do not respect the header length field.
1408 if (di_buf.items.len > dbg_line_prg_off) {
1409 // Move the first N files to the end to make more padding for the header.
1410 @panic("TODO: handle .debug_line header exceeding its padding");
1411 }
1412 const jmp_amt = dbg_line_prg_off - di_buf.items.len;
1413 try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset);
1414 self.debug_line_header_dirty = false;1033 self.debug_line_header_dirty = false;
1415 }1034 }
14161035
...@@ -1481,11 +1100,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -1481,11 +1100,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
1481 self.shstrtab_dirty = false;1100 self.shstrtab_dirty = false;
1482 }1101 }
1483 }1102 }
1103
1484 {1104 {
1485 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];1105 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
1486 if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) {1106 const dwarf = self.dwarf.?;
1107 if (self.debug_strtab_dirty or dwarf.strtab.items.len != debug_strtab_sect.sh_size) {
1487 const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset);1108 const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset);
1488 const needed_size = self.debug_strtab.items.len;1109 const needed_size = dwarf.strtab.items.len;
14891110
1490 if (needed_size > allocated_size) {1111 if (needed_size > allocated_size) {
1491 debug_strtab_sect.sh_size = 0; // free the space1112 debug_strtab_sect.sh_size = 0; // free the space
...@@ -1494,7 +1115,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -1494,7 +1115,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
1494 debug_strtab_sect.sh_size = needed_size;1115 debug_strtab_sect.sh_size = needed_size;
1495 log.debug("debug_strtab start=0x{x} end=0x{x}", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size });1116 log.debug("debug_strtab start=0x{x} end=0x{x}", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size });
14961117
1497 try self.base.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset);1118 try self.base.file.?.pwriteAll(dwarf.strtab.items, debug_strtab_sect.sh_offset);
1498 if (!self.shdr_table_dirty) {1119 if (!self.shdr_table_dirty) {
1499 // Then it won't get written with the others and we need to do it.1120 // Then it won't get written with the others and we need to do it.
1500 try self.writeSectHeader(self.debug_str_section_index.?);1121 try self.writeSectHeader(self.debug_str_section_index.?);
...@@ -1502,6 +1123,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -1502,6 +1123,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
1502 self.debug_strtab_dirty = false;1123 self.debug_strtab_dirty = false;
1503 }1124 }
1504 }1125 }
1126
1505 if (self.shdr_table_dirty) {1127 if (self.shdr_table_dirty) {
1506 const shsize: u64 = switch (self.ptr_width) {1128 const shsize: u64 = switch (self.ptr_width) {
1507 .p32 => @sizeOf(elf.Elf32_Shdr),1129 .p32 => @sizeOf(elf.Elf32_Shdr),
...@@ -2340,7 +1962,6 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void {...@@ -2340,7 +1962,6 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void {
2340 i += 1;1962 i += 1;
2341 }1963 }
2342 }1964 }
2343 // TODO process free list for dbg info just like we do above for vaddrs
23441965
2345 if (self.atoms.getPtr(phdr_index)) |last_block| {1966 if (self.atoms.getPtr(phdr_index)) |last_block| {
2346 if (last_block.* == text_block) {1967 if (last_block.* == text_block) {
...@@ -2353,14 +1974,6 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void {...@@ -2353,14 +1974,6 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void {
2353 }1974 }
2354 }1975 }
23551976
2356 if (self.dbg_info_decl_first == text_block) {
2357 self.dbg_info_decl_first = text_block.dbg_info_next;
2358 }
2359 if (self.dbg_info_decl_last == text_block) {
2360 // TODO shrink the .debug_info section size here
2361 self.dbg_info_decl_last = text_block.dbg_info_prev;
2362 }
2363
2364 if (text_block.prev) |prev| {1977 if (text_block.prev) |prev| {
2365 prev.next = text_block.next;1978 prev.next = text_block.next;
23661979
...@@ -2379,18 +1992,8 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void {...@@ -2379,18 +1992,8 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void {
2379 text_block.next = null;1992 text_block.next = null;
2380 }1993 }
23811994
2382 if (text_block.dbg_info_prev) |prev| {1995 if (self.dwarf) |*dw| {
2383 prev.dbg_info_next = text_block.dbg_info_next;1996 dw.freeAtom(&text_block.dbg_info_atom);
2384
2385 // TODO the free list logic like we do for text blocks above
2386 } else {
2387 text_block.dbg_info_prev = null;
2388 }
2389
2390 if (text_block.dbg_info_next) |next| {
2391 next.dbg_info_prev = text_block.dbg_info_prev;
2392 } else {
2393 text_block.dbg_info_next = null;
2394 }1997 }
2395}1998}
23961999
...@@ -2619,26 +2222,9 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {...@@ -2619,26 +2222,9 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
26192222
2620 self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};2223 self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
2621 }2224 }
2622 // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing2225
2623 // is desired for both.2226 if (self.dwarf) |*dw| {
2624 _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf);2227 dw.freeDecl(decl);
2625 if (decl.fn_link.elf.prev) |prev| {
2626 self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
2627 prev.next = decl.fn_link.elf.next;
2628 if (decl.fn_link.elf.next) |next| {
2629 next.prev = prev;
2630 } else {
2631 self.dbg_line_fn_last = prev;
2632 }
2633 } else if (decl.fn_link.elf.next) |next| {
2634 self.dbg_line_fn_first = next;
2635 next.prev = null;
2636 }
2637 if (self.dbg_line_fn_first == &decl.fn_link.elf) {
2638 self.dbg_line_fn_first = decl.fn_link.elf.next;
2639 }
2640 if (self.dbg_line_fn_last == &decl.fn_link.elf) {
2641 self.dbg_line_fn_last = decl.fn_link.elf.prev;
2642 }2228 }
2643}2229}
26442230
...@@ -2739,62 +2325,6 @@ fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8...@@ -2739,62 +2325,6 @@ fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8
2739 return local_sym;2325 return local_sym;
2740}2326}
27412327
2742fn 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
2798pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {2328pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
2799 if (build_options.skip_non_native and builtin.object_format != .elf) {2329 if (build_options.skip_non_native and builtin.object_format != .elf) {
2800 @panic("Attempted to compile for object format that was disabled by build configuration");2330 @panic("Attempted to compile for object format that was disabled by build configuration");
...@@ -2809,96 +2339,33 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven...@@ -2809,96 +2339,33 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
2809 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2339 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2810 defer code_buffer.deinit();2340 defer code_buffer.deinit();
28112341
2812 // For functions we need to add a prologue to the debug line program.
2813 var dbg_line_buffer = try std.ArrayList(u8).initCapacity(self.base.allocator, 26);
2814 defer dbg_line_buffer.deinit();
2815
2816 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
2817 defer dbg_info_buffer.deinit();
2818
2819 var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
2820 defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs);
2821
2822 const decl = func.owner_decl;2342 const decl = func.owner_decl;
2823 self.freeUnnamedConsts(decl);2343 self.freeUnnamedConsts(decl);
28242344
2825 const decl_name = try decl.getFullyQualifiedName(self.base.allocator);2345 var debug_buffers_buf: Dwarf.DeclDebugBuffers = undefined;
2826 defer self.base.allocator.free(decl_name);2346 const debug_buffers = if (self.dwarf) |*dw| blk: {
28272347 debug_buffers_buf = try dw.initDeclDebugInfo(decl);
2828 log.debug("updateFunc {s}{*}", .{ decl_name, func.owner_decl });2348 break :blk &debug_buffers_buf;
2829 log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{2349 } else null;
2830 decl.src_line,2350 defer {
2831 func.lbrace_line,2351 if (debug_buffers) |dbg| {
2832 func.rbrace_line,2352 dbg.dbg_line_buffer.deinit();
2833 });2353 dbg.dbg_info_buffer.deinit();
2834 const line = @intCast(u28, decl.src_line + func.lbrace_line);2354 deinitRelocs(self.base.allocator, &dbg.dbg_info_type_relocs);
2835
2836 const ptr_width_bytes = self.ptrWidthBytes();
2837 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
2838 DW.LNS.extended_op,
2839 ptr_width_bytes + 1,
2840 DW.LNE.set_address,
2841 });
2842 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
2843 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
2844 dbg_line_buffer.items.len += ptr_width_bytes;
2845
2846 dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line);
2847 // This is the "relocatable" relative line offset from the previous function's end curly
2848 // to this function's begin curly.
2849 assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
2850 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
2851 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line);
2852
2853 dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file);
2854 assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
2855 // Once we support more than one source file, this will have the ability to be more
2856 // than one possible value.
2857 const file_index = 1;
2858 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
2859
2860 // Emit a line for the begin curly with prologue_end=false. The codegen will
2861 // do the work of setting prologue_end=true and epilogue_begin=true.
2862 dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy);
2863
2864 // .debug_info subprogram
2865 const decl_name_with_null = decl_name[0 .. decl_name.len + 1];
2866 try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len);
2867
2868 const fn_ret_type = decl.ty.fnReturnType();
2869 const fn_ret_has_bits = fn_ret_type.hasRuntimeBits();
2870 if (fn_ret_has_bits) {
2871 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
2872 } else {
2873 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
2874 }
2875 // These get overwritten after generating the machine code. These values are
2876 // "relocations" and have to be in this fixed place so that functions can be
2877 // moved in virtual address space.
2878 assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
2879 dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT.low_pc, DW.FORM.addr
2880 assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
2881 dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4
2882 if (fn_ret_has_bits) {
2883 const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
2884 if (!gop.found_existing) {
2885 gop.value_ptr.* = .{
2886 .off = undefined,
2887 .relocs = .{},
2888 };
2889 }2355 }
2890 try gop.value_ptr.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
2891 dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4
2892 }2356 }
2893 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string
28942357
2895 const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{2358 const res = if (debug_buffers) |dbg|
2896 .dwarf = .{2359 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
2897 .dbg_line = &dbg_line_buffer,2360 .dwarf = .{
2898 .dbg_info = &dbg_info_buffer,2361 .dbg_line = &dbg.dbg_line_buffer,
2899 .dbg_info_type_relocs = &dbg_info_type_relocs,2362 .dbg_info = &dbg.dbg_info_buffer,
2900 },2363 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
2901 });2364 },
2365 })
2366 else
2367 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
2368
2902 const code = switch (res) {2369 const code = switch (res) {
2903 .appended => code_buffer.items,2370 .appended => code_buffer.items,
2904 .fail => |em| {2371 .fail => |em| {
...@@ -2907,128 +2374,14 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven...@@ -2907,128 +2374,14 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
2907 return;2374 return;
2908 },2375 },
2909 };2376 };
2910
2911 const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC);2377 const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC);
29122378 if (debug_buffers) |dbg| {
2913 const target_endian = self.base.options.target.cpu.arch.endian();2379 try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg);
2914
2915 // Since the Decl is a function, we need to update the .debug_line program.
2916 // Perform the relocations based on vaddr.
2917 switch (self.ptr_width) {
2918 .p32 => {
2919 {
2920 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
2921 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
2922 }
2923 {
2924 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4];
2925 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
2926 }
2927 },
2928 .p64 => {
2929 {
2930 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
2931 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
2932 }
2933 {
2934 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
2935 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
2936 }
2937 },
2938 }2380 }
2939 {
2940 const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4];
2941 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian);
2942 }
2943
2944 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence });
2945
2946 // Now we have the full contents and may allocate a region to store it.
2947
2948 // This logic is nearly identical to the logic below in `updateDeclDebugInfoAllocation` for
2949 // `TextBlock` and the .debug_info. If you are editing this logic, you
2950 // probably need to edit that logic too.
2951
2952 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
2953 const src_fn = &decl.fn_link.elf;
2954 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
2955 if (self.dbg_line_fn_last) |last| not_first: {
2956 if (src_fn.next) |next| {
2957 // Update existing function - non-last item.
2958 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
2959 // It grew too big, so we move it to a new location.
2960 if (src_fn.prev) |prev| {
2961 self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
2962 prev.next = src_fn.next;
2963 }
2964 assert(src_fn.prev != next);
2965 next.prev = src_fn.prev;
2966 src_fn.next = null;
2967 // Populate where it used to be with NOPs.
2968 const file_pos = debug_line_sect.sh_offset + src_fn.off;
2969 try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
2970 // TODO Look at the free list before appending at the end.
2971 src_fn.prev = last;
2972 last.next = src_fn;
2973 self.dbg_line_fn_last = src_fn;
2974
2975 src_fn.off = last.off + padToIdeal(last.len);
2976 }
2977 } else if (src_fn.prev == null) {
2978 if (src_fn == last) {
2979 // Special case: there is only 1 function and it is being updated.
2980 // In this case there is nothing to do. The function's length has
2981 // already been updated, and the logic below takes care of
2982 // resizing the .debug_line section.
2983 break :not_first;
2984 }
2985 // Append new function.
2986 // TODO Look at the free list before appending at the end.
2987 src_fn.prev = last;
2988 last.next = src_fn;
2989 self.dbg_line_fn_last = src_fn;
29902381
2991 src_fn.off = last.off + padToIdeal(last.len);2382 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
2992 }2383 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
2993 } else {2384 return self.updateDeclExports(module, decl, decl_exports);
2994 // This is the first function of the Line Number Program.
2995 self.dbg_line_fn_first = src_fn;
2996 self.dbg_line_fn_last = src_fn;
2997
2998 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes());
2999 }
3000
3001 const last_src_fn = self.dbg_line_fn_last.?;
3002 const needed_size = last_src_fn.off + last_src_fn.len;
3003 if (needed_size != debug_line_sect.sh_size) {
3004 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
3005 const new_offset = self.findFreeSpace(needed_size, 1);
3006 const existing_size = last_src_fn.off;
3007 log.debug("moving .debug_line section: {d} bytes from 0x{x} to 0x{x}", .{
3008 existing_size,
3009 debug_line_sect.sh_offset,
3010 new_offset,
3011 });
3012 const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size);
3013 if (amt != existing_size) return error.InputOutput;
3014 debug_line_sect.sh_offset = new_offset;
3015 }
3016 debug_line_sect.sh_size = needed_size;
3017 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
3018 self.debug_line_header_dirty = true;
3019 }
3020 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
3021 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
3022
3023 // We only have support for one compilation unit so far, so the offsets are directly
3024 // from the .debug_line section.
3025 const file_pos = debug_line_sect.sh_offset + src_fn.off;
3026 try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
3027
3028 // .debug_info - End the TAG.subprogram children.
3029 try dbg_info_buffer.append(0);
3030
3031 return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer);
3032}2385}
30332386
3034pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {2387pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
...@@ -3057,29 +2410,42 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {...@@ -3057,29 +2410,42 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
3057 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2410 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3058 defer code_buffer.deinit();2411 defer code_buffer.deinit();
30592412
3060 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);2413 var debug_buffers_buf: Dwarf.DeclDebugBuffers = undefined;
3061 defer dbg_line_buffer.deinit();2414 const debug_buffers = if (self.dwarf) |*dw| blk: {
30622415 debug_buffers_buf = try dw.initDeclDebugInfo(decl);
3063 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);2416 break :blk &debug_buffers_buf;
3064 defer dbg_info_buffer.deinit();2417 } else null;
30652418 defer {
3066 var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};2419 if (debug_buffers) |dbg| {
3067 defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs);2420 dbg.dbg_line_buffer.deinit();
2421 dbg.dbg_info_buffer.deinit();
2422 deinitRelocs(self.base.allocator, &dbg.dbg_info_type_relocs);
2423 }
2424 }
30682425
3069 // TODO implement .debug_info for global variables2426 // TODO implement .debug_info for global variables
3070 const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;2427 const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
3071 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{2428 const res = if (debug_buffers) |dbg|
3072 .ty = decl.ty,2429 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
3073 .val = decl_val,2430 .ty = decl.ty,
3074 }, &code_buffer, .{2431 .val = decl_val,
3075 .dwarf = .{2432 }, &code_buffer, .{
3076 .dbg_line = &dbg_line_buffer,2433 .dwarf = .{
3077 .dbg_info = &dbg_info_buffer,2434 .dbg_line = &dbg.dbg_line_buffer,
3078 .dbg_info_type_relocs = &dbg_info_type_relocs,2435 .dbg_info = &dbg.dbg_info_buffer,
3079 },2436 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
3080 }, .{2437 },
3081 .parent_atom_index = decl.link.elf.local_sym_index,2438 }, .{
3082 });2439 .parent_atom_index = decl.link.elf.local_sym_index,
2440 })
2441 else
2442 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
2443 .ty = decl.ty,
2444 .val = decl_val,
2445 }, &code_buffer, .none, .{
2446 .parent_atom_index = decl.link.elf.local_sym_index,
2447 });
2448
3083 const code = switch (res) {2449 const code = switch (res) {
3084 .externally_managed => |x| x,2450 .externally_managed => |x| x,
3085 .appended => code_buffer.items,2451 .appended => code_buffer.items,
...@@ -3090,8 +2456,14 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {...@@ -3090,8 +2456,14 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
3090 },2456 },
3091 };2457 };
30922458
3093 _ = try self.updateDeclCode(decl, code, elf.STT_OBJECT);2459 const local_sym = try self.updateDeclCode(decl, code, elf.STT_OBJECT);
3094 return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer);2460 if (debug_buffers) |dbg| {
2461 try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg);
2462 }
2463
2464 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
2465 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
2466 return self.updateDeclExports(module, decl, decl_exports);
3095}2467}
30962468
3097pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl) !u32 {2469pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl) !u32 {
...@@ -3170,304 +2542,6 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl...@@ -3170,304 +2542,6 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl
3170 return atom.local_sym_index;2542 return atom.local_sym_index;
3171}2543}
31722544
3173/// Asserts the type has codegen bits.
3174fn 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
3367fn 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
3423fn 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
3471pub fn updateDeclExports(2545pub fn updateDeclExports(
3472 self: *Elf,2546 self: *Elf,
3473 module: *Module,2547 module: *Module,
...@@ -3568,20 +2642,9 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec...@@ -3568,20 +2642,9 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
3568 log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });2642 log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
35692643
3570 if (self.llvm_object) |_| return;2644 if (self.llvm_object) |_| return;
35712645 if (self.dwarf) |*dw| {
3572 const func = decl.val.castTag(.function).?.data;2646 try dw.updateDeclLineNumber(&self.base, decl);
3573 log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{2647 }
3574 decl.src_line,
3575 func.lbrace_line,
3576 func.rbrace_line,
3577 });
3578 const line = @intCast(u28, decl.src_line + func.lbrace_line);
3579
3580 const shdr = &self.sections.items[self.debug_line_section_index.?];
3581 const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
3582 var data: [4]u8 = undefined;
3583 leb128.writeUnsignedFixed(4, &data, line);
3584 try self.base.file.?.pwriteAll(&data, file_pos);
3585}2648}
35862649
3587pub fn deleteExport(self: *Elf, exp: Export) void {2650pub fn deleteExport(self: *Elf, exp: Export) void {
...@@ -3806,201 +2869,6 @@ fn archPtrWidthBytes(self: Elf) u8 {...@@ -3806,201 +2869,6 @@ fn archPtrWidthBytes(self: Elf) u8 {
3806 return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8);2869 return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8);
3807}2870}
38082871
3809/// The reloc offset for the virtual address of a function in its Line Number Program.
3810/// Size is a virtual address integer.
3811const 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.
3814const 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.
3818fn getRelocDbgLineOff(self: Elf) usize {
3819 return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
3820}
3821
3822fn getRelocDbgFileIndex(self: Elf) usize {
3823 return self.getRelocDbgLineOff() + 5;
3824}
3825
3826fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 {
3827 return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
3828}
3829
3830fn 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
3844fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
3845 _ = self;
3846 return 120;
3847}
3848
3849const 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`).
3856fn 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.
3934fn 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
4004fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {2872fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
4005 return .{2873 return .{
4006 .p_type = phdr.p_type,2874 .p_type = phdr.p_type,
src/link/MachO.zig+10-72
...@@ -295,26 +295,6 @@ pub const Export = struct {...@@ -295,26 +295,6 @@ pub const Export = struct {
295 sym_index: ?u32 = null,295 sym_index: ?u32 = null,
296};296};
297297
298pub 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
318pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {298pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
319 assert(options.object_format == .macho);299 assert(options.object_format == .macho);
320300
...@@ -376,6 +356,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -376,6 +356,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
376356
377 self.d_sym = .{357 self.d_sym = .{
378 .base = self,358 .base = self,
359 .dwarf = link.File.Dwarf.init(allocator, .macho, options.target),
379 .file = d_sym_file,360 .file = d_sym_file,
380 };361 };
381 }362 }
...@@ -3523,7 +3504,6 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)...@@ -3523,7 +3504,6 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
3523 i += 1;3504 i += 1;
3524 }3505 }
3525 }3506 }
3526 // TODO process free list for dbg info just like we do above for vaddrs
35273507
3528 if (self.atoms.getPtr(match)) |last_atom| {3508 if (self.atoms.getPtr(match)) |last_atom| {
3529 if (last_atom.* == atom) {3509 if (last_atom.* == atom) {
...@@ -3536,16 +3516,6 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)...@@ -3536,16 +3516,6 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
3536 }3516 }
3537 }3517 }
35383518
3539 if (self.d_sym) |*d_sym| {
3540 if (d_sym.dbg_info_decl_first == atom) {
3541 d_sym.dbg_info_decl_first = atom.dbg_info_next;
3542 }
3543 if (d_sym.dbg_info_decl_last == atom) {
3544 // TODO shrink the .debug_info section size here
3545 d_sym.dbg_info_decl_last = atom.dbg_info_prev;
3546 }
3547 }
3548
3549 if (atom.prev) |prev| {3519 if (atom.prev) |prev| {
3550 prev.next = atom.next;3520 prev.next = atom.next;
35513521
...@@ -3564,18 +3534,8 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)...@@ -3564,18 +3534,8 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
3564 atom.next = null;3534 atom.next = null;
3565 }3535 }
35663536
3567 if (atom.dbg_info_prev) |prev| {3537 if (self.d_sym) |*d_sym| {
3568 prev.dbg_info_next = atom.dbg_info_next;3538 d_sym.dwarf.freeAtom(&atom.dbg_info_atom);
3569
3570 // TODO the free list logic like we do for atoms above
3571 } else {
3572 atom.dbg_info_prev = null;
3573 }
3574
3575 if (atom.dbg_info_next) |next| {
3576 next.dbg_info_prev = atom.dbg_info_prev;
3577 } else {
3578 atom.dbg_info_next = null;
3579 }3539 }
3580}3540}
35813541
...@@ -3725,9 +3685,9 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -3725,9 +3685,9 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
3725 var code_buffer = std.ArrayList(u8).init(self.base.allocator);3685 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3726 defer code_buffer.deinit();3686 defer code_buffer.deinit();
37273687
3728 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;3688 var debug_buffers_buf: link.File.Dwarf.DeclDebugBuffers = undefined;
3729 const debug_buffers = if (self.d_sym) |*d_sym| blk: {3689 const debug_buffers = if (self.d_sym) |*d_sym| blk: {
3730 debug_buffers_buf = try d_sym.initDeclDebugBuffers(self.base.allocator, module, decl);3690 debug_buffers_buf = try d_sym.initDeclDebugInfo(module, decl);
3731 break :blk &debug_buffers_buf;3691 break :blk &debug_buffers_buf;
3732 } else null;3692 } else null;
3733 defer {3693 defer {
...@@ -3766,7 +3726,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -3766,7 +3726,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
37663726
3767 if (debug_buffers) |db| {3727 if (debug_buffers) |db| {
3768 if (self.d_sym) |*d_sym| {3728 if (self.d_sym) |*d_sym| {
3769 try d_sym.commitDeclDebugInfo(self.base.allocator, module, decl, db);3729 try d_sym.commitDeclDebugInfo(module, decl, db);
3770 }3730 }
3771 }3731 }
37723732
...@@ -3805,9 +3765,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De...@@ -3805,9 +3765,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De
3805 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment));3765 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment));
3806 try self.atom_by_index_table.putNoClobber(self.base.allocator, local_sym_index, atom);3766 try self.atom_by_index_table.putNoClobber(self.base.allocator, local_sym_index, atom);
38073767
3808 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{3768 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
3809 .none = .{},
3810 }, .{
3811 .parent_atom_index = local_sym_index,3769 .parent_atom_index = local_sym_index,
3812 });3770 });
3813 const code = switch (res) {3771 const code = switch (res) {
...@@ -3869,9 +3827,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -3869,9 +3827,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
3869 var code_buffer = std.ArrayList(u8).init(self.base.allocator);3827 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3870 defer code_buffer.deinit();3828 defer code_buffer.deinit();
38713829
3872 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;3830 var debug_buffers_buf: link.File.Dwarf.DeclDebugBuffers = undefined;
3873 const debug_buffers = if (self.d_sym) |*d_sym| blk: {3831 const debug_buffers = if (self.d_sym) |*d_sym| blk: {
3874 debug_buffers_buf = try d_sym.initDeclDebugBuffers(self.base.allocator, module, decl);3832 debug_buffers_buf = try d_sym.initDeclDebugInfo(module, decl);
3875 break :blk &debug_buffers_buf;3833 break :blk &debug_buffers_buf;
3876 } else null;3834 } else null;
3877 defer {3835 defer {
...@@ -4364,27 +4322,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {...@@ -4364,27 +4322,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
4364 decl.link.macho.local_sym_index = 0;4322 decl.link.macho.local_sym_index = 0;
4365 }4323 }
4366 if (self.d_sym) |*d_sym| {4324 if (self.d_sym) |*d_sym| {
4367 // TODO make this logic match freeAtom. Maybe abstract the logic4325 d_sym.dwarf.freeDecl(decl);
4368 // out since the same thing is desired for both.
4369 _ = d_sym.dbg_line_fn_free_list.remove(&decl.fn_link.macho);
4370 if (decl.fn_link.macho.prev) |prev| {
4371 d_sym.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
4372 prev.next = decl.fn_link.macho.next;
4373 if (decl.fn_link.macho.next) |next| {
4374 next.prev = prev;
4375 } else {
4376 d_sym.dbg_line_fn_last = prev;
4377 }
4378 } else if (decl.fn_link.macho.next) |next| {
4379 d_sym.dbg_line_fn_first = next;
4380 next.prev = null;
4381 }
4382 if (d_sym.dbg_line_fn_first == &decl.fn_link.macho) {
4383 d_sym.dbg_line_fn_first = decl.fn_link.macho.next;
4384 }
4385 if (d_sym.dbg_line_fn_last == &decl.fn_link.macho) {
4386 d_sym.dbg_line_fn_last = decl.fn_link.macho.prev;
4387 }
4388 }4326 }
4389}4327}
43904328
src/link/MachO/Atom.zig+3-12
...@@ -13,6 +13,7 @@ const trace = @import("../../tracy.zig").trace;...@@ -13,6 +13,7 @@ const trace = @import("../../tracy.zig").trace;
1313
14const Allocator = mem.Allocator;14const Allocator = mem.Allocator;
15const Arch = std.Target.Cpu.Arch;15const Arch = std.Target.Cpu.Arch;
16const Dwarf = @import("../Dwarf.zig");
16const MachO = @import("../MachO.zig");17const MachO = @import("../MachO.zig");
17const Object = @import("Object.zig");18const Object = @import("Object.zig");
18const StringIndexAdapter = std.hash_map.StringIndexAdapter;19const StringIndexAdapter = std.hash_map.StringIndexAdapter;
...@@ -71,14 +72,7 @@ stab: ?Stab = null,...@@ -71,14 +72,7 @@ stab: ?Stab = null,
71next: ?*Atom,72next: ?*Atom,
72prev: ?*Atom,73prev: ?*Atom,
7374
74/// Previous/next linked list pointers.75dbg_info_atom: Dwarf.DebugInfoAtom,
75/// This is the linked list node for this Decl's corresponding .debug_info tag.
76dbg_info_prev: ?*Atom,
77dbg_info_next: ?*Atom,
78/// Offset into .debug_info pointing to the tag for this Decl.
79dbg_info_off: u32,
80/// Size of the .debug_info tag for this Decl, not including padding.
81dbg_info_len: u32,
8276
83dirty: bool = true,77dirty: bool = true,
8478
...@@ -188,10 +182,7 @@ pub const empty = Atom{...@@ -188,10 +182,7 @@ pub const empty = Atom{
188 .alignment = 0,182 .alignment = 0,
189 .prev = null,183 .prev = null,
190 .next = null,184 .next = null,
191 .dbg_info_prev = null,185 .dbg_info_atom = undefined,
192 .dbg_info_next = null,
193 .dbg_info_off = undefined,
194 .dbg_info_len = undefined,
195};186};
196187
197pub fn deinit(self: *Atom, allocator: Allocator) void {188pub fn deinit(self: *Atom, allocator: Allocator) void {
src/link/MachO/DebugSymbols.zig+31-1139
...@@ -1,29 +1,27 @@...@@ -1,29 +1,27 @@
1const DebugSymbols = @This();1const DebugSymbols = @This();
22
3const std = @import("std");3const std = @import("std");
4const build_options = @import("build_options");
4const assert = std.debug.assert;5const assert = std.debug.assert;
5const fs = std.fs;6const fs = std.fs;
7const link = @import("../../link.zig");
6const log = std.log.scoped(.link);8const log = std.log.scoped(.link);
7const leb128 = std.leb;
8const macho = std.macho;9const macho = std.macho;
10const makeStaticString = MachO.makeStaticString;
9const math = std.math;11const math = std.math;
10const mem = std.mem;12const mem = std.mem;
11const DW = std.dwarf;13const padToIdeal = MachO.padToIdeal;
12const leb = std.leb;
13const Allocator = mem.Allocator;
14
15const build_options = @import("build_options");
16const trace = @import("../../tracy.zig").trace;14const trace = @import("../../tracy.zig").trace;
17const Module = @import("../../Module.zig");15
18const Type = @import("../../type.zig").Type;16const Allocator = mem.Allocator;
19const link = @import("../../link.zig");17const Dwarf = @import("../Dwarf.zig");
20const MachO = @import("../MachO.zig");18const MachO = @import("../MachO.zig");
19const Module = @import("../../Module.zig");
21const TextBlock = MachO.TextBlock;20const TextBlock = MachO.TextBlock;
22const SrcFn = MachO.SrcFn;21const Type = @import("../../type.zig").Type;
23const makeStaticString = MachO.makeStaticString;
24const padToIdeal = MachO.padToIdeal;
2522
26base: *MachO,23base: *MachO,
24dwarf: Dwarf,
27file: fs.File,25file: fs.File,
2826
29/// Table of all load commands27/// Table of all load commands
...@@ -54,23 +52,6 @@ debug_str_section_index: ?u16 = null,...@@ -54,23 +52,6 @@ debug_str_section_index: ?u16 = null,
54debug_aranges_section_index: ?u16 = null,52debug_aranges_section_index: ?u16 = null,
55debug_line_section_index: ?u16 = null,53debug_line_section_index: ?u16 = null,
5654
57debug_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.
61dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
62dbg_line_fn_first: ?*SrcFn = null,
63dbg_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.
67dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
68dbg_info_decl_first: ?*TextBlock = null,
69dbg_info_decl_last: ?*TextBlock = null,
70
71/// Table of debug symbol names aka the debug string table.
72debug_string_table: std.ArrayListUnmanaged(u8) = .{},
73
74load_commands_dirty: bool = false,55load_commands_dirty: bool = false,
75debug_string_table_dirty: bool = false,56debug_string_table_dirty: bool = false,
76debug_abbrev_section_dirty: bool = false,57debug_abbrev_section_dirty: bool = false,
...@@ -78,25 +59,6 @@ debug_aranges_section_dirty: bool = false,...@@ -78,25 +59,6 @@ debug_aranges_section_dirty: bool = false,
78debug_info_header_dirty: bool = false,59debug_info_header_dirty: bool = false,
79debug_line_header_dirty: bool = false,60debug_line_header_dirty: bool = false,
8061
81pub const abbrev_compile_unit = 1;
82pub const abbrev_subprogram = 2;
83pub const abbrev_subprogram_retvoid = 3;
84pub const abbrev_base_type = 4;
85pub const abbrev_ptr_type = 5;
86pub const abbrev_struct_type = 6;
87pub const abbrev_struct_member = 7;
88pub const abbrev_pad1 = 8;
89pub 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.
93const 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.
96const dbg_info_low_pc_reloc_index = 1;
97
98const min_nop_size = 2;
99
100/// You must call this function *after* `MachO.populateMissingMetadata()`62/// You must call this function *after* `MachO.populateMissingMetadata()`
101/// has been called to get a viable debug symbols output.63/// has been called to get a viable debug symbols output.
102pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {64pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {
...@@ -193,10 +155,10 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void...@@ -193,10 +155,10 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
193 }155 }
194156
195 if (self.debug_str_section_index == null) {157 if (self.debug_str_section_index == null) {
196 assert(self.debug_string_table.items.len == 0);158 assert(self.dwarf.strtab.items.len == 0);
197 self.debug_str_section_index = try self.allocateSection(159 self.debug_str_section_index = try self.allocateSection(
198 "__debug_str",160 "__debug_str",
199 @intCast(u32, self.debug_string_table.items.len),161 @intCast(u32, self.dwarf.strtab.items.len),
200 0,162 0,
201 );163 );
202 self.debug_string_table_dirty = true;164 self.debug_string_table_dirty = true;
...@@ -277,7 +239,7 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {...@@ -277,7 +239,7 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
277 return null;239 return null;
278}240}
279241
280fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {242pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {
281 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;243 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
282 var offset: u64 = seg.inner.fileoff;244 var offset: u64 = seg.inner.fileoff;
283 while (self.detectAllocCollision(offset, object_size)) |item_end| {245 while (self.detectAllocCollision(offset, object_size)) |item_end| {
...@@ -290,322 +252,45 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -290,322 +252,45 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
290 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the252 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
291 // Zig source code.253 // Zig source code.
292 const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented;254 const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
293 const init_len_size: usize = 4;
294255
295 if (self.debug_abbrev_section_dirty) {256 if (self.debug_abbrev_section_dirty) {
296 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;257 try self.dwarf.writeDbgAbbrev(&self.base.base);
297 const debug_abbrev_sect = &dwarf_segment.sections.items[self.debug_abbrev_section_index.?];
298
299 // These are LEB encoded but since the values are all less than 127
300 // we can simply append these bytes.
301 const abbrev_buf = [_]u8{
302 abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header
303 DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc,
304 DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr,
305 DW.AT.name, DW.FORM.strp, DW.AT.comp_dir,
306 DW.FORM.strp, DW.AT.producer, DW.FORM.strp,
307 DW.AT.language, DW.FORM.data2, 0,
308 0, // table sentinel
309 abbrev_subprogram,
310 DW.TAG.subprogram,
311 DW.CHILDREN.yes, // header
312 DW.AT.low_pc,
313 DW.FORM.addr,
314 DW.AT.high_pc,
315 DW.FORM.data4,
316 DW.AT.type,
317 DW.FORM.ref4,
318 DW.AT.name,
319 DW.FORM.string,
320 0, 0, // table sentinel
321 abbrev_subprogram_retvoid,
322 DW.TAG.subprogram, DW.CHILDREN.yes, // header
323 DW.AT.low_pc, DW.FORM.addr,
324 DW.AT.high_pc, DW.FORM.data4,
325 DW.AT.name, DW.FORM.string,
326 0,
327 0, // table sentinel
328 abbrev_base_type,
329 DW.TAG.base_type,
330 DW.CHILDREN.no, // header
331 DW.AT.encoding,
332 DW.FORM.data1,
333 DW.AT.byte_size,
334 DW.FORM.data1,
335 DW.AT.name,
336 DW.FORM.string,
337 0,
338 0, // table sentinel
339 abbrev_ptr_type,
340 DW.TAG.pointer_type,
341 DW.CHILDREN.no, // header
342 DW.AT.type,
343 DW.FORM.ref4,
344 0,
345 0, // table sentinel
346 abbrev_struct_type,
347 DW.TAG.structure_type,
348 DW.CHILDREN.yes, // header
349 DW.AT.byte_size,
350 DW.FORM.sdata,
351 DW.AT.name,
352 DW.FORM.string,
353 0,
354 0, // table sentinel
355 abbrev_struct_member,
356 DW.TAG.member,
357 DW.CHILDREN.no, // header
358 DW.AT.name,
359 DW.FORM.string,
360 DW.AT.type,
361 DW.FORM.ref4,
362 DW.AT.data_member_location,
363 DW.FORM.sdata,
364 0,
365 0, // table sentinel
366 abbrev_pad1,
367 DW.TAG.unspecified_type,
368 DW.CHILDREN.no, // header
369 0,
370 0, // table sentinel
371 abbrev_parameter,
372 DW.TAG.formal_parameter, DW.CHILDREN.no, // header
373 DW.AT.location, DW.FORM.exprloc,
374 DW.AT.type, DW.FORM.ref4,
375 DW.AT.name, DW.FORM.string,
376 0,
377 0, // table sentinel
378 0,
379 0,
380 0, // section sentinel
381 };
382
383 const needed_size = abbrev_buf.len;
384 const allocated_size = self.allocatedSize(debug_abbrev_sect.offset);
385 if (needed_size > allocated_size) {
386 debug_abbrev_sect.size = 0; // free the space
387 const offset = self.findFreeSpace(needed_size, 1);
388 debug_abbrev_sect.offset = @intCast(u32, offset);
389 debug_abbrev_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff;
390 }
391 debug_abbrev_sect.size = needed_size;
392 log.debug("__debug_abbrev start=0x{x} end=0x{x}", .{
393 debug_abbrev_sect.offset,
394 debug_abbrev_sect.offset + needed_size,
395 });
396
397 const abbrev_offset = 0;
398 self.debug_abbrev_table_offset = abbrev_offset;
399 try self.file.pwriteAll(&abbrev_buf, debug_abbrev_sect.offset + abbrev_offset);
400 self.load_commands_dirty = true;258 self.load_commands_dirty = true;
401 self.debug_abbrev_section_dirty = false;259 self.debug_abbrev_section_dirty = false;
402 }260 }
403261
404 if (self.debug_info_header_dirty) debug_info: {262 if (self.debug_info_header_dirty) {
405 // If this value is null it means there is an error in the module;
406 // leave debug_info_header_dirty=true.
407 const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
408 const last_dbg_info_decl = self.dbg_info_decl_last.?;
409 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
410 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
411
412 // We have a function to compute the upper bound size, because it's needed
413 // for determining where to put the offset of the first `LinkBlock`.
414 const needed_bytes = self.dbgInfoNeededHeaderBytes();
415 var di_buf = try std.ArrayList(u8).initCapacity(allocator, needed_bytes);
416 defer di_buf.deinit();
417
418 // initial length - length of the .debug_info contribution for this compilation unit,
419 // not including the initial length itself.
420 // We have to come back and write it later after we know the size.
421 const after_init_len = di_buf.items.len + init_len_size;
422 // +1 for the final 0 that ends the compilation unit children.
423 const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1;
424 const init_len = dbg_info_end - after_init_len;
425 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len));
426 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4); // DWARF version
427 const abbrev_offset = self.debug_abbrev_table_offset.?;
428 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset));
429 di_buf.appendAssumeCapacity(8); // address size
430 // Write the form for the compile unit, which must match the abbrev table above.
431 const name_strp = try self.makeDebugString(allocator, module.root_pkg.root_src_path);
432 const comp_dir_strp = try self.makeDebugString(allocator, module.root_pkg.root_src_directory.path orelse ".");
433 const producer_strp = try self.makeDebugString(allocator, link.producer_string);
434 // Currently only one compilation unit is supported, so the address range is simply263 // Currently only one compilation unit is supported, so the address range is simply
435 // identical to the main program header virtual address and memory size.264 // identical to the main program header virtual address and memory size.
436 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;265 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
437 const text_section = text_segment.sections.items[self.text_section_index.?];266 const text_section = text_segment.sections.items[self.text_section_index.?];
438 const low_pc = text_section.addr;267 const low_pc = text_section.addr;
439 const high_pc = text_section.addr + text_section.size;268 const high_pc = text_section.addr + text_section.size;
440269 try self.dwarf.writeDbgInfoHeader(&self.base.base, module, low_pc, high_pc);
441 di_buf.appendAssumeCapacity(abbrev_compile_unit);
442 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // DW.AT.stmt_list, DW.FORM.sec_offset
443 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), low_pc);
444 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), high_pc);
445 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, name_strp));
446 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, comp_dir_strp));
447 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, producer_strp));
448 // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
449 // http://dwarfstd.org/ShowIssue.php?issue=171115.1
450 // Until then we say it is C99.
451 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99);
452
453 if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
454 // Move the first N decls to the end to make more padding for the header.
455 @panic("TODO: handle __debug_info header exceeding its padding");
456 }
457 const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len;
458 try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.offset);
459 self.debug_info_header_dirty = false;270 self.debug_info_header_dirty = false;
460 }271 }
461272
462 if (self.debug_aranges_section_dirty) {273 if (self.debug_aranges_section_dirty) {
463 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
464 const debug_aranges_sect = &dwarf_segment.sections.items[self.debug_aranges_section_index.?];
465
466 // Enough for all the data without resizing. When support for more compilation units
467 // is added, the size of this section will become more variable.
468 var di_buf = try std.ArrayList(u8).initCapacity(allocator, 100);
469 defer di_buf.deinit();
470
471 // initial length - length of the .debug_aranges contribution for this compilation unit,
472 // not including the initial length itself.
473 // We have to come back and write it later after we know the size.
474 const init_len_index = di_buf.items.len;
475 di_buf.items.len += init_len_size;
476 const after_init_len = di_buf.items.len;
477 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2); // version
478 // When more than one compilation unit is supported, this will be the offset to it.
479 // For now it is always at offset 0 in .debug_info.
480 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // __debug_info offset
481 di_buf.appendAssumeCapacity(@sizeOf(u64)); // address_size
482 di_buf.appendAssumeCapacity(0); // segment_selector_size
483
484 const end_header_offset = di_buf.items.len;
485 const begin_entries_offset = mem.alignForward(end_header_offset, @sizeOf(u64) * 2);
486 di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
487
488 // Currently only one compilation unit is supported, so the address range is simply274 // Currently only one compilation unit is supported, so the address range is simply
489 // identical to the main program header virtual address and memory size.275 // identical to the main program header virtual address and memory size.
490 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;276 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
491 const text_section = text_segment.sections.items[self.text_section_index.?];277 const text_section = text_segment.sections.items[self.text_section_index.?];
492 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.addr);278 try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);
493 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.size);
494
495 // Sentinel.
496 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), 0);
497 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), 0);
498
499 // Go back and populate the initial length.
500 const init_len = di_buf.items.len - after_init_len;
501 // initial length - length of the .debug_aranges contribution for this compilation unit,
502 // not including the initial length itself.
503 mem.writeIntLittle(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len));
504
505 const needed_size = di_buf.items.len;
506 const allocated_size = self.allocatedSize(debug_aranges_sect.offset);
507 if (needed_size > allocated_size) {
508 debug_aranges_sect.size = 0; // free the space
509 const new_offset = self.findFreeSpace(needed_size, 16);
510 debug_aranges_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
511 debug_aranges_sect.offset = @intCast(u32, new_offset);
512 }
513 debug_aranges_sect.size = needed_size;
514 log.debug("__debug_aranges start=0x{x} end=0x{x}", .{
515 debug_aranges_sect.offset,
516 debug_aranges_sect.offset + needed_size,
517 });
518
519 try self.file.pwriteAll(di_buf.items, debug_aranges_sect.offset);
520 self.load_commands_dirty = true;279 self.load_commands_dirty = true;
521 self.debug_aranges_section_dirty = false;280 self.debug_aranges_section_dirty = false;
522 }281 }
523 if (self.debug_line_header_dirty) debug_line: {
524 if (self.dbg_line_fn_first == null) {
525 break :debug_line; // Error in module; leave debug_line_header_dirty=true.
526 }
527 const dbg_line_prg_off = self.getDebugLineProgramOff();
528 const dbg_line_prg_end = self.getDebugLineProgramEnd();
529 assert(dbg_line_prg_end != 0);
530282
531 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;283 if (self.debug_line_header_dirty) {
532 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];284 try self.dwarf.writeDbgLineHeader(&self.base.base, module);
533
534 // The size of this header is variable, depending on the number of directories,
535 // files, and padding. We have a function to compute the upper bound size, however,
536 // because it's needed for determining where to put the offset of the first `SrcFn`.
537 const needed_bytes = self.dbgLineNeededHeaderBytes(module);
538 var di_buf = try std.ArrayList(u8).initCapacity(allocator, needed_bytes);
539 defer di_buf.deinit();
540
541 // initial length - length of the .debug_line contribution for this compilation unit,
542 // not including the initial length itself.
543 const after_init_len = di_buf.items.len + init_len_size;
544 const init_len = dbg_line_prg_end - after_init_len;
545 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len));
546 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4); // version
547
548 // Empirically, debug info consumers do not respect this field, or otherwise
549 // consider it to be an error when it does not point exactly to the end of the header.
550 // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
551 // padding rather than this field.
552 const before_header_len = di_buf.items.len;
553 di_buf.items.len += @sizeOf(u32); // We will come back and write this.
554 const after_header_len = di_buf.items.len;
555
556 const opcode_base = DW.LNS.set_isa + 1;
557 di_buf.appendSliceAssumeCapacity(&[_]u8{
558 1, // minimum_instruction_length
559 1, // maximum_operations_per_instruction
560 1, // default_is_stmt
561 1, // line_base (signed)
562 1, // line_range
563 opcode_base,
564
565 // Standard opcode lengths. The number of items here is based on `opcode_base`.
566 // The value is the number of LEB128 operands the instruction takes.
567 0, // `DW.LNS.copy`
568 1, // `DW.LNS.advance_pc`
569 1, // `DW.LNS.advance_line`
570 1, // `DW.LNS.set_file`
571 1, // `DW.LNS.set_column`
572 0, // `DW.LNS.negate_stmt`
573 0, // `DW.LNS.set_basic_block`
574 0, // `DW.LNS.const_add_pc`
575 1, // `DW.LNS.fixed_advance_pc`
576 0, // `DW.LNS.set_prologue_end`
577 0, // `DW.LNS.set_epilogue_begin`
578 1, // `DW.LNS.set_isa`
579 0, // include_directories (none except the compilation unit cwd)
580 });
581 // file_names[0]
582 di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name
583 di_buf.appendSliceAssumeCapacity(&[_]u8{
584 0, // null byte for the relative path name
585 0, // directory_index
586 0, // mtime (TODO supply this)
587 0, // file size bytes (TODO supply this)
588 0, // file_names sentinel
589 });
590
591 const header_len = di_buf.items.len - after_header_len;
592 mem.writeIntLittle(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len));
593
594 // We use NOPs because consumers empirically do not respect the header length field.
595 if (di_buf.items.len > dbg_line_prg_off) {
596 // Move the first N files to the end to make more padding for the header.
597 @panic("TODO: handle __debug_line header exceeding its padding");
598 }
599 const jmp_amt = dbg_line_prg_off - di_buf.items.len;
600 try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.offset);
601 self.debug_line_header_dirty = false;285 self.debug_line_header_dirty = false;
602 }286 }
287
603 {288 {
604 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;289 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
605 const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];290 const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];
606 if (self.debug_string_table_dirty or self.debug_string_table.items.len != debug_strtab_sect.size) {291 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {
607 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);292 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
608 const needed_size = self.debug_string_table.items.len;293 const needed_size = self.dwarf.strtab.items.len;
609294
610 if (needed_size > allocated_size) {295 if (needed_size > allocated_size) {
611 debug_strtab_sect.size = 0; // free the space296 debug_strtab_sect.size = 0; // free the space
...@@ -620,7 +305,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -620,7 +305,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
620 debug_strtab_sect.offset + needed_size,305 debug_strtab_sect.offset + needed_size,
621 });306 });
622307
623 try self.file.pwriteAll(self.debug_string_table.items, debug_strtab_sect.offset);308 try self.file.pwriteAll(self.dwarf.strtab.items, debug_strtab_sect.offset);
624 self.load_commands_dirty = true;309 self.load_commands_dirty = true;
625 self.debug_string_table_dirty = false;310 self.debug_string_table_dirty = false;
626 }311 }
...@@ -639,13 +324,11 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -639,13 +324,11 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
639}324}
640325
641pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {326pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
642 self.dbg_info_decl_free_list.deinit(allocator);
643 self.dbg_line_fn_free_list.deinit(allocator);
644 self.debug_string_table.deinit(allocator);
645 for (self.load_commands.items) |*lc| {327 for (self.load_commands.items) |*lc| {
646 lc.deinit(allocator);328 lc.deinit(allocator);
647 }329 }
648 self.load_commands.deinit(allocator);330 self.load_commands.deinit(allocator);
331 self.dwarf.deinit();
649 self.file.close();332 self.file.close();
650}333}
651334
...@@ -777,7 +460,7 @@ fn writeHeader(self: *DebugSymbols) !void {...@@ -777,7 +460,7 @@ fn writeHeader(self: *DebugSymbols) !void {
777 try self.file.pwriteAll(mem.asBytes(&header), 0);460 try self.file.pwriteAll(mem.asBytes(&header), 0);
778}461}
779462
780fn allocatedSize(self: *DebugSymbols, start: u64) u64 {463pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
781 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;464 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
782 assert(start >= seg.inner.fileoff);465 assert(start >= seg.inner.fileoff);
783 var min_pos: u64 = std.math.maxInt(u64);466 var min_pos: u64 = std.math.maxInt(u64);
...@@ -964,813 +647,22 @@ fn writeStringTable(self: *DebugSymbols) !void {...@@ -964,813 +647,22 @@ fn writeStringTable(self: *DebugSymbols) !void {
964647
965pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void {648pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void {
966 _ = module;649 _ = module;
967 const tracy = trace(@src());650 return self.dwarf.updateDeclLineNumber(&self.base.base, decl);
968 defer tracy.end();
969
970 log.debug("updateDeclLineNumber {s}{*}", .{ decl.name, decl });
971
972 const func = decl.val.castTag(.function).?.data;
973 log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{
974 decl.src_line,
975 func.lbrace_line,
976 func.rbrace_line,
977 });
978 const line = @intCast(u28, decl.src_line + func.lbrace_line);
979
980 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
981 const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?];
982 const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff();
983 var data: [4]u8 = undefined;
984 leb.writeUnsignedFixed(4, &data, line);
985 try self.file.pwriteAll(&data, file_pos);
986}651}
987652
988pub const DeclDebugBuffers = struct {
989 dbg_line_buffer: std.ArrayList(u8),
990 dbg_info_buffer: std.ArrayList(u8),
991 dbg_info_type_relocs: link.File.DbgInfoTypeRelocsTable,
992};
993
994/// Caller owns the returned memory.653/// Caller owns the returned memory.
995pub fn initDeclDebugBuffers(654pub fn initDeclDebugInfo(self: *DebugSymbols, module: *Module, decl: *Module.Decl) !Dwarf.DeclDebugBuffers {
996 self: *DebugSymbols,
997 allocator: Allocator,
998 module: *Module,
999 decl: *Module.Decl,
1000) !DeclDebugBuffers {
1001 _ = self;
1002 _ = module;655 _ = module;
1003 const tracy = trace(@src());656 return self.dwarf.initDeclDebugInfo(decl);
1004 defer tracy.end();
1005
1006 var dbg_line_buffer = std.ArrayList(u8).init(allocator);
1007 var dbg_info_buffer = std.ArrayList(u8).init(allocator);
1008 var dbg_info_type_relocs: link.File.DbgInfoTypeRelocsTable = .{};
1009
1010 assert(decl.has_tv);
1011 switch (decl.ty.zigTypeTag()) {
1012 .Fn => {
1013 // For functions we need to add a prologue to the debug line program.
1014 try dbg_line_buffer.ensureTotalCapacity(26);
1015
1016 const func = decl.val.castTag(.function).?.data;
1017 log.debug("updateFunc {s}{*}", .{ decl.name, func.owner_decl });
1018 log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{
1019 decl.src_line,
1020 func.lbrace_line,
1021 func.rbrace_line,
1022 });
1023 const line = @intCast(u28, decl.src_line + func.lbrace_line);
1024
1025 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
1026 DW.LNS.extended_op,
1027 @sizeOf(u64) + 1,
1028 DW.LNE.set_address,
1029 });
1030 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
1031 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
1032 dbg_line_buffer.items.len += @sizeOf(u64);
1033
1034 dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line);
1035 // This is the "relocatable" relative line offset from the previous function's end curly
1036 // to this function's begin curly.
1037 assert(getRelocDbgLineOff() == dbg_line_buffer.items.len);
1038 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
1039 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line);
1040
1041 dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file);
1042 assert(getRelocDbgFileIndex() == dbg_line_buffer.items.len);
1043 // Once we support more than one source file, this will have the ability to be more
1044 // than one possible value.
1045 const file_index = 1;
1046 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
1047
1048 // Emit a line for the begin curly with prologue_end=false. The codegen will
1049 // do the work of setting prologue_end=true and epilogue_begin=true.
1050 dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy);
1051
1052 // .debug_info subprogram
1053 const decl_name_with_null = decl.name[0 .. mem.sliceTo(decl.name, 0).len + 1];
1054 try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len);
1055
1056 const fn_ret_type = decl.ty.fnReturnType();
1057 const fn_ret_has_bits = fn_ret_type.hasRuntimeBits();
1058 if (fn_ret_has_bits) {
1059 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
1060 } else {
1061 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
1062 }
1063 // These get overwritten after generating the machine code. These values are
1064 // "relocations" and have to be in this fixed place so that functions can be
1065 // moved in virtual address space.
1066 assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
1067 dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT.low_pc, DW.FORM.addr
1068 assert(getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
1069 dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4
1070 if (fn_ret_has_bits) {
1071 const gop = try dbg_info_type_relocs.getOrPut(allocator, fn_ret_type);
1072 if (!gop.found_existing) {
1073 gop.value_ptr.* = .{
1074 .off = undefined,
1075 .relocs = .{},
1076 };
1077 }
1078 try gop.value_ptr.relocs.append(allocator, @intCast(u32, dbg_info_buffer.items.len));
1079 dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4
1080 }
1081 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string
1082 },
1083 else => {
1084 // TODO implement .debug_info for global variables
1085 },
1086 }
1087
1088 return DeclDebugBuffers{
1089 .dbg_info_buffer = dbg_info_buffer,
1090 .dbg_line_buffer = dbg_line_buffer,
1091 .dbg_info_type_relocs = dbg_info_type_relocs,
1092 };
1093}657}
1094658
1095pub fn commitDeclDebugInfo(659pub fn commitDeclDebugInfo(
1096 self: *DebugSymbols,660 self: *DebugSymbols,
1097 allocator: Allocator,
1098 module: *Module,661 module: *Module,
1099 decl: *Module.Decl,662 decl: *Module.Decl,
1100 debug_buffers: *DeclDebugBuffers,663 debug_buffers: *Dwarf.DeclDebugBuffers,
1101) !void {664) !void {
1102 const tracy = trace(@src());
1103 defer tracy.end();
1104
1105 var dbg_line_buffer = &debug_buffers.dbg_line_buffer;
1106 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;
1107 var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs;
1108
1109 const symbol = self.base.locals.items[decl.link.macho.local_sym_index];665 const symbol = self.base.locals.items[decl.link.macho.local_sym_index];
1110 const text_block = &decl.link.macho;666 const atom = &decl.link.macho;
1111 // If the Decl is a function, we need to update the __debug_line program.667 return self.dwarf.commitDeclDebugInfo(&self.base.base, module, decl, symbol.n_value, atom.size, debug_buffers);
1112 assert(decl.has_tv);
1113 switch (decl.ty.zigTypeTag()) {
1114 .Fn => {
1115 // Perform the relocations based on vaddr.
1116 {
1117 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
1118 mem.writeIntLittle(u64, ptr, symbol.n_value);
1119 }
1120 {
1121 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
1122 mem.writeIntLittle(u64, ptr, symbol.n_value);
1123 }
1124 {
1125 const ptr = dbg_info_buffer.items[getRelocDbgInfoSubprogramHighPC()..][0..4];
1126 mem.writeIntLittle(u32, ptr, @intCast(u32, text_block.size));
1127 }
1128
1129 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence });
1130
1131 // Now we have the full contents and may allocate a region to store it.
1132
1133 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
1134 // `TextBlock` and the .debug_info. If you are editing this logic, you
1135 // probably need to edit that logic too.
1136
1137 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
1138 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
1139 const src_fn = &decl.fn_link.macho;
1140 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
1141 if (self.dbg_line_fn_last) |last| blk: {
1142 if (src_fn == last) break :blk;
1143 if (src_fn.next) |next| {
1144 // Update existing function - non-last item.
1145 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
1146 // It grew too big, so we move it to a new location.
1147 if (src_fn.prev) |prev| {
1148 self.dbg_line_fn_free_list.put(allocator, prev, {}) catch {};
1149 prev.next = src_fn.next;
1150 }
1151 next.prev = src_fn.prev;
1152 src_fn.next = null;
1153 // Populate where it used to be with NOPs.
1154 const file_pos = debug_line_sect.offset + src_fn.off;
1155 try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
1156 // TODO Look at the free list before appending at the end.
1157 src_fn.prev = last;
1158 last.next = src_fn;
1159 self.dbg_line_fn_last = src_fn;
1160
1161 src_fn.off = last.off + padToIdeal(last.len);
1162 }
1163 } else if (src_fn.prev == null) {
1164 // Append new function.
1165 // TODO Look at the free list before appending at the end.
1166 src_fn.prev = last;
1167 last.next = src_fn;
1168 self.dbg_line_fn_last = src_fn;
1169
1170 src_fn.off = last.off + padToIdeal(last.len);
1171 }
1172 } else {
1173 // This is the first function of the Line Number Program.
1174 self.dbg_line_fn_first = src_fn;
1175 self.dbg_line_fn_last = src_fn;
1176
1177 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(module));
1178 }
1179
1180 const last_src_fn = self.dbg_line_fn_last.?;
1181 const needed_size = last_src_fn.off + last_src_fn.len;
1182 if (needed_size != debug_line_sect.size) {
1183 if (needed_size > self.allocatedSize(debug_line_sect.offset)) {
1184 const new_offset = self.findFreeSpace(needed_size, 1);
1185 const existing_size = last_src_fn.off;
1186
1187 log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
1188 existing_size,
1189 debug_line_sect.offset,
1190 new_offset,
1191 });
1192
1193 try MachO.copyRangeAllOverlappingAlloc(
1194 self.base.base.allocator,
1195 self.file,
1196 debug_line_sect.offset,
1197 new_offset,
1198 existing_size,
1199 );
1200
1201 debug_line_sect.offset = @intCast(u32, new_offset);
1202 debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
1203 }
1204 debug_line_sect.size = needed_size;
1205 self.load_commands_dirty = true; // TODO look into making only the one section dirty
1206 self.debug_line_header_dirty = true;
1207 }
1208 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
1209 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
1210
1211 // We only have support for one compilation unit so far, so the offsets are directly
1212 // from the .debug_line section.
1213 const file_pos = debug_line_sect.offset + src_fn.off;
1214 try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
1215
1216 // .debug_info - End the TAG.subprogram children.
1217 try dbg_info_buffer.append(0);
1218 },
1219 else => {},
1220 }
1221
1222 if (dbg_info_buffer.items.len == 0)
1223 return;
1224
1225 // We need this for the duration of this function only so that for composite
1226 // types such as []const u32, if the type *u32 is non-existent, we create
1227 // it synthetically and store the backing bytes in this arena. After we are
1228 // done with the relocations, we can safely deinit the entire memory slab.
1229 // TODO currently, we do not store the relocations for future use, however,
1230 // if that is the case, we should move memory management to a higher scope,
1231 // such as linker scope, or whatnot.
1232 var dbg_type_arena = std.heap.ArenaAllocator.init(allocator);
1233 defer dbg_type_arena.deinit();
1234
1235 {
1236 // Now we emit the .debug_info types of the Decl. These will count towards the size of
1237 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
1238 // relocations yet.
1239 var it: usize = 0;
1240 while (it < dbg_info_type_relocs.count()) : (it += 1) {
1241 const ty = dbg_info_type_relocs.keys()[it];
1242 const value_ptr = dbg_info_type_relocs.getPtr(ty).?;
1243 value_ptr.off = @intCast(u32, dbg_info_buffer.items.len);
1244 try self.addDbgInfoType(dbg_type_arena.allocator(), ty, dbg_info_buffer, dbg_info_type_relocs);
1245 }
1246 }
1247
1248 try self.updateDeclDebugInfoAllocation(allocator, text_block, @intCast(u32, dbg_info_buffer.items.len));
1249
1250 {
1251 // Now that we have the offset assigned we can finally perform type relocations.
1252 for (dbg_info_type_relocs.values()) |value| {
1253 for (value.relocs.items) |off| {
1254 mem.writeIntLittle(
1255 u32,
1256 dbg_info_buffer.items[off..][0..4],
1257 text_block.dbg_info_off + value.off,
1258 );
1259 }
1260 }
1261 }
1262
1263 try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
1264}
1265
1266/// Asserts the type has codegen bits.
1267fn 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
1460fn 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
1516fn 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
1574fn getDebugLineProgramOff(self: DebugSymbols) u32 {
1575 return self.dbg_line_fn_first.?.off;
1576}
1577
1578fn 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.
1583fn 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.
1593fn getRelocDbgLineOff() usize {
1594 return dbg_line_vaddr_reloc_index + @sizeOf(u64) + 1;
1595}
1596
1597fn getRelocDbgFileIndex() usize {
1598 return getRelocDbgLineOff() + 5;
1599}
1600
1601fn getRelocDbgInfoSubprogramHighPC() u32 {
1602 return dbg_info_low_pc_reloc_index + @sizeOf(u64);
1603}
1604
1605fn 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
1620fn 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`).
1630fn 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.
1708fn pwriteDbgInfoNops(
1709 self: *DebugSymbols,
1710 prev_padding_size: usize,
1711 buf: []const u8,
1712 next_padding_size: usize,
1713 trailing_zero: bool,
1714 offset: u64,
1715) !void {
1716 const tracy = trace(@src());
1717 defer tracy.end();
1718
1719 const page_of_nops = [1]u8{abbrev_pad1} ** 4096;
1720 var vecs: [32]std.os.iovec_const = undefined;
1721 var vec_index: usize = 0;
1722 {
1723 var padding_left = prev_padding_size;
1724 while (padding_left > page_of_nops.len) {
1725 vecs[vec_index] = .{
1726 .iov_base = &page_of_nops,
1727 .iov_len = page_of_nops.len,
1728 };
1729 vec_index += 1;
1730 padding_left -= page_of_nops.len;
1731 }
1732 if (padding_left > 0) {
1733 vecs[vec_index] = .{
1734 .iov_base = &page_of_nops,
1735 .iov_len = padding_left,
1736 };
1737 vec_index += 1;
1738 }
1739 }
1740
1741 vecs[vec_index] = .{
1742 .iov_base = buf.ptr,
1743 .iov_len = buf.len,
1744 };
1745 vec_index += 1;
1746
1747 {
1748 var padding_left = next_padding_size;
1749 while (padding_left > page_of_nops.len) {
1750 vecs[vec_index] = .{
1751 .iov_base = &page_of_nops,
1752 .iov_len = page_of_nops.len,
1753 };
1754 vec_index += 1;
1755 padding_left -= page_of_nops.len;
1756 }
1757 if (padding_left > 0) {
1758 vecs[vec_index] = .{
1759 .iov_base = &page_of_nops,
1760 .iov_len = padding_left,
1761 };
1762 vec_index += 1;
1763 }
1764 }
1765
1766 if (trailing_zero) {
1767 var zbuf = [1]u8{0};
1768 vecs[vec_index] = .{
1769 .iov_base = &zbuf,
1770 .iov_len = zbuf.len,
1771 };
1772 vec_index += 1;
1773 }
1774
1775 try self.file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
1776}668}