Commit cf94341910

Jakub Konka <kubkon@jakubkonka.com>
2020-12-27 09:47:56
macho: write Mach-O dSym header
1 parent bd99a87
Changed files (2)
src/link/MachO/DebugSymbols.zig
@@ -1,7 +1,9 @@
 const DebugSymbols = @This();
 
 const std = @import("std");
+const assert = std.debug.assert;
 const fs = std.fs;
+const log = std.log.scoped(.link);
 const macho = std.macho;
 const mem = std.mem;
 const DW = std.dwarf;
@@ -35,6 +37,49 @@ symtab_cmd_index: ?u16 = null,
 /// UUID load command
 uuid_cmd_index: ?u16 = null,
 
+header_dirty: bool = false,
+load_commands_dirty: bool = false,
+
+/// You must call this function *after* `MachO.populateMissingMetadata()`
+/// has been called to get a viable debug symbols output.
+pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void {
+    if (self.header == null) {
+        const base_header = self.base.header.?;
+        var header: macho.mach_header_64 = undefined;
+        header.magic = macho.MH_MAGIC_64;
+        header.cputype = base_header.cputype;
+        header.cpusubtype = base_header.cpusubtype;
+        header.filetype = macho.MH_DSYM;
+        // These will get populated at the end of flushing the results to file.
+        header.ncmds = 0;
+        header.sizeofcmds = 0;
+        header.flags = 0;
+        header.reserved = 0;
+        self.header = header;
+        self.header_dirty = true;
+    }
+}
+
+pub fn flush(self: *DebugSymbols) !void {
+    try self.writeHeader();
+    assert(!self.header_dirty);
+    assert(!self.load_commands_dirty);
+}
+
 pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
     self.file.close();
 }
+
+fn writeHeader(self: *DebugSymbols) !void {
+    if (!self.header_dirty) return;
+
+    self.header.?.ncmds = @intCast(u32, self.load_commands.items.len);
+    var sizeofcmds: u32 = 0;
+    for (self.load_commands.items) |cmd| {
+        sizeofcmds += cmd.cmdsize();
+    }
+    self.header.?.sizeofcmds = sizeofcmds;
+    log.debug("writing Mach-O dSym header {}", .{self.header.?});
+    try self.file.pwriteAll(mem.asBytes(&self.header.?), 0);
+    self.header_dirty = false;
+}
src/link/MachO.zig
@@ -299,6 +299,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
     }
 
     try self.populateMissingMetadata();
+    try self.d_sym.?.populateMissingMetadata(allocator);
 
     return self;
 }
@@ -352,6 +353,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
             try self.writeStringTable();
             try self.updateLinkeditSegmentSizes();
 
+            if (self.d_sym) |*ds| {
+                // Flush debug symbols bundle.
+                try ds.flush();
+            }
+
             if (target.cpu.arch == .aarch64) {
                 // Preallocate space for the code signature.
                 // We need to do this at this stage so that we have the load commands with proper values