Commit 67a02bee2c

Jakub Konka <kubkon@jakubkonka.com>
2023-10-09 23:43:13
elf: port more linker tests
1 parent 81b68c7
Changed files (1)
test
link
test/link/elf.zig
@@ -82,6 +82,26 @@ pub fn build(b: *Build) void {
     elf_step.dependOn(testTlsDfStaticTls(b, .{ .target = glibc_target }));
     elf_step.dependOn(testTlsDso(b, .{ .target = glibc_target }));
     elf_step.dependOn(testTlsGd(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testTlsGdNoPlt(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testTlsGdToIe(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testTlsIe(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testTlsLargeAlignment(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testTlsLargeTbss(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testTlsLargeStaticImage(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testTlsLd(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testTlsLdDso(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testTlsLdNoPlt(b, .{ .target = glibc_target }));
+    // https://github.com/ziglang/zig/issues/17430
+    // elf_step.dependOn(testTlsNoPic(b, .{ .target = glibc_target }));
+    // TODO
+    // elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testTlsPic(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testTlsSmallAlignment(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testWeakExports(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testWeakUndefsDso(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testZNow(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testZStackSize(b, .{ .target = glibc_target }));
+    elf_step.dependOn(testZText(b, .{ .target = glibc_target }));
 }
 
 fn testAbsSymbols(b: *Build, opts: Options) *Step {
@@ -1924,37 +1944,871 @@ fn testTlsGd(b: *Build, opts: Options) *Step {
     return test_step;
 }
 
-fn testTlsStatic(b: *Build, opts: Options) *Step {
-    const test_step = addTestStep(b, "tls-static", opts);
+fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-gd-no-plt", opts);
 
-    const exe = addExecutable(b, "test", opts);
-    addCSourceBytes(exe,
+    const obj = addObject(b, "obj", opts);
+    addCSourceBytes(obj,
         \\#include <stdio.h>
-        \\_Thread_local int a = 10;
-        \\_Thread_local int b;
-        \\_Thread_local char c = 'a';
-        \\int main(int argc, char* argv[]) {
-        \\  printf("%d %d %c\n", a, b, c);
-        \\  a += 1;
-        \\  b += 1;
-        \\  c += 1;
-        \\  printf("%d %d %c\n", a, b, c);
+        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
+        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2;
+        \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x3;
+        \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x4;
+        \\int get_x5();
+        \\int get_x6();
+        \\int main() {
+        \\  x2 = 2;
+        \\
+        \\  printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6());
+        \\  return 0;
+        \\}
+    , &.{"-fno-plt"});
+    obj.force_pic = true;
+    obj.linkLibC();
+
+    const a_so = addSharedLibrary(b, "a", opts);
+    addCSourceBytes(a_so,
+        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
+        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
+        \\int get_x5() { return x5; }
+    , &.{"-fno-plt"});
+
+    const b_so = addSharedLibrary(b, "b", opts);
+    addCSourceBytes(b_so,
+        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
+        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
+        \\int get_x6() { return x6; }
+    , &.{"-fno-plt"});
+    // b_so.link_relax = false; // TODO
+
+    {
+        const exe = addExecutable(b, "main1", opts);
+        exe.addObject(obj);
+        exe.linkLibrary(a_so);
+        exe.linkLibrary(b_so);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("1 2 3 4 5 6\n");
+        test_step.dependOn(&run.step);
+    }
+
+    {
+        const exe = addExecutable(b, "main2", opts);
+        exe.addObject(obj);
+        exe.linkLibrary(a_so);
+        exe.linkLibrary(b_so);
+        exe.linkLibC();
+        // exe.link_relax = false; // TODO
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("1 2 3 4 5 6\n");
+        test_step.dependOn(&run.step);
+    }
+
+    return test_step;
+}
+
+fn testTlsGdToIe(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-gd-to-ie", opts);
+
+    const a_o = addObject(b, "a", opts);
+    addCSourceBytes(a_o,
+        \\#include <stdio.h>
+        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
+        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x2 = 2;
+        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3;
+        \\int foo() {
+        \\  x3 = 3;
+        \\
+        \\  printf("%d %d %d\n", x1, x2, x3);
         \\  return 0;
         \\}
     , &.{});
+    a_o.linkLibC();
+    a_o.force_pic = true;
+
+    const b_o = addObject(b, "b", opts);
+    addCSourceBytes(b_o,
+        \\int foo();
+        \\int main() { foo(); }
+    , &.{});
+    b_o.force_pic = true;
+
+    {
+        const dso = addSharedLibrary(b, "a", opts);
+        dso.addObject(a_o);
+
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(b_o);
+        exe.linkLibrary(dso);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("1 2 3\n");
+        test_step.dependOn(&run.step);
+    }
+
+    {
+        const dso = addSharedLibrary(b, "a", opts);
+        dso.addObject(a_o);
+        // dso.link_relax = false; // TODO
+
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(b_o);
+        exe.linkLibrary(dso);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("1 2 3\n");
+        test_step.dependOn(&run.step);
+    }
+
+    // {
+    //     const dso = addSharedLibrary(b, "a", opts);
+    //     dso.addObject(a_o);
+    //     dso.link_z_nodlopen = true;
+
+    //     const exe = addExecutable(b, "main", opts);
+    //     exe.addObject(b_o);
+    //     exe.linkLibrary(dso);
+
+    //     const run = addRunArtifact(exe);
+    //     run.expectStdOutEqual("1 2 3\n");
+    //     test_step.dependOn(&run.step);
+    // }
+
+    // {
+    //     const dso = addSharedLibrary(b, "a", opts);
+    //     dso.addObject(a_o);
+    //     dso.link_relax = false;
+    //     dso.link_z_nodlopen = true;
+
+    //     const exe = addExecutable(b, "main", opts);
+    //     exe.addObject(b_o);
+    //     exe.linkLibrary(dso);
+
+    //     const run = addRunArtifact(exe);
+    //     run.expectStdOutEqual("1 2 3\n");
+    //     test_step.dependOn(&run.step);
+    // }
+
+    return test_step;
+}
+
+fn testTlsIe(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-ie", opts);
+
+    const dso = addSharedLibrary(b, "a", opts);
+    addCSourceBytes(dso,
+        \\#include <stdio.h>
+        \\__attribute__((tls_model("initial-exec"))) static _Thread_local int foo;
+        \\__attribute__((tls_model("initial-exec"))) static _Thread_local int bar;
+        \\void set() {
+        \\  foo = 3;
+        \\  bar = 5;
+        \\}
+        \\void print() {
+        \\  printf("%d %d ", foo, bar);
+        \\}
+    , &.{});
+    dso.linkLibC();
+
+    const main_o = addObject(b, "main", opts);
+    addCSourceBytes(main_o,
+        \\#include <stdio.h>
+        \\_Thread_local int baz;
+        \\void set();
+        \\void print();
+        \\int main() {
+        \\  baz = 7;
+        \\  print();
+        \\  set();
+        \\  print();
+        \\  printf("%d\n", baz);
+        \\}
+    , &.{});
+    main_o.linkLibC();
+
+    const exp_stdout = "0 0 3 5 7\n";
+
+    {
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(main_o);
+        exe.linkLibrary(dso);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual(exp_stdout);
+        test_step.dependOn(&run.step);
+    }
+
+    {
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(main_o);
+        exe.linkLibrary(dso);
+        exe.linkLibC();
+        // exe.link_relax = false; // TODO
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual(exp_stdout);
+        test_step.dependOn(&run.step);
+    }
+
+    return test_step;
+}
+
+fn testTlsLargeAlignment(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-large-alignment", opts);
+
+    const a_o = addObject(b, "a", opts);
+    addCSourceBytes(a_o,
+        \\__attribute__((section(".tdata1")))
+        \\_Thread_local int x = 42;
+    , &.{"-std=c11"});
+    a_o.force_pic = true;
+
+    const b_o = addObject(b, "b", opts);
+    addCSourceBytes(b_o,
+        \\__attribute__((section(".tdata2")))
+        \\_Alignas(256) _Thread_local int y[] = { 1, 2, 3 };
+    , &.{"-std=c11"});
+    b_o.force_pic = true;
+
+    const c_o = addObject(b, "c", opts);
+    addCSourceBytes(c_o,
+        \\#include <stdio.h>
+        \\extern _Thread_local int x;
+        \\extern _Thread_local int y[];
+        \\int main() {
+        \\  printf("%d %d %d %d\n", x, y[0], y[1], y[2]);
+        \\}
+    , &.{});
+    c_o.force_pic = true;
+    c_o.linkLibC();
+
+    {
+        const dso = addSharedLibrary(b, "a", opts);
+        dso.addObject(a_o);
+        dso.addObject(b_o);
+
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(c_o);
+        exe.linkLibrary(dso);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("42 1 2 3\n");
+        test_step.dependOn(&run.step);
+    }
+
+    {
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(a_o);
+        exe.addObject(b_o);
+        exe.addObject(c_o);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("42 1 2 3\n");
+        test_step.dependOn(&run.step);
+    }
+
+    return test_step;
+}
+
+fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-large-tbss", opts);
+
+    const exe = addExecutable(b, "main", opts);
+    addAsmSourceBytes(exe,
+        \\.globl x, y
+        \\.section .tbss,"awT",@nobits
+        \\x:
+        \\.zero 1024
+        \\.section .tcommon,"awT",@nobits
+        \\y:
+        \\.zero 1024
+    );
+    addCSourceBytes(exe,
+        \\#include <stdio.h>
+        \\extern _Thread_local char x[1024000];
+        \\extern _Thread_local char y[1024000];
+        \\int main() {
+        \\  x[0] = 3;
+        \\  x[1023] = 5;
+        \\  printf("%d %d %d %d %d %d\n", x[0], x[1], x[1023], y[0], y[1], y[1023]);
+        \\}
+    , &.{});
     exe.linkLibC();
 
     const run = addRunArtifact(exe);
-    run.expectStdOutEqual(
-        \\10 0 a
-        \\11 1 b
-        \\
-    );
+    run.expectStdOutEqual("3 0 5 0 0 0\n");
+    test_step.dependOn(&run.step);
+
+    return test_step;
+}
+
+fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-large-static-image", opts);
+
+    const exe = addExecutable(b, "main", opts);
+    addCSourceBytes(exe, "_Thread_local int x[] = { 1, 2, 3, [10000] = 5 };", &.{});
+    addCSourceBytes(exe,
+        \\#include <stdio.h>
+        \\extern _Thread_local int x[];
+        \\int main() {
+        \\  printf("%d %d %d %d %d\n", x[0], x[1], x[2], x[3], x[10000]);
+        \\}
+    , &.{});
+    exe.force_pic = true;
+    exe.linkLibC();
+
+    const run = addRunArtifact(exe);
+    run.expectStdOutEqual("1 2 3 0 5\n");
     test_step.dependOn(&run.step);
 
     return test_step;
 }
 
+fn testTlsLd(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-ld", opts);
+
+    const main_o = addObject(b, "main", opts);
+    addCSourceBytes(main_o,
+        \\#include <stdio.h>
+        \\extern _Thread_local int foo;
+        \\static _Thread_local int bar;
+        \\int *get_foo_addr() { return &foo; }
+        \\int *get_bar_addr() { return &bar; }
+        \\int main() {
+        \\  bar = 5;
+        \\  printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
+        \\  return 0;
+        \\}
+    , &.{"-ftls-model=local-dynamic"});
+    main_o.force_pic = true;
+    main_o.linkLibC();
+
+    const a_o = addObject(b, "a", opts);
+    addCSourceBytes(a_o, "_Thread_local int foo = 3;", &.{"-ftls-model=local-dynamic"});
+    a_o.force_pic = true;
+
+    const exp_stdout = "3 5 3 5\n";
+
+    {
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(main_o);
+        exe.addObject(a_o);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual(exp_stdout);
+        test_step.dependOn(&run.step);
+    }
+
+    {
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(main_o);
+        exe.addObject(a_o);
+        exe.linkLibC();
+        // exe.link_relax = false; // TODO
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual(exp_stdout);
+        test_step.dependOn(&run.step);
+    }
+
+    return test_step;
+}
+
+fn testTlsLdDso(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-ld-dso", opts);
+
+    const dso = addSharedLibrary(b, "a", opts);
+    addCSourceBytes(dso,
+        \\static _Thread_local int def, def1;
+        \\int f0() { return ++def; }
+        \\int f1() { return ++def1 + def; }
+    , &.{"-ftls-model=local-dynamic"});
+
+    const exe = addExecutable(b, "main", opts);
+    addCSourceBytes(exe,
+        \\#include <stdio.h>
+        \\extern int f0();
+        \\extern int f1();
+        \\int main() {
+        \\  int x = f0();
+        \\  int y = f1();
+        \\  printf("%d %d\n", x, y);
+        \\  return 0;
+        \\}
+    , &.{});
+    exe.linkLibrary(dso);
+    exe.linkLibC();
+
+    const run = addRunArtifact(exe);
+    run.expectStdOutEqual("1 2\n");
+    test_step.dependOn(&run.step);
+
+    return test_step;
+}
+
+fn testTlsLdNoPlt(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-ld-no-plt", opts);
+
+    const a_o = addObject(b, "a", opts);
+    addCSourceBytes(a_o,
+        \\#include <stdio.h>
+        \\extern _Thread_local int foo;
+        \\static _Thread_local int bar;
+        \\int *get_foo_addr() { return &foo; }
+        \\int *get_bar_addr() { return &bar; }
+        \\int main() {
+        \\  bar = 5;
+        \\
+        \\  printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
+        \\  return 0;
+        \\}
+    , &.{ "-ftls-model=local-dynamic", "-fno-plt" });
+    a_o.linkLibC();
+    a_o.force_pic = true;
+
+    const b_o = addObject(b, "b", opts);
+    addCSourceBytes(b_o, "_Thread_local int foo = 3;", &.{ "-ftls-model=local-dynamic", "-fno-plt" });
+    b_o.force_pic = true;
+
+    {
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(a_o);
+        exe.addObject(b_o);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("3 5 3 5\n");
+        test_step.dependOn(&run.step);
+    }
+
+    {
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(a_o);
+        exe.addObject(b_o);
+        exe.linkLibC();
+        // exe.link_relax = false; // TODO
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("3 5 3 5\n");
+        test_step.dependOn(&run.step);
+    }
+
+    return test_step;
+}
+
+fn testTlsNoPic(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-no-pic", opts);
+
+    const exe = addExecutable(b, "main", opts);
+    addCSourceBytes(exe,
+        \\#include <stdio.h>
+        \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
+        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar;
+        \\int *get_foo_addr() { return &foo; }
+        \\int *get_bar_addr() { return &bar; }
+        \\int main() {
+        \\  foo = 3;
+        \\  bar = 5;
+        \\
+        \\  printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
+        \\  return 0;
+        \\}
+    , .{});
+    addCSourceBytes(exe,
+        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo;
+    , &.{});
+    exe.force_pic = false;
+    exe.linkLibC();
+
+    const run = addRunArtifact(exe);
+    run.expectStdOutEqual("3 5 3 5\n");
+    test_step.dependOn(&run.step);
+
+    return test_step;
+}
+
+fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-offset-alignment", opts);
+
+    const dso = addSharedLibrary(b, "a", opts);
+    addCSourceBytes(dso,
+        \\#include <assert.h>
+        \\#include <stdlib.h>
+        \\
+        \\// .tdata
+        \\_Thread_local int x = 42;
+        \\// .tbss
+        \\__attribute__ ((aligned(64)))
+        \\_Thread_local int y = 0;
+        \\
+        \\void *verify(void *unused) {
+        \\  assert((unsigned long)(&y) % 64 == 0);
+        \\  return NULL;
+        \\}
+    , &.{});
+    dso.linkLibC();
+
+    const exe = addExecutable(b, "main", opts);
+    addCSourceBytes(exe,
+        \\#include <pthread.h>
+        \\#include <dlfcn.h>
+        \\#include <assert.h>
+        \\void *(*verify)(void *);
+        \\
+        \\int main() {
+        \\  void *handle = dlopen("a.so", RTLD_NOW);
+        \\  assert(handle);
+        \\  *(void**)(&verify) = dlsym(handle, "verify");
+        \\  assert(verify);
+        \\
+        \\  pthread_t thread;
+        \\
+        \\  verify(NULL);
+        \\
+        \\  pthread_create(&thread, NULL, verify, NULL);
+        \\  pthread_join(thread, NULL);
+        \\}
+    , &.{});
+    exe.linkLibrary(dso);
+    exe.linkLibC();
+    exe.linkSystemLibrary2("dl", .{});
+    exe.linkSystemLibrary2("pthread", .{});
+    exe.force_pic = true;
+
+    const run = addRunArtifact(exe);
+    run.expectExitCode(0);
+    test_step.dependOn(&run.step);
+
+    return test_step;
+}
+
+fn testTlsPic(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-pic", opts);
+
+    const obj = addObject(b, "obj", opts);
+    addCSourceBytes(obj,
+        \\#include <stdio.h>
+        \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
+        \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar;
+        \\int *get_foo_addr() { return &foo; }
+        \\int *get_bar_addr() { return &bar; }
+        \\int main() {
+        \\  bar = 5;
+        \\
+        \\  printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
+        \\  return 0;
+        \\}
+    , &.{});
+    obj.linkLibC();
+    obj.force_pic = true;
+
+    const exe = addExecutable(b, "main", opts);
+    addCSourceBytes(exe,
+        \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo = 3;
+    , &.{});
+    exe.addObject(obj);
+    exe.linkLibC();
+
+    const run = addRunArtifact(exe);
+    run.expectStdOutEqual("3 5 3 5\n");
+    test_step.dependOn(&run.step);
+
+    return test_step;
+}
+
+fn testTlsSmallAlignment(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-small-alignment", opts);
+
+    const a_o = addObject(b, "a", opts);
+    addAsmSourceBytes(a_o,
+        \\.text
+        \\.byte 0
+    );
+    a_o.force_pic = true;
+
+    const b_o = addObject(b, "b", opts);
+    addCSourceBytes(b_o, "_Thread_local char x = 42;", &.{"-std=c11"});
+    b_o.force_pic = true;
+
+    const c_o = addObject(b, "c", opts);
+    addCSourceBytes(c_o,
+        \\#include <stdio.h>
+        \\extern _Thread_local char x;
+        \\int main() {
+        \\  printf("%d\n", x);
+        \\}
+    , &.{});
+    c_o.linkLibC();
+    c_o.force_pic = true;
+
+    {
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(a_o);
+        exe.addObject(b_o);
+        exe.addObject(c_o);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("42\n");
+        test_step.dependOn(&run.step);
+    }
+
+    {
+        const dso = addSharedLibrary(b, "a", opts);
+        dso.addObject(a_o);
+        dso.addObject(b_o);
+
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(c_o);
+        exe.linkLibrary(dso);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("42\n");
+        test_step.dependOn(&run.step);
+    }
+
+    return test_step;
+}
+
+fn testTlsStatic(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "tls-static", opts);
+
+    const exe = addExecutable(b, "test", opts);
+    addCSourceBytes(exe,
+        \\#include <stdio.h>
+        \\_Thread_local int a = 10;
+        \\_Thread_local int b;
+        \\_Thread_local char c = 'a';
+        \\int main(int argc, char* argv[]) {
+        \\  printf("%d %d %c\n", a, b, c);
+        \\  a += 1;
+        \\  b += 1;
+        \\  c += 1;
+        \\  printf("%d %d %c\n", a, b, c);
+        \\  return 0;
+        \\}
+    , &.{});
+    exe.linkLibC();
+
+    const run = addRunArtifact(exe);
+    run.expectStdOutEqual(
+        \\10 0 a
+        \\11 1 b
+        \\
+    );
+    test_step.dependOn(&run.step);
+
+    return test_step;
+}
+
+fn testWeakExports(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "weak-exports", opts);
+
+    const obj = addObject(b, "obj", opts);
+    addCSourceBytes(obj,
+        \\#include <stdio.h>
+        \\__attribute__((weak)) int foo();
+        \\int main() {
+        \\  printf("%d\n", foo ? foo() : 3);
+        \\}
+    , &.{});
+    obj.linkLibC();
+    obj.force_pic = true;
+
+    {
+        const dso = addSharedLibrary(b, "a", opts);
+        dso.addObject(obj);
+        dso.linkLibC();
+
+        const check = dso.checkObject();
+        check.checkInDynamicSymtab();
+        check.checkContains("UND NOTYPE WEAK DEFAULT foo");
+        test_step.dependOn(&check.step);
+    }
+
+    {
+        const exe = addExecutable(b, "main", opts);
+        exe.addObject(obj);
+        exe.linkLibC();
+
+        const check = exe.checkObject();
+        check.checkInDynamicSymtab();
+        check.checkNotPresent("UND NOTYPE WEAK DEFAULT foo");
+        test_step.dependOn(&check.step);
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("3\n");
+        test_step.dependOn(&run.step);
+    }
+
+    return test_step;
+}
+
+fn testWeakUndefsDso(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "weak-undef-dso", opts);
+
+    const dso = addSharedLibrary(b, "a", opts);
+    addCSourceBytes(dso,
+        \\__attribute__((weak)) int foo();
+        \\int bar() { return foo ? foo() : -1; }
+    , &.{});
+
+    {
+        const exe = addExecutable(b, "main", opts);
+        addCSourceBytes(exe,
+            \\#include <stdio.h>
+            \\int bar();
+            \\int main() { printf("bar=%d\n", bar()); }
+        , &.{});
+        exe.linkLibrary(dso);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("bar=-1\n");
+        test_step.dependOn(&run.step);
+    }
+
+    {
+        const exe = addExecutable(b, "main", opts);
+        addCSourceBytes(exe,
+            \\#include <stdio.h>
+            \\int foo() { return 5; }
+            \\int bar();
+            \\int main() { printf("bar=%d\n", bar()); }
+        , &.{});
+        exe.linkLibrary(dso);
+        exe.linkLibC();
+
+        const run = addRunArtifact(exe);
+        run.expectStdOutEqual("bar=5\n");
+        test_step.dependOn(&run.step);
+    }
+
+    return test_step;
+}
+
+fn testZNow(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "z-now", opts);
+
+    const obj = addObject(b, "obj", opts);
+    addCSourceBytes(obj, "int main() { return 0; }", &.{});
+    obj.force_pic = true;
+
+    {
+        const dso = addSharedLibrary(b, "a", opts);
+        dso.addObject(obj);
+
+        const check = dso.checkObject();
+        check.checkInDynamicSection();
+        check.checkContains("NOW");
+        test_step.dependOn(&check.step);
+    }
+
+    {
+        const dso = addSharedLibrary(b, "a", opts);
+        dso.addObject(obj);
+        dso.link_z_lazy = true;
+
+        const check = dso.checkObject();
+        check.checkInDynamicSection();
+        check.checkNotPresent("NOW");
+        test_step.dependOn(&check.step);
+    }
+
+    return test_step;
+}
+
+fn testZStackSize(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "z-stack-size", opts);
+
+    const exe = addExecutable(b, "main", opts);
+    addCSourceBytes(exe, "int main() { return 0; }", &.{});
+    exe.stack_size = 0x800000;
+    exe.linkLibC();
+
+    const check = exe.checkObject();
+    check.checkStart();
+    check.checkExact("program headers");
+    check.checkExact("type GNU_STACK");
+    check.checkExact("memsz 800000");
+    test_step.dependOn(&check.step);
+
+    return test_step;
+}
+
+fn testZText(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "z-text", opts);
+
+    // Previously, following mold, this test tested text relocs present in a PIE executable.
+    // However, as we want to cover musl AND glibc, it is now modified to test presence of
+    // text relocs in a DSO which is then linked with an executable.
+    // According to Rich and this thread https://www.openwall.com/lists/musl/2020/09/25/4
+    // musl supports only a very limited number of text relocations and only in DSOs (and
+    // rightly so!).
+
+    const a_o = addObject(b, "a", opts);
+    addAsmSourceBytes(a_o,
+        \\.globl fn1
+        \\fn1:
+        \\  sub $8, %rsp
+        \\  movabs ptr, %rax
+        \\  call *%rax
+        \\  add $8, %rsp
+        \\  ret
+    );
+
+    const b_o = addObject(b, "b", opts);
+    addCSourceBytes(b_o,
+        \\int fn1();
+        \\int fn2() {
+        \\  return 3;
+        \\}
+        \\void *ptr = fn2;
+        \\int fnn() {
+        \\  return fn1();
+        \\}
+    , &.{});
+    b_o.force_pic = true;
+
+    const dso = addSharedLibrary(b, "a", opts);
+    dso.addObject(a_o);
+    dso.addObject(b_o);
+    dso.link_z_notext = true;
+
+    const exe = addExecutable(b, "main", opts);
+    addCSourceBytes(exe,
+        \\#include <stdio.h>
+        \\int fnn();
+        \\int main() {
+        \\  printf("%d\n", fnn());
+        \\}
+    , &.{});
+    exe.linkLibrary(dso);
+    exe.linkLibC();
+
+    const run = addRunArtifact(exe);
+    run.expectStdOutEqual("3\n");
+    test_step.dependOn(&run.step);
+
+    // Check for DT_TEXTREL in a DSO
+    const check = dso.checkObject();
+    check.checkInDynamicSection();
+    // check.checkExact("TEXTREL 0"); // TODO fix in CheckObject parser
+    check.checkContains("FLAGS TEXTREL");
+    test_step.dependOn(&check.step);
+
+    return test_step;
+}
+
 const Options = struct {
     target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux },
     optimize: std.builtin.OptimizeMode = .Debug,