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}