1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
// 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
);
const c_string = @cImport(
@cInclude("string.h"), // for strlcpy
);
// Version of glibc this test is being built to run against
const glibc_ver = builtin.os.versionRange().gnuLibCVersion().?;
// 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.posix.errno(result) == .NOENT);
result = std.c.stat("a_file_that_definitely_does_not_exist", &stat);
assert(result == -1);
assert(std.posix.errno(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);
}
// strlcpy introduced in v2.38, which is newer than many installed glibcs
fn checkStrlcpy() !void {
if (comptime glibc_ver.order(.{ .major = 2, .minor = 38, .patch = 0 }) == .lt) {
if (@hasDecl(c_string, "strlcpy")) {
@compileError("Before v2.38 glibc does not define 'strlcpy'");
}
} else {
try checkStrlcpy_v2_38();
}
}
fn checkStrlcpy_v2_38() !void {
var buf: [99]u8 = undefined;
const used = c_string.strlcpy(&buf, "strlcpy works!", buf.len);
assert(used == 14);
}
// 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 checkStrlcpy();
try checkGetAuxVal();
try checkAtExit();
std.c.exit(1); // overridden by atexit() callback
}
|