From 1f896c1bf89aa0e3d2a0dce1f4cf6ba6ce5ae9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 10 Apr 2025 19:17:29 +0200 Subject: [PATCH] Introduce libzigc for libc function implementations in Zig. This lays the groundwork for #2879. This library will be built and linked when a static libc is going to be linked into the compilation. Currently, that means musl, wasi-libc, and MinGW-w64. As a demonstration, this commit removes the musl C code for a few string functions and implements them in libzigc. This means that those libzigc functions are now load-bearing for musl and wasi-libc. Note that if a function has an implementation in compiler-rt already, libzigc should not implement it. Instead, as we recently did for memcpy/memmove, we should delete the libc copy and rely on the compiler-rt implementation. I repurposed the existing "universal libc" code to do this. That code hadn't seen development beyond basic string functions in years, and was only usable-ish on freestanding. I think that if we want to seriously pursue the idea of Zig providing a freestanding libc, we should do so only after defining clear goals (and non-goals) for it. See also #22240 for a similar case. --- build.zig | 4 +- lib/c.zig | 185 +++-------------------------- lib/c/common.zig | 15 +++ lib/c/string.zig | 45 +++++++ lib/libc/musl/src/string/strcmp.c | 7 -- lib/libc/musl/src/string/strlen.c | 22 ---- lib/libc/musl/src/string/strncmp.c | 9 -- src/Compilation.zig | 66 +++++----- src/link/Coff.zig | 11 +- src/link/Elf.zig | 14 +-- src/link/MachO.zig | 12 ++ src/link/Wasm.zig | 37 +++--- src/musl.zig | 3 - src/wasi_libc.zig | 3 - test/tests.zig | 6 +- 15 files changed, 151 insertions(+), 288 deletions(-) create mode 100644 lib/c/common.zig create mode 100644 lib/c/string.zig delete mode 100644 lib/libc/musl/src/string/strcmp.c delete mode 100644 lib/libc/musl/src/string/strlen.c delete mode 100644 lib/libc/musl/src/string/strncmp.c diff --git a/build.zig b/build.zig index d3fe688f9f..5c27609666 100644 --- a/build.zig +++ b/build.zig @@ -482,8 +482,8 @@ pub fn build(b: *std.Build) !void { .test_target_filters = test_target_filters, .test_extra_targets = test_extra_targets, .root_src = "lib/c.zig", - .name = "universal-libc", - .desc = "Run the universal libc tests", + .name = "zigc", + .desc = "Run the zigc tests", .optimize_modes = optimization_modes, .include_paths = &.{}, .skip_single_threaded = true, diff --git a/lib/c.zig b/lib/c.zig index bbb79eadab..156f471573 100644 --- a/lib/c.zig +++ b/lib/c.zig @@ -1,180 +1,33 @@ //! This is Zig's multi-target implementation of libc. -//! When builtin.link_libc is true, we need to export all the functions and -//! provide an entire C API. +//! +//! When `builtin.link_libc` is true, we need to export all the functions and +//! provide a libc API compatible with the target (e.g. musl, wasi-libc, ...). -const std = @import("std"); const builtin = @import("builtin"); -const math = std.math; -const isNan = std.math.isNan; -const maxInt = std.math.maxInt; -const native_os = builtin.os.tag; -const native_arch = builtin.cpu.arch; -const native_abi = builtin.abi; +const std = @import("std"); -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .internal else .strong; - -const is_wasm = switch (native_arch) { - .wasm32, .wasm64 => true, - else => false, -}; -const is_freestanding = switch (native_os) { - .freestanding, .other => true, - else => false, -}; +// Avoid dragging in the runtime safety mechanisms into this .o file, unless +// we're trying to test zigc. +pub const panic = if (builtin.is_test) + std.debug.FullPanic(std.debug.defaultPanic) +else + std.debug.no_panic; comptime { - if (is_freestanding and is_wasm and builtin.link_libc) { - @export(&wasm_start, .{ .name = "_start", .linkage = .strong }); + if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) { + // Files specific to musl and wasi-libc. + _ = @import("c/string.zig"); } - if (builtin.link_libc) { - @export(&strcmp, .{ .name = "strcmp", .linkage = linkage }); - @export(&strncmp, .{ .name = "strncmp", .linkage = linkage }); - @export(&strerror, .{ .name = "strerror", .linkage = linkage }); - @export(&strlen, .{ .name = "strlen", .linkage = linkage }); - @export(&strcpy, .{ .name = "strcpy", .linkage = linkage }); - @export(&strncpy, .{ .name = "strncpy", .linkage = linkage }); - @export(&strcat, .{ .name = "strcat", .linkage = linkage }); - @export(&strncat, .{ .name = "strncat", .linkage = linkage }); - } -} - -// Avoid dragging in the runtime safety mechanisms into this .o file, -// unless we're trying to test this file. -pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - @branchHint(.cold); - _ = error_return_trace; - if (builtin.is_test) { - std.debug.panic("{s}", .{msg}); - } - switch (native_os) { - .freestanding, .other, .amdhsa, .amdpal => while (true) {}, - else => std.os.abort(), - } -} - -extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; -fn wasm_start() callconv(.c) void { - _ = main(0, undefined); -} - -fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.c) [*:0]u8 { - var i: usize = 0; - while (src[i] != 0) : (i += 1) { - dest[i] = src[i]; - } - dest[i] = 0; - - return dest; -} - -test "strcpy" { - var s1: [9:0]u8 = undefined; - - s1[0] = 0; - _ = strcpy(&s1, "foobarbaz"); - try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0)); -} - -fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.c) [*:0]u8 { - var i: usize = 0; - while (i < n and src[i] != 0) : (i += 1) { - dest[i] = src[i]; - } - while (i < n) : (i += 1) { - dest[i] = 0; + if (builtin.target.isMuslLibC()) { + // Files specific to musl. } - return dest; -} - -test "strncpy" { - var s1: [9:0]u8 = undefined; - - s1[0] = 0; - _ = strncpy(&s1, "foobarbaz", @sizeOf(@TypeOf(s1))); - try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0)); -} - -fn strcat(dest: [*:0]u8, src: [*:0]const u8) callconv(.c) [*:0]u8 { - var dest_end: usize = 0; - while (dest[dest_end] != 0) : (dest_end += 1) {} - - var i: usize = 0; - while (src[i] != 0) : (i += 1) { - dest[dest_end + i] = src[i]; + if (builtin.target.isWasiLibC()) { + // Files specific to wasi-libc. } - dest[dest_end + i] = 0; - return dest; -} - -test "strcat" { - var s1: [9:0]u8 = undefined; - - s1[0] = 0; - _ = strcat(&s1, "foo"); - _ = strcat(&s1, "bar"); - _ = strcat(&s1, "baz"); - try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0)); -} - -fn strncat(dest: [*:0]u8, src: [*:0]const u8, avail: usize) callconv(.c) [*:0]u8 { - var dest_end: usize = 0; - while (dest[dest_end] != 0) : (dest_end += 1) {} - - var i: usize = 0; - while (i < avail and src[i] != 0) : (i += 1) { - dest[dest_end + i] = src[i]; + if (builtin.target.isMinGW()) { + // Files specific to MinGW-w64. } - dest[dest_end + i] = 0; - - return dest; -} - -test "strncat" { - var s1: [9:0]u8 = undefined; - - s1[0] = 0; - _ = strncat(&s1, "foo1111", 3); - _ = strncat(&s1, "bar1111", 3); - _ = strncat(&s1, "baz1111", 3); - try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0)); -} - -fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.c) c_int { - return switch (std.mem.orderZ(u8, s1, s2)) { - .lt => -1, - .eq => 0, - .gt => 1, - }; -} - -fn strlen(s: [*:0]const u8) callconv(.c) usize { - return std.mem.len(s); -} - -fn strncmp(_l: [*:0]const u8, _r: [*:0]const u8, _n: usize) callconv(.c) c_int { - if (_n == 0) return 0; - var l = _l; - var r = _r; - var n = _n - 1; - while (l[0] != 0 and r[0] != 0 and n != 0 and l[0] == r[0]) { - l += 1; - r += 1; - n -= 1; - } - return @as(c_int, l[0]) - @as(c_int, r[0]); -} - -fn strerror(errnum: c_int) callconv(.c) [*:0]const u8 { - _ = errnum; - return "TODO strerror implementation"; -} - -test "strncmp" { - try std.testing.expect(strncmp("a", "b", 1) < 0); - try std.testing.expect(strncmp("a", "c", 1) < 0); - try std.testing.expect(strncmp("b", "a", 1) > 0); - try std.testing.expect(strncmp("\xff", "\x02", 1) > 0); } diff --git a/lib/c/common.zig b/lib/c/common.zig new file mode 100644 index 0000000000..e9536f63b4 --- /dev/null +++ b/lib/c/common.zig @@ -0,0 +1,15 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) + .internal +else + .strong; + +/// Determines the symbol's visibility to other objects. +/// For WebAssembly this allows the symbol to be resolved to other modules, but will not +/// export it to the host runtime. +pub const visibility: std.builtin.SymbolVisibility = if (builtin.cpu.arch.isWasm() and linkage != .internal) + .hidden +else + .default; diff --git a/lib/c/string.zig b/lib/c/string.zig new file mode 100644 index 0000000000..0633f7e654 --- /dev/null +++ b/lib/c/string.zig @@ -0,0 +1,45 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const common = @import("common.zig"); + +comptime { + @export(&strcmp, .{ .name = "strcmp", .linkage = common.linkage, .visibility = common.visibility }); + @export(&strlen, .{ .name = "strlen", .linkage = common.linkage, .visibility = common.visibility }); + @export(&strncmp, .{ .name = "strncmp", .linkage = common.linkage, .visibility = common.visibility }); +} + +fn strcmp(s1: [*:0]const c_char, s2: [*:0]const c_char) callconv(.c) c_int { + // We need to perform unsigned comparisons. + return switch (std.mem.orderZ(u8, @ptrCast(s1), @ptrCast(s2))) { + .lt => -1, + .eq => 0, + .gt => 1, + }; +} + +fn strncmp(s1: [*:0]const c_char, s2: [*:0]const c_char, n: usize) callconv(.c) c_int { + if (n == 0) return 0; + + var l: [*:0]const u8 = @ptrCast(s1); + var r: [*:0]const u8 = @ptrCast(s2); + var i = n - 1; + + while (l[0] != 0 and r[0] != 0 and i != 0 and l[0] == r[0]) { + l += 1; + r += 1; + i -= 1; + } + + return @as(c_int, l[0]) - @as(c_int, r[0]); +} + +test strncmp { + try std.testing.expect(strncmp(@ptrCast("a"), @ptrCast("b"), 1) < 0); + try std.testing.expect(strncmp(@ptrCast("a"), @ptrCast("c"), 1) < 0); + try std.testing.expect(strncmp(@ptrCast("b"), @ptrCast("a"), 1) > 0); + try std.testing.expect(strncmp(@ptrCast("\xff"), @ptrCast("\x02"), 1) > 0); +} + +fn strlen(s: [*:0]const c_char) callconv(.c) usize { + return std.mem.len(s); +} diff --git a/lib/libc/musl/src/string/strcmp.c b/lib/libc/musl/src/string/strcmp.c deleted file mode 100644 index 808bd8370d..0000000000 --- a/lib/libc/musl/src/string/strcmp.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -int strcmp(const char *l, const char *r) -{ - for (; *l==*r && *l; l++, r++); - return *(unsigned char *)l - *(unsigned char *)r; -} diff --git a/lib/libc/musl/src/string/strlen.c b/lib/libc/musl/src/string/strlen.c deleted file mode 100644 index 309990f029..0000000000 --- a/lib/libc/musl/src/string/strlen.c +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include -#include - -#define ALIGN (sizeof(size_t)) -#define ONES ((size_t)-1/UCHAR_MAX) -#define HIGHS (ONES * (UCHAR_MAX/2+1)) -#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS) - -size_t strlen(const char *s) -{ - const char *a = s; -#ifdef __GNUC__ - typedef size_t __attribute__((__may_alias__)) word; - const word *w; - for (; (uintptr_t)s % ALIGN; s++) if (!*s) return s-a; - for (w = (const void *)s; !HASZERO(*w); w++); - s = (const void *)w; -#endif - for (; *s; s++); - return s-a; -} diff --git a/lib/libc/musl/src/string/strncmp.c b/lib/libc/musl/src/string/strncmp.c deleted file mode 100644 index e228843f0b..0000000000 --- a/lib/libc/musl/src/string/strncmp.c +++ /dev/null @@ -1,9 +0,0 @@ -#include - -int strncmp(const char *_l, const char *_r, size_t n) -{ - const unsigned char *l=(void *)_l, *r=(void *)_r; - if (!n--) return 0; - for (; *l && *r && n && *l == *r ; l++, r++, n--); - return *l - *r; -} diff --git a/src/Compilation.zig b/src/Compilation.zig index 16f762d995..87ff597d9f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -235,7 +235,7 @@ ubsan_rt_lib: ?CrtFile = null, ubsan_rt_obj: ?CrtFile = null, /// Populated when we build the libc static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -libc_static_lib: ?CrtFile = null, +zigc_static_lib: ?CrtFile = null, /// Populated when we build the libcompiler_rt static library. A Job to build this is indicated /// by setting `queued_jobs.compiler_rt_lib` and resolved before calling linker.flush(). compiler_rt_lib: ?CrtFile = null, @@ -307,7 +307,7 @@ const QueuedJobs = struct { libcxx: bool = false, libcxxabi: bool = false, libtsan: bool = false, - zig_libc: bool = false, + zigc_lib: bool = false, }; pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size; @@ -801,7 +801,7 @@ pub const MiscTask = enum { libfuzzer, wasi_libc_crt_file, compiler_rt, - zig_libc, + libzigc, analyze_mod, docs_copy, docs_wasm, @@ -1759,7 +1759,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil const target = comp.root_mod.resolved_target.result; const capable_of_building_compiler_rt = canBuildLibCompilerRt(target, comp.config.use_llvm); - const capable_of_building_zig_libc = canBuildZigLibC(target, comp.config.use_llvm); // Add a `CObject` for each `c_source_files`. try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len); @@ -1891,12 +1890,17 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil // When linking mingw-w64 there are some import libs we always need. try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {}); - } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) { - comp.queued_jobs.zig_libc = true; - comp.remaining_prelink_tasks += 1; } else { return error.LibCUnavailable; } + + if ((target.isMuslLibC() and comp.config.link_mode == .static) or + target.isWasiLibC() or + target.isMinGW()) + { + comp.queued_jobs.zigc_lib = true; + comp.remaining_prelink_tasks += 1; + } } // Generate Windows import libs. @@ -2010,7 +2014,7 @@ pub fn destroy(comp: *Compilation) void { crt_file.deinit(gpa); } - if (comp.libc_static_lib) |*crt_file| { + if (comp.zigc_static_lib) |*crt_file| { crt_file.deinit(gpa); } @@ -3761,23 +3765,23 @@ fn performAllTheWorkInner( // compiler-rt due to LLD bugs as well, e.g.: // // https://github.com/llvm/llvm-project/issues/43698#issuecomment-2542660611 - comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Lib, false, &comp.compiler_rt_lib, main_progress_node }); + comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", "compiler_rt", .compiler_rt, .Lib, false, &comp.compiler_rt_lib, main_progress_node }); } if (comp.queued_jobs.compiler_rt_obj and comp.compiler_rt_obj == null) { - comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Obj, false, &comp.compiler_rt_obj, main_progress_node }); + comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", "compiler_rt", .compiler_rt, .Obj, false, &comp.compiler_rt_obj, main_progress_node }); } if (comp.queued_jobs.fuzzer_lib and comp.fuzzer_lib == null) { - comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", .libfuzzer, .Lib, true, &comp.fuzzer_lib, main_progress_node }); + comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", "fuzzer", .libfuzzer, .Lib, true, &comp.fuzzer_lib, main_progress_node }); } if (comp.queued_jobs.ubsan_rt_lib and comp.ubsan_rt_lib == null) { - comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan_rt.zig", .libubsan, .Lib, false, &comp.ubsan_rt_lib, main_progress_node }); + comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan_rt.zig", "ubsan_rt", .libubsan, .Lib, false, &comp.ubsan_rt_lib, main_progress_node }); } if (comp.queued_jobs.ubsan_rt_obj and comp.ubsan_rt_obj == null) { - comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan_rt.zig", .libubsan, .Obj, false, &comp.ubsan_rt_obj, main_progress_node }); + comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan_rt.zig", "ubsan_rt", .libubsan, .Obj, false, &comp.ubsan_rt_obj, main_progress_node }); } if (comp.queued_jobs.glibc_shared_objects) { @@ -3800,8 +3804,8 @@ fn performAllTheWorkInner( comp.link_task_wait_group.spawnManager(buildLibTsan, .{ comp, main_progress_node }); } - if (comp.queued_jobs.zig_libc and comp.libc_static_lib == null) { - comp.link_task_wait_group.spawnManager(buildZigLibc, .{ comp, main_progress_node }); + if (comp.queued_jobs.zigc_lib and comp.zigc_static_lib == null) { + comp.link_task_wait_group.spawnManager(buildLibZigC, .{ comp, main_progress_node }); } for (0..@typeInfo(musl.CrtFile).@"enum".fields.len) |i| { @@ -4764,6 +4768,7 @@ fn workerUpdateWin32Resource( fn buildRt( comp: *Compilation, root_source_name: []const u8, + root_name: []const u8, misc_task: MiscTask, output_mode: std.builtin.OutputMode, allow_lto: bool, @@ -4772,6 +4777,7 @@ fn buildRt( ) void { comp.buildOutputFromZig( root_source_name, + root_name, output_mode, allow_lto, out, @@ -4877,17 +4883,18 @@ fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void { } } -fn buildZigLibc(comp: *Compilation, prog_node: std.Progress.Node) void { +fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void { comp.buildOutputFromZig( "c.zig", + "zigc", .Lib, true, - &comp.libc_static_lib, - .zig_libc, + &comp.zigc_static_lib, + .libzigc, prog_node, ) catch |err| switch (err) { error.SubCompilationFailed => return, // error reported already - else => comp.lockAndSetMiscFailure(.zig_libc, "unable to build zig's multitarget libc: {s}", .{@errorName(err)}), + else => comp.lockAndSetMiscFailure(.libzigc, "unable to build libzigc: {s}", .{@errorName(err)}), }; } @@ -6521,25 +6528,6 @@ fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool { }; } -/// Not to be confused with canBuildLibC, which builds musl, glibc, and similar. -/// This one builds lib/c.zig. -fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool { - switch (target.os.tag) { - .plan9 => return false, - else => {}, - } - switch (target.cpu.arch) { - .spirv, .spirv32, .spirv64 => return false, - else => {}, - } - return switch (target_util.zigBackend(target, use_llvm)) { - .stage2_llvm => true, - .stage2_riscv64 => true, - .stage2_x86_64 => if (target.ofmt == .elf or target.ofmt == .macho) true else build_options.have_llvm, - else => build_options.have_llvm, - }; -} - pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend { const target = comp.root_mod.resolved_target.result; return target_util.zigBackend(target, comp.config.use_llvm); @@ -6580,6 +6568,7 @@ pub fn updateSubCompilation( fn buildOutputFromZig( comp: *Compilation, src_basename: []const u8, + root_name: []const u8, output_mode: std.builtin.OutputMode, allow_lto: bool, out: *?CrtFile, @@ -6643,7 +6632,6 @@ fn buildOutputFromZig( .builtin_mod = null, .builtin_modules = null, // there is only one module in this compilation }); - const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; const target = comp.getTarget(); const bin_basename = try std.zig.binNameAlloc(arena, .{ .root_name = root_name, diff --git a/src/link/Coff.zig b/src/link/Coff.zig index da27c5c076..4b20ba99dd 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -2147,6 +2147,12 @@ fn linkWithLLD(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: }, } + if (comp.config.link_libc and link_in_crt) { + if (comp.zigc_static_lib) |zigc| { + try argv.append(try zigc.full_object_path.toString(arena)); + } + } + // libc++ dep if (comp.config.link_libcpp) { try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); @@ -2172,11 +2178,6 @@ fn linkWithLLD(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: } if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) { - if (!comp.config.link_libc) { - if (comp.libc_static_lib) |lib| { - try argv.append(try lib.full_object_path.toString(arena)); - } - } // MSVC compiler_rt is missing some stuff, so we build it unconditionally but // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena)); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 53f88101b1..00bd940500 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1984,16 +1984,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s try argv.append(try p.toString(arena)); } - // libc - if (is_exe_or_dyn_lib and - !comp.skip_linker_dependencies and - !comp.config.link_libc) - { - if (comp.libc_static_lib) |lib| { - try argv.append(try lib.full_object_path.toString(arena)); - } - } - // Shared libraries. if (is_exe_or_dyn_lib) { // Worst-case, we need an --as-needed argument for every lib, as well @@ -2071,6 +2061,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s } else { diags.flags.missing_libc = true; } + + if (comp.zigc_static_lib) |zigc| { + try argv.append(try zigc.full_object_path.toString(arena)); + } } } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 2b4bc3e557..2043be3e7b 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -454,6 +454,17 @@ pub fn flushModule( system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path }); } + const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or + (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic); + + if (comp.config.link_libc and is_exe_or_dyn_lib) { + if (comp.zigc_static_lib) |zigc| { + const path = zigc.full_object_path; + self.classifyInputFile(try link.openArchiveInput(diags, path, false, false)) catch |err| + diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); + } + } + // libc/libSystem dep self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) { error.MissingLibSystem => {}, // already reported @@ -831,6 +842,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { try argv.append("-lSystem"); + if (comp.zigc_static_lib) |lib| try argv.append(try lib.full_object_path.toString(arena)); if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena)); if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena)); if (comp.ubsan_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena)); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 0d996aff15..466d4aa6bb 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -4100,10 +4100,11 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: try argv.append("-mwasm64"); } - if (target.os.tag == .wasi) { - const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or - (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic); - if (is_exe_or_dyn_lib) { + const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or + (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic); + + if (comp.config.link_libc and is_exe_or_dyn_lib) { + if (target.os.tag == .wasi) { for (comp.wasi_emulated_libs) |crt_file| { try argv.append(try comp.crtFileAsString( arena, @@ -4111,18 +4112,20 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: )); } - if (comp.config.link_libc) { - try argv.append(try comp.crtFileAsString( - arena, - wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model), - )); - try argv.append(try comp.crtFileAsString(arena, "libc.a")); - } + try argv.append(try comp.crtFileAsString( + arena, + wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model), + )); + try argv.append(try comp.crtFileAsString(arena, "libc.a")); + } - if (comp.config.link_libcpp) { - try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); - try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); - } + if (comp.zigc_static_lib) |zigc| { + try argv.append(try zigc.full_object_path.toString(arena)); + } + + if (comp.config.link_libcpp) { + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); } } @@ -4157,10 +4160,6 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: try argv.append(p); } - if (comp.libc_static_lib) |crt_file| { - try argv.append(try crt_file.full_object_path.toString(arena)); - } - if (compiler_rt_path) |p| { try argv.append(try p.toString(arena)); } diff --git a/src/musl.zig b/src/musl.zig index 12189df89d..50bd625f01 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -1852,17 +1852,14 @@ const src_files = [_][]const u8{ "musl/src/string/strcat.c", "musl/src/string/strchr.c", "musl/src/string/strchrnul.c", - "musl/src/string/strcmp.c", "musl/src/string/strcpy.c", "musl/src/string/strcspn.c", "musl/src/string/strdup.c", "musl/src/string/strerror_r.c", "musl/src/string/strlcat.c", "musl/src/string/strlcpy.c", - "musl/src/string/strlen.c", "musl/src/string/strncasecmp.c", "musl/src/string/strncat.c", - "musl/src/string/strncmp.c", "musl/src/string/strncpy.c", "musl/src/string/strndup.c", "musl/src/string/strnlen.c", diff --git a/src/wasi_libc.zig b/src/wasi_libc.zig index 7f1094c81e..33c03e8068 100644 --- a/src/wasi_libc.zig +++ b/src/wasi_libc.zig @@ -1061,17 +1061,14 @@ const libc_top_half_src_files = [_][]const u8{ "musl/src/string/strcat.c", "musl/src/string/strchr.c", "musl/src/string/strchrnul.c", - "musl/src/string/strcmp.c", "musl/src/string/strcpy.c", "musl/src/string/strcspn.c", "musl/src/string/strdup.c", "musl/src/string/strerror_r.c", "musl/src/string/strlcat.c", "musl/src/string/strlcpy.c", - "musl/src/string/strlen.c", "musl/src/string/strncasecmp.c", "musl/src/string/strncat.c", - "musl/src/string/strncmp.c", "musl/src/string/strncpy.c", "musl/src/string/strndup.c", "musl/src/string/strnlen.c", diff --git a/test/tests.zig b/test/tests.zig index ef2cb6127f..a004a0b093 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -145,7 +145,7 @@ const test_targets = blk: { }) catch unreachable, .use_llvm = false, .use_lld = false, - .skip_modules = &.{ "c-import", "universal-libc", "std" }, + .skip_modules = &.{ "c-import", "zigc", "std" }, }, // https://github.com/ziglang/zig/issues/13623 //.{ @@ -1443,9 +1443,9 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt")) continue; - // TODO get universal-libc tests passing for other self-hosted backends. + // TODO get zigc tests passing for other self-hosted backends. if (target.cpu.arch != .x86_64 and - test_target.use_llvm == false and mem.eql(u8, options.name, "universal-libc")) + test_target.use_llvm == false and mem.eql(u8, options.name, "zigc")) continue; // TODO get std lib tests passing for other self-hosted backends.