mirror of
https://github.com/ziglang/zig.git
synced 2024-11-15 00:26:57 +00:00
Merge pull request #19007 from rootbeer/glibc-c-test
test/link/glibc_compat: Add C test case for glibc versions
This commit is contained in:
commit
7ae9d8089d
4
lib/libc/glibc/README.md
vendored
4
lib/libc/glibc/README.md
vendored
@ -31,7 +31,9 @@ The GNU C Library supports a very wide set of platforms and architectures.
|
||||
The current Zig support for glibc only includes Linux.
|
||||
|
||||
Zig supports glibc versions back to v2.17 (2012) as the Zig standard
|
||||
library depends on symbols that were introduced in 2.17.
|
||||
library depends on symbols that were introduced in 2.17. When used as a C
|
||||
or C++ compiler (i.e., `zig cc`) zig supports glibc versions back to
|
||||
v2.2.5.
|
||||
|
||||
## Glibc stubs
|
||||
|
||||
|
@ -12,7 +12,7 @@ pub const available_libcs = [_]ArchOsAbi{
|
||||
.{ .arch = .aarch64_be, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } },
|
||||
.{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .aarch64_be, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .aarch64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .aarch64, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } },
|
||||
.{ .arch = .aarch64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .aarch64, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 11, .minor = 0, .patch = 0 } },
|
||||
|
@ -28,7 +28,114 @@ pub fn build(b: *std.Build) void {
|
||||
test_step.dependOn(&exe.step);
|
||||
}
|
||||
|
||||
// Build & run against a sampling of supported glibc versions
|
||||
// Build & run a C test case against a sampling of supported glibc versions
|
||||
for ([_][]const u8{
|
||||
// "native-linux-gnu.2.0", // fails with a pile of missing symbols.
|
||||
"native-linux-gnu.2.2.5",
|
||||
"native-linux-gnu.2.4",
|
||||
"native-linux-gnu.2.12",
|
||||
"native-linux-gnu.2.16",
|
||||
"native-linux-gnu.2.22",
|
||||
"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;
|
||||
|
||||
// only build test if glibc version supports the architecture
|
||||
if (target.result.cpu.arch.isAARCH64()) {
|
||||
if (glibc_ver.order(.{ .major = 2, .minor = 17, .patch = 0 }) == .lt) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = t,
|
||||
.target = target,
|
||||
});
|
||||
exe.addCSourceFile(.{ .file = b.path("glibc_runtime_check.c") });
|
||||
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 fstat redirects through __fxstat, afterwards its a
|
||||
// normal dynamic symbol
|
||||
check.checkInDynamicSymtab();
|
||||
if (glibc_ver.order(.{ .major = 2, .minor = 32, .patch = 0 }) == .lt) {
|
||||
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __fxstat");
|
||||
|
||||
check.checkInSymtab();
|
||||
check.checkContains("FUNC LOCAL HIDDEN fstat");
|
||||
} else {
|
||||
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT fstat");
|
||||
|
||||
check.checkInSymtab();
|
||||
check.checkNotPresent("__fxstat");
|
||||
}
|
||||
|
||||
// before v2.26 reallocarray is not supported
|
||||
check.checkInDynamicSymtab();
|
||||
if (glibc_ver.order(.{ .major = 2, .minor = 26, .patch = 0 }) == .lt) {
|
||||
check.checkNotPresent("reallocarray");
|
||||
} else {
|
||||
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT reallocarray");
|
||||
}
|
||||
|
||||
// before v2.38 strlcpy is not supported
|
||||
check.checkInDynamicSymtab();
|
||||
if (glibc_ver.order(.{ .major = 2, .minor = 38, .patch = 0 }) == .lt) {
|
||||
check.checkNotPresent("strlcpy");
|
||||
} else {
|
||||
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT strlcpy");
|
||||
}
|
||||
|
||||
// v2.16 introduced getauxval()
|
||||
check.checkInDynamicSymtab();
|
||||
if (glibc_ver.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) {
|
||||
check.checkNotPresent("getauxval");
|
||||
} else {
|
||||
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT getauxval");
|
||||
}
|
||||
|
||||
// Always have dynamic "exit", "pow", and "powf" references
|
||||
check.checkInDynamicSymtab();
|
||||
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT exit");
|
||||
check.checkInDynamicSymtab();
|
||||
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT pow");
|
||||
check.checkInDynamicSymtab();
|
||||
check.checkExact("0 0 UND FUNC GLOBAL DEFAULT powf");
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Build & run a Zig test case 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",
|
||||
|
114
test/link/glibc_compat/glibc_runtime_check.c
Normal file
114
test/link/glibc_compat/glibc_runtime_check.c
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Exercise complicating glibc symbols from C code. Complicating symbols
|
||||
* are ones that have moved between glibc versions, or use floating point
|
||||
* parameters, or have otherwise tripped up the Zig glibc compatibility
|
||||
* code.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <features.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* errno is compilcated (thread-local, dynamically provided, etc). */
|
||||
static void check_errno()
|
||||
{
|
||||
int invalid_fd = open("/doesnotexist", O_RDONLY);
|
||||
assert(invalid_fd == -1);
|
||||
assert(errno == ENOENT);
|
||||
}
|
||||
|
||||
/* fstat has moved around in glibc (between libc_nonshared and libc) */
|
||||
static void check_fstat()
|
||||
{
|
||||
int self_fd = open("/proc/self/exe", O_RDONLY);
|
||||
|
||||
struct stat statbuf = {0};
|
||||
int rc = fstat(self_fd, &statbuf);
|
||||
|
||||
assert(rc == 0);
|
||||
|
||||
assert(statbuf.st_dev != 0);
|
||||
assert(statbuf.st_ino != 0);
|
||||
assert(statbuf.st_mode != 0);
|
||||
assert(statbuf.st_size > 0);
|
||||
assert(statbuf.st_blocks > 0);
|
||||
assert(statbuf.st_ctim.tv_sec > 0);
|
||||
|
||||
close(self_fd);
|
||||
}
|
||||
|
||||
/* Some targets have a complicated ABI for floats and doubles */
|
||||
static void check_fp_abi()
|
||||
{
|
||||
// Picked "pow" as it takes and returns doubles
|
||||
assert(pow(10.0, 10.0) == 10000000000.0);
|
||||
assert(powf(10.0f, 10.0f) == 10000000000.0f);
|
||||
}
|
||||
|
||||
/* strlcpy introduced in glibc 2.38 */
|
||||
static void check_strlcpy()
|
||||
{
|
||||
#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || (__GLIBC__ > 2)
|
||||
char target[4] = {0};
|
||||
strlcpy(target, "this is a source string", 4);
|
||||
|
||||
assert(strcmp(target, "thi") == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* reallocarray introduced in glibc 2.26 */
|
||||
static void check_reallocarray()
|
||||
{
|
||||
#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 26) || (__GLIBC__ > 2)
|
||||
const size_t el_size = 32;
|
||||
void* base = reallocarray(NULL, 10, el_size);
|
||||
void* grown = reallocarray(base, 100, el_size);
|
||||
|
||||
assert(base != NULL);
|
||||
assert(grown != NULL);
|
||||
|
||||
free(grown);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* getauxval introduced in glibc 2.16 */
|
||||
static void check_getauxval()
|
||||
{
|
||||
#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16) || (__GLIBC__ > 2)
|
||||
int pgsz = getauxval(AT_PAGESZ);
|
||||
assert(pgsz >= 4*1024);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* atexit() is part of libc_nonshared */
|
||||
static void force_exit_0()
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void check_atexit()
|
||||
{
|
||||
int rc = atexit(force_exit_0);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int rc;
|
||||
|
||||
check_errno();
|
||||
check_fstat();
|
||||
check_fp_abi();
|
||||
check_strlcpy();
|
||||
check_reallocarray();
|
||||
check_getauxval();
|
||||
check_atexit();
|
||||
|
||||
exit(99); // exit code overridden by atexit handler
|
||||
}
|
Loading…
Reference in New Issue
Block a user