master
1//! Here we test our MachO linker for correctness and functionality.
2
3pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
4 const macho_step = b.step("test-macho", "Run MachO tests");
5
6 // https://github.com/ziglang/zig/issues/25323
7 if (builtin.os.tag == .freebsd) return macho_step;
8
9 // https://github.com/ziglang/zig/issues/25961
10 if (comptime builtin.cpu.arch.endian() == .big) return macho_step;
11
12 const x86_64_target = b.resolveTargetQuery(.{
13 .cpu_arch = .x86_64,
14 .os_tag = .macos,
15 });
16 const aarch64_target = b.resolveTargetQuery(.{
17 .cpu_arch = .aarch64,
18 .os_tag = .macos,
19 });
20
21 const default_target = switch (builtin.cpu.arch) {
22 .x86_64, .aarch64 => b.resolveTargetQuery(.{
23 .os_tag = .macos,
24 }),
25 else => aarch64_target,
26 };
27
28 // Exercise linker with self-hosted backend (no LLVM)
29 macho_step.dependOn(testEmptyZig(b, .{ .use_llvm = false, .target = x86_64_target }));
30 macho_step.dependOn(testHelloZig(b, .{ .use_llvm = false, .target = x86_64_target }));
31 macho_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = x86_64_target }));
32 macho_step.dependOn(testReexportsZig(b, .{ .use_llvm = false, .target = x86_64_target }));
33 macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .target = x86_64_target }));
34 macho_step.dependOn(testTlsZig(b, .{ .use_llvm = false, .target = x86_64_target }));
35 macho_step.dependOn(testUnresolvedError(b, .{ .use_llvm = false, .target = x86_64_target }));
36
37 // Exercise linker with LLVM backend
38 macho_step.dependOn(testDeadStrip(b, .{ .target = default_target }));
39 macho_step.dependOn(testDuplicateDefinitions(b, .{ .target = default_target }));
40 macho_step.dependOn(testEmptyObject(b, .{ .target = default_target }));
41 macho_step.dependOn(testEmptyZig(b, .{ .target = default_target }));
42 macho_step.dependOn(testEntryPoint(b, .{ .target = default_target }));
43 macho_step.dependOn(testHeaderWeakFlags(b, .{ .target = default_target }));
44 macho_step.dependOn(testHelloC(b, .{ .target = default_target }));
45 macho_step.dependOn(testHelloZig(b, .{ .target = default_target }));
46 macho_step.dependOn(testLargeBss(b, .{ .target = default_target }));
47 macho_step.dependOn(testLayout(b, .{ .target = default_target }));
48 macho_step.dependOn(testLinkingStaticLib(b, .{ .target = default_target }));
49 macho_step.dependOn(testLinksection(b, .{ .target = default_target }));
50 macho_step.dependOn(testMergeLiteralsX64(b, .{ .target = x86_64_target }));
51 macho_step.dependOn(testMergeLiteralsArm64(b, .{ .target = aarch64_target }));
52 macho_step.dependOn(testMergeLiteralsArm642(b, .{ .target = aarch64_target }));
53 macho_step.dependOn(testMergeLiteralsAlignment(b, .{ .target = aarch64_target }));
54 macho_step.dependOn(testMhExecuteHeader(b, .{ .target = default_target }));
55 macho_step.dependOn(testNoDeadStrip(b, .{ .target = default_target }));
56 macho_step.dependOn(testNoExportsDylib(b, .{ .target = default_target }));
57 macho_step.dependOn(testPagezeroSize(b, .{ .target = default_target }));
58 macho_step.dependOn(testReexportsZig(b, .{ .target = default_target }));
59 macho_step.dependOn(testRelocatable(b, .{ .target = default_target }));
60 macho_step.dependOn(testRelocatableZig(b, .{ .target = default_target }));
61 macho_step.dependOn(testSectionBoundarySymbols(b, .{ .target = default_target }));
62 macho_step.dependOn(testSectionBoundarySymbols2(b, .{ .target = default_target }));
63 macho_step.dependOn(testSegmentBoundarySymbols(b, .{ .target = default_target }));
64 macho_step.dependOn(testSymbolStabs(b, .{ .target = default_target }));
65 macho_step.dependOn(testStackSize(b, .{ .target = default_target }));
66 macho_step.dependOn(testTentative(b, .{ .target = default_target }));
67 macho_step.dependOn(testThunks(b, .{ .target = aarch64_target }));
68 macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target }));
69 macho_step.dependOn(testTlsZig(b, .{ .target = default_target }));
70 macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target }));
71 macho_step.dependOn(testDiscardLocalSymbols(b, .{ .target = default_target }));
72 macho_step.dependOn(testUnresolvedError(b, .{ .target = default_target }));
73 macho_step.dependOn(testUnresolvedError2(b, .{ .target = default_target }));
74 macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target }));
75 macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target }));
76 macho_step.dependOn(testUnwindInfoNoSubsectionsArm64(b, .{ .target = aarch64_target }));
77 macho_step.dependOn(testWeakBind(b, .{ .target = x86_64_target }));
78 macho_step.dependOn(testWeakRef(b, .{ .target = b.resolveTargetQuery(.{
79 .cpu_arch = .x86_64,
80 .os_tag = .macos,
81 .os_version_min = .{ .semver = .{ .major = 10, .minor = 13, .patch = 0 } },
82 }) }));
83
84 // Tests requiring symlinks
85 if (build_opts.has_symlinks) {
86 macho_step.dependOn(testEntryPointArchive(b, .{ .target = default_target }));
87 macho_step.dependOn(testEntryPointDylib(b, .{ .target = default_target }));
88 macho_step.dependOn(testDylib(b, .{ .target = default_target }));
89 macho_step.dependOn(testDylibVersionTbd(b, .{ .target = default_target }));
90 macho_step.dependOn(testNeededLibrary(b, .{ .target = default_target }));
91 macho_step.dependOn(testSearchStrategy(b, .{ .target = default_target }));
92 macho_step.dependOn(testTbdv3(b, .{ .target = default_target }));
93 macho_step.dependOn(testTls(b, .{ .target = default_target }));
94 macho_step.dependOn(testTlsPointers(b, .{ .target = default_target }));
95 macho_step.dependOn(testTwoLevelNamespace(b, .{ .target = default_target }));
96 macho_step.dependOn(testWeakLibrary(b, .{ .target = default_target }));
97
98 // Tests requiring presence of macOS SDK in system path
99 if (build_opts.has_macos_sdk) {
100 macho_step.dependOn(testDeadStripDylibs(b, .{ .target = b.graph.host }));
101 macho_step.dependOn(testHeaderpad(b, .{ .target = b.graph.host }));
102 macho_step.dependOn(testLinkDirectlyCppTbd(b, .{ .target = b.graph.host }));
103 macho_step.dependOn(testMergeLiteralsObjc(b, .{ .target = b.graph.host }));
104 macho_step.dependOn(testNeededFramework(b, .{ .target = b.graph.host }));
105 macho_step.dependOn(testObjc(b, .{ .target = b.graph.host }));
106 macho_step.dependOn(testObjcpp(b, .{ .target = b.graph.host }));
107 macho_step.dependOn(testWeakFramework(b, .{ .target = b.graph.host }));
108 }
109 }
110
111 return macho_step;
112}
113
114fn testDeadStrip(b: *Build, opts: Options) *Step {
115 const test_step = addTestStep(b, "dead-strip", opts);
116
117 const obj = addObject(b, opts, .{ .name = "a", .cpp_source_bytes =
118 \\#include <stdio.h>
119 \\int two() { return 2; }
120 \\int live_var1 = 1;
121 \\int live_var2 = two();
122 \\int dead_var1 = 3;
123 \\int dead_var2 = 4;
124 \\void live_fn1() {}
125 \\void live_fn2() { live_fn1(); }
126 \\void dead_fn1() {}
127 \\void dead_fn2() { dead_fn1(); }
128 \\int main() {
129 \\ printf("%d %d\n", live_var1, live_var2);
130 \\ live_fn2();
131 \\}
132 });
133
134 {
135 const exe = addExecutable(b, opts, .{ .name = "no_dead_strip" });
136 exe.root_module.addObject(obj);
137 exe.link_gc_sections = false;
138
139 const check = exe.checkObject();
140 check.checkInSymtab();
141 check.checkContains("live_var1");
142 check.checkInSymtab();
143 check.checkContains("live_var2");
144 check.checkInSymtab();
145 check.checkContains("dead_var1");
146 check.checkInSymtab();
147 check.checkContains("dead_var2");
148 check.checkInSymtab();
149 check.checkContains("live_fn1");
150 check.checkInSymtab();
151 check.checkContains("live_fn2");
152 check.checkInSymtab();
153 check.checkContains("dead_fn1");
154 check.checkInSymtab();
155 check.checkContains("dead_fn2");
156 test_step.dependOn(&check.step);
157
158 const run = addRunArtifact(exe);
159 run.expectStdOutEqual("1 2\n");
160 test_step.dependOn(&run.step);
161 }
162
163 {
164 const exe = addExecutable(b, opts, .{ .name = "yes_dead_strip" });
165 exe.root_module.addObject(obj);
166 exe.link_gc_sections = true;
167
168 const check = exe.checkObject();
169 check.checkInSymtab();
170 check.checkContains("live_var1");
171 check.checkInSymtab();
172 check.checkContains("live_var2");
173 check.checkInSymtab();
174 check.checkNotPresent("dead_var1");
175 check.checkInSymtab();
176 check.checkNotPresent("dead_var2");
177 check.checkInSymtab();
178 check.checkContains("live_fn1");
179 check.checkInSymtab();
180 check.checkContains("live_fn2");
181 check.checkInSymtab();
182 check.checkNotPresent("dead_fn1");
183 check.checkInSymtab();
184 check.checkNotPresent("dead_fn2");
185 test_step.dependOn(&check.step);
186
187 const run = addRunArtifact(exe);
188 run.expectStdOutEqual("1 2\n");
189 test_step.dependOn(&run.step);
190 }
191
192 return test_step;
193}
194
195fn testDuplicateDefinitions(b: *Build, opts: Options) *Step {
196 const test_step = addTestStep(b, "duplicate-definitions", opts);
197
198 const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
199 \\var x: usize = 1;
200 \\export fn strong() void { x += 1; }
201 \\export fn weak() void { x += 1; }
202 });
203
204 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
205 \\var x: usize = 1;
206 \\export fn strong() void { x += 1; }
207 \\comptime { @export(&weakImpl, .{ .name = "weak", .linkage = .weak }); }
208 \\fn weakImpl() callconv(.c) void { x += 1; }
209 \\extern fn weak() void;
210 \\pub fn main() void {
211 \\ weak();
212 \\ strong();
213 \\}
214 });
215 exe.root_module.addObject(obj);
216
217 expectLinkErrors(exe, test_step, .{ .exact = &.{
218 "error: duplicate symbol definition: _strong",
219 "note: defined by /?/a.o",
220 "note: defined by /?/main_zcu.o",
221 } });
222
223 return test_step;
224}
225
226fn testDeadStripDylibs(b: *Build, opts: Options) *Step {
227 const test_step = addTestStep(b, "dead-strip-dylibs", opts);
228
229 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
230 \\#include <objc/runtime.h>
231 \\int main() {
232 \\ if (objc_getClass("NSObject") == 0) {
233 \\ return -1;
234 \\ }
235 \\ if (objc_getClass("NSApplication") == 0) {
236 \\ return -2;
237 \\ }
238 \\ return 0;
239 \\}
240 });
241
242 {
243 const exe = addExecutable(b, opts, .{ .name = "main1" });
244 exe.root_module.addObject(main_o);
245 exe.root_module.linkFramework("Cocoa", .{});
246
247 const check = exe.checkObject();
248 check.checkInHeaders();
249 check.checkExact("cmd LOAD_DYLIB");
250 check.checkContains("Cocoa");
251 check.checkInHeaders();
252 check.checkExact("cmd LOAD_DYLIB");
253 check.checkContains("libobjc");
254 test_step.dependOn(&check.step);
255
256 const run = addRunArtifact(exe);
257 run.expectExitCode(0);
258 test_step.dependOn(&run.step);
259 }
260
261 {
262 const exe = addExecutable(b, opts, .{ .name = "main2" });
263 exe.root_module.addObject(main_o);
264 exe.root_module.linkFramework("Cocoa", .{});
265 exe.dead_strip_dylibs = true;
266
267 const run = addRunArtifact(exe);
268 run.expectExitCode(@as(u8, @bitCast(@as(i8, -2))));
269 test_step.dependOn(&run.step);
270 }
271
272 return test_step;
273}
274
275fn testDylib(b: *Build, opts: Options) *Step {
276 const test_step = addTestStep(b, "dylib", opts);
277
278 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes =
279 \\#include<stdio.h>
280 \\char world[] = "world";
281 \\char* hello() {
282 \\ return "Hello";
283 \\}
284 });
285
286 const check = dylib.checkObject();
287 check.checkInHeaders();
288 check.checkExact("header");
289 check.checkNotPresent("PIE");
290 test_step.dependOn(&check.step);
291
292 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
293 \\#include<stdio.h>
294 \\char* hello();
295 \\extern char world[];
296 \\int main() {
297 \\ printf("%s %s", hello(), world);
298 \\ return 0;
299 \\}
300 });
301 exe.root_module.linkSystemLibrary("a", .{});
302 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
303 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
304
305 const run = addRunArtifact(exe);
306 run.expectStdOutEqual("Hello world");
307 test_step.dependOn(&run.step);
308
309 return test_step;
310}
311
312fn testDylibVersionTbd(b: *Build, opts: Options) *Step {
313 const test_step = addTestStep(b, "dylib-version-tbd", opts);
314
315 const tbd = tbd: {
316 const wf = WriteFile.create(b);
317 break :tbd wf.add("liba.tbd",
318 \\--- !tapi-tbd
319 \\tbd-version: 4
320 \\targets: [ x86_64-macos, arm64-macos ]
321 \\uuids:
322 \\ - target: x86_64-macos
323 \\ value: DEADBEEF
324 \\ - target: arm64-macos
325 \\ value: BEEFDEAD
326 \\install-name: '@rpath/liba.dylib'
327 \\current-version: 1.2
328 \\exports:
329 \\ - targets: [ x86_64-macos, arm64-macos ]
330 \\ symbols: [ _foo ]
331 );
332 };
333
334 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() {}" });
335 exe.root_module.linkSystemLibrary("a", .{});
336 exe.root_module.addLibraryPath(tbd.dirname());
337
338 const check = exe.checkObject();
339 check.checkInHeaders();
340 check.checkExact("cmd LOAD_DYLIB");
341 check.checkExact("name @rpath/liba.dylib");
342 check.checkExact("current version 10200");
343 test_step.dependOn(&check.step);
344
345 return test_step;
346}
347
348fn testEmptyObject(b: *Build, opts: Options) *Step {
349 const test_step = addTestStep(b, "empty-object", opts);
350
351 const empty = addObject(b, opts, .{ .name = "empty", .c_source_bytes = "" });
352
353 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
354 \\#include <stdio.h>
355 \\int main() {
356 \\ printf("Hello world!");
357 \\}
358 });
359 exe.root_module.addObject(empty);
360
361 const run = addRunArtifact(exe);
362 run.expectStdOutEqual("Hello world!");
363 test_step.dependOn(&run.step);
364
365 return test_step;
366}
367
368fn testEmptyZig(b: *Build, opts: Options) *Step {
369 const test_step = addTestStep(b, "empty-zig", opts);
370
371 const exe = addExecutable(b, opts, .{ .name = "empty", .zig_source_bytes = "pub fn main() void {}" });
372
373 const run = addRunArtifact(exe);
374 run.expectExitCode(0);
375 test_step.dependOn(&run.step);
376
377 return test_step;
378}
379
380fn testEntryPoint(b: *Build, opts: Options) *Step {
381 const test_step = addTestStep(b, "entry-point", opts);
382
383 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
384 \\#include<stdio.h>
385 \\int non_main() {
386 \\ printf("%d", 42);
387 \\ return 0;
388 \\}
389 });
390 exe.entry = .{ .symbol_name = "_non_main" };
391
392 const run = addRunArtifact(exe);
393 run.expectStdOutEqual("42");
394 test_step.dependOn(&run.step);
395
396 const check = exe.checkObject();
397 check.checkInHeaders();
398 check.checkExact("segname __TEXT");
399 check.checkExtract("vmaddr {vmaddr}");
400 check.checkInHeaders();
401 check.checkExact("cmd MAIN");
402 check.checkExtract("entryoff {entryoff}");
403 check.checkInSymtab();
404 check.checkExtract("{n_value} (__TEXT,__text) external _non_main");
405 check.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } });
406 test_step.dependOn(&check.step);
407
408 return test_step;
409}
410
411fn testEntryPointArchive(b: *Build, opts: Options) *Step {
412 const test_step = addTestStep(b, "entry-point-archive", opts);
413
414 const lib = addStaticLibrary(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
415
416 {
417 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "" });
418 exe.root_module.linkSystemLibrary("main", .{});
419 exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
420
421 const run = addRunArtifact(exe);
422 run.expectExitCode(0);
423 test_step.dependOn(&run.step);
424 }
425
426 {
427 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "" });
428 exe.root_module.linkSystemLibrary("main", .{});
429 exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
430 exe.link_gc_sections = true;
431
432 const run = addRunArtifact(exe);
433 run.expectExitCode(0);
434 test_step.dependOn(&run.step);
435 }
436
437 return test_step;
438}
439
440fn testEntryPointDylib(b: *Build, opts: Options) *Step {
441 const test_step = addTestStep(b, "entry-point-dylib", opts);
442
443 const dylib = addSharedLibrary(b, opts, .{ .name = "a" });
444 addCSourceBytes(dylib,
445 \\extern int my_main();
446 \\int bootstrap() {
447 \\ return my_main();
448 \\}
449 , &.{});
450 dylib.linker_allow_shlib_undefined = true;
451
452 const exe = addExecutable(b, opts, .{ .name = "main" });
453 addCSourceBytes(dylib,
454 \\#include<stdio.h>
455 \\int my_main() {
456 \\ fprintf(stdout, "Hello!\n");
457 \\ return 0;
458 \\}
459 , &.{});
460 exe.root_module.linkLibrary(dylib);
461 exe.entry = .{ .symbol_name = "_bootstrap" };
462 exe.forceUndefinedSymbol("_my_main");
463
464 const check = exe.checkObject();
465 check.checkInHeaders();
466 check.checkExact("segname __TEXT");
467 check.checkExtract("vmaddr {text_vmaddr}");
468 check.checkInHeaders();
469 check.checkExact("sectname __stubs");
470 check.checkExtract("addr {stubs_vmaddr}");
471 check.checkInHeaders();
472 check.checkExact("sectname __stubs");
473 check.checkExtract("size {stubs_vmsize}");
474 check.checkInHeaders();
475 check.checkExact("cmd MAIN");
476 check.checkExtract("entryoff {entryoff}");
477 check.checkComputeCompare("text_vmaddr entryoff +", .{
478 .op = .gte,
479 .value = .{ .variable = "stubs_vmaddr" }, // The entrypoint should be a synthetic stub
480 });
481 check.checkComputeCompare("text_vmaddr entryoff + stubs_vmaddr -", .{
482 .op = .lt,
483 .value = .{ .variable = "stubs_vmsize" }, // The entrypoint should be a synthetic stub
484 });
485 test_step.dependOn(&check.step);
486
487 const run = addRunArtifact(exe);
488 run.expectStdOutEqual("Hello!\n");
489 test_step.dependOn(&run.step);
490
491 return test_step;
492}
493
494fn testHeaderpad(b: *Build, opts: Options) *Step {
495 const test_step = addTestStep(b, "headerpad", opts);
496
497 const addExe = struct {
498 fn addExe(bb: *Build, o: Options, name: []const u8) *Compile {
499 const exe = addExecutable(bb, o, .{
500 .name = name,
501 .c_source_bytes = "int main() { return 0; }",
502 });
503 exe.root_module.linkFramework("CoreFoundation", .{});
504 exe.root_module.linkFramework("Foundation", .{});
505 exe.root_module.linkFramework("Cocoa", .{});
506 exe.root_module.linkFramework("CoreGraphics", .{});
507 exe.root_module.linkFramework("CoreHaptics", .{});
508 exe.root_module.linkFramework("CoreAudio", .{});
509 exe.root_module.linkFramework("AVFoundation", .{});
510 exe.root_module.linkFramework("CoreImage", .{});
511 exe.root_module.linkFramework("CoreLocation", .{});
512 exe.root_module.linkFramework("CoreML", .{});
513 exe.root_module.linkFramework("CoreVideo", .{});
514 exe.root_module.linkFramework("CoreText", .{});
515 exe.root_module.linkFramework("CryptoKit", .{});
516 exe.root_module.linkFramework("GameKit", .{});
517 exe.root_module.linkFramework("SwiftUI", .{});
518 exe.root_module.linkFramework("StoreKit", .{});
519 exe.root_module.linkFramework("SpriteKit", .{});
520 return exe;
521 }
522 }.addExe;
523
524 {
525 const exe = addExe(b, opts, "main1");
526 exe.headerpad_max_install_names = true;
527
528 const check = exe.checkObject();
529 check.checkInHeaders();
530 check.checkExact("sectname __text");
531 check.checkExtract("offset {offset}");
532 switch (opts.target.result.cpu.arch) {
533 .aarch64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x4000 } }),
534 .x86_64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x1000 } }),
535 else => unreachable,
536 }
537 test_step.dependOn(&check.step);
538
539 const run = addRunArtifact(exe);
540 run.expectExitCode(0);
541 test_step.dependOn(&run.step);
542 }
543
544 {
545 const exe = addExe(b, opts, "main2");
546 exe.headerpad_size = 0x10000;
547
548 const check = exe.checkObject();
549 check.checkInHeaders();
550 check.checkExact("sectname __text");
551 check.checkExtract("offset {offset}");
552 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });
553 test_step.dependOn(&check.step);
554
555 const run = addRunArtifact(exe);
556 run.expectExitCode(0);
557 test_step.dependOn(&run.step);
558 }
559
560 {
561 const exe = addExe(b, opts, "main3");
562 exe.headerpad_max_install_names = true;
563 exe.headerpad_size = 0x10000;
564
565 const check = exe.checkObject();
566 check.checkInHeaders();
567 check.checkExact("sectname __text");
568 check.checkExtract("offset {offset}");
569 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });
570 test_step.dependOn(&check.step);
571
572 const run = addRunArtifact(exe);
573 run.expectExitCode(0);
574 test_step.dependOn(&run.step);
575 }
576
577 {
578 const exe = addExe(b, opts, "main4");
579 exe.headerpad_max_install_names = true;
580 exe.headerpad_size = 0x1000;
581
582 const check = exe.checkObject();
583 check.checkInHeaders();
584 check.checkExact("sectname __text");
585 check.checkExtract("offset {offset}");
586 switch (opts.target.result.cpu.arch) {
587 .aarch64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x4000 } }),
588 .x86_64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x1000 } }),
589 else => unreachable,
590 }
591 test_step.dependOn(&check.step);
592
593 const run = addRunArtifact(exe);
594 run.expectExitCode(0);
595 test_step.dependOn(&run.step);
596 }
597
598 return test_step;
599}
600
601// Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-header-flags.s
602fn testHeaderWeakFlags(b: *Build, opts: Options) *Step {
603 const test_step = addTestStep(b, "header-weak-flags", opts);
604
605 const obj1 = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
606 \\.globl _x
607 \\.weak_definition _x
608 \\_x:
609 \\ ret
610 });
611
612 const lib = addSharedLibrary(b, opts, .{ .name = "a" });
613 lib.root_module.addObject(obj1);
614
615 {
616 const exe = addExecutable(b, opts, .{ .name = "main1", .c_source_bytes = "int main() { return 0; }" });
617 exe.root_module.addObject(obj1);
618
619 const check = exe.checkObject();
620 check.checkInHeaders();
621 check.checkExact("header");
622 check.checkContains("WEAK_DEFINES");
623 check.checkInHeaders();
624 check.checkExact("header");
625 check.checkContains("BINDS_TO_WEAK");
626 check.checkInExports();
627 check.checkExtract("[WEAK] {vmaddr} _x");
628 test_step.dependOn(&check.step);
629 }
630
631 {
632 const obj = addObject(b, opts, .{ .name = "b" });
633
634 switch (opts.target.result.cpu.arch) {
635 .aarch64 => addAsmSourceBytes(obj,
636 \\.globl _main
637 \\_main:
638 \\ bl _x
639 \\ ret
640 ),
641 .x86_64 => addAsmSourceBytes(obj,
642 \\.globl _main
643 \\_main:
644 \\ callq _x
645 \\ ret
646 ),
647 else => unreachable,
648 }
649
650 const exe = addExecutable(b, opts, .{ .name = "main2" });
651 exe.root_module.linkLibrary(lib);
652 exe.root_module.addObject(obj);
653
654 const check = exe.checkObject();
655 check.checkInHeaders();
656 check.checkExact("header");
657 check.checkNotPresent("WEAK_DEFINES");
658 check.checkInHeaders();
659 check.checkExact("header");
660 check.checkContains("BINDS_TO_WEAK");
661 check.checkInExports();
662 check.checkNotPresent("[WEAK] {vmaddr} _x");
663 test_step.dependOn(&check.step);
664 }
665
666 {
667 const exe = addExecutable(b, opts, .{ .name = "main3", .asm_source_bytes =
668 \\.globl _main, _x
669 \\_x:
670 \\
671 \\_main:
672 \\ ret
673 });
674 exe.root_module.linkLibrary(lib);
675
676 const check = exe.checkObject();
677 check.checkInHeaders();
678 check.checkExact("header");
679 check.checkNotPresent("WEAK_DEFINES");
680 check.checkInHeaders();
681 check.checkExact("header");
682 check.checkNotPresent("BINDS_TO_WEAK");
683 test_step.dependOn(&check.step);
684 }
685
686 return test_step;
687}
688
689fn testHelloC(b: *Build, opts: Options) *Step {
690 const test_step = addTestStep(b, "hello-c", opts);
691
692 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
693 \\#include <stdio.h>
694 \\int main() {
695 \\ printf("Hello world!\n");
696 \\ return 0;
697 \\}
698 });
699
700 const run = addRunArtifact(exe);
701 run.expectStdOutEqual("Hello world!\n");
702 test_step.dependOn(&run.step);
703
704 const check = exe.checkObject();
705 check.checkInHeaders();
706 check.checkExact("header");
707 check.checkContains("PIE");
708 test_step.dependOn(&check.step);
709
710 return test_step;
711}
712
713fn testHelloZig(b: *Build, opts: Options) *Step {
714 const test_step = addTestStep(b, "hello-zig", opts);
715
716 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
717 \\const std = @import("std");
718 \\pub fn main() void {
719 \\ std.fs.File.stdout().writeAll("Hello world!\n") catch @panic("fail");
720 \\}
721 });
722
723 const run = addRunArtifact(exe);
724 run.expectStdOutEqual("Hello world!\n");
725 test_step.dependOn(&run.step);
726
727 return test_step;
728}
729
730fn testLargeBss(b: *Build, opts: Options) *Step {
731 const test_step = addTestStep(b, "large-bss", opts);
732
733 // TODO this test used use a 4GB zerofill section but this actually fails and causes every
734 // linker I tried misbehave in different ways. This only happened on arm64. I thought that
735 // maybe S_GB_ZEROFILL section is an answer to this but it doesn't seem supported by dyld
736 // anymore. When I get some free time I will re-investigate this.
737 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
738 \\char arr[0x1000000];
739 \\int main() {
740 \\ return arr[2000];
741 \\}
742 });
743
744 const run = addRunArtifact(exe);
745 run.expectExitCode(0);
746 test_step.dependOn(&run.step);
747
748 return test_step;
749}
750
751fn testLayout(b: *Build, opts: Options) *Step {
752 const test_step = addTestStep(b, "layout", opts);
753
754 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
755 \\#include <stdio.h>
756 \\int main() {
757 \\ printf("Hello world!");
758 \\ return 0;
759 \\}
760 });
761
762 const check = exe.checkObject();
763 check.checkInHeaders();
764 check.checkExact("cmd SEGMENT_64");
765 check.checkExact("segname __LINKEDIT");
766 check.checkExtract("fileoff {fileoff}");
767 check.checkExtract("filesz {filesz}");
768 check.checkInHeaders();
769 check.checkExact("cmd DYLD_INFO_ONLY");
770 check.checkExtract("rebaseoff {rebaseoff}");
771 check.checkExtract("rebasesize {rebasesize}");
772 check.checkExtract("bindoff {bindoff}");
773 check.checkExtract("bindsize {bindsize}");
774 check.checkExtract("lazybindoff {lazybindoff}");
775 check.checkExtract("lazybindsize {lazybindsize}");
776 check.checkExtract("exportoff {exportoff}");
777 check.checkExtract("exportsize {exportsize}");
778 check.checkInHeaders();
779 check.checkExact("cmd FUNCTION_STARTS");
780 check.checkExtract("dataoff {fstartoff}");
781 check.checkExtract("datasize {fstartsize}");
782 check.checkInHeaders();
783 check.checkExact("cmd DATA_IN_CODE");
784 check.checkExtract("dataoff {diceoff}");
785 check.checkExtract("datasize {dicesize}");
786 check.checkInHeaders();
787 check.checkExact("cmd SYMTAB");
788 check.checkExtract("symoff {symoff}");
789 check.checkExtract("nsyms {symnsyms}");
790 check.checkExtract("stroff {stroff}");
791 check.checkExtract("strsize {strsize}");
792 check.checkInHeaders();
793 check.checkExact("cmd DYSYMTAB");
794 check.checkExtract("indirectsymoff {dysymoff}");
795 check.checkExtract("nindirectsyms {dysymnsyms}");
796
797 switch (opts.target.result.cpu.arch) {
798 .aarch64 => {
799 check.checkInHeaders();
800 check.checkExact("cmd CODE_SIGNATURE");
801 check.checkExtract("dataoff {codesigoff}");
802 check.checkExtract("datasize {codesigsize}");
803 },
804 .x86_64 => {},
805 else => unreachable,
806 }
807
808 // DYLD_INFO_ONLY subsections are in order: rebase < bind < lazy < export,
809 // and there are no gaps between them
810 check.checkComputeCompare("rebaseoff rebasesize +", .{ .op = .eq, .value = .{ .variable = "bindoff" } });
811 check.checkComputeCompare("bindoff bindsize +", .{ .op = .eq, .value = .{ .variable = "lazybindoff" } });
812 check.checkComputeCompare("lazybindoff lazybindsize +", .{ .op = .eq, .value = .{ .variable = "exportoff" } });
813
814 // FUNCTION_STARTS directly follows DYLD_INFO_ONLY (no gap)
815 check.checkComputeCompare("exportoff exportsize +", .{ .op = .eq, .value = .{ .variable = "fstartoff" } });
816
817 // DATA_IN_CODE directly follows FUNCTION_STARTS (no gap)
818 check.checkComputeCompare("fstartoff fstartsize +", .{ .op = .eq, .value = .{ .variable = "diceoff" } });
819
820 // SYMTAB directly follows DATA_IN_CODE (no gap)
821 check.checkComputeCompare("diceoff dicesize +", .{ .op = .eq, .value = .{ .variable = "symoff" } });
822
823 // DYSYMTAB directly follows SYMTAB (no gap)
824 check.checkComputeCompare("symnsyms 16 symoff * +", .{ .op = .eq, .value = .{ .variable = "dysymoff" } });
825
826 // STRTAB follows DYSYMTAB with possible gap
827 check.checkComputeCompare("dysymnsyms 4 dysymoff * +", .{ .op = .lte, .value = .{ .variable = "stroff" } });
828
829 // all LINKEDIT sections apart from CODE_SIGNATURE are 8-bytes aligned
830 check.checkComputeCompare("rebaseoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
831 check.checkComputeCompare("bindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
832 check.checkComputeCompare("lazybindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
833 check.checkComputeCompare("exportoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
834 check.checkComputeCompare("fstartoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
835 check.checkComputeCompare("diceoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
836 check.checkComputeCompare("symoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
837 check.checkComputeCompare("stroff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
838 check.checkComputeCompare("dysymoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
839
840 switch (opts.target.result.cpu.arch) {
841 .aarch64 => {
842 // LINKEDIT segment does not extend beyond, or does not include, CODE_SIGNATURE data
843 check.checkComputeCompare("fileoff filesz codesigoff codesigsize + - -", .{
844 .op = .eq,
845 .value = .{ .literal = 0 },
846 });
847
848 // CODE_SIGNATURE data offset is 16-bytes aligned
849 check.checkComputeCompare("codesigoff 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
850 },
851 .x86_64 => {
852 // LINKEDIT segment does not extend beyond, or does not include, strtab data
853 check.checkComputeCompare("fileoff filesz stroff strsize + - -", .{
854 .op = .eq,
855 .value = .{ .literal = 0 },
856 });
857 },
858 else => unreachable,
859 }
860
861 test_step.dependOn(&check.step);
862
863 const run = addRunArtifact(exe);
864 run.expectStdOutEqual("Hello world!");
865 test_step.dependOn(&run.step);
866
867 return test_step;
868}
869
870fn testLinkDirectlyCppTbd(b: *Build, opts: Options) *Step {
871 const test_step = addTestStep(b, "link-directly-cpp-tbd", opts);
872
873 const sdk = std.zig.system.darwin.getSdk(b.allocator, &opts.target.result) orelse
874 @panic("macOS SDK is required to run the test");
875
876 const exe = addExecutable(b, opts, .{
877 .name = "main",
878 .cpp_source_bytes =
879 \\#include <new>
880 \\#include <cstdio>
881 \\int main() {
882 \\ int *x = new int;
883 \\ *x = 5;
884 \\ fprintf(stderr, "x: %d\n", *x);
885 \\ delete x;
886 \\}
887 ,
888 .cpp_source_flags = &.{ "-nostdlib++", "-nostdinc++" },
889 });
890 exe.root_module.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/include" }) });
891 exe.root_module.addIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/include/c++/v1" }) });
892 exe.root_module.addObjectFile(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/lib/libc++.tbd" }) });
893
894 const check = exe.checkObject();
895 check.checkInSymtab();
896 check.checkContains("[referenced dynamically] external __mh_execute_header");
897 test_step.dependOn(&check.step);
898
899 return test_step;
900}
901
902fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
903 const test_step = addTestStep(b, "linking-static-lib", opts);
904
905 const obj = addObject(b, opts, .{
906 .name = "bobj",
907 .zig_source_bytes = "export var bar: i32 = -42;",
908 .strip = true, // TODO for self-hosted, we don't really emit any valid DWARF yet since we only export a global
909 });
910
911 const lib = addStaticLibrary(b, opts, .{
912 .name = "alib",
913 .zig_source_bytes =
914 \\export fn foo() i32 {
915 \\ return 42;
916 \\}
917 ,
918 });
919 lib.root_module.addObject(obj);
920
921 const exe = addExecutable(b, opts, .{
922 .name = "testlib",
923 .zig_source_bytes =
924 \\const std = @import("std");
925 \\extern fn foo() i32;
926 \\extern var bar: i32;
927 \\pub fn main() void {
928 \\ std.debug.print("{d}\n", .{foo() + bar});
929 \\}
930 ,
931 });
932 exe.root_module.linkLibrary(lib);
933
934 const run = addRunArtifact(exe);
935 run.expectStdErrEqual("0\n");
936 test_step.dependOn(&run.step);
937
938 return test_step;
939}
940
941fn testLinksection(b: *Build, opts: Options) *Step {
942 const test_step = addTestStep(b, "linksection", opts);
943
944 const obj = addObject(b, opts, .{ .name = "main", .zig_source_bytes =
945 \\export var test_global: u32 linksection("__DATA,__TestGlobal") = undefined;
946 \\export fn testFn() linksection("__TEXT,__TestFn") callconv(.c) void {
947 \\ TestGenericFn("A").f();
948 \\}
949 \\fn TestGenericFn(comptime suffix: []const u8) type {
950 \\ return struct {
951 \\ fn f() linksection("__TEXT,__TestGenFn" ++ suffix) void {}
952 \\ };
953 \\}
954 });
955
956 const check = obj.checkObject();
957 check.checkInSymtab();
958 check.checkContains("(__DATA,__TestGlobal) external _test_global");
959 check.checkInSymtab();
960 check.checkContains("(__TEXT,__TestFn) external _testFn");
961
962 if (opts.optimize == .Debug) {
963 check.checkInSymtab();
964 check.checkContains("(__TEXT,__TestGenFnA) _main.TestGenericFn(");
965 }
966
967 test_step.dependOn(&check.step);
968
969 return test_step;
970}
971
972fn testMergeLiteralsX64(b: *Build, opts: Options) *Step {
973 const test_step = addTestStep(b, "merge-literals-x64", opts);
974
975 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
976 \\.globl _q1
977 \\.globl _s1
978 \\
979 \\.align 4
980 \\_q1:
981 \\ lea L._q1(%rip), %rax
982 \\ mov (%rax), %xmm0
983 \\ ret
984 \\
985 \\.section __TEXT,__cstring,cstring_literals
986 \\l._s1:
987 \\ .asciz "hello"
988 \\
989 \\.section __TEXT,__literal8,8byte_literals
990 \\.align 8
991 \\L._q1:
992 \\ .double 1.2345
993 \\
994 \\.section __DATA,__data
995 \\.align 8
996 \\_s1:
997 \\ .quad l._s1
998 });
999
1000 const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes =
1001 \\.globl _q2
1002 \\.globl _s2
1003 \\.globl _s3
1004 \\
1005 \\.align 4
1006 \\_q2:
1007 \\ lea L._q2(%rip), %rax
1008 \\ mov (%rax), %xmm0
1009 \\ ret
1010 \\
1011 \\.section __TEXT,__cstring,cstring_literals
1012 \\l._s2:
1013 \\ .asciz "hello"
1014 \\l._s3:
1015 \\ .asciz "world"
1016 \\
1017 \\.section __TEXT,__literal8,8byte_literals
1018 \\.align 8
1019 \\L._q2:
1020 \\ .double 1.2345
1021 \\
1022 \\.section __DATA,__data
1023 \\.align 8
1024 \\_s2:
1025 \\ .quad l._s2
1026 \\_s3:
1027 \\ .quad l._s3
1028 });
1029
1030 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
1031 \\#include <stdio.h>
1032 \\extern double q1();
1033 \\extern double q2();
1034 \\extern const char* s1;
1035 \\extern const char* s2;
1036 \\extern const char* s3;
1037 \\int main() {
1038 \\ printf("%s, %s, %s, %f, %f", s1, s2, s3, q1(), q2());
1039 \\ return 0;
1040 \\}
1041 });
1042
1043 const runWithChecks = struct {
1044 fn runWithChecks(step: *Step, exe: *Compile) void {
1045 const run = addRunArtifact(exe);
1046 run.expectStdOutEqual("hello, hello, world, 1.234500, 1.234500");
1047 step.dependOn(&run.step);
1048
1049 const check = exe.checkObject();
1050 check.dumpSection("__TEXT,__const");
1051 check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?");
1052 check.dumpSection("__TEXT,__cstring");
1053 check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00");
1054 step.dependOn(&check.step);
1055 }
1056 }.runWithChecks;
1057
1058 {
1059 const exe = addExecutable(b, opts, .{ .name = "main1" });
1060 exe.root_module.addObject(a_o);
1061 exe.root_module.addObject(b_o);
1062 exe.root_module.addObject(main_o);
1063 runWithChecks(test_step, exe);
1064 }
1065
1066 {
1067 const exe = addExecutable(b, opts, .{ .name = "main2" });
1068 exe.root_module.addObject(b_o);
1069 exe.root_module.addObject(a_o);
1070 exe.root_module.addObject(main_o);
1071 runWithChecks(test_step, exe);
1072 }
1073
1074 {
1075 const c_o = addObject(b, opts, .{ .name = "c" });
1076 c_o.root_module.addObject(a_o);
1077 c_o.root_module.addObject(b_o);
1078 c_o.root_module.addObject(main_o);
1079
1080 const exe = addExecutable(b, opts, .{ .name = "main3" });
1081 exe.root_module.addObject(c_o);
1082 runWithChecks(test_step, exe);
1083 }
1084
1085 return test_step;
1086}
1087
1088fn testMergeLiteralsArm64(b: *Build, opts: Options) *Step {
1089 const test_step = addTestStep(b, "merge-literals-arm64", opts);
1090
1091 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
1092 \\.globl _q1
1093 \\.globl _s1
1094 \\
1095 \\.align 4
1096 \\_q1:
1097 \\ adrp x8, L._q1@PAGE
1098 \\ ldr d0, [x8, L._q1@PAGEOFF]
1099 \\ ret
1100 \\
1101 \\.section __TEXT,__cstring,cstring_literals
1102 \\l._s1:
1103 \\ .asciz "hello"
1104 \\
1105 \\.section __TEXT,__literal8,8byte_literals
1106 \\.align 8
1107 \\L._q1:
1108 \\ .double 1.2345
1109 \\
1110 \\.section __DATA,__data
1111 \\.align 8
1112 \\_s1:
1113 \\ .quad l._s1
1114 });
1115
1116 const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes =
1117 \\.globl _q2
1118 \\.globl _s2
1119 \\.globl _s3
1120 \\
1121 \\.align 4
1122 \\_q2:
1123 \\ adrp x8, L._q2@PAGE
1124 \\ ldr d0, [x8, L._q2@PAGEOFF]
1125 \\ ret
1126 \\
1127 \\.section __TEXT,__cstring,cstring_literals
1128 \\l._s2:
1129 \\ .asciz "hello"
1130 \\l._s3:
1131 \\ .asciz "world"
1132 \\
1133 \\.section __TEXT,__literal8,8byte_literals
1134 \\.align 8
1135 \\L._q2:
1136 \\ .double 1.2345
1137 \\
1138 \\.section __DATA,__data
1139 \\.align 8
1140 \\_s2:
1141 \\ .quad l._s2
1142 \\_s3:
1143 \\ .quad l._s3
1144 });
1145
1146 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
1147 \\#include <stdio.h>
1148 \\extern double q1();
1149 \\extern double q2();
1150 \\extern const char* s1;
1151 \\extern const char* s2;
1152 \\extern const char* s3;
1153 \\int main() {
1154 \\ printf("%s, %s, %s, %f, %f", s1, s2, s3, q1(), q2());
1155 \\ return 0;
1156 \\}
1157 });
1158
1159 const runWithChecks = struct {
1160 fn runWithChecks(step: *Step, exe: *Compile) void {
1161 const run = addRunArtifact(exe);
1162 run.expectStdOutEqual("hello, hello, world, 1.234500, 1.234500");
1163 step.dependOn(&run.step);
1164
1165 const check = exe.checkObject();
1166 check.dumpSection("__TEXT,__const");
1167 check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?");
1168 check.dumpSection("__TEXT,__cstring");
1169 check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00");
1170 step.dependOn(&check.step);
1171 }
1172 }.runWithChecks;
1173
1174 {
1175 const exe = addExecutable(b, opts, .{ .name = "main1" });
1176 exe.root_module.addObject(a_o);
1177 exe.root_module.addObject(b_o);
1178 exe.root_module.addObject(main_o);
1179 runWithChecks(test_step, exe);
1180 }
1181
1182 {
1183 const exe = addExecutable(b, opts, .{ .name = "main2" });
1184 exe.root_module.addObject(b_o);
1185 exe.root_module.addObject(a_o);
1186 exe.root_module.addObject(main_o);
1187 runWithChecks(test_step, exe);
1188 }
1189
1190 {
1191 const c_o = addObject(b, opts, .{ .name = "c" });
1192 c_o.root_module.addObject(a_o);
1193 c_o.root_module.addObject(b_o);
1194 c_o.root_module.addObject(main_o);
1195
1196 const exe = addExecutable(b, opts, .{ .name = "main3" });
1197 exe.root_module.addObject(c_o);
1198 runWithChecks(test_step, exe);
1199 }
1200
1201 return test_step;
1202}
1203
1204/// This particular test case will generate invalid machine code that will segfault at runtime.
1205/// However, this is by design as we want to test that the linker does not panic when linking it
1206/// which is also the case for the system linker and lld - linking succeeds, runtime segfaults.
1207/// It should also be mentioned that runtime segfault is not due to the linker but faulty input asm.
1208fn testMergeLiteralsArm642(b: *Build, opts: Options) *Step {
1209 const test_step = addTestStep(b, "merge-literals-arm64-2", opts);
1210
1211 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
1212 \\.globl _q1
1213 \\.globl _s1
1214 \\
1215 \\.align 4
1216 \\_q1:
1217 \\ adrp x0, L._q1@PAGE
1218 \\ ldr x0, [x0, L._q1@PAGEOFF]
1219 \\ ret
1220 \\
1221 \\.section __TEXT,__cstring,cstring_literals
1222 \\_s1:
1223 \\ .asciz "hello"
1224 \\
1225 \\.section __TEXT,__literal8,8byte_literals
1226 \\.align 8
1227 \\L._q1:
1228 \\ .double 1.2345
1229 });
1230
1231 const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes =
1232 \\.globl _q2
1233 \\.globl _s2
1234 \\.globl _s3
1235 \\
1236 \\.align 4
1237 \\_q2:
1238 \\ adrp x0, L._q2@PAGE
1239 \\ ldr x0, [x0, L._q2@PAGEOFF]
1240 \\ ret
1241 \\
1242 \\.section __TEXT,__cstring,cstring_literals
1243 \\_s2:
1244 \\ .asciz "hello"
1245 \\_s3:
1246 \\ .asciz "world"
1247 \\
1248 \\.section __TEXT,__literal8,8byte_literals
1249 \\.align 8
1250 \\L._q2:
1251 \\ .double 1.2345
1252 });
1253
1254 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
1255 \\#include <stdio.h>
1256 \\extern double q1();
1257 \\extern double q2();
1258 \\extern const char* s1;
1259 \\extern const char* s2;
1260 \\extern const char* s3;
1261 \\int main() {
1262 \\ printf("%s, %s, %s, %f, %f", s1, s2, s3, q1(), q2());
1263 \\ return 0;
1264 \\}
1265 });
1266
1267 const exe = addExecutable(b, opts, .{ .name = "main1" });
1268 exe.root_module.addObject(a_o);
1269 exe.root_module.addObject(b_o);
1270 exe.root_module.addObject(main_o);
1271
1272 const check = exe.checkObject();
1273 check.dumpSection("__TEXT,__const");
1274 check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?");
1275 check.dumpSection("__TEXT,__cstring");
1276 check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00");
1277 test_step.dependOn(&check.step);
1278
1279 return test_step;
1280}
1281
1282fn testMergeLiteralsAlignment(b: *Build, opts: Options) *Step {
1283 const test_step = addTestStep(b, "merge-literals-alignment", opts);
1284
1285 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
1286 \\.globl _s1
1287 \\.globl _s2
1288 \\
1289 \\.section __TEXT,__cstring,cstring_literals
1290 \\.align 3
1291 \\_s1:
1292 \\ .asciz "str1"
1293 \\_s2:
1294 \\ .asciz "str2"
1295 });
1296
1297 const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes =
1298 \\.globl _s3
1299 \\.globl _s4
1300 \\
1301 \\.section __TEXT,__cstring,cstring_literals
1302 \\.align 2
1303 \\_s3:
1304 \\ .asciz "str1"
1305 \\_s4:
1306 \\ .asciz "str2"
1307 });
1308
1309 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
1310 \\#include <assert.h>
1311 \\#include <stdint.h>
1312 \\#include <stdio.h>
1313 \\extern const char* s1;
1314 \\extern const char* s2;
1315 \\extern const char* s3;
1316 \\extern const char* s4;
1317 \\int main() {
1318 \\ assert((uintptr_t)(&s1) % 8 == 0 && s1 == s3);
1319 \\ assert((uintptr_t)(&s2) % 8 == 0 && s2 == s4);
1320 \\ printf("%s%s%s%s", &s1, &s2, &s3, &s4);
1321 \\ return 0;
1322 \\}
1323 , .c_source_flags = &.{"-Wno-format"} });
1324
1325 const runWithChecks = struct {
1326 fn runWithChecks(step: *Step, exe: *Compile) void {
1327 const run = addRunArtifact(exe);
1328 run.expectStdOutEqual("str1str2str1str2");
1329 step.dependOn(&run.step);
1330
1331 const check = exe.checkObject();
1332 check.dumpSection("__TEXT,__cstring");
1333 check.checkContains("str1\x00\x00\x00\x00str2\x00");
1334 check.checkInHeaders();
1335 check.checkExact("segname __TEXT");
1336 check.checkExact("sectname __cstring");
1337 check.checkExact("align 3");
1338 step.dependOn(&check.step);
1339 }
1340 }.runWithChecks;
1341
1342 {
1343 const exe = addExecutable(b, opts, .{ .name = "main1" });
1344 exe.root_module.addObject(a_o);
1345 exe.root_module.addObject(b_o);
1346 exe.root_module.addObject(main_o);
1347 runWithChecks(test_step, exe);
1348 }
1349
1350 {
1351 const exe = addExecutable(b, opts, .{ .name = "main2" });
1352 exe.root_module.addObject(b_o);
1353 exe.root_module.addObject(a_o);
1354 exe.root_module.addObject(main_o);
1355 runWithChecks(test_step, exe);
1356 }
1357
1358 return test_step;
1359}
1360
1361fn testMergeLiteralsObjc(b: *Build, opts: Options) *Step {
1362 const test_step = addTestStep(b, "merge-literals-objc", opts);
1363
1364 const main_o = addObject(b, opts, .{ .name = "main", .objc_source_bytes =
1365 \\#import <Foundation/Foundation.h>;
1366 \\
1367 \\extern void foo();
1368 \\
1369 \\int main() {
1370 \\ NSString *thing = @"aaa";
1371 \\
1372 \\ SEL sel = @selector(lowercaseString);
1373 \\ NSString *lower = (([thing respondsToSelector:sel]) ? @"YES" : @"NO");
1374 \\ NSLog (@"Responds to lowercaseString: %@", lower);
1375 \\ if ([thing respondsToSelector:sel]) //(lower == @"YES")
1376 \\ NSLog(@"lowercaseString is: %@", [thing lowercaseString]);
1377 \\
1378 \\ foo();
1379 \\}
1380 });
1381
1382 const a_o = addObject(b, opts, .{ .name = "a", .objc_source_bytes =
1383 \\#import <Foundation/Foundation.h>;
1384 \\
1385 \\void foo() {
1386 \\ NSString *thing = @"aaa";
1387 \\ SEL sel = @selector(lowercaseString);
1388 \\ NSString *lower = (([thing respondsToSelector:sel]) ? @"YES" : @"NO");
1389 \\ NSLog (@"Responds to lowercaseString in foo(): %@", lower);
1390 \\ if ([thing respondsToSelector:sel]) //(lower == @"YES")
1391 \\ NSLog(@"lowercaseString in foo() is: %@", [thing lowercaseString]);
1392 \\ SEL sel2 = @selector(uppercaseString);
1393 \\ NSString *upper = (([thing respondsToSelector:sel2]) ? @"YES" : @"NO");
1394 \\ NSLog (@"Responds to uppercaseString in foo(): %@", upper);
1395 \\ if ([thing respondsToSelector:sel2]) //(upper == @"YES")
1396 \\ NSLog(@"uppercaseString in foo() is: %@", [thing uppercaseString]);
1397 \\}
1398 });
1399
1400 const runWithChecks = struct {
1401 fn runWithChecks(step: *Step, exe: *Compile) void {
1402 const builder = step.owner;
1403 const run = addRunArtifact(exe);
1404 run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to lowercaseString: YES") });
1405 run.addCheck(.{ .expect_stderr_match = builder.dupe("lowercaseString is: aaa") });
1406 run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to lowercaseString in foo(): YES") });
1407 run.addCheck(.{ .expect_stderr_match = builder.dupe("lowercaseString in foo() is: aaa") });
1408 run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to uppercaseString in foo(): YES") });
1409 run.addCheck(.{ .expect_stderr_match = builder.dupe("uppercaseString in foo() is: AAA") });
1410 step.dependOn(&run.step);
1411
1412 const check = exe.checkObject();
1413 check.dumpSection("__TEXT,__objc_methname");
1414 check.checkContains("lowercaseString\x00");
1415 check.dumpSection("__TEXT,__objc_methname");
1416 check.checkContains("uppercaseString\x00");
1417 step.dependOn(&check.step);
1418 }
1419 }.runWithChecks;
1420
1421 {
1422 const exe = addExecutable(b, opts, .{ .name = "main1" });
1423 exe.root_module.addObject(main_o);
1424 exe.root_module.addObject(a_o);
1425 exe.root_module.linkFramework("Foundation", .{});
1426 runWithChecks(test_step, exe);
1427 }
1428
1429 {
1430 const exe = addExecutable(b, opts, .{ .name = "main2" });
1431 exe.root_module.addObject(a_o);
1432 exe.root_module.addObject(main_o);
1433 exe.root_module.linkFramework("Foundation", .{});
1434 runWithChecks(test_step, exe);
1435 }
1436
1437 {
1438 const b_o = addObject(b, opts, .{ .name = "b" });
1439 b_o.root_module.addObject(a_o);
1440 b_o.root_module.addObject(main_o);
1441
1442 const exe = addExecutable(b, opts, .{ .name = "main3" });
1443 exe.root_module.addObject(b_o);
1444 exe.root_module.linkFramework("Foundation", .{});
1445 runWithChecks(test_step, exe);
1446 }
1447
1448 return test_step;
1449}
1450
1451fn testMhExecuteHeader(b: *Build, opts: Options) *Step {
1452 const test_step = addTestStep(b, "mh-execute-header", opts);
1453
1454 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
1455
1456 const check = exe.checkObject();
1457 check.checkInSymtab();
1458 check.checkContains("[referenced dynamically] external __mh_execute_header");
1459 test_step.dependOn(&check.step);
1460
1461 return test_step;
1462}
1463
1464fn testNoDeadStrip(b: *Build, opts: Options) *Step {
1465 const test_step = addTestStep(b, "no-dead-strip", opts);
1466
1467 const exe = addExecutable(b, opts, .{ .name = "name", .c_source_bytes =
1468 \\__attribute__((used)) int bogus1 = 0;
1469 \\int bogus2 = 0;
1470 \\int foo = 42;
1471 \\int main() {
1472 \\ return foo - 42;
1473 \\}
1474 });
1475 exe.link_gc_sections = true;
1476
1477 const check = exe.checkObject();
1478 check.checkInSymtab();
1479 check.checkContains("external _bogus1");
1480 check.checkInSymtab();
1481 check.checkNotPresent("external _bogus2");
1482 test_step.dependOn(&check.step);
1483
1484 const run = addRunArtifact(exe);
1485 run.expectExitCode(0);
1486 test_step.dependOn(&run.step);
1487
1488 return test_step;
1489}
1490
1491fn testNoExportsDylib(b: *Build, opts: Options) *Step {
1492 const test_step = addTestStep(b, "no-exports-dylib", opts);
1493
1494 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "static void abc() {}" });
1495
1496 const check = dylib.checkObject();
1497 check.checkInSymtab();
1498 check.checkNotPresent("external _abc");
1499 test_step.dependOn(&check.step);
1500
1501 return test_step;
1502}
1503
1504fn testNeededFramework(b: *Build, opts: Options) *Step {
1505 const test_step = addTestStep(b, "needed-framework", opts);
1506
1507 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
1508 exe.root_module.linkFramework("Cocoa", .{ .needed = true });
1509 exe.dead_strip_dylibs = true;
1510
1511 const check = exe.checkObject();
1512 check.checkInHeaders();
1513 check.checkExact("cmd LOAD_DYLIB");
1514 check.checkContains("Cocoa");
1515 test_step.dependOn(&check.step);
1516
1517 const run = addRunArtifact(exe);
1518 run.expectExitCode(0);
1519 test_step.dependOn(&run.step);
1520
1521 return test_step;
1522}
1523
1524fn testNeededLibrary(b: *Build, opts: Options) *Step {
1525 const test_step = addTestStep(b, "needed-library", opts);
1526
1527 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "int a = 42;" });
1528
1529 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
1530 exe.root_module.linkSystemLibrary("a", .{ .needed = true });
1531 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
1532 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
1533 exe.dead_strip_dylibs = true;
1534
1535 const check = exe.checkObject();
1536 check.checkInHeaders();
1537 check.checkExact("cmd LOAD_DYLIB");
1538 check.checkContains("liba.dylib");
1539 test_step.dependOn(&check.step);
1540
1541 const run = addRunArtifact(exe);
1542 run.expectExitCode(0);
1543 test_step.dependOn(&run.step);
1544
1545 return test_step;
1546}
1547
1548fn testObjc(b: *Build, opts: Options) *Step {
1549 const test_step = addTestStep(b, "objc", opts);
1550
1551 const lib = addStaticLibrary(b, opts, .{ .name = "a", .objc_source_bytes =
1552 \\#import <Foundation/Foundation.h>
1553 \\@interface Foo : NSObject
1554 \\@end
1555 \\@implementation Foo
1556 \\@end
1557 });
1558
1559 {
1560 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
1561 exe.root_module.linkSystemLibrary("a", .{});
1562 exe.root_module.linkFramework("Foundation", .{});
1563 exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
1564
1565 const check = exe.checkObject();
1566 check.checkInSymtab();
1567 check.checkNotPresent("_OBJC_");
1568 test_step.dependOn(&check.step);
1569
1570 const run = addRunArtifact(exe);
1571 run.expectExitCode(0);
1572 test_step.dependOn(&run.step);
1573 }
1574
1575 {
1576 const exe = addExecutable(b, opts, .{ .name = "main2", .c_source_bytes = "int main() { return 0; }" });
1577 exe.root_module.linkSystemLibrary("a", .{});
1578 exe.root_module.linkFramework("Foundation", .{});
1579 exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
1580 exe.force_load_objc = true;
1581
1582 const check = exe.checkObject();
1583 check.checkInSymtab();
1584 check.checkContains("_OBJC_");
1585 test_step.dependOn(&check.step);
1586
1587 const run = addRunArtifact(exe);
1588 run.expectExitCode(0);
1589 test_step.dependOn(&run.step);
1590 }
1591
1592 return test_step;
1593}
1594
1595fn testObjcpp(b: *Build, opts: Options) *Step {
1596 const test_step = addTestStep(b, "objcpp", opts);
1597
1598 const foo_h = foo_h: {
1599 const wf = WriteFile.create(b);
1600 break :foo_h wf.add("Foo.h",
1601 \\#import <Foundation/Foundation.h>
1602 \\@interface Foo : NSObject
1603 \\- (NSString *)name;
1604 \\@end
1605 );
1606 };
1607
1608 const foo_o = addObject(b, opts, .{ .name = "foo", .objcpp_source_bytes =
1609 \\#import "Foo.h"
1610 \\@implementation Foo
1611 \\- (NSString *)name
1612 \\{
1613 \\ NSString *str = [[NSString alloc] initWithFormat:@"Zig"];
1614 \\ return str;
1615 \\}
1616 \\@end
1617 });
1618 foo_o.root_module.addIncludePath(foo_h.dirname());
1619 foo_o.root_module.link_libcpp = true;
1620
1621 const exe = addExecutable(b, opts, .{ .name = "main", .objcpp_source_bytes =
1622 \\#import "Foo.h"
1623 \\#import <assert.h>
1624 \\#include <iostream>
1625 \\int main(int argc, char *argv[])
1626 \\{
1627 \\ @autoreleasepool {
1628 \\ Foo *foo = [[Foo alloc] init];
1629 \\ NSString *result = [foo name];
1630 \\ std::cout << "Hello from C++ and " << [result UTF8String];
1631 \\ assert([result isEqualToString:@"Zig"]);
1632 \\ return 0;
1633 \\ }
1634 \\}
1635 });
1636 exe.root_module.addIncludePath(foo_h.dirname());
1637 exe.root_module.addObject(foo_o);
1638 exe.root_module.link_libcpp = true;
1639 exe.root_module.linkFramework("Foundation", .{});
1640
1641 const run = addRunArtifact(exe);
1642 run.expectStdOutEqual("Hello from C++ and Zig");
1643 test_step.dependOn(&run.step);
1644
1645 return test_step;
1646}
1647
1648fn testPagezeroSize(b: *Build, opts: Options) *Step {
1649 const test_step = addTestStep(b, "pagezero-size", opts);
1650
1651 {
1652 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main () { return 0; }" });
1653 exe.pagezero_size = 0x4000;
1654
1655 const check = exe.checkObject();
1656 check.checkInHeaders();
1657 check.checkExact("LC 0");
1658 check.checkExact("segname __PAGEZERO");
1659 check.checkExact("vmaddr 0");
1660 check.checkExact("vmsize 4000");
1661 check.checkInHeaders();
1662 check.checkExact("segname __TEXT");
1663 check.checkExact("vmaddr 4000");
1664 test_step.dependOn(&check.step);
1665 }
1666
1667 {
1668 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main () { return 0; }" });
1669 exe.pagezero_size = 0;
1670
1671 const check = exe.checkObject();
1672 check.checkInHeaders();
1673 check.checkExact("LC 0");
1674 check.checkExact("segname __TEXT");
1675 check.checkExact("vmaddr 0");
1676 test_step.dependOn(&check.step);
1677 }
1678
1679 return test_step;
1680}
1681
1682fn testReexportsZig(b: *Build, opts: Options) *Step {
1683 const test_step = addTestStep(b, "reexports-zig", opts);
1684
1685 const lib = addStaticLibrary(b, opts, .{ .name = "a", .zig_source_bytes =
1686 \\const x: i32 = 42;
1687 \\export fn foo() i32 {
1688 \\ return x;
1689 \\}
1690 \\comptime {
1691 \\ @export(&foo, .{ .name = "bar", .linkage = .strong });
1692 \\}
1693 });
1694
1695 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
1696 \\extern int foo();
1697 \\extern int bar();
1698 \\int main() {
1699 \\ return bar() - foo();
1700 \\}
1701 });
1702 exe.root_module.linkLibrary(lib);
1703
1704 const run = addRunArtifact(exe);
1705 run.expectExitCode(0);
1706 test_step.dependOn(&run.step);
1707
1708 return test_step;
1709}
1710
1711fn testRelocatable(b: *Build, opts: Options) *Step {
1712 const test_step = addTestStep(b, "relocatable", opts);
1713
1714 const a_o = addObject(b, opts, .{ .name = "a", .cpp_source_bytes =
1715 \\#include <stdexcept>
1716 \\int try_me() {
1717 \\ throw std::runtime_error("Oh no!");
1718 \\}
1719 });
1720 a_o.root_module.link_libcpp = true;
1721
1722 const b_o = addObject(b, opts, .{ .name = "b", .cpp_source_bytes =
1723 \\extern int try_me();
1724 \\int try_again() {
1725 \\ return try_me();
1726 \\}
1727 });
1728
1729 const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes =
1730 \\#include <iostream>
1731 \\#include <stdexcept>
1732 \\extern int try_again();
1733 \\int main() {
1734 \\ try {
1735 \\ try_again();
1736 \\ } catch (const std::exception &e) {
1737 \\ std::cout << "exception=" << e.what();
1738 \\ }
1739 \\ return 0;
1740 \\}
1741 });
1742 main_o.root_module.link_libcpp = true;
1743
1744 const exp_stdout = "exception=Oh no!";
1745
1746 {
1747 const c_o = addObject(b, opts, .{ .name = "c" });
1748 c_o.root_module.addObject(a_o);
1749 c_o.root_module.addObject(b_o);
1750
1751 const exe = addExecutable(b, opts, .{ .name = "main1" });
1752 exe.root_module.addObject(main_o);
1753 exe.root_module.addObject(c_o);
1754 exe.root_module.link_libcpp = true;
1755
1756 const run = addRunArtifact(exe);
1757 run.expectStdOutEqual(exp_stdout);
1758 test_step.dependOn(&run.step);
1759 }
1760
1761 {
1762 const d_o = addObject(b, opts, .{ .name = "d" });
1763 d_o.root_module.addObject(a_o);
1764 d_o.root_module.addObject(b_o);
1765 d_o.root_module.addObject(main_o);
1766
1767 const exe = addExecutable(b, opts, .{ .name = "main2" });
1768 exe.root_module.addObject(d_o);
1769 exe.root_module.link_libcpp = true;
1770
1771 const run = addRunArtifact(exe);
1772 run.expectStdOutEqual(exp_stdout);
1773 test_step.dependOn(&run.step);
1774 }
1775
1776 return test_step;
1777}
1778
1779fn testRelocatableZig(b: *Build, opts: Options) *Step {
1780 const test_step = addTestStep(b, "relocatable-zig", opts);
1781
1782 const a_o = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
1783 \\const std = @import("std");
1784 \\export var foo: i32 = 0;
1785 \\export fn incrFoo() void {
1786 \\ foo += 1;
1787 \\ std.debug.print("incrFoo={d}\n", .{foo});
1788 \\}
1789 });
1790
1791 const b_o = addObject(b, opts, .{ .name = "b", .zig_source_bytes =
1792 \\const std = @import("std");
1793 \\extern var foo: i32;
1794 \\export fn decrFoo() void {
1795 \\ foo -= 1;
1796 \\ std.debug.print("decrFoo={d}\n", .{foo});
1797 \\}
1798 });
1799
1800 const main_o = addObject(b, opts, .{ .name = "main", .zig_source_bytes =
1801 \\const std = @import("std");
1802 \\extern var foo: i32;
1803 \\extern fn incrFoo() void;
1804 \\extern fn decrFoo() void;
1805 \\pub fn main() void {
1806 \\ const init = foo;
1807 \\ incrFoo();
1808 \\ decrFoo();
1809 \\ if (init == foo) @panic("Oh no!");
1810 \\}
1811 });
1812
1813 const c_o = addObject(b, opts, .{ .name = "c" });
1814 c_o.root_module.addObject(a_o);
1815 c_o.root_module.addObject(b_o);
1816 c_o.root_module.addObject(main_o);
1817
1818 const exe = addExecutable(b, opts, .{ .name = "main" });
1819 exe.root_module.addObject(c_o);
1820
1821 const run = addRunArtifact(exe);
1822 run.addCheck(.{ .expect_stderr_match = b.dupe("incrFoo=1") });
1823 run.addCheck(.{ .expect_stderr_match = b.dupe("decrFoo=0") });
1824 run.addCheck(.{ .expect_stderr_match = b.dupe("panic: Oh no!") });
1825 test_step.dependOn(&run.step);
1826
1827 return test_step;
1828}
1829
1830fn testSearchStrategy(b: *Build, opts: Options) *Step {
1831 const test_step = addTestStep(b, "search-strategy", opts);
1832
1833 const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes =
1834 \\#include<stdio.h>
1835 \\char world[] = "world";
1836 \\char* hello() {
1837 \\ return "Hello";
1838 \\}
1839 });
1840
1841 const liba = addStaticLibrary(b, opts, .{ .name = "a" });
1842 liba.root_module.addObject(obj);
1843
1844 const dylib = addSharedLibrary(b, opts, .{ .name = "a" });
1845 dylib.root_module.addObject(obj);
1846
1847 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
1848 \\#include<stdio.h>
1849 \\char* hello();
1850 \\extern char world[];
1851 \\int main() {
1852 \\ printf("%s %s", hello(), world);
1853 \\ return 0;
1854 \\}
1855 });
1856
1857 {
1858 const exe = addExecutable(b, opts, .{ .name = "main" });
1859 exe.root_module.addObject(main_o);
1860 exe.root_module.linkSystemLibrary("a", .{ .use_pkg_config = .no, .search_strategy = .mode_first });
1861 exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
1862 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
1863 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
1864
1865 const run = addRunArtifact(exe);
1866 run.expectStdOutEqual("Hello world");
1867 test_step.dependOn(&run.step);
1868
1869 const check = exe.checkObject();
1870 check.checkInHeaders();
1871 check.checkExact("cmd LOAD_DYLIB");
1872 check.checkContains("liba.dylib");
1873 test_step.dependOn(&check.step);
1874 }
1875
1876 {
1877 const exe = addExecutable(b, opts, .{ .name = "main" });
1878 exe.root_module.addObject(main_o);
1879 exe.root_module.linkSystemLibrary("a", .{ .use_pkg_config = .no, .search_strategy = .paths_first });
1880 exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
1881 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
1882 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
1883
1884 const run = addRunArtifact(exe);
1885 run.expectStdOutEqual("Hello world");
1886 test_step.dependOn(&run.step);
1887
1888 const check = exe.checkObject();
1889 check.checkInHeaders();
1890 check.checkExact("cmd LOAD_DYLIB");
1891 check.checkNotPresent("liba.dylib");
1892 test_step.dependOn(&check.step);
1893 }
1894
1895 return test_step;
1896}
1897
1898fn testSectionBoundarySymbols(b: *Build, opts: Options) *Step {
1899 const test_step = addTestStep(b, "section-boundary-symbols", opts);
1900
1901 const obj1 = addObject(b, opts, .{
1902 .name = "obj1",
1903 .cpp_source_bytes =
1904 \\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST,__message_ptr"))) = "codebase";
1905 ,
1906 });
1907
1908 const main_o = addObject(b, opts, .{
1909 .name = "main",
1910 .zig_source_bytes =
1911 \\const std = @import("std");
1912 \\extern fn interop() ?[*:0]const u8;
1913 \\pub fn main() !void {
1914 \\ std.debug.print("All your {s} are belong to us.\n", .{
1915 \\ if (interop()) |ptr| std.mem.span(ptr) else "(null)",
1916 \\ });
1917 \\}
1918 ,
1919 });
1920
1921 {
1922 const obj2 = addObject(b, opts, .{
1923 .name = "obj2",
1924 .cpp_source_bytes =
1925 \\extern const char* message_pointer __asm("section$start$__DATA_CONST$__message_ptr");
1926 \\extern "C" const char* interop() {
1927 \\ return message_pointer;
1928 \\}
1929 ,
1930 });
1931
1932 const exe = addExecutable(b, opts, .{ .name = "test" });
1933 exe.root_module.addObject(obj1);
1934 exe.root_module.addObject(obj2);
1935 exe.root_module.addObject(main_o);
1936
1937 const run = b.addRunArtifact(exe);
1938 run.skip_foreign_checks = true;
1939 run.expectStdErrEqual("All your codebase are belong to us.\n");
1940 test_step.dependOn(&run.step);
1941
1942 const check = exe.checkObject();
1943 check.checkInSymtab();
1944 check.checkNotPresent("external section$start$__DATA_CONST$__message_ptr");
1945 test_step.dependOn(&check.step);
1946 }
1947
1948 {
1949 const obj3 = addObject(b, opts, .{
1950 .name = "obj3",
1951 .cpp_source_bytes =
1952 \\extern const char* message_pointer __asm("section$start$__DATA_CONST$__not_present");
1953 \\extern "C" const char* interop() {
1954 \\ return message_pointer;
1955 \\}
1956 ,
1957 });
1958
1959 const exe = addExecutable(b, opts, .{ .name = "test" });
1960 exe.root_module.addObject(obj1);
1961 exe.root_module.addObject(obj3);
1962 exe.root_module.addObject(main_o);
1963
1964 const run = b.addRunArtifact(exe);
1965 run.skip_foreign_checks = true;
1966 run.expectStdErrEqual("All your (null) are belong to us.\n");
1967 test_step.dependOn(&run.step);
1968
1969 const check = exe.checkObject();
1970 check.checkInSymtab();
1971 check.checkNotPresent("external section$start$__DATA_CONST$__not_present");
1972 test_step.dependOn(&check.step);
1973 }
1974
1975 return test_step;
1976}
1977
1978fn testSectionBoundarySymbols2(b: *Build, opts: Options) *Step {
1979 const test_step = addTestStep(b, "section-boundary-symbols-2", opts);
1980
1981 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
1982 \\#include <stdio.h>
1983 \\struct pair { int a; int b; };
1984 \\struct pair first __attribute__((section("__DATA,__pairs"))) = { 1, 2 };
1985 \\struct pair second __attribute__((section("__DATA,__pairs"))) = { 3, 4 };
1986 \\extern struct pair pairs_start __asm("section$start$__DATA$__pairs");
1987 \\extern struct pair pairs_end __asm("section$end$__DATA$__pairs");
1988 \\int main() {
1989 \\ printf("%d,%d\n", first.a, first.b);
1990 \\ printf("%d,%d\n", second.a, second.b);
1991 \\ struct pair* p;
1992 \\ for (p = &pairs_start; p < &pairs_end; p++) {
1993 \\ p->a = 0;
1994 \\ }
1995 \\ printf("%d,%d\n", first.a, first.b);
1996 \\ printf("%d,%d\n", second.a, second.b);
1997 \\ return 0;
1998 \\}
1999 });
2000
2001 const run = b.addRunArtifact(exe);
2002 run.skip_foreign_checks = true;
2003 run.expectStdOutEqual(
2004 \\1,2
2005 \\3,4
2006 \\0,2
2007 \\0,4
2008 \\
2009 );
2010 test_step.dependOn(&run.step);
2011
2012 return test_step;
2013}
2014
2015fn testSegmentBoundarySymbols(b: *Build, opts: Options) *Step {
2016 const test_step = addTestStep(b, "segment-boundary-symbols", opts);
2017
2018 const obj1 = addObject(b, opts, .{ .name = "a", .cpp_source_bytes =
2019 \\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST_1,__message_ptr"))) = "codebase";
2020 });
2021
2022 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
2023 \\#include <stdio.h>
2024 \\const char* interop();
2025 \\int main() {
2026 \\ printf("All your %s are belong to us.\n", interop());
2027 \\ return 0;
2028 \\}
2029 });
2030
2031 {
2032 const obj2 = addObject(b, opts, .{ .name = "b", .cpp_source_bytes =
2033 \\extern const char* message_pointer __asm("segment$start$__DATA_CONST_1");
2034 \\extern "C" const char* interop() {
2035 \\ return message_pointer;
2036 \\}
2037 });
2038
2039 const exe = addExecutable(b, opts, .{ .name = "main" });
2040 exe.root_module.addObject(obj1);
2041 exe.root_module.addObject(obj2);
2042 exe.root_module.addObject(main_o);
2043
2044 const run = addRunArtifact(exe);
2045 run.expectStdOutEqual("All your codebase are belong to us.\n");
2046 test_step.dependOn(&run.step);
2047
2048 const check = exe.checkObject();
2049 check.checkInSymtab();
2050 check.checkNotPresent("external segment$start$__DATA_CONST_1");
2051 test_step.dependOn(&check.step);
2052 }
2053
2054 {
2055 const obj2 = addObject(b, opts, .{ .name = "c", .cpp_source_bytes =
2056 \\extern const char* message_pointer __asm("segment$start$__DATA_1");
2057 \\extern "C" const char* interop() {
2058 \\ return message_pointer;
2059 \\}
2060 });
2061
2062 const exe = addExecutable(b, opts, .{ .name = "main2" });
2063 exe.root_module.addObject(obj1);
2064 exe.root_module.addObject(obj2);
2065 exe.root_module.addObject(main_o);
2066
2067 const check = exe.checkObject();
2068 check.checkInHeaders();
2069 check.checkExact("cmd SEGMENT_64");
2070 check.checkExact("segname __DATA_1");
2071 check.checkExtract("vmsize {vmsize}");
2072 check.checkExtract("filesz {filesz}");
2073 check.checkComputeCompare("vmsize", .{ .op = .eq, .value = .{ .literal = 0 } });
2074 check.checkComputeCompare("filesz", .{ .op = .eq, .value = .{ .literal = 0 } });
2075 check.checkInSymtab();
2076 check.checkNotPresent("external segment$start$__DATA_1");
2077 test_step.dependOn(&check.step);
2078 }
2079
2080 return test_step;
2081}
2082
2083fn testSymbolStabs(b: *Build, opts: Options) *Step {
2084 const test_step = addTestStep(b, "symbol-stabs", opts);
2085
2086 const a_o = addObject(b, opts, .{ .name = "a", .c_source_bytes =
2087 \\int foo = 42;
2088 \\int getFoo() {
2089 \\ return foo;
2090 \\}
2091 });
2092
2093 const b_o = addObject(b, opts, .{ .name = "b", .c_source_bytes =
2094 \\int bar = 24;
2095 \\int getBar() {
2096 \\ return bar;
2097 \\}
2098 });
2099
2100 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
2101 \\#include <stdio.h>
2102 \\extern int getFoo();
2103 \\extern int getBar();
2104 \\int main() {
2105 \\ printf("foo=%d,bar=%d", getFoo(), getBar());
2106 \\ return 0;
2107 \\}
2108 });
2109
2110 const exe = addExecutable(b, opts, .{ .name = "main" });
2111 exe.root_module.addObject(a_o);
2112 exe.root_module.addObject(b_o);
2113 exe.root_module.addObject(main_o);
2114
2115 const run = addRunArtifact(exe);
2116 run.expectStdOutEqual("foo=42,bar=24");
2117 test_step.dependOn(&run.step);
2118
2119 const check = exe.checkObject();
2120 check.checkInSymtab();
2121 check.checkContains("a.o"); // TODO we really should do a fuzzy search like OSO <ignore>/a.o
2122 check.checkInSymtab();
2123 check.checkContains("b.o");
2124 check.checkInSymtab();
2125 check.checkContains("main.o");
2126 test_step.dependOn(&check.step);
2127
2128 return test_step;
2129}
2130
2131fn testStackSize(b: *Build, opts: Options) *Step {
2132 const test_step = addTestStep(b, "stack-size", opts);
2133
2134 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
2135 exe.stack_size = 0x100000000;
2136
2137 const run = addRunArtifact(exe);
2138 run.expectExitCode(0);
2139 test_step.dependOn(&run.step);
2140
2141 const check = exe.checkObject();
2142 check.checkInHeaders();
2143 check.checkExact("cmd MAIN");
2144 check.checkExact("stacksize 100000000");
2145 test_step.dependOn(&check.step);
2146
2147 return test_step;
2148}
2149
2150fn testTbdv3(b: *Build, opts: Options) *Step {
2151 const test_step = addTestStep(b, "tbdv3", opts);
2152
2153 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "int getFoo() { return 42; }" });
2154
2155 const tbd = tbd: {
2156 const wf = WriteFile.create(b);
2157 break :tbd wf.add("liba.tbd",
2158 \\--- !tapi-tbd-v3
2159 \\archs: [ arm64, x86_64 ]
2160 \\uuids: [ 'arm64: DEADBEEF', 'x86_64: BEEFDEAD' ]
2161 \\platform: macos
2162 \\install-name: @rpath/liba.dylib
2163 \\current-version: 0
2164 \\exports:
2165 \\ - archs: [ arm64, x86_64 ]
2166 \\ symbols: [ _getFoo ]
2167 );
2168 };
2169
2170 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2171 \\#include <stdio.h>
2172 \\int getFoo();
2173 \\int main() {
2174 \\ return getFoo() - 42;
2175 \\}
2176 });
2177 exe.root_module.linkSystemLibrary("a", .{});
2178 exe.root_module.addLibraryPath(tbd.dirname());
2179 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
2180
2181 const run = addRunArtifact(exe);
2182 run.expectExitCode(0);
2183 test_step.dependOn(&run.step);
2184
2185 return test_step;
2186}
2187
2188fn testTentative(b: *Build, opts: Options) *Step {
2189 const test_step = addTestStep(b, "tentative", opts);
2190
2191 const exe = addExecutable(b, opts, .{ .name = "main" });
2192 addCSourceBytes(exe,
2193 \\int foo;
2194 \\int bar;
2195 \\int baz = 42;
2196 , &.{"-fcommon"});
2197 addCSourceBytes(exe,
2198 \\#include<stdio.h>
2199 \\int foo;
2200 \\int bar = 5;
2201 \\int baz;
2202 \\int main() {
2203 \\ printf("%d %d %d\n", foo, bar, baz);
2204 \\}
2205 , &.{"-fcommon"});
2206
2207 const run = addRunArtifact(exe);
2208 run.expectStdOutEqual("0 5 42\n");
2209 test_step.dependOn(&run.step);
2210
2211 return test_step;
2212}
2213
2214fn testThunks(b: *Build, opts: Options) *Step {
2215 const test_step = addTestStep(b, "thunks", opts);
2216
2217 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2218 \\#include <stdio.h>
2219 \\void bar() {
2220 \\ printf("bar");
2221 \\}
2222 \\void foo() {
2223 \\ fprintf(stdout, "foo");
2224 \\}
2225 \\int main() {
2226 \\ foo();
2227 \\ bar();
2228 \\ return 0;
2229 \\}
2230 });
2231
2232 const check = exe.checkObject();
2233 check.checkInSymtab();
2234 check.checkContains("_printf__thunk");
2235 check.checkInSymtab();
2236 check.checkContains("_fprintf__thunk");
2237 test_step.dependOn(&check.step);
2238
2239 const run = addRunArtifact(exe);
2240 run.expectStdOutEqual("foobar");
2241 test_step.dependOn(&run.step);
2242
2243 return test_step;
2244}
2245
2246fn testTls(b: *Build, opts: Options) *Step {
2247 const test_step = addTestStep(b, "tls", opts);
2248
2249 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes =
2250 \\_Thread_local int a;
2251 \\int getA() {
2252 \\ return a;
2253 \\}
2254 });
2255
2256 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2257 \\#include<stdio.h>
2258 \\extern _Thread_local int a;
2259 \\extern int getA();
2260 \\int getA2() {
2261 \\ return a;
2262 \\}
2263 \\int main() {
2264 \\ a = 2;
2265 \\ printf("%d %d %d", a, getA(), getA2());
2266 \\ return 0;
2267 \\}
2268 });
2269 exe.root_module.linkSystemLibrary("a", .{});
2270 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
2271 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
2272
2273 const run = addRunArtifact(exe);
2274 run.expectStdOutEqual("2 2 2");
2275 test_step.dependOn(&run.step);
2276
2277 return test_step;
2278}
2279
2280// https://github.com/ziglang/zig/issues/19221
2281fn testTlsPointers(b: *Build, opts: Options) *Step {
2282 const test_step = addTestStep(b, "tls-pointers", opts);
2283
2284 const foo_h = foo_h: {
2285 const wf = WriteFile.create(b);
2286 break :foo_h wf.add("foo.h",
2287 \\template<typename just4fun>
2288 \\struct Foo {
2289 \\
2290 \\public:
2291 \\ static int getVar() {
2292 \\ static int thread_local var = 0;
2293 \\ ++var;
2294 \\ return var;
2295 \\}
2296 \\};
2297 );
2298 };
2299
2300 const bar_o = addObject(b, opts, .{ .name = "bar", .cpp_source_bytes =
2301 \\#include "foo.h"
2302 \\int bar() {
2303 \\ int v1 = Foo<int>::getVar();
2304 \\ return v1;
2305 \\}
2306 });
2307 bar_o.root_module.addIncludePath(foo_h.dirname());
2308 bar_o.root_module.link_libcpp = true;
2309
2310 const baz_o = addObject(b, opts, .{ .name = "baz", .cpp_source_bytes =
2311 \\#include "foo.h"
2312 \\int baz() {
2313 \\ int v1 = Foo<unsigned>::getVar();
2314 \\ return v1;
2315 \\}
2316 });
2317 baz_o.root_module.addIncludePath(foo_h.dirname());
2318 baz_o.root_module.link_libcpp = true;
2319
2320 const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes =
2321 \\extern int bar();
2322 \\extern int baz();
2323 \\int main() {
2324 \\ int v1 = bar();
2325 \\ int v2 = baz();
2326 \\ return v1 != v2;
2327 \\}
2328 });
2329 main_o.root_module.addIncludePath(foo_h.dirname());
2330 main_o.root_module.link_libcpp = true;
2331
2332 const exe = addExecutable(b, opts, .{ .name = "main" });
2333 exe.root_module.addObject(bar_o);
2334 exe.root_module.addObject(baz_o);
2335 exe.root_module.addObject(main_o);
2336 exe.root_module.link_libcpp = true;
2337
2338 const run = addRunArtifact(exe);
2339 run.expectExitCode(0);
2340 test_step.dependOn(&run.step);
2341
2342 return test_step;
2343}
2344
2345fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
2346 const test_step = addTestStep(b, "tls-large-tbss", opts);
2347
2348 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2349 \\#include <stdio.h>
2350 \\_Thread_local int x[0x8000];
2351 \\_Thread_local int y[0x8000];
2352 \\int main() {
2353 \\ x[0] = 3;
2354 \\ x[0x7fff] = 5;
2355 \\ printf("%d %d %d %d %d %d\n", x[0], x[1], x[0x7fff], y[0], y[1], y[0x7fff]);
2356 \\}
2357 });
2358
2359 const run = addRunArtifact(exe);
2360 run.expectStdOutEqual("3 0 5 0 0 0\n");
2361 test_step.dependOn(&run.step);
2362
2363 return test_step;
2364}
2365
2366fn testTlsZig(b: *Build, opts: Options) *Step {
2367 const test_step = addTestStep(b, "tls-zig", opts);
2368
2369 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
2370 \\const std = @import("std");
2371 \\threadlocal var x: i32 = 0;
2372 \\threadlocal var y: i32 = -1;
2373 \\pub fn main() void {
2374 \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
2375 \\ stdout_writer.interface.print("{d} {d}\n", .{x, y}) catch unreachable;
2376 \\ x -= 1;
2377 \\ y += 1;
2378 \\ stdout_writer.interface.print("{d} {d}\n", .{x, y}) catch unreachable;
2379 \\}
2380 });
2381
2382 const run = addRunArtifact(exe);
2383 run.expectStdOutEqual(
2384 \\0 -1
2385 \\-1 0
2386 \\
2387 );
2388 test_step.dependOn(&run.step);
2389
2390 return test_step;
2391}
2392
2393fn testTwoLevelNamespace(b: *Build, opts: Options) *Step {
2394 const test_step = addTestStep(b, "two-level-namespace", opts);
2395
2396 const liba = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes =
2397 \\#include <stdio.h>
2398 \\int foo = 1;
2399 \\int* ptr_to_foo = &foo;
2400 \\int getFoo() {
2401 \\ return foo;
2402 \\}
2403 \\void printInA() {
2404 \\ printf("liba: getFoo()=%d, ptr_to_foo=%d\n", getFoo(), *ptr_to_foo);
2405 \\}
2406 });
2407
2408 {
2409 const check = liba.checkObject();
2410 check.checkInDyldLazyBind();
2411 check.checkNotPresent("(flat lookup) _getFoo");
2412 check.checkInIndirectSymtab();
2413 check.checkNotPresent("_getFoo");
2414 test_step.dependOn(&check.step);
2415 }
2416
2417 const libb = addSharedLibrary(b, opts, .{ .name = "b", .c_source_bytes =
2418 \\#include <stdio.h>
2419 \\int foo = 2;
2420 \\int* ptr_to_foo = &foo;
2421 \\int getFoo() {
2422 \\ return foo;
2423 \\}
2424 \\void printInB() {
2425 \\ printf("libb: getFoo()=%d, ptr_to_foo=%d\n", getFoo(), *ptr_to_foo);
2426 \\}
2427 });
2428
2429 {
2430 const check = libb.checkObject();
2431 check.checkInDyldLazyBind();
2432 check.checkNotPresent("(flat lookup) _getFoo");
2433 check.checkInIndirectSymtab();
2434 check.checkNotPresent("_getFoo");
2435 test_step.dependOn(&check.step);
2436 }
2437
2438 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
2439 \\#include <stdio.h>
2440 \\int getFoo();
2441 \\extern int* ptr_to_foo;
2442 \\void printInA();
2443 \\void printInB();
2444 \\int main() {
2445 \\ printf("main: getFoo()=%d, ptr_to_foo=%d\n", getFoo(), *ptr_to_foo);
2446 \\ printInA();
2447 \\ printInB();
2448 \\ return 0;
2449 \\}
2450 });
2451
2452 {
2453 const exe = addExecutable(b, opts, .{ .name = "main1" });
2454 exe.root_module.addObject(main_o);
2455 exe.root_module.linkSystemLibrary("a", .{});
2456 exe.root_module.linkSystemLibrary("b", .{});
2457 exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
2458 exe.root_module.addLibraryPath(libb.getEmittedBinDirectory());
2459 exe.root_module.addRPath(liba.getEmittedBinDirectory());
2460 exe.root_module.addRPath(libb.getEmittedBinDirectory());
2461
2462 const check = exe.checkObject();
2463 check.checkInSymtab();
2464 check.checkExact("(undefined) external _getFoo (from liba)");
2465 check.checkInSymtab();
2466 check.checkExact("(undefined) external _printInA (from liba)");
2467 check.checkInSymtab();
2468 check.checkExact("(undefined) external _printInB (from libb)");
2469 test_step.dependOn(&check.step);
2470
2471 const run = addRunArtifact(exe);
2472 run.expectStdOutEqual(
2473 \\main: getFoo()=1, ptr_to_foo=1
2474 \\liba: getFoo()=1, ptr_to_foo=1
2475 \\libb: getFoo()=2, ptr_to_foo=2
2476 \\
2477 );
2478 test_step.dependOn(&run.step);
2479 }
2480
2481 {
2482 const exe = addExecutable(b, opts, .{ .name = "main2" });
2483 exe.root_module.addObject(main_o);
2484 exe.root_module.linkSystemLibrary("b", .{});
2485 exe.root_module.linkSystemLibrary("a", .{});
2486 exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
2487 exe.root_module.addLibraryPath(libb.getEmittedBinDirectory());
2488 exe.root_module.addRPath(liba.getEmittedBinDirectory());
2489 exe.root_module.addRPath(libb.getEmittedBinDirectory());
2490
2491 const check = exe.checkObject();
2492 check.checkInSymtab();
2493 check.checkExact("(undefined) external _getFoo (from libb)");
2494 check.checkInSymtab();
2495 check.checkExact("(undefined) external _printInA (from liba)");
2496 check.checkInSymtab();
2497 check.checkExact("(undefined) external _printInB (from libb)");
2498 test_step.dependOn(&check.step);
2499
2500 const run = addRunArtifact(exe);
2501 run.expectStdOutEqual(
2502 \\main: getFoo()=2, ptr_to_foo=2
2503 \\liba: getFoo()=1, ptr_to_foo=1
2504 \\libb: getFoo()=2, ptr_to_foo=2
2505 \\
2506 );
2507 test_step.dependOn(&run.step);
2508 }
2509
2510 return test_step;
2511}
2512
2513fn testDiscardLocalSymbols(b: *Build, opts: Options) *Step {
2514 const test_step = addTestStep(b, "discard-local-symbols", opts);
2515
2516 const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = "static int foo = 42;" });
2517
2518 const lib = addStaticLibrary(b, opts, .{ .name = "a" });
2519 lib.root_module.addObject(obj);
2520
2521 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
2522
2523 {
2524 const exe = addExecutable(b, opts, .{ .name = "main3" });
2525 exe.root_module.addObject(main_o);
2526 exe.root_module.addObject(obj);
2527 exe.discard_local_symbols = true;
2528
2529 const run = addRunArtifact(exe);
2530 run.expectExitCode(0);
2531 test_step.dependOn(&run.step);
2532
2533 const check = exe.checkObject();
2534 check.checkInSymtab();
2535 check.checkNotPresent("_foo");
2536 test_step.dependOn(&check.step);
2537 }
2538
2539 {
2540 const exe = addExecutable(b, opts, .{ .name = "main4" });
2541 exe.root_module.addObject(main_o);
2542 exe.root_module.linkLibrary(lib);
2543 exe.discard_local_symbols = true;
2544
2545 const run = addRunArtifact(exe);
2546 run.expectExitCode(0);
2547 test_step.dependOn(&run.step);
2548
2549 const check = exe.checkObject();
2550 check.checkInSymtab();
2551 check.checkNotPresent("_foo");
2552 test_step.dependOn(&check.step);
2553 }
2554
2555 return test_step;
2556}
2557
2558fn testUndefinedFlag(b: *Build, opts: Options) *Step {
2559 const test_step = addTestStep(b, "undefined-flag", opts);
2560
2561 const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = "int foo = 42;" });
2562
2563 const lib = addStaticLibrary(b, opts, .{ .name = "a" });
2564 lib.root_module.addObject(obj);
2565
2566 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
2567
2568 {
2569 const exe = addExecutable(b, opts, .{ .name = "main1" });
2570 exe.root_module.addObject(main_o);
2571 exe.root_module.linkLibrary(lib);
2572 exe.forceUndefinedSymbol("_foo");
2573
2574 const run = addRunArtifact(exe);
2575 run.expectExitCode(0);
2576 test_step.dependOn(&run.step);
2577
2578 const check = exe.checkObject();
2579 check.checkInSymtab();
2580 check.checkContains("_foo");
2581 test_step.dependOn(&check.step);
2582 }
2583
2584 {
2585 const exe = addExecutable(b, opts, .{ .name = "main2" });
2586 exe.root_module.addObject(main_o);
2587 exe.root_module.linkLibrary(lib);
2588 exe.forceUndefinedSymbol("_foo");
2589 exe.link_gc_sections = true;
2590
2591 const run = addRunArtifact(exe);
2592 run.expectExitCode(0);
2593 test_step.dependOn(&run.step);
2594
2595 const check = exe.checkObject();
2596 check.checkInSymtab();
2597 check.checkContains("_foo");
2598 test_step.dependOn(&check.step);
2599 }
2600
2601 {
2602 const exe = addExecutable(b, opts, .{ .name = "main3" });
2603 exe.root_module.addObject(main_o);
2604 exe.root_module.addObject(obj);
2605
2606 const run = addRunArtifact(exe);
2607 run.expectExitCode(0);
2608 test_step.dependOn(&run.step);
2609
2610 const check = exe.checkObject();
2611 check.checkInSymtab();
2612 check.checkContains("_foo");
2613 test_step.dependOn(&check.step);
2614 }
2615
2616 {
2617 const exe = addExecutable(b, opts, .{ .name = "main4" });
2618 exe.root_module.addObject(main_o);
2619 exe.root_module.addObject(obj);
2620 exe.link_gc_sections = true;
2621
2622 const run = addRunArtifact(exe);
2623 run.expectExitCode(0);
2624 test_step.dependOn(&run.step);
2625
2626 const check = exe.checkObject();
2627 check.checkInSymtab();
2628 check.checkNotPresent("_foo");
2629 test_step.dependOn(&check.step);
2630 }
2631
2632 return test_step;
2633}
2634
2635fn testUnresolvedError(b: *Build, opts: Options) *Step {
2636 const test_step = addTestStep(b, "unresolved-error", opts);
2637
2638 const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
2639 \\extern fn foo() i32;
2640 \\export fn bar() i32 { return foo() + 1; }
2641 });
2642
2643 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
2644 \\const std = @import("std");
2645 \\extern fn foo() i32;
2646 \\extern fn bar() i32;
2647 \\pub fn main() void {
2648 \\ std.debug.print("foo() + bar() = {d}", .{foo() + bar()});
2649 \\}
2650 });
2651 exe.root_module.addObject(obj);
2652
2653 // TODO order should match across backends if possible
2654 if (opts.use_llvm) {
2655 expectLinkErrors(exe, test_step, .{ .exact = &.{
2656 "error: undefined symbol: _foo",
2657 "note: referenced by /?/a.o:_bar",
2658 "note: referenced by /?/main_zcu.o:_main.main",
2659 } });
2660 } else {
2661 expectLinkErrors(exe, test_step, .{ .exact = &.{
2662 "error: undefined symbol: _foo",
2663 "note: referenced by /?/main.o:_main.main",
2664 "note: referenced by /?/a.o:__TEXT$__text_zig",
2665 } });
2666 }
2667
2668 return test_step;
2669}
2670
2671fn testUnresolvedError2(b: *Build, opts: Options) *Step {
2672 const test_step = addTestStep(b, "unresolved-error-2", opts);
2673
2674 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
2675 \\pub fn main() !void {
2676 \\ const msg_send_fn = @extern(
2677 \\ *const fn () callconv(.c) usize,
2678 \\ .{ .name = "objc_msgSend$initWithContentRect:styleMask:backing:defer:screen:" },
2679 \\ );
2680 \\ _ = @call(
2681 \\ .auto,
2682 \\ msg_send_fn,
2683 \\ .{},
2684 \\ );
2685 \\}
2686 });
2687
2688 expectLinkErrors(exe, test_step, .{ .exact = &.{
2689 "error: undefined symbol: _objc_msgSend",
2690 "note: referenced implicitly",
2691 } });
2692
2693 return test_step;
2694}
2695
2696fn testUnwindInfo(b: *Build, opts: Options) *Step {
2697 const test_step = addTestStep(b, "unwind-info", opts);
2698
2699 const all_h = all_h: {
2700 const wf = WriteFile.create(b);
2701 break :all_h wf.add("all.h",
2702 \\#ifndef ALL
2703 \\#define ALL
2704 \\
2705 \\#include <cstddef>
2706 \\#include <string>
2707 \\#include <stdexcept>
2708 \\
2709 \\struct SimpleString {
2710 \\ SimpleString(size_t max_size);
2711 \\ ~SimpleString();
2712 \\
2713 \\ void print(const char* tag) const;
2714 \\ bool append_line(const char* x);
2715 \\
2716 \\private:
2717 \\ size_t max_size;
2718 \\ char* buffer;
2719 \\ size_t length;
2720 \\};
2721 \\
2722 \\struct SimpleStringOwner {
2723 \\ SimpleStringOwner(const char* x);
2724 \\ ~SimpleStringOwner();
2725 \\
2726 \\private:
2727 \\ SimpleString string;
2728 \\};
2729 \\
2730 \\class Error: public std::exception {
2731 \\public:
2732 \\ explicit Error(const char* msg) : msg{ msg } {}
2733 \\ virtual ~Error() noexcept {}
2734 \\ virtual const char* what() const noexcept {
2735 \\ return msg.c_str();
2736 \\ }
2737 \\
2738 \\protected:
2739 \\ std::string msg;
2740 \\};
2741 \\
2742 \\#endif
2743 );
2744 };
2745
2746 const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes =
2747 \\#include "all.h"
2748 \\#include <cstdio>
2749 \\
2750 \\void fn_c() {
2751 \\ SimpleStringOwner c{ "cccccccccc" };
2752 \\}
2753 \\
2754 \\void fn_b() {
2755 \\ SimpleStringOwner b{ "b" };
2756 \\ fn_c();
2757 \\}
2758 \\
2759 \\int main() {
2760 \\ try {
2761 \\ SimpleStringOwner a{ "a" };
2762 \\ fn_b();
2763 \\ SimpleStringOwner d{ "d" };
2764 \\ } catch (const Error& e) {
2765 \\ printf("Error: %s\n", e.what());
2766 \\ } catch(const std::exception& e) {
2767 \\ printf("Exception: %s\n", e.what());
2768 \\ }
2769 \\ return 0;
2770 \\}
2771 });
2772 main_o.root_module.addIncludePath(all_h.dirname());
2773 main_o.root_module.link_libcpp = true;
2774
2775 const simple_string_o = addObject(b, opts, .{ .name = "simple_string", .cpp_source_bytes =
2776 \\#include "all.h"
2777 \\#include <cstdio>
2778 \\#include <cstring>
2779 \\
2780 \\SimpleString::SimpleString(size_t max_size)
2781 \\: max_size{ max_size }, length{} {
2782 \\ if (max_size == 0) {
2783 \\ throw Error{ "Max size must be at least 1." };
2784 \\ }
2785 \\ buffer = new char[max_size];
2786 \\ buffer[0] = 0;
2787 \\}
2788 \\
2789 \\SimpleString::~SimpleString() {
2790 \\ delete[] buffer;
2791 \\}
2792 \\
2793 \\void SimpleString::print(const char* tag) const {
2794 \\ printf("%s: %s", tag, buffer);
2795 \\}
2796 \\
2797 \\bool SimpleString::append_line(const char* x) {
2798 \\ const auto x_len = strlen(x);
2799 \\ if (x_len + length + 2 > max_size) return false;
2800 \\ std::strncpy(buffer + length, x, max_size - length);
2801 \\ length += x_len;
2802 \\ buffer[length++] = '\n';
2803 \\ buffer[length] = 0;
2804 \\ return true;
2805 \\}
2806 });
2807 simple_string_o.root_module.addIncludePath(all_h.dirname());
2808 simple_string_o.root_module.link_libcpp = true;
2809
2810 const simple_string_owner_o = addObject(b, opts, .{ .name = "simple_string_owner", .cpp_source_bytes =
2811 \\#include "all.h"
2812 \\
2813 \\SimpleStringOwner::SimpleStringOwner(const char* x) : string{ 10 } {
2814 \\ if (!string.append_line(x)) {
2815 \\ throw Error{ "Not enough memory!" };
2816 \\ }
2817 \\ string.print("Constructed");
2818 \\}
2819 \\
2820 \\SimpleStringOwner::~SimpleStringOwner() {
2821 \\ string.print("About to destroy");
2822 \\}
2823 });
2824 simple_string_owner_o.root_module.addIncludePath(all_h.dirname());
2825 simple_string_owner_o.root_module.link_libcpp = true;
2826
2827 const exp_stdout =
2828 \\Constructed: a
2829 \\Constructed: b
2830 \\About to destroy: b
2831 \\About to destroy: a
2832 \\Error: Not enough memory!
2833 \\
2834 ;
2835
2836 const exe = addExecutable(b, opts, .{ .name = "main" });
2837 exe.root_module.addObject(main_o);
2838 exe.root_module.addObject(simple_string_o);
2839 exe.root_module.addObject(simple_string_owner_o);
2840 exe.root_module.link_libcpp = true;
2841
2842 const run = addRunArtifact(exe);
2843 run.expectStdOutEqual(exp_stdout);
2844 test_step.dependOn(&run.step);
2845
2846 const check = exe.checkObject();
2847 check.checkInSymtab();
2848 check.checkContains("(was private external) ___gxx_personality_v0");
2849 test_step.dependOn(&check.step);
2850
2851 return test_step;
2852}
2853
2854fn testUnwindInfoNoSubsectionsArm64(b: *Build, opts: Options) *Step {
2855 const test_step = addTestStep(b, "unwind-info-no-subsections-arm64", opts);
2856
2857 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
2858 \\.globl _foo
2859 \\.align 4
2860 \\_foo:
2861 \\ .cfi_startproc
2862 \\ stp x29, x30, [sp, #-32]!
2863 \\ .cfi_def_cfa_offset 32
2864 \\ .cfi_offset w30, -24
2865 \\ .cfi_offset w29, -32
2866 \\ mov x29, sp
2867 \\ .cfi_def_cfa w29, 32
2868 \\ bl _bar
2869 \\ ldp x29, x30, [sp], #32
2870 \\ .cfi_restore w29
2871 \\ .cfi_restore w30
2872 \\ .cfi_def_cfa_offset 0
2873 \\ ret
2874 \\ .cfi_endproc
2875 \\
2876 \\.globl _bar
2877 \\.align 4
2878 \\_bar:
2879 \\ .cfi_startproc
2880 \\ sub sp, sp, #32
2881 \\ .cfi_def_cfa_offset -32
2882 \\ stp x29, x30, [sp, #16]
2883 \\ .cfi_offset w30, -24
2884 \\ .cfi_offset w29, -32
2885 \\ mov x29, sp
2886 \\ .cfi_def_cfa w29, 32
2887 \\ mov w0, #4
2888 \\ ldp x29, x30, [sp, #16]
2889 \\ .cfi_restore w29
2890 \\ .cfi_restore w30
2891 \\ add sp, sp, #32
2892 \\ .cfi_def_cfa_offset 0
2893 \\ ret
2894 \\ .cfi_endproc
2895 });
2896
2897 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2898 \\#include <stdio.h>
2899 \\int foo();
2900 \\int main() {
2901 \\ printf("%d\n", foo());
2902 \\ return 0;
2903 \\}
2904 });
2905 exe.root_module.addObject(a_o);
2906
2907 const run = addRunArtifact(exe);
2908 run.expectStdOutEqual("4\n");
2909 test_step.dependOn(&run.step);
2910
2911 return test_step;
2912}
2913
2914fn testUnwindInfoNoSubsectionsX64(b: *Build, opts: Options) *Step {
2915 const test_step = addTestStep(b, "unwind-info-no-subsections-x64", opts);
2916
2917 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
2918 \\.globl _foo
2919 \\_foo:
2920 \\ .cfi_startproc
2921 \\ push %rbp
2922 \\ .cfi_def_cfa_offset 8
2923 \\ .cfi_offset %rbp, -8
2924 \\ mov %rsp, %rbp
2925 \\ .cfi_def_cfa_register %rbp
2926 \\ call _bar
2927 \\ pop %rbp
2928 \\ .cfi_restore %rbp
2929 \\ .cfi_def_cfa_offset 0
2930 \\ ret
2931 \\ .cfi_endproc
2932 \\
2933 \\.globl _bar
2934 \\_bar:
2935 \\ .cfi_startproc
2936 \\ push %rbp
2937 \\ .cfi_def_cfa_offset 8
2938 \\ .cfi_offset %rbp, -8
2939 \\ mov %rsp, %rbp
2940 \\ .cfi_def_cfa_register %rbp
2941 \\ mov $4, %rax
2942 \\ pop %rbp
2943 \\ .cfi_restore %rbp
2944 \\ .cfi_def_cfa_offset 0
2945 \\ ret
2946 \\ .cfi_endproc
2947 });
2948
2949 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2950 \\#include <stdio.h>
2951 \\int foo();
2952 \\int main() {
2953 \\ printf("%d\n", foo());
2954 \\ return 0;
2955 \\}
2956 });
2957 exe.root_module.addObject(a_o);
2958
2959 const run = addRunArtifact(exe);
2960 run.expectStdOutEqual("4\n");
2961 test_step.dependOn(&run.step);
2962
2963 return test_step;
2964}
2965
2966// Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-binding.s
2967fn testWeakBind(b: *Build, opts: Options) *Step {
2968 const test_step = addTestStep(b, "weak-bind", opts);
2969
2970 const lib = addSharedLibrary(b, opts, .{ .name = "foo", .asm_source_bytes =
2971 \\.globl _weak_dysym
2972 \\.weak_definition _weak_dysym
2973 \\_weak_dysym:
2974 \\ .quad 0x1234
2975 \\
2976 \\.globl _weak_dysym_for_gotpcrel
2977 \\.weak_definition _weak_dysym_for_gotpcrel
2978 \\_weak_dysym_for_gotpcrel:
2979 \\ .quad 0x1234
2980 \\
2981 \\.globl _weak_dysym_fn
2982 \\.weak_definition _weak_dysym_fn
2983 \\_weak_dysym_fn:
2984 \\ ret
2985 \\
2986 \\.section __DATA,__thread_vars,thread_local_variables
2987 \\
2988 \\.globl _weak_dysym_tlv
2989 \\.weak_definition _weak_dysym_tlv
2990 \\_weak_dysym_tlv:
2991 \\ .quad 0x1234
2992 });
2993
2994 {
2995 const check = lib.checkObject();
2996 check.checkInExports();
2997 check.checkExtract("[WEAK] {vmaddr1} _weak_dysym");
2998 check.checkExtract("[WEAK] {vmaddr2} _weak_dysym_for_gotpcrel");
2999 check.checkExtract("[WEAK] {vmaddr3} _weak_dysym_fn");
3000 check.checkExtract("[THREAD_LOCAL, WEAK] {vmaddr4} _weak_dysym_tlv");
3001 test_step.dependOn(&check.step);
3002 }
3003
3004 const exe = addExecutable(b, opts, .{ .name = "main", .asm_source_bytes =
3005 \\.globl _main, _weak_external, _weak_external_for_gotpcrel, _weak_external_fn
3006 \\.weak_definition _weak_external, _weak_external_for_gotpcrel, _weak_external_fn, _weak_internal, _weak_internal_for_gotpcrel, _weak_internal_fn
3007 \\
3008 \\_main:
3009 \\ mov _weak_dysym_for_gotpcrel@GOTPCREL(%rip), %rax
3010 \\ mov _weak_external_for_gotpcrel@GOTPCREL(%rip), %rax
3011 \\ mov _weak_internal_for_gotpcrel@GOTPCREL(%rip), %rax
3012 \\ mov _weak_tlv@TLVP(%rip), %rax
3013 \\ mov _weak_dysym_tlv@TLVP(%rip), %rax
3014 \\ mov _weak_internal_tlv@TLVP(%rip), %rax
3015 \\ callq _weak_dysym_fn
3016 \\ callq _weak_external_fn
3017 \\ callq _weak_internal_fn
3018 \\ mov $0, %rax
3019 \\ ret
3020 \\
3021 \\_weak_external:
3022 \\ .quad 0x1234
3023 \\
3024 \\_weak_external_for_gotpcrel:
3025 \\ .quad 0x1234
3026 \\
3027 \\_weak_external_fn:
3028 \\ ret
3029 \\
3030 \\_weak_internal:
3031 \\ .quad 0x1234
3032 \\
3033 \\_weak_internal_for_gotpcrel:
3034 \\ .quad 0x1234
3035 \\
3036 \\_weak_internal_fn:
3037 \\ ret
3038 \\
3039 \\.data
3040 \\ .quad _weak_dysym
3041 \\ .quad _weak_external + 2
3042 \\ .quad _weak_internal
3043 \\
3044 \\.tbss _weak_tlv$tlv$init, 4, 2
3045 \\.tbss _weak_internal_tlv$tlv$init, 4, 2
3046 \\
3047 \\.section __DATA,__thread_vars,thread_local_variables
3048 \\.globl _weak_tlv
3049 \\.weak_definition _weak_tlv, _weak_internal_tlv
3050 \\
3051 \\_weak_tlv:
3052 \\ .quad __tlv_bootstrap
3053 \\ .quad 0
3054 \\ .quad _weak_tlv$tlv$init
3055 \\
3056 \\_weak_internal_tlv:
3057 \\ .quad __tlv_bootstrap
3058 \\ .quad 0
3059 \\ .quad _weak_internal_tlv$tlv$init
3060 });
3061 exe.root_module.linkLibrary(lib);
3062
3063 {
3064 const check = exe.checkObject();
3065
3066 check.checkInExports();
3067 check.checkExtract("[WEAK] {vmaddr1} _weak_external");
3068 check.checkExtract("[WEAK] {vmaddr2} _weak_external_for_gotpcrel");
3069 check.checkExtract("[WEAK] {vmaddr3} _weak_external_fn");
3070 check.checkExtract("[THREAD_LOCAL, WEAK] {vmaddr4} _weak_tlv");
3071
3072 check.checkInDyldBind();
3073 check.checkContains("(libfoo.dylib) _weak_dysym_for_gotpcrel");
3074 check.checkContains("(libfoo.dylib) _weak_dysym_fn");
3075 check.checkContains("(libfoo.dylib) _weak_dysym");
3076 check.checkContains("(libfoo.dylib) _weak_dysym_tlv");
3077
3078 check.checkInDyldWeakBind();
3079 check.checkContains("_weak_external_for_gotpcrel");
3080 check.checkContains("_weak_dysym_for_gotpcrel");
3081 check.checkContains("_weak_external_fn");
3082 check.checkContains("_weak_dysym_fn");
3083 check.checkContains("_weak_dysym");
3084 check.checkContains("_weak_external");
3085 check.checkContains("_weak_tlv");
3086 check.checkContains("_weak_dysym_tlv");
3087
3088 test_step.dependOn(&check.step);
3089 }
3090
3091 const run = addRunArtifact(exe);
3092 run.expectExitCode(0);
3093 test_step.dependOn(&run.step);
3094
3095 return test_step;
3096}
3097
3098fn testWeakFramework(b: *Build, opts: Options) *Step {
3099 const test_step = addTestStep(b, "weak-framework", opts);
3100
3101 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
3102 exe.root_module.linkFramework("Cocoa", .{ .weak = true });
3103
3104 const run = addRunArtifact(exe);
3105 run.expectExitCode(0);
3106 test_step.dependOn(&run.step);
3107
3108 const check = exe.checkObject();
3109 check.checkInHeaders();
3110 check.checkExact("cmd LOAD_WEAK_DYLIB");
3111 check.checkContains("Cocoa");
3112 test_step.dependOn(&check.step);
3113
3114 return test_step;
3115}
3116
3117fn testWeakLibrary(b: *Build, opts: Options) *Step {
3118 const test_step = addTestStep(b, "weak-library", opts);
3119
3120 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes =
3121 \\#include<stdio.h>
3122 \\int a = 42;
3123 \\const char* asStr() {
3124 \\ static char str[3];
3125 \\ sprintf(str, "%d", 42);
3126 \\ return str;
3127 \\}
3128 });
3129
3130 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
3131 \\#include<stdio.h>
3132 \\extern int a;
3133 \\extern const char* asStr();
3134 \\int main() {
3135 \\ printf("%d %s", a, asStr());
3136 \\ return 0;
3137 \\}
3138 });
3139 exe.root_module.linkSystemLibrary("a", .{ .weak = true });
3140 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
3141 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
3142
3143 const check = exe.checkObject();
3144 check.checkInHeaders();
3145 check.checkExact("cmd LOAD_WEAK_DYLIB");
3146 check.checkContains("liba.dylib");
3147 check.checkInSymtab();
3148 check.checkExact("(undefined) weakref external _a (from liba)");
3149 check.checkInSymtab();
3150 check.checkExact("(undefined) weakref external _asStr (from liba)");
3151 test_step.dependOn(&check.step);
3152
3153 const run = addRunArtifact(exe);
3154 run.expectStdOutEqual("42 42");
3155 test_step.dependOn(&run.step);
3156
3157 return test_step;
3158}
3159
3160fn testWeakRef(b: *Build, opts: Options) *Step {
3161 const test_step = addTestStep(b, "weak-ref", opts);
3162
3163 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
3164 \\#include <stdio.h>
3165 \\#include <sys/_types/_fd_def.h>
3166 \\int main(int argc, char** argv) {
3167 \\ printf("__darwin_check_fd_set_overflow: %p\n", __darwin_check_fd_set_overflow);
3168 \\}
3169 });
3170
3171 const check = exe.checkObject();
3172 check.checkInSymtab();
3173 check.checkExact("(undefined) weakref external ___darwin_check_fd_set_overflow (from libSystem.B)");
3174 test_step.dependOn(&check.step);
3175
3176 return test_step;
3177}
3178
3179fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
3180 return link.addTestStep(b, "" ++ prefix, opts);
3181}
3182
3183const builtin = @import("builtin");
3184const addAsmSourceBytes = link.addAsmSourceBytes;
3185const addCSourceBytes = link.addCSourceBytes;
3186const addRunArtifact = link.addRunArtifact;
3187const addObject = link.addObject;
3188const addExecutable = link.addExecutable;
3189const addStaticLibrary = link.addStaticLibrary;
3190const addSharedLibrary = link.addSharedLibrary;
3191const expectLinkErrors = link.expectLinkErrors;
3192const link = @import("link.zig");
3193const std = @import("std");
3194
3195const Build = std.Build;
3196const BuildOptions = link.BuildOptions;
3197const Compile = Step.Compile;
3198const Options = link.Options;
3199const Step = Build.Step;
3200const WriteFile = Step.WriteFile;