authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-12-17 10:26:56+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-12-17 10:26:56+01:00
log270b6c4c2f28c26576422d0f8d334762e7efdba9
tree61a62224897c0dc5b8e8f66ad35be68865d93abb
parent68d2f68ed823984a59724e256e78c5654d55b088
parentb20a610f03b0c281958802770b927cde5f47b99c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13964 from ziglang/issue-11737

Misc MachO linker improvements and link-tests refactor

13 files changed, 897 insertions(+), 1010 deletions(-)

CMakeLists.txt+3
......@@ -585,10 +585,13 @@ set(ZIG_STAGE2_SOURCES
585585 "${CMAKE_SOURCE_DIR}/src/link/MachO/DwarfInfo.zig"
586586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
587587 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
588 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
588589 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
589590 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
590591 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
591592 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
593 "${CMAKE_SOURCE_DIR}/src/link/MachO/fat.zig"
594 "${CMAKE_SOURCE_DIR}/src/link/MachO/load_commands.zig"
592595 "${CMAKE_SOURCE_DIR}/src/link/MachO/thunks.zig"
593596 "${CMAKE_SOURCE_DIR}/src/link/MachO/zld.zig"
594597 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
ci/aarch64-macos.sh+16
......@@ -52,3 +52,19 @@ stage3-release/bin/zig build test docs \
5252
5353# Produce the experimental std lib documentation.
5454stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
55
56# Ensure that stage3 and stage4 are byte-for-byte identical.
57stage3-release/bin/zig build \
58 --prefix stage4-release \
59 -Denable-llvm \
60 -Dno-lib \
61 -Drelease \
62 -Dstrip \
63 -Dtarget=$TARGET \
64 -Duse-zig-libcxx \
65 -Dversion-string="$(stage3-release/bin/zig version)"
66
67# diff returns an error code if the files differ.
68echo "If the following command fails, it means nondeterminism has been"
69echo "introduced, making stage3 and stage4 no longer byte-for-byte identical."
70diff stage3-release/bin/zig stage4-release/bin/zig
ci/x86_64-macos.sh+16
......@@ -60,3 +60,19 @@ stage3-release/bin/zig build test docs \
6060
6161# Produce the experimental std lib documentation.
6262stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
63
64# Ensure that stage3 and stage4 are byte-for-byte identical.
65stage3-release/bin/zig build \
66 --prefix stage4-release \
67 -Denable-llvm \
68 -Dno-lib \
69 -Drelease \
70 -Dstrip \
71 -Dtarget=$TARGET \
72 -Duse-zig-libcxx \
73 -Dversion-string="$(stage3-release/bin/zig version)"
74
75# diff returns an error code if the files differ.
76echo "If the following command fails, it means nondeterminism has been"
77echo "introduced, making stage3 and stage4 no longer byte-for-byte identical."
78diff stage3-release/bin/zig stage4-release/bin/zig
lib/std/build/CheckObjectStep.zig+6
......@@ -571,6 +571,12 @@ const MachODumper = struct {
571571 });
572572 },
573573
574 .UUID => {
575 const uuid = lc.cast(macho.uuid_command).?;
576 try writer.writeByte('\n');
577 try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
578 },
579
574580 else => {},
575581 }
576582 }
lib/std/macho.zig+46-46
......@@ -58,10 +58,10 @@ pub const uuid_command = extern struct {
5858 cmd: LC = .UUID,
5959
6060 /// sizeof(struct uuid_command)
61 cmdsize: u32,
61 cmdsize: u32 = @sizeOf(uuid_command),
6262
6363 /// the 128-bit uuid
64 uuid: [16]u8,
64 uuid: [16]u8 = undefined,
6565};
6666
6767/// The version_min_command contains the min OS version on which this
......@@ -71,7 +71,7 @@ pub const version_min_command = extern struct {
7171 cmd: LC,
7272
7373 /// sizeof(struct version_min_command)
74 cmdsize: u32,
74 cmdsize: u32 = @sizeOf(version_min_command),
7575
7676 /// X.Y.Z is encoded in nibbles xxxx.yy.zz
7777 version: u32,
......@@ -87,7 +87,7 @@ pub const source_version_command = extern struct {
8787 cmd: LC = .SOURCE_VERSION,
8888
8989 /// sizeof(source_version_command)
90 cmdsize: u32,
90 cmdsize: u32 = @sizeOf(source_version_command),
9191
9292 /// A.B.C.D.E packed as a24.b10.c10.d10.e10
9393 version: u64,
......@@ -155,13 +155,13 @@ pub const entry_point_command = extern struct {
155155 cmd: LC = .MAIN,
156156
157157 /// sizeof(struct entry_point_command)
158 cmdsize: u32,
158 cmdsize: u32 = @sizeOf(entry_point_command),
159159
160160 /// file (__TEXT) offset of main()
161 entryoff: u64,
161 entryoff: u64 = 0,
162162
163163 /// if not zero, initial stack size
164 stacksize: u64,
164 stacksize: u64 = 0,
165165};
166166
167167/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
......@@ -172,19 +172,19 @@ pub const symtab_command = extern struct {
172172 cmd: LC = .SYMTAB,
173173
174174 /// sizeof(struct symtab_command)
175 cmdsize: u32,
175 cmdsize: u32 = @sizeOf(symtab_command),
176176
177177 /// symbol table offset
178 symoff: u32,
178 symoff: u32 = 0,
179179
180180 /// number of symbol table entries
181 nsyms: u32,
181 nsyms: u32 = 0,
182182
183183 /// string table offset
184 stroff: u32,
184 stroff: u32 = 0,
185185
186186 /// string table size in bytes
187 strsize: u32,
187 strsize: u32 = 0,
188188};
189189
190190/// This is the second set of the symbolic information which is used to support
......@@ -230,7 +230,7 @@ pub const dysymtab_command = extern struct {
230230 cmd: LC = .DYSYMTAB,
231231
232232 /// sizeof(struct dysymtab_command)
233 cmdsize: u32,
233 cmdsize: u32 = @sizeOf(dysymtab_command),
234234
235235 // The symbols indicated by symoff and nsyms of the LC_SYMTAB load command
236236 // are grouped into the following three groups:
......@@ -247,22 +247,22 @@ pub const dysymtab_command = extern struct {
247247 // table when this is a dynamically linked shared library file).
248248
249249 /// index of local symbols
250 ilocalsym: u32,
250 ilocalsym: u32 = 0,
251251
252252 /// number of local symbols
253 nlocalsym: u32,
253 nlocalsym: u32 = 0,
254254
255255 /// index to externally defined symbols
256 iextdefsym: u32,
256 iextdefsym: u32 = 0,
257257
258258 /// number of externally defined symbols
259 nextdefsym: u32,
259 nextdefsym: u32 = 0,
260260
261261 /// index to undefined symbols
262 iundefsym: u32,
262 iundefsym: u32 = 0,
263263
264264 /// number of undefined symbols
265 nundefsym: u32,
265 nundefsym: u32 = 0,
266266
267267 // For the for the dynamic binding process to find which module a symbol
268268 // is defined in the table of contents is used (analogous to the ranlib
......@@ -272,10 +272,10 @@ pub const dysymtab_command = extern struct {
272272 // symbols are sorted by name and is use as the table of contents.
273273
274274 /// file offset to table of contents
275 tocoff: u32,
275 tocoff: u32 = 0,
276276
277277 /// number of entries in table of contents
278 ntoc: u32,
278 ntoc: u32 = 0,
279279
280280 // To support dynamic binding of "modules" (whole object files) the symbol
281281 // table must reflect the modules that the file was created from. This is
......@@ -286,10 +286,10 @@ pub const dysymtab_command = extern struct {
286286 // contains one module so everything in the file belongs to the module.
287287
288288 /// file offset to module table
289 modtaboff: u32,
289 modtaboff: u32 = 0,
290290
291291 /// number of module table entries
292 nmodtab: u32,
292 nmodtab: u32 = 0,
293293
294294 // To support dynamic module binding the module structure for each module
295295 // indicates the external references (defined and undefined) each module
......@@ -300,10 +300,10 @@ pub const dysymtab_command = extern struct {
300300 // undefined external symbols indicates the external references.
301301
302302 /// offset to referenced symbol table
303 extrefsymoff: u32,
303 extrefsymoff: u32 = 0,
304304
305305 /// number of referenced symbol table entries
306 nextrefsyms: u32,
306 nextrefsyms: u32 = 0,
307307
308308 // The sections that contain "symbol pointers" and "routine stubs" have
309309 // indexes and (implied counts based on the size of the section and fixed
......@@ -315,10 +315,10 @@ pub const dysymtab_command = extern struct {
315315 // The indirect symbol table is ordered to match the entries in the section.
316316
317317 /// file offset to the indirect symbol table
318 indirectsymoff: u32,
318 indirectsymoff: u32 = 0,
319319
320320 /// number of indirect symbol table entries
321 nindirectsyms: u32,
321 nindirectsyms: u32 = 0,
322322
323323 // To support relocating an individual module in a library file quickly the
324324 // external relocation entries for each module in the library need to be
......@@ -347,20 +347,20 @@ pub const dysymtab_command = extern struct {
347347 // remaining relocation entries must be local).
348348
349349 /// offset to external relocation entries
350 extreloff: u32,
350 extreloff: u32 = 0,
351351
352352 /// number of external relocation entries
353 nextrel: u32,
353 nextrel: u32 = 0,
354354
355355 // All the local relocation entries are grouped together (they are not
356356 // grouped by their module since they are only used if the object is moved
357357 // from it staticly link edited address).
358358
359359 /// offset to local relocation entries
360 locreloff: u32,
360 locreloff: u32 = 0,
361361
362362 /// number of local relocation entries
363 nlocrel: u32,
363 nlocrel: u32 = 0,
364364};
365365
366366/// The linkedit_data_command contains the offsets and sizes of a blob
......@@ -370,13 +370,13 @@ pub const linkedit_data_command = extern struct {
370370 cmd: LC,
371371
372372 /// sizeof(struct linkedit_data_command)
373 cmdsize: u32,
373 cmdsize: u32 = @sizeOf(linkedit_data_command),
374374
375375 /// file offset of data in __LINKEDIT segment
376 dataoff: u32,
376 dataoff: u32 = 0,
377377
378378 /// file size of data in __LINKEDIT segment
379 datasize: u32,
379 datasize: u32 = 0,
380380};
381381
382382/// The dyld_info_command contains the file offsets and sizes of
......@@ -387,10 +387,10 @@ pub const linkedit_data_command = extern struct {
387387/// to interpret it.
388388pub const dyld_info_command = extern struct {
389389 /// LC_DYLD_INFO or LC_DYLD_INFO_ONLY
390 cmd: LC,
390 cmd: LC = .DYLD_INFO_ONLY,
391391
392392 /// sizeof(struct dyld_info_command)
393 cmdsize: u32,
393 cmdsize: u32 = @sizeOf(dyld_info_command),
394394
395395 // Dyld rebases an image whenever dyld loads it at an address different
396396 // from its preferred address. The rebase information is a stream
......@@ -403,10 +403,10 @@ pub const dyld_info_command = extern struct {
403403 // bytes.
404404
405405 /// file offset to rebase info
406 rebase_off: u32,
406 rebase_off: u32 = 0,
407407
408408 /// size of rebase info
409 rebase_size: u32,
409 rebase_size: u32 = 0,
410410
411411 // Dyld binds an image during the loading process, if the image
412412 // requires any pointers to be initialized to symbols in other images.
......@@ -420,10 +420,10 @@ pub const dyld_info_command = extern struct {
420420 // encoded in a few bytes.
421421
422422 /// file offset to binding info
423 bind_off: u32,
423 bind_off: u32 = 0,
424424
425425 /// size of binding info
426 bind_size: u32,
426 bind_size: u32 = 0,
427427
428428 // Some C++ programs require dyld to unique symbols so that all
429429 // images in the process use the same copy of some code/data.
......@@ -440,10 +440,10 @@ pub const dyld_info_command = extern struct {
440440 // and the call to operator new is then rebound.
441441
442442 /// file offset to weak binding info
443 weak_bind_off: u32,
443 weak_bind_off: u32 = 0,
444444
445445 /// size of weak binding info
446 weak_bind_size: u32,
446 weak_bind_size: u32 = 0,
447447
448448 // Some uses of external symbols do not need to be bound immediately.
449449 // Instead they can be lazily bound on first use. The lazy_bind
......@@ -457,10 +457,10 @@ pub const dyld_info_command = extern struct {
457457 // to bind.
458458
459459 /// file offset to lazy binding info
460 lazy_bind_off: u32,
460 lazy_bind_off: u32 = 0,
461461
462462 /// size of lazy binding info
463 lazy_bind_size: u32,
463 lazy_bind_size: u32 = 0,
464464
465465 // The symbols exported by a dylib are encoded in a trie. This
466466 // is a compact representation that factors out common prefixes.
......@@ -494,10 +494,10 @@ pub const dyld_info_command = extern struct {
494494 // edge points to.
495495
496496 /// file offset to lazy binding info
497 export_off: u32,
497 export_off: u32 = 0,
498498
499499 /// size of lazy binding info
500 export_size: u32,
500 export_size: u32 = 0,
501501};
502502
503503/// A program that uses a dynamic linker contains a dylinker_command to identify
src/link/MachO.zig+94-424
......@@ -20,6 +20,7 @@ const dead_strip = @import("MachO/dead_strip.zig");
2020const fat = @import("MachO/fat.zig");
2121const link = @import("../link.zig");
2222const llvm_backend = @import("../codegen/llvm.zig");
23const load_commands = @import("MachO/load_commands.zig");
2324const target_util = @import("../target.zig");
2425const trace = @import("../tracy.zig").trace;
2526const zld = @import("MachO/zld.zig");
......@@ -38,6 +39,7 @@ const Object = @import("MachO/Object.zig");
3839const LibStub = @import("tapi.zig").LibStub;
3940const Liveness = @import("../Liveness.zig");
4041const LlvmObject = @import("../codegen/llvm.zig").Object;
42const Md5 = std.crypto.hash.Md5;
4143const Module = @import("../Module.zig");
4244const Relocation = @import("MachO/Relocation.zig");
4345const StringTable = @import("strtab.zig").StringTable;
......@@ -98,10 +100,11 @@ page_size: u16,
98100/// fashion (default for LLVM backend).
99101mode: enum { incremental, one_shot },
100102
101uuid: macho.uuid_command = .{
102 .cmdsize = @sizeOf(macho.uuid_command),
103 .uuid = undefined,
104},
103dyld_info_cmd: macho.dyld_info_command = .{},
104symtab_cmd: macho.symtab_command = .{},
105dysymtab_cmd: macho.dysymtab_command = .{},
106uuid_cmd: macho.uuid_command = .{},
107codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
105108
106109dylibs: std.ArrayListUnmanaged(Dylib) = .{},
107110dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
......@@ -265,9 +268,6 @@ pub const SymbolWithLoc = struct {
265268/// actual_capacity + (actual_capacity / ideal_factor)
266269const ideal_factor = 3;
267270
268/// Default path to dyld
269pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
270
271271/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
272272/// it as a possible place to put new symbols, it must have enough room for this many bytes
273273/// (plus extra for reserved capacity).
......@@ -556,40 +556,55 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
556556 self.logAtoms();
557557 }
558558
559 try self.writeLinkeditSegmentData();
560
561 // Write load commands
559562 var lc_buffer = std.ArrayList(u8).init(arena);
560563 const lc_writer = lc_buffer.writer();
561 var ncmds: u32 = 0;
562564
563 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
564 try writeDylinkerLC(&ncmds, lc_writer);
565 try self.writeSegmentHeaders(lc_writer);
566 try lc_writer.writeStruct(self.dyld_info_cmd);
567 try lc_writer.writeStruct(self.symtab_cmd);
568 try lc_writer.writeStruct(self.dysymtab_cmd);
569 try load_commands.writeDylinkerLC(lc_writer);
565570
566 self.writeMainLC(&ncmds, lc_writer) catch |err| switch (err) {
567 error.MissingMainEntrypoint => {
568 self.error_flags.no_entry_point_found = true;
571 switch (self.base.options.output_mode) {
572 .Exe => blk: {
573 const seg_id = self.header_segment_cmd_index.?;
574 const seg = self.segments.items[seg_id];
575 const global = self.getEntryPoint() catch |err| switch (err) {
576 error.MissingMainEntrypoint => {
577 self.error_flags.no_entry_point_found = true;
578 break :blk;
579 },
580 else => |e| return e,
581 };
582 const sym = self.getSymbol(global);
583 try lc_writer.writeStruct(macho.entry_point_command{
584 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
585 .stacksize = self.base.options.stack_size_override orelse 0,
586 });
569587 },
570 else => |e| return e,
571 };
572
573 try self.writeDylibIdLC(&ncmds, lc_writer);
574 try self.writeRpathLCs(&ncmds, lc_writer);
575
576 {
577 try lc_writer.writeStruct(macho.source_version_command{
578 .cmdsize = @sizeOf(macho.source_version_command),
579 .version = 0x0,
580 });
581 ncmds += 1;
588 .Lib => if (self.base.options.link_mode == .Dynamic) {
589 try load_commands.writeDylibIdLC(self.base.allocator, &self.base.options, lc_writer);
590 },
591 else => {},
582592 }
583593
584 try self.writeBuildVersionLC(&ncmds, lc_writer);
594 try load_commands.writeRpathLCs(self.base.allocator, &self.base.options, lc_writer);
595 try lc_writer.writeStruct(macho.source_version_command{
596 .version = 0,
597 });
598 try load_commands.writeBuildVersionLC(&self.base.options, lc_writer);
585599
586 {
587 std.crypto.random.bytes(&self.uuid.uuid);
588 try lc_writer.writeStruct(self.uuid);
589 ncmds += 1;
600 if (self.cold_start) {
601 std.crypto.random.bytes(&self.uuid_cmd.uuid);
602 Md5.hash(&self.uuid_cmd.uuid, &self.uuid_cmd.uuid, .{});
603 conformUuid(&self.uuid_cmd.uuid);
590604 }
605 try lc_writer.writeStruct(self.uuid_cmd);
591606
592 try self.writeLoadDylibLCs(&ncmds, lc_writer);
607 try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer);
593608
594609 const target = self.base.options.target;
595610 const requires_codesig = blk: {
......@@ -598,7 +613,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
598613 break :blk true;
599614 break :blk false;
600615 };
601 var codesig_offset: ?u32 = null;
602616 var codesig: ?CodeSignature = if (requires_codesig) blk: {
603617 // Preallocate space for the code signature.
604618 // We need to do this at this stage so that we have the load commands with proper values
......@@ -610,20 +624,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
610624 if (self.base.options.entitlements) |path| {
611625 try codesig.addEntitlements(arena, path);
612626 }
613 codesig_offset = try self.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
627 try self.writeCodeSignaturePadding(&codesig);
628 try lc_writer.writeStruct(self.codesig_cmd);
614629 break :blk codesig;
615630 } else null;
616631
617 var headers_buf = std.ArrayList(u8).init(arena);
618 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
619
620 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
621 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
632 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
622633
623 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
634 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
635 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len));
624636
625637 if (codesig) |*csig| {
626 try self.writeCodeSignature(comp, csig, codesig_offset.?); // code signing always comes last
638 try self.writeCodeSignature(comp, csig); // code signing always comes last
627639 }
628640
629641 if (self.d_sym) |*d_sym| {
......@@ -653,6 +665,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
653665
654666 self.cold_start = false;
655667}
668inline fn conformUuid(out: *[Md5.digest_length]u8) void {
669 // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
670 out[6] = (out[6] & 0x0F) | (3 << 4);
671 out[8] = (out[8] & 0x3F) | 0x80;
672}
656673
657674pub fn resolveLibSystem(
658675 arena: Allocator,
......@@ -1702,195 +1719,6 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
17021719 try self.writePtrWidthAtom(got_atom);
17031720}
17041721
1705pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
1706 const name_len = mem.sliceTo(default_dyld_path, 0).len;
1707 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1708 u64,
1709 @sizeOf(macho.dylinker_command) + name_len,
1710 @sizeOf(u64),
1711 ));
1712 try lc_writer.writeStruct(macho.dylinker_command{
1713 .cmd = .LOAD_DYLINKER,
1714 .cmdsize = cmdsize,
1715 .name = @sizeOf(macho.dylinker_command),
1716 });
1717 try lc_writer.writeAll(mem.sliceTo(default_dyld_path, 0));
1718 const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
1719 if (padding > 0) {
1720 try lc_writer.writeByteNTimes(0, padding);
1721 }
1722 ncmds.* += 1;
1723}
1724
1725pub fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1726 if (self.base.options.output_mode != .Exe) return;
1727 const seg_id = self.header_segment_cmd_index.?;
1728 const seg = self.segments.items[seg_id];
1729 const global = try self.getEntryPoint();
1730 const sym = self.getSymbol(global);
1731 try lc_writer.writeStruct(macho.entry_point_command{
1732 .cmd = .MAIN,
1733 .cmdsize = @sizeOf(macho.entry_point_command),
1734 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
1735 .stacksize = self.base.options.stack_size_override orelse 0,
1736 });
1737 ncmds.* += 1;
1738}
1739
1740const WriteDylibLCCtx = struct {
1741 cmd: macho.LC,
1742 name: []const u8,
1743 timestamp: u32 = 2,
1744 current_version: u32 = 0x10000,
1745 compatibility_version: u32 = 0x10000,
1746};
1747
1748pub fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
1749 const name_len = ctx.name.len + 1;
1750 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1751 u64,
1752 @sizeOf(macho.dylib_command) + name_len,
1753 @sizeOf(u64),
1754 ));
1755 try lc_writer.writeStruct(macho.dylib_command{
1756 .cmd = ctx.cmd,
1757 .cmdsize = cmdsize,
1758 .dylib = .{
1759 .name = @sizeOf(macho.dylib_command),
1760 .timestamp = ctx.timestamp,
1761 .current_version = ctx.current_version,
1762 .compatibility_version = ctx.compatibility_version,
1763 },
1764 });
1765 try lc_writer.writeAll(ctx.name);
1766 try lc_writer.writeByte(0);
1767 const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
1768 if (padding > 0) {
1769 try lc_writer.writeByteNTimes(0, padding);
1770 }
1771 ncmds.* += 1;
1772}
1773
1774pub fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1775 if (self.base.options.output_mode != .Lib) return;
1776 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
1777 const curr = self.base.options.version orelse std.builtin.Version{
1778 .major = 1,
1779 .minor = 0,
1780 .patch = 0,
1781 };
1782 const compat = self.base.options.compatibility_version orelse std.builtin.Version{
1783 .major = 1,
1784 .minor = 0,
1785 .patch = 0,
1786 };
1787 try writeDylibLC(.{
1788 .cmd = .ID_DYLIB,
1789 .name = install_name,
1790 .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
1791 .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
1792 }, ncmds, lc_writer);
1793}
1794
1795const RpathIterator = struct {
1796 buffer: []const []const u8,
1797 table: std.StringHashMap(void),
1798 count: usize = 0,
1799
1800 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
1801 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
1802 }
1803
1804 fn deinit(it: *RpathIterator) void {
1805 it.table.deinit();
1806 }
1807
1808 fn next(it: *RpathIterator) !?[]const u8 {
1809 while (true) {
1810 if (it.count >= it.buffer.len) return null;
1811 const rpath = it.buffer[it.count];
1812 it.count += 1;
1813 const gop = try it.table.getOrPut(rpath);
1814 if (gop.found_existing) continue;
1815 return rpath;
1816 }
1817 }
1818};
1819
1820pub fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1821 const gpa = self.base.allocator;
1822
1823 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
1824 defer it.deinit();
1825
1826 while (try it.next()) |rpath| {
1827 const rpath_len = rpath.len + 1;
1828 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1829 u64,
1830 @sizeOf(macho.rpath_command) + rpath_len,
1831 @sizeOf(u64),
1832 ));
1833 try lc_writer.writeStruct(macho.rpath_command{
1834 .cmdsize = cmdsize,
1835 .path = @sizeOf(macho.rpath_command),
1836 });
1837 try lc_writer.writeAll(rpath);
1838 try lc_writer.writeByte(0);
1839 const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
1840 if (padding > 0) {
1841 try lc_writer.writeByteNTimes(0, padding);
1842 }
1843 ncmds.* += 1;
1844 }
1845}
1846
1847pub fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1848 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
1849 const platform_version = blk: {
1850 const ver = self.base.options.target.os.version_range.semver.min;
1851 const platform_version = ver.major << 16 | ver.minor << 8;
1852 break :blk platform_version;
1853 };
1854 const sdk_version = if (self.base.options.native_darwin_sdk) |sdk| blk: {
1855 const ver = sdk.version;
1856 const sdk_version = ver.major << 16 | ver.minor << 8;
1857 break :blk sdk_version;
1858 } else platform_version;
1859 const is_simulator_abi = self.base.options.target.abi == .simulator;
1860 try lc_writer.writeStruct(macho.build_version_command{
1861 .cmdsize = cmdsize,
1862 .platform = switch (self.base.options.target.os.tag) {
1863 .macos => .MACOS,
1864 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
1865 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
1866 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
1867 else => unreachable,
1868 },
1869 .minos = platform_version,
1870 .sdk = sdk_version,
1871 .ntools = 1,
1872 });
1873 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
1874 .tool = .LD,
1875 .version = 0x0,
1876 }));
1877 ncmds.* += 1;
1878}
1879
1880pub fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1881 for (self.referenced_dylibs.keys()) |id| {
1882 const dylib = self.dylibs.items[id];
1883 const dylib_id = dylib.id orelse unreachable;
1884 try writeDylibLC(.{
1885 .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
1886 .name = dylib_id.name,
1887 .timestamp = dylib_id.timestamp,
1888 .current_version = dylib_id.current_version,
1889 .compatibility_version = dylib_id.compatibility_version,
1890 }, ncmds, lc_writer);
1891 }
1892}
1893
18941722pub fn deinit(self: *MachO) void {
18951723 const gpa = self.base.allocator;
18961724
......@@ -2976,98 +2804,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
29762804 }
29772805}
29782806
2979pub inline fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
2980 const darwin_path_max = 1024;
2981 const name_len = if (assume_max_path_len) darwin_path_max else std.mem.len(name) + 1;
2982 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
2983}
2984
2985fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {
2986 const gpa = self.base.allocator;
2987 var sizeofcmds: u64 = 0;
2988 for (self.segments.items) |seg| {
2989 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
2990 }
2991
2992 // LC_DYLD_INFO_ONLY
2993 sizeofcmds += @sizeOf(macho.dyld_info_command);
2994 // LC_FUNCTION_STARTS
2995 if (self.text_section_index != null) {
2996 sizeofcmds += @sizeOf(macho.linkedit_data_command);
2997 }
2998 // LC_DATA_IN_CODE
2999 sizeofcmds += @sizeOf(macho.linkedit_data_command);
3000 // LC_SYMTAB
3001 sizeofcmds += @sizeOf(macho.symtab_command);
3002 // LC_DYSYMTAB
3003 sizeofcmds += @sizeOf(macho.dysymtab_command);
3004 // LC_LOAD_DYLINKER
3005 sizeofcmds += calcInstallNameLen(
3006 @sizeOf(macho.dylinker_command),
3007 mem.sliceTo(default_dyld_path, 0),
3008 false,
3009 );
3010 // LC_MAIN
3011 if (self.base.options.output_mode == .Exe) {
3012 sizeofcmds += @sizeOf(macho.entry_point_command);
3013 }
3014 // LC_ID_DYLIB
3015 if (self.base.options.output_mode == .Lib) {
3016 sizeofcmds += blk: {
3017 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
3018 break :blk calcInstallNameLen(
3019 @sizeOf(macho.dylib_command),
3020 install_name,
3021 assume_max_path_len,
3022 );
3023 };
3024 }
3025 // LC_RPATH
3026 {
3027 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
3028 defer it.deinit();
3029 while (try it.next()) |rpath| {
3030 sizeofcmds += calcInstallNameLen(
3031 @sizeOf(macho.rpath_command),
3032 rpath,
3033 assume_max_path_len,
3034 );
3035 }
3036 }
3037 // LC_SOURCE_VERSION
3038 sizeofcmds += @sizeOf(macho.source_version_command);
3039 // LC_BUILD_VERSION
3040 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
3041 // LC_UUID
3042 sizeofcmds += @sizeOf(macho.uuid_command);
3043 // LC_LOAD_DYLIB
3044 for (self.referenced_dylibs.keys()) |id| {
3045 const dylib = self.dylibs.items[id];
3046 const dylib_id = dylib.id orelse unreachable;
3047 sizeofcmds += calcInstallNameLen(
3048 @sizeOf(macho.dylib_command),
3049 dylib_id.name,
3050 assume_max_path_len,
3051 );
3052 }
3053 // LC_CODE_SIGNATURE
3054 {
3055 const target = self.base.options.target;
3056 const requires_codesig = blk: {
3057 if (self.base.options.entitlements) |_| break :blk true;
3058 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
3059 break :blk true;
3060 break :blk false;
3061 };
3062 if (requires_codesig) {
3063 sizeofcmds += @sizeOf(macho.linkedit_data_command);
3064 }
3065 }
3066
3067 return @intCast(u32, sizeofcmds);
3068}
3069
3070pub fn calcPagezeroSize(self: *MachO) u64 {
2807fn calcPagezeroSize(self: *MachO) u64 {
30712808 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
30722809 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
30732810 if (self.base.options.output_mode == .Lib) return 0;
......@@ -3079,23 +2816,6 @@ pub fn calcPagezeroSize(self: *MachO) u64 {
30792816 return aligned_pagezero_vmsize;
30802817}
30812818
3082pub fn calcMinHeaderPad(self: *MachO) !u64 {
3083 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 0);
3084 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
3085
3086 if (self.base.options.headerpad_max_install_names) {
3087 var min_headerpad_size: u32 = try self.calcLCsSize(true);
3088 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
3089 min_headerpad_size + @sizeOf(macho.mach_header_64),
3090 });
3091 padding = @max(padding, min_headerpad_size);
3092 }
3093 const offset = @sizeOf(macho.mach_header_64) + padding;
3094 log.debug("actual headerpad size 0x{x}", .{offset});
3095
3096 return offset;
3097}
3098
30992819fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {
31002820 size: u64 = 0,
31012821 alignment: u32 = 0,
......@@ -3433,18 +3153,17 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
34333153 return global_index;
34343154}
34353155
3436fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
3156fn writeSegmentHeaders(self: *MachO, writer: anytype) !void {
34373157 for (self.segments.items) |seg, i| {
34383158 const indexes = self.getSectionIndexes(@intCast(u8, i));
34393159 try writer.writeStruct(seg);
34403160 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
34413161 try writer.writeStruct(header);
34423162 }
3443 ncmds.* += 1;
34443163 }
34453164}
34463165
3447fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3166fn writeLinkeditSegmentData(self: *MachO) !void {
34483167 const seg = self.getLinkeditSegmentPtr();
34493168 seg.filesize = 0;
34503169 seg.vmsize = 0;
......@@ -3459,8 +3178,8 @@ fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void
34593178 }
34603179 }
34613180
3462 try self.writeDyldInfoData(ncmds, lc_writer);
3463 try self.writeSymtabs(ncmds, lc_writer);
3181 try self.writeDyldInfoData();
3182 try self.writeSymtabs();
34643183
34653184 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
34663185}
......@@ -3612,7 +3331,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {
36123331 try trie.finalize(gpa);
36133332}
36143333
3615fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3334fn writeDyldInfoData(self: *MachO) !void {
36163335 const tracy = trace(@src());
36173336 defer tracy.end();
36183337
......@@ -3683,21 +3402,14 @@ fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
36833402 const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow);
36843403 try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]);
36853404
3686 try lc_writer.writeStruct(macho.dyld_info_command{
3687 .cmd = .DYLD_INFO_ONLY,
3688 .cmdsize = @sizeOf(macho.dyld_info_command),
3689 .rebase_off = @intCast(u32, rebase_off),
3690 .rebase_size = @intCast(u32, rebase_size),
3691 .bind_off = @intCast(u32, bind_off),
3692 .bind_size = @intCast(u32, bind_size),
3693 .weak_bind_off = 0,
3694 .weak_bind_size = 0,
3695 .lazy_bind_off = @intCast(u32, lazy_bind_off),
3696 .lazy_bind_size = @intCast(u32, lazy_bind_size),
3697 .export_off = @intCast(u32, export_off),
3698 .export_size = @intCast(u32, export_size),
3699 });
3700 ncmds.* += 1;
3405 self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off);
3406 self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size);
3407 self.dyld_info_cmd.bind_off = @intCast(u32, bind_off);
3408 self.dyld_info_cmd.bind_size = @intCast(u32, bind_size);
3409 self.dyld_info_cmd.lazy_bind_off = @intCast(u32, lazy_bind_off);
3410 self.dyld_info_cmd.lazy_bind_size = @intCast(u32, lazy_bind_size);
3411 self.dyld_info_cmd.export_off = @intCast(u32, export_off);
3412 self.dyld_info_cmd.export_size = @intCast(u32, export_size);
37013413}
37023414
37033415fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
......@@ -3799,45 +3511,14 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
37993511 }
38003512}
38013513
3802fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3803 var symtab_cmd = macho.symtab_command{
3804 .cmdsize = @sizeOf(macho.symtab_command),
3805 .symoff = 0,
3806 .nsyms = 0,
3807 .stroff = 0,
3808 .strsize = 0,
3809 };
3810 var dysymtab_cmd = macho.dysymtab_command{
3811 .cmdsize = @sizeOf(macho.dysymtab_command),
3812 .ilocalsym = 0,
3813 .nlocalsym = 0,
3814 .iextdefsym = 0,
3815 .nextdefsym = 0,
3816 .iundefsym = 0,
3817 .nundefsym = 0,
3818 .tocoff = 0,
3819 .ntoc = 0,
3820 .modtaboff = 0,
3821 .nmodtab = 0,
3822 .extrefsymoff = 0,
3823 .nextrefsyms = 0,
3824 .indirectsymoff = 0,
3825 .nindirectsyms = 0,
3826 .extreloff = 0,
3827 .nextrel = 0,
3828 .locreloff = 0,
3829 .nlocrel = 0,
3830 };
3831 var ctx = try self.writeSymtab(&symtab_cmd);
3514fn writeSymtabs(self: *MachO) !void {
3515 var ctx = try self.writeSymtab();
38323516 defer ctx.imports_table.deinit();
3833 try self.writeDysymtab(ctx, &dysymtab_cmd);
3834 try self.writeStrtab(&symtab_cmd);
3835 try lc_writer.writeStruct(symtab_cmd);
3836 try lc_writer.writeStruct(dysymtab_cmd);
3837 ncmds.* += 2;
3517 try self.writeDysymtab(ctx);
3518 try self.writeStrtab();
38383519}
38393520
3840fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
3521fn writeSymtab(self: *MachO) !SymtabCtx {
38413522 const gpa = self.base.allocator;
38423523
38433524 var locals = std.ArrayList(macho.nlist_64).init(gpa);
......@@ -3902,8 +3583,8 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
39023583 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
39033584 try self.base.file.?.pwriteAll(buffer.items, offset);
39043585
3905 lc.symoff = @intCast(u32, offset);
3906 lc.nsyms = nsyms;
3586 self.symtab_cmd.symoff = @intCast(u32, offset);
3587 self.symtab_cmd.nsyms = nsyms;
39073588
39083589 return SymtabCtx{
39093590 .nlocalsym = nlocals,
......@@ -3913,7 +3594,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
39133594 };
39143595}
39153596
3916fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void {
3597fn writeStrtab(self: *MachO) !void {
39173598 const seg = self.getLinkeditSegmentPtr();
39183599 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
39193600 const needed_size = self.strtab.buffer.items.len;
......@@ -3923,8 +3604,8 @@ fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void {
39233604
39243605 try self.base.file.?.pwriteAll(self.strtab.buffer.items, offset);
39253606
3926 lc.stroff = @intCast(u32, offset);
3927 lc.strsize = @intCast(u32, needed_size);
3607 self.symtab_cmd.stroff = @intCast(u32, offset);
3608 self.symtab_cmd.strsize = @intCast(u32, needed_size);
39283609}
39293610
39303611const SymtabCtx = struct {
......@@ -3934,7 +3615,7 @@ const SymtabCtx = struct {
39343615 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
39353616};
39363617
3937fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {
3618fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
39383619 const gpa = self.base.allocator;
39393620 const nstubs = @intCast(u32, self.stubs_table.count());
39403621 const ngot_entries = @intCast(u32, self.got_entries_table.count());
......@@ -3993,21 +3674,16 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi
39933674 assert(buf.items.len == needed_size);
39943675 try self.base.file.?.pwriteAll(buf.items, offset);
39953676
3996 lc.nlocalsym = ctx.nlocalsym;
3997 lc.iextdefsym = iextdefsym;
3998 lc.nextdefsym = ctx.nextdefsym;
3999 lc.iundefsym = iundefsym;
4000 lc.nundefsym = ctx.nundefsym;
4001 lc.indirectsymoff = @intCast(u32, offset);
4002 lc.nindirectsyms = nindirectsyms;
3677 self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;
3678 self.dysymtab_cmd.iextdefsym = iextdefsym;
3679 self.dysymtab_cmd.nextdefsym = ctx.nextdefsym;
3680 self.dysymtab_cmd.iundefsym = iundefsym;
3681 self.dysymtab_cmd.nundefsym = ctx.nundefsym;
3682 self.dysymtab_cmd.indirectsymoff = @intCast(u32, offset);
3683 self.dysymtab_cmd.nindirectsyms = nindirectsyms;
40033684}
40043685
4005fn writeCodeSignaturePadding(
4006 self: *MachO,
4007 code_sig: *CodeSignature,
4008 ncmds: *u32,
4009 lc_writer: anytype,
4010) !u32 {
3686fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
40113687 const seg = self.getLinkeditSegmentPtr();
40123688 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
40133689 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
......@@ -4020,19 +3696,13 @@ fn writeCodeSignaturePadding(
40203696 // except for code signature data.
40213697 try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
40223698
4023 try lc_writer.writeStruct(macho.linkedit_data_command{
4024 .cmd = .CODE_SIGNATURE,
4025 .cmdsize = @sizeOf(macho.linkedit_data_command),
4026 .dataoff = @intCast(u32, offset),
4027 .datasize = @intCast(u32, needed_size),
4028 });
4029 ncmds.* += 1;
4030
4031 return @intCast(u32, offset);
3699 self.codesig_cmd.dataoff = @intCast(u32, offset);
3700 self.codesig_cmd.datasize = @intCast(u32, needed_size);
40323701}
40333702
4034fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature, offset: u32) !void {
3703fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {
40353704 const seg = self.getSegment(self.text_section_index.?);
3705 const offset = self.codesig_cmd.dataoff;
40363706
40373707 var buffer = std.ArrayList(u8).init(self.base.allocator);
40383708 defer buffer.deinit();
src/link/MachO/CodeSignature.zig+54-35
......@@ -1,6 +1,4 @@
11const CodeSignature = @This();
2const Compilation = @import("../../Compilation.zig");
3const WaitGroup = @import("../../WaitGroup.zig");
42
53const std = @import("std");
64const assert = std.debug.assert;
......@@ -9,10 +7,14 @@ const log = std.log.scoped(.link);
97const macho = std.macho;
108const mem = std.mem;
119const testing = std.testing;
10
1211const Allocator = mem.Allocator;
12const Compilation = @import("../../Compilation.zig");
1313const Sha256 = std.crypto.hash.sha2.Sha256;
14const ThreadPool = @import("../../ThreadPool.zig");
15const WaitGroup = @import("../../WaitGroup.zig");
1416
15const hash_size: u8 = 32;
17const hash_size = Sha256.digest_length;
1618
1719const Blob = union(enum) {
1820 code_directory: *CodeDirectory,
......@@ -109,7 +111,7 @@ const CodeDirectory = struct {
109111 fn size(self: CodeDirectory) u32 {
110112 const code_slots = self.inner.nCodeSlots * hash_size;
111113 const special_slots = self.inner.nSpecialSlots * hash_size;
112 return @sizeOf(macho.CodeDirectory) + @intCast(u32, self.ident.len + 1) + special_slots + code_slots;
114 return @sizeOf(macho.CodeDirectory) + @intCast(u32, self.ident.len + 1 + special_slots + code_slots);
113115 }
114116
115117 fn write(self: CodeDirectory, writer: anytype) !void {
......@@ -287,33 +289,7 @@ pub fn writeAdhocSignature(
287289 self.code_directory.inner.nCodeSlots = total_pages;
288290
289291 // Calculate hash for each page (in file) and write it to the buffer
290 var wg: WaitGroup = .{};
291 {
292 const buffer = try gpa.alloc(u8, self.page_size * total_pages);
293 defer gpa.free(buffer);
294
295 const results = try gpa.alloc(fs.File.PReadError!usize, total_pages);
296 defer gpa.free(results);
297 {
298 wg.reset();
299 defer wg.wait();
300
301 var i: usize = 0;
302 while (i < total_pages) : (i += 1) {
303 const fstart = i * self.page_size;
304 const fsize = if (fstart + self.page_size > opts.file_size)
305 opts.file_size - fstart
306 else
307 self.page_size;
308 const out_hash = &self.code_directory.code_slots.items[i];
309 wg.start();
310 try comp.thread_pool.spawn(workerSha256Hash, .{
311 opts.file, fstart, buffer[fstart..][0..fsize], out_hash, &results[i], &wg,
312 });
313 }
314 }
315 for (results) |result| _ = try result;
316 }
292 try self.parallelHash(gpa, comp.thread_pool, opts.file, opts.file_size);
317293
318294 try blobs.append(.{ .code_directory = &self.code_directory });
319295 header.length += @sizeOf(macho.BlobIndex);
......@@ -352,7 +328,7 @@ pub fn writeAdhocSignature(
352328 }
353329
354330 self.code_directory.inner.hashOffset =
355 @sizeOf(macho.CodeDirectory) + @intCast(u32, self.code_directory.ident.len + 1) + self.code_directory.inner.nSpecialSlots * hash_size;
331 @sizeOf(macho.CodeDirectory) + @intCast(u32, self.code_directory.ident.len + 1 + self.code_directory.inner.nSpecialSlots * hash_size);
356332 self.code_directory.inner.length = self.code_directory.size();
357333 header.length += self.code_directory.size();
358334
......@@ -372,17 +348,60 @@ pub fn writeAdhocSignature(
372348 }
373349}
374350
375fn workerSha256Hash(
351fn parallelHash(
352 self: *CodeSignature,
353 gpa: Allocator,
354 pool: *ThreadPool,
355 file: fs.File,
356 file_size: u32,
357) !void {
358 var wg: WaitGroup = .{};
359
360 const total_num_chunks = mem.alignForward(file_size, self.page_size) / self.page_size;
361 assert(self.code_directory.code_slots.items.len >= total_num_chunks);
362
363 const buffer = try gpa.alloc(u8, self.page_size * total_num_chunks);
364 defer gpa.free(buffer);
365
366 const results = try gpa.alloc(fs.File.PReadError!usize, total_num_chunks);
367 defer gpa.free(results);
368
369 {
370 wg.reset();
371 defer wg.wait();
372
373 var i: usize = 0;
374 while (i < total_num_chunks) : (i += 1) {
375 const fstart = i * self.page_size;
376 const fsize = if (fstart + self.page_size > file_size)
377 file_size - fstart
378 else
379 self.page_size;
380 wg.start();
381 try pool.spawn(worker, .{
382 file,
383 fstart,
384 buffer[fstart..][0..fsize],
385 &self.code_directory.code_slots.items[i],
386 &results[i],
387 &wg,
388 });
389 }
390 }
391 for (results) |result| _ = try result;
392}
393
394fn worker(
376395 file: fs.File,
377396 fstart: usize,
378397 buffer: []u8,
379 hash: *[hash_size]u8,
398 out: *[hash_size]u8,
380399 err: *fs.File.PReadError!usize,
381400 wg: *WaitGroup,
382401) void {
383402 defer wg.finish();
384403 err.* = file.preadAll(buffer, fstart);
385 Sha256.hash(buffer, hash, .{});
404 Sha256.hash(buffer, out, .{});
386405}
387406
388407pub fn size(self: CodeSignature) u32 {
src/link/MachO/DebugSymbols.zig+29-53
......@@ -5,6 +5,7 @@ const build_options = @import("build_options");
55const assert = std.debug.assert;
66const fs = std.fs;
77const link = @import("../../link.zig");
8const load_commands = @import("load_commands.zig");
89const log = std.log.scoped(.dsym);
910const macho = std.macho;
1011const makeStaticString = MachO.makeStaticString;
......@@ -25,6 +26,8 @@ dwarf: Dwarf,
2526file: fs.File,
2627page_size: u16,
2728
29symtab_cmd: macho.symtab_command = .{},
30
2831segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
2932sections: std.ArrayListUnmanaged(macho.section_64) = .{},
3033
......@@ -295,31 +298,21 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
295298 }
296299 }
297300
301 self.finalizeDwarfSegment(macho_file);
302 try self.writeLinkeditSegmentData(macho_file);
303
304 // Write load commands
298305 var lc_buffer = std.ArrayList(u8).init(self.allocator);
299306 defer lc_buffer.deinit();
300307 const lc_writer = lc_buffer.writer();
301 var ncmds: u32 = 0;
302
303 self.finalizeDwarfSegment(macho_file);
304 try self.writeLinkeditSegmentData(macho_file, &ncmds, lc_writer);
305
306 {
307 try lc_writer.writeStruct(macho_file.uuid);
308 ncmds += 1;
309 }
310
311 var headers_buf = std.ArrayList(u8).init(self.allocator);
312 defer headers_buf.deinit();
313 try self.writeSegmentHeaders(macho_file, &ncmds, headers_buf.writer());
314308
315 try self.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
316 try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
309 try self.writeSegmentHeaders(macho_file, lc_writer);
310 try lc_writer.writeStruct(self.symtab_cmd);
311 try lc_writer.writeStruct(macho_file.uuid_cmd);
317312
318 try self.writeHeader(
319 macho_file,
320 ncmds,
321 @intCast(u32, lc_buffer.items.len + headers_buf.items.len),
322 );
313 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
314 try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
315 try self.writeHeader(macho_file, ncmds, @intCast(u32, lc_buffer.items.len));
323316
324317 assert(!self.debug_abbrev_section_dirty);
325318 assert(!self.debug_aranges_section_dirty);
......@@ -386,7 +379,7 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {
386379 log.debug("found __LINKEDIT segment free space at 0x{x}", .{linkedit.fileoff});
387380}
388381
389fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, writer: anytype) !void {
382fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, writer: anytype) !void {
390383 // Write segment/section headers from the binary file first.
391384 const end = macho_file.linkedit_segment_cmd_index.?;
392385 for (macho_file.segments.items[0..end]) |seg, i| {
......@@ -416,8 +409,6 @@ fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, wri
416409 out_header.offset = 0;
417410 try writer.writeStruct(out_header);
418411 }
419
420 ncmds.* += 1;
421412 }
422413 // Next, commit DSYM's __LINKEDIT and __DWARF segments headers.
423414 for (self.segments.items) |seg, i| {
......@@ -426,7 +417,6 @@ fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, wri
426417 for (self.sections.items[indexes.start..indexes.end]) |header| {
427418 try writer.writeStruct(header);
428419 }
429 ncmds.* += 1;
430420 }
431421}
432422
......@@ -465,33 +455,19 @@ fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
465455 return min_pos - start;
466456}
467457
468fn writeLinkeditSegmentData(
469 self: *DebugSymbols,
470 macho_file: *MachO,
471 ncmds: *u32,
472 lc_writer: anytype,
473) !void {
458fn writeLinkeditSegmentData(self: *DebugSymbols, macho_file: *MachO) !void {
474459 const tracy = trace(@src());
475460 defer tracy.end();
476461
477 var symtab_cmd = macho.symtab_command{
478 .cmdsize = @sizeOf(macho.symtab_command),
479 .symoff = 0,
480 .nsyms = 0,
481 .stroff = 0,
482 .strsize = 0,
483 };
484 try self.writeSymtab(macho_file, &symtab_cmd);
485 try self.writeStrtab(&symtab_cmd);
486 try lc_writer.writeStruct(symtab_cmd);
487 ncmds.* += 1;
462 try self.writeSymtab(macho_file);
463 try self.writeStrtab();
488464
489465 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
490466 const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
491467 seg.vmsize = aligned_size;
492468}
493469
494fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_command) !void {
470fn writeSymtab(self: *DebugSymbols, macho_file: *MachO) !void {
495471 const tracy = trace(@src());
496472 defer tracy.end();
497473
......@@ -530,10 +506,10 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_comman
530506 const needed_size = nsyms * @sizeOf(macho.nlist_64);
531507 seg.filesize = offset + needed_size - seg.fileoff;
532508
533 lc.symoff = @intCast(u32, offset);
534 lc.nsyms = @intCast(u32, nsyms);
509 self.symtab_cmd.symoff = @intCast(u32, offset);
510 self.symtab_cmd.nsyms = @intCast(u32, nsyms);
535511
536 const locals_off = lc.symoff;
512 const locals_off = @intCast(u32, offset);
537513 const locals_size = nlocals * @sizeOf(macho.nlist_64);
538514 const exports_off = locals_off + locals_size;
539515 const exports_size = nexports * @sizeOf(macho.nlist_64);
......@@ -545,26 +521,26 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_comman
545521 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
546522}
547523
548fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
524fn writeStrtab(self: *DebugSymbols) !void {
549525 const tracy = trace(@src());
550526 defer tracy.end();
551527
552528 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
553 const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));
554 const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));
529 const symtab_size = @intCast(u32, self.symtab_cmd.nsyms * @sizeOf(macho.nlist_64));
530 const offset = mem.alignForwardGeneric(u64, self.symtab_cmd.symoff + symtab_size, @alignOf(u64));
555531 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));
556532
557533 seg.filesize = offset + needed_size - seg.fileoff;
558 lc.stroff = @intCast(u32, offset);
559 lc.strsize = @intCast(u32, needed_size);
534 self.symtab_cmd.stroff = @intCast(u32, offset);
535 self.symtab_cmd.strsize = @intCast(u32, needed_size);
560536
561 log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize });
537 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
562538
563 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
539 try self.file.pwriteAll(self.strtab.buffer.items, offset);
564540
565541 if (self.strtab.buffer.items.len < needed_size) {
566542 // Ensure we are always padded to the actual length of the file.
567 try self.file.pwriteAll(&[_]u8{0}, lc.stroff + lc.strsize);
543 try self.file.pwriteAll(&[_]u8{0}, offset + needed_size);
568544 }
569545}
570546
src/link/MachO/load_commands.zig created+314
......@@ -0,0 +1,314 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const link = @import("../../link.zig");
4const log = std.log.scoped(.link);
5const macho = std.macho;
6const mem = std.mem;
7
8const Allocator = mem.Allocator;
9const Dylib = @import("Dylib.zig");
10
11pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
12
13fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
14 const darwin_path_max = 1024;
15 const name_len = if (assume_max_path_len) darwin_path_max else std.mem.len(name) + 1;
16 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
17}
18
19const CalcLCsSizeCtx = struct {
20 segments: []const macho.segment_command_64,
21 dylibs: []const Dylib,
22 referenced_dylibs: []u16,
23 wants_function_starts: bool = true,
24};
25
26fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx, assume_max_path_len: bool) !u32 {
27 var has_text_segment: bool = false;
28 var sizeofcmds: u64 = 0;
29 for (ctx.segments) |seg| {
30 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
31 if (mem.eql(u8, seg.segName(), "__TEXT")) {
32 has_text_segment = true;
33 }
34 }
35
36 // LC_DYLD_INFO_ONLY
37 sizeofcmds += @sizeOf(macho.dyld_info_command);
38 // LC_FUNCTION_STARTS
39 if (has_text_segment and ctx.wants_function_starts) |_| {
40 sizeofcmds += @sizeOf(macho.linkedit_data_command);
41 }
42 // LC_DATA_IN_CODE
43 sizeofcmds += @sizeOf(macho.linkedit_data_command);
44 // LC_SYMTAB
45 sizeofcmds += @sizeOf(macho.symtab_command);
46 // LC_DYSYMTAB
47 sizeofcmds += @sizeOf(macho.dysymtab_command);
48 // LC_LOAD_DYLINKER
49 sizeofcmds += calcInstallNameLen(
50 @sizeOf(macho.dylinker_command),
51 mem.sliceTo(default_dyld_path, 0),
52 false,
53 );
54 // LC_MAIN
55 if (options.output_mode == .Exe) {
56 sizeofcmds += @sizeOf(macho.entry_point_command);
57 }
58 // LC_ID_DYLIB
59 if (options.output_mode == .Lib and options.link_mode == .Dynamic) {
60 sizeofcmds += blk: {
61 const emit = options.emit.?;
62 const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path});
63 defer if (options.install_name == null) gpa.free(install_name);
64 break :blk calcInstallNameLen(
65 @sizeOf(macho.dylib_command),
66 install_name,
67 assume_max_path_len,
68 );
69 };
70 }
71 // LC_RPATH
72 {
73 var it = RpathIterator.init(gpa, options.rpath_list);
74 defer it.deinit();
75 while (try it.next()) |rpath| {
76 sizeofcmds += calcInstallNameLen(
77 @sizeOf(macho.rpath_command),
78 rpath,
79 assume_max_path_len,
80 );
81 }
82 }
83 // LC_SOURCE_VERSION
84 sizeofcmds += @sizeOf(macho.source_version_command);
85 // LC_BUILD_VERSION
86 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
87 // LC_UUID
88 sizeofcmds += @sizeOf(macho.uuid_command);
89 // LC_LOAD_DYLIB
90 for (ctx.referenced_dylibs) |id| {
91 const dylib = ctx.dylibs[id];
92 const dylib_id = dylib.id orelse unreachable;
93 sizeofcmds += calcInstallNameLen(
94 @sizeOf(macho.dylib_command),
95 dylib_id.name,
96 assume_max_path_len,
97 );
98 }
99 // LC_CODE_SIGNATURE
100 {
101 const target = options.target;
102 const requires_codesig = blk: {
103 if (options.entitlements) |_| break :blk true;
104 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
105 break :blk true;
106 break :blk false;
107 };
108 if (requires_codesig) {
109 sizeofcmds += @sizeOf(macho.linkedit_data_command);
110 }
111 }
112
113 return @intCast(u32, sizeofcmds);
114}
115
116pub fn calcMinHeaderPad(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx) !u64 {
117 var padding: u32 = (try calcLCsSize(gpa, options, ctx, false)) + (options.headerpad_size orelse 0);
118 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
119
120 if (options.headerpad_max_install_names) {
121 var min_headerpad_size: u32 = try calcLCsSize(gpa, options, ctx, true);
122 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
123 min_headerpad_size + @sizeOf(macho.mach_header_64),
124 });
125 padding = @max(padding, min_headerpad_size);
126 }
127
128 const offset = @sizeOf(macho.mach_header_64) + padding;
129 log.debug("actual headerpad size 0x{x}", .{offset});
130
131 return offset;
132}
133
134pub fn calcNumOfLCs(lc_buffer: []const u8) u32 {
135 var ncmds: u32 = 0;
136 var pos: usize = 0;
137 while (true) {
138 if (pos >= lc_buffer.len) break;
139 const cmd = @ptrCast(*align(1) const macho.load_command, lc_buffer.ptr + pos).*;
140 ncmds += 1;
141 pos += cmd.cmdsize;
142 }
143 return ncmds;
144}
145
146pub fn writeDylinkerLC(lc_writer: anytype) !void {
147 const name_len = mem.sliceTo(default_dyld_path, 0).len;
148 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
149 u64,
150 @sizeOf(macho.dylinker_command) + name_len,
151 @sizeOf(u64),
152 ));
153 try lc_writer.writeStruct(macho.dylinker_command{
154 .cmd = .LOAD_DYLINKER,
155 .cmdsize = cmdsize,
156 .name = @sizeOf(macho.dylinker_command),
157 });
158 try lc_writer.writeAll(mem.sliceTo(default_dyld_path, 0));
159 const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
160 if (padding > 0) {
161 try lc_writer.writeByteNTimes(0, padding);
162 }
163}
164
165const WriteDylibLCCtx = struct {
166 cmd: macho.LC,
167 name: []const u8,
168 timestamp: u32 = 2,
169 current_version: u32 = 0x10000,
170 compatibility_version: u32 = 0x10000,
171};
172
173fn writeDylibLC(ctx: WriteDylibLCCtx, lc_writer: anytype) !void {
174 const name_len = ctx.name.len + 1;
175 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
176 u64,
177 @sizeOf(macho.dylib_command) + name_len,
178 @sizeOf(u64),
179 ));
180 try lc_writer.writeStruct(macho.dylib_command{
181 .cmd = ctx.cmd,
182 .cmdsize = cmdsize,
183 .dylib = .{
184 .name = @sizeOf(macho.dylib_command),
185 .timestamp = ctx.timestamp,
186 .current_version = ctx.current_version,
187 .compatibility_version = ctx.compatibility_version,
188 },
189 });
190 try lc_writer.writeAll(ctx.name);
191 try lc_writer.writeByte(0);
192 const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
193 if (padding > 0) {
194 try lc_writer.writeByteNTimes(0, padding);
195 }
196}
197
198pub fn writeDylibIdLC(gpa: Allocator, options: *const link.Options, lc_writer: anytype) !void {
199 assert(options.output_mode == .Lib and options.link_mode == .Dynamic);
200 const emit = options.emit.?;
201 const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path});
202 defer if (options.install_name == null) gpa.free(install_name);
203 const curr = options.version orelse std.builtin.Version{
204 .major = 1,
205 .minor = 0,
206 .patch = 0,
207 };
208 const compat = options.compatibility_version orelse std.builtin.Version{
209 .major = 1,
210 .minor = 0,
211 .patch = 0,
212 };
213 try writeDylibLC(.{
214 .cmd = .ID_DYLIB,
215 .name = install_name,
216 .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
217 .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
218 }, lc_writer);
219}
220
221const RpathIterator = struct {
222 buffer: []const []const u8,
223 table: std.StringHashMap(void),
224 count: usize = 0,
225
226 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
227 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
228 }
229
230 fn deinit(it: *RpathIterator) void {
231 it.table.deinit();
232 }
233
234 fn next(it: *RpathIterator) !?[]const u8 {
235 while (true) {
236 if (it.count >= it.buffer.len) return null;
237 const rpath = it.buffer[it.count];
238 it.count += 1;
239 const gop = try it.table.getOrPut(rpath);
240 if (gop.found_existing) continue;
241 return rpath;
242 }
243 }
244};
245
246pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, lc_writer: anytype) !void {
247 var it = RpathIterator.init(gpa, options.rpath_list);
248 defer it.deinit();
249
250 while (try it.next()) |rpath| {
251 const rpath_len = rpath.len + 1;
252 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
253 u64,
254 @sizeOf(macho.rpath_command) + rpath_len,
255 @sizeOf(u64),
256 ));
257 try lc_writer.writeStruct(macho.rpath_command{
258 .cmdsize = cmdsize,
259 .path = @sizeOf(macho.rpath_command),
260 });
261 try lc_writer.writeAll(rpath);
262 try lc_writer.writeByte(0);
263 const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
264 if (padding > 0) {
265 try lc_writer.writeByteNTimes(0, padding);
266 }
267 }
268}
269
270pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !void {
271 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
272 const platform_version = blk: {
273 const ver = options.target.os.version_range.semver.min;
274 const platform_version = ver.major << 16 | ver.minor << 8;
275 break :blk platform_version;
276 };
277 const sdk_version = if (options.native_darwin_sdk) |sdk| blk: {
278 const ver = sdk.version;
279 const sdk_version = ver.major << 16 | ver.minor << 8;
280 break :blk sdk_version;
281 } else platform_version;
282 const is_simulator_abi = options.target.abi == .simulator;
283 try lc_writer.writeStruct(macho.build_version_command{
284 .cmdsize = cmdsize,
285 .platform = switch (options.target.os.tag) {
286 .macos => .MACOS,
287 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
288 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
289 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
290 else => unreachable,
291 },
292 .minos = platform_version,
293 .sdk = sdk_version,
294 .ntools = 1,
295 });
296 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
297 .tool = .LD,
298 .version = 0x0,
299 }));
300}
301
302pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, lc_writer: anytype) !void {
303 for (referenced) |index| {
304 const dylib = dylibs[index];
305 const dylib_id = dylib.id orelse unreachable;
306 try writeDylibLC(.{
307 .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
308 .name = dylib_id.name,
309 .timestamp = dylib_id.timestamp,
310 .current_version = dylib_id.current_version,
311 .compatibility_version = dylib_id.compatibility_version,
312 }, lc_writer);
313 }
314}
src/link/MachO/zld.zig+272-452
......@@ -13,6 +13,7 @@ const bind = @import("bind.zig");
1313const dead_strip = @import("dead_strip.zig");
1414const fat = @import("fat.zig");
1515const link = @import("../../link.zig");
16const load_commands = @import("load_commands.zig");
1617const thunks = @import("thunks.zig");
1718const trace = @import("../../tracy.zig").trace;
1819
......@@ -25,6 +26,7 @@ const Compilation = @import("../../Compilation.zig");
2526const DwarfInfo = @import("DwarfInfo.zig");
2627const Dylib = @import("Dylib.zig");
2728const MachO = @import("../MachO.zig");
29const Md5 = std.crypto.hash.Md5;
2830const LibStub = @import("../tapi.zig").LibStub;
2931const Object = @import("Object.zig");
3032const StringTable = @import("../strtab.zig").StringTable;
......@@ -34,7 +36,17 @@ pub const Zld = struct {
3436 gpa: Allocator,
3537 file: fs.File,
3638 page_size: u16,
37 options: link.Options,
39 options: *const link.Options,
40
41 dyld_info_cmd: macho.dyld_info_command = .{},
42 symtab_cmd: macho.symtab_command = .{},
43 dysymtab_cmd: macho.dysymtab_command = .{},
44 function_starts_cmd: macho.linkedit_data_command = .{ .cmd = .FUNCTION_STARTS },
45 data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE },
46 uuid_cmd: macho.uuid_command = .{
47 .uuid = [_]u8{0} ** 16,
48 },
49 codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
3850
3951 objects: std.ArrayListUnmanaged(Object) = .{},
4052 archives: std.ArrayListUnmanaged(Archive) = .{},
......@@ -1227,195 +1239,6 @@ pub const Zld = struct {
12271239 }
12281240 }
12291241
1230 fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
1231 const name_len = mem.sliceTo(MachO.default_dyld_path, 0).len;
1232 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1233 u64,
1234 @sizeOf(macho.dylinker_command) + name_len,
1235 @sizeOf(u64),
1236 ));
1237 try lc_writer.writeStruct(macho.dylinker_command{
1238 .cmd = .LOAD_DYLINKER,
1239 .cmdsize = cmdsize,
1240 .name = @sizeOf(macho.dylinker_command),
1241 });
1242 try lc_writer.writeAll(mem.sliceTo(MachO.default_dyld_path, 0));
1243 const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
1244 if (padding > 0) {
1245 try lc_writer.writeByteNTimes(0, padding);
1246 }
1247 ncmds.* += 1;
1248 }
1249
1250 fn writeMainLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1251 if (self.options.output_mode != .Exe) return;
1252 const seg_id = self.getSegmentByName("__TEXT").?;
1253 const seg = self.segments.items[seg_id];
1254 const global = self.getEntryPoint();
1255 const sym = self.getSymbol(global);
1256 try lc_writer.writeStruct(macho.entry_point_command{
1257 .cmd = .MAIN,
1258 .cmdsize = @sizeOf(macho.entry_point_command),
1259 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
1260 .stacksize = self.options.stack_size_override orelse 0,
1261 });
1262 ncmds.* += 1;
1263 }
1264
1265 const WriteDylibLCCtx = struct {
1266 cmd: macho.LC,
1267 name: []const u8,
1268 timestamp: u32 = 2,
1269 current_version: u32 = 0x10000,
1270 compatibility_version: u32 = 0x10000,
1271 };
1272
1273 fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
1274 const name_len = ctx.name.len + 1;
1275 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1276 u64,
1277 @sizeOf(macho.dylib_command) + name_len,
1278 @sizeOf(u64),
1279 ));
1280 try lc_writer.writeStruct(macho.dylib_command{
1281 .cmd = ctx.cmd,
1282 .cmdsize = cmdsize,
1283 .dylib = .{
1284 .name = @sizeOf(macho.dylib_command),
1285 .timestamp = ctx.timestamp,
1286 .current_version = ctx.current_version,
1287 .compatibility_version = ctx.compatibility_version,
1288 },
1289 });
1290 try lc_writer.writeAll(ctx.name);
1291 try lc_writer.writeByte(0);
1292 const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
1293 if (padding > 0) {
1294 try lc_writer.writeByteNTimes(0, padding);
1295 }
1296 ncmds.* += 1;
1297 }
1298
1299 fn writeDylibIdLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1300 if (self.options.output_mode != .Lib) return;
1301 const install_name = self.options.install_name orelse self.options.emit.?.sub_path;
1302 const curr = self.options.version orelse std.builtin.Version{
1303 .major = 1,
1304 .minor = 0,
1305 .patch = 0,
1306 };
1307 const compat = self.options.compatibility_version orelse std.builtin.Version{
1308 .major = 1,
1309 .minor = 0,
1310 .patch = 0,
1311 };
1312 try writeDylibLC(.{
1313 .cmd = .ID_DYLIB,
1314 .name = install_name,
1315 .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
1316 .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
1317 }, ncmds, lc_writer);
1318 }
1319
1320 const RpathIterator = struct {
1321 buffer: []const []const u8,
1322 table: std.StringHashMap(void),
1323 count: usize = 0,
1324
1325 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
1326 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
1327 }
1328
1329 fn deinit(it: *RpathIterator) void {
1330 it.table.deinit();
1331 }
1332
1333 fn next(it: *RpathIterator) !?[]const u8 {
1334 while (true) {
1335 if (it.count >= it.buffer.len) return null;
1336 const rpath = it.buffer[it.count];
1337 it.count += 1;
1338 const gop = try it.table.getOrPut(rpath);
1339 if (gop.found_existing) continue;
1340 return rpath;
1341 }
1342 }
1343 };
1344
1345 fn writeRpathLCs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1346 const gpa = self.gpa;
1347
1348 var it = RpathIterator.init(gpa, self.options.rpath_list);
1349 defer it.deinit();
1350
1351 while (try it.next()) |rpath| {
1352 const rpath_len = rpath.len + 1;
1353 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1354 u64,
1355 @sizeOf(macho.rpath_command) + rpath_len,
1356 @sizeOf(u64),
1357 ));
1358 try lc_writer.writeStruct(macho.rpath_command{
1359 .cmdsize = cmdsize,
1360 .path = @sizeOf(macho.rpath_command),
1361 });
1362 try lc_writer.writeAll(rpath);
1363 try lc_writer.writeByte(0);
1364 const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
1365 if (padding > 0) {
1366 try lc_writer.writeByteNTimes(0, padding);
1367 }
1368 ncmds.* += 1;
1369 }
1370 }
1371
1372 fn writeBuildVersionLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1373 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
1374 const platform_version = blk: {
1375 const ver = self.options.target.os.version_range.semver.min;
1376 const platform_version = ver.major << 16 | ver.minor << 8;
1377 break :blk platform_version;
1378 };
1379 const sdk_version = if (self.options.native_darwin_sdk) |sdk| blk: {
1380 const ver = sdk.version;
1381 const sdk_version = ver.major << 16 | ver.minor << 8;
1382 break :blk sdk_version;
1383 } else platform_version;
1384 const is_simulator_abi = self.options.target.abi == .simulator;
1385 try lc_writer.writeStruct(macho.build_version_command{
1386 .cmdsize = cmdsize,
1387 .platform = switch (self.options.target.os.tag) {
1388 .macos => .MACOS,
1389 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
1390 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
1391 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
1392 else => unreachable,
1393 },
1394 .minos = platform_version,
1395 .sdk = sdk_version,
1396 .ntools = 1,
1397 });
1398 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
1399 .tool = .LD,
1400 .version = 0x0,
1401 }));
1402 ncmds.* += 1;
1403 }
1404
1405 fn writeLoadDylibLCs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1406 for (self.referenced_dylibs.keys()) |id| {
1407 const dylib = self.dylibs.items[id];
1408 const dylib_id = dylib.id orelse unreachable;
1409 try writeDylibLC(.{
1410 .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
1411 .name = dylib_id.name,
1412 .timestamp = dylib_id.timestamp,
1413 .current_version = dylib_id.current_version,
1414 .compatibility_version = dylib_id.compatibility_version,
1415 }, ncmds, lc_writer);
1416 }
1417 }
1418
14191242 pub fn deinit(self: *Zld) void {
14201243 const gpa = self.gpa;
14211244
......@@ -1516,110 +1339,6 @@ pub const Zld = struct {
15161339 }
15171340 }
15181341
1519 fn calcLCsSize(self: *Zld, assume_max_path_len: bool) !u32 {
1520 const gpa = self.gpa;
1521
1522 var sizeofcmds: u64 = 0;
1523 for (self.segments.items) |seg| {
1524 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
1525 }
1526
1527 // LC_DYLD_INFO_ONLY
1528 sizeofcmds += @sizeOf(macho.dyld_info_command);
1529 // LC_FUNCTION_STARTS
1530 if (self.getSectionByName("__TEXT", "__text")) |_| {
1531 sizeofcmds += @sizeOf(macho.linkedit_data_command);
1532 }
1533 // LC_DATA_IN_CODE
1534 sizeofcmds += @sizeOf(macho.linkedit_data_command);
1535 // LC_SYMTAB
1536 sizeofcmds += @sizeOf(macho.symtab_command);
1537 // LC_DYSYMTAB
1538 sizeofcmds += @sizeOf(macho.dysymtab_command);
1539 // LC_LOAD_DYLINKER
1540 sizeofcmds += MachO.calcInstallNameLen(
1541 @sizeOf(macho.dylinker_command),
1542 mem.sliceTo(MachO.default_dyld_path, 0),
1543 false,
1544 );
1545 // LC_MAIN
1546 if (self.options.output_mode == .Exe) {
1547 sizeofcmds += @sizeOf(macho.entry_point_command);
1548 }
1549 // LC_ID_DYLIB
1550 if (self.options.output_mode == .Lib) {
1551 sizeofcmds += blk: {
1552 const install_name = self.options.install_name orelse self.options.emit.?.sub_path;
1553 break :blk MachO.calcInstallNameLen(
1554 @sizeOf(macho.dylib_command),
1555 install_name,
1556 assume_max_path_len,
1557 );
1558 };
1559 }
1560 // LC_RPATH
1561 {
1562 var it = RpathIterator.init(gpa, self.options.rpath_list);
1563 defer it.deinit();
1564 while (try it.next()) |rpath| {
1565 sizeofcmds += MachO.calcInstallNameLen(
1566 @sizeOf(macho.rpath_command),
1567 rpath,
1568 assume_max_path_len,
1569 );
1570 }
1571 }
1572 // LC_SOURCE_VERSION
1573 sizeofcmds += @sizeOf(macho.source_version_command);
1574 // LC_BUILD_VERSION
1575 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
1576 // LC_UUID
1577 sizeofcmds += @sizeOf(macho.uuid_command);
1578 // LC_LOAD_DYLIB
1579 for (self.referenced_dylibs.keys()) |id| {
1580 const dylib = self.dylibs.items[id];
1581 const dylib_id = dylib.id orelse unreachable;
1582 sizeofcmds += MachO.calcInstallNameLen(
1583 @sizeOf(macho.dylib_command),
1584 dylib_id.name,
1585 assume_max_path_len,
1586 );
1587 }
1588 // LC_CODE_SIGNATURE
1589 {
1590 const target = self.options.target;
1591 const requires_codesig = blk: {
1592 if (self.options.entitlements) |_| break :blk true;
1593 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
1594 break :blk true;
1595 break :blk false;
1596 };
1597 if (requires_codesig) {
1598 sizeofcmds += @sizeOf(macho.linkedit_data_command);
1599 }
1600 }
1601
1602 return @intCast(u32, sizeofcmds);
1603 }
1604
1605 fn calcMinHeaderPad(self: *Zld) !u64 {
1606 var padding: u32 = (try self.calcLCsSize(false)) + (self.options.headerpad_size orelse 0);
1607 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
1608
1609 if (self.options.headerpad_max_install_names) {
1610 var min_headerpad_size: u32 = try self.calcLCsSize(true);
1611 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
1612 min_headerpad_size + @sizeOf(macho.mach_header_64),
1613 });
1614 padding = @max(padding, min_headerpad_size);
1615 }
1616
1617 const offset = @sizeOf(macho.mach_header_64) + padding;
1618 log.debug("actual headerpad size 0x{x}", .{offset});
1619
1620 return offset;
1621 }
1622
16231342 pub fn allocateSymbol(self: *Zld) !u32 {
16241343 try self.locals.ensureUnusedCapacity(self.gpa, 1);
16251344 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
......@@ -1842,7 +1561,11 @@ pub const Zld = struct {
18421561 fn allocateSegments(self: *Zld) !void {
18431562 for (self.segments.items) |*segment, segment_index| {
18441563 const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
1845 const base_size = if (is_text_segment) try self.calcMinHeaderPad() else 0;
1564 const base_size = if (is_text_segment) try load_commands.calcMinHeaderPad(self.gpa, self.options, .{
1565 .segments = self.segments.items,
1566 .dylibs = self.dylibs.items,
1567 .referenced_dylibs = self.referenced_dylibs.keys(),
1568 }) else 0;
18461569 try self.allocateSegment(@intCast(u8, segment_index), base_size);
18471570 }
18481571 }
......@@ -2015,7 +1738,7 @@ pub const Zld = struct {
20151738 return (@intCast(u8, segment_precedence) << 4) + section_precedence;
20161739 }
20171740
2018 fn writeSegmentHeaders(self: *Zld, ncmds: *u32, writer: anytype) !void {
1741 fn writeSegmentHeaders(self: *Zld, writer: anytype) !void {
20191742 for (self.segments.items) |seg, i| {
20201743 const indexes = self.getSectionIndexes(@intCast(u8, i));
20211744 var out_seg = seg;
......@@ -2039,16 +1762,14 @@ pub const Zld = struct {
20391762 if (header.size == 0) continue;
20401763 try writer.writeStruct(header);
20411764 }
2042
2043 ncmds.* += 1;
20441765 }
20451766 }
20461767
2047 fn writeLinkeditSegmentData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void {
2048 try self.writeDyldInfoData(ncmds, lc_writer, reverse_lookups);
2049 try self.writeFunctionStarts(ncmds, lc_writer);
2050 try self.writeDataInCode(ncmds, lc_writer);
2051 try self.writeSymtabs(ncmds, lc_writer);
1768 fn writeLinkeditSegmentData(self: *Zld, reverse_lookups: [][]u32) !void {
1769 try self.writeDyldInfoData(reverse_lookups);
1770 try self.writeFunctionStarts();
1771 try self.writeDataInCode();
1772 try self.writeSymtabs();
20521773
20531774 const seg = self.getLinkeditSegmentPtr();
20541775 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
......@@ -2437,7 +2158,7 @@ pub const Zld = struct {
24372158 try trie.finalize(gpa);
24382159 }
24392160
2440 fn writeDyldInfoData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void {
2161 fn writeDyldInfoData(self: *Zld, reverse_lookups: [][]u32) !void {
24412162 const gpa = self.gpa;
24422163
24432164 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
......@@ -2506,21 +2227,14 @@ pub const Zld = struct {
25062227 const size = math.cast(usize, lazy_bind_size) orelse return error.Overflow;
25072228 try self.populateLazyBindOffsetsInStubHelper(buffer[offset..][0..size]);
25082229
2509 try lc_writer.writeStruct(macho.dyld_info_command{
2510 .cmd = .DYLD_INFO_ONLY,
2511 .cmdsize = @sizeOf(macho.dyld_info_command),
2512 .rebase_off = @intCast(u32, rebase_off),
2513 .rebase_size = @intCast(u32, rebase_size),
2514 .bind_off = @intCast(u32, bind_off),
2515 .bind_size = @intCast(u32, bind_size),
2516 .weak_bind_off = 0,
2517 .weak_bind_size = 0,
2518 .lazy_bind_off = @intCast(u32, lazy_bind_off),
2519 .lazy_bind_size = @intCast(u32, lazy_bind_size),
2520 .export_off = @intCast(u32, export_off),
2521 .export_size = @intCast(u32, export_size),
2522 });
2523 ncmds.* += 1;
2230 self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off);
2231 self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size);
2232 self.dyld_info_cmd.bind_off = @intCast(u32, bind_off);
2233 self.dyld_info_cmd.bind_size = @intCast(u32, bind_size);
2234 self.dyld_info_cmd.lazy_bind_off = @intCast(u32, lazy_bind_off);
2235 self.dyld_info_cmd.lazy_bind_size = @intCast(u32, lazy_bind_size);
2236 self.dyld_info_cmd.export_off = @intCast(u32, export_off);
2237 self.dyld_info_cmd.export_size = @intCast(u32, export_size);
25242238 }
25252239
25262240 fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
......@@ -2638,7 +2352,7 @@ pub const Zld = struct {
26382352
26392353 const asc_u64 = std.sort.asc(u64);
26402354
2641 fn writeFunctionStarts(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
2355 fn writeFunctionStarts(self: *Zld) !void {
26422356 const text_seg_index = self.getSegmentByName("__TEXT") orelse return;
26432357 const text_sect_index = self.getSectionByName("__TEXT", "__text") orelse return;
26442358 const text_seg = self.segments.items[text_seg_index];
......@@ -2697,13 +2411,8 @@ pub const Zld = struct {
26972411
26982412 try self.file.pwriteAll(buffer.items, offset);
26992413
2700 try lc_writer.writeStruct(macho.linkedit_data_command{
2701 .cmd = .FUNCTION_STARTS,
2702 .cmdsize = @sizeOf(macho.linkedit_data_command),
2703 .dataoff = @intCast(u32, offset),
2704 .datasize = @intCast(u32, needed_size),
2705 });
2706 ncmds.* += 1;
2414 self.function_starts_cmd.dataoff = @intCast(u32, offset);
2415 self.function_starts_cmd.datasize = @intCast(u32, needed_size);
27072416 }
27082417
27092418 fn filterDataInCode(
......@@ -2725,7 +2434,7 @@ pub const Zld = struct {
27252434 return dices[start..end];
27262435 }
27272436
2728 fn writeDataInCode(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
2437 fn writeDataInCode(self: *Zld) !void {
27292438 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa);
27302439 defer out_dice.deinit();
27312440
......@@ -2775,54 +2484,19 @@ pub const Zld = struct {
27752484 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
27762485
27772486 try self.file.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
2778 try lc_writer.writeStruct(macho.linkedit_data_command{
2779 .cmd = .DATA_IN_CODE,
2780 .cmdsize = @sizeOf(macho.linkedit_data_command),
2781 .dataoff = @intCast(u32, offset),
2782 .datasize = @intCast(u32, needed_size),
2783 });
2784 ncmds.* += 1;
2487
2488 self.data_in_code_cmd.dataoff = @intCast(u32, offset);
2489 self.data_in_code_cmd.datasize = @intCast(u32, needed_size);
27852490 }
27862491
2787 fn writeSymtabs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
2788 var symtab_cmd = macho.symtab_command{
2789 .cmdsize = @sizeOf(macho.symtab_command),
2790 .symoff = 0,
2791 .nsyms = 0,
2792 .stroff = 0,
2793 .strsize = 0,
2794 };
2795 var dysymtab_cmd = macho.dysymtab_command{
2796 .cmdsize = @sizeOf(macho.dysymtab_command),
2797 .ilocalsym = 0,
2798 .nlocalsym = 0,
2799 .iextdefsym = 0,
2800 .nextdefsym = 0,
2801 .iundefsym = 0,
2802 .nundefsym = 0,
2803 .tocoff = 0,
2804 .ntoc = 0,
2805 .modtaboff = 0,
2806 .nmodtab = 0,
2807 .extrefsymoff = 0,
2808 .nextrefsyms = 0,
2809 .indirectsymoff = 0,
2810 .nindirectsyms = 0,
2811 .extreloff = 0,
2812 .nextrel = 0,
2813 .locreloff = 0,
2814 .nlocrel = 0,
2815 };
2816 var ctx = try self.writeSymtab(&symtab_cmd);
2492 fn writeSymtabs(self: *Zld) !void {
2493 var ctx = try self.writeSymtab();
28172494 defer ctx.imports_table.deinit();
2818 try self.writeDysymtab(ctx, &dysymtab_cmd);
2819 try self.writeStrtab(&symtab_cmd);
2820 try lc_writer.writeStruct(symtab_cmd);
2821 try lc_writer.writeStruct(dysymtab_cmd);
2822 ncmds.* += 2;
2495 try self.writeDysymtab(ctx);
2496 try self.writeStrtab();
28232497 }
28242498
2825 fn writeSymtab(self: *Zld, lc: *macho.symtab_command) !SymtabCtx {
2499 fn writeSymtab(self: *Zld) !SymtabCtx {
28262500 const gpa = self.gpa;
28272501
28282502 var locals = std.ArrayList(macho.nlist_64).init(gpa);
......@@ -2843,12 +2517,6 @@ pub const Zld = struct {
28432517 }
28442518 }
28452519
2846 if (!self.options.strip) {
2847 for (self.objects.items) |object| {
2848 try self.generateSymbolStabs(object, &locals);
2849 }
2850 }
2851
28522520 var exports = std.ArrayList(macho.nlist_64).init(gpa);
28532521 defer exports.deinit();
28542522
......@@ -2879,6 +2547,14 @@ pub const Zld = struct {
28792547 try imports_table.putNoClobber(global, new_index);
28802548 }
28812549
2550 // We generate stabs last in order to ensure that the strtab always has debug info
2551 // strings trailing
2552 if (!self.options.strip) {
2553 for (self.objects.items) |object| {
2554 try self.generateSymbolStabs(object, &locals);
2555 }
2556 }
2557
28822558 const nlocals = @intCast(u32, locals.items.len);
28832559 const nexports = @intCast(u32, exports.items.len);
28842560 const nimports = @intCast(u32, imports.items.len);
......@@ -2903,8 +2579,8 @@ pub const Zld = struct {
29032579 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
29042580 try self.file.pwriteAll(buffer.items, offset);
29052581
2906 lc.symoff = @intCast(u32, offset);
2907 lc.nsyms = nsyms;
2582 self.symtab_cmd.symoff = @intCast(u32, offset);
2583 self.symtab_cmd.nsyms = nsyms;
29082584
29092585 return SymtabCtx{
29102586 .nlocalsym = nlocals,
......@@ -2914,7 +2590,7 @@ pub const Zld = struct {
29142590 };
29152591 }
29162592
2917 fn writeStrtab(self: *Zld, lc: *macho.symtab_command) !void {
2593 fn writeStrtab(self: *Zld) !void {
29182594 const seg = self.getLinkeditSegmentPtr();
29192595 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
29202596 const needed_size = self.strtab.buffer.items.len;
......@@ -2924,8 +2600,8 @@ pub const Zld = struct {
29242600
29252601 try self.file.pwriteAll(self.strtab.buffer.items, offset);
29262602
2927 lc.stroff = @intCast(u32, offset);
2928 lc.strsize = @intCast(u32, needed_size);
2603 self.symtab_cmd.stroff = @intCast(u32, offset);
2604 self.symtab_cmd.strsize = @intCast(u32, needed_size);
29292605 }
29302606
29312607 const SymtabCtx = struct {
......@@ -2935,7 +2611,7 @@ pub const Zld = struct {
29352611 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
29362612 };
29372613
2938 fn writeDysymtab(self: *Zld, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {
2614 fn writeDysymtab(self: *Zld, ctx: SymtabCtx) !void {
29392615 const gpa = self.gpa;
29402616 const nstubs = @intCast(u32, self.stubs.items.len);
29412617 const ngot_entries = @intCast(u32, self.got_entries.items.len);
......@@ -2991,21 +2667,161 @@ pub const Zld = struct {
29912667 assert(buf.items.len == needed_size);
29922668 try self.file.pwriteAll(buf.items, offset);
29932669
2994 lc.nlocalsym = ctx.nlocalsym;
2995 lc.iextdefsym = iextdefsym;
2996 lc.nextdefsym = ctx.nextdefsym;
2997 lc.iundefsym = iundefsym;
2998 lc.nundefsym = ctx.nundefsym;
2999 lc.indirectsymoff = @intCast(u32, offset);
3000 lc.nindirectsyms = nindirectsyms;
2670 self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;
2671 self.dysymtab_cmd.iextdefsym = iextdefsym;
2672 self.dysymtab_cmd.nextdefsym = ctx.nextdefsym;
2673 self.dysymtab_cmd.iundefsym = iundefsym;
2674 self.dysymtab_cmd.nundefsym = ctx.nundefsym;
2675 self.dysymtab_cmd.indirectsymoff = @intCast(u32, offset);
2676 self.dysymtab_cmd.nindirectsyms = nindirectsyms;
30012677 }
30022678
3003 fn writeCodeSignaturePadding(
3004 self: *Zld,
3005 code_sig: *CodeSignature,
3006 ncmds: *u32,
3007 lc_writer: anytype,
3008 ) !u32 {
2679 fn writeUuid(self: *Zld, comp: *const Compilation, args: struct {
2680 linkedit_cmd_offset: u32,
2681 symtab_cmd_offset: u32,
2682 uuid_cmd_offset: u32,
2683 codesig_cmd_offset: ?u32,
2684 }) !void {
2685 _ = comp;
2686 switch (self.options.optimize_mode) {
2687 .Debug => {
2688 // In Debug we don't really care about reproducibility, so put in a random value
2689 // and be done with it.
2690 std.crypto.random.bytes(&self.uuid_cmd.uuid);
2691 Md5.hash(&self.uuid_cmd.uuid, &self.uuid_cmd.uuid, .{});
2692 conformUuid(&self.uuid_cmd.uuid);
2693 },
2694 else => {
2695 const max_file_end = self.symtab_cmd.stroff + self.symtab_cmd.strsize;
2696
2697 const FileSubsection = struct {
2698 start: u32,
2699 end: u32,
2700 };
2701
2702 var subsections: [5]FileSubsection = undefined;
2703 var count: usize = 0;
2704
2705 // Exclude LINKEDIT segment command as it contains file size that includes stabs contribution
2706 // and code signature.
2707 subsections[count] = .{
2708 .start = 0,
2709 .end = args.linkedit_cmd_offset,
2710 };
2711 count += 1;
2712
2713 // Exclude SYMTAB and DYSYMTAB commands for the same reason.
2714 subsections[count] = .{
2715 .start = subsections[count - 1].end + @sizeOf(macho.segment_command_64),
2716 .end = args.symtab_cmd_offset,
2717 };
2718 count += 1;
2719
2720 // Exclude CODE_SIGNATURE command (if present).
2721 if (args.codesig_cmd_offset) |offset| {
2722 subsections[count] = .{
2723 .start = subsections[count - 1].end + @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command),
2724 .end = offset,
2725 };
2726 count += 1;
2727 }
2728
2729 if (!self.options.strip) {
2730 // Exclude region comprising all symbol stabs.
2731 const nlocals = self.dysymtab_cmd.nlocalsym;
2732
2733 const locals_buf = try self.gpa.alloc(u8, nlocals * @sizeOf(macho.nlist_64));
2734 defer self.gpa.free(locals_buf);
2735
2736 const amt = try self.file.preadAll(locals_buf, self.symtab_cmd.symoff);
2737 if (amt != locals_buf.len) return error.InputOutput;
2738 const locals = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), locals_buf))[0..nlocals];
2739
2740 const istab: usize = for (locals) |local, i| {
2741 if (local.stab()) break i;
2742 } else locals.len;
2743 const nstabs = locals.len - istab;
2744
2745 if (nstabs == 0) {
2746 subsections[count] = .{
2747 .start = subsections[count - 1].end + if (args.codesig_cmd_offset == null)
2748 @as(u32, @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command))
2749 else
2750 @sizeOf(macho.linkedit_data_command),
2751 .end = max_file_end,
2752 };
2753 count += 1;
2754 } else {
2755 // Exclude a subsection of the strtab with names of the stabs.
2756 // We do not care about anything succeeding strtab as it is the code signature data which is
2757 // not part of the UUID calculation anyway.
2758 const stab_stroff = locals[istab].n_strx;
2759
2760 subsections[count] = .{
2761 .start = subsections[count - 1].end + if (args.codesig_cmd_offset == null)
2762 @as(u32, @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command))
2763 else
2764 @sizeOf(macho.linkedit_data_command),
2765 .end = @intCast(u32, self.symtab_cmd.symoff + istab * @sizeOf(macho.nlist_64)),
2766 };
2767 count += 1;
2768
2769 subsections[count] = .{
2770 .start = subsections[count - 1].end + @intCast(u32, nstabs * @sizeOf(macho.nlist_64)),
2771 .end = self.symtab_cmd.stroff + stab_stroff,
2772 };
2773 count += 1;
2774 }
2775 } else {
2776 subsections[count] = .{
2777 .start = subsections[count - 1].end + if (args.codesig_cmd_offset == null)
2778 @as(u32, @sizeOf(macho.symtab_command) + @sizeOf(macho.dysymtab_command))
2779 else
2780 @sizeOf(macho.linkedit_data_command),
2781 .end = max_file_end,
2782 };
2783 count += 1;
2784 }
2785
2786 const chunk_size = 0x4000;
2787
2788 var hasher = Md5.init(.{});
2789 var buffer: [chunk_size]u8 = undefined;
2790
2791 for (subsections[0..count]) |cut| {
2792 const size = cut.end - cut.start;
2793 const num_chunks = mem.alignForward(size, chunk_size) / chunk_size;
2794
2795 var i: usize = 0;
2796 while (i < num_chunks) : (i += 1) {
2797 const fstart = cut.start + i * chunk_size;
2798 const fsize = if (fstart + chunk_size > cut.end)
2799 cut.end - fstart
2800 else
2801 chunk_size;
2802 const amt = try self.file.preadAll(buffer[0..fsize], fstart);
2803 if (amt != fsize) return error.InputOutput;
2804
2805 hasher.update(buffer[0..fsize]);
2806 }
2807 }
2808
2809 hasher.final(&self.uuid_cmd.uuid);
2810 conformUuid(&self.uuid_cmd.uuid);
2811 },
2812 }
2813
2814 const in_file = args.uuid_cmd_offset + @sizeOf(macho.load_command);
2815 try self.file.pwriteAll(&self.uuid_cmd.uuid, in_file);
2816 }
2817
2818 inline fn conformUuid(out: *[Md5.digest_length]u8) void {
2819 // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
2820 out[6] = (out[6] & 0x0F) | (3 << 4);
2821 out[8] = (out[8] & 0x3F) | 0x80;
2822 }
2823
2824 fn writeCodeSignaturePadding(self: *Zld, code_sig: *CodeSignature) !void {
30092825 const seg = self.getLinkeditSegmentPtr();
30102826 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
30112827 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
......@@ -3018,23 +2834,11 @@ pub const Zld = struct {
30182834 // except for code signature data.
30192835 try self.file.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
30202836
3021 try lc_writer.writeStruct(macho.linkedit_data_command{
3022 .cmd = .CODE_SIGNATURE,
3023 .cmdsize = @sizeOf(macho.linkedit_data_command),
3024 .dataoff = @intCast(u32, offset),
3025 .datasize = @intCast(u32, needed_size),
3026 });
3027 ncmds.* += 1;
3028
3029 return @intCast(u32, offset);
2837 self.codesig_cmd.dataoff = @intCast(u32, offset);
2838 self.codesig_cmd.datasize = @intCast(u32, needed_size);
30302839 }
30312840
3032 fn writeCodeSignature(
3033 self: *Zld,
3034 comp: *const Compilation,
3035 code_sig: *CodeSignature,
3036 offset: u32,
3037 ) !void {
2841 fn writeCodeSignature(self: *Zld, comp: *const Compilation, code_sig: *CodeSignature) !void {
30382842 const seg_id = self.getSegmentByName("__TEXT").?;
30392843 const seg = self.segments.items[seg_id];
30402844
......@@ -3045,17 +2849,17 @@ pub const Zld = struct {
30452849 .file = self.file,
30462850 .exec_seg_base = seg.fileoff,
30472851 .exec_seg_limit = seg.filesize,
3048 .file_size = offset,
2852 .file_size = self.codesig_cmd.dataoff,
30492853 .output_mode = self.options.output_mode,
30502854 }, buffer.writer());
30512855 assert(buffer.items.len == code_sig.size());
30522856
30532857 log.debug("writing code signature from 0x{x} to 0x{x}", .{
3054 offset,
3055 offset + buffer.items.len,
2858 self.codesig_cmd.dataoff,
2859 self.codesig_cmd.dataoff + buffer.items.len,
30562860 });
30572861
3058 try self.file.pwriteAll(buffer.items, offset);
2862 try self.file.pwriteAll(buffer.items, self.codesig_cmd.dataoff);
30592863 }
30602864
30612865 /// Writes Mach-O file header.
......@@ -3734,7 +3538,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
37343538 defer tracy.end();
37353539
37363540 const gpa = macho_file.base.allocator;
3737 const options = macho_file.base.options;
3541 const options = &macho_file.base.options;
37383542 const target = options.target;
37393543
37403544 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
......@@ -3884,7 +3688,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
38843688 macho_file.base.file = try directory.handle.createFile(sub_path, .{
38853689 .truncate = true,
38863690 .read = true,
3887 .mode = link.determineMode(options),
3691 .mode = link.determineMode(options.*),
38883692 });
38893693 }
38903694 var zld = Zld{
......@@ -4271,12 +4075,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
42714075 }
42724076
42734077 try zld.writeAtoms(reverse_lookups);
4274
4275 var lc_buffer = std.ArrayList(u8).init(arena);
4276 const lc_writer = lc_buffer.writer();
4277 var ncmds: u32 = 0;
4278
4279 try zld.writeLinkeditSegmentData(&ncmds, lc_writer, reverse_lookups);
4078 try zld.writeLinkeditSegmentData(reverse_lookups);
42804079
42814080 // If the last section of __DATA segment is zerofill section, we need to ensure
42824081 // that the free space between the end of the last non-zerofill section of __DATA
......@@ -4301,39 +4100,54 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
43014100 }
43024101 }
43034102
4304 try Zld.writeDylinkerLC(&ncmds, lc_writer);
4305 try zld.writeMainLC(&ncmds, lc_writer);
4306 try zld.writeDylibIdLC(&ncmds, lc_writer);
4307 try zld.writeRpathLCs(&ncmds, lc_writer);
4103 // Write load commands
4104 var lc_buffer = std.ArrayList(u8).init(arena);
4105 const lc_writer = lc_buffer.writer();
43084106
4309 {
4310 try lc_writer.writeStruct(macho.source_version_command{
4311 .cmdsize = @sizeOf(macho.source_version_command),
4312 .version = 0x0,
4107 try zld.writeSegmentHeaders(lc_writer);
4108 const linkedit_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len - @sizeOf(macho.segment_command_64));
4109
4110 try lc_writer.writeStruct(zld.dyld_info_cmd);
4111 try lc_writer.writeStruct(zld.function_starts_cmd);
4112 try lc_writer.writeStruct(zld.data_in_code_cmd);
4113
4114 const symtab_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len);
4115 try lc_writer.writeStruct(zld.symtab_cmd);
4116 try lc_writer.writeStruct(zld.dysymtab_cmd);
4117
4118 try load_commands.writeDylinkerLC(lc_writer);
4119
4120 if (zld.options.output_mode == .Exe) {
4121 const seg_id = zld.getSegmentByName("__TEXT").?;
4122 const seg = zld.segments.items[seg_id];
4123 const global = zld.getEntryPoint();
4124 const sym = zld.getSymbol(global);
4125 try lc_writer.writeStruct(macho.entry_point_command{
4126 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
4127 .stacksize = options.stack_size_override orelse 0,
43134128 });
4314 ncmds += 1;
4129 } else {
4130 assert(zld.options.output_mode == .Lib);
4131 try load_commands.writeDylibIdLC(zld.gpa, zld.options, lc_writer);
43154132 }
43164133
4317 try zld.writeBuildVersionLC(&ncmds, lc_writer);
4134 try load_commands.writeRpathLCs(zld.gpa, zld.options, lc_writer);
4135 try lc_writer.writeStruct(macho.source_version_command{
4136 .version = 0,
4137 });
4138 try load_commands.writeBuildVersionLC(zld.options, lc_writer);
43184139
4319 {
4320 var uuid_lc = macho.uuid_command{
4321 .cmdsize = @sizeOf(macho.uuid_command),
4322 .uuid = undefined,
4323 };
4324 std.crypto.random.bytes(&uuid_lc.uuid);
4325 try lc_writer.writeStruct(uuid_lc);
4326 ncmds += 1;
4327 }
4140 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len);
4141 try lc_writer.writeStruct(zld.uuid_cmd);
43284142
4329 try zld.writeLoadDylibLCs(&ncmds, lc_writer);
4143 try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), lc_writer);
43304144
43314145 const requires_codesig = blk: {
43324146 if (options.entitlements) |_| break :blk true;
43334147 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
43344148 break :blk false;
43354149 };
4336 var codesig_offset: ?u32 = null;
4150 var codesig_cmd_offset: ?u32 = null;
43374151 var codesig: ?CodeSignature = if (requires_codesig) blk: {
43384152 // Preallocate space for the code signature.
43394153 // We need to do this at this stage so that we have the load commands with proper values
......@@ -4341,24 +4155,30 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
43414155 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
43424156 // where the code signature goes into.
43434157 var codesig = CodeSignature.init(page_size);
4344 codesig.code_directory.ident = options.emit.?.sub_path;
4158 codesig.code_directory.ident = fs.path.basename(full_out_path);
43454159 if (options.entitlements) |path| {
43464160 try codesig.addEntitlements(gpa, path);
43474161 }
4348 codesig_offset = try zld.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
4162 try zld.writeCodeSignaturePadding(&codesig);
4163 codesig_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len);
4164 try lc_writer.writeStruct(zld.codesig_cmd);
43494165 break :blk codesig;
43504166 } else null;
43514167 defer if (codesig) |*csig| csig.deinit(gpa);
43524168
4353 var headers_buf = std.ArrayList(u8).init(arena);
4354 try zld.writeSegmentHeaders(&ncmds, headers_buf.writer());
4169 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
4170 try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
4171 try zld.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len));
43554172
4356 try zld.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
4357 try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
4358 try zld.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
4173 try zld.writeUuid(comp, .{
4174 .linkedit_cmd_offset = linkedit_cmd_offset,
4175 .symtab_cmd_offset = symtab_cmd_offset,
4176 .uuid_cmd_offset = uuid_cmd_offset,
4177 .codesig_cmd_offset = codesig_cmd_offset,
4178 });
43594179
43604180 if (codesig) |*csig| {
4361 try zld.writeCodeSignature(comp, csig, codesig_offset.?); // code signing always comes last
4181 try zld.writeCodeSignature(comp, csig); // code signing always comes last
43624182 }
43634183 }
43644184
test/link.zig+5
......@@ -170,6 +170,11 @@ fn addMachOCases(cases: *tests.StandaloneContext) void {
170170 .requires_symlinks = true,
171171 });
172172
173 cases.addBuildFile("test/link/macho/uuid/build.zig", .{
174 .build_modes = false,
175 .requires_symlinks = true,
176 });
177
173178 cases.addBuildFile("test/link/macho/weak_library/build.zig", .{
174179 .build_modes = true,
175180 .requires_symlinks = true,
test/link/macho/uuid/build.zig created+40
......@@ -0,0 +1,40 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
4
5pub fn build(b: *Builder) void {
6 const test_step = b.step("test", "Test");
7 test_step.dependOn(b.getInstallStep());
8 testUuid(b, test_step, .ReleaseSafe, "eb1203019e453d808d4f1e71053af9af");
9 testUuid(b, test_step, .ReleaseFast, "eb1203019e453d808d4f1e71053af9af");
10 testUuid(b, test_step, .ReleaseSmall, "eb1203019e453d808d4f1e71053af9af");
11}
12
13fn testUuid(b: *Builder, test_step: *std.build.Step, mode: std.builtin.Mode, comptime exp: []const u8) void {
14 // The calculated UUID value is independent of debug info and so it should
15 // stay the same across builds.
16 {
17 const dylib = simpleDylib(b, mode);
18 const check_dylib = dylib.checkObject(.macho);
19 check_dylib.checkStart("cmd UUID");
20 check_dylib.checkNext("uuid " ++ exp);
21 test_step.dependOn(&check_dylib.step);
22 }
23 {
24 const dylib = simpleDylib(b, mode);
25 dylib.strip = true;
26 const check_dylib = dylib.checkObject(.macho);
27 check_dylib.checkStart("cmd UUID");
28 check_dylib.checkNext("uuid " ++ exp);
29 test_step.dependOn(&check_dylib.step);
30 }
31}
32
33fn simpleDylib(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {
34 const dylib = b.addSharedLibrary("test", null, b.version(1, 0, 0));
35 dylib.setBuildMode(mode);
36 dylib.setTarget(.{ .cpu_arch = .aarch64, .os_tag = .macos });
37 dylib.addCSourceFile("test.c", &.{});
38 dylib.linkLibC();
39 return dylib;
40}
test/link/macho/uuid/test.c created+2
......@@ -0,0 +1,2 @@
1void test() {}
2