Commit 9b3b7aa911
test/src/Libc.zig
@@ -0,0 +1,118 @@
+b: *std.Build,
+options: Options,
+root_step: *std.Build.Step,
+
+libc_test_src_path: std.Build.LazyPath,
+
+test_cases: std.ArrayList(TestCase) = .empty,
+
+pub const Options = struct {
+ optimize_modes: []const std.builtin.OptimizeMode,
+ test_filters: []const []const u8,
+ test_target_filters: []const []const u8,
+};
+
+const TestCase = struct {
+ name: []const u8,
+ src_file: std.Build.LazyPath,
+ additional_src_file: ?std.Build.LazyPath,
+ supports_wasi_libc: bool,
+};
+
+pub const LibcTestCaseOption = struct {
+ additional_src_file: ?[]const u8 = null,
+};
+
+pub fn addLibcTestCase(
+ libc: *Libc,
+ path: []const u8,
+ supports_wasi_libc: bool,
+ options: LibcTestCaseOption,
+) void {
+ const name = libc.b.dupe(path[0 .. path.len - std.fs.path.extension(path).len]);
+ std.mem.replaceScalar(u8, name, '/', '.');
+ libc.test_cases.append(libc.b.allocator, .{
+ .name = name,
+ .src_file = libc.libc_test_src_path.path(libc.b, path),
+ .additional_src_file = if (options.additional_src_file) |additional_src_file| libc.libc_test_src_path.path(libc.b, additional_src_file) else null,
+ .supports_wasi_libc = supports_wasi_libc,
+ }) catch @panic("OOM");
+}
+
+pub fn addTarget(libc: *const Libc, target: std.Build.ResolvedTarget) void {
+ if (libc.options.test_target_filters.len > 0) {
+ const triple_txt = target.query.zigTriple(libc.b.allocator) catch @panic("OOM");
+ for (libc.options.test_target_filters) |filter| {
+ if (std.mem.indexOf(u8, triple_txt, filter)) |_| break;
+ } else return;
+ }
+
+ const common = libc.libc_test_src_path.path(libc.b, "common");
+
+ for (libc.options.optimize_modes) |optimize| {
+ const libtest_mod = libc.b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ .link_libc = true,
+ });
+
+ var libtest_c_source_files: []const []const u8 = &.{ "print.c", "rand.c", "setrlim.c", "memfill.c", "vmfill.c", "fdfill.c", "utf8.c" };
+ libtest_mod.addCSourceFiles(.{
+ .root = common,
+ .files = libtest_c_source_files[0..if (target.result.isMuslLibC()) 7 else 2],
+ .flags = &.{"-fno-builtin"},
+ });
+
+ const libtest = libc.b.addLibrary(.{
+ .name = "test",
+ .root_module = libtest_mod,
+ });
+
+ for (libc.test_cases.items) |*test_case| {
+ if (target.result.isWasiLibC() and !test_case.supports_wasi_libc)
+ continue;
+
+ const annotated_case_name = libc.b.fmt("run libc-test {s} ({t})", .{ test_case.name, optimize });
+ for (libc.options.test_filters) |test_filter| {
+ if (std.mem.indexOf(u8, annotated_case_name, test_filter)) |_| break;
+ } else if (libc.options.test_filters.len > 0) continue;
+
+ const mod = libc.b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ .link_libc = true,
+ });
+ mod.addIncludePath(common);
+ if (target.result.isWasiLibC())
+ mod.addCMacro("_WASI_EMULATED_SIGNAL", "");
+ mod.addCSourceFile(.{
+ .file = test_case.src_file,
+ .flags = &.{"-fno-builtin"},
+ });
+ if (test_case.additional_src_file) |additional_src_file| {
+ mod.addCSourceFile(.{
+ .file = additional_src_file,
+ .flags = &.{"-fno-builtin"},
+ });
+ }
+ mod.linkLibrary(libtest);
+
+ const exe = libc.b.addExecutable(.{
+ .name = test_case.name,
+ .root_module = mod,
+ });
+
+ const run = libc.b.addRunArtifact(exe);
+ run.setName(annotated_case_name);
+ run.skip_foreign_checks = true;
+ run.expectStdErrEqual("");
+ run.expectStdOutEqual("");
+ run.expectExitCode(0);
+
+ libc.root_step.dependOn(&run.step);
+ }
+ }
+}
+
+const Libc = @This();
+const std = @import("std");
test/libc.zig
@@ -0,0 +1,143 @@
+pub fn addCases(cases: *tests.LibcContext) void {
+ cases.addLibcTestCase("api/main.c", true, .{});
+
+ cases.addLibcTestCase("functional/argv.c", true, .{});
+ cases.addLibcTestCase("functional/basename.c", true, .{});
+ cases.addLibcTestCase("functional/clocale_mbfuncs.c", true, .{});
+ cases.addLibcTestCase("functional/clock_gettime.c", true, .{});
+ cases.addLibcTestCase("functional/crypt.c", true, .{});
+ cases.addLibcTestCase("functional/dirname.c", true, .{});
+ cases.addLibcTestCase("functional/env.c", true, .{});
+ cases.addLibcTestCase("functional/fcntl.c", false, .{});
+ cases.addLibcTestCase("functional/fdopen.c", false, .{});
+ cases.addLibcTestCase("functional/fnmatch.c", true, .{});
+ cases.addLibcTestCase("functional/fscanf.c", false, .{});
+ cases.addLibcTestCase("functional/fwscanf.c", false, .{});
+ cases.addLibcTestCase("functional/iconv_open.c", true, .{});
+ cases.addLibcTestCase("functional/inet_pton.c", false, .{});
+ // "functional/ipc_msg.c": Probably a bug in qemu
+ // "functional/ipc_sem.c": Probably a bug in qemu
+ // "functional/ipc_shm.c": Probably a bug in qemu
+ cases.addLibcTestCase("functional/mbc.c", true, .{});
+ cases.addLibcTestCase("functional/memstream.c", true, .{});
+ // "functional/mntent.c": https://www.openwall.com/lists/musl/2024/10/22/1
+ cases.addLibcTestCase("functional/popen.c", false, .{});
+ cases.addLibcTestCase("functional/pthread_cancel-points.c", false, .{});
+ cases.addLibcTestCase("functional/pthread_cancel.c", false, .{});
+ cases.addLibcTestCase("functional/pthread_cond.c", false, .{});
+ cases.addLibcTestCase("functional/pthread_mutex.c", false, .{});
+ // "functional/pthread_mutex_pi.c": Probably a bug in qemu (big/little endian FUTEX_LOCK_PI)
+ // "functional/pthread_robust.c": https://gitlab.com/qemu-project/qemu/-/issues/2424
+ cases.addLibcTestCase("functional/pthread_tsd.c", false, .{});
+ cases.addLibcTestCase("functional/qsort.c", true, .{});
+ cases.addLibcTestCase("functional/random.c", true, .{});
+ cases.addLibcTestCase("functional/search_hsearch.c", false, .{}); // The test suite of wasi-libc runs this test case
+ cases.addLibcTestCase("functional/search_insque.c", true, .{});
+ cases.addLibcTestCase("functional/search_lsearch.c", true, .{});
+ cases.addLibcTestCase("functional/search_tsearch.c", true, .{});
+ cases.addLibcTestCase("functional/sem_init.c", false, .{});
+ cases.addLibcTestCase("functional/sem_open.c", false, .{});
+ cases.addLibcTestCase("functional/setjmp.c", false, .{});
+ cases.addLibcTestCase("functional/snprintf.c", true, .{});
+ cases.addLibcTestCase("functional/socket.c", false, .{});
+ cases.addLibcTestCase("functional/spawn.c", false, .{});
+ cases.addLibcTestCase("functional/sscanf.c", true, .{});
+ cases.addLibcTestCase("functional/sscanf_long.c", false, .{});
+ cases.addLibcTestCase("functional/stat.c", false, .{});
+ cases.addLibcTestCase("functional/strftime.c", true, .{});
+ cases.addLibcTestCase("functional/string.c", true, .{});
+ cases.addLibcTestCase("functional/string_memcpy.c", true, .{});
+ cases.addLibcTestCase("functional/string_memmem.c", true, .{});
+ cases.addLibcTestCase("functional/string_memset.c", true, .{});
+ cases.addLibcTestCase("functional/string_strchr.c", true, .{});
+ cases.addLibcTestCase("functional/string_strcspn.c", true, .{});
+ cases.addLibcTestCase("functional/string_strstr.c", true, .{});
+ cases.addLibcTestCase("functional/strtod.c", true, .{});
+ cases.addLibcTestCase("functional/strtod_long.c", true, .{});
+ cases.addLibcTestCase("functional/strtod_simple.c", true, .{});
+ cases.addLibcTestCase("functional/strtof.c", true, .{});
+ cases.addLibcTestCase("functional/strtol.c", true, .{});
+ cases.addLibcTestCase("functional/strtold.c", true, .{});
+ cases.addLibcTestCase("functional/swprintf.c", true, .{});
+ cases.addLibcTestCase("functional/tgmath.c", true, .{});
+ cases.addLibcTestCase("functional/time.c", false, .{});
+ cases.addLibcTestCase("functional/tls_align.c", true, .{ .additional_src_file = "functional/tls_align_dso.c" });
+ cases.addLibcTestCase("functional/tls_init.c", false, .{});
+ cases.addLibcTestCase("functional/tls_local_exec.c", false, .{});
+ cases.addLibcTestCase("functional/udiv.c", true, .{});
+ cases.addLibcTestCase("functional/ungetc.c", false, .{});
+ cases.addLibcTestCase("functional/utime.c", false, .{});
+ cases.addLibcTestCase("functional/vfork.c", false, .{});
+ cases.addLibcTestCase("functional/wcsstr.c", true, .{});
+ cases.addLibcTestCase("functional/wcstol.c", true, .{});
+
+ cases.addLibcTestCase("regression/daemon-failure.c", false, .{});
+ cases.addLibcTestCase("regression/dn_expand-empty.c", false, .{});
+ cases.addLibcTestCase("regression/dn_expand-ptr-0.c", false, .{});
+ cases.addLibcTestCase("regression/execle-env.c", false, .{});
+ cases.addLibcTestCase("regression/fflush-exit.c", false, .{});
+ cases.addLibcTestCase("regression/fgets-eof.c", true, .{});
+ cases.addLibcTestCase("regression/fgetwc-buffering.c", false, .{});
+ cases.addLibcTestCase("regression/flockfile-list.c", false, .{});
+ cases.addLibcTestCase("regression/fpclassify-invalid-ld80.c", true, .{});
+ cases.addLibcTestCase("regression/ftello-unflushed-append.c", false, .{});
+ cases.addLibcTestCase("regression/getpwnam_r-crash.c", false, .{});
+ cases.addLibcTestCase("regression/getpwnam_r-errno.c", false, .{});
+ cases.addLibcTestCase("regression/iconv-roundtrips.c", true, .{});
+ cases.addLibcTestCase("regression/inet_ntop-v4mapped.c", true, .{});
+ cases.addLibcTestCase("regression/inet_pton-empty-last-field.c", true, .{});
+ cases.addLibcTestCase("regression/iswspace-null.c", true, .{});
+ cases.addLibcTestCase("regression/lrand48-signextend.c", true, .{});
+ cases.addLibcTestCase("regression/lseek-large.c", false, .{});
+ cases.addLibcTestCase("regression/malloc-0.c", true, .{});
+ // "regression/malloc-brk-fail.c": QEMU OOM
+ cases.addLibcTestCase("regression/malloc-oom.c", false, .{}); // wasi-libc: requires t_memfill
+ cases.addLibcTestCase("regression/mbsrtowcs-overflow.c", true, .{});
+ cases.addLibcTestCase("regression/memmem-oob-read.c", true, .{});
+ cases.addLibcTestCase("regression/memmem-oob.c", true, .{});
+ cases.addLibcTestCase("regression/mkdtemp-failure.c", false, .{});
+ cases.addLibcTestCase("regression/mkstemp-failure.c", false, .{});
+ cases.addLibcTestCase("regression/printf-1e9-oob.c", true, .{});
+ cases.addLibcTestCase("regression/printf-fmt-g-round.c", true, .{});
+ cases.addLibcTestCase("regression/printf-fmt-g-zeros.c", true, .{});
+ cases.addLibcTestCase("regression/printf-fmt-n.c", true, .{});
+ // "regression/pthread-robust-detach.c": https://gitlab.com/qemu-project/qemu/-/issues/2424
+ cases.addLibcTestCase("regression/pthread_atfork-errno-clobber.c", false, .{});
+ cases.addLibcTestCase("regression/pthread_cancel-sem_wait.c", false, .{});
+ cases.addLibcTestCase("regression/pthread_cond-smasher.c", false, .{});
+ cases.addLibcTestCase("regression/pthread_cond_wait-cancel_ignored.c", false, .{});
+ cases.addLibcTestCase("regression/pthread_condattr_setclock.c", false, .{});
+ // "regression/pthread_create-oom.c": QEMU OOM
+ cases.addLibcTestCase("regression/pthread_exit-cancel.c", false, .{});
+ cases.addLibcTestCase("regression/pthread_exit-dtor.c", false, .{});
+ cases.addLibcTestCase("regression/pthread_once-deadlock.c", false, .{});
+ cases.addLibcTestCase("regression/pthread_rwlock-ebusy.c", false, .{});
+ cases.addLibcTestCase("regression/putenv-doublefree.c", true, .{});
+ cases.addLibcTestCase("regression/raise-race.c", false, .{});
+ cases.addLibcTestCase("regression/regex-backref-0.c", true, .{});
+ cases.addLibcTestCase("regression/regex-bracket-icase.c", true, .{});
+ cases.addLibcTestCase("regression/regex-ere-backref.c", true, .{});
+ cases.addLibcTestCase("regression/regex-escaped-high-byte.c", true, .{});
+ cases.addLibcTestCase("regression/regex-negated-range.c", true, .{});
+ cases.addLibcTestCase("regression/regexec-nosub.c", true, .{});
+ cases.addLibcTestCase("regression/rewind-clear-error.c", false, .{});
+ cases.addLibcTestCase("regression/rlimit-open-files.c", false, .{});
+ cases.addLibcTestCase("regression/scanf-bytes-consumed.c", true, .{});
+ cases.addLibcTestCase("regression/scanf-match-literal-eof.c", true, .{});
+ cases.addLibcTestCase("regression/scanf-nullbyte-char.c", true, .{});
+ cases.addLibcTestCase("regression/sem_close-unmap.c", false, .{});
+ // "regression/setenv-oom.c": QEMU OOM
+ cases.addLibcTestCase("regression/setvbuf-unget.c", true, .{});
+ cases.addLibcTestCase("regression/sigaltstack.c", false, .{});
+ cases.addLibcTestCase("regression/sigprocmask-internal.c", false, .{});
+ cases.addLibcTestCase("regression/sigreturn.c", true, .{});
+ cases.addLibcTestCase("regression/sscanf-eof.c", true, .{});
+ cases.addLibcTestCase("regression/strverscmp.c", true, .{});
+ cases.addLibcTestCase("regression/syscall-sign-extend.c", false, .{});
+ cases.addLibcTestCase("regression/uselocale-0.c", true, .{});
+ cases.addLibcTestCase("regression/wcsncpy-read-overflow.c", true, .{});
+ cases.addLibcTestCase("regression/wcsstr-false-negative.c", true, .{});
+}
+
+const std = @import("std");
+const tests = @import("tests.zig");
test/tests.zig
@@ -10,6 +10,7 @@ const stack_traces = @import("stack_traces.zig");
const translate_c = @import("translate_c.zig");
const run_translated_c = @import("run_translated_c.zig");
const llvm_ir = @import("llvm_ir.zig");
+const libc = @import("libc.zig");
// Implementations
pub const TranslateCContext = @import("src/TranslateC.zig");
@@ -17,6 +18,7 @@ pub const RunTranslatedCContext = @import("src/RunTranslatedC.zig");
pub const StackTracesContext = @import("src/StackTrace.zig");
pub const DebuggerContext = @import("src/Debugger.zig");
pub const LlvmIrContext = @import("src/LlvmIr.zig");
+pub const LibcContext = @import("src/Libc.zig");
const TestTarget = struct {
linkage: ?std.builtin.LinkMode = null,
@@ -2669,3 +2671,191 @@ pub fn addLlvmIrTests(b: *std.Build, options: LlvmIrContext.Options) ?*Step {
return step;
}
+
+const libc_targets: []const std.Target.Query = &.{
+ .{
+ .cpu_arch = .arm,
+ .os_tag = .linux,
+ .abi = .musleabi,
+ },
+ .{
+ .cpu_arch = .arm,
+ .os_tag = .linux,
+ .abi = .musleabihf,
+ },
+ .{
+ .cpu_arch = .armeb,
+ .os_tag = .linux,
+ .abi = .musleabi,
+ },
+ .{
+ .cpu_arch = .armeb,
+ .os_tag = .linux,
+ .abi = .musleabihf,
+ },
+ .{
+ .cpu_arch = .thumb,
+ .os_tag = .linux,
+ .abi = .musleabi,
+ },
+ .{
+ .cpu_arch = .thumb,
+ .os_tag = .linux,
+ .abi = .musleabihf,
+ },
+ .{
+ .cpu_arch = .thumbeb,
+ .os_tag = .linux,
+ .abi = .musleabi,
+ },
+ .{
+ .cpu_arch = .thumbeb,
+ .os_tag = .linux,
+ .abi = .musleabihf,
+ },
+ .{
+ .cpu_arch = .aarch64,
+ .os_tag = .linux,
+ .abi = .musl,
+ },
+ .{
+ .cpu_arch = .aarch64_be,
+ .os_tag = .linux,
+ .abi = .musl,
+ },
+ // .{
+ // .cpu_arch = .hexagon,
+ // .os_tag = .linux,
+ // .abi = .musl,
+ // },
+ .{
+ .cpu_arch = .loongarch64,
+ .os_tag = .linux,
+ .abi = .musl,
+ },
+ .{
+ .cpu_arch = .loongarch64,
+ .os_tag = .linux,
+ .abi = .muslsf,
+ },
+ // .{
+ // .cpu_arch = .mips,
+ // .os_tag = .linux,
+ // .abi = .musleabi,
+ // },
+ // .{
+ // .cpu_arch = .mips,
+ // .os_tag = .linux,
+ // .abi = .musleabihf,
+ // },
+ // .{
+ // .cpu_arch = .mipsel,
+ // .os_tag = .linux,
+ // .abi = .musleabi,
+ // },
+ // .{
+ // .cpu_arch = .mipsel,
+ // .os_tag = .linux,
+ // .abi = .musleabihf,
+ // },
+ // .{
+ // .cpu_arch = .mips64,
+ // .os_tag = .linux,
+ // .abi = .muslabi64,
+ // },
+ // .{
+ // .cpu_arch = .mips64,
+ // .os_tag = .linux,
+ // .abi = .muslabin32,
+ // },
+ // .{
+ // .cpu_arch = .mips64el,
+ // .os_tag = .linux,
+ // .abi = .muslabi64,
+ // },
+ // .{
+ // .cpu_arch = .mips64el,
+ // .os_tag = .linux,
+ // .abi = .muslabin32,
+ // },
+ .{
+ .cpu_arch = .powerpc,
+ .os_tag = .linux,
+ .abi = .musleabi,
+ },
+ .{
+ .cpu_arch = .powerpc,
+ .os_tag = .linux,
+ .abi = .musleabihf,
+ },
+ .{
+ .cpu_arch = .powerpc64,
+ .os_tag = .linux,
+ .abi = .musl,
+ },
+ .{
+ .cpu_arch = .powerpc64le,
+ .os_tag = .linux,
+ .abi = .musl,
+ },
+ .{
+ .cpu_arch = .riscv32,
+ .os_tag = .linux,
+ .abi = .musl,
+ },
+ .{
+ .cpu_arch = .riscv64,
+ .os_tag = .linux,
+ .abi = .musl,
+ },
+ .{
+ .cpu_arch = .s390x,
+ .os_tag = .linux,
+ .abi = .musl,
+ },
+ .{
+ .cpu_arch = .wasm32,
+ .os_tag = .wasi,
+ .abi = .musl,
+ },
+ .{
+ .cpu_arch = .x86,
+ .os_tag = .linux,
+ .abi = .musl,
+ },
+ .{
+ .cpu_arch = .x86_64,
+ .os_tag = .linux,
+ .abi = .musl,
+ },
+ .{
+ .cpu_arch = .x86_64,
+ .os_tag = .linux,
+ .abi = .muslx32,
+ },
+};
+
+pub fn addLibcTests(b: *std.Build, options: LibcContext.Options) ?*Step {
+ const step = b.step("test-libc", "Run libc-test test cases");
+ const opt_libc_test_path = b.option(std.Build.LazyPath, "libc-test-path", "path to libc-test source directory");
+ if (opt_libc_test_path) |libc_test_path| {
+ var context: LibcContext = .{
+ .b = b,
+ .options = options,
+ .root_step = step,
+ .libc_test_src_path = libc_test_path.path(b, "src"),
+ };
+
+ libc.addCases(&context);
+
+ for (libc_targets) |target_query| {
+ const target = b.resolveTargetQuery(target_query);
+ context.addTarget(target);
+ }
+
+ return step;
+ } else {
+ step.dependOn(&b.addFail("The -Dlibc-test-path=... option is required for this step").step);
+ return null;
+ }
+}
build.zig
@@ -632,6 +632,12 @@ pub fn build(b: *std.Build) !void {
const test_incremental_step = b.step("test-incremental", "Run the incremental compilation test cases");
try tests.addIncrementalTests(b, test_incremental_step);
test_step.dependOn(test_incremental_step);
+
+ if (tests.addLibcTests(b, .{
+ .optimize_modes = optimization_modes,
+ .test_filters = test_filters,
+ .test_target_filters = test_target_filters,
+ })) |test_libc_step| test_step.dependOn(test_libc_step);
}
fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {