authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-12 18:41:16+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-14 19:24:23+01:00
log27cfbf949a25375a6ae7671a00ed3f7bae67d3f9
tree27fcf1fbe7d3e5732f044f0e34dd31edd6f6e65a
parent04f3d9301798e41ac8272822328914073e01a6ab

macho: re-enable creating dSYM bundle

* update number of type abbrevs to match Elf linker * update `DebugSymbols` to write symbol and string tables at the end to match the `MachO` linker * TODO: update segment vm addresses when growing segments in the binary * TODO: store DWARF relocations in linker's interned arena

2 files changed, 368 insertions(+), 233 deletions(-)

src/link/MachO.zig+55-59
......@@ -354,32 +354,31 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
354354 return self;
355355 }
356356
357 // TODO Migrate DebugSymbols to the merged linker codepaths
358 // if (!options.strip and options.module != null) {
359 // // Create dSYM bundle.
360 // const dir = options.module.?.zig_cache_artifact_directory;
361 // log.debug("creating {s}.dSYM bundle in {s}", .{ sub_path, dir.path });
362
363 // const d_sym_path = try fmt.allocPrint(
364 // allocator,
365 // "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",
366 // .{sub_path},
367 // );
368 // defer allocator.free(d_sym_path);
369
370 // var d_sym_bundle = try dir.handle.makeOpenPath(d_sym_path, .{});
371 // defer d_sym_bundle.close();
372
373 // const d_sym_file = try d_sym_bundle.createFile(sub_path, .{
374 // .truncate = false,
375 // .read = true,
376 // });
377
378 // self.d_sym = .{
379 // .base = self,
380 // .file = d_sym_file,
381 // };
382 // }
357 if (!options.strip and options.module != null) {
358 // Create dSYM bundle.
359 const dir = options.module.?.zig_cache_artifact_directory;
360 log.debug("creating {s}.dSYM bundle in {s}", .{ emit.sub_path, dir.path });
361
362 const d_sym_path = try fmt.allocPrint(
363 allocator,
364 "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF",
365 .{emit.sub_path},
366 );
367 defer allocator.free(d_sym_path);
368
369 var d_sym_bundle = try dir.handle.makeOpenPath(d_sym_path, .{});
370 defer d_sym_bundle.close();
371
372 const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{
373 .truncate = false,
374 .read = true,
375 });
376
377 self.d_sym = .{
378 .base = self,
379 .file = d_sym_file,
380 };
381 }
383382
384383 // Index 0 is always a null symbol.
385384 try self.locals.append(allocator, .{
......@@ -393,8 +392,8 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
393392
394393 try self.populateMissingMetadata();
395394
396 if (self.d_sym) |*ds| {
397 try ds.populateMissingMetadata(allocator);
395 if (self.d_sym) |*d_sym| {
396 try d_sym.populateMissingMetadata(allocator);
398397 }
399398
400399 return self;
......@@ -1048,9 +1047,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
10481047 try self.updateSectionOrdinals();
10491048 try self.writeLinkeditSegment();
10501049
1051 if (self.d_sym) |*ds| {
1050 if (self.d_sym) |*d_sym| {
10521051 // Flush debug symbols bundle.
1053 try ds.flushModule(self.base.allocator, self.base.options);
1052 try d_sym.flushModule(self.base.allocator, self.base.options);
10541053 }
10551054
10561055 if (self.requires_adhoc_codesig) {
......@@ -3374,8 +3373,8 @@ pub fn deinit(self: *MachO) void {
33743373 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
33753374 }
33763375
3377 if (self.d_sym) |*ds| {
3378 ds.deinit(self.base.allocator);
3376 if (self.d_sym) |*d_sym| {
3377 d_sym.deinit(self.base.allocator);
33793378 }
33803379
33813380 self.section_ordinals.deinit(self.base.allocator);
......@@ -3497,13 +3496,13 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
34973496 }
34983497 }
34993498
3500 if (self.d_sym) |*ds| {
3501 if (ds.dbg_info_decl_first == atom) {
3502 ds.dbg_info_decl_first = atom.dbg_info_next;
3499 if (self.d_sym) |*d_sym| {
3500 if (d_sym.dbg_info_decl_first == atom) {
3501 d_sym.dbg_info_decl_first = atom.dbg_info_next;
35033502 }
3504 if (ds.dbg_info_decl_last == atom) {
3503 if (d_sym.dbg_info_decl_last == atom) {
35053504 // TODO shrink the .debug_info section size here
3506 ds.dbg_info_decl_last = atom.dbg_info_prev;
3505 d_sym.dbg_info_decl_last = atom.dbg_info_prev;
35073506 }
35083507 }
35093508
......@@ -3675,6 +3674,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
36753674
36763675 const decl = func.owner_decl;
36773676 self.freeUnnamedConsts(decl);
3677
36783678 // TODO clearing the code and relocs buffer should probably be orchestrated
36793679 // in a different, smarter, more automatic way somewhere else, in a more centralised
36803680 // way than this.
......@@ -3686,8 +3686,8 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
36863686 defer code_buffer.deinit();
36873687
36883688 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
3689 const debug_buffers = if (self.d_sym) |*ds| blk: {
3690 debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
3689 const debug_buffers = if (self.d_sym) |*d_sym| blk: {
3690 debug_buffers_buf = try d_sym.initDeclDebugBuffers(self.base.allocator, module, decl);
36913691 break :blk &debug_buffers_buf;
36923692 } else null;
36933693 defer {
......@@ -3725,13 +3725,9 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
37253725 _ = try self.placeDecl(decl, decl.link.macho.code.items.len);
37263726
37273727 if (debug_buffers) |db| {
3728 try self.d_sym.?.commitDeclDebugInfo(
3729 self.base.allocator,
3730 module,
3731 decl,
3732 db,
3733 self.base.options.target,
3734 );
3728 if (self.d_sym) |*d_sym| {
3729 try d_sym.commitDeclDebugInfo(self.base.allocator, module, decl, db);
3730 }
37353731 }
37363732
37373733 // Since we updated the vaddr and the size, each corresponding export symbol also
......@@ -3827,8 +3823,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
38273823 defer code_buffer.deinit();
38283824
38293825 var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined;
3830 const debug_buffers = if (self.d_sym) |*ds| blk: {
3831 debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl);
3826 const debug_buffers = if (self.d_sym) |*d_sym| blk: {
3827 debug_buffers_buf = try d_sym.initDeclDebugBuffers(self.base.allocator, module, decl);
38323828 break :blk &debug_buffers_buf;
38333829 } else null;
38343830 defer {
......@@ -4125,8 +4121,8 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
41254121}
41264122
41274123pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
4128 if (self.d_sym) |*ds| {
4129 try ds.updateDeclLineNumber(module, decl);
4124 if (self.d_sym) |*d_sym| {
4125 try d_sym.updateDeclLineNumber(module, decl);
41304126 }
41314127}
41324128
......@@ -4322,27 +4318,27 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
43224318 _ = self.atom_by_index_table.remove(decl.link.macho.local_sym_index);
43234319 decl.link.macho.local_sym_index = 0;
43244320 }
4325 if (self.d_sym) |*ds| {
4321 if (self.d_sym) |*d_sym| {
43264322 // TODO make this logic match freeAtom. Maybe abstract the logic
43274323 // out since the same thing is desired for both.
4328 _ = ds.dbg_line_fn_free_list.remove(&decl.fn_link.macho);
4324 _ = d_sym.dbg_line_fn_free_list.remove(&decl.fn_link.macho);
43294325 if (decl.fn_link.macho.prev) |prev| {
4330 ds.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
4326 d_sym.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
43314327 prev.next = decl.fn_link.macho.next;
43324328 if (decl.fn_link.macho.next) |next| {
43334329 next.prev = prev;
43344330 } else {
4335 ds.dbg_line_fn_last = prev;
4331 d_sym.dbg_line_fn_last = prev;
43364332 }
43374333 } else if (decl.fn_link.macho.next) |next| {
4338 ds.dbg_line_fn_first = next;
4334 d_sym.dbg_line_fn_first = next;
43394335 next.prev = null;
43404336 }
4341 if (ds.dbg_line_fn_first == &decl.fn_link.macho) {
4342 ds.dbg_line_fn_first = decl.fn_link.macho.next;
4337 if (d_sym.dbg_line_fn_first == &decl.fn_link.macho) {
4338 d_sym.dbg_line_fn_first = decl.fn_link.macho.next;
43434339 }
4344 if (ds.dbg_line_fn_last == &decl.fn_link.macho) {
4345 ds.dbg_line_fn_last = decl.fn_link.macho.prev;
4340 if (d_sym.dbg_line_fn_last == &decl.fn_link.macho) {
4341 d_sym.dbg_line_fn_last = decl.fn_link.macho.prev;
43464342 }
43474343 }
43484344}
src/link/MachO/DebugSymbols.zig+313-174
......@@ -3,7 +3,8 @@ const DebugSymbols = @This();
33const std = @import("std");
44const assert = std.debug.assert;
55const fs = std.fs;
6const log = std.log.scoped(.dsym);
6const log = std.log.scoped(.link);
7const leb128 = std.leb;
78const macho = std.macho;
89const math = std.math;
910const mem = std.mem;
......@@ -22,8 +23,6 @@ const SrcFn = MachO.SrcFn;
2223const makeStaticString = MachO.makeStaticString;
2324const padToIdeal = MachO.padToIdeal;
2425
25const page_size: u16 = 0x1000;
26
2726base: *MachO,
2827file: fs.File,
2928
......@@ -49,9 +48,6 @@ uuid_cmd_index: ?u16 = null,
4948/// Index into __TEXT,__text section.
5049text_section_index: ?u16 = null,
5150
52linkedit_off: u16 = page_size,
53linkedit_size: u16 = page_size,
54
5551debug_info_section_index: ?u16 = null,
5652debug_abbrev_section_index: ?u16 = null,
5753debug_str_section_index: ?u16 = null,
......@@ -76,7 +72,6 @@ dbg_info_decl_last: ?*TextBlock = null,
7672debug_string_table: std.ArrayListUnmanaged(u8) = .{},
7773
7874load_commands_dirty: bool = false,
79strtab_dirty: bool = false,
8075debug_string_table_dirty: bool = false,
8176debug_abbrev_section_dirty: bool = false,
8277debug_aranges_section_dirty: bool = false,
......@@ -87,8 +82,12 @@ const abbrev_compile_unit = 1;
8782const abbrev_subprogram = 2;
8883const abbrev_subprogram_retvoid = 3;
8984const abbrev_base_type = 4;
90const abbrev_pad1 = 5;
91const abbrev_parameter = 6;
85const abbrev_ptr_type = 5;
86const abbrev_struct_type = 6;
87const abbrev_anon_struct_type = 7;
88const abbrev_struct_member = 8;
89const abbrev_pad1 = 9;
90const abbrev_parameter = 10;
9291
9392/// The reloc offset for the virtual address of a function in its Line Number Program.
9493/// Size is a virtual address integer.
......@@ -108,30 +107,21 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
108107 try self.load_commands.append(allocator, base_cmd);
109108 self.load_commands_dirty = true;
110109 }
110
111111 if (self.symtab_cmd_index == null) {
112112 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
113 const base_cmd = self.base.load_commands.items[self.base.symtab_cmd_index.?].symtab;
114 const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64);
115 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
116
117 log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size });
118
119 const strtab_off = self.findFreeSpaceLinkedit(base_cmd.strsize, 1);
120
121 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize });
122
123 try self.load_commands.append(allocator, .{
113 try self.load_commands.append(self.base.base.allocator, .{
124114 .symtab = .{
125115 .cmdsize = @sizeOf(macho.symtab_command),
126 .symoff = @intCast(u32, symtab_off),
127 .nsyms = base_cmd.nsyms,
128 .stroff = @intCast(u32, strtab_off),
129 .strsize = base_cmd.strsize,
116 .symoff = 0,
117 .nsyms = 0,
118 .stroff = 0,
119 .strsize = 0,
130120 },
131121 });
132122 self.load_commands_dirty = true;
133 self.strtab_dirty = true;
134123 }
124
135125 if (self.pagezero_segment_cmd_index == null) {
136126 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
137127 const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].segment;
......@@ -139,6 +129,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
139129 try self.load_commands.append(allocator, .{ .segment = cmd });
140130 self.load_commands_dirty = true;
141131 }
132
142133 if (self.text_segment_cmd_index == null) {
143134 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
144135 const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].segment;
......@@ -146,6 +137,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
146137 try self.load_commands.append(allocator, .{ .segment = cmd });
147138 self.load_commands_dirty = true;
148139 }
140
149141 if (self.data_const_segment_cmd_index == null) outer: {
150142 if (self.base.data_const_segment_cmd_index == null) break :outer; // __DATA_CONST is optional
151143 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -154,6 +146,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
154146 try self.load_commands.append(allocator, .{ .segment = cmd });
155147 self.load_commands_dirty = true;
156148 }
149
157150 if (self.data_segment_cmd_index == null) outer: {
158151 if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
159152 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -162,26 +155,29 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
162155 try self.load_commands.append(allocator, .{ .segment = cmd });
163156 self.load_commands_dirty = true;
164157 }
158
165159 if (self.linkedit_segment_cmd_index == null) {
166160 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
167161 const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].segment;
168162 var cmd = try self.copySegmentCommand(allocator, base_cmd);
169 cmd.inner.vmsize = self.linkedit_size;
170 cmd.inner.fileoff = self.linkedit_off;
171 cmd.inner.filesize = self.linkedit_size;
163 // TODO this needs reworking
164 cmd.inner.vmsize = self.base.page_size;
165 cmd.inner.fileoff = self.base.page_size;
166 cmd.inner.filesize = self.base.page_size;
172167 try self.load_commands.append(allocator, .{ .segment = cmd });
173168 self.load_commands_dirty = true;
174169 }
170
175171 if (self.dwarf_segment_cmd_index == null) {
176172 self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
177173
178174 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
179175 const ideal_size: u16 = 200 + 128 + 160 + 250;
180 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), page_size);
181 const off = linkedit.inner.fileoff + linkedit.inner.filesize;
176 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size);
177 const fileoff = linkedit.inner.fileoff + linkedit.inner.filesize;
182178 const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize;
183179
184 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
180 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
185181
186182 try self.load_commands.append(allocator, .{
187183 .segment = .{
......@@ -189,13 +185,14 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
189185 .segname = makeStaticString("__DWARF"),
190186 .vmaddr = vmaddr,
191187 .vmsize = needed_size,
192 .fileoff = off,
188 .fileoff = fileoff,
193189 .filesize = needed_size,
194190 },
195191 },
196192 });
197193 self.load_commands_dirty = true;
198194 }
195
199196 if (self.debug_str_section_index == null) {
200197 assert(self.debug_string_table.items.len == 0);
201198 self.debug_str_section_index = try self.allocateSection(
......@@ -205,18 +202,22 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
205202 );
206203 self.debug_string_table_dirty = true;
207204 }
205
208206 if (self.debug_info_section_index == null) {
209207 self.debug_info_section_index = try self.allocateSection("__debug_info", 200, 0);
210208 self.debug_info_header_dirty = true;
211209 }
210
212211 if (self.debug_abbrev_section_index == null) {
213212 self.debug_abbrev_section_index = try self.allocateSection("__debug_abbrev", 128, 0);
214213 self.debug_abbrev_section_dirty = true;
215214 }
215
216216 if (self.debug_aranges_section_index == null) {
217217 self.debug_aranges_section_index = try self.allocateSection("__debug_aranges", 160, 4);
218218 self.debug_aranges_section_dirty = true;
219219 }
220
220221 if (self.debug_line_section_index == null) {
221222 self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0);
222223 self.debug_line_header_dirty = true;
......@@ -300,41 +301,91 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
300301 // we can simply append these bytes.
301302 const abbrev_buf = [_]u8{
302303 abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header
303 DW.AT.stmt_list, DW.FORM.sec_offset, // offset
304 DW.AT.low_pc, DW.FORM.addr,
305 DW.AT.high_pc, DW.FORM.addr,
306 DW.AT.name, DW.FORM.strp,
307 DW.AT.comp_dir, DW.FORM.strp,
308 DW.AT.producer, DW.FORM.strp,
309 DW.AT.language, DW.FORM.data2,
310 0, 0, // table sentinel
311 abbrev_subprogram, DW.TAG.subprogram, DW.CHILDREN.yes, // header
312 DW.AT.low_pc, DW.FORM.addr, // start VM address
313 DW.AT.high_pc, DW.FORM.data4,
314 DW.AT.type, DW.FORM.ref4,
315 DW.AT.name, DW.FORM.string,
316 DW.AT.decl_line, DW.FORM.data4,
317 DW.AT.decl_file, DW.FORM.data1,
304 DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc,
305 DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr,
306 DW.AT.name, DW.FORM.strp, DW.AT.comp_dir,
307 DW.FORM.strp, DW.AT.producer, DW.FORM.strp,
308 DW.AT.language, DW.FORM.data2, 0,
309 0, // table sentinel
310 abbrev_subprogram,
311 DW.TAG.subprogram,
312 DW.CHILDREN.yes, // header
313 DW.AT.low_pc,
314 DW.FORM.addr,
315 DW.AT.high_pc,
316 DW.FORM.data4,
317 DW.AT.type,
318 DW.FORM.ref4,
319 DW.AT.name,
320 DW.FORM.string,
318321 0, 0, // table sentinel
319322 abbrev_subprogram_retvoid,
320323 DW.TAG.subprogram, DW.CHILDREN.yes, // header
321324 DW.AT.low_pc, DW.FORM.addr,
322325 DW.AT.high_pc, DW.FORM.data4,
323326 DW.AT.name, DW.FORM.string,
324 DW.AT.decl_line, DW.FORM.data4,
325 DW.AT.decl_file, DW.FORM.data1,
326 0, 0, // table sentinel
327 abbrev_base_type, DW.TAG.base_type, DW.CHILDREN.no, // header
328 DW.AT.encoding, DW.FORM.data1, DW.AT.byte_size,
329 DW.FORM.data1, DW.AT.name, DW.FORM.string,
330 0, 0, // table sentinel
331 abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header
332 0, 0, // table sentinel
333 abbrev_parameter, DW.TAG.formal_parameter, DW.CHILDREN.no, // header
334 DW.AT.location, DW.FORM.exprloc, DW.AT.type,
335 DW.FORM.ref4, DW.AT.name, DW.FORM.string,
336 0, 0, // table sentinel
337 0, 0, 0, // section sentinel
327 0,
328 0, // table sentinel
329 abbrev_base_type,
330 DW.TAG.base_type,
331 DW.CHILDREN.no, // header
332 DW.AT.encoding,
333 DW.FORM.data1,
334 DW.AT.byte_size,
335 DW.FORM.data1,
336 DW.AT.name,
337 DW.FORM.string,
338 0,
339 0, // table sentinel
340 abbrev_ptr_type,
341 DW.TAG.pointer_type,
342 DW.CHILDREN.no, // header
343 DW.AT.type,
344 DW.FORM.ref4,
345 0,
346 0, // table sentinel
347 abbrev_struct_type,
348 DW.TAG.structure_type,
349 DW.CHILDREN.yes, // header
350 DW.AT.byte_size,
351 DW.FORM.sdata,
352 DW.AT.name,
353 DW.FORM.string,
354 0,
355 0, // table sentinel
356 abbrev_anon_struct_type,
357 DW.TAG.structure_type,
358 DW.CHILDREN.yes, // header
359 DW.AT.byte_size,
360 DW.FORM.sdata,
361 0,
362 0, // table sentinel
363 abbrev_struct_member,
364 DW.TAG.member,
365 DW.CHILDREN.no, // header
366 DW.AT.name,
367 DW.FORM.string,
368 DW.AT.type,
369 DW.FORM.ref4,
370 DW.AT.data_member_location,
371 DW.FORM.sdata,
372 0,
373 0, // table sentinel
374 abbrev_pad1,
375 DW.TAG.unspecified_type,
376 DW.CHILDREN.no, // header
377 0,
378 0, // table sentinel
379 abbrev_parameter,
380 DW.TAG.formal_parameter, DW.CHILDREN.no, // header
381 DW.AT.location, DW.FORM.exprloc,
382 DW.AT.type, DW.FORM.ref4,
383 DW.AT.name, DW.FORM.string,
384 0,
385 0, // table sentinel
386 0,
387 0,
388 0, // section sentinel
338389 };
339390
340391 const needed_size = abbrev_buf.len;
......@@ -583,13 +634,12 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
583634 }
584635 }
585636
586 try self.writeStringTable();
637 try self.writeLinkeditSegment();
587638 self.updateDwarfSegment();
588639 try self.writeLoadCommands(allocator);
589640 try self.writeHeader();
590641
591642 assert(!self.load_commands_dirty);
592 assert(!self.strtab_dirty);
593643 assert(!self.debug_abbrev_section_dirty);
594644 assert(!self.debug_aranges_section_dirty);
595645 assert(!self.debug_string_table_dirty);
......@@ -663,7 +713,7 @@ fn updateDwarfSegment(self: *DebugSymbols) void {
663713 if (file_size != dwarf_segment.inner.filesize) {
664714 dwarf_segment.inner.filesize = file_size;
665715 if (dwarf_segment.inner.vmsize < dwarf_segment.inner.filesize) {
666 dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, page_size);
716 dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, self.base.page_size);
667717 }
668718 self.load_commands_dirty = true;
669719 }
......@@ -719,23 +769,10 @@ fn writeHeader(self: *DebugSymbols) !void {
719769 try self.file.pwriteAll(mem.asBytes(&header), 0);
720770}
721771
722fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {
723 assert(start > 0);
724 var min_pos: u64 = std.math.maxInt(u64);
725
726 if (self.symtab_cmd_index) |idx| {
727 const symtab = self.load_commands.items[idx].symtab;
728 if (symtab.symoff >= start and symtab.symoff < min_pos) min_pos = symtab.symoff;
729 if (symtab.stroff >= start and symtab.stroff < min_pos) min_pos = symtab.stroff;
730 }
731
732 return min_pos - start;
733}
734
735772fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
736773 const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
737774 assert(start >= seg.inner.fileoff);
738 var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize;
775 var min_pos: u64 = std.math.maxInt(u64);
739776 for (seg.sections.items) |section| {
740777 if (section.offset <= start) continue;
741778 if (section.offset < min_pos) min_pos = section.offset;
......@@ -743,102 +780,72 @@ fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
743780 return min_pos - start;
744781}
745782
746fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 {
747 const end = start + padToIdeal(size);
783fn writeLinkeditSegment(self: *DebugSymbols) !void {
784 const tracy = trace(@src());
785 defer tracy.end();
748786
749 if (self.symtab_cmd_index) |idx| outer: {
750 if (self.load_commands.items.len == idx) break :outer;
751 const symtab = self.load_commands.items[idx].symtab;
752 {
753 // Symbol table
754 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
755 const increased_size = padToIdeal(symsize);
756 const test_end = symtab.symoff + increased_size;
757 if (end > symtab.symoff and start < test_end) {
758 return test_end;
759 }
760 }
761 {
762 // String table
763 const increased_size = padToIdeal(symtab.strsize);
764 const test_end = symtab.stroff + increased_size;
765 if (end > symtab.stroff and start < test_end) {
766 return test_end;
767 }
768 }
769 }
787 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
788 seg.inner.filesize = 0;
770789
771 return null;
790 try self.writeSymbolTable();
791 try self.writeStringTable();
772792}
773793
774fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u16) u64 {
775 var start: u64 = self.linkedit_off;
776 while (self.detectAllocCollisionLinkedit(start, object_size)) |item_end| {
777 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
778 }
779 return start;
780}
794fn writeSymbolTable(self: *DebugSymbols) !void {
795 const tracy = trace(@src());
796 defer tracy.end();
781797
782fn relocateSymbolTable(self: *DebugSymbols) !void {
798 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
783799 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
784 const nlocals = self.base.locals.items.len;
785 const nglobals = self.base.globals.items.len;
786 const nsyms = nlocals + nglobals;
787
788 if (symtab.nsyms < nsyms) {
789 const needed_size = nsyms * @sizeOf(macho.nlist_64);
790 if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) {
791 // Move the entire symbol table to a new location
792 const new_symoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.nlist_64));
793 const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64);
794
795 assert(new_symoff + existing_size <= self.linkedit_off + self.linkedit_size); // TODO expand LINKEDIT segment.
796 log.debug("relocating symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{
797 symtab.symoff,
798 symtab.symoff + existing_size,
799 new_symoff,
800 new_symoff + existing_size,
801 });
800 symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
802801
803 const amt = try self.file.copyRangeAll(symtab.symoff, self.file, new_symoff, existing_size);
804 if (amt != existing_size) return error.InputOutput;
805 symtab.symoff = @intCast(u32, new_symoff);
806 }
807 symtab.nsyms = @intCast(u32, nsyms);
808 self.load_commands_dirty = true;
802 var locals = std.ArrayList(macho.nlist_64).init(self.base.base.allocator);
803 defer locals.deinit();
804
805 for (self.base.locals.items) |sym| {
806 if (sym.n_strx == 0) continue;
807 if (self.base.symbol_resolver.get(sym.n_strx)) |_| continue;
808 try locals.append(sym);
809809 }
810}
811810
812pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
813 const tracy = trace(@src());
814 defer tracy.end();
815 try self.relocateSymbolTable();
816 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
817 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
818 log.debug("writing local symbol {} at 0x{x}", .{ index, off });
819 try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off);
811 const nlocals = locals.items.len;
812 const nexports = self.base.globals.items.len;
813
814 const locals_off = symtab.symoff;
815 const locals_size = nlocals * @sizeOf(macho.nlist_64);
816 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
817 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
818
819 const exports_off = locals_off + locals_size;
820 const exports_size = nexports * @sizeOf(macho.nlist_64);
821 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
822 try self.file.pwriteAll(mem.sliceAsBytes(self.base.globals.items), exports_off);
823
824 symtab.nsyms = @intCast(u32, nlocals + nexports);
825 seg.inner.filesize += locals_size + exports_size;
826
827 self.load_commands_dirty = true;
820828}
821829
822830fn writeStringTable(self: *DebugSymbols) !void {
823 if (!self.strtab_dirty) return;
824
825831 const tracy = trace(@src());
826832 defer tracy.end();
827833
834 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
828835 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
829 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
830 const needed_size = mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64));
836 symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
837 symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64)));
838 seg.inner.filesize += symtab.strsize;
831839
832 if (needed_size > allocated_size) {
833 symtab.strsize = 0;
834 symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
835 }
836 symtab.strsize = @intCast(u32, needed_size);
837840 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
838841
839842 try self.file.pwriteAll(self.base.strtab.items, symtab.stroff);
843
844 if (symtab.strsize > self.base.strtab.items.len) {
845 // This is potentially the last section, so we need to pad it out.
846 try self.file.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);
847 }
840848 self.load_commands_dirty = true;
841 self.strtab_dirty = false;
842849}
843850
844851pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void {
......@@ -846,14 +853,21 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M
846853 const tracy = trace(@src());
847854 defer tracy.end();
848855
856 log.debug("updateDeclLineNumber {s}{*}", .{ decl.name, decl });
857
849858 const func = decl.val.castTag(.function).?.data;
850 const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
859 log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{
860 decl.src_line,
861 func.lbrace_line,
862 func.rbrace_line,
863 });
864 const line = @intCast(u28, decl.src_line + func.lbrace_line);
851865
852866 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
853867 const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?];
854868 const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff();
855869 var data: [4]u8 = undefined;
856 leb.writeUnsignedFixed(4, &data, line_off);
870 leb.writeUnsignedFixed(4, &data, line);
857871 try self.file.pwriteAll(&data, file_pos);
858872}
859873
......@@ -886,7 +900,13 @@ pub fn initDeclDebugBuffers(
886900 try dbg_line_buffer.ensureTotalCapacity(26);
887901
888902 const func = decl.val.castTag(.function).?.data;
889 const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
903 log.debug("updateFunc {s}{*}", .{ decl.name, func.owner_decl });
904 log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{
905 decl.src_line,
906 func.lbrace_line,
907 func.rbrace_line,
908 });
909 const line = @intCast(u28, decl.src_line + func.lbrace_line);
890910
891911 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
892912 DW.LNS.extended_op,
......@@ -902,7 +922,7 @@ pub fn initDeclDebugBuffers(
902922 // to this function's begin curly.
903923 assert(getRelocDbgLineOff() == dbg_line_buffer.items.len);
904924 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
905 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
925 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line);
906926
907927 dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file);
908928 assert(getRelocDbgFileIndex() == dbg_line_buffer.items.len);
......@@ -917,7 +937,7 @@ pub fn initDeclDebugBuffers(
917937
918938 // .debug_info subprogram
919939 const decl_name_with_null = decl.name[0 .. mem.sliceTo(decl.name, 0).len + 1];
920 try dbg_info_buffer.ensureUnusedCapacity(27 + decl_name_with_null.len);
940 try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len);
921941
922942 const fn_ret_type = decl.ty.fnReturnType();
923943 const fn_ret_has_bits = fn_ret_type.hasRuntimeBits();
......@@ -945,8 +965,6 @@ pub fn initDeclDebugBuffers(
945965 dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4
946966 }
947967 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string
948 mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT.decl_line, DW.FORM.data4
949 dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT.decl_file, DW.FORM.data1
950968 },
951969 else => {
952970 // TODO implement .debug_info for global variables
......@@ -966,7 +984,6 @@ pub fn commitDeclDebugInfo(
966984 module: *Module,
967985 decl: *Module.Decl,
968986 debug_buffers: *DeclDebugBuffers,
969 target: std.Target,
970987) !void {
971988 const tracy = trace(@src());
972989 defer tracy.end();
......@@ -1097,14 +1114,26 @@ pub fn commitDeclDebugInfo(
10971114 if (dbg_info_buffer.items.len == 0)
10981115 return;
10991116
1117 // We need this for the duration of this function only so that for composite
1118 // types such as []const u32, if the type *u32 is non-existent, we create
1119 // it synthetically and store the backing bytes in this arena. After we are
1120 // done with the relocations, we can safely deinit the entire memory slab.
1121 // TODO currently, we do not store the relocations for future use, however,
1122 // if that is the case, we should move memory management to a higher scope,
1123 // such as linker scope, or whatnot.
1124 var dbg_type_arena = std.heap.ArenaAllocator.init(allocator);
1125 defer dbg_type_arena.deinit();
1126
11001127 {
11011128 // Now we emit the .debug_info types of the Decl. These will count towards the size of
11021129 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
11031130 // relocations yet.
1104 var it = dbg_info_type_relocs.iterator();
1105 while (it.next()) |entry| {
1106 entry.value_ptr.off = @intCast(u32, dbg_info_buffer.items.len);
1107 try self.addDbgInfoType(entry.key_ptr.*, dbg_info_buffer, target);
1131 var it: usize = 0;
1132 while (it < dbg_info_type_relocs.count()) : (it += 1) {
1133 const ty = dbg_info_type_relocs.keys()[it];
1134 const value_ptr = dbg_info_type_relocs.getPtr(ty).?;
1135 value_ptr.off = @intCast(u32, dbg_info_buffer.items.len);
1136 try self.addDbgInfoType(dbg_type_arena.allocator(), ty, dbg_info_buffer, dbg_info_type_relocs);
11081137 }
11091138 }
11101139
......@@ -1129,24 +1158,25 @@ pub fn commitDeclDebugInfo(
11291158/// Asserts the type has codegen bits.
11301159fn addDbgInfoType(
11311160 self: *DebugSymbols,
1161 arena: Allocator,
11321162 ty: Type,
11331163 dbg_info_buffer: *std.ArrayList(u8),
1134 target: std.Target,
1164 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
11351165) !void {
1136 _ = self;
1166 const target = self.base.base.options.target;
1167 var relocs = std.ArrayList(struct { ty: Type, reloc: u32 }).init(arena);
1168
11371169 switch (ty.zigTypeTag()) {
1138 .Void => unreachable,
11391170 .NoReturn => unreachable,
1171 .Void => {
1172 try dbg_info_buffer.append(abbrev_pad1);
1173 },
11401174 .Bool => {
11411175 try dbg_info_buffer.appendSlice(&[_]u8{
11421176 abbrev_base_type,
11431177 DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1
11441178 1, // DW.AT.byte_size, DW.FORM.data1
1145 'b',
1146 'o',
1147 'o',
1148 'l',
1149 0, // DW.AT.name, DW.FORM.string
1179 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string
11501180 });
11511181 },
11521182 .Int => {
......@@ -1163,11 +1193,120 @@ fn addDbgInfoType(
11631193 // DW.AT.name, DW.FORM.string
11641194 try dbg_info_buffer.writer().print("{}\x00", .{ty});
11651195 },
1196 .Optional => {
1197 if (ty.isPtrLikeOptional()) {
1198 try dbg_info_buffer.ensureUnusedCapacity(12);
1199 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
1200 // DW.AT.encoding, DW.FORM.data1
1201 dbg_info_buffer.appendAssumeCapacity(DW.ATE.address);
1202 // DW.AT.byte_size, DW.FORM.data1
1203 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target)));
1204 // DW.AT.name, DW.FORM.string
1205 try dbg_info_buffer.writer().print("{}\x00", .{ty});
1206 } else {
1207 log.debug("TODO implement .debug_info for type '{}'", .{ty});
1208 try dbg_info_buffer.append(abbrev_pad1);
1209 }
1210 },
1211 .Pointer => {
1212 if (ty.isSlice()) {
1213 // Slices are anonymous structs: struct { .ptr = *, .len = N }
1214 try dbg_info_buffer.ensureUnusedCapacity(23);
1215 // DW.AT.structure_type
1216 dbg_info_buffer.appendAssumeCapacity(abbrev_anon_struct_type);
1217 // DW.AT.byte_size, DW.FORM.sdata
1218 dbg_info_buffer.appendAssumeCapacity(16);
1219 // DW.AT.member
1220 dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member);
1221 // DW.AT.name, DW.FORM.string
1222 dbg_info_buffer.appendSliceAssumeCapacity("ptr");
1223 dbg_info_buffer.appendAssumeCapacity(0);
1224 // DW.AT.type, DW.FORM.ref4
1225 var index = dbg_info_buffer.items.len;
1226 try dbg_info_buffer.resize(index + 4);
1227 var buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
1228 const ptr_ty = ty.slicePtrFieldType(buf);
1229 try relocs.append(.{ .ty = ptr_ty, .reloc = @intCast(u32, index) });
1230 // DW.AT.data_member_location, DW.FORM.sdata
1231 dbg_info_buffer.appendAssumeCapacity(0);
1232 // DW.AT.member
1233 dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member);
1234 // DW.AT.name, DW.FORM.string
1235 dbg_info_buffer.appendSliceAssumeCapacity("len");
1236 dbg_info_buffer.appendAssumeCapacity(0);
1237 // DW.AT.type, DW.FORM.ref4
1238 index = dbg_info_buffer.items.len;
1239 try dbg_info_buffer.resize(index + 4);
1240 try relocs.append(.{ .ty = Type.initTag(.usize), .reloc = @intCast(u32, index) });
1241 // DW.AT.data_member_location, DW.FORM.sdata
1242 dbg_info_buffer.appendAssumeCapacity(8);
1243 // DW.AT.structure_type delimit children
1244 dbg_info_buffer.appendAssumeCapacity(0);
1245 } else {
1246 try dbg_info_buffer.ensureUnusedCapacity(5);
1247 dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type);
1248 // DW.AT.type, DW.FORM.ref4
1249 const index = dbg_info_buffer.items.len;
1250 try dbg_info_buffer.resize(index + 4);
1251 try relocs.append(.{ .ty = ty.childType(), .reloc = @intCast(u32, index) });
1252 }
1253 },
1254 .Struct => blk: {
1255 // try dbg_info_buffer.ensureUnusedCapacity(23);
1256 // DW.AT.structure_type
1257 try dbg_info_buffer.append(abbrev_struct_type);
1258 // DW.AT.byte_size, DW.FORM.sdata
1259 const abi_size = ty.abiSize(target);
1260 try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size);
1261 // DW.AT.name, DW.FORM.string
1262 const struct_name = try ty.nameAlloc(arena);
1263 try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1);
1264 dbg_info_buffer.appendSliceAssumeCapacity(struct_name);
1265 dbg_info_buffer.appendAssumeCapacity(0);
1266
1267 const struct_obj = ty.castTag(.@"struct").?.data;
1268 if (struct_obj.layout == .Packed) {
1269 log.debug("TODO implement .debug_info for packed structs", .{});
1270 break :blk;
1271 }
1272
1273 const fields = ty.structFields();
1274 for (fields.keys()) |field_name, field_index| {
1275 const field = fields.get(field_name).?;
1276 // DW.AT.member
1277 try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2);
1278 dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member);
1279 // DW.AT.name, DW.FORM.string
1280 dbg_info_buffer.appendSliceAssumeCapacity(field_name);
1281 dbg_info_buffer.appendAssumeCapacity(0);
1282 // DW.AT.type, DW.FORM.ref4
1283 var index = dbg_info_buffer.items.len;
1284 try dbg_info_buffer.resize(index + 4);
1285 try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) });
1286 // DW.AT.data_member_location, DW.FORM.sdata
1287 const field_off = ty.structFieldOffset(field_index, target);
1288 try leb128.writeULEB128(dbg_info_buffer.writer(), field_off);
1289 }
1290
1291 // DW.AT.structure_type delimit children
1292 try dbg_info_buffer.append(0);
1293 },
11661294 else => {
1167 std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});
1295 log.debug("TODO implement .debug_info for type '{}'", .{ty});
11681296 try dbg_info_buffer.append(abbrev_pad1);
11691297 },
11701298 }
1299
1300 for (relocs.items) |rel| {
1301 const gop = try dbg_info_type_relocs.getOrPut(self.base.base.allocator, rel.ty);
1302 if (!gop.found_existing) {
1303 gop.value_ptr.* = .{
1304 .off = undefined,
1305 .relocs = .{},
1306 };
1307 }
1308 try gop.value_ptr.relocs.append(self.base.base.allocator, rel.reloc);
1309 }
11711310}
11721311
11731312fn updateDeclDebugInfoAllocation(