| ... | @@ -1,4 +1,14 @@ | ... | @@ -1,4 +1,14 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| | 2 | const builtin = @import("builtin"); |
| | 3 | |
| | 4 | // To run executables linked against a specific glibc version, the |
| | 5 | // run-time glibc version needs to be new enough. Check the host's glibc |
| | 6 | // version. Note that this does not allow for translation/vm/emulation |
| | 7 | // services to run these tests. |
| | 8 | const running_glibc_ver: ?std.SemanticVersion = switch (builtin.os.tag) { |
| | 9 | .linux => builtin.os.version_range.linux.glibc, |
| | 10 | else => null, |
| | 11 | }; |
| 2 | | 12 | |
| 3 | pub fn build(b: *std.Build) void { | 13 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 14 | const test_step = b.step("test", "Test"); |
| ... | @@ -17,4 +27,89 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,4 +27,89 @@ pub fn build(b: *std.Build) void { |
| 17 | _ = exe.getEmittedBin(); | 27 | _ = exe.getEmittedBin(); |
| 18 | test_step.dependOn(&exe.step); | 28 | test_step.dependOn(&exe.step); |
| 19 | } | 29 | } |
| | 30 | |
| | 31 | // Build & run against a sampling of supported glibc versions |
| | 32 | for ([_][]const u8{ |
| | 33 | "native-linux-gnu.2.17", // Currently oldest supported, see #17769 |
| | 34 | "native-linux-gnu.2.23", |
| | 35 | "native-linux-gnu.2.28", |
| | 36 | "native-linux-gnu.2.33", |
| | 37 | "native-linux-gnu.2.38", |
| | 38 | "native-linux-gnu", |
| | 39 | }) |t| { |
| | 40 | const target = b.resolveTargetQuery(std.Target.Query.parse( |
| | 41 | .{ .arch_os_abi = t }, |
| | 42 | ) catch unreachable); |
| | 43 | |
| | 44 | const glibc_ver = target.result.os.version_range.linux.glibc; |
| | 45 | |
| | 46 | const exe = b.addExecutable(.{ |
| | 47 | .name = t, |
| | 48 | .root_source_file = .{ .path = "glibc_runtime_check.zig" }, |
| | 49 | .target = target, |
| | 50 | }); |
| | 51 | exe.linkLibC(); |
| | 52 | |
| | 53 | // Only try running the test if the host glibc is known to be good enough. Ideally, the Zig |
| | 54 | // test runner would be able to check this, but see https://github.com/ziglang/zig/pull/17702#issuecomment-1831310453 |
| | 55 | if (running_glibc_ver) |running_ver| { |
| | 56 | if (glibc_ver.order(running_ver) == .lt) { |
| | 57 | const run_cmd = b.addRunArtifact(exe); |
| | 58 | run_cmd.skip_foreign_checks = true; |
| | 59 | run_cmd.expectExitCode(0); |
| | 60 | |
| | 61 | test_step.dependOn(&run_cmd.step); |
| | 62 | } |
| | 63 | } |
| | 64 | const check = exe.checkObject(); |
| | 65 | |
| | 66 | // __errno_location is always a dynamically linked symbol |
| | 67 | check.checkInDynamicSymtab(); |
| | 68 | check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __errno_location"); |
| | 69 | |
| | 70 | // before v2.32 fstatat redirects through __fxstatat, afterwards its a |
| | 71 | // normal dynamic symbol |
| | 72 | if (glibc_ver.order(.{ .major = 2, .minor = 32, .patch = 0 }) == .lt) { |
| | 73 | check.checkInDynamicSymtab(); |
| | 74 | check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __fxstatat"); |
| | 75 | |
| | 76 | check.checkInSymtab(); |
| | 77 | check.checkContains("FUNC LOCAL HIDDEN fstatat"); |
| | 78 | } else { |
| | 79 | check.checkInDynamicSymtab(); |
| | 80 | check.checkExact("0 0 UND FUNC GLOBAL DEFAULT fstatat"); |
| | 81 | |
| | 82 | check.checkInSymtab(); |
| | 83 | check.checkNotPresent("FUNC LOCAL HIDDEN fstatat"); |
| | 84 | } |
| | 85 | |
| | 86 | // before v2.26 reallocarray is not supported |
| | 87 | if (glibc_ver.order(.{ .major = 2, .minor = 26, .patch = 0 }) == .lt) { |
| | 88 | check.checkInDynamicSymtab(); |
| | 89 | check.checkNotPresent("reallocarray"); |
| | 90 | } else { |
| | 91 | check.checkInDynamicSymtab(); |
| | 92 | check.checkExact("0 0 UND FUNC GLOBAL DEFAULT reallocarray"); |
| | 93 | } else { |
| | 94 | check.checkInDynamicSymtab(); |
| | 95 | check.checkNotPresent("reallocarray"); |
| | 96 | } |
| | 97 | |
| | 98 | // v2.16 introduced getauxval(), so always present |
| | 99 | check.checkInDynamicSymtab(); |
| | 100 | check.checkExact("0 0 UND FUNC GLOBAL DEFAULT getauxval"); |
| | 101 | |
| | 102 | // Always have a dynamic "exit" reference |
| | 103 | check.checkInDynamicSymtab(); |
| | 104 | check.checkExact("0 0 UND FUNC GLOBAL DEFAULT exit"); |
| | 105 | |
| | 106 | // An atexit local symbol is defined, and depends on undefined dynamic |
| | 107 | // __cxa_atexit. |
| | 108 | check.checkInSymtab(); |
| | 109 | check.checkContains("FUNC LOCAL HIDDEN atexit"); |
| | 110 | check.checkInDynamicSymtab(); |
| | 111 | check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __cxa_atexit"); |
| | 112 | |
| | 113 | test_step.dependOn(&check.step); |
| | 114 | } |
| 20 | } | 115 | } |