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;