mirror of
https://github.com/ziglang/zig.git
synced 2025-12-10 00:03:10 +00:00
test/link/glibc_compat: test various older glibc versions
Compile, link and run a test case against various glibc versions. Exercise symbols that have been probelmatic in the past.
This commit is contained in:
parent
1564cb0ab9
commit
42d7b69d81
@ -1,4 +1,14 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
// To run executables linked against a specific glibc version, the
|
||||||
|
// run-time glibc version needs to be new enough. Check the host's glibc
|
||||||
|
// version. Note that this does not allow for translation/vm/emulation
|
||||||
|
// services to run these tests.
|
||||||
|
const running_glibc_ver: ?std.SemanticVersion = switch (builtin.os.tag) {
|
||||||
|
.linux => builtin.os.version_range.linux.glibc,
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
const test_step = b.step("test", "Test");
|
const test_step = b.step("test", "Test");
|
||||||
@ -17,4 +27,89 @@ pub fn build(b: *std.Build) void {
|
|||||||
_ = exe.getEmittedBin();
|
_ = exe.getEmittedBin();
|
||||||
test_step.dependOn(&exe.step);
|
test_step.dependOn(&exe.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build & run against a sampling of supported glibc versions
|
||||||
|
for ([_][]const u8{
|
||||||
|
"native-linux-gnu.2.17", // Currently oldest supported, see #17769
|
||||||
|
"native-linux-gnu.2.23",
|
||||||
|
"native-linux-gnu.2.28",
|
||||||
|
"native-linux-gnu.2.33",
|
||||||
|
"native-linux-gnu.2.38",
|
||||||
|
"native-linux-gnu",
|
||||||
|
}) |t| {
|
||||||
|
const target = b.resolveTargetQuery(std.Target.Query.parse(
|
||||||
|
.{ .arch_os_abi = t },
|
||||||
|
) catch unreachable);
|
||||||
|
|
||||||
|
const glibc_ver = target.result.os.version_range.linux.glibc;
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = t,
|
||||||
|
.root_source_file = .{ .path = "glibc_runtime_check.zig" },
|
||||||
|
.target = target,
|
||||||
|
});
|
||||||
|
exe.linkLibC();
|
||||||
|
|
||||||
|
// Only try running the test if the host glibc is known to be good enough. Ideally, the Zig
|
||||||
|
// test runner would be able to check this, but see https://github.com/ziglang/zig/pull/17702#issuecomment-1831310453
|
||||||
|
if (running_glibc_ver) |running_ver| {
|
||||||
|
if (glibc_ver.order(running_ver) == .lt) {
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
run_cmd.skip_foreign_checks = true;
|
||||||
|
run_cmd.expectExitCode(0);
|
||||||
|
|
||||||
|
test_step.dependOn(&run_cmd.step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const check = exe.checkObject();
|
||||||
|
|
||||||
|
// __errno_location is always a dynamically linked symbol
|
||||||
|
check.checkInDynamicSymtab();
|
||||||
|
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __errno_location");
|
||||||
|
|
||||||
|
// before v2.32 fstatat redirects through __fxstatat, afterwards its a
|
||||||
|
// normal dynamic symbol
|
||||||
|
if (glibc_ver.order(.{ .major = 2, .minor = 32, .patch = 0 }) == .lt) {
|
||||||
|
check.checkInDynamicSymtab();
|
||||||
|
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __fxstatat");
|
||||||
|
|
||||||
|
check.checkInSymtab();
|
||||||
|
check.checkContains("FUNC LOCAL HIDDEN fstatat");
|
||||||
|
} else {
|
||||||
|
check.checkInDynamicSymtab();
|
||||||
|
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT fstatat");
|
||||||
|
|
||||||
|
check.checkInSymtab();
|
||||||
|
check.checkNotPresent("FUNC LOCAL HIDDEN fstatat");
|
||||||
|
}
|
||||||
|
|
||||||
|
// before v2.26 reallocarray is not supported
|
||||||
|
if (glibc_ver.order(.{ .major = 2, .minor = 26, .patch = 0 }) == .lt) {
|
||||||
|
check.checkInDynamicSymtab();
|
||||||
|
check.checkNotPresent("reallocarray");
|
||||||
|
} else {
|
||||||
|
check.checkInDynamicSymtab();
|
||||||
|
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT reallocarray");
|
||||||
|
} else {
|
||||||
|
check.checkInDynamicSymtab();
|
||||||
|
check.checkNotPresent("reallocarray");
|
||||||
|
}
|
||||||
|
|
||||||
|
// v2.16 introduced getauxval(), so always present
|
||||||
|
check.checkInDynamicSymtab();
|
||||||
|
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT getauxval");
|
||||||
|
|
||||||
|
// Always have a dynamic "exit" reference
|
||||||
|
check.checkInDynamicSymtab();
|
||||||
|
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT exit");
|
||||||
|
|
||||||
|
// An atexit local symbol is defined, and depends on undefined dynamic
|
||||||
|
// __cxa_atexit.
|
||||||
|
check.checkInSymtab();
|
||||||
|
check.checkContains("FUNC LOCAL HIDDEN atexit");
|
||||||
|
check.checkInDynamicSymtab();
|
||||||
|
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __cxa_atexit");
|
||||||
|
|
||||||
|
test_step.dependOn(&check.step);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
94
test/link/glibc_compat/glibc_runtime_check.zig
Normal file
94
test/link/glibc_compat/glibc_runtime_check.zig
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// A zig test case that exercises some glibc symbols that have uncovered
|
||||||
|
// problems in the past. This test must be compiled against a glibc.
|
||||||
|
//
|
||||||
|
// The build.zig tests the binary built from this source to see that
|
||||||
|
// symbols are statically or dynamically linked, as expected.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
|
const c_malloc = @cImport(
|
||||||
|
@cInclude("malloc.h"), // for reallocarray
|
||||||
|
);
|
||||||
|
|
||||||
|
const c_stdlib = @cImport(
|
||||||
|
@cInclude("stdlib.h"), // for atexit
|
||||||
|
);
|
||||||
|
|
||||||
|
// Version of glibc this test is being built to run against
|
||||||
|
const glibc_ver = builtin.target.os.version_range.linux.glibc;
|
||||||
|
|
||||||
|
// PR #17034 - fstat moved between libc_nonshared and libc
|
||||||
|
fn checkStat() !void {
|
||||||
|
const cwdFd = std.fs.cwd().fd;
|
||||||
|
|
||||||
|
var stat = std.mem.zeroes(std.c.Stat);
|
||||||
|
var result = std.c.fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &stat, 0);
|
||||||
|
assert(result == -1);
|
||||||
|
assert(std.c.getErrno(result) == .NOENT);
|
||||||
|
|
||||||
|
result = std.c.stat("a_file_that_definitely_does_not_exist", &stat);
|
||||||
|
assert(result == -1);
|
||||||
|
assert(std.c.getErrno(result) == .NOENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PR #17607 - reallocarray not visible in headers
|
||||||
|
fn checkReallocarray() !void {
|
||||||
|
// reallocarray was introduced in v2.26
|
||||||
|
if (comptime glibc_ver.order(.{ .major = 2, .minor = 26, .patch = 0 }) == .lt) {
|
||||||
|
if (@hasDecl(c_malloc, "reallocarray")) {
|
||||||
|
@compileError("Before v2.26 'malloc.h' does not define 'reallocarray'");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return try checkReallocarray_v2_26();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn checkReallocarray_v2_26() !void {
|
||||||
|
const size = 16;
|
||||||
|
const tenX = c_malloc.reallocarray(c_malloc.NULL, 10, size);
|
||||||
|
const elevenX = c_malloc.reallocarray(tenX, 11, size);
|
||||||
|
|
||||||
|
assert(tenX != c_malloc.NULL);
|
||||||
|
assert(elevenX != c_malloc.NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// getauxval introduced in v2.16
|
||||||
|
fn checkGetAuxVal() !void {
|
||||||
|
if (comptime glibc_ver.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) {
|
||||||
|
if (@hasDecl(std.c, "getauxval")) {
|
||||||
|
@compileError("Before v2.16 glibc does not define 'getauxval'");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try checkGetAuxVal_v2_16();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn checkGetAuxVal_v2_16() !void {
|
||||||
|
const base = std.c.getauxval(std.elf.AT_BASE);
|
||||||
|
const pgsz = std.c.getauxval(std.elf.AT_PAGESZ);
|
||||||
|
|
||||||
|
assert(base != 0);
|
||||||
|
assert(pgsz != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// atexit is part of libc_nonshared, so ensure its linked in correctly
|
||||||
|
fn forceExit0Callback() callconv(.C) void {
|
||||||
|
std.c.exit(0); // Override the main() exit code
|
||||||
|
}
|
||||||
|
|
||||||
|
fn checkAtExit() !void {
|
||||||
|
const result = c_stdlib.atexit(forceExit0Callback);
|
||||||
|
assert(result == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() !u8 {
|
||||||
|
try checkStat();
|
||||||
|
try checkReallocarray();
|
||||||
|
|
||||||
|
try checkGetAuxVal();
|
||||||
|
try checkAtExit();
|
||||||
|
|
||||||
|
std.c.exit(1); // overridden by atexit() callback
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user