master
  1// A zig test case that exercises some glibc symbols that have uncovered
  2// problems in the past.  This test must be compiled against a glibc.
  3//
  4// The build.zig tests the binary built from this source to see that
  5// symbols are statically or dynamically linked, as expected.
  6
  7const std = @import("std");
  8const builtin = @import("builtin");
  9const assert = std.debug.assert;
 10
 11const c_malloc = @cImport(
 12    @cInclude("malloc.h"), // for reallocarray
 13);
 14
 15const c_stdlib = @cImport(
 16    @cInclude("stdlib.h"), // for atexit
 17);
 18
 19const c_string = @cImport(
 20    @cInclude("string.h"), // for strlcpy
 21);
 22
 23// Version of glibc this test is being built to run against
 24const glibc_ver = builtin.os.versionRange().gnuLibCVersion().?;
 25
 26// PR #17034 - fstat moved between libc_nonshared and libc
 27fn checkStat() !void {
 28    const cwdFd = std.fs.cwd().fd;
 29
 30    var stat = std.mem.zeroes(std.c.Stat);
 31    var result = std.c.fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &stat, 0);
 32    assert(result == -1);
 33    assert(std.posix.errno(result) == .NOENT);
 34
 35    result = std.c.stat("a_file_that_definitely_does_not_exist", &stat);
 36    assert(result == -1);
 37    assert(std.posix.errno(result) == .NOENT);
 38}
 39
 40// PR #17607 - reallocarray not visible in headers
 41fn checkReallocarray() !void {
 42    // reallocarray was introduced in v2.26
 43    if (comptime glibc_ver.order(.{ .major = 2, .minor = 26, .patch = 0 }) == .lt) {
 44        if (@hasDecl(c_malloc, "reallocarray")) {
 45            @compileError("Before v2.26 'malloc.h' does not define 'reallocarray'");
 46        }
 47    } else {
 48        return try checkReallocarray_v2_26();
 49    }
 50}
 51
 52fn checkReallocarray_v2_26() !void {
 53    const size = 16;
 54    const tenX = c_malloc.reallocarray(c_malloc.NULL, 10, size);
 55    const elevenX = c_malloc.reallocarray(tenX, 11, size);
 56
 57    assert(tenX != c_malloc.NULL);
 58    assert(elevenX != c_malloc.NULL);
 59}
 60
 61// getauxval introduced in v2.16
 62fn checkGetAuxVal() !void {
 63    if (comptime glibc_ver.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) {
 64        if (@hasDecl(std.c, "getauxval")) {
 65            @compileError("Before v2.16 glibc does not define 'getauxval'");
 66        }
 67    } else {
 68        try checkGetAuxVal_v2_16();
 69    }
 70}
 71
 72fn checkGetAuxVal_v2_16() !void {
 73    const base = std.c.getauxval(std.elf.AT_BASE);
 74    const pgsz = std.c.getauxval(std.elf.AT_PAGESZ);
 75
 76    assert(base != 0);
 77    assert(pgsz != 0);
 78}
 79
 80// strlcpy introduced in v2.38, which is newer than many installed glibcs
 81fn checkStrlcpy() !void {
 82    if (comptime glibc_ver.order(.{ .major = 2, .minor = 38, .patch = 0 }) == .lt) {
 83        if (@hasDecl(c_string, "strlcpy")) {
 84            @compileError("Before v2.38 glibc does not define 'strlcpy'");
 85        }
 86    } else {
 87        try checkStrlcpy_v2_38();
 88    }
 89}
 90
 91fn checkStrlcpy_v2_38() !void {
 92    var buf: [99]u8 = undefined;
 93    const used = c_string.strlcpy(&buf, "strlcpy works!", buf.len);
 94    assert(used == 14);
 95}
 96
 97// atexit is part of libc_nonshared, so ensure its linked in correctly
 98fn forceExit0Callback() callconv(.c) void {
 99    std.c.exit(0); // Override the main() exit code
100}
101
102fn checkAtExit() !void {
103    const result = c_stdlib.atexit(forceExit0Callback);
104    assert(result == 0);
105}
106
107pub fn main() !u8 {
108    try checkStat();
109    try checkReallocarray();
110    try checkStrlcpy();
111
112    try checkGetAuxVal();
113    try checkAtExit();
114
115    std.c.exit(1); // overridden by atexit() callback
116}