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...@@ -585,10 +585,13 @@ set(ZIG_STAGE2_SOURCES
585 "${CMAKE_SOURCE_DIR}/src/link/MachO/DwarfInfo.zig"585 "${CMAKE_SOURCE_DIR}/src/link/MachO/DwarfInfo.zig"
586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
587 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"587 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
588 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
588 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"589 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
589 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"590 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
590 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"591 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
591 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"592 "${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"
592 "${CMAKE_SOURCE_DIR}/src/link/MachO/thunks.zig"595 "${CMAKE_SOURCE_DIR}/src/link/MachO/thunks.zig"
593 "${CMAKE_SOURCE_DIR}/src/link/MachO/zld.zig"596 "${CMAKE_SOURCE_DIR}/src/link/MachO/zld.zig"
594 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"597 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
ci/aarch64-macos.sh+16
...@@ -52,3 +52,19 @@ stage3-release/bin/zig build test docs \...@@ -52,3 +52,19 @@ stage3-release/bin/zig build test docs \
5252
53# Produce the experimental std lib documentation.53# Produce the experimental std lib documentation.
54stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib54stage3-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 \...@@ -60,3 +60,19 @@ stage3-release/bin/zig build test docs \
6060
61# Produce the experimental std lib documentation.61# Produce the experimental std lib documentation.
62stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib62stage3-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 {...@@ -571,6 +571,12 @@ const MachODumper = struct {
571 });571 });
572 },572 },
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
574 else => {},580 else => {},
575 }581 }
576 }582 }
lib/std/macho.zig+46-46
...@@ -58,10 +58,10 @@ pub const uuid_command = extern struct {...@@ -58,10 +58,10 @@ pub const uuid_command = extern struct {
58 cmd: LC = .UUID,58 cmd: LC = .UUID,
5959
60 /// sizeof(struct uuid_command)60 /// sizeof(struct uuid_command)
61 cmdsize: u32,61 cmdsize: u32 = @sizeOf(uuid_command),
6262
63 /// the 128-bit uuid63 /// the 128-bit uuid
64 uuid: [16]u8,64 uuid: [16]u8 = undefined,
65};65};
6666
67/// The version_min_command contains the min OS version on which this67/// The version_min_command contains the min OS version on which this
...@@ -71,7 +71,7 @@ pub const version_min_command = extern struct {...@@ -71,7 +71,7 @@ pub const version_min_command = extern struct {
71 cmd: LC,71 cmd: LC,
7272
73 /// sizeof(struct version_min_command)73 /// sizeof(struct version_min_command)
74 cmdsize: u32,74 cmdsize: u32 = @sizeOf(version_min_command),
7575
76 /// X.Y.Z is encoded in nibbles xxxx.yy.zz76 /// X.Y.Z is encoded in nibbles xxxx.yy.zz
77 version: u32,77 version: u32,
...@@ -87,7 +87,7 @@ pub const source_version_command = extern struct {...@@ -87,7 +87,7 @@ pub const source_version_command = extern struct {
87 cmd: LC = .SOURCE_VERSION,87 cmd: LC = .SOURCE_VERSION,
8888
89 /// sizeof(source_version_command)89 /// sizeof(source_version_command)
90 cmdsize: u32,90 cmdsize: u32 = @sizeOf(source_version_command),
9191
92 /// A.B.C.D.E packed as a24.b10.c10.d10.e1092 /// A.B.C.D.E packed as a24.b10.c10.d10.e10
93 version: u64,93 version: u64,
...@@ -155,13 +155,13 @@ pub const entry_point_command = extern struct {...@@ -155,13 +155,13 @@ pub const entry_point_command = extern struct {
155 cmd: LC = .MAIN,155 cmd: LC = .MAIN,
156156
157 /// sizeof(struct entry_point_command)157 /// sizeof(struct entry_point_command)
158 cmdsize: u32,158 cmdsize: u32 = @sizeOf(entry_point_command),
159159
160 /// file (__TEXT) offset of main()160 /// file (__TEXT) offset of main()
161 entryoff: u64,161 entryoff: u64 = 0,
162162
163 /// if not zero, initial stack size163 /// if not zero, initial stack size
164 stacksize: u64,164 stacksize: u64 = 0,
165};165};
166166
167/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD167/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
...@@ -172,19 +172,19 @@ pub const symtab_command = extern struct {...@@ -172,19 +172,19 @@ pub const symtab_command = extern struct {
172 cmd: LC = .SYMTAB,172 cmd: LC = .SYMTAB,
173173
174 /// sizeof(struct symtab_command)174 /// sizeof(struct symtab_command)
175 cmdsize: u32,175 cmdsize: u32 = @sizeOf(symtab_command),
176176
177 /// symbol table offset177 /// symbol table offset
178 symoff: u32,178 symoff: u32 = 0,
179179
180 /// number of symbol table entries180 /// number of symbol table entries
181 nsyms: u32,181 nsyms: u32 = 0,
182182
183 /// string table offset183 /// string table offset
184 stroff: u32,184 stroff: u32 = 0,
185185
186 /// string table size in bytes186 /// string table size in bytes
187 strsize: u32,187 strsize: u32 = 0,
188};188};
189189
190/// This is the second set of the symbolic information which is used to support190/// This is the second set of the symbolic information which is used to support
...@@ -230,7 +230,7 @@ pub const dysymtab_command = extern struct {...@@ -230,7 +230,7 @@ pub const dysymtab_command = extern struct {
230 cmd: LC = .DYSYMTAB,230 cmd: LC = .DYSYMTAB,
231231
232 /// sizeof(struct dysymtab_command)232 /// sizeof(struct dysymtab_command)
233 cmdsize: u32,233 cmdsize: u32 = @sizeOf(dysymtab_command),
234234
235 // The symbols indicated by symoff and nsyms of the LC_SYMTAB load command235 // The symbols indicated by symoff and nsyms of the LC_SYMTAB load command
236 // are grouped into the following three groups:236 // are grouped into the following three groups:
...@@ -247,22 +247,22 @@ pub const dysymtab_command = extern struct {...@@ -247,22 +247,22 @@ pub const dysymtab_command = extern struct {
247 // table when this is a dynamically linked shared library file).247 // table when this is a dynamically linked shared library file).
248248
249 /// index of local symbols249 /// index of local symbols
250 ilocalsym: u32,250 ilocalsym: u32 = 0,
251251
252 /// number of local symbols252 /// number of local symbols
253 nlocalsym: u32,253 nlocalsym: u32 = 0,
254254
255 /// index to externally defined symbols255 /// index to externally defined symbols
256 iextdefsym: u32,256 iextdefsym: u32 = 0,
257257
258 /// number of externally defined symbols258 /// number of externally defined symbols
259 nextdefsym: u32,259 nextdefsym: u32 = 0,
260260
261 /// index to undefined symbols261 /// index to undefined symbols
262 iundefsym: u32,262 iundefsym: u32 = 0,
263263
264 /// number of undefined symbols264 /// number of undefined symbols
265 nundefsym: u32,265 nundefsym: u32 = 0,
266266
267 // For the for the dynamic binding process to find which module a symbol267 // For the for the dynamic binding process to find which module a symbol
268 // is defined in the table of contents is used (analogous to the ranlib268 // is defined in the table of contents is used (analogous to the ranlib
...@@ -272,10 +272,10 @@ pub const dysymtab_command = extern struct {...@@ -272,10 +272,10 @@ pub const dysymtab_command = extern struct {
272 // symbols are sorted by name and is use as the table of contents.272 // symbols are sorted by name and is use as the table of contents.
273273
274 /// file offset to table of contents274 /// file offset to table of contents
275 tocoff: u32,275 tocoff: u32 = 0,
276276
277 /// number of entries in table of contents277 /// number of entries in table of contents
278 ntoc: u32,278 ntoc: u32 = 0,
279279
280 // To support dynamic binding of "modules" (whole object files) the symbol280 // To support dynamic binding of "modules" (whole object files) the symbol
281 // table must reflect the modules that the file was created from. This is281 // table must reflect the modules that the file was created from. This is
...@@ -286,10 +286,10 @@ pub const dysymtab_command = extern struct {...@@ -286,10 +286,10 @@ pub const dysymtab_command = extern struct {
286 // contains one module so everything in the file belongs to the module.286 // contains one module so everything in the file belongs to the module.
287287
288 /// file offset to module table288 /// file offset to module table
289 modtaboff: u32,289 modtaboff: u32 = 0,
290290
291 /// number of module table entries291 /// number of module table entries
292 nmodtab: u32,292 nmodtab: u32 = 0,
293293
294 // To support dynamic module binding the module structure for each module294 // To support dynamic module binding the module structure for each module
295 // indicates the external references (defined and undefined) each module295 // indicates the external references (defined and undefined) each module
...@@ -300,10 +300,10 @@ pub const dysymtab_command = extern struct {...@@ -300,10 +300,10 @@ pub const dysymtab_command = extern struct {
300 // undefined external symbols indicates the external references.300 // undefined external symbols indicates the external references.
301301
302 /// offset to referenced symbol table302 /// offset to referenced symbol table
303 extrefsymoff: u32,303 extrefsymoff: u32 = 0,
304304
305 /// number of referenced symbol table entries305 /// number of referenced symbol table entries
306 nextrefsyms: u32,306 nextrefsyms: u32 = 0,
307307
308 // The sections that contain "symbol pointers" and "routine stubs" have308 // The sections that contain "symbol pointers" and "routine stubs" have
309 // indexes and (implied counts based on the size of the section and fixed309 // indexes and (implied counts based on the size of the section and fixed
...@@ -315,10 +315,10 @@ pub const dysymtab_command = extern struct {...@@ -315,10 +315,10 @@ pub const dysymtab_command = extern struct {
315 // The indirect symbol table is ordered to match the entries in the section.315 // The indirect symbol table is ordered to match the entries in the section.
316316
317 /// file offset to the indirect symbol table317 /// file offset to the indirect symbol table
318 indirectsymoff: u32,318 indirectsymoff: u32 = 0,
319319
320 /// number of indirect symbol table entries320 /// number of indirect symbol table entries
321 nindirectsyms: u32,321 nindirectsyms: u32 = 0,
322322
323 // To support relocating an individual module in a library file quickly the323 // To support relocating an individual module in a library file quickly the
324 // external relocation entries for each module in the library need to be324 // external relocation entries for each module in the library need to be
...@@ -347,20 +347,20 @@ pub const dysymtab_command = extern struct {...@@ -347,20 +347,20 @@ pub const dysymtab_command = extern struct {
347 // remaining relocation entries must be local).347 // remaining relocation entries must be local).
348348
349 /// offset to external relocation entries349 /// offset to external relocation entries
350 extreloff: u32,350 extreloff: u32 = 0,
351351
352 /// number of external relocation entries352 /// number of external relocation entries
353 nextrel: u32,353 nextrel: u32 = 0,
354354
355 // All the local relocation entries are grouped together (they are not355 // All the local relocation entries are grouped together (they are not
356 // grouped by their module since they are only used if the object is moved356 // grouped by their module since they are only used if the object is moved
357 // from it staticly link edited address).357 // from it staticly link edited address).
358358
359 /// offset to local relocation entries359 /// offset to local relocation entries
360 locreloff: u32,360 locreloff: u32 = 0,
361361
362 /// number of local relocation entries362 /// number of local relocation entries
363 nlocrel: u32,363 nlocrel: u32 = 0,
364};364};
365365
366/// The linkedit_data_command contains the offsets and sizes of a blob366/// The linkedit_data_command contains the offsets and sizes of a blob
...@@ -370,13 +370,13 @@ pub const linkedit_data_command = extern struct {...@@ -370,13 +370,13 @@ pub const linkedit_data_command = extern struct {
370 cmd: LC,370 cmd: LC,
371371
372 /// sizeof(struct linkedit_data_command)372 /// sizeof(struct linkedit_data_command)
373 cmdsize: u32,373 cmdsize: u32 = @sizeOf(linkedit_data_command),
374374
375 /// file offset of data in __LINKEDIT segment375 /// file offset of data in __LINKEDIT segment
376 dataoff: u32,376 dataoff: u32 = 0,
377377
378 /// file size of data in __LINKEDIT segment378 /// file size of data in __LINKEDIT segment
379 datasize: u32,379 datasize: u32 = 0,
380};380};
381381
382/// The dyld_info_command contains the file offsets and sizes of382/// The dyld_info_command contains the file offsets and sizes of
...@@ -387,10 +387,10 @@ pub const linkedit_data_command = extern struct {...@@ -387,10 +387,10 @@ pub const linkedit_data_command = extern struct {
387/// to interpret it.387/// to interpret it.
388pub const dyld_info_command = extern struct {388pub const dyld_info_command = extern struct {
389 /// LC_DYLD_INFO or LC_DYLD_INFO_ONLY389 /// LC_DYLD_INFO or LC_DYLD_INFO_ONLY
390 cmd: LC,390 cmd: LC = .DYLD_INFO_ONLY,
391391
392 /// sizeof(struct dyld_info_command)392 /// sizeof(struct dyld_info_command)
393 cmdsize: u32,393 cmdsize: u32 = @sizeOf(dyld_info_command),
394394
395 // Dyld rebases an image whenever dyld loads it at an address different395 // Dyld rebases an image whenever dyld loads it at an address different
396 // from its preferred address. The rebase information is a stream396 // from its preferred address. The rebase information is a stream
...@@ -403,10 +403,10 @@ pub const dyld_info_command = extern struct {...@@ -403,10 +403,10 @@ pub const dyld_info_command = extern struct {
403 // bytes.403 // bytes.
404404
405 /// file offset to rebase info405 /// file offset to rebase info
406 rebase_off: u32,406 rebase_off: u32 = 0,
407407
408 /// size of rebase info408 /// size of rebase info
409 rebase_size: u32,409 rebase_size: u32 = 0,
410410
411 // Dyld binds an image during the loading process, if the image411 // Dyld binds an image during the loading process, if the image
412 // requires any pointers to be initialized to symbols in other images.412 // requires any pointers to be initialized to symbols in other images.
...@@ -420,10 +420,10 @@ pub const dyld_info_command = extern struct {...@@ -420,10 +420,10 @@ pub const dyld_info_command = extern struct {
420 // encoded in a few bytes.420 // encoded in a few bytes.
421421
422 /// file offset to binding info422 /// file offset to binding info
423 bind_off: u32,423 bind_off: u32 = 0,
424424
425 /// size of binding info425 /// size of binding info
426 bind_size: u32,426 bind_size: u32 = 0,
427427
428 // Some C++ programs require dyld to unique symbols so that all428 // Some C++ programs require dyld to unique symbols so that all
429 // images in the process use the same copy of some code/data.429 // images in the process use the same copy of some code/data.
...@@ -440,10 +440,10 @@ pub const dyld_info_command = extern struct {...@@ -440,10 +440,10 @@ pub const dyld_info_command = extern struct {
440 // and the call to operator new is then rebound.440 // and the call to operator new is then rebound.
441441
442 /// file offset to weak binding info442 /// file offset to weak binding info
443 weak_bind_off: u32,443 weak_bind_off: u32 = 0,
444444
445 /// size of weak binding info445 /// size of weak binding info
446 weak_bind_size: u32,446 weak_bind_size: u32 = 0,
447447
448 // Some uses of external symbols do not need to be bound immediately.448 // Some uses of external symbols do not need to be bound immediately.
449 // Instead they can be lazily bound on first use. The lazy_bind449 // Instead they can be lazily bound on first use. The lazy_bind
...@@ -457,10 +457,10 @@ pub const dyld_info_command = extern struct {...@@ -457,10 +457,10 @@ pub const dyld_info_command = extern struct {
457 // to bind.457 // to bind.
458458
459 /// file offset to lazy binding info459 /// file offset to lazy binding info
460 lazy_bind_off: u32,460 lazy_bind_off: u32 = 0,
461461
462 /// size of lazy binding info462 /// size of lazy binding info
463 lazy_bind_size: u32,463 lazy_bind_size: u32 = 0,
464464
465 // The symbols exported by a dylib are encoded in a trie. This465 // The symbols exported by a dylib are encoded in a trie. This
466 // is a compact representation that factors out common prefixes.466 // is a compact representation that factors out common prefixes.
...@@ -494,10 +494,10 @@ pub const dyld_info_command = extern struct {...@@ -494,10 +494,10 @@ pub const dyld_info_command = extern struct {
494 // edge points to.494 // edge points to.
495495
496 /// file offset to lazy binding info496 /// file offset to lazy binding info
497 export_off: u32,497 export_off: u32 = 0,
498498
499 /// size of lazy binding info499 /// size of lazy binding info
500 export_size: u32,500 export_size: u32 = 0,
501};501};
502502
503/// A program that uses a dynamic linker contains a dylinker_command to identify503/// 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");...@@ -20,6 +20,7 @@ const dead_strip = @import("MachO/dead_strip.zig");
20const fat = @import("MachO/fat.zig");20const fat = @import("MachO/fat.zig");
21const link = @import("../link.zig");21const link = @import("../link.zig");
22const llvm_backend = @import("../codegen/llvm.zig");22const llvm_backend = @import("../codegen/llvm.zig");
23const load_commands = @import("MachO/load_commands.zig");
23const target_util = @import("../target.zig");24const target_util = @import("../target.zig");
24const trace = @import("../tracy.zig").trace;25const trace = @import("../tracy.zig").trace;
25const zld = @import("MachO/zld.zig");26const zld = @import("MachO/zld.zig");
...@@ -38,6 +39,7 @@ const Object = @import("MachO/Object.zig");...@@ -38,6 +39,7 @@ const Object = @import("MachO/Object.zig");
38const LibStub = @import("tapi.zig").LibStub;39const LibStub = @import("tapi.zig").LibStub;
39const Liveness = @import("../Liveness.zig");40const Liveness = @import("../Liveness.zig");
40const LlvmObject = @import("../codegen/llvm.zig").Object;41const LlvmObject = @import("../codegen/llvm.zig").Object;
42const Md5 = std.crypto.hash.Md5;
41const Module = @import("../Module.zig");43const Module = @import("../Module.zig");
42const Relocation = @import("MachO/Relocation.zig");44const Relocation = @import("MachO/Relocation.zig");
43const StringTable = @import("strtab.zig").StringTable;45const StringTable = @import("strtab.zig").StringTable;
...@@ -98,10 +100,11 @@ page_size: u16,...@@ -98,10 +100,11 @@ page_size: u16,
98/// fashion (default for LLVM backend).100/// fashion (default for LLVM backend).
99mode: enum { incremental, one_shot },101mode: enum { incremental, one_shot },
100102
101uuid: macho.uuid_command = .{103dyld_info_cmd: macho.dyld_info_command = .{},
102 .cmdsize = @sizeOf(macho.uuid_command),104symtab_cmd: macho.symtab_command = .{},
103 .uuid = undefined,105dysymtab_cmd: macho.dysymtab_command = .{},
104},106uuid_cmd: macho.uuid_command = .{},
107codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
105108
106dylibs: std.ArrayListUnmanaged(Dylib) = .{},109dylibs: std.ArrayListUnmanaged(Dylib) = .{},
107dylibs_map: std.StringHashMapUnmanaged(u16) = .{},110dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
...@@ -265,9 +268,6 @@ pub const SymbolWithLoc = struct {...@@ -265,9 +268,6 @@ pub const SymbolWithLoc = struct {
265/// actual_capacity + (actual_capacity / ideal_factor)268/// actual_capacity + (actual_capacity / ideal_factor)
266const ideal_factor = 3;269const ideal_factor = 3;
267270
268/// Default path to dyld
269pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
270
271/// In order for a slice of bytes to be considered eligible to keep metadata pointing at271/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
272/// it as a possible place to put new symbols, it must have enough room for this many bytes272/// it as a possible place to put new symbols, it must have enough room for this many bytes
273/// (plus extra for reserved capacity).273/// (plus extra for reserved capacity).
...@@ -556,40 +556,55 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -556,40 +556,55 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
556 self.logAtoms();556 self.logAtoms();
557 }557 }
558558
559 try self.writeLinkeditSegmentData();
560
561 // Write load commands
559 var lc_buffer = std.ArrayList(u8).init(arena);562 var lc_buffer = std.ArrayList(u8).init(arena);
560 const lc_writer = lc_buffer.writer();563 const lc_writer = lc_buffer.writer();
561 var ncmds: u32 = 0;
562564
563 try self.writeLinkeditSegmentData(&ncmds, lc_writer);565 try self.writeSegmentHeaders(lc_writer);
564 try writeDylinkerLC(&ncmds, 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) {571 switch (self.base.options.output_mode) {
567 error.MissingMainEntrypoint => {572 .Exe => blk: {
568 self.error_flags.no_entry_point_found = true;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 });
569 },587 },
570 else => |e| return e,588 .Lib => if (self.base.options.link_mode == .Dynamic) {
571 };589 try load_commands.writeDylibIdLC(self.base.allocator, &self.base.options, lc_writer);
572590 },
573 try self.writeDylibIdLC(&ncmds, lc_writer);591 else => {},
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;
582 }592 }
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 {600 if (self.cold_start) {
587 std.crypto.random.bytes(&self.uuid.uuid);601 std.crypto.random.bytes(&self.uuid_cmd.uuid);
588 try lc_writer.writeStruct(self.uuid);602 Md5.hash(&self.uuid_cmd.uuid, &self.uuid_cmd.uuid, .{});
589 ncmds += 1;603 conformUuid(&self.uuid_cmd.uuid);
590 }604 }
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
594 const target = self.base.options.target;609 const target = self.base.options.target;
595 const requires_codesig = blk: {610 const requires_codesig = blk: {
...@@ -598,7 +613,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -598,7 +613,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
598 break :blk true;613 break :blk true;
599 break :blk false;614 break :blk false;
600 };615 };
601 var codesig_offset: ?u32 = null;
602 var codesig: ?CodeSignature = if (requires_codesig) blk: {616 var codesig: ?CodeSignature = if (requires_codesig) blk: {
603 // Preallocate space for the code signature.617 // Preallocate space for the code signature.
604 // We need to do this at this stage so that we have the load commands with proper values618 // 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...@@ -610,20 +624,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
610 if (self.base.options.entitlements) |path| {624 if (self.base.options.entitlements) |path| {
611 try codesig.addEntitlements(arena, path);625 try codesig.addEntitlements(arena, path);
612 }626 }
613 codesig_offset = try self.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);627 try self.writeCodeSignaturePadding(&codesig);
628 try lc_writer.writeStruct(self.codesig_cmd);
614 break :blk codesig;629 break :blk codesig;
615 } else null;630 } else null;
616631
617 var headers_buf = std.ArrayList(u8).init(arena);632 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
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);
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
625 if (codesig) |*csig| {637 if (codesig) |*csig| {
626 try self.writeCodeSignature(comp, csig, codesig_offset.?); // code signing always comes last638 try self.writeCodeSignature(comp, csig); // code signing always comes last
627 }639 }
628640
629 if (self.d_sym) |*d_sym| {641 if (self.d_sym) |*d_sym| {
...@@ -653,6 +665,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -653,6 +665,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
653665
654 self.cold_start = false;666 self.cold_start = false;
655}667}
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
657pub fn resolveLibSystem(674pub fn resolveLibSystem(
658 arena: Allocator,675 arena: Allocator,
...@@ -1702,195 +1719,6 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {...@@ -1702,195 +1719,6 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
1702 try self.writePtrWidthAtom(got_atom);1719 try self.writePtrWidthAtom(got_atom);
1703}1720}
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
1894pub fn deinit(self: *MachO) void {1722pub fn deinit(self: *MachO) void {
1895 const gpa = self.base.allocator;1723 const gpa = self.base.allocator;
18961724
...@@ -2976,98 +2804,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -2976,98 +2804,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
2976 }2804 }
2977}2805}
29782806
2979pub inline fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {2807fn calcPagezeroSize(self: *MachO) 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 {
3071 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;2808 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
3072 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);2809 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
3073 if (self.base.options.output_mode == .Lib) return 0;2810 if (self.base.options.output_mode == .Lib) return 0;
...@@ -3079,23 +2816,6 @@ pub fn calcPagezeroSize(self: *MachO) u64 {...@@ -3079,23 +2816,6 @@ pub fn calcPagezeroSize(self: *MachO) u64 {
3079 return aligned_pagezero_vmsize;2816 return aligned_pagezero_vmsize;
3080}2817}
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
3099fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {2819fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {
3100 size: u64 = 0,2820 size: u64 = 0,
3101 alignment: u32 = 0,2821 alignment: u32 = 0,
...@@ -3433,18 +3153,17 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {...@@ -3433,18 +3153,17 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
3433 return global_index;3153 return global_index;
3434}3154}
34353155
3436fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {3156fn writeSegmentHeaders(self: *MachO, writer: anytype) !void {
3437 for (self.segments.items) |seg, i| {3157 for (self.segments.items) |seg, i| {
3438 const indexes = self.getSectionIndexes(@intCast(u8, i));3158 const indexes = self.getSectionIndexes(@intCast(u8, i));
3439 try writer.writeStruct(seg);3159 try writer.writeStruct(seg);
3440 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {3160 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
3441 try writer.writeStruct(header);3161 try writer.writeStruct(header);
3442 }3162 }
3443 ncmds.* += 1;
3444 }3163 }
3445}3164}
34463165
3447fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {3166fn writeLinkeditSegmentData(self: *MachO) !void {
3448 const seg = self.getLinkeditSegmentPtr();3167 const seg = self.getLinkeditSegmentPtr();
3449 seg.filesize = 0;3168 seg.filesize = 0;
3450 seg.vmsize = 0;3169 seg.vmsize = 0;
...@@ -3459,8 +3178,8 @@ fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void...@@ -3459,8 +3178,8 @@ fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void
3459 }3178 }
3460 }3179 }
34613180
3462 try self.writeDyldInfoData(ncmds, lc_writer);3181 try self.writeDyldInfoData();
3463 try self.writeSymtabs(ncmds, lc_writer);3182 try self.writeSymtabs();
34643183
3465 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);3184 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
3466}3185}
...@@ -3612,7 +3331,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {...@@ -3612,7 +3331,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {
3612 try trie.finalize(gpa);3331 try trie.finalize(gpa);
3613}3332}
36143333
3615fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {3334fn writeDyldInfoData(self: *MachO) !void {
3616 const tracy = trace(@src());3335 const tracy = trace(@src());
3617 defer tracy.end();3336 defer tracy.end();
36183337
...@@ -3683,21 +3402,14 @@ fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {...@@ -3683,21 +3402,14 @@ fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
3683 const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow);3402 const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow);
3684 try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]);3403 try self.populateLazyBindOffsetsInStubHelper(buffer[start..end]);
36853404
3686 try lc_writer.writeStruct(macho.dyld_info_command{3405 self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off);
3687 .cmd = .DYLD_INFO_ONLY,3406 self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size);
3688 .cmdsize = @sizeOf(macho.dyld_info_command),3407 self.dyld_info_cmd.bind_off = @intCast(u32, bind_off);
3689 .rebase_off = @intCast(u32, rebase_off),3408 self.dyld_info_cmd.bind_size = @intCast(u32, bind_size);
3690 .rebase_size = @intCast(u32, rebase_size),3409 self.dyld_info_cmd.lazy_bind_off = @intCast(u32, lazy_bind_off);
3691 .bind_off = @intCast(u32, bind_off),3410 self.dyld_info_cmd.lazy_bind_size = @intCast(u32, lazy_bind_size);
3692 .bind_size = @intCast(u32, bind_size),3411 self.dyld_info_cmd.export_off = @intCast(u32, export_off);
3693 .weak_bind_off = 0,3412 self.dyld_info_cmd.export_size = @intCast(u32, export_size);
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;
3701}3413}
37023414
3703fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {3415fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
...@@ -3799,45 +3511,14 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -3799,45 +3511,14 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
3799 }3511 }
3800}3512}
38013513
3802fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {3514fn writeSymtabs(self: *MachO) !void {
3803 var symtab_cmd = macho.symtab_command{3515 var ctx = try self.writeSymtab();
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);
3832 defer ctx.imports_table.deinit();3516 defer ctx.imports_table.deinit();
3833 try self.writeDysymtab(ctx, &dysymtab_cmd);3517 try self.writeDysymtab(ctx);
3834 try self.writeStrtab(&symtab_cmd);3518 try self.writeStrtab();
3835 try lc_writer.writeStruct(symtab_cmd);
3836 try lc_writer.writeStruct(dysymtab_cmd);
3837 ncmds.* += 2;
3838}3519}
38393520
3840fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {3521fn writeSymtab(self: *MachO) !SymtabCtx {
3841 const gpa = self.base.allocator;3522 const gpa = self.base.allocator;
38423523
3843 var locals = std.ArrayList(macho.nlist_64).init(gpa);3524 var locals = std.ArrayList(macho.nlist_64).init(gpa);
...@@ -3902,8 +3583,8 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {...@@ -3902,8 +3583,8 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
3902 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });3583 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
3903 try self.base.file.?.pwriteAll(buffer.items, offset);3584 try self.base.file.?.pwriteAll(buffer.items, offset);
39043585
3905 lc.symoff = @intCast(u32, offset);3586 self.symtab_cmd.symoff = @intCast(u32, offset);
3906 lc.nsyms = nsyms;3587 self.symtab_cmd.nsyms = nsyms;
39073588
3908 return SymtabCtx{3589 return SymtabCtx{
3909 .nlocalsym = nlocals,3590 .nlocalsym = nlocals,
...@@ -3913,7 +3594,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {...@@ -3913,7 +3594,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
3913 };3594 };
3914}3595}
39153596
3916fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void {3597fn writeStrtab(self: *MachO) !void {
3917 const seg = self.getLinkeditSegmentPtr();3598 const seg = self.getLinkeditSegmentPtr();
3918 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));3599 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
3919 const needed_size = self.strtab.buffer.items.len;3600 const needed_size = self.strtab.buffer.items.len;
...@@ -3923,8 +3604,8 @@ fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void {...@@ -3923,8 +3604,8 @@ fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void {
39233604
3924 try self.base.file.?.pwriteAll(self.strtab.buffer.items, offset);3605 try self.base.file.?.pwriteAll(self.strtab.buffer.items, offset);
39253606
3926 lc.stroff = @intCast(u32, offset);3607 self.symtab_cmd.stroff = @intCast(u32, offset);
3927 lc.strsize = @intCast(u32, needed_size);3608 self.symtab_cmd.strsize = @intCast(u32, needed_size);
3928}3609}
39293610
3930const SymtabCtx = struct {3611const SymtabCtx = struct {
...@@ -3934,7 +3615,7 @@ const SymtabCtx = struct {...@@ -3934,7 +3615,7 @@ const SymtabCtx = struct {
3934 imports_table: std.AutoHashMap(SymbolWithLoc, u32),3615 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
3935};3616};
39363617
3937fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {3618fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3938 const gpa = self.base.allocator;3619 const gpa = self.base.allocator;
3939 const nstubs = @intCast(u32, self.stubs_table.count());3620 const nstubs = @intCast(u32, self.stubs_table.count());
3940 const ngot_entries = @intCast(u32, self.got_entries_table.count());3621 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...@@ -3993,21 +3674,16 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi
3993 assert(buf.items.len == needed_size);3674 assert(buf.items.len == needed_size);
3994 try self.base.file.?.pwriteAll(buf.items, offset);3675 try self.base.file.?.pwriteAll(buf.items, offset);
39953676
3996 lc.nlocalsym = ctx.nlocalsym;3677 self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;
3997 lc.iextdefsym = iextdefsym;3678 self.dysymtab_cmd.iextdefsym = iextdefsym;
3998 lc.nextdefsym = ctx.nextdefsym;3679 self.dysymtab_cmd.nextdefsym = ctx.nextdefsym;
3999 lc.iundefsym = iundefsym;3680 self.dysymtab_cmd.iundefsym = iundefsym;
4000 lc.nundefsym = ctx.nundefsym;3681 self.dysymtab_cmd.nundefsym = ctx.nundefsym;
4001 lc.indirectsymoff = @intCast(u32, offset);3682 self.dysymtab_cmd.indirectsymoff = @intCast(u32, offset);
4002 lc.nindirectsyms = nindirectsyms;3683 self.dysymtab_cmd.nindirectsyms = nindirectsyms;
4003}3684}
40043685
4005fn writeCodeSignaturePadding(3686fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
4006 self: *MachO,
4007 code_sig: *CodeSignature,
4008 ncmds: *u32,
4009 lc_writer: anytype,
4010) !u32 {
4011 const seg = self.getLinkeditSegmentPtr();3687 const seg = self.getLinkeditSegmentPtr();
4012 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file3688 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
4013 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L2713689 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
...@@ -4020,19 +3696,13 @@ fn writeCodeSignaturePadding(...@@ -4020,19 +3696,13 @@ fn writeCodeSignaturePadding(
4020 // except for code signature data.3696 // except for code signature data.
4021 try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);3697 try self.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
40223698
4023 try lc_writer.writeStruct(macho.linkedit_data_command{3699 self.codesig_cmd.dataoff = @intCast(u32, offset);
4024 .cmd = .CODE_SIGNATURE,3700 self.codesig_cmd.datasize = @intCast(u32, needed_size);
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);
4032}3701}
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 {
4035 const seg = self.getSegment(self.text_section_index.?);3704 const seg = self.getSegment(self.text_section_index.?);
3705 const offset = self.codesig_cmd.dataoff;
40363706
4037 var buffer = std.ArrayList(u8).init(self.base.allocator);3707 var buffer = std.ArrayList(u8).init(self.base.allocator);
4038 defer buffer.deinit();3708 defer buffer.deinit();
src/link/MachO/CodeSignature.zig+54-35
...@@ -1,6 +1,4 @@...@@ -1,6 +1,4 @@
1const CodeSignature = @This();1const CodeSignature = @This();
2const Compilation = @import("../../Compilation.zig");
3const WaitGroup = @import("../../WaitGroup.zig");
42
5const std = @import("std");3const std = @import("std");
6const assert = std.debug.assert;4const assert = std.debug.assert;
...@@ -9,10 +7,14 @@ const log = std.log.scoped(.link);...@@ -9,10 +7,14 @@ const log = std.log.scoped(.link);
9const macho = std.macho;7const macho = std.macho;
10const mem = std.mem;8const mem = std.mem;
11const testing = std.testing;9const testing = std.testing;
10
12const Allocator = mem.Allocator;11const Allocator = mem.Allocator;
12const Compilation = @import("../../Compilation.zig");
13const Sha256 = std.crypto.hash.sha2.Sha256;13const 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
17const Blob = union(enum) {19const Blob = union(enum) {
18 code_directory: *CodeDirectory,20 code_directory: *CodeDirectory,
...@@ -109,7 +111,7 @@ const CodeDirectory = struct {...@@ -109,7 +111,7 @@ const CodeDirectory = struct {
109 fn size(self: CodeDirectory) u32 {111 fn size(self: CodeDirectory) u32 {
110 const code_slots = self.inner.nCodeSlots * hash_size;112 const code_slots = self.inner.nCodeSlots * hash_size;
111 const special_slots = self.inner.nSpecialSlots * hash_size;113 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);
113 }115 }
114116
115 fn write(self: CodeDirectory, writer: anytype) !void {117 fn write(self: CodeDirectory, writer: anytype) !void {
...@@ -287,33 +289,7 @@ pub fn writeAdhocSignature(...@@ -287,33 +289,7 @@ pub fn writeAdhocSignature(
287 self.code_directory.inner.nCodeSlots = total_pages;289 self.code_directory.inner.nCodeSlots = total_pages;
288290
289 // Calculate hash for each page (in file) and write it to the buffer291 // Calculate hash for each page (in file) and write it to the buffer
290 var wg: WaitGroup = .{};292 try self.parallelHash(gpa, comp.thread_pool, opts.file, opts.file_size);
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 }
317293
318 try blobs.append(.{ .code_directory = &self.code_directory });294 try blobs.append(.{ .code_directory = &self.code_directory });
319 header.length += @sizeOf(macho.BlobIndex);295 header.length += @sizeOf(macho.BlobIndex);
...@@ -352,7 +328,7 @@ pub fn writeAdhocSignature(...@@ -352,7 +328,7 @@ pub fn writeAdhocSignature(
352 }328 }
353329
354 self.code_directory.inner.hashOffset =330 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);
356 self.code_directory.inner.length = self.code_directory.size();332 self.code_directory.inner.length = self.code_directory.size();
357 header.length += self.code_directory.size();333 header.length += self.code_directory.size();
358334
...@@ -372,17 +348,60 @@ pub fn writeAdhocSignature(...@@ -372,17 +348,60 @@ pub fn writeAdhocSignature(
372 }348 }
373}349}
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(
376 file: fs.File,395 file: fs.File,
377 fstart: usize,396 fstart: usize,
378 buffer: []u8,397 buffer: []u8,
379 hash: *[hash_size]u8,398 out: *[hash_size]u8,
380 err: *fs.File.PReadError!usize,399 err: *fs.File.PReadError!usize,
381 wg: *WaitGroup,400 wg: *WaitGroup,
382) void {401) void {
383 defer wg.finish();402 defer wg.finish();
384 err.* = file.preadAll(buffer, fstart);403 err.* = file.preadAll(buffer, fstart);
385 Sha256.hash(buffer, hash, .{});404 Sha256.hash(buffer, out, .{});
386}405}
387406
388pub fn size(self: CodeSignature) u32 {407pub fn size(self: CodeSignature) u32 {
src/link/MachO/DebugSymbols.zig+29-53
...@@ -5,6 +5,7 @@ const build_options = @import("build_options");...@@ -5,6 +5,7 @@ const build_options = @import("build_options");
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const fs = std.fs;6const fs = std.fs;
7const link = @import("../../link.zig");7const link = @import("../../link.zig");
8const load_commands = @import("load_commands.zig");
8const log = std.log.scoped(.dsym);9const log = std.log.scoped(.dsym);
9const macho = std.macho;10const macho = std.macho;
10const makeStaticString = MachO.makeStaticString;11const makeStaticString = MachO.makeStaticString;
...@@ -25,6 +26,8 @@ dwarf: Dwarf,...@@ -25,6 +26,8 @@ dwarf: Dwarf,
25file: fs.File,26file: fs.File,
26page_size: u16,27page_size: u16,
2728
29symtab_cmd: macho.symtab_command = .{},
30
28segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},31segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
29sections: std.ArrayListUnmanaged(macho.section_64) = .{},32sections: std.ArrayListUnmanaged(macho.section_64) = .{},
3033
...@@ -295,31 +298,21 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {...@@ -295,31 +298,21 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
295 }298 }
296 }299 }
297300
301 self.finalizeDwarfSegment(macho_file);
302 try self.writeLinkeditSegmentData(macho_file);
303
304 // Write load commands
298 var lc_buffer = std.ArrayList(u8).init(self.allocator);305 var lc_buffer = std.ArrayList(u8).init(self.allocator);
299 defer lc_buffer.deinit();306 defer lc_buffer.deinit();
300 const lc_writer = lc_buffer.writer();307 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));309 try self.writeSegmentHeaders(macho_file, lc_writer);
316 try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);310 try lc_writer.writeStruct(self.symtab_cmd);
311 try lc_writer.writeStruct(macho_file.uuid_cmd);
317312
318 try self.writeHeader(313 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
319 macho_file,314 try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
320 ncmds,315 try self.writeHeader(macho_file, ncmds, @intCast(u32, lc_buffer.items.len));
321 @intCast(u32, lc_buffer.items.len + headers_buf.items.len),
322 );
323316
324 assert(!self.debug_abbrev_section_dirty);317 assert(!self.debug_abbrev_section_dirty);
325 assert(!self.debug_aranges_section_dirty);318 assert(!self.debug_aranges_section_dirty);
...@@ -386,7 +379,7 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {...@@ -386,7 +379,7 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {
386 log.debug("found __LINKEDIT segment free space at 0x{x}", .{linkedit.fileoff});379 log.debug("found __LINKEDIT segment free space at 0x{x}", .{linkedit.fileoff});
387}380}
388381
389fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, writer: anytype) !void {382fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, writer: anytype) !void {
390 // Write segment/section headers from the binary file first.383 // Write segment/section headers from the binary file first.
391 const end = macho_file.linkedit_segment_cmd_index.?;384 const end = macho_file.linkedit_segment_cmd_index.?;
392 for (macho_file.segments.items[0..end]) |seg, i| {385 for (macho_file.segments.items[0..end]) |seg, i| {
...@@ -416,8 +409,6 @@ fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, wri...@@ -416,8 +409,6 @@ fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, wri
416 out_header.offset = 0;409 out_header.offset = 0;
417 try writer.writeStruct(out_header);410 try writer.writeStruct(out_header);
418 }411 }
419
420 ncmds.* += 1;
421 }412 }
422 // Next, commit DSYM's __LINKEDIT and __DWARF segments headers.413 // Next, commit DSYM's __LINKEDIT and __DWARF segments headers.
423 for (self.segments.items) |seg, i| {414 for (self.segments.items) |seg, i| {
...@@ -426,7 +417,6 @@ fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, wri...@@ -426,7 +417,6 @@ fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, wri
426 for (self.sections.items[indexes.start..indexes.end]) |header| {417 for (self.sections.items[indexes.start..indexes.end]) |header| {
427 try writer.writeStruct(header);418 try writer.writeStruct(header);
428 }419 }
429 ncmds.* += 1;
430 }420 }
431}421}
432422
...@@ -465,33 +455,19 @@ fn allocatedSize(self: *DebugSymbols, start: u64) u64 {...@@ -465,33 +455,19 @@ fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
465 return min_pos - start;455 return min_pos - start;
466}456}
467457
468fn writeLinkeditSegmentData(458fn writeLinkeditSegmentData(self: *DebugSymbols, macho_file: *MachO) !void {
469 self: *DebugSymbols,
470 macho_file: *MachO,
471 ncmds: *u32,
472 lc_writer: anytype,
473) !void {
474 const tracy = trace(@src());459 const tracy = trace(@src());
475 defer tracy.end();460 defer tracy.end();
476461
477 var symtab_cmd = macho.symtab_command{462 try self.writeSymtab(macho_file);
478 .cmdsize = @sizeOf(macho.symtab_command),463 try self.writeStrtab();
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;
488464
489 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];465 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
490 const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);466 const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
491 seg.vmsize = aligned_size;467 seg.vmsize = aligned_size;
492}468}
493469
494fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_command) !void {470fn writeSymtab(self: *DebugSymbols, macho_file: *MachO) !void {
495 const tracy = trace(@src());471 const tracy = trace(@src());
496 defer tracy.end();472 defer tracy.end();
497473
...@@ -530,10 +506,10 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_comman...@@ -530,10 +506,10 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_comman
530 const needed_size = nsyms * @sizeOf(macho.nlist_64);506 const needed_size = nsyms * @sizeOf(macho.nlist_64);
531 seg.filesize = offset + needed_size - seg.fileoff;507 seg.filesize = offset + needed_size - seg.fileoff;
532508
533 lc.symoff = @intCast(u32, offset);509 self.symtab_cmd.symoff = @intCast(u32, offset);
534 lc.nsyms = @intCast(u32, nsyms);510 self.symtab_cmd.nsyms = @intCast(u32, nsyms);
535511
536 const locals_off = lc.symoff;512 const locals_off = @intCast(u32, offset);
537 const locals_size = nlocals * @sizeOf(macho.nlist_64);513 const locals_size = nlocals * @sizeOf(macho.nlist_64);
538 const exports_off = locals_off + locals_size;514 const exports_off = locals_off + locals_size;
539 const exports_size = nexports * @sizeOf(macho.nlist_64);515 const exports_size = nexports * @sizeOf(macho.nlist_64);
...@@ -545,26 +521,26 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_comman...@@ -545,26 +521,26 @@ fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_comman
545 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);521 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
546}522}
547523
548fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {524fn writeStrtab(self: *DebugSymbols) !void {
549 const tracy = trace(@src());525 const tracy = trace(@src());
550 defer tracy.end();526 defer tracy.end();
551527
552 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];528 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
553 const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));529 const symtab_size = @intCast(u32, self.symtab_cmd.nsyms * @sizeOf(macho.nlist_64));
554 const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));530 const offset = mem.alignForwardGeneric(u64, self.symtab_cmd.symoff + symtab_size, @alignOf(u64));
555 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));531 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));
556532
557 seg.filesize = offset + needed_size - seg.fileoff;533 seg.filesize = offset + needed_size - seg.fileoff;
558 lc.stroff = @intCast(u32, offset);534 self.symtab_cmd.stroff = @intCast(u32, offset);
559 lc.strsize = @intCast(u32, needed_size);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
565 if (self.strtab.buffer.items.len < needed_size) {541 if (self.strtab.buffer.items.len < needed_size) {
566 // Ensure we are always padded to the actual length of the file.542 // 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);
568 }544 }
569}545}
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");...@@ -13,6 +13,7 @@ const bind = @import("bind.zig");
13const dead_strip = @import("dead_strip.zig");13const dead_strip = @import("dead_strip.zig");
14const fat = @import("fat.zig");14const fat = @import("fat.zig");
15const link = @import("../../link.zig");15const link = @import("../../link.zig");
16const load_commands = @import("load_commands.zig");
16const thunks = @import("thunks.zig");17const thunks = @import("thunks.zig");
17const trace = @import("../../tracy.zig").trace;18const trace = @import("../../tracy.zig").trace;
1819
...@@ -25,6 +26,7 @@ const Compilation = @import("../../Compilation.zig");...@@ -25,6 +26,7 @@ const Compilation = @import("../../Compilation.zig");
25const DwarfInfo = @import("DwarfInfo.zig");26const DwarfInfo = @import("DwarfInfo.zig");
26const Dylib = @import("Dylib.zig");27const Dylib = @import("Dylib.zig");
27const MachO = @import("../MachO.zig");28const MachO = @import("../MachO.zig");
29const Md5 = std.crypto.hash.Md5;
28const LibStub = @import("../tapi.zig").LibStub;30const LibStub = @import("../tapi.zig").LibStub;
29const Object = @import("Object.zig");31const Object = @import("Object.zig");
30const StringTable = @import("../strtab.zig").StringTable;32const StringTable = @import("../strtab.zig").StringTable;
...@@ -34,7 +36,17 @@ pub const Zld = struct {...@@ -34,7 +36,17 @@ pub const Zld = struct {
34 gpa: Allocator,36 gpa: Allocator,
35 file: fs.File,37 file: fs.File,
36 page_size: u16,38 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
39 objects: std.ArrayListUnmanaged(Object) = .{},51 objects: std.ArrayListUnmanaged(Object) = .{},
40 archives: std.ArrayListUnmanaged(Archive) = .{},52 archives: std.ArrayListUnmanaged(Archive) = .{},
...@@ -1227,195 +1239,6 @@ pub const Zld = struct {...@@ -1227,195 +1239,6 @@ pub const Zld = struct {
1227 }1239 }
1228 }1240 }
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
1419 pub fn deinit(self: *Zld) void {1242 pub fn deinit(self: *Zld) void {
1420 const gpa = self.gpa;1243 const gpa = self.gpa;
14211244
...@@ -1516,110 +1339,6 @@ pub const Zld = struct {...@@ -1516,110 +1339,6 @@ pub const Zld = struct {
1516 }1339 }
1517 }1340 }
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
1623 pub fn allocateSymbol(self: *Zld) !u32 {1342 pub fn allocateSymbol(self: *Zld) !u32 {
1624 try self.locals.ensureUnusedCapacity(self.gpa, 1);1343 try self.locals.ensureUnusedCapacity(self.gpa, 1);
1625 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});1344 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
...@@ -1842,7 +1561,11 @@ pub const Zld = struct {...@@ -1842,7 +1561,11 @@ pub const Zld = struct {
1842 fn allocateSegments(self: *Zld) !void {1561 fn allocateSegments(self: *Zld) !void {
1843 for (self.segments.items) |*segment, segment_index| {1562 for (self.segments.items) |*segment, segment_index| {
1844 const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");1563 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;
1846 try self.allocateSegment(@intCast(u8, segment_index), base_size);1569 try self.allocateSegment(@intCast(u8, segment_index), base_size);
1847 }1570 }
1848 }1571 }
...@@ -2015,7 +1738,7 @@ pub const Zld = struct {...@@ -2015,7 +1738,7 @@ pub const Zld = struct {
2015 return (@intCast(u8, segment_precedence) << 4) + section_precedence;1738 return (@intCast(u8, segment_precedence) << 4) + section_precedence;
2016 }1739 }
20171740
2018 fn writeSegmentHeaders(self: *Zld, ncmds: *u32, writer: anytype) !void {1741 fn writeSegmentHeaders(self: *Zld, writer: anytype) !void {
2019 for (self.segments.items) |seg, i| {1742 for (self.segments.items) |seg, i| {
2020 const indexes = self.getSectionIndexes(@intCast(u8, i));1743 const indexes = self.getSectionIndexes(@intCast(u8, i));
2021 var out_seg = seg;1744 var out_seg = seg;
...@@ -2039,16 +1762,14 @@ pub const Zld = struct {...@@ -2039,16 +1762,14 @@ pub const Zld = struct {
2039 if (header.size == 0) continue;1762 if (header.size == 0) continue;
2040 try writer.writeStruct(header);1763 try writer.writeStruct(header);
2041 }1764 }
2042
2043 ncmds.* += 1;
2044 }1765 }
2045 }1766 }
20461767
2047 fn writeLinkeditSegmentData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void {1768 fn writeLinkeditSegmentData(self: *Zld, reverse_lookups: [][]u32) !void {
2048 try self.writeDyldInfoData(ncmds, lc_writer, reverse_lookups);1769 try self.writeDyldInfoData(reverse_lookups);
2049 try self.writeFunctionStarts(ncmds, lc_writer);1770 try self.writeFunctionStarts();
2050 try self.writeDataInCode(ncmds, lc_writer);1771 try self.writeDataInCode();
2051 try self.writeSymtabs(ncmds, lc_writer);1772 try self.writeSymtabs();
20521773
2053 const seg = self.getLinkeditSegmentPtr();1774 const seg = self.getLinkeditSegmentPtr();
2054 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);1775 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
...@@ -2437,7 +2158,7 @@ pub const Zld = struct {...@@ -2437,7 +2158,7 @@ pub const Zld = struct {
2437 try trie.finalize(gpa);2158 try trie.finalize(gpa);
2438 }2159 }
24392160
2440 fn writeDyldInfoData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void {2161 fn writeDyldInfoData(self: *Zld, reverse_lookups: [][]u32) !void {
2441 const gpa = self.gpa;2162 const gpa = self.gpa;
24422163
2443 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);2164 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
...@@ -2506,21 +2227,14 @@ pub const Zld = struct {...@@ -2506,21 +2227,14 @@ pub const Zld = struct {
2506 const size = math.cast(usize, lazy_bind_size) orelse return error.Overflow;2227 const size = math.cast(usize, lazy_bind_size) orelse return error.Overflow;
2507 try self.populateLazyBindOffsetsInStubHelper(buffer[offset..][0..size]);2228 try self.populateLazyBindOffsetsInStubHelper(buffer[offset..][0..size]);
25082229
2509 try lc_writer.writeStruct(macho.dyld_info_command{2230 self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off);
2510 .cmd = .DYLD_INFO_ONLY,2231 self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size);
2511 .cmdsize = @sizeOf(macho.dyld_info_command),2232 self.dyld_info_cmd.bind_off = @intCast(u32, bind_off);
2512 .rebase_off = @intCast(u32, rebase_off),2233 self.dyld_info_cmd.bind_size = @intCast(u32, bind_size);
2513 .rebase_size = @intCast(u32, rebase_size),2234 self.dyld_info_cmd.lazy_bind_off = @intCast(u32, lazy_bind_off);
2514 .bind_off = @intCast(u32, bind_off),2235 self.dyld_info_cmd.lazy_bind_size = @intCast(u32, lazy_bind_size);
2515 .bind_size = @intCast(u32, bind_size),2236 self.dyld_info_cmd.export_off = @intCast(u32, export_off);
2516 .weak_bind_off = 0,2237 self.dyld_info_cmd.export_size = @intCast(u32, export_size);
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;
2524 }2238 }
25252239
2526 fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {2240 fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
...@@ -2638,7 +2352,7 @@ pub const Zld = struct {...@@ -2638,7 +2352,7 @@ pub const Zld = struct {
26382352
2639 const asc_u64 = std.sort.asc(u64);2353 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 {
2642 const text_seg_index = self.getSegmentByName("__TEXT") orelse return;2356 const text_seg_index = self.getSegmentByName("__TEXT") orelse return;
2643 const text_sect_index = self.getSectionByName("__TEXT", "__text") orelse return;2357 const text_sect_index = self.getSectionByName("__TEXT", "__text") orelse return;
2644 const text_seg = self.segments.items[text_seg_index];2358 const text_seg = self.segments.items[text_seg_index];
...@@ -2697,13 +2411,8 @@ pub const Zld = struct {...@@ -2697,13 +2411,8 @@ pub const Zld = struct {
26972411
2698 try self.file.pwriteAll(buffer.items, offset);2412 try self.file.pwriteAll(buffer.items, offset);
26992413
2700 try lc_writer.writeStruct(macho.linkedit_data_command{2414 self.function_starts_cmd.dataoff = @intCast(u32, offset);
2701 .cmd = .FUNCTION_STARTS,2415 self.function_starts_cmd.datasize = @intCast(u32, needed_size);
2702 .cmdsize = @sizeOf(macho.linkedit_data_command),
2703 .dataoff = @intCast(u32, offset),
2704 .datasize = @intCast(u32, needed_size),
2705 });
2706 ncmds.* += 1;
2707 }2416 }
27082417
2709 fn filterDataInCode(2418 fn filterDataInCode(
...@@ -2725,7 +2434,7 @@ pub const Zld = struct {...@@ -2725,7 +2434,7 @@ pub const Zld = struct {
2725 return dices[start..end];2434 return dices[start..end];
2726 }2435 }
27272436
2728 fn writeDataInCode(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {2437 fn writeDataInCode(self: *Zld) !void {
2729 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa);2438 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa);
2730 defer out_dice.deinit();2439 defer out_dice.deinit();
27312440
...@@ -2775,54 +2484,19 @@ pub const Zld = struct {...@@ -2775,54 +2484,19 @@ pub const Zld = struct {
2775 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });2484 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
27762485
2777 try self.file.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);2486 try self.file.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
2778 try lc_writer.writeStruct(macho.linkedit_data_command{2487
2779 .cmd = .DATA_IN_CODE,2488 self.data_in_code_cmd.dataoff = @intCast(u32, offset);
2780 .cmdsize = @sizeOf(macho.linkedit_data_command),2489 self.data_in_code_cmd.datasize = @intCast(u32, needed_size);
2781 .dataoff = @intCast(u32, offset),
2782 .datasize = @intCast(u32, needed_size),
2783 });
2784 ncmds.* += 1;
2785 }2490 }
27862491
2787 fn writeSymtabs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {2492 fn writeSymtabs(self: *Zld) !void {
2788 var symtab_cmd = macho.symtab_command{2493 var ctx = try self.writeSymtab();
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);
2817 defer ctx.imports_table.deinit();2494 defer ctx.imports_table.deinit();
2818 try self.writeDysymtab(ctx, &dysymtab_cmd);2495 try self.writeDysymtab(ctx);
2819 try self.writeStrtab(&symtab_cmd);2496 try self.writeStrtab();
2820 try lc_writer.writeStruct(symtab_cmd);
2821 try lc_writer.writeStruct(dysymtab_cmd);
2822 ncmds.* += 2;
2823 }2497 }
28242498
2825 fn writeSymtab(self: *Zld, lc: *macho.symtab_command) !SymtabCtx {2499 fn writeSymtab(self: *Zld) !SymtabCtx {
2826 const gpa = self.gpa;2500 const gpa = self.gpa;
28272501
2828 var locals = std.ArrayList(macho.nlist_64).init(gpa);2502 var locals = std.ArrayList(macho.nlist_64).init(gpa);
...@@ -2843,12 +2517,6 @@ pub const Zld = struct {...@@ -2843,12 +2517,6 @@ pub const Zld = struct {
2843 }2517 }
2844 }2518 }
28452519
2846 if (!self.options.strip) {
2847 for (self.objects.items) |object| {
2848 try self.generateSymbolStabs(object, &locals);
2849 }
2850 }
2851
2852 var exports = std.ArrayList(macho.nlist_64).init(gpa);2520 var exports = std.ArrayList(macho.nlist_64).init(gpa);
2853 defer exports.deinit();2521 defer exports.deinit();
28542522
...@@ -2879,6 +2547,14 @@ pub const Zld = struct {...@@ -2879,6 +2547,14 @@ pub const Zld = struct {
2879 try imports_table.putNoClobber(global, new_index);2547 try imports_table.putNoClobber(global, new_index);
2880 }2548 }
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
2882 const nlocals = @intCast(u32, locals.items.len);2558 const nlocals = @intCast(u32, locals.items.len);
2883 const nexports = @intCast(u32, exports.items.len);2559 const nexports = @intCast(u32, exports.items.len);
2884 const nimports = @intCast(u32, imports.items.len);2560 const nimports = @intCast(u32, imports.items.len);
...@@ -2903,8 +2579,8 @@ pub const Zld = struct {...@@ -2903,8 +2579,8 @@ pub const Zld = struct {
2903 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });2579 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2904 try self.file.pwriteAll(buffer.items, offset);2580 try self.file.pwriteAll(buffer.items, offset);
29052581
2906 lc.symoff = @intCast(u32, offset);2582 self.symtab_cmd.symoff = @intCast(u32, offset);
2907 lc.nsyms = nsyms;2583 self.symtab_cmd.nsyms = nsyms;
29082584
2909 return SymtabCtx{2585 return SymtabCtx{
2910 .nlocalsym = nlocals,2586 .nlocalsym = nlocals,
...@@ -2914,7 +2590,7 @@ pub const Zld = struct {...@@ -2914,7 +2590,7 @@ pub const Zld = struct {
2914 };2590 };
2915 }2591 }
29162592
2917 fn writeStrtab(self: *Zld, lc: *macho.symtab_command) !void {2593 fn writeStrtab(self: *Zld) !void {
2918 const seg = self.getLinkeditSegmentPtr();2594 const seg = self.getLinkeditSegmentPtr();
2919 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));2595 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
2920 const needed_size = self.strtab.buffer.items.len;2596 const needed_size = self.strtab.buffer.items.len;
...@@ -2924,8 +2600,8 @@ pub const Zld = struct {...@@ -2924,8 +2600,8 @@ pub const Zld = struct {
29242600
2925 try self.file.pwriteAll(self.strtab.buffer.items, offset);2601 try self.file.pwriteAll(self.strtab.buffer.items, offset);
29262602
2927 lc.stroff = @intCast(u32, offset);2603 self.symtab_cmd.stroff = @intCast(u32, offset);
2928 lc.strsize = @intCast(u32, needed_size);2604 self.symtab_cmd.strsize = @intCast(u32, needed_size);
2929 }2605 }
29302606
2931 const SymtabCtx = struct {2607 const SymtabCtx = struct {
...@@ -2935,7 +2611,7 @@ pub const Zld = struct {...@@ -2935,7 +2611,7 @@ pub const Zld = struct {
2935 imports_table: std.AutoHashMap(SymbolWithLoc, u32),2611 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
2936 };2612 };
29372613
2938 fn writeDysymtab(self: *Zld, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {2614 fn writeDysymtab(self: *Zld, ctx: SymtabCtx) !void {
2939 const gpa = self.gpa;2615 const gpa = self.gpa;
2940 const nstubs = @intCast(u32, self.stubs.items.len);2616 const nstubs = @intCast(u32, self.stubs.items.len);
2941 const ngot_entries = @intCast(u32, self.got_entries.items.len);2617 const ngot_entries = @intCast(u32, self.got_entries.items.len);
...@@ -2991,21 +2667,161 @@ pub const Zld = struct {...@@ -2991,21 +2667,161 @@ pub const Zld = struct {
2991 assert(buf.items.len == needed_size);2667 assert(buf.items.len == needed_size);
2992 try self.file.pwriteAll(buf.items, offset);2668 try self.file.pwriteAll(buf.items, offset);
29932669
2994 lc.nlocalsym = ctx.nlocalsym;2670 self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;
2995 lc.iextdefsym = iextdefsym;2671 self.dysymtab_cmd.iextdefsym = iextdefsym;
2996 lc.nextdefsym = ctx.nextdefsym;2672 self.dysymtab_cmd.nextdefsym = ctx.nextdefsym;
2997 lc.iundefsym = iundefsym;2673 self.dysymtab_cmd.iundefsym = iundefsym;
2998 lc.nundefsym = ctx.nundefsym;2674 self.dysymtab_cmd.nundefsym = ctx.nundefsym;
2999 lc.indirectsymoff = @intCast(u32, offset);2675 self.dysymtab_cmd.indirectsymoff = @intCast(u32, offset);
3000 lc.nindirectsyms = nindirectsyms;2676 self.dysymtab_cmd.nindirectsyms = nindirectsyms;
3001 }2677 }
30022678
3003 fn writeCodeSignaturePadding(2679 fn writeUuid(self: *Zld, comp: *const Compilation, args: struct {
3004 self: *Zld,2680 linkedit_cmd_offset: u32,
3005 code_sig: *CodeSignature,2681 symtab_cmd_offset: u32,
3006 ncmds: *u32,2682 uuid_cmd_offset: u32,
3007 lc_writer: anytype,2683 codesig_cmd_offset: ?u32,
3008 ) !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 {
3009 const seg = self.getLinkeditSegmentPtr();2825 const seg = self.getLinkeditSegmentPtr();
3010 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file2826 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
3011 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L2712827 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
...@@ -3018,23 +2834,11 @@ pub const Zld = struct {...@@ -3018,23 +2834,11 @@ pub const Zld = struct {
3018 // except for code signature data.2834 // except for code signature data.
3019 try self.file.pwriteAll(&[_]u8{0}, offset + needed_size - 1);2835 try self.file.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
30202836
3021 try lc_writer.writeStruct(macho.linkedit_data_command{2837 self.codesig_cmd.dataoff = @intCast(u32, offset);
3022 .cmd = .CODE_SIGNATURE,2838 self.codesig_cmd.datasize = @intCast(u32, needed_size);
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);
3030 }2839 }
30312840
3032 fn writeCodeSignature(2841 fn writeCodeSignature(self: *Zld, comp: *const Compilation, code_sig: *CodeSignature) !void {
3033 self: *Zld,
3034 comp: *const Compilation,
3035 code_sig: *CodeSignature,
3036 offset: u32,
3037 ) !void {
3038 const seg_id = self.getSegmentByName("__TEXT").?;2842 const seg_id = self.getSegmentByName("__TEXT").?;
3039 const seg = self.segments.items[seg_id];2843 const seg = self.segments.items[seg_id];
30402844
...@@ -3045,17 +2849,17 @@ pub const Zld = struct {...@@ -3045,17 +2849,17 @@ pub const Zld = struct {
3045 .file = self.file,2849 .file = self.file,
3046 .exec_seg_base = seg.fileoff,2850 .exec_seg_base = seg.fileoff,
3047 .exec_seg_limit = seg.filesize,2851 .exec_seg_limit = seg.filesize,
3048 .file_size = offset,2852 .file_size = self.codesig_cmd.dataoff,
3049 .output_mode = self.options.output_mode,2853 .output_mode = self.options.output_mode,
3050 }, buffer.writer());2854 }, buffer.writer());
3051 assert(buffer.items.len == code_sig.size());2855 assert(buffer.items.len == code_sig.size());
30522856
3053 log.debug("writing code signature from 0x{x} to 0x{x}", .{2857 log.debug("writing code signature from 0x{x} to 0x{x}", .{
3054 offset,2858 self.codesig_cmd.dataoff,
3055 offset + buffer.items.len,2859 self.codesig_cmd.dataoff + buffer.items.len,
3056 });2860 });
30572861
3058 try self.file.pwriteAll(buffer.items, offset);2862 try self.file.pwriteAll(buffer.items, self.codesig_cmd.dataoff);
3059 }2863 }
30602864
3061 /// Writes Mach-O file header.2865 /// Writes Mach-O file header.
...@@ -3734,7 +3538,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3734,7 +3538,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3734 defer tracy.end();3538 defer tracy.end();
37353539
3736 const gpa = macho_file.base.allocator;3540 const gpa = macho_file.base.allocator;
3737 const options = macho_file.base.options;3541 const options = &macho_file.base.options;
3738 const target = options.target;3542 const target = options.target;
37393543
3740 var arena_allocator = std.heap.ArenaAllocator.init(gpa);3544 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
...@@ -3884,7 +3688,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3884,7 +3688,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3884 macho_file.base.file = try directory.handle.createFile(sub_path, .{3688 macho_file.base.file = try directory.handle.createFile(sub_path, .{
3885 .truncate = true,3689 .truncate = true,
3886 .read = true,3690 .read = true,
3887 .mode = link.determineMode(options),3691 .mode = link.determineMode(options.*),
3888 });3692 });
3889 }3693 }
3890 var zld = Zld{3694 var zld = Zld{
...@@ -4271,12 +4075,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -4271,12 +4075,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4271 }4075 }
42724076
4273 try zld.writeAtoms(reverse_lookups);4077 try zld.writeAtoms(reverse_lookups);
42744078 try zld.writeLinkeditSegmentData(reverse_lookups);
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);
42804079
4281 // If the last section of __DATA segment is zerofill section, we need to ensure4080 // If the last section of __DATA segment is zerofill section, we need to ensure
4282 // that the free space between the end of the last non-zerofill section of __DATA4081 // 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...@@ -4301,39 +4100,54 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4301 }4100 }
4302 }4101 }
43034102
4304 try Zld.writeDylinkerLC(&ncmds, lc_writer);4103 // Write load commands
4305 try zld.writeMainLC(&ncmds, lc_writer);4104 var lc_buffer = std.ArrayList(u8).init(arena);
4306 try zld.writeDylibIdLC(&ncmds, lc_writer);4105 const lc_writer = lc_buffer.writer();
4307 try zld.writeRpathLCs(&ncmds, lc_writer);
43084106
4309 {4107 try zld.writeSegmentHeaders(lc_writer);
4310 try lc_writer.writeStruct(macho.source_version_command{4108 const linkedit_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len - @sizeOf(macho.segment_command_64));
4311 .cmdsize = @sizeOf(macho.source_version_command),4109
4312 .version = 0x0,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,
4313 });4128 });
4314 ncmds += 1;4129 } else {
4130 assert(zld.options.output_mode == .Lib);
4131 try load_commands.writeDylibIdLC(zld.gpa, zld.options, lc_writer);
4315 }4132 }
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 {4140 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @intCast(u32, lc_buffer.items.len);
4320 var uuid_lc = macho.uuid_command{4141 try lc_writer.writeStruct(zld.uuid_cmd);
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 }
43284142
4329 try zld.writeLoadDylibLCs(&ncmds, lc_writer);4143 try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), lc_writer);
43304144
4331 const requires_codesig = blk: {4145 const requires_codesig = blk: {
4332 if (options.entitlements) |_| break :blk true;4146 if (options.entitlements) |_| break :blk true;
4333 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;4147 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
4334 break :blk false;4148 break :blk false;
4335 };4149 };
4336 var codesig_offset: ?u32 = null;4150 var codesig_cmd_offset: ?u32 = null;
4337 var codesig: ?CodeSignature = if (requires_codesig) blk: {4151 var codesig: ?CodeSignature = if (requires_codesig) blk: {
4338 // Preallocate space for the code signature.4152 // Preallocate space for the code signature.
4339 // We need to do this at this stage so that we have the load commands with proper values4153 // 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...@@ -4341,24 +4155,30 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4341 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment4155 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
4342 // where the code signature goes into.4156 // where the code signature goes into.
4343 var codesig = CodeSignature.init(page_size);4157 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);
4345 if (options.entitlements) |path| {4159 if (options.entitlements) |path| {
4346 try codesig.addEntitlements(gpa, path);4160 try codesig.addEntitlements(gpa, path);
4347 }4161 }
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);
4349 break :blk codesig;4165 break :blk codesig;
4350 } else null;4166 } else null;
4351 defer if (codesig) |*csig| csig.deinit(gpa);4167 defer if (codesig) |*csig| csig.deinit(gpa);
43524168
4353 var headers_buf = std.ArrayList(u8).init(arena);4169 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
4354 try zld.writeSegmentHeaders(&ncmds, headers_buf.writer());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));4173 try zld.writeUuid(comp, .{
4357 try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);4174 .linkedit_cmd_offset = linkedit_cmd_offset,
4358 try zld.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));4175 .symtab_cmd_offset = symtab_cmd_offset,
4176 .uuid_cmd_offset = uuid_cmd_offset,
4177 .codesig_cmd_offset = codesig_cmd_offset,
4178 });
43594179
4360 if (codesig) |*csig| {4180 if (codesig) |*csig| {
4361 try zld.writeCodeSignature(comp, csig, codesig_offset.?); // code signing always comes last4181 try zld.writeCodeSignature(comp, csig); // code signing always comes last
4362 }4182 }
4363 }4183 }
43644184
test/link.zig+5
...@@ -170,6 +170,11 @@ fn addMachOCases(cases: *tests.StandaloneContext) void {...@@ -170,6 +170,11 @@ fn addMachOCases(cases: *tests.StandaloneContext) void {
170 .requires_symlinks = true,170 .requires_symlinks = true,
171 });171 });
172172
173 cases.addBuildFile("test/link/macho/uuid/build.zig", .{
174 .build_modes = false,
175 .requires_symlinks = true,
176 });
177
173 cases.addBuildFile("test/link/macho/weak_library/build.zig", .{178 cases.addBuildFile("test/link/macho/weak_library/build.zig", .{
174 .build_modes = true,179 .build_modes = true,
175 .requires_symlinks = true,180 .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