diff options
| -rw-r--r-- | lib/std/macho.zig | 92 | ||||
| -rw-r--r-- | src/link/MachO.zig | 189 | ||||
| -rw-r--r-- | src/link/MachO/DebugSymbols.zig | 78 | ||||
| -rw-r--r-- | src/link/MachO/load_commands.zig | 60 | ||||
| -rw-r--r-- | src/link/MachO/zld.zig | 265 |
5 files changed, 268 insertions, 416 deletions
diff --git a/lib/std/macho.zig b/lib/std/macho.zig index cb1fca20b2..24dd1749ea 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -58,10 +58,10 @@ pub const uuid_command = extern struct { cmd: LC = .UUID, /// sizeof(struct uuid_command) - cmdsize: u32, + cmdsize: u32 = @sizeOf(uuid_command), /// the 128-bit uuid - uuid: [16]u8, + uuid: [16]u8 = undefined, }; /// The version_min_command contains the min OS version on which this @@ -71,7 +71,7 @@ pub const version_min_command = extern struct { cmd: LC, /// sizeof(struct version_min_command) - cmdsize: u32, + cmdsize: u32 = @sizeOf(version_min_command), /// X.Y.Z is encoded in nibbles xxxx.yy.zz version: u32, @@ -87,7 +87,7 @@ pub const source_version_command = extern struct { cmd: LC = .SOURCE_VERSION, /// sizeof(source_version_command) - cmdsize: u32, + cmdsize: u32 = @sizeOf(source_version_command), /// A.B.C.D.E packed as a24.b10.c10.d10.e10 version: u64, @@ -155,13 +155,13 @@ pub const entry_point_command = extern struct { cmd: LC = .MAIN, /// sizeof(struct entry_point_command) - cmdsize: u32, + cmdsize: u32 = @sizeOf(entry_point_command), /// file (__TEXT) offset of main() - entryoff: u64, + entryoff: u64 = 0, /// if not zero, initial stack size - stacksize: u64, + stacksize: u64 = 0, }; /// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD @@ -172,19 +172,19 @@ pub const symtab_command = extern struct { cmd: LC = .SYMTAB, /// sizeof(struct symtab_command) - cmdsize: u32, + cmdsize: u32 = @sizeOf(symtab_command), /// symbol table offset - symoff: u32, + symoff: u32 = 0, /// number of symbol table entries - nsyms: u32, + nsyms: u32 = 0, /// string table offset - stroff: u32, + stroff: u32 = 0, /// string table size in bytes - strsize: u32, + strsize: u32 = 0, }; /// This is the second set of the symbolic information which is used to support @@ -230,7 +230,7 @@ pub const dysymtab_command = extern struct { cmd: LC = .DYSYMTAB, /// sizeof(struct dysymtab_command) - cmdsize: u32, + cmdsize: u32 = @sizeOf(dysymtab_command), // The symbols indicated by symoff and nsyms of the LC_SYMTAB load command // are grouped into the following three groups: @@ -247,22 +247,22 @@ pub const dysymtab_command = extern struct { // table when this is a dynamically linked shared library file). /// index of local symbols - ilocalsym: u32, + ilocalsym: u32 = 0, /// number of local symbols - nlocalsym: u32, + nlocalsym: u32 = 0, /// index to externally defined symbols - iextdefsym: u32, + iextdefsym: u32 = 0, /// number of externally defined symbols - nextdefsym: u32, + nextdefsym: u32 = 0, /// index to undefined symbols - iundefsym: u32, + iundefsym: u32 = 0, /// number of undefined symbols - nundefsym: u32, + nundefsym: u32 = 0, // For the for the dynamic binding process to find which module a symbol // is defined in the table of contents is used (analogous to the ranlib @@ -272,10 +272,10 @@ pub const dysymtab_command = extern struct { // symbols are sorted by name and is use as the table of contents. /// file offset to table of contents - tocoff: u32, + tocoff: u32 = 0, /// number of entries in table of contents - ntoc: u32, + ntoc: u32 = 0, // To support dynamic binding of "modules" (whole object files) the symbol // table must reflect the modules that the file was created from. This is @@ -286,10 +286,10 @@ pub const dysymtab_command = extern struct { // contains one module so everything in the file belongs to the module. /// file offset to module table - modtaboff: u32, + modtaboff: u32 = 0, /// number of module table entries - nmodtab: u32, + nmodtab: u32 = 0, // To support dynamic module binding the module structure for each module // indicates the external references (defined and undefined) each module @@ -300,10 +300,10 @@ pub const dysymtab_command = extern struct { // undefined external symbols indicates the external references. /// offset to referenced symbol table - extrefsymoff: u32, + extrefsymoff: u32 = 0, /// number of referenced symbol table entries - nextrefsyms: u32, + nextrefsyms: u32 = 0, // The sections that contain "symbol pointers" and "routine stubs" have // indexes and (implied counts based on the size of the section and fixed @@ -315,10 +315,10 @@ pub const dysymtab_command = extern struct { // The indirect symbol table is ordered to match the entries in the section. /// file offset to the indirect symbol table - indirectsymoff: u32, + indirectsymoff: u32 = 0, /// number of indirect symbol table entries - nindirectsyms: u32, + nindirectsyms: u32 = 0, // To support relocating an individual module in a library file quickly the // external relocation entries for each module in the library need to be @@ -347,20 +347,20 @@ pub const dysymtab_command = extern struct { // remaining relocation entries must be local). /// offset to external relocation entries - extreloff: u32, + extreloff: u32 = 0, /// number of external relocation entries - nextrel: u32, + nextrel: u32 = 0, // All the local relocation entries are grouped together (they are not // grouped by their module since they are only used if the object is moved // from it staticly link edited address). /// offset to local relocation entries - locreloff: u32, + locreloff: u32 = 0, /// number of local relocation entries - nlocrel: u32, + nlocrel: u32 = 0, }; /// The linkedit_data_command contains the offsets and sizes of a blob @@ -370,13 +370,13 @@ pub const linkedit_data_command = extern struct { cmd: LC, /// sizeof(struct linkedit_data_command) - cmdsize: u32, + cmdsize: u32 = @sizeOf(linkedit_data_command), /// file offset of data in __LINKEDIT segment - dataoff: u32, + dataoff: u32 = 0, /// file size of data in __LINKEDIT segment - datasize: u32, + datasize: u32 = 0, }; /// The dyld_info_command contains the file offsets and sizes of @@ -387,10 +387,10 @@ pub const linkedit_data_command = extern struct { /// to interpret it. pub const dyld_info_command = extern struct { /// LC_DYLD_INFO or LC_DYLD_INFO_ONLY - cmd: LC, + cmd: LC = .DYLD_INFO_ONLY, /// sizeof(struct dyld_info_command) - cmdsize: u32, + cmdsize: u32 = @sizeOf(dyld_info_command), // Dyld rebases an image whenever dyld loads it at an address different // from its preferred address. The rebase information is a stream @@ -403,10 +403,10 @@ pub const dyld_info_command = extern struct { // bytes. /// file offset to rebase info - rebase_off: u32, + rebase_off: u32 = 0, /// size of rebase info - rebase_size: u32, + rebase_size: u32 = 0, // Dyld binds an image during the loading process, if the image // requires any pointers to be initialized to symbols in other images. @@ -420,10 +420,10 @@ pub const dyld_info_command = extern struct { // encoded in a few bytes. /// file offset to binding info - bind_off: u32, + bind_off: u32 = 0, /// size of binding info - bind_size: u32, + bind_size: u32 = 0, // Some C++ programs require dyld to unique symbols so that all // images in the process use the same copy of some code/data. @@ -440,10 +440,10 @@ pub const dyld_info_command = extern struct { // and the call to operator new is then rebound. /// file offset to weak binding info - weak_bind_off: u32, + weak_bind_off: u32 = 0, /// size of weak binding info - weak_bind_size: u32, + weak_bind_size: u32 = 0, // Some uses of external symbols do not need to be bound immediately. // Instead they can be lazily bound on first use. The lazy_bind @@ -457,10 +457,10 @@ pub const dyld_info_command = extern struct { // to bind. /// file offset to lazy binding info - lazy_bind_off: u32, + lazy_bind_off: u32 = 0, /// size of lazy binding info - lazy_bind_size: u32, + lazy_bind_size: u32 = 0, // The symbols exported by a dylib are encoded in a trie. This // is a compact representation that factors out common prefixes. @@ -494,10 +494,10 @@ pub const dyld_info_command = extern struct { // edge points to. /// file offset to lazy binding info - export_off: u32, + export_off: u32 = 0, /// size of lazy binding info - export_size: u32, + export_size: u32 = 0, }; /// A program that uses a dynamic linker contains a dylinker_command to identify diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 0422a983dc..b06552bc2a 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -99,10 +99,11 @@ page_size: u16, /// fashion (default for LLVM backend). mode: enum { incremental, one_shot }, -uuid: struct { - buf: [16]u8 = undefined, - final: bool = false, -} = .{}, +dyld_info_cmd: macho.dyld_info_command = .{}, +symtab_cmd: macho.symtab_command = .{}, +dysymtab_cmd: macho.dysymtab_command = .{}, +uuid_cmd: macho.uuid_command = .{}, +codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE }, dylibs: std.ArrayListUnmanaged(Dylib) = .{}, dylibs_map: std.StringHashMapUnmanaged(u16) = .{}, @@ -554,12 +555,17 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No self.logAtoms(); } + try self.writeLinkeditSegmentData(); + + // Write load commands var lc_buffer = std.ArrayList(u8).init(arena); const lc_writer = lc_buffer.writer(); - var ncmds: u32 = 0; - try self.writeLinkeditSegmentData(&ncmds, lc_writer); - try load_commands.writeDylinkerLC(&ncmds, lc_writer); + try self.writeSegmentHeaders(lc_writer); + try lc_writer.writeStruct(self.dyld_info_cmd); + try lc_writer.writeStruct(self.symtab_cmd); + try lc_writer.writeStruct(self.dysymtab_cmd); + try load_commands.writeDylinkerLC(lc_writer); switch (self.base.options.output_mode) { .Exe => blk: { @@ -573,33 +579,29 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No else => |e| return e, }; const sym = self.getSymbol(global); - try load_commands.writeMainLC(@intCast(u32, sym.n_value - seg.vmaddr), &self.base.options, &ncmds, lc_writer); + try lc_writer.writeStruct(macho.entry_point_command{ + .entryoff = @intCast(u32, sym.n_value - seg.vmaddr), + .stacksize = self.base.options.stack_size_override orelse 0, + }); }, .Lib => if (self.base.options.link_mode == .Dynamic) { - try load_commands.writeDylibIdLC(self.base.allocator, &self.base.options, &ncmds, lc_writer); + try load_commands.writeDylibIdLC(self.base.allocator, &self.base.options, lc_writer); }, else => {}, } - try load_commands.writeRpathLCs(self.base.allocator, &self.base.options, &ncmds, lc_writer); - - { - try lc_writer.writeStruct(macho.source_version_command{ - .cmdsize = @sizeOf(macho.source_version_command), - .version = 0x0, - }); - ncmds += 1; - } - - try load_commands.writeBuildVersionLC(&self.base.options, &ncmds, lc_writer); + try load_commands.writeRpathLCs(self.base.allocator, &self.base.options, lc_writer); + try lc_writer.writeStruct(macho.source_version_command{ + .version = 0, + }); + try load_commands.writeBuildVersionLC(&self.base.options, lc_writer); - if (!self.uuid.final) { - std.crypto.random.bytes(&self.uuid.buf); - self.uuid.final = true; + if (self.cold_start) { + std.crypto.random.bytes(&self.uuid_cmd.uuid); } - try load_commands.writeUuidLC(&self.uuid.buf, &ncmds, lc_writer); + try lc_writer.writeStruct(self.uuid_cmd); - try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), &ncmds, lc_writer); + try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer); const target = self.base.options.target; const requires_codesig = blk: { @@ -608,7 +610,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No break :blk true; break :blk false; }; - var codesig_offset: ?u32 = null; var codesig: ?CodeSignature = if (requires_codesig) blk: { // Preallocate space for the code signature. // We need to do this at this stage so that we have the load commands with proper values @@ -620,20 +621,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No if (self.base.options.entitlements) |path| { try codesig.addEntitlements(arena, path); } - codesig_offset = try self.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer); + try self.writeCodeSignaturePadding(&codesig); + try lc_writer.writeStruct(self.codesig_cmd); break :blk codesig; } else null; - var headers_buf = std.ArrayList(u8).init(arena); - try self.writeSegmentHeaders(&ncmds, headers_buf.writer()); + try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64)); - try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64)); - try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len); - - try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len)); + const ncmds = load_commands.calcNumOfLCs(lc_buffer.items); + try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len)); if (codesig) |*csig| { - try self.writeCodeSignature(comp, csig, codesig_offset.?); // code signing always comes last + try self.writeCodeSignature(comp, csig); // code signing always comes last } if (self.d_sym) |*d_sym| { @@ -3146,18 +3145,17 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 { return global_index; } -fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void { +fn writeSegmentHeaders(self: *MachO, writer: anytype) !void { for (self.segments.items) |seg, i| { const indexes = self.getSectionIndexes(@intCast(u8, i)); try writer.writeStruct(seg); for (self.sections.items(.header)[indexes.start..indexes.end]) |header| { try writer.writeStruct(header); } - ncmds.* += 1; } } -fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void { +fn writeLinkeditSegmentData(self: *MachO) !void { const seg = self.getLinkeditSegmentPtr(); seg.filesize = 0; seg.vmsize = 0; @@ -3172,8 +3170,8 @@ fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void } } - try self.writeDyldInfoData(ncmds, lc_writer); - try self.writeSymtabs(ncmds, lc_writer); + try self.writeDyldInfoData(); + try self.writeSymtabs(); seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size); } @@ -3325,7 +3323,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void { try trie.finalize(gpa); } -fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void { +fn writeDyldInfoData(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); @@ -3396,21 +3394,14 @@ fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void { const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow); try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]); - try lc_writer.writeStruct(macho.dyld_info_command{ - .cmd = .DYLD_INFO_ONLY, - .cmdsize = @sizeOf(macho.dyld_info_command), - .rebase_off = @intCast(u32, rebase_off), - .rebase_size = @intCast(u32, rebase_size), - .bind_off = @intCast(u32, bind_off), - .bind_size = @intCast(u32, bind_size), - .weak_bind_off = 0, - .weak_bind_size = 0, - .lazy_bind_off = @intCast(u32, lazy_bind_off), - .lazy_bind_size = @intCast(u32, lazy_bind_size), - .export_off = @intCast(u32, export_off), - .export_size = @intCast(u32, export_size), - }); - ncmds.* += 1; + self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off); + self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size); + self.dyld_info_cmd.bind_off = @intCast(u32, bind_off); + self.dyld_info_cmd.bind_size = @intCast(u32, bind_size); + self.dyld_info_cmd.lazy_bind_off = @intCast(u32, lazy_bind_off); + self.dyld_info_cmd.lazy_bind_size = @intCast(u32, lazy_bind_size); + self.dyld_info_cmd.export_off = @intCast(u32, export_off); + self.dyld_info_cmd.export_size = @intCast(u32, export_size); } fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { @@ -3512,45 +3503,14 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { } } -fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void { - var symtab_cmd = macho.symtab_command{ - .cmdsize = @sizeOf(macho.symtab_command), - .symoff = 0, - .nsyms = 0, - .stroff = 0, - .strsize = 0, - }; - var dysymtab_cmd = macho.dysymtab_command{ - .cmdsize = @sizeOf(macho.dysymtab_command), - .ilocalsym = 0, - .nlocalsym = 0, - .iextdefsym = 0, - .nextdefsym = 0, - .iundefsym = 0, - .nundefsym = 0, - .tocoff = 0, - .ntoc = 0, - .modtaboff = 0, - .nmodtab = 0, - .extrefsymoff = 0, - .nextrefsyms = 0, - .indirectsymoff = 0, - .nindirectsyms = 0, - .extreloff = 0, - .nextrel = 0, - .locreloff = 0, - .nlocrel = 0, - }; - var ctx = try self.writeSymtab(&symtab_cmd); +fn writeSymtabs(self: *MachO) !void { + var ctx = try self.writeSymtab(); defer ctx.imports_table.deinit(); - try self.writeDysymtab(ctx, &dysymtab_cmd); - try self.writeStrtab(&symtab_cmd); - try lc_writer.writeStruct(symtab_cmd); - try lc_writer.writeStruct(dysymtab_cmd); - ncmds.* += 2; + try self.writeDysymtab(ctx); + try self.writeStrtab(); } -fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx { +fn writeSymtab(self: *MachO) !SymtabCtx { const gpa = self.base.allocator; var locals = std.ArrayList(macho.nlist_64).init(gpa); @@ -3615,8 +3575,8 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx { log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); try self.base.file.?.pwriteAll(buffer.items, offset); - lc.symoff = @intCast(u32, offset); - lc.nsyms = nsyms; + self.symtab_cmd.symoff = @intCast(u32, offset); + self.symtab_cmd.nsyms = nsyms; return SymtabCtx{ .nlocalsym = nlocals, @@ -3626,7 +3586,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx { }; } -fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void { +fn writeStrtab(self: *MachO) !void { const seg = self.getLinkeditSegmentPtr(); const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)); const needed_size = self.strtab.buffer.items.len; @@ -3636,8 +3596,8 @@ fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void { try self.base.file.?.pwriteAll(self.strtab.buffer.items, offset); - lc.stroff = @intCast(u32, offset); - lc.strsize = @intCast(u32, needed_size); + self.symtab_cmd.stroff = @intCast(u32, offset); + self.symtab_cmd.strsize = @intCast(u32, needed_size); } const SymtabCtx = struct { @@ -3647,7 +3607,7 @@ const SymtabCtx = struct { imports_table: std.AutoHashMap(SymbolWithLoc, u32), }; -fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void { +fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { const gpa = self.base.allocator; const nstubs = @intCast(u32, self.stubs_table.count()); const ngot_entries = @intCast(u32, self.got_entries_table.count()); @@ -3706,21 +3666,16 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi assert(buf.items.len == needed_size); try self.base.file.?.pwriteAll(buf.items, offset); - lc.nlocalsym = ctx.nlocalsym; - lc.iextdefsym = iextdefsym; - lc.nextdefsym = ctx.nextdefsym; - lc.iundefsym = iundefsym; - lc.nundefsym = ctx.nundefsym; - lc.indirectsymoff = @intCast(u32, offset); - lc.nindirectsyms = nindirectsyms; + self.dysymtab_cmd.nlocalsym = ctx.nlocalsym; + self.dysymtab_cmd.iextdefsym = iextdefsym; + self.dysymtab_cmd.nextdefsym = ctx.nextdefsym; + self.dysymtab_cmd.iundefsym = iundefsym; + self.dysymtab_cmd.nundefsym = ctx.nundefsym; + self.dysymtab_cmd.indirectsymoff = @intCast(u32, offset); + self.dysymtab_cmd.nindirectsyms = nindirectsyms; } -fn writeCodeSignaturePadding( - self: *MachO, - code_sig: *CodeSignature, - ncmds: *u32, - lc_writer: anytype, -) !u32 { +fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { const seg = self.getLinkeditSegmentPtr(); // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 @@ -3733,19 +3688,13 @@ fn writeCodeSignaturePadding( // except for code signature data. try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1); - try lc_writer.writeStruct(macho.linkedit_data_command{ - .cmd = .CODE_SIGNATURE, - .cmdsize = @sizeOf(macho.linkedit_data_command), - .dataoff = @intCast(u32, offset), - .datasize = @intCast(u32, needed_size), - }); - ncmds.* += 1; - - return @intCast(u32, offset); + self.codesig_cmd.dataoff = @intCast(u32, offset); + self.codesig_cmd.datasize = @intCast(u32, needed_size); } -fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature, offset: u32) !void { +fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void { const seg = self.getSegment(self.text_section_index.?); + const offset = self.codesig_cmd.dataoff; var buffer = std.ArrayList(u8).init(self.base.allocator); defer buffer.deinit(); diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 22905a520a..a13ad9c9f4 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -26,6 +26,8 @@ dwarf: Dwarf, file: fs.File, page_size: u16, +symtab_cmd: macho.symtab_command = .{}, + segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{}, sections: std.ArrayListUnmanaged(macho.section_64) = .{}, @@ -296,28 +298,21 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { } } + self.finalizeDwarfSegment(macho_file); + try self.writeLinkeditSegmentData(macho_file); + + // Write load commands var lc_buffer = std.ArrayList(u8).init(self.allocator); defer lc_buffer.deinit(); const lc_writer = lc_buffer.writer(); - var ncmds: u32 = 0; - - self.finalizeDwarfSegment(macho_file); - try self.writeLinkeditSegmentData(macho_file, &ncmds, lc_writer); - try load_commands.writeUuidLC(&macho_file.uuid.buf, &ncmds, lc_writer); + try self.writeSegmentHeaders(macho_file, lc_writer); + try lc_writer.writeStruct(self.symtab_cmd); + try lc_writer.writeStruct(macho_file.uuid_cmd); - var headers_buf = std.ArrayList(u8).init(self.allocator); - defer headers_buf.deinit(); - try self.writeSegmentHeaders(macho_file, &ncmds, headers_buf.writer()); - - try self.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64)); - try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len); - - try self.writeHeader( - macho_file, - ncmds, - @intCast(u32, lc_buffer.items.len + headers_buf.items.len), - ); + const ncmds = load_commands.calcNumOfLCs(lc_buffer.items); + try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64)); + try self.writeHeader(macho_file, ncmds, @intCast(u32, lc_buffer.items.len)); assert(!self.debug_abbrev_section_dirty); assert(!self.debug_aranges_section_dirty); @@ -384,7 +379,7 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void { log.debug("found __LINKEDIT segment free space at 0x{x}", .{linkedit.fileoff}); } -fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, writer: anytype) !void { +fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, writer: anytype) !void { // Write segment/section headers from the binary file first. const end = macho_file.linkedit_segment_cmd_index.?; for (macho_file.segments.items[0..end]) |seg, i| { @@ -414,8 +409,6 @@ fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, wri out_header.offset = 0; try writer.writeStruct(out_header); } - - ncmds.* += 1; } // Next, commit DSYM's __LINKEDIT and __DWARF segments headers. for (self.segments.items) |seg, i| { @@ -424,7 +417,6 @@ fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, wri for (self.sections.items[indexes.start..indexes.end]) |header| { try writer.writeStruct(header); } - ncmds.* += 1; } } @@ -463,33 +455,19 @@ fn allocatedSize(self: *DebugSymbols, start: u64) u64 { return min_pos - start; } -fn writeLinkeditSegmentData( - self: *DebugSymbols, - macho_file: *MachO, - ncmds: *u32, - lc_writer: anytype, -) !void { +fn writeLinkeditSegmentData(self: *DebugSymbols, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - var symtab_cmd = macho.symtab_command{ - .cmdsize = @sizeOf(macho.symtab_command), - .symoff = 0, - .nsyms = 0, - .stroff = 0, - .strsize = 0, - }; - try self.writeSymtab(macho_file, &symtab_cmd); - try self.writeStrtab(&symtab_cmd); - try lc_writer.writeStruct(symtab_cmd); - ncmds.* += 1; + try self.writeSymtab(macho_file); + try self.writeStrtab(); const seg = &self.segments.items[self.linkedit_segment_cmd_index.?]; const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.page_size); seg.vmsize = aligned_size; } -fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_command) !void { +fn writeSymtab(self: *DebugSymbols, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); @@ -528,10 +506,10 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_comman const needed_size = nsyms * @sizeOf(macho.nlist_64); seg.filesize = offset + needed_size - seg.fileoff; - lc.symoff = @intCast(u32, offset); - lc.nsyms = @intCast(u32, nsyms); + self.symtab_cmd.symoff = @intCast(u32, offset); + self.symtab_cmd.nsyms = @intCast(u32, nsyms); - const locals_off = lc.symoff; + const locals_off = @intCast(u32, offset); const locals_size = nlocals * @sizeOf(macho.nlist_64); const exports_off = locals_off + locals_size; const exports_size = nexports * @sizeOf(macho.nlist_64); @@ -543,26 +521,26 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_comman try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off); } -fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void { +fn writeStrtab(self: *DebugSymbols) !void { const tracy = trace(@src()); defer tracy.end(); const seg = &self.segments.items[self.linkedit_segment_cmd_index.?]; - const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64)); - const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64)); + const symtab_size = @intCast(u32, self.symtab_cmd.nsyms * @sizeOf(macho.nlist_64)); + const offset = mem.alignForwardGeneric(u64, self.symtab_cmd.symoff + symtab_size, @alignOf(u64)); const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64)); seg.filesize = offset + needed_size - seg.fileoff; - lc.stroff = @intCast(u32, offset); - lc.strsize = @intCast(u32, needed_size); + self.symtab_cmd.stroff = @intCast(u32, offset); + self.symtab_cmd.strsize = @intCast(u32, needed_size); - log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize }); + log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); - try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff); + try self.file.pwriteAll(self.strtab.buffer.items, offset); if (self.strtab.buffer.items.len < needed_size) { // Ensure we are always padded to the actual length of the file. - try self.file.pwriteAll(&[_]u8{0}, lc.stroff + lc.strsize); + try self.file.pwriteAll(&[_]u8{0}, offset + needed_size); } } diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index c4e565928b..73c28965ff 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -131,7 +131,19 @@ pub fn calcMinHeaderPad(gpa: Allocator, options: *const link.Options, ctx: CalcL return offset; } -pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void { +pub fn calcNumOfLCs(lc_buffer: []const u8) u32 { + var ncmds: u32 = 0; + var pos: usize = 0; + while (true) { + if (pos >= lc_buffer.len) break; + const cmd = @ptrCast(*align(1) const macho.load_command, lc_buffer.ptr + pos).*; + ncmds += 1; + pos += cmd.cmdsize; + } + return ncmds; +} + +pub fn writeDylinkerLC(lc_writer: anytype) !void { const name_len = mem.sliceTo(default_dyld_path, 0).len; const cmdsize = @intCast(u32, mem.alignForwardGeneric( u64, @@ -148,7 +160,6 @@ pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void { if (padding > 0) { try lc_writer.writeByteNTimes(0, padding); } - ncmds.* += 1; } const WriteDylibLCCtx = struct { @@ -159,7 +170,7 @@ const WriteDylibLCCtx = struct { compatibility_version: u32 = 0x10000, }; -fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void { +fn writeDylibLC(ctx: WriteDylibLCCtx, lc_writer: anytype) !void { const name_len = ctx.name.len + 1; const cmdsize = @intCast(u32, mem.alignForwardGeneric( u64, @@ -182,10 +193,9 @@ fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void { if (padding > 0) { try lc_writer.writeByteNTimes(0, padding); } - ncmds.* += 1; } -pub fn writeDylibIdLC(gpa: Allocator, options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void { +pub fn writeDylibIdLC(gpa: Allocator, options: *const link.Options, lc_writer: anytype) !void { assert(options.output_mode == .Lib and options.link_mode == .Dynamic); const emit = options.emit.?; const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path}); @@ -205,18 +215,7 @@ pub fn writeDylibIdLC(gpa: Allocator, options: *const link.Options, ncmds: *u32, .name = install_name, .current_version = curr.major << 16 | curr.minor << 8 | curr.patch, .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch, - }, ncmds, lc_writer); -} - -pub fn writeMainLC(entryoff: u32, options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void { - assert(options.output_mode == .Exe); - try lc_writer.writeStruct(macho.entry_point_command{ - .cmd = .MAIN, - .cmdsize = @sizeOf(macho.entry_point_command), - .entryoff = entryoff, - .stacksize = options.stack_size_override orelse 0, - }); - ncmds.* += 1; + }, lc_writer); } const RpathIterator = struct { @@ -244,7 +243,7 @@ const RpathIterator = struct { } }; -pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void { +pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, lc_writer: anytype) !void { var it = RpathIterator.init(gpa, options.rpath_list); defer it.deinit(); @@ -265,11 +264,10 @@ pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, ncmds: *u32, if (padding > 0) { try lc_writer.writeByteNTimes(0, padding); } - ncmds.* += 1; } } -pub fn writeBuildVersionLC(options: *const link.Options, ncmds: *u32, lc_writer: anytype) !void { +pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !void { const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version); const platform_version = blk: { const ver = options.target.os.version_range.semver.min; @@ -299,10 +297,9 @@ pub fn writeBuildVersionLC(options: *const link.Options, ncmds: *u32, lc_writer: .tool = .LD, .version = 0x0, })); - ncmds.* += 1; } -pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, ncmds: *u32, lc_writer: anytype) !void { +pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, lc_writer: anytype) !void { for (referenced) |index| { const dylib = dylibs[index]; const dylib_id = dylib.id orelse unreachable; @@ -312,23 +309,6 @@ pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, ncmds: *u32, .timestamp = dylib_id.timestamp, .current_version = dylib_id.current_version, .compatibility_version = dylib_id.compatibility_version, - }, ncmds, lc_writer); + }, lc_writer); } } - -pub fn writeSourceVersionLC(ncmds: *u32, lc_writer: anytype) !void { - try lc_writer.writeStruct(macho.source_version_command{ - .cmdsize = @sizeOf(macho.source_version_command), - .version = 0x0, - }); - ncmds.* += 1; -} - -pub fn writeUuidLC(uuid: *const [16]u8, ncmds: *u32, lc_writer: anytype) !void { - var uuid_lc = macho.uuid_command{ - .cmdsize = @sizeOf(macho.uuid_command), - .uuid = uuid.*, - }; - try lc_writer.writeStruct(uuid_lc); - ncmds.* += 1; -} diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index 60d0f853ab..5ca1afd98c 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -38,6 +38,14 @@ pub const Zld = struct { page_size: u16, options: *const link.Options, + dyld_info_cmd: macho.dyld_info_command = .{}, + symtab_cmd: macho.symtab_command = .{}, + dysymtab_cmd: macho.dysymtab_command = .{}, + function_starts_cmd: macho.linkedit_data_command = .{ .cmd = .FUNCTION_STARTS }, + data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE }, + uuid_cmd: macho.uuid_command = .{}, + codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE }, + objects: std.ArrayListUnmanaged(Object) = .{}, archives: std.ArrayListUnmanaged(Archive) = .{}, dylibs: std.ArrayListUnmanaged(Dylib) = .{}, @@ -1728,7 +1736,7 @@ pub const Zld = struct { return (@intCast(u8, segment_precedence) << 4) + section_precedence; } - fn writeSegmentHeaders(self: *Zld, ncmds: *u32, writer: anytype) !void { + fn writeSegmentHeaders(self: *Zld, writer: anytype) !void { for (self.segments.items) |seg, i| { const indexes = self.getSectionIndexes(@intCast(u8, i)); var out_seg = seg; @@ -1752,16 +1760,14 @@ pub const Zld = struct { if (header.size == 0) continue; try writer.writeStruct(header); } - - ncmds.* += 1; } } - fn writeLinkeditSegmentData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void { - try self.writeDyldInfoData(ncmds, lc_writer, reverse_lookups); - try self.writeFunctionStarts(ncmds, lc_writer); - try self.writeDataInCode(ncmds, lc_writer); - try self.writeSymtabs(ncmds, lc_writer); + fn writeLinkeditSegmentData(self: *Zld, reverse_lookups: [][]u32) !void { + try self.writeDyldInfoData(reverse_lookups); + try self.writeFunctionStarts(); + try self.writeDataInCode(); + try self.writeSymtabs(); const seg = self.getLinkeditSegmentPtr(); seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size); @@ -2150,7 +2156,7 @@ pub const Zld = struct { try trie.finalize(gpa); } - fn writeDyldInfoData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void { + fn writeDyldInfoData(self: *Zld, reverse_lookups: [][]u32) !void { const gpa = self.gpa; var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa); @@ -2219,21 +2225,14 @@ pub const Zld = struct { const size = math.cast(usize, lazy_bind_size) orelse return error.Overflow; try self.populateLazyBindOffsetsInStubHelper(buffer[offset..][0..size]); - try lc_writer.writeStruct(macho.dyld_info_command{ - .cmd = .DYLD_INFO_ONLY, - .cmdsize = @sizeOf(macho.dyld_info_command), - .rebase_off = @intCast(u32, rebase_off), - .rebase_size = @intCast(u32, rebase_size), - .bind_off = @intCast(u32, bind_off), - .bind_size = @intCast(u32, bind_size), - .weak_bind_off = 0, - .weak_bind_size = 0, - .lazy_bind_off = @intCast(u32, lazy_bind_off), - .lazy_bind_size = @intCast(u32, lazy_bind_size), - .export_off = @intCast(u32, export_off), - .export_size = @intCast(u32, export_size), - }); - ncmds.* += 1; + self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off); + self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size); + self.dyld_info_cmd.bind_off = @intCast(u32, bind_off); + self.dyld_info_cmd.bind_size = @intCast(u32, bind_size); + self.dyld_info_cmd.lazy_bind_off = @intCast(u32, lazy_bind_off); + self.dyld_info_cmd.lazy_bind_size = @intCast(u32, lazy_bind_size); + self.dyld_info_cmd.export_off = @intCast(u32, export_off); + self.dyld_info_cmd.export_size = @intCast(u32, export_size); } fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void { @@ -2351,7 +2350,7 @@ pub const Zld = struct { const asc_u64 = std.sort.asc(u64); - fn writeFunctionStarts(self: *Zld, ncmds: *u32, lc_writer: anytype) !void { + fn writeFunctionStarts(self: *Zld) !void { const text_seg_index = self.getSegmentByName("__TEXT") orelse return; const text_sect_index = self.getSectionByName("__TEXT", "__text") orelse return; const text_seg = self.segments.items[text_seg_index]; @@ -2410,13 +2409,8 @@ pub const Zld = struct { try self.file.pwriteAll(buffer.items, offset); - try lc_writer.writeStruct(macho.linkedit_data_command{ - .cmd = .FUNCTION_STARTS, - .cmdsize = @sizeOf(macho.linkedit_data_command), - .dataoff = @intCast(u32, offset), - .datasize = @intCast(u32, needed_size), - }); - ncmds.* += 1; + self.function_starts_cmd.dataoff = @intCast(u32, offset); + self.function_starts_cmd.datasize = @intCast(u32, needed_size); } fn filterDataInCode( @@ -2438,7 +2432,7 @@ pub const Zld = struct { return dices[start..end]; } - fn writeDataInCode(self: *Zld, ncmds: *u32, lc_writer: anytype) !void { + fn writeDataInCode(self: *Zld) !void { var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa); defer out_dice.deinit(); @@ -2488,54 +2482,19 @@ pub const Zld = struct { log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); try self.file.pwriteAll(mem.sliceAsBytes(out_dice.items), offset); - try lc_writer.writeStruct(macho.linkedit_data_command{ - .cmd = .DATA_IN_CODE, - .cmdsize = @sizeOf(macho.linkedit_data_command), - .dataoff = @intCast(u32, offset), - .datasize = @intCast(u32, needed_size), - }); - ncmds.* += 1; + + self.data_in_code_cmd.dataoff = @intCast(u32, offset); + self.data_in_code_cmd.datasize = @intCast(u32, needed_size); } - fn writeSymtabs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void { - var symtab_cmd = macho.symtab_command{ - .cmdsize = @sizeOf(macho.symtab_command), - .symoff = 0, - .nsyms = 0, - .stroff = 0, - .strsize = 0, - }; - var dysymtab_cmd = macho.dysymtab_command{ - .cmdsize = @sizeOf(macho.dysymtab_command), - .ilocalsym = 0, - .nlocalsym = 0, - .iextdefsym = 0, - .nextdefsym = 0, - .iundefsym = 0, - .nundefsym = 0, - .tocoff = 0, - .ntoc = 0, - .modtaboff = 0, - .nmodtab = 0, - .extrefsymoff = 0, - .nextrefsyms = 0, - .indirectsymoff = 0, - .nindirectsyms = 0, - .extreloff = 0, - .nextrel = 0, - .locreloff = 0, - .nlocrel = 0, - }; - var ctx = try self.writeSymtab(&symtab_cmd); + fn writeSymtabs(self: *Zld) !void { + var ctx = try self.writeSymtab(); defer ctx.imports_table.deinit(); - try self.writeDysymtab(ctx, &dysymtab_cmd); - try self.writeStrtab(&symtab_cmd); - try lc_writer.writeStruct(symtab_cmd); - try lc_writer.writeStruct(dysymtab_cmd); - ncmds.* += 2; + try self.writeDysymtab(ctx); + try self.writeStrtab(); } - fn writeSymtab(self: *Zld, lc: *macho.symtab_command) !SymtabCtx { + fn writeSymtab(self: *Zld) !SymtabCtx { const gpa = self.gpa; var locals = std.ArrayList(macho.nlist_64).init(gpa); @@ -2618,8 +2577,8 @@ pub const Zld = struct { log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); try self.file.pwriteAll(buffer.items, offset); - lc.symoff = @intCast(u32, offset); - lc.nsyms = nsyms; + self.symtab_cmd.symoff = @intCast(u32, offset); + self.symtab_cmd.nsyms = nsyms; return SymtabCtx{ .nlocalsym = nlocals, @@ -2629,7 +2588,7 @@ pub const Zld = struct { }; } - fn writeStrtab(self: *Zld, lc: *macho.symtab_command) !void { + fn writeStrtab(self: *Zld) !void { const seg = self.getLinkeditSegmentPtr(); const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)); const needed_size = self.strtab.buffer.items.len; @@ -2639,8 +2598,8 @@ pub const Zld = struct { try self.file.pwriteAll(self.strtab.buffer.items, offset); - lc.stroff = @intCast(u32, offset); - lc.strsize = @intCast(u32, needed_size); + self.symtab_cmd.stroff = @intCast(u32, offset); + self.symtab_cmd.strsize = @intCast(u32, needed_size); } const SymtabCtx = struct { @@ -2650,7 +2609,7 @@ pub const Zld = struct { imports_table: std.AutoHashMap(SymbolWithLoc, u32), }; - fn writeDysymtab(self: *Zld, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void { + fn writeDysymtab(self: *Zld, ctx: SymtabCtx) !void { const gpa = self.gpa; const nstubs = @intCast(u32, self.stubs.items.len); const ngot_entries = @intCast(u32, self.got_entries.items.len); @@ -2706,21 +2665,33 @@ pub const Zld = struct { assert(buf.items.len == needed_size); try self.file.pwriteAll(buf.items, offset); - lc.nlocalsym = ctx.nlocalsym; - lc.iextdefsym = iextdefsym; - lc.nextdefsym = ctx.nextdefsym; - lc.iundefsym = iundefsym; - lc.nundefsym = ctx.nundefsym; - lc.indirectsymoff = @intCast(u32, offset); - lc.nindirectsyms = nindirectsyms; + self.dysymtab_cmd.nlocalsym = ctx.nlocalsym; + self.dysymtab_cmd.iextdefsym = iextdefsym; + self.dysymtab_cmd.nextdefsym = ctx.nextdefsym; + self.dysymtab_cmd.iundefsym = iundefsym; + self.dysymtab_cmd.nundefsym = ctx.nundefsym; + self.dysymtab_cmd.indirectsymoff = @intCast(u32, offset); + self.dysymtab_cmd.nindirectsyms = nindirectsyms; } - fn writeCodeSignaturePadding( - self: *Zld, - code_sig: *CodeSignature, - ncmds: *u32, - lc_writer: anytype, - ) !u32 { + fn writeUuid(self: *Zld, comp: *const Compilation, offset: u32) !void { + switch (self.options.optimize_mode) { + .Debug => { + // In Debug we don't really care about reproducibility, so put in a random value + // and be done with it. + std.crypto.random.bytes(&self.uuid_cmd.uuid); + }, + else => { + const seg = self.getLinkeditSegmentPtr(); + const file_size = seg.fileoff + seg.filesize; + try uuid.calcUuidParallel(comp, self.file, file_size, &self.uuid_cmd.uuid); + }, + } + const in_file = @sizeOf(macho.mach_header_64) + offset + @sizeOf(macho.load_command); + try self.file.pwriteAll(&self.uuid_cmd.uuid, in_file); + } + + fn writeCodeSignaturePadding(self: *Zld, code_sig: *CodeSignature) !void { const seg = self.getLinkeditSegmentPtr(); // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 @@ -2733,23 +2704,11 @@ pub const Zld = struct { // except for code signature data. try self.file.pwriteAll(&[_]u8{0}, offset + needed_size - 1); - try lc_writer.writeStruct(macho.linkedit_data_command{ - .cmd = .CODE_SIGNATURE, - .cmdsize = @sizeOf(macho.linkedit_data_command), - .dataoff = @intCast(u32, offset), - .datasize = @intCast(u32, needed_size), - }); - ncmds.* += 1; - - return @intCast(u32, offset); + self.codesig_cmd.dataoff = @intCast(u32, offset); + self.codesig_cmd.datasize = @intCast(u32, needed_size); } - fn writeCodeSignature( - self: *Zld, - comp: *const Compilation, - code_sig: *CodeSignature, - offset: u32, - ) !void { + fn writeCodeSignature(self: *Zld, comp: *const Compilation, code_sig: *CodeSignature) !void { const seg_id = self.getSegmentByName("__TEXT").?; const seg = self.segments.items[seg_id]; @@ -2760,17 +2719,17 @@ pub const Zld = struct { .file = self.file, .exec_seg_base = seg.fileoff, .exec_seg_limit = seg.filesize, - .file_size = offset, + .file_size = self.codesig_cmd.dataoff, .output_mode = self.options.output_mode, }, buffer.writer()); assert(buffer.items.len == code_sig.size()); log.debug("writing code signature from 0x{x} to 0x{x}", .{ - offset, - offset + buffer.items.len, + self.codesig_cmd.dataoff, + self.codesig_cmd.dataoff + buffer.items.len, }); - try self.file.pwriteAll(buffer.items, offset); + try self.file.pwriteAll(buffer.items, self.codesig_cmd.dataoff); } /// Writes Mach-O file header. @@ -3986,13 +3945,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr } try zld.writeAtoms(reverse_lookups); - - var lc_buffer = std.ArrayList(u8).init(arena); - const lc_writer = lc_buffer.writer(); - - var ncmds: u32 = 0; - - try zld.writeLinkeditSegmentData(&ncmds, lc_writer, reverse_lookups); + try zld.writeLinkeditSegmentData(reverse_lookups); // If the last section of __DATA segment is zerofill section, we need to ensure // that the free space between the end of the last non-zerofill section of __DATA @@ -4017,47 +3970,48 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr } } - try load_commands.writeDylinkerLC(&ncmds, lc_writer); + // Write load commands + var lc_buffer = std.ArrayList(u8).init(arena); + const lc_writer = lc_buffer.writer(); + + try zld.writeSegmentHeaders(lc_writer); + try lc_writer.writeStruct(zld.dyld_info_cmd); + try lc_writer.writeStruct(zld.function_starts_cmd); + try lc_writer.writeStruct(zld.data_in_code_cmd); + try lc_writer.writeStruct(zld.symtab_cmd); + try lc_writer.writeStruct(zld.dysymtab_cmd); + try load_commands.writeDylinkerLC(lc_writer); if (zld.options.output_mode == .Exe) { const seg_id = zld.getSegmentByName("__TEXT").?; const seg = zld.segments.items[seg_id]; const global = zld.getEntryPoint(); const sym = zld.getSymbol(global); - try load_commands.writeMainLC(@intCast(u32, sym.n_value - seg.vmaddr), options, &ncmds, lc_writer); + try lc_writer.writeStruct(macho.entry_point_command{ + .entryoff = @intCast(u32, sym.n_value - seg.vmaddr), + .stacksize = options.stack_size_override orelse 0, + }); } else { assert(zld.options.output_mode == .Lib); - try load_commands.writeDylibIdLC(zld.gpa, zld.options, &ncmds, lc_writer); + try load_commands.writeDylibIdLC(zld.gpa, zld.options, lc_writer); } - try load_commands.writeRpathLCs(zld.gpa, zld.options, &ncmds, lc_writer); - try load_commands.writeSourceVersionLC(&ncmds, lc_writer); - try load_commands.writeBuildVersionLC(zld.options, &ncmds, lc_writer); - - // Looking forward into the future, we will want to offer `-no_uuid` support in which case - // there will be nothing to backpatch. - const uuid_offset_backpatch: ?usize = blk: { - const index = lc_buffer.items.len; - var uuid_buf: [16]u8 = [_]u8{0} ** 16; - - if (zld.options.optimize_mode == .Debug) { - // In Debug we don't really care about reproducibility, so put in a random value - // and be done with it. - std.crypto.random.bytes(&uuid_buf); - } + try load_commands.writeRpathLCs(zld.gpa, zld.options, lc_writer); + try lc_writer.writeStruct(macho.source_version_command{ + .version = 0, + }); + try load_commands.writeBuildVersionLC(zld.options, lc_writer); - try load_commands.writeUuidLC(&uuid_buf, &ncmds, lc_writer); - break :blk if (zld.options.optimize_mode == .Debug) null else index; - }; + const uuid_offset = @intCast(u32, lc_buffer.items.len); + try lc_writer.writeStruct(zld.uuid_cmd); - try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), &ncmds, lc_writer); + try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), lc_writer); const requires_codesig = blk: { if (options.entitlements) |_| break :blk true; if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true; break :blk false; }; - var codesig_offset: ?u32 = null; var codesig: ?CodeSignature = if (requires_codesig) blk: { // Preallocate space for the code signature. // We need to do this at this stage so that we have the load commands with proper values @@ -4069,29 +4023,20 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr if (options.entitlements) |path| { try codesig.addEntitlements(gpa, path); } - codesig_offset = try zld.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer); + try zld.writeCodeSignaturePadding(&codesig); + try lc_writer.writeStruct(zld.codesig_cmd); break :blk codesig; } else null; defer if (codesig) |*csig| csig.deinit(gpa); - var headers_buf = std.ArrayList(u8).init(arena); - try zld.writeSegmentHeaders(&ncmds, headers_buf.writer()); + const ncmds = load_commands.calcNumOfLCs(lc_buffer.items); + try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64)); + try zld.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len)); - try zld.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64)); - try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len); - try zld.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len)); - - if (uuid_offset_backpatch) |backpatch| { - const seg = zld.getLinkeditSegmentPtr(); - const file_size = seg.fileoff + seg.filesize; - var uuid_buf: [16]u8 = undefined; - try uuid.calcUuidParallel(comp, zld.file, file_size, &uuid_buf); - const offset = @sizeOf(macho.mach_header_64) + headers_buf.items.len + backpatch + @sizeOf(macho.load_command); - try zld.file.pwriteAll(&uuid_buf, offset); - } + try zld.writeUuid(comp, uuid_offset); if (codesig) |*csig| { - try zld.writeCodeSignature(comp, csig, codesig_offset.?); // code signing always comes last + try zld.writeCodeSignature(comp, csig); // code signing always comes last } } |
