master
   1pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
   2    _ = build_opts;
   3    const elf_step = b.step("test-elf", "Run ELF tests");
   4
   5    // https://github.com/ziglang/zig/issues/25323
   6    if (builtin.os.tag == .freebsd) return elf_step;
   7
   8    // https://github.com/ziglang/zig/issues/25961
   9    if (comptime builtin.cpu.arch.endian() == .big) return elf_step;
  10
  11    const default_target = b.resolveTargetQuery(.{
  12        .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs
  13        .os_tag = .linux,
  14    });
  15    const x86_64_musl = b.resolveTargetQuery(.{
  16        .cpu_arch = .x86_64,
  17        .os_tag = .linux,
  18        .abi = .musl,
  19    });
  20    const x86_64_gnu = b.resolveTargetQuery(.{
  21        .cpu_arch = .x86_64,
  22        .os_tag = .linux,
  23        .abi = .gnu,
  24    });
  25    const aarch64_musl = b.resolveTargetQuery(.{
  26        .cpu_arch = .aarch64,
  27        .os_tag = .linux,
  28        .abi = .musl,
  29    });
  30    const riscv64_musl = b.resolveTargetQuery(.{
  31        .cpu_arch = .riscv64,
  32        .os_tag = .linux,
  33        .abi = .musl,
  34    });
  35
  36    // Common tests
  37    for (&[_]std.Target.Cpu.Arch{
  38        .x86_64,
  39        .aarch64,
  40    }) |cpu_arch| {
  41        const musl_target = b.resolveTargetQuery(.{
  42            .cpu_arch = cpu_arch,
  43            .os_tag = .linux,
  44            .abi = .musl,
  45        });
  46        const gnu_target = b.resolveTargetQuery(.{
  47            .cpu_arch = cpu_arch,
  48            .os_tag = .linux,
  49            .abi = .gnu,
  50        });
  51
  52        // Exercise linker in -r mode
  53        elf_step.dependOn(testEmitRelocatable(b, .{ .target = musl_target }));
  54        elf_step.dependOn(testRelocatableArchive(b, .{ .target = musl_target }));
  55        elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = musl_target }));
  56        elf_step.dependOn(testRelocatableEhFrameComdatHeavy(b, .{ .target = musl_target }));
  57        elf_step.dependOn(testRelocatableNoEhFrame(b, .{ .target = musl_target }));
  58
  59        // Exercise linker in ar mode
  60        elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target }));
  61        elf_step.dependOn(testEmitStaticLibZig(b, .{ .target = musl_target }));
  62
  63        // Exercise linker with LLVM backend
  64        // musl tests
  65        elf_step.dependOn(testAbsSymbols(b, .{ .target = musl_target }));
  66        elf_step.dependOn(testComdatElimination(b, .{ .target = musl_target }));
  67        elf_step.dependOn(testCommonSymbols(b, .{ .target = musl_target }));
  68        elf_step.dependOn(testCommonSymbolsInArchive(b, .{ .target = musl_target }));
  69        elf_step.dependOn(testCommentString(b, .{ .target = musl_target }));
  70        elf_step.dependOn(testEmptyObject(b, .{ .target = musl_target }));
  71        elf_step.dependOn(testEntryPoint(b, .{ .target = musl_target }));
  72        elf_step.dependOn(testGcSections(b, .{ .target = musl_target }));
  73        elf_step.dependOn(testGcSectionsZig(b, .{ .target = musl_target }));
  74        elf_step.dependOn(testImageBase(b, .{ .target = musl_target }));
  75        elf_step.dependOn(testInitArrayOrder(b, .{ .target = musl_target }));
  76        elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = musl_target }));
  77        // https://github.com/ziglang/zig/issues/17449
  78        // elf_step.dependOn(testLargeBss(b, .{ .target = musl_target }));
  79        elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));
  80        elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target }));
  81        elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));
  82        elf_step.dependOn(testLinksection(b, .{ .target = musl_target }));
  83        elf_step.dependOn(testMergeStrings(b, .{ .target = musl_target }));
  84        elf_step.dependOn(testMergeStrings2(b, .{ .target = musl_target }));
  85        // https://github.com/ziglang/zig/issues/17451
  86        // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = musl_target }));
  87        elf_step.dependOn(testTlsStatic(b, .{ .target = musl_target }));
  88        elf_step.dependOn(testStrip(b, .{ .target = musl_target }));
  89
  90        // glibc tests
  91        elf_step.dependOn(testAsNeeded(b, .{ .target = gnu_target }));
  92        // https://github.com/ziglang/zig/issues/17430
  93        // elf_step.dependOn(testCanonicalPlt(b, .{ .target = gnu_target }));
  94        elf_step.dependOn(testCommentString(b, .{ .target = gnu_target }));
  95        elf_step.dependOn(testCopyrel(b, .{ .target = gnu_target }));
  96        // https://github.com/ziglang/zig/issues/17430
  97        // elf_step.dependOn(testCopyrelAlias(b, .{ .target = gnu_target }));
  98        // https://github.com/ziglang/zig/issues/17430
  99        // elf_step.dependOn(testCopyrelAlignment(b, .{ .target = gnu_target }));
 100        elf_step.dependOn(testDsoPlt(b, .{ .target = gnu_target }));
 101        elf_step.dependOn(testDsoUndef(b, .{ .target = gnu_target }));
 102        elf_step.dependOn(testExportDynamic(b, .{ .target = gnu_target }));
 103        elf_step.dependOn(testExportSymbolsFromExe(b, .{ .target = gnu_target }));
 104        // https://github.com/ziglang/zig/issues/17430
 105        // elf_step.dependOn(testFuncAddress(b, .{ .target = gnu_target }));
 106        elf_step.dependOn(testHiddenWeakUndef(b, .{ .target = gnu_target }));
 107        elf_step.dependOn(testIFuncAlias(b, .{ .target = gnu_target }));
 108        // https://github.com/ziglang/zig/issues/17430
 109        // elf_step.dependOn(testIFuncDlopen(b, .{ .target = gnu_target }));
 110        elf_step.dependOn(testIFuncDso(b, .{ .target = gnu_target }));
 111        elf_step.dependOn(testIFuncDynamic(b, .{ .target = gnu_target }));
 112        elf_step.dependOn(testIFuncExport(b, .{ .target = gnu_target }));
 113        elf_step.dependOn(testIFuncFuncPtr(b, .{ .target = gnu_target }));
 114        elf_step.dependOn(testIFuncNoPlt(b, .{ .target = gnu_target }));
 115        // https://github.com/ziglang/zig/issues/17430 ??
 116        // elf_step.dependOn(testIFuncStatic(b, .{ .target = gnu_target }));
 117        // elf_step.dependOn(testIFuncStaticPie(b, .{ .target = gnu_target }));
 118        elf_step.dependOn(testInitArrayOrder(b, .{ .target = gnu_target }));
 119        elf_step.dependOn(testLargeAlignmentDso(b, .{ .target = gnu_target }));
 120        elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = gnu_target }));
 121        elf_step.dependOn(testLargeBss(b, .{ .target = gnu_target }));
 122        elf_step.dependOn(testLinkOrder(b, .{ .target = gnu_target }));
 123        elf_step.dependOn(testLdScript(b, .{ .target = gnu_target }));
 124        // https://github.com/ziglang/zig/issues/23125
 125        // elf_step.dependOn(testLdScriptPathError(b, .{ .target = gnu_target }));
 126        elf_step.dependOn(testLdScriptAllowUndefinedVersion(b, .{ .target = gnu_target, .use_lld = true }));
 127        elf_step.dependOn(testLdScriptDisallowUndefinedVersion(b, .{ .target = gnu_target, .use_lld = true }));
 128        // https://github.com/ziglang/zig/issues/17451
 129        // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = gnu_target }));
 130        elf_step.dependOn(testPie(b, .{ .target = gnu_target }));
 131        elf_step.dependOn(testPltGot(b, .{ .target = gnu_target }));
 132        elf_step.dependOn(testPreinitArray(b, .{ .target = gnu_target }));
 133        elf_step.dependOn(testSharedAbsSymbol(b, .{ .target = gnu_target }));
 134        elf_step.dependOn(testTlsDfStaticTls(b, .{ .target = gnu_target }));
 135        elf_step.dependOn(testTlsDso(b, .{ .target = gnu_target }));
 136        elf_step.dependOn(testTlsGd(b, .{ .target = gnu_target }));
 137        elf_step.dependOn(testTlsGdNoPlt(b, .{ .target = gnu_target }));
 138        elf_step.dependOn(testTlsGdToIe(b, .{ .target = gnu_target }));
 139        elf_step.dependOn(testTlsIe(b, .{ .target = gnu_target }));
 140        elf_step.dependOn(testTlsLargeAlignment(b, .{ .target = gnu_target }));
 141        elf_step.dependOn(testTlsLargeTbss(b, .{ .target = gnu_target }));
 142        elf_step.dependOn(testTlsLargeStaticImage(b, .{ .target = gnu_target }));
 143        elf_step.dependOn(testTlsLd(b, .{ .target = gnu_target }));
 144        elf_step.dependOn(testTlsLdDso(b, .{ .target = gnu_target }));
 145        elf_step.dependOn(testTlsLdNoPlt(b, .{ .target = gnu_target }));
 146        // https://github.com/ziglang/zig/issues/17430
 147        // elf_step.dependOn(testTlsNoPic(b, .{ .target = gnu_target }));
 148        elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = gnu_target }));
 149        elf_step.dependOn(testTlsPic(b, .{ .target = gnu_target }));
 150        elf_step.dependOn(testTlsSmallAlignment(b, .{ .target = gnu_target }));
 151        elf_step.dependOn(testUnknownFileTypeError(b, .{ .target = gnu_target }));
 152        elf_step.dependOn(testUnresolvedError(b, .{ .target = gnu_target }));
 153        elf_step.dependOn(testWeakExports(b, .{ .target = gnu_target }));
 154        elf_step.dependOn(testWeakUndefsDso(b, .{ .target = gnu_target }));
 155        elf_step.dependOn(testZNow(b, .{ .target = gnu_target }));
 156        elf_step.dependOn(testZStackSize(b, .{ .target = gnu_target }));
 157    }
 158
 159    // x86_64 specific tests
 160    elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = x86_64_musl }));
 161    elf_step.dependOn(testZText(b, .{ .target = x86_64_gnu }));
 162
 163    // aarch64 specific tests
 164    elf_step.dependOn(testThunks(b, .{ .target = aarch64_musl }));
 165
 166    // x86_64 self-hosted backend
 167    elf_step.dependOn(testCommentString(b, .{ .use_llvm = false, .target = default_target }));
 168    elf_step.dependOn(testCommentStringStaticLib(b, .{ .use_llvm = false, .target = default_target }));
 169    elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = x86_64_musl }));
 170    elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = x86_64_musl }));
 171    elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));
 172    elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target }));
 173    elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target }));
 174    elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target }));
 175    elf_step.dependOn(testLinksection(b, .{ .use_llvm = false, .target = default_target }));
 176    elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = x86_64_gnu }));
 177    elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = x86_64_musl }));
 178
 179    // riscv64 linker backend is currently not complete enough to support more
 180    elf_step.dependOn(testLinkingC(b, .{ .target = riscv64_musl }));
 181
 182    return elf_step;
 183}
 184
 185fn testAbsSymbols(b: *Build, opts: Options) *Step {
 186    const test_step = addTestStep(b, "abs-symbols", opts);
 187
 188    const obj = addObject(b, opts, .{
 189        .name = "obj",
 190        .asm_source_bytes =
 191        \\.globl foo
 192        \\foo = 0x800008
 193        \\
 194        ,
 195    });
 196
 197    const exe = addExecutable(b, opts, .{
 198        .name = "test",
 199        .c_source_bytes =
 200        \\#include <signal.h>
 201        \\#include <stdio.h>
 202        \\#include <stdlib.h>
 203        \\#include <ucontext.h>
 204        \\#include <assert.h>
 205        \\void handler(int signum, siginfo_t *info, void *ptr) {
 206        \\  assert((size_t)info->si_addr == 0x800008);
 207        \\  exit(0);
 208        \\}
 209        \\extern int foo;
 210        \\int main() {
 211        \\  struct sigaction act;
 212        \\  act.sa_flags = SA_SIGINFO | SA_RESETHAND;
 213        \\  act.sa_sigaction = handler;
 214        \\  sigemptyset(&act.sa_mask);
 215        \\  sigaction(SIGSEGV, &act, 0);
 216        \\  foo = 5;
 217        \\  return 0;
 218        \\}
 219        ,
 220    });
 221    exe.root_module.addObject(obj);
 222    exe.root_module.link_libc = true;
 223
 224    const run = addRunArtifact(exe);
 225    run.expectExitCode(0);
 226    test_step.dependOn(&run.step);
 227
 228    return test_step;
 229}
 230
 231fn testAsNeeded(b: *Build, opts: Options) *Step {
 232    const test_step = addTestStep(b, "as-needed", opts);
 233
 234    const main_o = addObject(b, opts, .{
 235        .name = "main",
 236        .c_source_bytes =
 237        \\#include <stdio.h>
 238        \\int baz();
 239        \\int main() {
 240        \\  printf("%d\n", baz());
 241        \\  return 0;
 242        \\}
 243        \\
 244        ,
 245    });
 246    main_o.root_module.link_libc = true;
 247
 248    const libfoo = addSharedLibrary(b, opts, .{ .name = "foo" });
 249    addCSourceBytes(libfoo, "int foo() { return 42; }", &.{});
 250
 251    const libbar = addSharedLibrary(b, opts, .{ .name = "bar" });
 252    addCSourceBytes(libbar, "int bar() { return 42; }", &.{});
 253
 254    const libbaz = addSharedLibrary(b, opts, .{ .name = "baz" });
 255    addCSourceBytes(libbaz,
 256        \\int foo();
 257        \\int baz() { return foo(); }
 258    , &.{});
 259
 260    {
 261        const exe = addExecutable(b, opts, .{
 262            .name = "test",
 263        });
 264        exe.root_module.addObject(main_o);
 265        exe.root_module.linkSystemLibrary("foo", .{ .needed = true });
 266        exe.root_module.addLibraryPath(libfoo.getEmittedBinDirectory());
 267        exe.root_module.addRPath(libfoo.getEmittedBinDirectory());
 268        exe.root_module.linkSystemLibrary("bar", .{ .needed = true });
 269        exe.root_module.addLibraryPath(libbar.getEmittedBinDirectory());
 270        exe.root_module.addRPath(libbar.getEmittedBinDirectory());
 271        exe.root_module.linkSystemLibrary("baz", .{ .needed = true });
 272        exe.root_module.addLibraryPath(libbaz.getEmittedBinDirectory());
 273        exe.root_module.addRPath(libbaz.getEmittedBinDirectory());
 274        exe.root_module.link_libc = true;
 275
 276        const run = addRunArtifact(exe);
 277        run.expectStdOutEqual("42\n");
 278        test_step.dependOn(&run.step);
 279
 280        const check = exe.checkObject();
 281        check.checkInDynamicSection();
 282        check.checkExact("NEEDED libfoo.so");
 283        check.checkExact("NEEDED libbar.so");
 284        check.checkExact("NEEDED libbaz.so");
 285        test_step.dependOn(&check.step);
 286    }
 287
 288    {
 289        const exe = addExecutable(b, opts, .{
 290            .name = "test",
 291        });
 292        exe.root_module.addObject(main_o);
 293        exe.root_module.linkSystemLibrary("foo", .{ .needed = false });
 294        exe.root_module.addLibraryPath(libfoo.getEmittedBinDirectory());
 295        exe.root_module.addRPath(libfoo.getEmittedBinDirectory());
 296        exe.root_module.linkSystemLibrary("bar", .{ .needed = false });
 297        exe.root_module.addLibraryPath(libbar.getEmittedBinDirectory());
 298        exe.root_module.addRPath(libbar.getEmittedBinDirectory());
 299        exe.root_module.linkSystemLibrary("baz", .{ .needed = false });
 300        exe.root_module.addLibraryPath(libbaz.getEmittedBinDirectory());
 301        exe.root_module.addRPath(libbaz.getEmittedBinDirectory());
 302        exe.root_module.link_libc = true;
 303
 304        const run = addRunArtifact(exe);
 305        run.expectStdOutEqual("42\n");
 306        test_step.dependOn(&run.step);
 307
 308        const check = exe.checkObject();
 309        check.checkInDynamicSection();
 310        check.checkNotPresent("NEEDED libbar.so");
 311        check.checkInDynamicSection();
 312        check.checkExact("NEEDED libfoo.so");
 313        check.checkExact("NEEDED libbaz.so");
 314        test_step.dependOn(&check.step);
 315    }
 316
 317    return test_step;
 318}
 319
 320fn testCanonicalPlt(b: *Build, opts: Options) *Step {
 321    const test_step = addTestStep(b, "canonical-plt", opts);
 322
 323    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
 324    addCSourceBytes(dso,
 325        \\void *foo() {
 326        \\  return foo;
 327        \\}
 328        \\void *bar() {
 329        \\  return bar;
 330        \\}
 331    , &.{});
 332
 333    const b_o = addObject(b, opts, .{
 334        .name = "obj",
 335        .c_source_bytes =
 336        \\void *bar();
 337        \\void *baz() {
 338        \\  return bar;
 339        \\}
 340        \\
 341        ,
 342        .pic = true,
 343    });
 344
 345    const main_o = addObject(b, opts, .{
 346        .name = "main",
 347        .c_source_bytes =
 348        \\#include <assert.h>
 349        \\void *foo();
 350        \\void *bar();
 351        \\void *baz();
 352        \\int main() {
 353        \\  assert(foo == foo());
 354        \\  assert(bar == bar());
 355        \\  assert(bar == baz());
 356        \\  return 0;
 357        \\}
 358        \\
 359        ,
 360        .pic = false,
 361    });
 362    main_o.root_module.link_libc = true;
 363
 364    const exe = addExecutable(b, opts, .{
 365        .name = "main",
 366    });
 367    exe.root_module.addObject(main_o);
 368    exe.root_module.addObject(b_o);
 369    exe.root_module.linkLibrary(dso);
 370    exe.root_module.link_libc = true;
 371    exe.pie = false;
 372
 373    const run = addRunArtifact(exe);
 374    run.expectExitCode(0);
 375    test_step.dependOn(&run.step);
 376
 377    return test_step;
 378}
 379
 380fn testComdatElimination(b: *Build, opts: Options) *Step {
 381    const test_step = addTestStep(b, "comdat-elimination", opts);
 382
 383    const a_o = addObject(b, opts, .{
 384        .name = "a",
 385        .cpp_source_bytes =
 386        \\#include <stdio.h>
 387        \\inline void foo() {
 388        \\  printf("calling foo in a\n");
 389        \\}
 390        \\void hello() {
 391        \\  foo();
 392        \\}
 393        ,
 394    });
 395    a_o.root_module.link_libcpp = true;
 396
 397    const main_o = addObject(b, opts, .{
 398        .name = "main",
 399        .cpp_source_bytes =
 400        \\#include <stdio.h>
 401        \\inline void foo() {
 402        \\  printf("calling foo in main\n");
 403        \\}
 404        \\void hello();
 405        \\int main() {
 406        \\  foo();
 407        \\  hello();
 408        \\  return 0;
 409        \\}
 410        ,
 411    });
 412    main_o.root_module.link_libcpp = true;
 413
 414    {
 415        const exe = addExecutable(b, opts, .{ .name = "main1" });
 416        exe.root_module.addObject(a_o);
 417        exe.root_module.addObject(main_o);
 418        exe.root_module.link_libcpp = true;
 419
 420        const run = addRunArtifact(exe);
 421        run.expectStdOutEqual(
 422            \\calling foo in a
 423            \\calling foo in a
 424            \\
 425        );
 426        test_step.dependOn(&run.step);
 427    }
 428
 429    {
 430        const exe = addExecutable(b, opts, .{ .name = "main2" });
 431        exe.root_module.addObject(main_o);
 432        exe.root_module.addObject(a_o);
 433        exe.root_module.link_libcpp = true;
 434
 435        const run = addRunArtifact(exe);
 436        run.expectStdOutEqual(
 437            \\calling foo in main
 438            \\calling foo in main
 439            \\
 440        );
 441        test_step.dependOn(&run.step);
 442    }
 443
 444    {
 445        const c_o = addObject(b, opts, .{ .name = "c" });
 446        c_o.root_module.addObject(main_o);
 447        c_o.root_module.addObject(a_o);
 448
 449        const exe = addExecutable(b, opts, .{ .name = "main3" });
 450        exe.root_module.addObject(c_o);
 451        exe.root_module.link_libcpp = true;
 452
 453        const run = addRunArtifact(exe);
 454        run.expectStdOutEqual(
 455            \\calling foo in main
 456            \\calling foo in main
 457            \\
 458        );
 459        test_step.dependOn(&run.step);
 460    }
 461
 462    {
 463        const d_o = addObject(b, opts, .{ .name = "d" });
 464        d_o.root_module.addObject(a_o);
 465        d_o.root_module.addObject(main_o);
 466
 467        const exe = addExecutable(b, opts, .{ .name = "main4" });
 468        exe.root_module.addObject(d_o);
 469        exe.root_module.link_libcpp = true;
 470
 471        const run = addRunArtifact(exe);
 472        run.expectStdOutEqual(
 473            \\calling foo in a
 474            \\calling foo in a
 475            \\
 476        );
 477        test_step.dependOn(&run.step);
 478    }
 479
 480    return test_step;
 481}
 482
 483fn testCommentString(b: *Build, opts: Options) *Step {
 484    const test_step = addTestStep(b, "comment-string", opts);
 485
 486    const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
 487        \\pub fn main() void {}
 488    });
 489
 490    const check = exe.checkObject();
 491    check.dumpSection(".comment");
 492    check.checkContains("zig");
 493    test_step.dependOn(&check.step);
 494
 495    return test_step;
 496}
 497
 498fn testCommentStringStaticLib(b: *Build, opts: Options) *Step {
 499    const test_step = addTestStep(b, "comment-string-static-lib", opts);
 500
 501    const lib = addStaticLibrary(b, opts, .{ .name = "lib", .zig_source_bytes =
 502        \\export fn foo() void {}
 503    });
 504
 505    const check = lib.checkObject();
 506    check.dumpSection(".comment");
 507    check.checkContains("zig");
 508    test_step.dependOn(&check.step);
 509
 510    return test_step;
 511}
 512
 513fn testCommonSymbols(b: *Build, opts: Options) *Step {
 514    const test_step = addTestStep(b, "common-symbols", opts);
 515
 516    const exe = addExecutable(b, opts, .{
 517        .name = "test",
 518    });
 519    addCSourceBytes(exe,
 520        \\int foo;
 521        \\int bar;
 522        \\int baz = 42;
 523    , &.{"-fcommon"});
 524    addCSourceBytes(exe,
 525        \\#include<stdio.h>
 526        \\int foo;
 527        \\int bar = 5;
 528        \\int baz;
 529        \\int main() {
 530        \\  printf("%d %d %d\n", foo, bar, baz);
 531        \\}
 532    , &.{"-fcommon"});
 533    exe.root_module.link_libc = true;
 534
 535    const run = addRunArtifact(exe);
 536    run.expectStdOutEqual("0 5 42\n");
 537    test_step.dependOn(&run.step);
 538
 539    return test_step;
 540}
 541
 542fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step {
 543    const test_step = addTestStep(b, "common-symbols-in-archive", opts);
 544
 545    const a_o = addObject(b, opts, .{
 546        .name = "a",
 547        .c_source_bytes =
 548        \\#include <stdio.h>
 549        \\int foo;
 550        \\int bar;
 551        \\extern int baz;
 552        \\__attribute__((weak)) int two();
 553        \\int main() {
 554        \\  printf("%d %d %d %d\n", foo, bar, baz, two ? two() : -1);
 555        \\}
 556        \\
 557        ,
 558        .c_source_flags = &.{"-fcommon"},
 559    });
 560    a_o.root_module.link_libc = true;
 561
 562    const b_o = addObject(b, opts, .{
 563        .name = "b",
 564        .c_source_bytes = "int foo = 5;",
 565        .c_source_flags = &.{"-fcommon"},
 566    });
 567
 568    {
 569        const c_o = addObject(b, opts, .{
 570            .name = "c",
 571            .c_source_bytes =
 572            \\int bar;
 573            \\int two() { return 2; }
 574            \\
 575            ,
 576            .c_source_flags = &.{"-fcommon"},
 577        });
 578
 579        const d_o = addObject(b, opts, .{
 580            .name = "d",
 581            .c_source_bytes = "int baz;",
 582            .c_source_flags = &.{"-fcommon"},
 583        });
 584
 585        const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
 586        lib.root_module.addObject(b_o);
 587        lib.root_module.addObject(c_o);
 588        lib.root_module.addObject(d_o);
 589
 590        const exe = addExecutable(b, opts, .{
 591            .name = "test",
 592        });
 593        exe.root_module.addObject(a_o);
 594        exe.root_module.linkLibrary(lib);
 595        exe.root_module.link_libc = true;
 596
 597        const run = addRunArtifact(exe);
 598        run.expectStdOutEqual("5 0 0 -1\n");
 599        test_step.dependOn(&run.step);
 600    }
 601
 602    {
 603        const e_o = addObject(b, opts, .{
 604            .name = "e",
 605            .c_source_bytes =
 606            \\int bar = 0;
 607            \\int baz = 7;
 608            \\int two() { return 2; }
 609            ,
 610            .c_source_flags = &.{"-fcommon"},
 611        });
 612
 613        const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
 614        lib.root_module.addObject(b_o);
 615        lib.root_module.addObject(e_o);
 616
 617        const exe = addExecutable(b, opts, .{
 618            .name = "test",
 619        });
 620        exe.root_module.addObject(a_o);
 621        exe.root_module.linkLibrary(lib);
 622        exe.root_module.link_libc = true;
 623
 624        const run = addRunArtifact(exe);
 625        run.expectStdOutEqual("5 0 7 2\n");
 626        test_step.dependOn(&run.step);
 627    }
 628
 629    return test_step;
 630}
 631
 632fn testCopyrel(b: *Build, opts: Options) *Step {
 633    const test_step = addTestStep(b, "copyrel", opts);
 634
 635    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
 636    addCSourceBytes(dso,
 637        \\int foo = 3;
 638        \\int bar = 5;
 639    , &.{});
 640
 641    const exe = addExecutable(b, opts, .{
 642        .name = "main",
 643        .c_source_bytes =
 644        \\#include<stdio.h>
 645        \\extern int foo, bar;
 646        \\int main() {
 647        \\  printf("%d %d\n", foo, bar);
 648        \\  return 0;
 649        \\}
 650        ,
 651    });
 652    exe.root_module.linkLibrary(dso);
 653    exe.root_module.link_libc = true;
 654
 655    const run = addRunArtifact(exe);
 656    run.expectStdOutEqual("3 5\n");
 657    test_step.dependOn(&run.step);
 658
 659    return test_step;
 660}
 661
 662fn testCopyrelAlias(b: *Build, opts: Options) *Step {
 663    const test_step = addTestStep(b, "copyrel-alias", opts);
 664
 665    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
 666    addCSourceBytes(dso,
 667        \\int bruh = 31;
 668        \\int foo = 42;
 669        \\extern int bar __attribute__((alias("foo")));
 670        \\extern int baz __attribute__((alias("foo")));
 671    , &.{});
 672
 673    const exe = addExecutable(b, opts, .{
 674        .name = "main",
 675        .pic = false,
 676    });
 677    addCSourceBytes(exe,
 678        \\#include<stdio.h>
 679        \\extern int foo;
 680        \\extern int *get_bar();
 681        \\int main() {
 682        \\  printf("%d %d %d\n", foo, *get_bar(), &foo == get_bar());
 683        \\  return 0;
 684        \\}
 685    , &.{});
 686    addCSourceBytes(exe,
 687        \\extern int bar;
 688        \\int *get_bar() { return &bar; }
 689    , &.{});
 690    exe.root_module.linkLibrary(dso);
 691    exe.root_module.link_libc = true;
 692    exe.pie = false;
 693
 694    const run = addRunArtifact(exe);
 695    run.expectStdOutEqual("42 42 1\n");
 696    test_step.dependOn(&run.step);
 697
 698    return test_step;
 699}
 700
 701fn testCopyrelAlignment(b: *Build, opts: Options) *Step {
 702    const test_step = addTestStep(b, "copyrel-alignment", opts);
 703
 704    const a_so = addSharedLibrary(b, opts, .{ .name = "a" });
 705    addCSourceBytes(a_so, "__attribute__((aligned(32))) int foo = 5;", &.{});
 706
 707    const b_so = addSharedLibrary(b, opts, .{ .name = "b" });
 708    addCSourceBytes(b_so, "__attribute__((aligned(8))) int foo = 5;", &.{});
 709
 710    const c_so = addSharedLibrary(b, opts, .{ .name = "c" });
 711    addCSourceBytes(c_so, "__attribute__((aligned(256))) int foo = 5;", &.{});
 712
 713    const obj = addObject(b, opts, .{
 714        .name = "main",
 715        .c_source_bytes =
 716        \\#include <stdio.h>
 717        \\extern int foo;
 718        \\int main() { printf("%d\n", foo); }
 719        \\
 720        ,
 721        .pic = false,
 722    });
 723    obj.root_module.link_libc = true;
 724
 725    const exp_stdout = "5\n";
 726
 727    {
 728        const exe = addExecutable(b, opts, .{ .name = "main" });
 729        exe.root_module.addObject(obj);
 730        exe.root_module.linkLibrary(a_so);
 731        exe.root_module.link_libc = true;
 732        exe.pie = false;
 733
 734        const run = addRunArtifact(exe);
 735        run.expectStdOutEqual(exp_stdout);
 736        test_step.dependOn(&run.step);
 737
 738        const check = exe.checkObject();
 739        check.checkInHeaders();
 740        check.checkExact("section headers");
 741        check.checkExact("name .copyrel");
 742        check.checkExact("addralign 20");
 743        test_step.dependOn(&check.step);
 744    }
 745
 746    {
 747        const exe = addExecutable(b, opts, .{ .name = "main" });
 748        exe.root_module.addObject(obj);
 749        exe.root_module.linkLibrary(b_so);
 750        exe.root_module.link_libc = true;
 751        exe.pie = false;
 752
 753        const run = addRunArtifact(exe);
 754        run.expectStdOutEqual(exp_stdout);
 755        test_step.dependOn(&run.step);
 756
 757        const check = exe.checkObject();
 758        check.checkInHeaders();
 759        check.checkExact("section headers");
 760        check.checkExact("name .copyrel");
 761        check.checkExact("addralign 8");
 762        test_step.dependOn(&check.step);
 763    }
 764
 765    {
 766        const exe = addExecutable(b, opts, .{ .name = "main" });
 767        exe.root_module.addObject(obj);
 768        exe.root_module.linkLibrary(c_so);
 769        exe.root_module.link_libc = true;
 770        exe.pie = false;
 771
 772        const run = addRunArtifact(exe);
 773        run.expectStdOutEqual(exp_stdout);
 774        test_step.dependOn(&run.step);
 775
 776        const check = exe.checkObject();
 777        check.checkInHeaders();
 778        check.checkExact("section headers");
 779        check.checkExact("name .copyrel");
 780        check.checkExact("addralign 100");
 781        test_step.dependOn(&check.step);
 782    }
 783
 784    return test_step;
 785}
 786
 787fn testDsoPlt(b: *Build, opts: Options) *Step {
 788    const test_step = addTestStep(b, "dso-plt", opts);
 789
 790    const dso = addSharedLibrary(b, opts, .{ .name = "dso" });
 791    addCSourceBytes(dso,
 792        \\#include<stdio.h>
 793        \\void world() {
 794        \\  printf("world\n");
 795        \\}
 796        \\void real_hello() {
 797        \\  printf("Hello ");
 798        \\  world();
 799        \\}
 800        \\void hello() {
 801        \\  real_hello();
 802        \\}
 803    , &.{});
 804    dso.root_module.link_libc = true;
 805
 806    const exe = addExecutable(b, opts, .{ .name = "test" });
 807    addCSourceBytes(exe,
 808        \\#include<stdio.h>
 809        \\void world() {
 810        \\  printf("WORLD\n");
 811        \\}
 812        \\void hello();
 813        \\int main() {
 814        \\  hello();
 815        \\}
 816    , &.{});
 817    exe.root_module.linkLibrary(dso);
 818    exe.root_module.link_libc = true;
 819
 820    const run = addRunArtifact(exe);
 821    run.expectStdOutEqual("Hello WORLD\n");
 822    test_step.dependOn(&run.step);
 823
 824    return test_step;
 825}
 826
 827fn testDsoUndef(b: *Build, opts: Options) *Step {
 828    const test_step = addTestStep(b, "dso-undef", opts);
 829
 830    const dso = addSharedLibrary(b, opts, .{ .name = "dso" });
 831    addCSourceBytes(dso,
 832        \\extern int foo;
 833        \\int bar = 5;
 834        \\int baz() { return foo; }
 835    , &.{});
 836    dso.root_module.link_libc = true;
 837
 838    const obj = addObject(b, opts, .{
 839        .name = "obj",
 840        .c_source_bytes = "int foo = 3;",
 841    });
 842
 843    const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
 844    lib.root_module.addObject(obj);
 845
 846    const exe = addExecutable(b, opts, .{ .name = "test" });
 847    exe.root_module.linkLibrary(dso);
 848    exe.root_module.linkLibrary(lib);
 849    addCSourceBytes(exe,
 850        \\extern int bar;
 851        \\int main() {
 852        \\  return bar - 5;
 853        \\}
 854    , &.{});
 855    exe.root_module.link_libc = true;
 856
 857    const run = addRunArtifact(exe);
 858    run.expectExitCode(0);
 859    test_step.dependOn(&run.step);
 860
 861    const check = exe.checkObject();
 862    check.checkInDynamicSymtab();
 863    check.checkContains("foo");
 864    test_step.dependOn(&check.step);
 865
 866    return test_step;
 867}
 868
 869fn testEmitRelocatable(b: *Build, opts: Options) *Step {
 870    const test_step = addTestStep(b, "emit-relocatable", opts);
 871
 872    const a_o = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
 873        \\const std = @import("std");
 874        \\extern var bar: i32;
 875        \\export fn foo() i32 {
 876        \\   return bar;
 877        \\}
 878        \\export fn printFoo() void {
 879        \\    std.debug.print("foo={d}\n", .{foo()});
 880        \\}
 881    });
 882    a_o.root_module.link_libc = true;
 883
 884    const b_o = addObject(b, opts, .{ .name = "b", .c_source_bytes =
 885        \\#include <stdio.h>
 886        \\int bar = 42;
 887        \\void printBar() {
 888        \\  fprintf(stderr, "bar=%d\n", bar);
 889        \\}
 890    });
 891    b_o.root_module.link_libc = true;
 892
 893    const c_o = addObject(b, opts, .{ .name = "c" });
 894    c_o.root_module.addObject(a_o);
 895    c_o.root_module.addObject(b_o);
 896
 897    const exe = addExecutable(b, opts, .{ .name = "test", .zig_source_bytes =
 898        \\const std = @import("std");
 899        \\extern fn printFoo() void;
 900        \\extern fn printBar() void;
 901        \\pub fn main() void {
 902        \\    printFoo();
 903        \\    printBar();
 904        \\}
 905    });
 906    exe.root_module.addObject(c_o);
 907    exe.root_module.link_libc = true;
 908
 909    const run = addRunArtifact(exe);
 910    run.expectStdErrEqual(
 911        \\foo=42
 912        \\bar=42
 913        \\
 914    );
 915    test_step.dependOn(&run.step);
 916
 917    return test_step;
 918}
 919
 920fn testEmitStaticLib(b: *Build, opts: Options) *Step {
 921    const test_step = addTestStep(b, "emit-static-lib", opts);
 922
 923    const obj1 = addObject(b, opts, .{
 924        .name = "obj1",
 925        .c_source_bytes =
 926        \\int foo = 0;
 927        \\int bar = 2;
 928        \\int fooBar() {
 929        \\  return foo + bar;
 930        \\}
 931        ,
 932    });
 933
 934    const obj2 = addObject(b, opts, .{
 935        .name = "obj2",
 936        .c_source_bytes = "int tentative;",
 937        .c_source_flags = &.{"-fcommon"},
 938    });
 939
 940    const obj3 = addObject(b, opts, .{
 941        .name = "a_very_long_file_name_so_that_it_ends_up_in_strtab",
 942        .zig_source_bytes =
 943        \\fn weakFoo() callconv(.c) usize {
 944        \\    return 42;
 945        \\}
 946        \\export var strongBar: usize = 100;
 947        \\comptime {
 948        \\    @export(&weakFoo, .{ .name = "weakFoo", .linkage = .weak });
 949        \\    @export(&strongBar, .{ .name = "strongBarAlias", .linkage = .strong });
 950        \\}
 951        ,
 952    });
 953
 954    const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
 955    lib.root_module.addObject(obj1);
 956    lib.root_module.addObject(obj2);
 957    lib.root_module.addObject(obj3);
 958
 959    const check = lib.checkObject();
 960    check.checkInArchiveSymtab();
 961    check.checkExact("in object obj1.o");
 962    check.checkExact("foo");
 963    check.checkInArchiveSymtab();
 964    check.checkExact("in object obj1.o");
 965    check.checkExact("bar");
 966    check.checkInArchiveSymtab();
 967    check.checkExact("in object obj1.o");
 968    check.checkExact("fooBar");
 969    check.checkInArchiveSymtab();
 970    check.checkExact("in object obj2.o");
 971    check.checkExact("tentative");
 972    check.checkInArchiveSymtab();
 973    check.checkExact("in object a_very_long_file_name_so_that_it_ends_up_in_strtab.o");
 974    check.checkExact("weakFoo");
 975    check.checkInArchiveSymtab();
 976    check.checkExact("in object a_very_long_file_name_so_that_it_ends_up_in_strtab.o");
 977    check.checkExact("strongBar");
 978    check.checkInArchiveSymtab();
 979    check.checkExact("in object a_very_long_file_name_so_that_it_ends_up_in_strtab.o");
 980    check.checkExact("strongBarAlias");
 981    test_step.dependOn(&check.step);
 982
 983    return test_step;
 984}
 985
 986fn testEmitStaticLibZig(b: *Build, opts: Options) *Step {
 987    const test_step = addTestStep(b, "emit-static-lib-zig", opts);
 988
 989    const obj1 = addObject(b, opts, .{
 990        .name = "obj1",
 991        .zig_source_bytes =
 992        \\export var foo: i32 = 42;
 993        \\export var bar: i32 = 2;
 994        ,
 995    });
 996
 997    const lib = addStaticLibrary(b, opts, .{
 998        .name = "lib",
 999        .zig_source_bytes =
1000        \\extern var foo: i32;
1001        \\extern var bar: i32;
1002        \\export fn fooBar() i32 {
1003        \\  return foo + bar;
1004        \\}
1005        ,
1006    });
1007    lib.root_module.addObject(obj1);
1008
1009    const exe = addExecutable(b, opts, .{
1010        .name = "test",
1011        .zig_source_bytes =
1012        \\const std = @import("std");
1013        \\extern fn fooBar() i32;
1014        \\pub fn main() void {
1015        \\  std.debug.print("{d}", .{fooBar()});
1016        \\}
1017        ,
1018    });
1019    exe.root_module.linkLibrary(lib);
1020
1021    const run = addRunArtifact(exe);
1022    run.expectStdErrEqual("44");
1023    test_step.dependOn(&run.step);
1024
1025    return test_step;
1026}
1027
1028fn testEmptyObject(b: *Build, opts: Options) *Step {
1029    const test_step = addTestStep(b, "empty-object", opts);
1030
1031    const exe = addExecutable(b, opts, .{ .name = "test" });
1032    addCSourceBytes(exe, "int main() { return 0; }", &.{});
1033    addCSourceBytes(exe, "", &.{});
1034    exe.root_module.link_libc = true;
1035
1036    const run = addRunArtifact(exe);
1037    run.expectExitCode(0);
1038    test_step.dependOn(&run.step);
1039
1040    return test_step;
1041}
1042
1043fn testEntryPoint(b: *Build, opts: Options) *Step {
1044    const test_step = addTestStep(b, "entry-point", opts);
1045
1046    const a_o = addObject(b, opts, .{
1047        .name = "a",
1048        .asm_source_bytes =
1049        \\.globl foo, bar
1050        \\foo = 0x1000
1051        \\bar = 0x2000
1052        \\
1053        ,
1054    });
1055
1056    const b_o = addObject(b, opts, .{
1057        .name = "b",
1058        .c_source_bytes = "int main() { return 0; }",
1059    });
1060
1061    {
1062        const exe = addExecutable(b, opts, .{ .name = "main" });
1063        exe.root_module.addObject(a_o);
1064        exe.root_module.addObject(b_o);
1065        exe.entry = .{ .symbol_name = "foo" };
1066
1067        const check = exe.checkObject();
1068        check.checkInHeaders();
1069        check.checkExact("header");
1070        check.checkExact("entry 1000");
1071        test_step.dependOn(&check.step);
1072    }
1073
1074    {
1075        // TODO looks like not assigning a unique name to this executable will
1076        // cause an artifact collision taking the cached executable from the above
1077        // step instead of generating a new one.
1078        const exe = addExecutable(b, opts, .{ .name = "other" });
1079        exe.root_module.addObject(a_o);
1080        exe.root_module.addObject(b_o);
1081        exe.entry = .{ .symbol_name = "bar" };
1082
1083        const check = exe.checkObject();
1084        check.checkInHeaders();
1085        check.checkExact("header");
1086        check.checkExact("entry 2000");
1087        test_step.dependOn(&check.step);
1088    }
1089
1090    return test_step;
1091}
1092
1093fn testExportDynamic(b: *Build, opts: Options) *Step {
1094    const test_step = addTestStep(b, "export-dynamic", opts);
1095
1096    const obj = addObject(b, opts, .{
1097        .name = "obj",
1098        .asm_source_bytes =
1099        \\.text
1100        \\  .globl foo
1101        \\  .hidden foo
1102        \\foo:
1103        \\  nop
1104        \\  .globl bar
1105        \\bar:
1106        \\  nop
1107        \\  .globl _start
1108        \\_start:
1109        \\  nop
1110        \\
1111        ,
1112    });
1113
1114    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1115    addCSourceBytes(dso, "int baz = 10;", &.{});
1116
1117    const exe = addExecutable(b, opts, .{ .name = "main" });
1118    addCSourceBytes(exe,
1119        \\extern int baz;
1120        \\int callBaz() {
1121        \\  return baz;
1122        \\}
1123    , &.{});
1124    exe.root_module.addObject(obj);
1125    exe.root_module.linkLibrary(dso);
1126    exe.rdynamic = true;
1127
1128    const check = exe.checkObject();
1129    check.checkInDynamicSymtab();
1130    check.checkContains("bar");
1131    check.checkInDynamicSymtab();
1132    check.checkContains("_start");
1133    test_step.dependOn(&check.step);
1134
1135    return test_step;
1136}
1137
1138fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step {
1139    const test_step = addTestStep(b, "export-symbols-from-exe", opts);
1140
1141    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1142    addCSourceBytes(dso,
1143        \\void expfn1();
1144        \\void expfn2() {}
1145        \\
1146        \\void foo() {
1147        \\  expfn1();
1148        \\}
1149    , &.{});
1150
1151    const exe = addExecutable(b, opts, .{ .name = "main" });
1152    addCSourceBytes(exe,
1153        \\void expfn1() {}
1154        \\void expfn2() {}
1155        \\void foo();
1156        \\
1157        \\int main() {
1158        \\  expfn1();
1159        \\  expfn2();
1160        \\  foo();
1161        \\}
1162    , &.{});
1163    exe.root_module.linkLibrary(dso);
1164    exe.root_module.link_libc = true;
1165
1166    const check = exe.checkObject();
1167    check.checkInDynamicSymtab();
1168    check.checkContains("expfn2");
1169    check.checkInDynamicSymtab();
1170    check.checkContains("expfn1");
1171    test_step.dependOn(&check.step);
1172
1173    return test_step;
1174}
1175
1176fn testFuncAddress(b: *Build, opts: Options) *Step {
1177    const test_step = addTestStep(b, "func-address", opts);
1178
1179    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1180    addCSourceBytes(dso, "void fn() {}", &.{});
1181
1182    const exe = addExecutable(b, opts, .{ .name = "main" });
1183    addCSourceBytes(exe,
1184        \\#include <assert.h>
1185        \\typedef void Func();
1186        \\void fn();
1187        \\Func *const ptr = fn;
1188        \\int main() {
1189        \\  assert(fn == ptr);
1190        \\}
1191    , &.{});
1192    exe.root_module.linkLibrary(dso);
1193    exe.root_module.pic = false;
1194    exe.pie = false;
1195
1196    const run = addRunArtifact(exe);
1197    run.expectExitCode(0);
1198    test_step.dependOn(&run.step);
1199
1200    return test_step;
1201}
1202
1203fn testGcSections(b: *Build, opts: Options) *Step {
1204    const test_step = addTestStep(b, "gc-sections", opts);
1205
1206    const obj = addObject(b, opts, .{
1207        .name = "obj",
1208        .cpp_source_bytes =
1209        \\#include <stdio.h>
1210        \\int two() { return 2; }
1211        \\int live_var1 = 1;
1212        \\int live_var2 = two();
1213        \\int dead_var1 = 3;
1214        \\int dead_var2 = 4;
1215        \\void live_fn1() {}
1216        \\void live_fn2() { live_fn1(); }
1217        \\void dead_fn1() {}
1218        \\void dead_fn2() { dead_fn1(); }
1219        \\int main() {
1220        \\  printf("%d %d\n", live_var1, live_var2);
1221        \\  live_fn2();
1222        \\}
1223        ,
1224    });
1225    obj.link_function_sections = true;
1226    obj.link_data_sections = true;
1227    obj.root_module.link_libc = true;
1228    obj.root_module.link_libcpp = true;
1229
1230    {
1231        const exe = addExecutable(b, opts, .{ .name = "test" });
1232        exe.root_module.addObject(obj);
1233        exe.link_gc_sections = false;
1234        exe.root_module.link_libc = true;
1235        exe.root_module.link_libcpp = true;
1236
1237        const run = addRunArtifact(exe);
1238        run.expectStdOutEqual("1 2\n");
1239        test_step.dependOn(&run.step);
1240
1241        const check = exe.checkObject();
1242        check.checkInSymtab();
1243        check.checkContains("live_var1");
1244        check.checkInSymtab();
1245        check.checkContains("live_var2");
1246        check.checkInSymtab();
1247        check.checkContains("dead_var1");
1248        check.checkInSymtab();
1249        check.checkContains("dead_var2");
1250        check.checkInSymtab();
1251        check.checkContains("live_fn1");
1252        check.checkInSymtab();
1253        check.checkContains("live_fn2");
1254        check.checkInSymtab();
1255        check.checkContains("dead_fn1");
1256        check.checkInSymtab();
1257        check.checkContains("dead_fn2");
1258        test_step.dependOn(&check.step);
1259    }
1260
1261    {
1262        const exe = addExecutable(b, opts, .{ .name = "test" });
1263        exe.root_module.addObject(obj);
1264        exe.link_gc_sections = true;
1265        exe.root_module.link_libc = true;
1266        exe.root_module.link_libcpp = true;
1267
1268        const run = addRunArtifact(exe);
1269        run.expectStdOutEqual("1 2\n");
1270        test_step.dependOn(&run.step);
1271
1272        const check = exe.checkObject();
1273        check.checkInSymtab();
1274        check.checkContains("live_var1");
1275        check.checkInSymtab();
1276        check.checkContains("live_var2");
1277        check.checkInSymtab();
1278        check.checkNotPresent("dead_var1");
1279        check.checkInSymtab();
1280        check.checkNotPresent("dead_var2");
1281        check.checkInSymtab();
1282        check.checkContains("live_fn1");
1283        check.checkInSymtab();
1284        check.checkContains("live_fn2");
1285        check.checkInSymtab();
1286        check.checkNotPresent("dead_fn1");
1287        check.checkInSymtab();
1288        check.checkNotPresent("dead_fn2");
1289        test_step.dependOn(&check.step);
1290    }
1291
1292    return test_step;
1293}
1294
1295fn testGcSectionsZig(b: *Build, opts: Options) *Step {
1296    const test_step = addTestStep(b, "gc-sections-zig", opts);
1297
1298    const obj = addObject(b, .{
1299        .target = opts.target,
1300        .use_llvm = true,
1301    }, .{
1302        .name = "obj",
1303        .c_source_bytes =
1304        \\int live_var1 = 1;
1305        \\int live_var2 = 2;
1306        \\int dead_var1 = 3;
1307        \\int dead_var2 = 4;
1308        \\void live_fn1() {}
1309        \\void live_fn2() { live_fn1(); }
1310        \\void dead_fn1() {}
1311        \\void dead_fn2() { dead_fn1(); }
1312        ,
1313    });
1314    obj.link_function_sections = true;
1315    obj.link_data_sections = true;
1316
1317    {
1318        const exe = addExecutable(b, opts, .{
1319            .name = "test1",
1320            .zig_source_bytes =
1321            \\const std = @import("std");
1322            \\extern var live_var1: i32;
1323            \\extern var live_var2: i32;
1324            \\extern fn live_fn2() void;
1325            \\pub fn main() void {
1326            \\    var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
1327            \\    stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail");
1328            \\    live_fn2();
1329            \\}
1330            ,
1331        });
1332        exe.root_module.addObject(obj);
1333        exe.link_gc_sections = false;
1334
1335        const run = addRunArtifact(exe);
1336        run.expectStdOutEqual("1 2\n");
1337        test_step.dependOn(&run.step);
1338
1339        const check = exe.checkObject();
1340        check.checkInSymtab();
1341        check.checkContains("live_var1");
1342        check.checkInSymtab();
1343        check.checkContains("live_var2");
1344        check.checkInSymtab();
1345        check.checkContains("dead_var1");
1346        check.checkInSymtab();
1347        check.checkContains("dead_var2");
1348        check.checkInSymtab();
1349        check.checkContains("live_fn1");
1350        check.checkInSymtab();
1351        check.checkContains("live_fn2");
1352        check.checkInSymtab();
1353        check.checkContains("dead_fn1");
1354        check.checkInSymtab();
1355        check.checkContains("dead_fn2");
1356        test_step.dependOn(&check.step);
1357    }
1358
1359    {
1360        const exe = addExecutable(b, opts, .{
1361            .name = "test2",
1362            .zig_source_bytes =
1363            \\const std = @import("std");
1364            \\extern var live_var1: i32;
1365            \\extern var live_var2: i32;
1366            \\extern fn live_fn2() void;
1367            \\pub fn main() void {
1368            \\    var stdout_writer = std.fs.File.stdout().writerStreaming(&.{});
1369            \\    stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail");
1370            \\    live_fn2();
1371            \\}
1372            ,
1373        });
1374        exe.root_module.addObject(obj);
1375        exe.link_gc_sections = true;
1376
1377        const run = addRunArtifact(exe);
1378        run.expectStdOutEqual("1 2\n");
1379        test_step.dependOn(&run.step);
1380
1381        const check = exe.checkObject();
1382        check.checkInSymtab();
1383        check.checkContains("live_var1");
1384        check.checkInSymtab();
1385        check.checkContains("live_var2");
1386        check.checkInSymtab();
1387        check.checkNotPresent("dead_var1");
1388        check.checkInSymtab();
1389        check.checkNotPresent("dead_var2");
1390        check.checkInSymtab();
1391        check.checkContains("live_fn1");
1392        check.checkInSymtab();
1393        check.checkContains("live_fn2");
1394        check.checkInSymtab();
1395        check.checkNotPresent("dead_fn1");
1396        check.checkInSymtab();
1397        check.checkNotPresent("dead_fn2");
1398        test_step.dependOn(&check.step);
1399    }
1400
1401    return test_step;
1402}
1403
1404fn testHiddenWeakUndef(b: *Build, opts: Options) *Step {
1405    const test_step = addTestStep(b, "hidden-weak-undef", opts);
1406
1407    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1408    addCSourceBytes(dso,
1409        \\__attribute__((weak, visibility("hidden"))) void foo();
1410        \\void bar() { foo(); }
1411    , &.{});
1412
1413    const check = dso.checkObject();
1414    check.checkInDynamicSymtab();
1415    check.checkNotPresent("foo");
1416    check.checkInDynamicSymtab();
1417    check.checkContains("bar");
1418    test_step.dependOn(&check.step);
1419
1420    return test_step;
1421}
1422
1423fn testIFuncAlias(b: *Build, opts: Options) *Step {
1424    const test_step = addTestStep(b, "ifunc-alias", opts);
1425
1426    const exe = addExecutable(b, opts, .{ .name = "main" });
1427    addCSourceBytes(exe,
1428        \\#include <assert.h>
1429        \\void foo() {}
1430        \\int bar() __attribute__((ifunc("resolve_bar")));
1431        \\void *resolve_bar() { return foo; }
1432        \\void *bar2 = bar;
1433        \\int main() {
1434        \\  assert(bar == bar2);
1435        \\}
1436    , &.{});
1437    exe.root_module.pic = true;
1438    exe.root_module.link_libc = true;
1439
1440    const run = addRunArtifact(exe);
1441    run.expectExitCode(0);
1442    test_step.dependOn(&run.step);
1443
1444    return test_step;
1445}
1446
1447fn testIFuncDlopen(b: *Build, opts: Options) *Step {
1448    const test_step = addTestStep(b, "ifunc-dlopen", opts);
1449
1450    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1451    addCSourceBytes(dso,
1452        \\__attribute__((ifunc("resolve_foo")))
1453        \\void foo(void);
1454        \\static void real_foo(void) {
1455        \\}
1456        \\typedef void Func();
1457        \\static Func *resolve_foo(void) {
1458        \\  return real_foo;
1459        \\}
1460    , &.{});
1461
1462    const exe = addExecutable(b, opts, .{ .name = "main" });
1463    addCSourceBytes(exe,
1464        \\#include <dlfcn.h>
1465        \\#include <assert.h>
1466        \\#include <stdlib.h>
1467        \\typedef void Func();
1468        \\void foo(void);
1469        \\int main() {
1470        \\  void *handle = dlopen(NULL, RTLD_NOW);
1471        \\  Func *p = dlsym(handle, "foo");
1472        \\
1473        \\  foo();
1474        \\  p();
1475        \\  assert(foo == p);
1476        \\}
1477    , &.{});
1478    exe.root_module.linkLibrary(dso);
1479    exe.root_module.link_libc = true;
1480    exe.root_module.linkSystemLibrary("dl", .{});
1481    exe.root_module.pic = false;
1482    exe.pie = false;
1483
1484    const run = addRunArtifact(exe);
1485    run.expectExitCode(0);
1486    test_step.dependOn(&run.step);
1487
1488    return test_step;
1489}
1490
1491fn testIFuncDso(b: *Build, opts: Options) *Step {
1492    const test_step = addTestStep(b, "ifunc-dso", opts);
1493
1494    const dso = addSharedLibrary(b, opts, .{
1495        .name = "a",
1496        .c_source_bytes =
1497        \\#include<stdio.h>
1498        \\__attribute__((ifunc("resolve_foobar")))
1499        \\void foobar(void);
1500        \\static void real_foobar(void) {
1501        \\  printf("Hello world\n");
1502        \\}
1503        \\typedef void Func();
1504        \\static Func *resolve_foobar(void) {
1505        \\  return real_foobar;
1506        \\}
1507        ,
1508    });
1509    dso.root_module.link_libc = true;
1510
1511    const exe = addExecutable(b, opts, .{
1512        .name = "main",
1513        .c_source_bytes =
1514        \\void foobar(void);
1515        \\int main() {
1516        \\  foobar();
1517        \\}
1518        ,
1519    });
1520    exe.root_module.linkLibrary(dso);
1521
1522    const run = addRunArtifact(exe);
1523    run.expectStdOutEqual("Hello world\n");
1524    test_step.dependOn(&run.step);
1525
1526    return test_step;
1527}
1528
1529fn testIFuncDynamic(b: *Build, opts: Options) *Step {
1530    const test_step = addTestStep(b, "ifunc-dynamic", opts);
1531
1532    const main_c =
1533        \\#include <stdio.h>
1534        \\__attribute__((ifunc("resolve_foobar")))
1535        \\static void foobar(void);
1536        \\static void real_foobar(void) {
1537        \\  printf("Hello world\n");
1538        \\}
1539        \\typedef void Func();
1540        \\static Func *resolve_foobar(void) {
1541        \\  return real_foobar;
1542        \\}
1543        \\int main() {
1544        \\  foobar();
1545        \\}
1546    ;
1547
1548    {
1549        const exe = addExecutable(b, opts, .{ .name = "main" });
1550        addCSourceBytes(exe, main_c, &.{});
1551        exe.root_module.link_libc = true;
1552        exe.link_z_lazy = true;
1553
1554        const run = addRunArtifact(exe);
1555        run.expectStdOutEqual("Hello world\n");
1556        test_step.dependOn(&run.step);
1557    }
1558    {
1559        const exe = addExecutable(b, opts, .{ .name = "other" });
1560        addCSourceBytes(exe, main_c, &.{});
1561        exe.root_module.link_libc = true;
1562
1563        const run = addRunArtifact(exe);
1564        run.expectStdOutEqual("Hello world\n");
1565        test_step.dependOn(&run.step);
1566    }
1567
1568    return test_step;
1569}
1570
1571fn testIFuncExport(b: *Build, opts: Options) *Step {
1572    const test_step = addTestStep(b, "ifunc-export", opts);
1573
1574    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1575    addCSourceBytes(dso,
1576        \\#include <stdio.h>
1577        \\__attribute__((ifunc("resolve_foobar")))
1578        \\void foobar(void);
1579        \\void real_foobar(void) {
1580        \\  printf("Hello world\n");
1581        \\}
1582        \\typedef void Func();
1583        \\Func *resolve_foobar(void) {
1584        \\  return real_foobar;
1585        \\}
1586    , &.{});
1587    dso.root_module.link_libc = true;
1588
1589    const check = dso.checkObject();
1590    check.checkInDynamicSymtab();
1591    check.checkContains("IFUNC GLOBAL DEFAULT foobar");
1592    test_step.dependOn(&check.step);
1593
1594    return test_step;
1595}
1596
1597fn testIFuncFuncPtr(b: *Build, opts: Options) *Step {
1598    const test_step = addTestStep(b, "ifunc-func-ptr", opts);
1599
1600    const exe = addExecutable(b, opts, .{ .name = "main" });
1601    addCSourceBytes(exe,
1602        \\typedef int Fn();
1603        \\int foo() __attribute__((ifunc("resolve_foo")));
1604        \\int real_foo() { return 3; }
1605        \\Fn *resolve_foo(void) {
1606        \\  return real_foo;
1607        \\}
1608    , &.{});
1609    addCSourceBytes(exe,
1610        \\typedef int Fn();
1611        \\int foo();
1612        \\Fn *get_foo() { return foo; }
1613    , &.{});
1614    addCSourceBytes(exe,
1615        \\#include <stdio.h>
1616        \\typedef int Fn();
1617        \\Fn *get_foo();
1618        \\int main() {
1619        \\  Fn *f = get_foo();
1620        \\  printf("%d\n", f());
1621        \\}
1622    , &.{});
1623    exe.root_module.pic = true;
1624    exe.root_module.link_libc = true;
1625
1626    const run = addRunArtifact(exe);
1627    run.expectStdOutEqual("3\n");
1628    test_step.dependOn(&run.step);
1629
1630    return test_step;
1631}
1632
1633fn testIFuncNoPlt(b: *Build, opts: Options) *Step {
1634    const test_step = addTestStep(b, "ifunc-noplt", opts);
1635
1636    const exe = addExecutable(b, opts, .{ .name = "main" });
1637    addCSourceBytes(exe,
1638        \\#include <stdio.h>
1639        \\__attribute__((ifunc("resolve_foo")))
1640        \\void foo(void);
1641        \\void hello(void) {
1642        \\  printf("Hello world\n");
1643        \\}
1644        \\typedef void Fn();
1645        \\Fn *resolve_foo(void) {
1646        \\  return hello;
1647        \\}
1648        \\int main() {
1649        \\  foo();
1650        \\}
1651    , &.{"-fno-plt"});
1652    exe.root_module.pic = true;
1653    exe.root_module.link_libc = true;
1654
1655    const run = addRunArtifact(exe);
1656    run.expectStdOutEqual("Hello world\n");
1657    test_step.dependOn(&run.step);
1658
1659    return test_step;
1660}
1661
1662fn testIFuncStatic(b: *Build, opts: Options) *Step {
1663    const test_step = addTestStep(b, "ifunc-static", opts);
1664
1665    const exe = addExecutable(b, opts, .{ .name = "main" });
1666    addCSourceBytes(exe,
1667        \\#include <stdio.h>
1668        \\void foo() __attribute__((ifunc("resolve_foo")));
1669        \\void hello() {
1670        \\  printf("Hello world\n");
1671        \\}
1672        \\void *resolve_foo() {
1673        \\  return hello;
1674        \\}
1675        \\int main() {
1676        \\  foo();
1677        \\  return 0;
1678        \\}
1679    , &.{});
1680    exe.root_module.link_libc = true;
1681    exe.linkage = .static;
1682
1683    const run = addRunArtifact(exe);
1684    run.expectStdOutEqual("Hello world\n");
1685    test_step.dependOn(&run.step);
1686
1687    return test_step;
1688}
1689
1690fn testIFuncStaticPie(b: *Build, opts: Options) *Step {
1691    const test_step = addTestStep(b, "ifunc-static-pie", opts);
1692
1693    const exe = addExecutable(b, opts, .{ .name = "main" });
1694    addCSourceBytes(exe,
1695        \\#include <stdio.h>
1696        \\void foo() __attribute__((ifunc("resolve_foo")));
1697        \\void hello() {
1698        \\  printf("Hello world\n");
1699        \\}
1700        \\void *resolve_foo() {
1701        \\  return hello;
1702        \\}
1703        \\int main() {
1704        \\  foo();
1705        \\  return 0;
1706        \\}
1707    , &.{});
1708    exe.linkage = .static;
1709    exe.root_module.pic = true;
1710    exe.pie = true;
1711    exe.root_module.link_libc = true;
1712
1713    const run = addRunArtifact(exe);
1714    run.expectStdOutEqual("Hello world\n");
1715    test_step.dependOn(&run.step);
1716
1717    const check = exe.checkObject();
1718    check.checkInHeaders();
1719    check.checkExact("header");
1720    check.checkExact("type DYN");
1721    check.checkInHeaders();
1722    check.checkExact("section headers");
1723    check.checkExact("name .dynamic");
1724    check.checkInHeaders();
1725    check.checkExact("section headers");
1726    check.checkNotPresent("name .interp");
1727    test_step.dependOn(&check.step);
1728
1729    return test_step;
1730}
1731
1732fn testImageBase(b: *Build, opts: Options) *Step {
1733    const test_step = addTestStep(b, "image-base", opts);
1734
1735    {
1736        const exe = addExecutable(b, opts, .{ .name = "main1" });
1737        addCSourceBytes(exe,
1738            \\#include <stdio.h>
1739            \\int main() {
1740            \\  printf("Hello World!\n");
1741            \\  return 0;
1742            \\}
1743        , &.{});
1744        exe.root_module.link_libc = true;
1745        exe.image_base = 0x8000000;
1746
1747        const run = addRunArtifact(exe);
1748        run.expectStdOutEqual("Hello World!\n");
1749        test_step.dependOn(&run.step);
1750
1751        const check = exe.checkObject();
1752        check.checkInHeaders();
1753        check.checkExact("header");
1754        check.checkExtract("entry {addr}");
1755        check.checkComputeCompare("addr", .{ .op = .gte, .value = .{ .literal = 0x8000000 } });
1756        test_step.dependOn(&check.step);
1757    }
1758
1759    {
1760        const exe = addExecutable(b, opts, .{ .name = "main2" });
1761        addCSourceBytes(exe, "void _start() {}", &.{});
1762        exe.image_base = 0xffffffff8000000;
1763
1764        const check = exe.checkObject();
1765        check.checkInHeaders();
1766        check.checkExact("header");
1767        check.checkExtract("entry {addr}");
1768        check.checkComputeCompare("addr", .{ .op = .gte, .value = .{ .literal = 0xffffffff8000000 } });
1769        test_step.dependOn(&check.step);
1770    }
1771
1772    return test_step;
1773}
1774
1775fn testImportingDataDynamic(b: *Build, opts: Options) *Step {
1776    const test_step = addTestStep(b, "importing-data-dynamic", opts);
1777
1778    const dso = addSharedLibrary(b, .{
1779        .target = opts.target,
1780        .optimize = opts.optimize,
1781        .use_llvm = true,
1782    }, .{
1783        .name = "a",
1784        .c_source_bytes =
1785        \\#include <stdio.h>
1786        \\int foo = 42;
1787        \\void printFoo() { fprintf(stderr, "lib foo=%d\n", foo); }
1788        ,
1789    });
1790    dso.root_module.link_libc = true;
1791
1792    const main = addExecutable(b, opts, .{
1793        .name = "main",
1794        .zig_source_bytes =
1795        \\const std = @import("std");
1796        \\extern var foo: i32;
1797        \\extern fn printFoo() void;
1798        \\pub fn main() void {
1799        \\    std.debug.print("exe foo={d}\n", .{foo});
1800        \\    printFoo();
1801        \\    foo += 1;
1802        \\    std.debug.print("exe foo={d}\n", .{foo});
1803        \\    printFoo();
1804        \\}
1805        ,
1806        .strip = true, // TODO temp hack
1807    });
1808    main.pie = true;
1809    main.root_module.linkLibrary(dso);
1810
1811    const run = addRunArtifact(main);
1812    run.expectStdErrEqual(
1813        \\exe foo=42
1814        \\lib foo=42
1815        \\exe foo=43
1816        \\lib foo=43
1817        \\
1818    );
1819    test_step.dependOn(&run.step);
1820
1821    return test_step;
1822}
1823
1824fn testImportingDataStatic(b: *Build, opts: Options) *Step {
1825    const test_step = addTestStep(b, "importing-data-static", opts);
1826
1827    const obj = addObject(b, .{
1828        .target = opts.target,
1829        .optimize = opts.optimize,
1830        .use_llvm = true,
1831    }, .{
1832        .name = "a",
1833        .c_source_bytes = "int foo = 42;",
1834    });
1835
1836    const lib = addStaticLibrary(b, .{
1837        .target = opts.target,
1838        .optimize = opts.optimize,
1839        .use_llvm = true,
1840    }, .{
1841        .name = "a",
1842    });
1843    lib.root_module.addObject(obj);
1844
1845    const main = addExecutable(b, opts, .{
1846        .name = "main",
1847        .zig_source_bytes =
1848        \\extern var foo: i32;
1849        \\pub fn main() void {
1850        \\    @import("std").debug.print("{d}\n", .{foo});
1851        \\}
1852        ,
1853        .strip = true, // TODO temp hack
1854    });
1855    main.root_module.linkLibrary(lib);
1856    main.root_module.link_libc = true;
1857
1858    const run = addRunArtifact(main);
1859    run.expectStdErrEqual("42\n");
1860    test_step.dependOn(&run.step);
1861
1862    return test_step;
1863}
1864
1865fn testInitArrayOrder(b: *Build, opts: Options) *Step {
1866    const test_step = addTestStep(b, "init-array-order", opts);
1867
1868    const a_o = addObject(b, opts, .{
1869        .name = "a",
1870        .c_source_bytes =
1871        \\#include <stdio.h>
1872        \\__attribute__((constructor(10000))) void init4() { printf("1"); }
1873        ,
1874    });
1875    a_o.root_module.link_libc = true;
1876
1877    const b_o = addObject(b, opts, .{
1878        .name = "b",
1879        .c_source_bytes =
1880        \\#include <stdio.h>
1881        \\__attribute__((constructor(1000))) void init3() { printf("2"); }
1882        ,
1883    });
1884    b_o.root_module.link_libc = true;
1885
1886    const c_o = addObject(b, opts, .{
1887        .name = "c",
1888        .c_source_bytes =
1889        \\#include <stdio.h>
1890        \\__attribute__((constructor)) void init1() { printf("3"); }
1891        ,
1892    });
1893    c_o.root_module.link_libc = true;
1894
1895    const d_o = addObject(b, opts, .{
1896        .name = "d",
1897        .c_source_bytes =
1898        \\#include <stdio.h>
1899        \\__attribute__((constructor)) void init2() { printf("4"); }
1900        ,
1901    });
1902    d_o.root_module.link_libc = true;
1903
1904    const e_o = addObject(b, opts, .{
1905        .name = "e",
1906        .c_source_bytes =
1907        \\#include <stdio.h>
1908        \\__attribute__((destructor(10000))) void fini4() { printf("5"); }
1909        ,
1910    });
1911    e_o.root_module.link_libc = true;
1912
1913    const f_o = addObject(b, opts, .{
1914        .name = "f",
1915        .c_source_bytes =
1916        \\#include <stdio.h>
1917        \\__attribute__((destructor(1000))) void fini3() { printf("6"); }
1918        ,
1919    });
1920    f_o.root_module.link_libc = true;
1921
1922    const g_o = addObject(b, opts, .{
1923        .name = "g",
1924        .c_source_bytes =
1925        \\#include <stdio.h>
1926        \\__attribute__((destructor)) void fini1() { printf("7"); }
1927        ,
1928    });
1929    g_o.root_module.link_libc = true;
1930
1931    const h_o = addObject(b, opts, .{ .name = "h", .c_source_bytes =
1932        \\#include <stdio.h>
1933        \\__attribute__((destructor)) void fini2() { printf("8"); }
1934    });
1935    h_o.root_module.link_libc = true;
1936
1937    const exe = addExecutable(b, opts, .{ .name = "main" });
1938    addCSourceBytes(exe, "int main() { return 0; }", &.{});
1939    exe.root_module.addObject(a_o);
1940    exe.root_module.addObject(b_o);
1941    exe.root_module.addObject(c_o);
1942    exe.root_module.addObject(d_o);
1943    exe.root_module.addObject(e_o);
1944    exe.root_module.addObject(f_o);
1945    exe.root_module.addObject(g_o);
1946    exe.root_module.addObject(h_o);
1947
1948    if (opts.target.result.isGnuLibC()) {
1949        // TODO I think we need to clarify our use of `-fPIC -fPIE` flags for different targets
1950        exe.pie = true;
1951    }
1952
1953    const run = addRunArtifact(exe);
1954    run.expectStdOutEqual("21348756");
1955    test_step.dependOn(&run.step);
1956
1957    return test_step;
1958}
1959
1960fn testLargeAlignmentDso(b: *Build, opts: Options) *Step {
1961    const test_step = addTestStep(b, "large-alignment-dso", opts);
1962
1963    const dso = addSharedLibrary(b, opts, .{ .name = "dso" });
1964    addCSourceBytes(dso,
1965        \\#include <stdio.h>
1966        \\#include <stdint.h>
1967        \\void hello() __attribute__((aligned(32768), section(".hello")));
1968        \\void world() __attribute__((aligned(32768), section(".world")));
1969        \\void hello() {
1970        \\  printf("Hello");
1971        \\}
1972        \\void world() {
1973        \\  printf(" world");
1974        \\}
1975        \\void greet() {
1976        \\  hello();
1977        \\  world();
1978        \\}
1979    , &.{});
1980    dso.link_function_sections = true;
1981    dso.root_module.link_libc = true;
1982
1983    const check = dso.checkObject();
1984    check.checkInSymtab();
1985    check.checkExtract("{addr1} {size1} {shndx1} FUNC GLOBAL DEFAULT hello");
1986    check.checkInSymtab();
1987    check.checkExtract("{addr2} {size2} {shndx2} FUNC GLOBAL DEFAULT world");
1988    check.checkComputeCompare("addr1 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
1989    check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
1990    test_step.dependOn(&check.step);
1991
1992    const exe = addExecutable(b, opts, .{ .name = "test" });
1993    addCSourceBytes(exe,
1994        \\void greet();
1995        \\int main() { greet(); }
1996    , &.{});
1997    exe.root_module.linkLibrary(dso);
1998    exe.root_module.link_libc = true;
1999
2000    const run = addRunArtifact(exe);
2001    run.expectStdOutEqual("Hello world");
2002    test_step.dependOn(&run.step);
2003
2004    return test_step;
2005}
2006
2007fn testLargeAlignmentExe(b: *Build, opts: Options) *Step {
2008    const test_step = addTestStep(b, "large-alignment-exe", opts);
2009
2010    const exe = addExecutable(b, opts, .{ .name = "test" });
2011    addCSourceBytes(exe,
2012        \\#include <stdio.h>
2013        \\#include <stdint.h>
2014        \\
2015        \\void hello() __attribute__((aligned(32768), section(".hello")));
2016        \\void world() __attribute__((aligned(32768), section(".world")));
2017        \\
2018        \\void hello() {
2019        \\  printf("Hello");
2020        \\}
2021        \\
2022        \\void world() {
2023        \\  printf(" world");
2024        \\}
2025        \\
2026        \\int main() {
2027        \\  hello();
2028        \\  world();
2029        \\}
2030    , &.{});
2031    exe.link_function_sections = true;
2032    exe.root_module.link_libc = true;
2033
2034    const check = exe.checkObject();
2035    check.checkInSymtab();
2036    check.checkExtract("{addr1} {size1} {shndx1} FUNC LOCAL DEFAULT hello");
2037    check.checkInSymtab();
2038    check.checkExtract("{addr2} {size2} {shndx2} FUNC LOCAL DEFAULT world");
2039    check.checkComputeCompare("addr1 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
2040    check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
2041    test_step.dependOn(&check.step);
2042
2043    const run = addRunArtifact(exe);
2044    run.expectStdOutEqual("Hello world");
2045    test_step.dependOn(&run.step);
2046
2047    return test_step;
2048}
2049
2050fn testLargeBss(b: *Build, opts: Options) *Step {
2051    const test_step = addTestStep(b, "large-bss", opts);
2052
2053    const exe = addExecutable(b, opts, .{ .name = "main" });
2054    addCSourceBytes(exe,
2055        \\char arr[0x100000000];
2056        \\int main() {
2057        \\  return arr[2000];
2058        \\}
2059    , &.{});
2060    exe.root_module.link_libc = true;
2061    // Disabled to work around the ELF linker crashing.
2062    // Can be reproduced on a x86_64-linux host by commenting out the line below.
2063    exe.root_module.sanitize_c = .off;
2064
2065    const run = addRunArtifact(exe);
2066    run.expectExitCode(0);
2067    test_step.dependOn(&run.step);
2068
2069    return test_step;
2070}
2071
2072fn testLinkOrder(b: *Build, opts: Options) *Step {
2073    const test_step = addTestStep(b, "link-order", opts);
2074
2075    const obj = addObject(b, opts, .{
2076        .name = "obj",
2077        .c_source_bytes = "void foo() {}",
2078        .pic = true,
2079    });
2080
2081    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
2082    dso.root_module.addObject(obj);
2083
2084    const lib = addStaticLibrary(b, opts, .{ .name = "b" });
2085    lib.root_module.addObject(obj);
2086
2087    const main_o = addObject(b, opts, .{
2088        .name = "main",
2089        .c_source_bytes =
2090        \\void foo();
2091        \\int main() {
2092        \\  foo();
2093        \\}
2094        ,
2095    });
2096
2097    // https://github.com/ziglang/zig/issues/17450
2098    // {
2099    //     const exe = addExecutable(b, opts, .{ .name = "main1"});
2100    //     exe.root_module.addObject(main_o);
2101    //     exe.root_module.linkSystemLibrary("a", .{});
2102    //     exe.root_module.addLibraryPath(dso.getEmittedBinDirectory());
2103    //     exe.root_module.addRPath(dso.getEmittedBinDirectory());
2104    //     exe.root_module.linkSystemLibrary("b", .{});
2105    //     exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
2106    //     exe.root_module.addRPath(lib.getEmittedBinDirectory());
2107    //     exe.root_module.link_libc = true;
2108
2109    //     const check = exe.checkObject();
2110    //     check.checkInDynamicSection();
2111    //     check.checkContains("libb.so");
2112    //     test_step.dependOn(&check.step);
2113    // }
2114
2115    {
2116        const exe = addExecutable(b, opts, .{ .name = "main2" });
2117        exe.root_module.addObject(main_o);
2118        exe.root_module.linkSystemLibrary("b", .{});
2119        exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
2120        exe.root_module.addRPath(lib.getEmittedBinDirectory());
2121        exe.root_module.linkSystemLibrary("a", .{});
2122        exe.root_module.addLibraryPath(dso.getEmittedBinDirectory());
2123        exe.root_module.addRPath(dso.getEmittedBinDirectory());
2124        exe.root_module.link_libc = true;
2125
2126        const check = exe.checkObject();
2127        check.checkInDynamicSection();
2128        check.checkNotPresent("libb.so");
2129        test_step.dependOn(&check.step);
2130    }
2131
2132    return test_step;
2133}
2134
2135fn testLdScript(b: *Build, opts: Options) *Step {
2136    const test_step = addTestStep(b, "ld-script", opts);
2137
2138    const bar = addSharedLibrary(b, opts, .{ .name = "bar" });
2139    addCSourceBytes(bar, "int bar() { return 42; }", &.{});
2140
2141    const baz = addSharedLibrary(b, opts, .{ .name = "baz" });
2142    addCSourceBytes(baz, "int baz() { return 42; }", &.{});
2143
2144    const scripts = WriteFile.create(b);
2145    _ = scripts.add("liba.so", "INPUT(libfoo.so libfoo2.so.1)");
2146    _ = scripts.add("libfoo.so", "GROUP(AS_NEEDED(-lbar))");
2147
2148    // Check finding a versioned .so file that is elsewhere in the library search paths.
2149    const scripts2 = WriteFile.create(b);
2150    _ = scripts2.add("libfoo2.so.1", "GROUP(AS_NEEDED(-lbaz))");
2151
2152    const exe = addExecutable(b, opts, .{ .name = "main" });
2153    addCSourceBytes(exe,
2154        \\int bar();
2155        \\int baz();
2156        \\int main() {
2157        \\  return bar() - baz();
2158        \\}
2159    , &.{});
2160    exe.root_module.linkSystemLibrary("a", .{});
2161    exe.root_module.addLibraryPath(scripts.getDirectory());
2162    exe.root_module.addLibraryPath(scripts2.getDirectory());
2163    exe.root_module.addLibraryPath(bar.getEmittedBinDirectory());
2164    exe.root_module.addLibraryPath(baz.getEmittedBinDirectory());
2165    exe.root_module.addRPath(bar.getEmittedBinDirectory());
2166    exe.root_module.addRPath(baz.getEmittedBinDirectory());
2167    exe.root_module.link_libc = true;
2168    exe.allow_so_scripts = true;
2169
2170    const run = addRunArtifact(exe);
2171    run.expectExitCode(0);
2172    test_step.dependOn(&run.step);
2173
2174    return test_step;
2175}
2176
2177fn testLdScriptPathError(b: *Build, opts: Options) *Step {
2178    const test_step = addTestStep(b, "ld-script-path-error", opts);
2179
2180    const scripts = WriteFile.create(b);
2181    _ = scripts.add("liba.so", "INPUT(libfoo.so)");
2182
2183    const exe = addExecutable(b, opts, .{ .name = "main" });
2184    addCSourceBytes(exe, "int main() { return 0; }", &.{});
2185    exe.root_module.linkSystemLibrary("a", .{});
2186    exe.root_module.addLibraryPath(scripts.getDirectory());
2187    exe.root_module.link_libc = true;
2188    exe.allow_so_scripts = true;
2189
2190    // TODO: A future enhancement could make this error message also mention
2191    // the file that references the missing library.
2192    expectLinkErrors(exe, test_step, .{
2193        .stderr_contains = "error: libfoo.so: file listed in linker script not found",
2194    });
2195
2196    return test_step;
2197}
2198
2199fn testLdScriptAllowUndefinedVersion(b: *Build, opts: Options) *Step {
2200    const test_step = addTestStep(b, "ld-script-allow-undefined-version", opts);
2201
2202    const so = addSharedLibrary(b, opts, .{
2203        .name = "add",
2204        .zig_source_bytes =
2205        \\export fn add(a: i32, b: i32) i32 {
2206        \\    return a + b;
2207        \\}
2208        ,
2209    });
2210    const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }");
2211    so.setLinkerScript(ld);
2212    so.linker_allow_undefined_version = true;
2213
2214    const exe = addExecutable(b, opts, .{
2215        .name = "main",
2216        .zig_source_bytes =
2217        \\const std = @import("std");
2218        \\extern fn add(a: i32, b: i32) i32;
2219        \\pub fn main() void {
2220        \\    std.debug.print("{d}\n", .{add(1, 2)});
2221        \\}
2222        ,
2223    });
2224    exe.root_module.linkLibrary(so);
2225    exe.root_module.link_libc = true;
2226    exe.allow_so_scripts = true;
2227
2228    const run = addRunArtifact(exe);
2229    run.expectStdErrEqual("3\n");
2230    test_step.dependOn(&run.step);
2231
2232    return test_step;
2233}
2234
2235fn testLdScriptDisallowUndefinedVersion(b: *Build, opts: Options) *Step {
2236    const test_step = addTestStep(b, "ld-script-disallow-undefined-version", opts);
2237
2238    const so = addSharedLibrary(b, opts, .{
2239        .name = "add",
2240        .zig_source_bytes =
2241        \\export fn add(a: i32, b: i32) i32 {
2242        \\    return a + b;
2243        \\}
2244        ,
2245    });
2246    const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }");
2247    so.setLinkerScript(ld);
2248    so.linker_allow_undefined_version = false;
2249    so.allow_so_scripts = true;
2250
2251    expectLinkErrors(
2252        so,
2253        test_step,
2254        .{
2255            .contains = "error: ld.lld: version script assignment of 'ADD_1.0' to symbol 'sub' failed: symbol not defined",
2256        },
2257    );
2258
2259    return test_step;
2260}
2261
2262fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
2263    const test_step = addTestStep(b, "mismatched-cpu-architecture-error", opts);
2264
2265    const obj = addObject(b, .{
2266        .target = b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .gnu }),
2267    }, .{
2268        .name = "a",
2269        .c_source_bytes = "int foo;",
2270        .strip = true,
2271    });
2272
2273    const exe = addExecutable(b, opts, .{ .name = "main" });
2274    addCSourceBytes(exe,
2275        \\extern int foo;
2276        \\int main() {
2277        \\  return foo;
2278        \\}
2279    , &.{});
2280    exe.root_module.addObject(obj);
2281    exe.root_module.link_libc = true;
2282
2283    expectLinkErrors(exe, test_step, .{ .exact = &.{
2284        "invalid ELF machine type: AARCH64",
2285        "note: while parsing /?/a.o",
2286    } });
2287
2288    return test_step;
2289}
2290
2291fn testLinkingC(b: *Build, opts: Options) *Step {
2292    const test_step = addTestStep(b, "linking-c", opts);
2293
2294    const exe = addExecutable(b, opts, .{ .name = "test" });
2295    addCSourceBytes(exe,
2296        \\#include <stdio.h>
2297        \\int main() {
2298        \\  printf("Hello World!\n");
2299        \\  return 0;
2300        \\}
2301    , &.{});
2302    exe.root_module.link_libc = true;
2303
2304    const run = addRunArtifact(exe);
2305    run.expectStdOutEqual("Hello World!\n");
2306    test_step.dependOn(&run.step);
2307
2308    const check = exe.checkObject();
2309    check.checkInHeaders();
2310    check.checkExact("header");
2311    check.checkExact("type EXEC");
2312    check.checkInHeaders();
2313    check.checkExact("section headers");
2314    check.checkNotPresent("name .dynamic");
2315    test_step.dependOn(&check.step);
2316
2317    return test_step;
2318}
2319
2320fn testLinkingCpp(b: *Build, opts: Options) *Step {
2321    const test_step = addTestStep(b, "linking-cpp", opts);
2322
2323    const exe = addExecutable(b, opts, .{ .name = "test" });
2324    addCppSourceBytes(exe,
2325        \\#include <iostream>
2326        \\int main() {
2327        \\  std::cout << "Hello World!" << std::endl;
2328        \\  return 0;
2329        \\}
2330    , &.{});
2331    exe.root_module.link_libc = true;
2332    exe.root_module.link_libcpp = true;
2333
2334    const run = addRunArtifact(exe);
2335    run.expectStdOutEqual("Hello World!\n");
2336    test_step.dependOn(&run.step);
2337
2338    const check = exe.checkObject();
2339    check.checkInHeaders();
2340    check.checkExact("header");
2341    check.checkExact("type EXEC");
2342    check.checkInHeaders();
2343    check.checkExact("section headers");
2344    check.checkNotPresent("name .dynamic");
2345    test_step.dependOn(&check.step);
2346
2347    return test_step;
2348}
2349
2350fn testLinkingObj(b: *Build, opts: Options) *Step {
2351    const test_step = addTestStep(b, "linking-obj", opts);
2352
2353    const obj = addObject(b, opts, .{
2354        .name = "aobj",
2355        .zig_source_bytes =
2356        \\extern var mod: usize;
2357        \\export fn callMe() usize {
2358        \\    return me * mod;
2359        \\}
2360        \\var me: usize = 42;
2361        ,
2362    });
2363
2364    const exe = addExecutable(b, opts, .{
2365        .name = "testobj",
2366        .zig_source_bytes =
2367        \\const std = @import("std");
2368        \\extern fn callMe() usize;
2369        \\export var mod: usize = 2;
2370        \\pub fn main() void {
2371        \\    std.debug.print("{d}\n", .{callMe()});
2372        \\}
2373        ,
2374    });
2375    exe.root_module.addObject(obj);
2376
2377    const run = addRunArtifact(exe);
2378    run.expectStdErrEqual("84\n");
2379    test_step.dependOn(&run.step);
2380
2381    return test_step;
2382}
2383
2384fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
2385    const test_step = addTestStep(b, "linking-static-lib", opts);
2386
2387    const obj = addObject(b, opts, .{
2388        .name = "bobj",
2389        .zig_source_bytes = "export var bar: i32 = -42;",
2390    });
2391
2392    const lib = addStaticLibrary(b, opts, .{
2393        .name = "alib",
2394        .zig_source_bytes =
2395        \\export fn foo() i32 {
2396        \\    return 42;
2397        \\}
2398        ,
2399    });
2400    lib.root_module.addObject(obj);
2401
2402    const exe = addExecutable(b, opts, .{
2403        .name = "testlib",
2404        .zig_source_bytes =
2405        \\const std = @import("std");
2406        \\extern fn foo() i32;
2407        \\extern var bar: i32;
2408        \\pub fn main() void {
2409        \\    std.debug.print("{d}\n", .{foo() + bar});
2410        \\}
2411        ,
2412    });
2413    exe.root_module.linkLibrary(lib);
2414
2415    const run = addRunArtifact(exe);
2416    run.expectStdErrEqual("0\n");
2417    test_step.dependOn(&run.step);
2418
2419    return test_step;
2420}
2421
2422fn testLinkingZig(b: *Build, opts: Options) *Step {
2423    const test_step = addTestStep(b, "linking-zig-static", opts);
2424
2425    const exe = addExecutable(b, opts, .{
2426        .name = "test",
2427        .zig_source_bytes =
2428        \\pub fn main() void {
2429        \\    @import("std").debug.print("Hello World!\n", .{});
2430        \\}
2431        ,
2432    });
2433
2434    const run = addRunArtifact(exe);
2435    run.expectStdErrEqual("Hello World!\n");
2436    test_step.dependOn(&run.step);
2437
2438    const check = exe.checkObject();
2439    check.checkInHeaders();
2440    check.checkExact("header");
2441    check.checkExact("type EXEC");
2442    check.checkInHeaders();
2443    check.checkExact("section headers");
2444    check.checkNotPresent("name .dynamic");
2445    test_step.dependOn(&check.step);
2446
2447    return test_step;
2448}
2449
2450fn testLinksection(b: *Build, opts: Options) *Step {
2451    const test_step = addTestStep(b, "linksection", opts);
2452
2453    const obj = addObject(b, opts, .{ .name = "main", .zig_source_bytes =
2454        \\export var test_global: u32 linksection(".TestGlobal") = undefined;
2455        \\export fn testFn() linksection(".TestFn") callconv(.c) void {
2456        \\    TestGenericFn("A").f();
2457        \\}
2458        \\fn TestGenericFn(comptime suffix: []const u8) type {
2459        \\    return struct {
2460        \\        fn f() linksection(".TestGenFn" ++ suffix) void {}
2461        \\    };
2462        \\}
2463    });
2464
2465    const check = obj.checkObject();
2466    check.checkInSymtab();
2467    check.checkContains("SECTION LOCAL DEFAULT .TestGlobal");
2468    check.checkInSymtab();
2469    check.checkContains("SECTION LOCAL DEFAULT .TestFn");
2470    check.checkInSymtab();
2471    check.checkContains("SECTION LOCAL DEFAULT .TestGenFnA");
2472    check.checkInSymtab();
2473    check.checkContains("OBJECT GLOBAL DEFAULT test_global");
2474    check.checkInSymtab();
2475    check.checkContains("FUNC GLOBAL DEFAULT testFn");
2476
2477    if (opts.optimize == .Debug) {
2478        check.checkInSymtab();
2479        check.checkContains("FUNC LOCAL DEFAULT main.TestGenericFn(");
2480    }
2481
2482    test_step.dependOn(&check.step);
2483
2484    return test_step;
2485}
2486
2487// Adapted from https://github.com/rui314/mold/blob/main/test/elf/mergeable-strings.sh
2488fn testMergeStrings(b: *Build, opts: Options) *Step {
2489    const test_step = addTestStep(b, "merge-strings", opts);
2490
2491    const obj1 = addObject(b, opts, .{ .name = "a.o" });
2492    addCSourceBytes(obj1,
2493        \\#include <uchar.h>
2494        \\#include <wchar.h>
2495        \\char *cstr1 = "foo";
2496        \\wchar_t *wide1 = L"foo";
2497        \\char16_t *utf16_1 = u"foo";
2498        \\char32_t *utf32_1 = U"foo";
2499    , &.{"-O2"});
2500    obj1.root_module.link_libc = true;
2501
2502    const obj2 = addObject(b, opts, .{ .name = "b.o" });
2503    addCSourceBytes(obj2,
2504        \\#include <stdio.h>
2505        \\#include <assert.h>
2506        \\#include <uchar.h>
2507        \\#include <wchar.h>
2508        \\extern char *cstr1;
2509        \\extern wchar_t *wide1;
2510        \\extern char16_t *utf16_1;
2511        \\extern char32_t *utf32_1;
2512        \\char *cstr2 = "foo";
2513        \\wchar_t *wide2 = L"foo";
2514        \\char16_t *utf16_2 = u"foo";
2515        \\char32_t *utf32_2 = U"foo";
2516        \\int main() {
2517        \\ printf("%p %p %p %p %p %p %p %p\n",
2518        \\ cstr1, cstr2, wide1, wide2, utf16_1, utf16_2, utf32_1, utf32_2);
2519        \\  assert((void*)cstr1 ==   (void*)cstr2);
2520        \\  assert((void*)wide1 ==   (void*)wide2);
2521        \\  assert((void*)utf16_1 == (void*)utf16_2);
2522        \\  assert((void*)utf32_1 == (void*)utf32_2);
2523        \\  assert((void*)wide1 ==   (void*)utf32_1);
2524        \\  assert((void*)cstr1 !=   (void*)wide1);
2525        \\  assert((void*)cstr1 !=   (void*)utf32_1);
2526        \\  assert((void*)wide1 !=   (void*)utf16_1);
2527        \\}
2528    , &.{"-O2"});
2529    obj2.root_module.link_libc = true;
2530
2531    const exe = addExecutable(b, opts, .{ .name = "main" });
2532    exe.root_module.addObject(obj1);
2533    exe.root_module.addObject(obj2);
2534    exe.root_module.link_libc = true;
2535
2536    const run = addRunArtifact(exe);
2537    run.expectExitCode(0);
2538    test_step.dependOn(&run.step);
2539
2540    return test_step;
2541}
2542
2543fn testMergeStrings2(b: *Build, opts: Options) *Step {
2544    const test_step = addTestStep(b, "merge-strings2", opts);
2545
2546    const obj1 = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
2547        \\const std = @import("std");
2548        \\export fn foo() void {
2549        \\    var arr: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
2550        \\    const slice = std.mem.sliceTo(&arr, 3);
2551        \\    std.testing.expectEqualSlices(u16, arr[0..2], slice) catch unreachable;
2552        \\}
2553    });
2554
2555    const obj2 = addObject(b, opts, .{ .name = "b", .zig_source_bytes =
2556        \\const std = @import("std");
2557        \\extern fn foo() void;
2558        \\pub fn main() void {
2559        \\    foo();
2560        \\    var arr: [5:0]u16 = [_:0]u16{ 5, 4, 3, 2, 1 };
2561        \\    const slice = std.mem.sliceTo(&arr, 3);
2562        \\    std.testing.expectEqualSlices(u16, arr[0..2], slice) catch unreachable;
2563        \\}
2564    });
2565
2566    {
2567        const exe = addExecutable(b, opts, .{ .name = "main1" });
2568        exe.root_module.addObject(obj1);
2569        exe.root_module.addObject(obj2);
2570
2571        const run = addRunArtifact(exe);
2572        run.expectExitCode(0);
2573        test_step.dependOn(&run.step);
2574
2575        const check = exe.checkObject();
2576        check.dumpSection(".rodata.str");
2577        check.checkContains("\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x00\x00");
2578        check.dumpSection(".rodata.str");
2579        check.checkContains("\x05\x00\x04\x00\x03\x00\x02\x00\x01\x00\x00\x00");
2580        test_step.dependOn(&check.step);
2581    }
2582
2583    {
2584        const obj3 = addObject(b, opts, .{ .name = "c" });
2585        obj3.root_module.addObject(obj1);
2586        obj3.root_module.addObject(obj2);
2587
2588        const exe = addExecutable(b, opts, .{ .name = "main2" });
2589        exe.root_module.addObject(obj3);
2590
2591        const run = addRunArtifact(exe);
2592        run.expectExitCode(0);
2593        test_step.dependOn(&run.step);
2594
2595        const check = exe.checkObject();
2596        check.dumpSection(".rodata.str");
2597        check.checkContains("\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x00\x00");
2598        check.dumpSection(".rodata.str");
2599        check.checkContains("\x05\x00\x04\x00\x03\x00\x02\x00\x01\x00\x00\x00");
2600        test_step.dependOn(&check.step);
2601    }
2602
2603    return test_step;
2604}
2605
2606fn testNoEhFrameHdr(b: *Build, opts: Options) *Step {
2607    const test_step = addTestStep(b, "no-eh-frame-hdr", opts);
2608
2609    const exe = addExecutable(b, opts, .{ .name = "main" });
2610    addCSourceBytes(exe, "int main() { return 0; }", &.{});
2611    exe.link_eh_frame_hdr = false;
2612    exe.root_module.link_libc = true;
2613
2614    const check = exe.checkObject();
2615    check.checkInHeaders();
2616    check.checkExact("section headers");
2617    check.checkNotPresent("name .eh_frame_hdr");
2618    test_step.dependOn(&check.step);
2619
2620    return test_step;
2621}
2622
2623fn testPie(b: *Build, opts: Options) *Step {
2624    const test_step = addTestStep(b, "hello-pie", opts);
2625
2626    const exe = addExecutable(b, opts, .{ .name = "main" });
2627    addCSourceBytes(exe,
2628        \\#include <stdio.h>
2629        \\int main() {
2630        \\  printf("Hello!\n");
2631        \\  return 0;
2632        \\}
2633    , &.{});
2634    exe.root_module.link_libc = true;
2635    exe.root_module.pic = true;
2636    exe.pie = true;
2637
2638    const run = addRunArtifact(exe);
2639    run.expectStdOutEqual("Hello!\n");
2640    test_step.dependOn(&run.step);
2641
2642    const check = exe.checkObject();
2643    check.checkInHeaders();
2644    check.checkExact("header");
2645    check.checkExact("type DYN");
2646    check.checkInHeaders();
2647    check.checkExact("section headers");
2648    check.checkExact("name .dynamic");
2649    test_step.dependOn(&check.step);
2650
2651    return test_step;
2652}
2653
2654fn testPltGot(b: *Build, opts: Options) *Step {
2655    const test_step = addTestStep(b, "plt-got", opts);
2656
2657    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
2658    addCSourceBytes(dso,
2659        \\#include <stdio.h>
2660        \\void ignore(void *foo) {}
2661        \\void hello() {
2662        \\  printf("Hello world\n");
2663        \\}
2664    , &.{});
2665    dso.root_module.link_libc = true;
2666
2667    const exe = addExecutable(b, opts, .{ .name = "main" });
2668    addCSourceBytes(exe,
2669        \\void ignore(void *);
2670        \\int hello();
2671        \\void foo() { ignore(hello); }
2672        \\int main() { hello(); }
2673    , &.{});
2674    exe.root_module.linkLibrary(dso);
2675    exe.root_module.pic = true;
2676    exe.root_module.link_libc = true;
2677
2678    const run = addRunArtifact(exe);
2679    run.expectStdOutEqual("Hello world\n");
2680    test_step.dependOn(&run.step);
2681
2682    return test_step;
2683}
2684
2685fn testPreinitArray(b: *Build, opts: Options) *Step {
2686    const test_step = addTestStep(b, "preinit-array", opts);
2687
2688    {
2689        const obj = addObject(b, opts, .{
2690            .name = "obj",
2691            .c_source_bytes = "void _start() {}",
2692        });
2693
2694        const exe = addExecutable(b, opts, .{ .name = "main1" });
2695        exe.root_module.addObject(obj);
2696
2697        const check = exe.checkObject();
2698        check.checkInDynamicSection();
2699        check.checkNotPresent("PREINIT_ARRAY");
2700    }
2701
2702    {
2703        const exe = addExecutable(b, opts, .{ .name = "main2" });
2704        addCSourceBytes(exe,
2705            \\void preinit_fn() {}
2706            \\int main() {}
2707            \\__attribute__((section(".preinit_array")))
2708            \\void *preinit[] = { preinit_fn };
2709        , &.{});
2710        exe.root_module.link_libc = true;
2711
2712        const check = exe.checkObject();
2713        check.checkInDynamicSection();
2714        check.checkContains("PREINIT_ARRAY");
2715    }
2716
2717    return test_step;
2718}
2719
2720fn testRelocatableArchive(b: *Build, opts: Options) *Step {
2721    const test_step = addTestStep(b, "relocatable-archive", opts);
2722
2723    const obj1 = addObject(b, opts, .{
2724        .name = "obj1",
2725        .c_source_bytes =
2726        \\void bar();
2727        \\void foo() {
2728        \\  bar();
2729        \\}
2730        ,
2731    });
2732
2733    const obj2 = addObject(b, opts, .{
2734        .name = "obj2",
2735        .c_source_bytes =
2736        \\void bar() {}
2737        ,
2738    });
2739
2740    const obj3 = addObject(b, opts, .{
2741        .name = "obj3",
2742        .c_source_bytes =
2743        \\void baz();
2744        ,
2745    });
2746
2747    const obj4 = addObject(b, opts, .{
2748        .name = "obj4",
2749        .c_source_bytes =
2750        \\void foo();
2751        \\int main() {
2752        \\  foo();
2753        \\}
2754        ,
2755    });
2756
2757    const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
2758    lib.root_module.addObject(obj1);
2759    lib.root_module.addObject(obj2);
2760    lib.root_module.addObject(obj3);
2761
2762    const obj5 = addObject(b, opts, .{
2763        .name = "obj5",
2764    });
2765    obj5.root_module.addObject(obj4);
2766    obj5.root_module.linkLibrary(lib);
2767
2768    const check = obj5.checkObject();
2769    check.checkInSymtab();
2770    check.checkContains("foo");
2771    check.checkInSymtab();
2772    check.checkContains("bar");
2773    check.checkInSymtab();
2774    check.checkNotPresent("baz");
2775    test_step.dependOn(&check.step);
2776
2777    return test_step;
2778}
2779
2780fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
2781    const test_step = addTestStep(b, "relocatable-eh-frame", opts);
2782
2783    const obj1 = addObject(b, opts, .{
2784        .name = "obj1",
2785        .cpp_source_bytes =
2786        \\#include <stdexcept>
2787        \\int try_me() {
2788        \\  throw std::runtime_error("Oh no!");
2789        \\}
2790        ,
2791    });
2792    obj1.root_module.link_libcpp = true;
2793    const obj2 = addObject(b, opts, .{
2794        .name = "obj2",
2795        .cpp_source_bytes =
2796        \\extern int try_me();
2797        \\int try_again() {
2798        \\  return try_me();
2799        \\}
2800        ,
2801    });
2802    obj2.root_module.link_libcpp = true;
2803    const obj3 = addObject(b, opts, .{ .name = "obj3", .cpp_source_bytes =
2804        \\#include <iostream>
2805        \\#include <stdexcept>
2806        \\extern int try_again();
2807        \\int main() {
2808        \\  try {
2809        \\    try_again();
2810        \\  } catch (const std::exception &e) {
2811        \\    std::cout << "exception=" << e.what();
2812        \\  }
2813        \\  return 0;
2814        \\}
2815    });
2816    obj3.root_module.link_libcpp = true;
2817
2818    {
2819        const obj = addObject(b, opts, .{ .name = "obj" });
2820        obj.root_module.addObject(obj1);
2821        obj.root_module.addObject(obj2);
2822        obj.root_module.link_libcpp = true;
2823
2824        const exe = addExecutable(b, opts, .{ .name = "test1" });
2825        exe.root_module.addObject(obj3);
2826        exe.root_module.addObject(obj);
2827        exe.root_module.link_libcpp = true;
2828
2829        const run = addRunArtifact(exe);
2830        run.expectStdOutEqual("exception=Oh no!");
2831        test_step.dependOn(&run.step);
2832    }
2833    {
2834        // Flipping the order should not influence the end result.
2835        const obj = addObject(b, opts, .{ .name = "obj" });
2836        obj.root_module.addObject(obj2);
2837        obj.root_module.addObject(obj1);
2838        obj.root_module.link_libcpp = true;
2839
2840        const exe = addExecutable(b, opts, .{ .name = "test2" });
2841        exe.root_module.addObject(obj3);
2842        exe.root_module.addObject(obj);
2843        exe.root_module.link_libcpp = true;
2844
2845        const run = addRunArtifact(exe);
2846        run.expectStdOutEqual("exception=Oh no!");
2847        test_step.dependOn(&run.step);
2848    }
2849
2850    return test_step;
2851}
2852
2853fn testRelocatableEhFrameComdatHeavy(b: *Build, opts: Options) *Step {
2854    const test_step = addTestStep(b, "relocatable-eh-frame-comdat-heavy", opts);
2855
2856    const obj1 = addObject(b, opts, .{
2857        .name = "obj1",
2858        .cpp_source_bytes =
2859        \\#include <stdexcept>
2860        \\int try_me() {
2861        \\  throw std::runtime_error("Oh no!");
2862        \\}
2863        ,
2864    });
2865    obj1.root_module.link_libcpp = true;
2866    const obj2 = addObject(b, opts, .{
2867        .name = "obj2",
2868        .cpp_source_bytes =
2869        \\extern int try_me();
2870        \\int try_again() {
2871        \\  return try_me();
2872        \\}
2873        ,
2874    });
2875    obj2.root_module.link_libcpp = true;
2876    const obj3 = addObject(b, opts, .{
2877        .name = "obj3",
2878        .cpp_source_bytes =
2879        \\#include <iostream>
2880        \\#include <stdexcept>
2881        \\extern int try_again();
2882        \\int main() {
2883        \\  try {
2884        \\    try_again();
2885        \\  } catch (const std::exception &e) {
2886        \\    std::cout << "exception=" << e.what();
2887        \\  }
2888        \\  return 0;
2889        \\}
2890        ,
2891    });
2892    obj3.root_module.link_libcpp = true;
2893
2894    const obj = addObject(b, opts, .{ .name = "obj" });
2895    obj.root_module.addObject(obj1);
2896    obj.root_module.addObject(obj2);
2897    obj.root_module.addObject(obj3);
2898    obj.root_module.link_libcpp = true;
2899
2900    const exe = addExecutable(b, opts, .{ .name = "test2" });
2901    exe.root_module.addObject(obj);
2902    exe.root_module.link_libcpp = true;
2903
2904    const run = addRunArtifact(exe);
2905    run.expectStdOutEqual("exception=Oh no!");
2906    test_step.dependOn(&run.step);
2907
2908    return test_step;
2909}
2910
2911// Adapted from https://github.com/rui314/mold/blob/main/test/elf/relocatable-mergeable-sections.sh
2912fn testRelocatableMergeStrings(b: *Build, opts: Options) *Step {
2913    const test_step = addTestStep(b, "relocatable-merge-strings", opts);
2914
2915    const obj1 = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
2916        \\.section .rodata.str1.1,"aMS",@progbits,1
2917        \\val1:
2918        \\.ascii "Hello \0"
2919        \\.section .rodata.str1.1,"aMS",@progbits,1
2920        \\val5:
2921        \\.ascii "World \0"
2922        \\.section .rodata.str1.1,"aMS",@progbits,1
2923        \\val7:
2924        \\.ascii "Hello \0"
2925    });
2926
2927    const obj2 = addObject(b, opts, .{ .name = "b" });
2928    obj2.root_module.addObject(obj1);
2929
2930    const check = obj2.checkObject();
2931    check.dumpSection(".rodata.str1.1");
2932    check.checkExact("Hello \x00World \x00");
2933    test_step.dependOn(&check.step);
2934
2935    return test_step;
2936}
2937
2938fn testRelocatableNoEhFrame(b: *Build, opts: Options) *Step {
2939    const test_step = addTestStep(b, "relocatable-no-eh-frame", opts);
2940
2941    const obj1 = addObject(b, opts, .{
2942        .name = "obj1",
2943        .c_source_bytes = "int bar() { return 42; }",
2944        .c_source_flags = &.{
2945            "-fno-unwind-tables",
2946            "-fno-asynchronous-unwind-tables",
2947        },
2948    });
2949
2950    const obj2 = addObject(b, opts, .{
2951        .name = "obj2",
2952    });
2953    obj2.root_module.addObject(obj1);
2954
2955    const check1 = obj1.checkObject();
2956    check1.checkInHeaders();
2957    check1.checkExact("section headers");
2958    check1.checkNotPresent(".eh_frame");
2959    test_step.dependOn(&check1.step);
2960
2961    const check2 = obj2.checkObject();
2962    check2.checkInHeaders();
2963    check2.checkExact("section headers");
2964    check2.checkNotPresent(".eh_frame");
2965    test_step.dependOn(&check2.step);
2966
2967    return test_step;
2968}
2969
2970fn testSharedAbsSymbol(b: *Build, opts: Options) *Step {
2971    const test_step = addTestStep(b, "shared-abs-symbol", opts);
2972
2973    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
2974    addAsmSourceBytes(dso,
2975        \\.globl foo
2976        \\foo = 3;
2977    );
2978
2979    const obj = addObject(b, opts, .{
2980        .name = "obj",
2981        .c_source_bytes =
2982        \\#include <stdio.h>
2983        \\extern char foo;
2984        \\int main() { printf("foo=%p\n", &foo); }
2985        ,
2986        .pic = true,
2987    });
2988    obj.root_module.link_libc = true;
2989
2990    {
2991        const exe = addExecutable(b, opts, .{ .name = "main1" });
2992        exe.root_module.addObject(obj);
2993        exe.root_module.linkLibrary(dso);
2994        exe.pie = true;
2995
2996        const run = addRunArtifact(exe);
2997        run.expectStdOutEqual("foo=0x3\n");
2998        test_step.dependOn(&run.step);
2999
3000        const check = exe.checkObject();
3001        check.checkInHeaders();
3002        check.checkExact("header");
3003        check.checkExact("type DYN");
3004        // TODO fix/improve in CheckObject
3005        // check.checkInSymtab();
3006        // check.checkNotPresent("foo");
3007        test_step.dependOn(&check.step);
3008    }
3009
3010    // https://github.com/ziglang/zig/issues/17430
3011    // {
3012    //     const exe = addExecutable(b, opts, .{ .name = "main2"});
3013    //     exe.root_module.addObject(obj);
3014    //     exe.root_module.linkLibrary(dso);
3015    //     exe.pie = false;
3016
3017    //     const run = addRunArtifact(exe);
3018    //     run.expectStdOutEqual("foo=0x3\n");
3019    //     test_step.dependOn(&run.step);
3020
3021    //     const check = exe.checkObject();
3022    //     check.checkInHeaders();
3023    //     check.checkExact("header");
3024    //     check.checkExact("type EXEC");
3025    //     // TODO fix/improve in CheckObject
3026    //     // check.checkInSymtab();
3027    //     // check.checkNotPresent("foo");
3028    //     test_step.dependOn(&check.step);
3029    // }
3030
3031    return test_step;
3032}
3033
3034fn testStrip(b: *Build, opts: Options) *Step {
3035    const test_step = addTestStep(b, "strip", opts);
3036
3037    const obj = addObject(b, opts, .{
3038        .name = "obj",
3039        .c_source_bytes =
3040        \\#include <stdio.h>
3041        \\int main() {
3042        \\  printf("Hello!\n");
3043        \\  return 0;
3044        \\}
3045        ,
3046    });
3047    obj.root_module.link_libc = true;
3048
3049    {
3050        const exe = addExecutable(b, opts, .{ .name = "main1" });
3051        exe.root_module.addObject(obj);
3052        exe.root_module.strip = false;
3053        exe.root_module.link_libc = true;
3054
3055        const check = exe.checkObject();
3056        check.checkInHeaders();
3057        check.checkExact("section headers");
3058        check.checkExact("name .debug_info");
3059        test_step.dependOn(&check.step);
3060    }
3061
3062    {
3063        const exe = addExecutable(b, opts, .{ .name = "main2" });
3064        exe.root_module.addObject(obj);
3065        exe.root_module.strip = true;
3066        exe.root_module.link_libc = true;
3067
3068        const check = exe.checkObject();
3069        check.checkInHeaders();
3070        check.checkExact("section headers");
3071        check.checkNotPresent("name .debug_info");
3072        test_step.dependOn(&check.step);
3073    }
3074
3075    return test_step;
3076}
3077
3078fn testThunks(b: *Build, opts: Options) *Step {
3079    const test_step = addTestStep(b, "thunks", opts);
3080
3081    const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
3082        \\void foo();
3083        \\__attribute__((section(".bar"))) void bar() {
3084        \\  return foo();
3085        \\}
3086        \\__attribute__((section(".foo"))) void foo() {
3087        \\  return bar();
3088        \\}
3089        \\int main() {
3090        \\  foo();
3091        \\  bar();
3092        \\  return 0;
3093        \\}
3094    });
3095
3096    const check = exe.checkObject();
3097    check.checkInSymtab();
3098    check.checkContains("foo$thunk");
3099    check.checkInSymtab();
3100    check.checkContains("bar$thunk");
3101    test_step.dependOn(&check.step);
3102
3103    return test_step;
3104}
3105
3106fn testTlsDfStaticTls(b: *Build, opts: Options) *Step {
3107    const test_step = addTestStep(b, "tls-df-static-tls", opts);
3108
3109    const obj = addObject(b, opts, .{
3110        .name = "obj",
3111        .c_source_bytes =
3112        \\static _Thread_local int foo = 5;
3113        \\void mutate() { ++foo; }
3114        \\int bar() { return foo; }
3115        ,
3116        .c_source_flags = &.{"-ftls-model=initial-exec"},
3117        .pic = true,
3118    });
3119
3120    {
3121        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3122        dso.root_module.addObject(obj);
3123        // dso.link_relax = true;
3124
3125        const check = dso.checkObject();
3126        check.checkInDynamicSection();
3127        check.checkContains("STATIC_TLS");
3128        test_step.dependOn(&check.step);
3129    }
3130
3131    // TODO add -Wl,--no-relax
3132    // {
3133    //     const dso = addSharedLibrary(b, opts, .{ .name = "a"});
3134    //     dso.root_module.addObject(obj);
3135    //     dso.link_relax = false;
3136
3137    //     const check = dso.checkObject();
3138    //     check.checkInDynamicSection();
3139    //     check.checkContains("STATIC_TLS");
3140    //     test_step.dependOn(&check.step);
3141    // }
3142
3143    return test_step;
3144}
3145
3146fn testTlsDso(b: *Build, opts: Options) *Step {
3147    const test_step = addTestStep(b, "tls-dso", opts);
3148
3149    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3150    addCSourceBytes(dso,
3151        \\extern _Thread_local int foo;
3152        \\_Thread_local int bar;
3153        \\int get_foo1() { return foo; }
3154        \\int get_bar1() { return bar; }
3155    , &.{});
3156
3157    const exe = addExecutable(b, opts, .{ .name = "main" });
3158    addCSourceBytes(exe,
3159        \\#include <stdio.h>
3160        \\_Thread_local int foo;
3161        \\extern _Thread_local int bar;
3162        \\int get_foo1();
3163        \\int get_bar1();
3164        \\int get_foo2() { return foo; }
3165        \\int get_bar2() { return bar; }
3166        \\int main() {
3167        \\  foo = 5;
3168        \\  bar = 3;
3169        \\  printf("%d %d %d %d %d %d\n",
3170        \\         foo, bar,
3171        \\         get_foo1(), get_bar1(),
3172        \\         get_foo2(), get_bar2());
3173        \\  return 0;
3174        \\}
3175    , &.{});
3176    exe.root_module.linkLibrary(dso);
3177    exe.root_module.link_libc = true;
3178
3179    const run = addRunArtifact(exe);
3180    run.expectStdOutEqual("5 3 5 3 5 3\n");
3181    test_step.dependOn(&run.step);
3182
3183    return test_step;
3184}
3185
3186fn testTlsGd(b: *Build, opts: Options) *Step {
3187    const test_step = addTestStep(b, "tls-gd", opts);
3188
3189    const main_o = addObject(b, opts, .{
3190        .name = "main",
3191        .c_source_bytes =
3192        \\#include <stdio.h>
3193        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
3194        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2;
3195        \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x3;
3196        \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x4;
3197        \\int get_x5();
3198        \\int get_x6();
3199        \\int main() {
3200        \\  x2 = 2;
3201        \\  printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6());
3202        \\  return 0;
3203        \\}
3204        ,
3205        .pic = true,
3206    });
3207    main_o.root_module.link_libc = true;
3208
3209    const a_o = addObject(b, opts, .{
3210        .name = "a",
3211        .c_source_bytes =
3212        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
3213        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
3214        \\int get_x5() { return x5; }
3215        ,
3216        .pic = true,
3217    });
3218
3219    const b_o = addObject(b, opts, .{
3220        .name = "b",
3221        .c_source_bytes =
3222        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
3223        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
3224        \\int get_x6() { return x6; }
3225        ,
3226        .pic = true,
3227    });
3228
3229    const exp_stdout = "1 2 3 4 5 6\n";
3230
3231    const dso1 = addSharedLibrary(b, opts, .{ .name = "a" });
3232    dso1.root_module.addObject(a_o);
3233
3234    const dso2 = addSharedLibrary(b, opts, .{ .name = "b" });
3235    dso2.root_module.addObject(b_o);
3236    // dso2.link_relax = false; // TODO
3237
3238    {
3239        const exe = addExecutable(b, opts, .{ .name = "main1" });
3240        exe.root_module.addObject(main_o);
3241        exe.root_module.linkLibrary(dso1);
3242        exe.root_module.linkLibrary(dso2);
3243
3244        const run = addRunArtifact(exe);
3245        run.expectStdOutEqual(exp_stdout);
3246        test_step.dependOn(&run.step);
3247    }
3248
3249    {
3250        const exe = addExecutable(b, opts, .{ .name = "main2" });
3251        exe.root_module.addObject(main_o);
3252        // exe.link_relax = false; // TODO
3253        exe.root_module.linkLibrary(dso1);
3254        exe.root_module.linkLibrary(dso2);
3255
3256        const run = addRunArtifact(exe);
3257        run.expectStdOutEqual(exp_stdout);
3258        test_step.dependOn(&run.step);
3259    }
3260
3261    // https://github.com/ziglang/zig/issues/17430 ??
3262    // {
3263    //     const exe = addExecutable(b, opts, .{ .name = "main3"});
3264    //     exe.root_module.addObject(main_o);
3265    //     exe.root_module.linkLibrary(dso1);
3266    //     exe.root_module.linkLibrary(dso2);
3267    //     exe.linkage = .static;
3268
3269    //     const run = addRunArtifact(exe);
3270    //     run.expectStdOutEqual(exp_stdout);
3271    //     test_step.dependOn(&run.step);
3272    // }
3273
3274    // {
3275    //     const exe = addExecutable(b, opts, .{ .name = "main4"});
3276    //     exe.root_module.addObject(main_o);
3277    //     // exe.link_relax = false; // TODO
3278    //     exe.root_module.linkLibrary(dso1);
3279    //     exe.root_module.linkLibrary(dso2);
3280    //     exe.linkage = .static;
3281
3282    //     const run = addRunArtifact(exe);
3283    //     run.expectStdOutEqual(exp_stdout);
3284    //     test_step.dependOn(&run.step);
3285    // }
3286
3287    return test_step;
3288}
3289
3290fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
3291    const test_step = addTestStep(b, "tls-gd-no-plt", opts);
3292
3293    const obj = addObject(b, opts, .{
3294        .name = "obj",
3295        .c_source_bytes =
3296        \\#include <stdio.h>
3297        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
3298        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2;
3299        \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x3;
3300        \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x4;
3301        \\int get_x5();
3302        \\int get_x6();
3303        \\int main() {
3304        \\  x2 = 2;
3305        \\
3306        \\  printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6());
3307        \\  return 0;
3308        \\}
3309        ,
3310        .c_source_flags = &.{"-fno-plt"},
3311        .pic = true,
3312    });
3313    obj.root_module.link_libc = true;
3314
3315    const a_so = addSharedLibrary(b, opts, .{ .name = "a" });
3316    addCSourceBytes(a_so,
3317        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
3318        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
3319        \\int get_x5() { return x5; }
3320    , &.{"-fno-plt"});
3321
3322    const b_so = addSharedLibrary(b, opts, .{ .name = "b" });
3323    addCSourceBytes(b_so,
3324        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
3325        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
3326        \\int get_x6() { return x6; }
3327    , &.{"-fno-plt"});
3328    // b_so.link_relax = false; // TODO
3329
3330    {
3331        const exe = addExecutable(b, opts, .{ .name = "main1" });
3332        exe.root_module.addObject(obj);
3333        exe.root_module.linkLibrary(a_so);
3334        exe.root_module.linkLibrary(b_so);
3335        exe.root_module.link_libc = true;
3336
3337        const run = addRunArtifact(exe);
3338        run.expectStdOutEqual("1 2 3 4 5 6\n");
3339        test_step.dependOn(&run.step);
3340    }
3341
3342    {
3343        const exe = addExecutable(b, opts, .{ .name = "main2" });
3344        exe.root_module.addObject(obj);
3345        exe.root_module.linkLibrary(a_so);
3346        exe.root_module.linkLibrary(b_so);
3347        exe.root_module.link_libc = true;
3348        // exe.link_relax = false; // TODO
3349
3350        const run = addRunArtifact(exe);
3351        run.expectStdOutEqual("1 2 3 4 5 6\n");
3352        test_step.dependOn(&run.step);
3353    }
3354
3355    return test_step;
3356}
3357
3358fn testTlsGdToIe(b: *Build, opts: Options) *Step {
3359    const test_step = addTestStep(b, "tls-gd-to-ie", opts);
3360
3361    const a_o = addObject(b, opts, .{
3362        .name = "a",
3363        .c_source_bytes =
3364        \\#include <stdio.h>
3365        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
3366        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x2 = 2;
3367        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3;
3368        \\int foo() {
3369        \\  x3 = 3;
3370        \\
3371        \\  printf("%d %d %d\n", x1, x2, x3);
3372        \\  return 0;
3373        \\}
3374        ,
3375        .pic = true,
3376    });
3377    a_o.root_module.link_libc = true;
3378
3379    const b_o = addObject(b, opts, .{
3380        .name = "b",
3381        .c_source_bytes =
3382        \\int foo();
3383        \\int main() { foo(); }
3384        ,
3385        .pic = true,
3386    });
3387
3388    {
3389        const dso = addSharedLibrary(b, opts, .{ .name = "a1" });
3390        dso.root_module.addObject(a_o);
3391
3392        const exe = addExecutable(b, opts, .{ .name = "main1" });
3393        exe.root_module.addObject(b_o);
3394        exe.root_module.linkLibrary(dso);
3395        exe.root_module.link_libc = true;
3396
3397        const run = addRunArtifact(exe);
3398        run.expectStdOutEqual("1 2 3\n");
3399        test_step.dependOn(&run.step);
3400    }
3401
3402    {
3403        const dso = addSharedLibrary(b, opts, .{ .name = "a2" });
3404        dso.root_module.addObject(a_o);
3405        // dso.link_relax = false; // TODO
3406
3407        const exe = addExecutable(b, opts, .{ .name = "main2" });
3408        exe.root_module.addObject(b_o);
3409        exe.root_module.linkLibrary(dso);
3410        exe.root_module.link_libc = true;
3411
3412        const run = addRunArtifact(exe);
3413        run.expectStdOutEqual("1 2 3\n");
3414        test_step.dependOn(&run.step);
3415    }
3416
3417    // {
3418    //     const dso = addSharedLibrary(b, opts, .{ .name = "a"});
3419    //     dso.root_module.addObject(a_o);
3420    //     dso.link_z_nodlopen = true;
3421
3422    //     const exe = addExecutable(b, opts, .{ .name = "main"});
3423    //     exe.root_module.addObject(b_o);
3424    //     exe.root_module.linkLibrary(dso);
3425
3426    //     const run = addRunArtifact(exe);
3427    //     run.expectStdOutEqual("1 2 3\n");
3428    //     test_step.dependOn(&run.step);
3429    // }
3430
3431    // {
3432    //     const dso = addSharedLibrary(b, opts, .{ .name = "a"});
3433    //     dso.root_module.addObject(a_o);
3434    //     dso.link_relax = false;
3435    //     dso.link_z_nodlopen = true;
3436
3437    //     const exe = addExecutable(b, opts, .{ .name = "main"});
3438    //     exe.root_module.addObject(b_o);
3439    //     exe.root_module.linkLibrary(dso);
3440
3441    //     const run = addRunArtifact(exe);
3442    //     run.expectStdOutEqual("1 2 3\n");
3443    //     test_step.dependOn(&run.step);
3444    // }
3445
3446    return test_step;
3447}
3448
3449fn testTlsIe(b: *Build, opts: Options) *Step {
3450    const test_step = addTestStep(b, "tls-ie", opts);
3451
3452    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3453    addCSourceBytes(dso,
3454        \\#include <stdio.h>
3455        \\__attribute__((tls_model("initial-exec"))) static _Thread_local int foo;
3456        \\__attribute__((tls_model("initial-exec"))) static _Thread_local int bar;
3457        \\void set() {
3458        \\  foo = 3;
3459        \\  bar = 5;
3460        \\}
3461        \\void print() {
3462        \\  printf("%d %d ", foo, bar);
3463        \\}
3464    , &.{});
3465    dso.root_module.link_libc = true;
3466
3467    const main_o = addObject(b, opts, .{
3468        .name = "main",
3469        .c_source_bytes =
3470        \\#include <stdio.h>
3471        \\_Thread_local int baz;
3472        \\void set();
3473        \\void print();
3474        \\int main() {
3475        \\  baz = 7;
3476        \\  print();
3477        \\  set();
3478        \\  print();
3479        \\  printf("%d\n", baz);
3480        \\}
3481        ,
3482    });
3483    main_o.root_module.link_libc = true;
3484
3485    const exp_stdout = "0 0 3 5 7\n";
3486
3487    {
3488        const exe = addExecutable(b, opts, .{ .name = "main1" });
3489        exe.root_module.addObject(main_o);
3490        exe.root_module.linkLibrary(dso);
3491        exe.root_module.link_libc = true;
3492
3493        const run = addRunArtifact(exe);
3494        run.expectStdOutEqual(exp_stdout);
3495        test_step.dependOn(&run.step);
3496    }
3497
3498    {
3499        const exe = addExecutable(b, opts, .{ .name = "main2" });
3500        exe.root_module.addObject(main_o);
3501        exe.root_module.linkLibrary(dso);
3502        exe.root_module.link_libc = true;
3503        // exe.link_relax = false; // TODO
3504
3505        const run = addRunArtifact(exe);
3506        run.expectStdOutEqual(exp_stdout);
3507        test_step.dependOn(&run.step);
3508    }
3509
3510    return test_step;
3511}
3512
3513fn testTlsLargeAlignment(b: *Build, opts: Options) *Step {
3514    const test_step = addTestStep(b, "tls-large-alignment", opts);
3515
3516    const a_o = addObject(b, opts, .{
3517        .name = "a",
3518        .c_source_bytes =
3519        \\__attribute__((section(".tdata1")))
3520        \\_Thread_local int x = 42;
3521        ,
3522        .c_source_flags = &.{"-std=c11"},
3523        .pic = true,
3524    });
3525
3526    const b_o = addObject(b, opts, .{
3527        .name = "b",
3528        .c_source_bytes =
3529        \\__attribute__((section(".tdata2")))
3530        \\_Alignas(256) _Thread_local int y[] = { 1, 2, 3 };
3531        ,
3532        .c_source_flags = &.{"-std=c11"},
3533        .pic = true,
3534    });
3535
3536    const c_o = addObject(b, opts, .{
3537        .name = "c",
3538        .c_source_bytes =
3539        \\#include <stdio.h>
3540        \\extern _Thread_local int x;
3541        \\extern _Thread_local int y[];
3542        \\int main() {
3543        \\  printf("%d %d %d %d\n", x, y[0], y[1], y[2]);
3544        \\}
3545        ,
3546        .pic = true,
3547    });
3548    c_o.root_module.link_libc = true;
3549
3550    {
3551        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3552        dso.root_module.addObject(a_o);
3553        dso.root_module.addObject(b_o);
3554
3555        const exe = addExecutable(b, opts, .{ .name = "main" });
3556        exe.root_module.addObject(c_o);
3557        exe.root_module.linkLibrary(dso);
3558        exe.root_module.link_libc = true;
3559
3560        const run = addRunArtifact(exe);
3561        run.expectStdOutEqual("42 1 2 3\n");
3562        test_step.dependOn(&run.step);
3563    }
3564
3565    {
3566        const exe = addExecutable(b, opts, .{ .name = "main" });
3567        exe.root_module.addObject(a_o);
3568        exe.root_module.addObject(b_o);
3569        exe.root_module.addObject(c_o);
3570        exe.root_module.link_libc = true;
3571
3572        const run = addRunArtifact(exe);
3573        run.expectStdOutEqual("42 1 2 3\n");
3574        test_step.dependOn(&run.step);
3575    }
3576
3577    return test_step;
3578}
3579
3580fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
3581    const test_step = addTestStep(b, "tls-large-tbss", opts);
3582
3583    const exe = addExecutable(b, opts, .{ .name = "main" });
3584    addAsmSourceBytes(exe,
3585        \\.globl x, y
3586        \\.section .tbss,"awT",@nobits
3587        \\x:
3588        \\.zero 1024
3589        \\.section .tcommon,"awT",@nobits
3590        \\y:
3591        \\.zero 1024
3592    );
3593    addCSourceBytes(exe,
3594        \\#include <stdio.h>
3595        \\extern _Thread_local char x[1024000];
3596        \\extern _Thread_local char y[1024000];
3597        \\int main() {
3598        \\  x[0] = 3;
3599        \\  x[1023] = 5;
3600        \\  printf("%d %d %d %d %d %d\n", x[0], x[1], x[1023], y[0], y[1], y[1023]);
3601        \\}
3602    , &.{});
3603    exe.root_module.link_libc = true;
3604    // Disabled to work around the ELF linker crashing.
3605    // Can be reproduced on a x86_64-linux host by commenting out the line below.
3606    exe.root_module.sanitize_c = .off;
3607
3608    const run = addRunArtifact(exe);
3609    run.expectStdOutEqual("3 0 5 0 0 0\n");
3610    test_step.dependOn(&run.step);
3611
3612    return test_step;
3613}
3614
3615fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step {
3616    const test_step = addTestStep(b, "tls-large-static-image", opts);
3617
3618    const exe = addExecutable(b, opts, .{ .name = "main" });
3619    addCSourceBytes(exe, "_Thread_local int x[] = { 1, 2, 3, [10000] = 5 };", &.{});
3620    addCSourceBytes(exe,
3621        \\#include <stdio.h>
3622        \\extern _Thread_local int x[];
3623        \\int main() {
3624        \\  printf("%d %d %d %d %d\n", x[0], x[1], x[2], x[3], x[10000]);
3625        \\}
3626    , &.{});
3627    exe.root_module.pic = true;
3628    exe.root_module.link_libc = true;
3629
3630    const run = addRunArtifact(exe);
3631    run.expectStdOutEqual("1 2 3 0 5\n");
3632    test_step.dependOn(&run.step);
3633
3634    return test_step;
3635}
3636
3637fn testTlsLd(b: *Build, opts: Options) *Step {
3638    const test_step = addTestStep(b, "tls-ld", opts);
3639
3640    const main_o = addObject(b, opts, .{
3641        .name = "main",
3642        .c_source_bytes =
3643        \\#include <stdio.h>
3644        \\extern _Thread_local int foo;
3645        \\static _Thread_local int bar;
3646        \\int *get_foo_addr() { return &foo; }
3647        \\int *get_bar_addr() { return &bar; }
3648        \\int main() {
3649        \\  bar = 5;
3650        \\  printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
3651        \\  return 0;
3652        \\}
3653        ,
3654        .c_source_flags = &.{"-ftls-model=local-dynamic"},
3655        .pic = true,
3656    });
3657    main_o.root_module.link_libc = true;
3658
3659    const a_o = addObject(b, opts, .{
3660        .name = "a",
3661        .c_source_bytes = "_Thread_local int foo = 3;",
3662        .c_source_flags = &.{"-ftls-model=local-dynamic"},
3663        .pic = true,
3664    });
3665
3666    const exp_stdout = "3 5 3 5\n";
3667
3668    {
3669        const exe = addExecutable(b, opts, .{ .name = "main1" });
3670        exe.root_module.addObject(main_o);
3671        exe.root_module.addObject(a_o);
3672        exe.root_module.link_libc = true;
3673
3674        const run = addRunArtifact(exe);
3675        run.expectStdOutEqual(exp_stdout);
3676        test_step.dependOn(&run.step);
3677    }
3678
3679    {
3680        const exe = addExecutable(b, opts, .{ .name = "main2" });
3681        exe.root_module.addObject(main_o);
3682        exe.root_module.addObject(a_o);
3683        exe.root_module.link_libc = true;
3684        // exe.link_relax = false; // TODO
3685
3686        const run = addRunArtifact(exe);
3687        run.expectStdOutEqual(exp_stdout);
3688        test_step.dependOn(&run.step);
3689    }
3690
3691    return test_step;
3692}
3693
3694fn testTlsLdDso(b: *Build, opts: Options) *Step {
3695    const test_step = addTestStep(b, "tls-ld-dso", opts);
3696
3697    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3698    addCSourceBytes(dso,
3699        \\static _Thread_local int def, def1;
3700        \\int f0() { return ++def; }
3701        \\int f1() { return ++def1 + def; }
3702    , &.{"-ftls-model=local-dynamic"});
3703
3704    const exe = addExecutable(b, opts, .{ .name = "main" });
3705    addCSourceBytes(exe,
3706        \\#include <stdio.h>
3707        \\extern int f0();
3708        \\extern int f1();
3709        \\int main() {
3710        \\  int x = f0();
3711        \\  int y = f1();
3712        \\  printf("%d %d\n", x, y);
3713        \\  return 0;
3714        \\}
3715    , &.{});
3716    exe.root_module.linkLibrary(dso);
3717    exe.root_module.link_libc = true;
3718
3719    const run = addRunArtifact(exe);
3720    run.expectStdOutEqual("1 2\n");
3721    test_step.dependOn(&run.step);
3722
3723    return test_step;
3724}
3725
3726fn testTlsLdNoPlt(b: *Build, opts: Options) *Step {
3727    const test_step = addTestStep(b, "tls-ld-no-plt", opts);
3728
3729    const a_o = addObject(b, opts, .{
3730        .name = "a",
3731        .c_source_bytes =
3732        \\#include <stdio.h>
3733        \\extern _Thread_local int foo;
3734        \\static _Thread_local int bar;
3735        \\int *get_foo_addr() { return &foo; }
3736        \\int *get_bar_addr() { return &bar; }
3737        \\int main() {
3738        \\  bar = 5;
3739        \\
3740        \\  printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
3741        \\  return 0;
3742        \\}
3743        ,
3744        .c_source_flags = &.{ "-ftls-model=local-dynamic", "-fno-plt" },
3745        .pic = true,
3746    });
3747    a_o.root_module.link_libc = true;
3748
3749    const b_o = addObject(b, opts, .{
3750        .name = "b",
3751        .c_source_bytes = "_Thread_local int foo = 3;",
3752        .c_source_flags = &.{ "-ftls-model=local-dynamic", "-fno-plt" },
3753        .pic = true,
3754    });
3755
3756    {
3757        const exe = addExecutable(b, opts, .{ .name = "main1" });
3758        exe.root_module.addObject(a_o);
3759        exe.root_module.addObject(b_o);
3760        exe.root_module.link_libc = true;
3761
3762        const run = addRunArtifact(exe);
3763        run.expectStdOutEqual("3 5 3 5\n");
3764        test_step.dependOn(&run.step);
3765    }
3766
3767    {
3768        const exe = addExecutable(b, opts, .{ .name = "main2" });
3769        exe.root_module.addObject(a_o);
3770        exe.root_module.addObject(b_o);
3771        exe.root_module.link_libc = true;
3772        // exe.link_relax = false; // TODO
3773
3774        const run = addRunArtifact(exe);
3775        run.expectStdOutEqual("3 5 3 5\n");
3776        test_step.dependOn(&run.step);
3777    }
3778
3779    return test_step;
3780}
3781
3782fn testTlsNoPic(b: *Build, opts: Options) *Step {
3783    const test_step = addTestStep(b, "tls-no-pic", opts);
3784
3785    const exe = addExecutable(b, opts, .{ .name = "main" });
3786    addCSourceBytes(exe,
3787        \\#include <stdio.h>
3788        \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
3789        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar;
3790        \\int *get_foo_addr() { return &foo; }
3791        \\int *get_bar_addr() { return &bar; }
3792        \\int main() {
3793        \\  foo = 3;
3794        \\  bar = 5;
3795        \\
3796        \\  printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
3797        \\  return 0;
3798        \\}
3799    , .{});
3800    addCSourceBytes(exe,
3801        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo;
3802    , &.{});
3803    exe.root_module.pic = false;
3804    exe.root_module.link_libc = true;
3805
3806    const run = addRunArtifact(exe);
3807    run.expectStdOutEqual("3 5 3 5\n");
3808    test_step.dependOn(&run.step);
3809
3810    return test_step;
3811}
3812
3813fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
3814    const test_step = addTestStep(b, "tls-offset-alignment", opts);
3815
3816    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3817    addCSourceBytes(dso,
3818        \\#include <assert.h>
3819        \\#include <stdlib.h>
3820        \\
3821        \\// .tdata
3822        \\_Thread_local int x = 42;
3823        \\// .tbss
3824        \\__attribute__ ((aligned(64)))
3825        \\_Thread_local int y = 0;
3826        \\
3827        \\void *verify(void *unused) {
3828        \\  assert((unsigned long)(&y) % 64 == 0);
3829        \\  return NULL;
3830        \\}
3831    , &.{});
3832    dso.root_module.link_libc = true;
3833
3834    const exe = addExecutable(b, opts, .{ .name = "main" });
3835    addCSourceBytes(exe,
3836        \\#include <pthread.h>
3837        \\#include <dlfcn.h>
3838        \\#include <assert.h>
3839        \\#include <stdio.h>
3840        \\void *(*verify)(void *);
3841        \\
3842        \\int main() {
3843        \\  void *handle = dlopen("liba.so", RTLD_NOW);
3844        \\  if (!handle) {
3845        \\    fprintf(stderr, "dlopen failed: %s\n", dlerror());
3846        \\    return 1;
3847        \\  }
3848        \\  *(void**)(&verify) = dlsym(handle, "verify");
3849        \\  assert(verify);
3850        \\
3851        \\  pthread_t thread;
3852        \\
3853        \\  verify(NULL);
3854        \\
3855        \\  pthread_create(&thread, NULL, verify, NULL);
3856        \\  pthread_join(thread, NULL);
3857        \\}
3858    , &.{});
3859    exe.root_module.addRPath(dso.getEmittedBinDirectory());
3860    exe.root_module.link_libc = true;
3861    exe.root_module.pic = true;
3862
3863    const run = addRunArtifact(exe);
3864    run.expectExitCode(0);
3865    test_step.dependOn(&run.step);
3866
3867    return test_step;
3868}
3869
3870fn testTlsPic(b: *Build, opts: Options) *Step {
3871    const test_step = addTestStep(b, "tls-pic", opts);
3872
3873    const obj = addObject(b, opts, .{
3874        .name = "obj",
3875        .c_source_bytes =
3876        \\#include <stdio.h>
3877        \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
3878        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar;
3879        \\int *get_foo_addr() { return &foo; }
3880        \\int *get_bar_addr() { return &bar; }
3881        \\int main() {
3882        \\  bar = 5;
3883        \\
3884        \\  printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
3885        \\  return 0;
3886        \\}
3887        ,
3888        .pic = true,
3889    });
3890    obj.root_module.link_libc = true;
3891
3892    const exe = addExecutable(b, opts, .{ .name = "main" });
3893    addCSourceBytes(exe,
3894        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo = 3;
3895    , &.{});
3896    exe.root_module.addObject(obj);
3897    exe.root_module.link_libc = true;
3898
3899    const run = addRunArtifact(exe);
3900    run.expectStdOutEqual("3 5 3 5\n");
3901    test_step.dependOn(&run.step);
3902
3903    return test_step;
3904}
3905
3906fn testTlsSmallAlignment(b: *Build, opts: Options) *Step {
3907    const test_step = addTestStep(b, "tls-small-alignment", opts);
3908
3909    const a_o = addObject(b, opts, .{
3910        .name = "a",
3911        .asm_source_bytes =
3912        \\.text
3913        \\.byte 0
3914        \\
3915        ,
3916        .pic = true,
3917    });
3918
3919    const b_o = addObject(b, opts, .{
3920        .name = "b",
3921        .c_source_bytes = "_Thread_local char x = 42;",
3922        .c_source_flags = &.{"-std=c11"},
3923        .pic = true,
3924    });
3925
3926    const c_o = addObject(b, opts, .{
3927        .name = "c",
3928        .c_source_bytes =
3929        \\#include <stdio.h>
3930        \\extern _Thread_local char x;
3931        \\int main() {
3932        \\  printf("%d\n", x);
3933        \\}
3934        ,
3935        .pic = true,
3936    });
3937    c_o.root_module.link_libc = true;
3938
3939    {
3940        const exe = addExecutable(b, opts, .{ .name = "main" });
3941        exe.root_module.addObject(a_o);
3942        exe.root_module.addObject(b_o);
3943        exe.root_module.addObject(c_o);
3944        exe.root_module.link_libc = true;
3945
3946        const run = addRunArtifact(exe);
3947        run.expectStdOutEqual("42\n");
3948        test_step.dependOn(&run.step);
3949    }
3950
3951    {
3952        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3953        dso.root_module.addObject(a_o);
3954        dso.root_module.addObject(b_o);
3955
3956        const exe = addExecutable(b, opts, .{ .name = "main" });
3957        exe.root_module.addObject(c_o);
3958        exe.root_module.linkLibrary(dso);
3959        exe.root_module.link_libc = true;
3960
3961        const run = addRunArtifact(exe);
3962        run.expectStdOutEqual("42\n");
3963        test_step.dependOn(&run.step);
3964    }
3965
3966    return test_step;
3967}
3968
3969fn testTlsStatic(b: *Build, opts: Options) *Step {
3970    const test_step = addTestStep(b, "tls-static", opts);
3971
3972    const exe = addExecutable(b, opts, .{ .name = "test" });
3973    addCSourceBytes(exe,
3974        \\#include <stdio.h>
3975        \\_Thread_local int a = 10;
3976        \\_Thread_local int b;
3977        \\_Thread_local char c = 'a';
3978        \\int main(int argc, char* argv[]) {
3979        \\  printf("%d %d %c\n", a, b, c);
3980        \\  a += 1;
3981        \\  b += 1;
3982        \\  c += 1;
3983        \\  printf("%d %d %c\n", a, b, c);
3984        \\  return 0;
3985        \\}
3986    , &.{});
3987    exe.root_module.link_libc = true;
3988
3989    const run = addRunArtifact(exe);
3990    run.expectStdOutEqual(
3991        \\10 0 a
3992        \\11 1 b
3993        \\
3994    );
3995    test_step.dependOn(&run.step);
3996
3997    return test_step;
3998}
3999
4000fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
4001    const test_step = addTestStep(b, "unknown-file-type-error", opts);
4002
4003    const dylib = addSharedLibrary(b, .{
4004        .target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos }),
4005    }, .{
4006        .name = "a",
4007        .zig_source_bytes = "export var foo: i32 = 0;",
4008    });
4009
4010    const exe = addExecutable(b, opts, .{ .name = "main" });
4011    addCSourceBytes(exe,
4012        \\extern int foo;
4013        \\int main() {
4014        \\  return foo;
4015        \\}
4016    , &.{});
4017    exe.root_module.linkLibrary(dylib);
4018    exe.root_module.link_libc = true;
4019
4020    expectLinkErrors(exe, test_step, .{
4021        .contains = "error: failed to parse shared library: BadMagic",
4022    });
4023
4024    return test_step;
4025}
4026
4027fn testUnresolvedError(b: *Build, opts: Options) *Step {
4028    const test_step = addTestStep(b, "unresolved-error", opts);
4029
4030    const obj1 = addObject(b, opts, .{
4031        .name = "a",
4032        .c_source_bytes =
4033        \\#include <stdio.h>
4034        \\int foo();
4035        \\int bar() {
4036        \\  return foo() + 1;
4037        \\}
4038        ,
4039        .c_source_flags = &.{"-ffunction-sections"},
4040    });
4041    obj1.root_module.link_libc = true;
4042
4043    const obj2 = addObject(b, opts, .{
4044        .name = "b",
4045        .c_source_bytes =
4046        \\#include <stdio.h>
4047        \\int foo();
4048        \\int bar();
4049        \\int main() {
4050        \\  return foo() + bar();
4051        \\}
4052        ,
4053        .c_source_flags = &.{"-ffunction-sections"},
4054    });
4055    obj2.root_module.link_libc = true;
4056
4057    const exe = addExecutable(b, opts, .{ .name = "main" });
4058    exe.root_module.addObject(obj1);
4059    exe.root_module.addObject(obj2);
4060    exe.root_module.link_libc = true;
4061
4062    expectLinkErrors(exe, test_step, .{ .exact = &.{
4063        "error: undefined symbol: foo",
4064        "note: referenced by /?/a.o:.text.bar",
4065        "note: referenced by /?/b.o:.text.main",
4066    } });
4067
4068    return test_step;
4069}
4070
4071fn testWeakExports(b: *Build, opts: Options) *Step {
4072    const test_step = addTestStep(b, "weak-exports", opts);
4073
4074    const obj = addObject(b, opts, .{
4075        .name = "obj",
4076        .c_source_bytes =
4077        \\#include <stdio.h>
4078        \\__attribute__((weak)) int foo();
4079        \\int main() {
4080        \\  printf("%d\n", foo ? foo() : 3);
4081        \\}
4082        ,
4083        .pic = true,
4084    });
4085    obj.root_module.link_libc = true;
4086
4087    {
4088        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
4089        dso.root_module.addObject(obj);
4090        dso.root_module.link_libc = true;
4091
4092        const check = dso.checkObject();
4093        check.checkInDynamicSymtab();
4094        check.checkContains("UND NOTYPE WEAK DEFAULT foo");
4095        test_step.dependOn(&check.step);
4096    }
4097
4098    {
4099        const exe = addExecutable(b, opts, .{ .name = "main" });
4100        exe.root_module.addObject(obj);
4101        exe.root_module.link_libc = true;
4102
4103        const check = exe.checkObject();
4104        check.checkInDynamicSymtab();
4105        check.checkNotPresent("UND NOTYPE WEAK DEFAULT foo");
4106        test_step.dependOn(&check.step);
4107
4108        const run = addRunArtifact(exe);
4109        run.expectStdOutEqual("3\n");
4110        test_step.dependOn(&run.step);
4111    }
4112
4113    return test_step;
4114}
4115
4116fn testWeakUndefsDso(b: *Build, opts: Options) *Step {
4117    const test_step = addTestStep(b, "weak-undef-dso", opts);
4118
4119    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
4120    addCSourceBytes(dso,
4121        \\__attribute__((weak)) int foo();
4122        \\int bar() { return foo ? foo() : -1; }
4123    , &.{});
4124
4125    {
4126        const exe = addExecutable(b, opts, .{ .name = "main" });
4127        addCSourceBytes(exe,
4128            \\#include <stdio.h>
4129            \\int bar();
4130            \\int main() { printf("bar=%d\n", bar()); }
4131        , &.{});
4132        exe.root_module.linkLibrary(dso);
4133        exe.root_module.link_libc = true;
4134
4135        const run = addRunArtifact(exe);
4136        run.expectStdOutEqual("bar=-1\n");
4137        test_step.dependOn(&run.step);
4138    }
4139
4140    {
4141        const exe = addExecutable(b, opts, .{ .name = "main" });
4142        addCSourceBytes(exe,
4143            \\#include <stdio.h>
4144            \\int foo() { return 5; }
4145            \\int bar();
4146            \\int main() { printf("bar=%d\n", bar()); }
4147        , &.{});
4148        exe.root_module.linkLibrary(dso);
4149        exe.root_module.link_libc = true;
4150
4151        const run = addRunArtifact(exe);
4152        run.expectStdOutEqual("bar=5\n");
4153        test_step.dependOn(&run.step);
4154    }
4155
4156    return test_step;
4157}
4158
4159fn testZNow(b: *Build, opts: Options) *Step {
4160    const test_step = addTestStep(b, "z-now", opts);
4161
4162    const obj = addObject(b, opts, .{
4163        .name = "obj",
4164        .c_source_bytes = "int main() { return 0; }",
4165        .pic = true,
4166    });
4167
4168    {
4169        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
4170        dso.root_module.addObject(obj);
4171
4172        const check = dso.checkObject();
4173        check.checkInDynamicSection();
4174        check.checkContains("NOW");
4175        test_step.dependOn(&check.step);
4176    }
4177
4178    {
4179        const dso = addSharedLibrary(b, opts, .{ .name = "a" });
4180        dso.root_module.addObject(obj);
4181        dso.link_z_lazy = true;
4182
4183        const check = dso.checkObject();
4184        check.checkInDynamicSection();
4185        check.checkNotPresent("NOW");
4186        test_step.dependOn(&check.step);
4187    }
4188
4189    return test_step;
4190}
4191
4192fn testZStackSize(b: *Build, opts: Options) *Step {
4193    const test_step = addTestStep(b, "z-stack-size", opts);
4194
4195    const exe = addExecutable(b, opts, .{ .name = "main" });
4196    addCSourceBytes(exe, "int main() { return 0; }", &.{});
4197    exe.stack_size = 0x800000;
4198    exe.root_module.link_libc = true;
4199
4200    const check = exe.checkObject();
4201    check.checkInHeaders();
4202    check.checkExact("program headers");
4203    check.checkExact("type GNU_STACK");
4204    check.checkExact("memsz 800000");
4205    test_step.dependOn(&check.step);
4206
4207    return test_step;
4208}
4209
4210fn testZText(b: *Build, opts: Options) *Step {
4211    const test_step = addTestStep(b, "z-text", opts);
4212
4213    // Previously, following mold, this test tested text relocs present in a PIE executable.
4214    // However, as we want to cover musl AND glibc, it is now modified to test presence of
4215    // text relocs in a DSO which is then linked with an executable.
4216    // According to Rich and this thread https://www.openwall.com/lists/musl/2020/09/25/4
4217    // musl supports only a very limited number of text relocations and only in DSOs (and
4218    // rightly so!).
4219
4220    const a_o = addObject(b, opts, .{
4221        .name = "a",
4222        .asm_source_bytes =
4223        \\.globl fn1
4224        \\fn1:
4225        \\  sub $8, %rsp
4226        \\  movabs ptr, %rax
4227        \\  call *%rax
4228        \\  add $8, %rsp
4229        \\  ret
4230        \\
4231        ,
4232    });
4233
4234    const b_o = addObject(b, opts, .{
4235        .name = "b",
4236        .c_source_bytes =
4237        \\int fn1();
4238        \\int fn2() {
4239        \\  return 3;
4240        \\}
4241        \\void *ptr = fn2;
4242        \\int fnn() {
4243        \\  return fn1();
4244        \\}
4245        ,
4246        .pic = true,
4247    });
4248
4249    const dso = addSharedLibrary(b, opts, .{ .name = "a" });
4250    dso.root_module.addObject(a_o);
4251    dso.root_module.addObject(b_o);
4252    dso.link_z_notext = true;
4253
4254    const exe = addExecutable(b, opts, .{ .name = "main" });
4255    addCSourceBytes(exe,
4256        \\#include <stdio.h>
4257        \\int fnn();
4258        \\int main() {
4259        \\  printf("%d\n", fnn());
4260        \\}
4261    , &.{});
4262    exe.root_module.linkLibrary(dso);
4263    exe.root_module.link_libc = true;
4264
4265    const run = addRunArtifact(exe);
4266    run.expectStdOutEqual("3\n");
4267    test_step.dependOn(&run.step);
4268
4269    // Check for DT_TEXTREL in a DSO
4270    const check = dso.checkObject();
4271    check.checkInDynamicSection();
4272    // check.checkExact("TEXTREL 0"); // TODO fix in CheckObject parser
4273    check.checkContains("FLAGS TEXTREL");
4274    test_step.dependOn(&check.step);
4275
4276    return test_step;
4277}
4278
4279fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
4280    return link.addTestStep(b, "elf-" ++ prefix, opts);
4281}
4282
4283const addAsmSourceBytes = link.addAsmSourceBytes;
4284const addCSourceBytes = link.addCSourceBytes;
4285const addCppSourceBytes = link.addCppSourceBytes;
4286const addExecutable = link.addExecutable;
4287const addObject = link.addObject;
4288const addRunArtifact = link.addRunArtifact;
4289const addSharedLibrary = link.addSharedLibrary;
4290const addStaticLibrary = link.addStaticLibrary;
4291const expectLinkErrors = link.expectLinkErrors;
4292const link = @import("link.zig");
4293const std = @import("std");
4294const builtin = @import("builtin");
4295
4296const Build = std.Build;
4297const BuildOptions = link.BuildOptions;
4298const Options = link.Options;
4299const Step = Build.Step;
4300const WriteFile = Step.WriteFile;