Commit bd99a87dc2

Jakub Konka <kubkon@jakubkonka.com>
2020-12-27 09:30:03
macho: create dSym bundle next to final artefact
macOS requires the debug symbols to either be part of the intermediate object file `whatever.o` or a companion `whatever.dSym` bundle. The former case seems ill-suited for our needs since it subscribes to the old-fashioned compilation strategy using intermediate compilation units; the latter is what we need however on macOS the debug symbols unlike in Elf are not part of the final artefact; rather they sit next to it in its own Mach-O file.
1 parent 0ff56e8
Changed files (2)
src/link/MachO/DebugSymbols.zig
@@ -0,0 +1,40 @@
+const DebugSymbols = @This();
+
+const std = @import("std");
+const fs = std.fs;
+const macho = std.macho;
+const mem = std.mem;
+const DW = std.dwarf;
+const leb = std.leb;
+const Allocator = mem.Allocator;
+
+const MachO = @import("../MachO.zig");
+
+usingnamespace @import("commands.zig");
+
+base: *MachO,
+file: fs.File,
+
+/// Mach header
+header: ?macho.mach_header_64 = null,
+
+/// Table of all load commands
+load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
+/// __PAGEZERO segment
+pagezero_segment_cmd_index: ?u16 = null,
+/// __TEXT segment
+text_segment_cmd_index: ?u16 = null,
+/// __DWARF segment
+dwarf_segment_cmd_index: ?u16 = null,
+/// __DATA segment
+data_segment_cmd_index: ?u16 = null,
+/// __LINKEDIT segment
+linkedit_segment_cmd_index: ?u16 = null,
+/// Symbol table
+symtab_cmd_index: ?u16 = null,
+/// UUID load command
+uuid_cmd_index: ?u16 = null,
+
+pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
+    self.file.close();
+}
src/link/MachO.zig
@@ -3,6 +3,7 @@ const MachO = @This();
 const std = @import("std");
 const Allocator = std.mem.Allocator;
 const assert = std.debug.assert;
+const fmt = std.fmt;
 const fs = std.fs;
 const log = std.log.scoped(.link);
 const macho = std.macho;
@@ -21,6 +22,7 @@ const File = link.File;
 const Cache = @import("../Cache.zig");
 const target_util = @import("../target.zig");
 
+const DebugSymbols = @import("MachO/DebugSymbols.zig");
 const Trie = @import("MachO/Trie.zig");
 const CodeSignature = @import("MachO/CodeSignature.zig");
 
@@ -31,6 +33,9 @@ pub const base_tag: File.Tag = File.Tag.macho;
 
 base: File,
 
+/// Debug symbols bundle (or dSym).
+d_sym: ?DebugSymbols = null,
+
 /// Page size is dependent on the target cpu architecture.
 /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.
 page_size: u16,
@@ -264,6 +269,20 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
 
     self.base.file = file;
 
+    // Create dSym bundle.
+    const d_sym_path = try fmt.allocPrint(allocator, "{}.dSym/Contents/Resources/DWARF/", .{sub_path});
+    defer allocator.free(d_sym_path);
+    var d_sym_bundle = try options.emit.?.directory.handle.makeOpenPath(d_sym_path, .{});
+    defer d_sym_bundle.close();
+    const d_sym_file = try d_sym_bundle.createFile(sub_path, .{
+        .truncate = false,
+        .read = true,
+    });
+    self.d_sym = .{
+        .base = self,
+        .file = d_sym_file,
+    };
+
     // Index 0 is always a null symbol.
     try self.local_symbols.append(allocator, .{
         .n_strx = 0,
@@ -943,6 +962,9 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
 }
 
 pub fn deinit(self: *MachO) void {
+    if (self.d_sym) |*ds| {
+        ds.deinit(self.base.allocator);
+    }
     self.binding_info_table.deinit(self.base.allocator);
     self.lazy_binding_info_table.deinit(self.base.allocator);
     self.pie_fixups.deinit(self.base.allocator);