authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-04-10 19:17:29+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-04-11 17:12:31+02:00
log1f896c1bf89aa0e3d2a0dce1f4cf6ba6ce5ae9ed
tree66d5586f636c37b65a1b99de3c0665bab97ec3f0
parentee0ff134e9f82bf87751a5174c27b191c04e16c0
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

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.

15 files changed, 151 insertions(+), 288 deletions(-)

build.zig+2-2
...@@ -482,8 +482,8 @@ pub fn build(b: *std.Build) !void {...@@ -482,8 +482,8 @@ pub fn build(b: *std.Build) !void {
482 .test_target_filters = test_target_filters,482 .test_target_filters = test_target_filters,
483 .test_extra_targets = test_extra_targets,483 .test_extra_targets = test_extra_targets,
484 .root_src = "lib/c.zig",484 .root_src = "lib/c.zig",
485 .name = "universal-libc",485 .name = "zigc",
486 .desc = "Run the universal libc tests",486 .desc = "Run the zigc tests",
487 .optimize_modes = optimization_modes,487 .optimize_modes = optimization_modes,
488 .include_paths = &.{},488 .include_paths = &.{},
489 .skip_single_threaded = true,489 .skip_single_threaded = true,
lib/c.zig+19-166
...@@ -1,180 +1,33 @@...@@ -1,180 +1,33 @@
1//! This is Zig's multi-target implementation of libc.1//! This is Zig's multi-target implementation of libc.
2//! When builtin.link_libc is true, we need to export all the functions and2//!
3//! provide an entire C API.3//! When `builtin.link_libc` is true, we need to export all the functions and
4//! provide a libc API compatible with the target (e.g. musl, wasi-libc, ...).
45
5const std = @import("std");
6const builtin = @import("builtin");6const builtin = @import("builtin");
7const math = std.math;7const std = @import("std");
8const isNan = std.math.isNan;
9const maxInt = std.math.maxInt;
10const native_os = builtin.os.tag;
11const native_arch = builtin.cpu.arch;
12const native_abi = builtin.abi;
13
14const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .internal else .strong;
158
16const is_wasm = switch (native_arch) {9// Avoid dragging in the runtime safety mechanisms into this .o file, unless
17 .wasm32, .wasm64 => true,10// we're trying to test zigc.
18 else => false,11pub const panic = if (builtin.is_test)
19};12 std.debug.FullPanic(std.debug.defaultPanic)
20const is_freestanding = switch (native_os) {13else
21 .freestanding, .other => true,14 std.debug.no_panic;
22 else => false,
23};
2415
25comptime {16comptime {
26 if (is_freestanding and is_wasm and builtin.link_libc) {17 if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {
27 @export(&wasm_start, .{ .name = "_start", .linkage = .strong });18 // Files specific to musl and wasi-libc.
28 }19 _ = @import("c/string.zig");
29
30 if (builtin.link_libc) {
31 @export(&strcmp, .{ .name = "strcmp", .linkage = linkage });
32 @export(&strncmp, .{ .name = "strncmp", .linkage = linkage });
33 @export(&strerror, .{ .name = "strerror", .linkage = linkage });
34 @export(&strlen, .{ .name = "strlen", .linkage = linkage });
35 @export(&strcpy, .{ .name = "strcpy", .linkage = linkage });
36 @export(&strncpy, .{ .name = "strncpy", .linkage = linkage });
37 @export(&strcat, .{ .name = "strcat", .linkage = linkage });
38 @export(&strncat, .{ .name = "strncat", .linkage = linkage });
39 }
40}
41
42// Avoid dragging in the runtime safety mechanisms into this .o file,
43// unless we're trying to test this file.
44pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
45 @branchHint(.cold);
46 _ = error_return_trace;
47 if (builtin.is_test) {
48 std.debug.panic("{s}", .{msg});
49 }
50 switch (native_os) {
51 .freestanding, .other, .amdhsa, .amdpal => while (true) {},
52 else => std.os.abort(),
53 }20 }
54}
55
56extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
57fn wasm_start() callconv(.c) void {
58 _ = main(0, undefined);
59}
6021
61fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.c) [*:0]u8 {22 if (builtin.target.isMuslLibC()) {
62 var i: usize = 0;23 // Files specific to musl.
63 while (src[i] != 0) : (i += 1) {
64 dest[i] = src[i];
65 }24 }
66 dest[i] = 0;
6725
68 return dest;26 if (builtin.target.isWasiLibC()) {
69}27 // Files specific to wasi-libc.
70
71test "strcpy" {
72 var s1: [9:0]u8 = undefined;
73
74 s1[0] = 0;
75 _ = strcpy(&s1, "foobarbaz");
76 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
77}
78
79fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.c) [*:0]u8 {
80 var i: usize = 0;
81 while (i < n and src[i] != 0) : (i += 1) {
82 dest[i] = src[i];
83 }
84 while (i < n) : (i += 1) {
85 dest[i] = 0;
86 }28 }
8729
88 return dest;30 if (builtin.target.isMinGW()) {
89}31 // Files specific to MinGW-w64.
90
91test "strncpy" {
92 var s1: [9:0]u8 = undefined;
93
94 s1[0] = 0;
95 _ = strncpy(&s1, "foobarbaz", @sizeOf(@TypeOf(s1)));
96 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
97}
98
99fn strcat(dest: [*:0]u8, src: [*:0]const u8) callconv(.c) [*:0]u8 {
100 var dest_end: usize = 0;
101 while (dest[dest_end] != 0) : (dest_end += 1) {}
102
103 var i: usize = 0;
104 while (src[i] != 0) : (i += 1) {
105 dest[dest_end + i] = src[i];
106 }32 }
107 dest[dest_end + i] = 0;
108
109 return dest;
110}
111
112test "strcat" {
113 var s1: [9:0]u8 = undefined;
114
115 s1[0] = 0;
116 _ = strcat(&s1, "foo");
117 _ = strcat(&s1, "bar");
118 _ = strcat(&s1, "baz");
119 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
120}
121
122fn strncat(dest: [*:0]u8, src: [*:0]const u8, avail: usize) callconv(.c) [*:0]u8 {
123 var dest_end: usize = 0;
124 while (dest[dest_end] != 0) : (dest_end += 1) {}
125
126 var i: usize = 0;
127 while (i < avail and src[i] != 0) : (i += 1) {
128 dest[dest_end + i] = src[i];
129 }
130 dest[dest_end + i] = 0;
131
132 return dest;
133}
134
135test "strncat" {
136 var s1: [9:0]u8 = undefined;
137
138 s1[0] = 0;
139 _ = strncat(&s1, "foo1111", 3);
140 _ = strncat(&s1, "bar1111", 3);
141 _ = strncat(&s1, "baz1111", 3);
142 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
143}
144
145fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.c) c_int {
146 return switch (std.mem.orderZ(u8, s1, s2)) {
147 .lt => -1,
148 .eq => 0,
149 .gt => 1,
150 };
151}
152
153fn strlen(s: [*:0]const u8) callconv(.c) usize {
154 return std.mem.len(s);
155}
156
157fn strncmp(_l: [*:0]const u8, _r: [*:0]const u8, _n: usize) callconv(.c) c_int {
158 if (_n == 0) return 0;
159 var l = _l;
160 var r = _r;
161 var n = _n - 1;
162 while (l[0] != 0 and r[0] != 0 and n != 0 and l[0] == r[0]) {
163 l += 1;
164 r += 1;
165 n -= 1;
166 }
167 return @as(c_int, l[0]) - @as(c_int, r[0]);
168}
169
170fn strerror(errnum: c_int) callconv(.c) [*:0]const u8 {
171 _ = errnum;
172 return "TODO strerror implementation";
173}
174
175test "strncmp" {
176 try std.testing.expect(strncmp("a", "b", 1) < 0);
177 try std.testing.expect(strncmp("a", "c", 1) < 0);
178 try std.testing.expect(strncmp("b", "a", 1) > 0);
179 try std.testing.expect(strncmp("\xff", "\x02", 1) > 0);
180}33}
lib/c/common.zig created+15
...@@ -0,0 +1,15 @@
1const builtin = @import("builtin");
2const std = @import("std");
3
4pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test)
5 .internal
6else
7 .strong;
8
9/// Determines the symbol's visibility to other objects.
10/// For WebAssembly this allows the symbol to be resolved to other modules, but will not
11/// export it to the host runtime.
12pub const visibility: std.builtin.SymbolVisibility = if (builtin.cpu.arch.isWasm() and linkage != .internal)
13 .hidden
14else
15 .default;
lib/c/string.zig created+45
...@@ -0,0 +1,45 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const common = @import("common.zig");
4
5comptime {
6 @export(&strcmp, .{ .name = "strcmp", .linkage = common.linkage, .visibility = common.visibility });
7 @export(&strlen, .{ .name = "strlen", .linkage = common.linkage, .visibility = common.visibility });
8 @export(&strncmp, .{ .name = "strncmp", .linkage = common.linkage, .visibility = common.visibility });
9}
10
11fn strcmp(s1: [*:0]const c_char, s2: [*:0]const c_char) callconv(.c) c_int {
12 // We need to perform unsigned comparisons.
13 return switch (std.mem.orderZ(u8, @ptrCast(s1), @ptrCast(s2))) {
14 .lt => -1,
15 .eq => 0,
16 .gt => 1,
17 };
18}
19
20fn strncmp(s1: [*:0]const c_char, s2: [*:0]const c_char, n: usize) callconv(.c) c_int {
21 if (n == 0) return 0;
22
23 var l: [*:0]const u8 = @ptrCast(s1);
24 var r: [*:0]const u8 = @ptrCast(s2);
25 var i = n - 1;
26
27 while (l[0] != 0 and r[0] != 0 and i != 0 and l[0] == r[0]) {
28 l += 1;
29 r += 1;
30 i -= 1;
31 }
32
33 return @as(c_int, l[0]) - @as(c_int, r[0]);
34}
35
36test strncmp {
37 try std.testing.expect(strncmp(@ptrCast("a"), @ptrCast("b"), 1) < 0);
38 try std.testing.expect(strncmp(@ptrCast("a"), @ptrCast("c"), 1) < 0);
39 try std.testing.expect(strncmp(@ptrCast("b"), @ptrCast("a"), 1) > 0);
40 try std.testing.expect(strncmp(@ptrCast("\xff"), @ptrCast("\x02"), 1) > 0);
41}
42
43fn strlen(s: [*:0]const c_char) callconv(.c) usize {
44 return std.mem.len(s);
45}
lib/libc/musl/src/string/strcmp.c deleted-7
...@@ -1,7 +0,0 @@
1#include <string.h>
2
3int strcmp(const char *l, const char *r)
4{
5 for (; *l==*r && *l; l++, r++);
6 return *(unsigned char *)l - *(unsigned char *)r;
7}
lib/libc/musl/src/string/strlen.c deleted-22
...@@ -1,22 +0,0 @@
1#include <string.h>
2#include <stdint.h>
3#include <limits.h>
4
5#define ALIGN (sizeof(size_t))
6#define ONES ((size_t)-1/UCHAR_MAX)
7#define HIGHS (ONES * (UCHAR_MAX/2+1))
8#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)
9
10size_t strlen(const char *s)
11{
12 const char *a = s;
13#ifdef __GNUC__
14 typedef size_t __attribute__((__may_alias__)) word;
15 const word *w;
16 for (; (uintptr_t)s % ALIGN; s++) if (!*s) return s-a;
17 for (w = (const void *)s; !HASZERO(*w); w++);
18 s = (const void *)w;
19#endif
20 for (; *s; s++);
21 return s-a;
22}
lib/libc/musl/src/string/strncmp.c deleted-9
...@@ -1,9 +0,0 @@
1#include <string.h>
2
3int strncmp(const char *_l, const char *_r, size_t n)
4{
5 const unsigned char *l=(void *)_l, *r=(void *)_r;
6 if (!n--) return 0;
7 for (; *l && *r && n && *l == *r ; l++, r++, n--);
8 return *l - *r;
9}
src/Compilation.zig+27-39
...@@ -235,7 +235,7 @@ ubsan_rt_lib: ?CrtFile = null,...@@ -235,7 +235,7 @@ ubsan_rt_lib: ?CrtFile = null,
235ubsan_rt_obj: ?CrtFile = null,235ubsan_rt_obj: ?CrtFile = null,
236/// Populated when we build the libc static library. A Job to build this is placed in the queue236/// Populated when we build the libc static library. A Job to build this is placed in the queue
237/// and resolved before calling linker.flush().237/// and resolved before calling linker.flush().
238libc_static_lib: ?CrtFile = null,238zigc_static_lib: ?CrtFile = null,
239/// Populated when we build the libcompiler_rt static library. A Job to build this is indicated239/// Populated when we build the libcompiler_rt static library. A Job to build this is indicated
240/// by setting `queued_jobs.compiler_rt_lib` and resolved before calling linker.flush().240/// by setting `queued_jobs.compiler_rt_lib` and resolved before calling linker.flush().
241compiler_rt_lib: ?CrtFile = null,241compiler_rt_lib: ?CrtFile = null,
...@@ -307,7 +307,7 @@ const QueuedJobs = struct {...@@ -307,7 +307,7 @@ const QueuedJobs = struct {
307 libcxx: bool = false,307 libcxx: bool = false,
308 libcxxabi: bool = false,308 libcxxabi: bool = false,
309 libtsan: bool = false,309 libtsan: bool = false,
310 zig_libc: bool = false,310 zigc_lib: bool = false,
311};311};
312312
313pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;313pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;
...@@ -801,7 +801,7 @@ pub const MiscTask = enum {...@@ -801,7 +801,7 @@ pub const MiscTask = enum {
801 libfuzzer,801 libfuzzer,
802 wasi_libc_crt_file,802 wasi_libc_crt_file,
803 compiler_rt,803 compiler_rt,
804 zig_libc,804 libzigc,
805 analyze_mod,805 analyze_mod,
806 docs_copy,806 docs_copy,
807 docs_wasm,807 docs_wasm,
...@@ -1759,7 +1759,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1759,7 +1759,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1759 const target = comp.root_mod.resolved_target.result;1759 const target = comp.root_mod.resolved_target.result;
17601760
1761 const capable_of_building_compiler_rt = canBuildLibCompilerRt(target, comp.config.use_llvm);1761 const capable_of_building_compiler_rt = canBuildLibCompilerRt(target, comp.config.use_llvm);
1762 const capable_of_building_zig_libc = canBuildZigLibC(target, comp.config.use_llvm);
17631762
1764 // Add a `CObject` for each `c_source_files`.1763 // Add a `CObject` for each `c_source_files`.
1765 try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len);1764 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...@@ -1891,12 +1890,17 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1891 // When linking mingw-w64 there are some import libs we always need.1890 // When linking mingw-w64 there are some import libs we always need.
1892 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);1891 try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
1893 for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {});1892 for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {});
1894 } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) {
1895 comp.queued_jobs.zig_libc = true;
1896 comp.remaining_prelink_tasks += 1;
1897 } else {1893 } else {
1898 return error.LibCUnavailable;1894 return error.LibCUnavailable;
1899 }1895 }
1896
1897 if ((target.isMuslLibC() and comp.config.link_mode == .static) or
1898 target.isWasiLibC() or
1899 target.isMinGW())
1900 {
1901 comp.queued_jobs.zigc_lib = true;
1902 comp.remaining_prelink_tasks += 1;
1903 }
1900 }1904 }
19011905
1902 // Generate Windows import libs.1906 // Generate Windows import libs.
...@@ -2010,7 +2014,7 @@ pub fn destroy(comp: *Compilation) void {...@@ -2010,7 +2014,7 @@ pub fn destroy(comp: *Compilation) void {
2010 crt_file.deinit(gpa);2014 crt_file.deinit(gpa);
2011 }2015 }
20122016
2013 if (comp.libc_static_lib) |*crt_file| {2017 if (comp.zigc_static_lib) |*crt_file| {
2014 crt_file.deinit(gpa);2018 crt_file.deinit(gpa);
2015 }2019 }
20162020
...@@ -3761,23 +3765,23 @@ fn performAllTheWorkInner(...@@ -3761,23 +3765,23 @@ fn performAllTheWorkInner(
3761 // compiler-rt due to LLD bugs as well, e.g.:3765 // compiler-rt due to LLD bugs as well, e.g.:
3762 //3766 //
3763 // https://github.com/llvm/llvm-project/issues/43698#issuecomment-25426606113767 // https://github.com/llvm/llvm-project/issues/43698#issuecomment-2542660611
3764 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Lib, false, &comp.compiler_rt_lib, main_progress_node });3768 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", "compiler_rt", .compiler_rt, .Lib, false, &comp.compiler_rt_lib, main_progress_node });
3765 }3769 }
37663770
3767 if (comp.queued_jobs.compiler_rt_obj and comp.compiler_rt_obj == null) {3771 if (comp.queued_jobs.compiler_rt_obj and comp.compiler_rt_obj == null) {
3768 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Obj, false, &comp.compiler_rt_obj, main_progress_node });3772 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", "compiler_rt", .compiler_rt, .Obj, false, &comp.compiler_rt_obj, main_progress_node });
3769 }3773 }
37703774
3771 if (comp.queued_jobs.fuzzer_lib and comp.fuzzer_lib == null) {3775 if (comp.queued_jobs.fuzzer_lib and comp.fuzzer_lib == null) {
3772 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", .libfuzzer, .Lib, true, &comp.fuzzer_lib, main_progress_node });3776 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", "fuzzer", .libfuzzer, .Lib, true, &comp.fuzzer_lib, main_progress_node });
3773 }3777 }
37743778
3775 if (comp.queued_jobs.ubsan_rt_lib and comp.ubsan_rt_lib == null) {3779 if (comp.queued_jobs.ubsan_rt_lib and comp.ubsan_rt_lib == null) {
3776 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan_rt.zig", .libubsan, .Lib, false, &comp.ubsan_rt_lib, main_progress_node });3780 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan_rt.zig", "ubsan_rt", .libubsan, .Lib, false, &comp.ubsan_rt_lib, main_progress_node });
3777 }3781 }
37783782
3779 if (comp.queued_jobs.ubsan_rt_obj and comp.ubsan_rt_obj == null) {3783 if (comp.queued_jobs.ubsan_rt_obj and comp.ubsan_rt_obj == null) {
3780 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan_rt.zig", .libubsan, .Obj, false, &comp.ubsan_rt_obj, main_progress_node });3784 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "ubsan_rt.zig", "ubsan_rt", .libubsan, .Obj, false, &comp.ubsan_rt_obj, main_progress_node });
3781 }3785 }
37823786
3783 if (comp.queued_jobs.glibc_shared_objects) {3787 if (comp.queued_jobs.glibc_shared_objects) {
...@@ -3800,8 +3804,8 @@ fn performAllTheWorkInner(...@@ -3800,8 +3804,8 @@ fn performAllTheWorkInner(
3800 comp.link_task_wait_group.spawnManager(buildLibTsan, .{ comp, main_progress_node });3804 comp.link_task_wait_group.spawnManager(buildLibTsan, .{ comp, main_progress_node });
3801 }3805 }
38023806
3803 if (comp.queued_jobs.zig_libc and comp.libc_static_lib == null) {3807 if (comp.queued_jobs.zigc_lib and comp.zigc_static_lib == null) {
3804 comp.link_task_wait_group.spawnManager(buildZigLibc, .{ comp, main_progress_node });3808 comp.link_task_wait_group.spawnManager(buildLibZigC, .{ comp, main_progress_node });
3805 }3809 }
38063810
3807 for (0..@typeInfo(musl.CrtFile).@"enum".fields.len) |i| {3811 for (0..@typeInfo(musl.CrtFile).@"enum".fields.len) |i| {
...@@ -4764,6 +4768,7 @@ fn workerUpdateWin32Resource(...@@ -4764,6 +4768,7 @@ fn workerUpdateWin32Resource(
4764fn buildRt(4768fn buildRt(
4765 comp: *Compilation,4769 comp: *Compilation,
4766 root_source_name: []const u8,4770 root_source_name: []const u8,
4771 root_name: []const u8,
4767 misc_task: MiscTask,4772 misc_task: MiscTask,
4768 output_mode: std.builtin.OutputMode,4773 output_mode: std.builtin.OutputMode,
4769 allow_lto: bool,4774 allow_lto: bool,
...@@ -4772,6 +4777,7 @@ fn buildRt(...@@ -4772,6 +4777,7 @@ fn buildRt(
4772) void {4777) void {
4773 comp.buildOutputFromZig(4778 comp.buildOutputFromZig(
4774 root_source_name,4779 root_source_name,
4780 root_name,
4775 output_mode,4781 output_mode,
4776 allow_lto,4782 allow_lto,
4777 out,4783 out,
...@@ -4877,17 +4883,18 @@ fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void {...@@ -4877,17 +4883,18 @@ fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void {
4877 }4883 }
4878}4884}
48794885
4880fn buildZigLibc(comp: *Compilation, prog_node: std.Progress.Node) void {4886fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void {
4881 comp.buildOutputFromZig(4887 comp.buildOutputFromZig(
4882 "c.zig",4888 "c.zig",
4889 "zigc",
4883 .Lib,4890 .Lib,
4884 true,4891 true,
4885 &comp.libc_static_lib,4892 &comp.zigc_static_lib,
4886 .zig_libc,4893 .libzigc,
4887 prog_node,4894 prog_node,
4888 ) catch |err| switch (err) {4895 ) catch |err| switch (err) {
4889 error.SubCompilationFailed => return, // error reported already4896 error.SubCompilationFailed => return, // error reported already
4890 else => comp.lockAndSetMiscFailure(.zig_libc, "unable to build zig's multitarget libc: {s}", .{@errorName(err)}),4897 else => comp.lockAndSetMiscFailure(.libzigc, "unable to build libzigc: {s}", .{@errorName(err)}),
4891 };4898 };
4892}4899}
48934900
...@@ -6521,25 +6528,6 @@ fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool {...@@ -6521,25 +6528,6 @@ fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool {
6521 };6528 };
6522}6529}
65236530
6524/// Not to be confused with canBuildLibC, which builds musl, glibc, and similar.
6525/// This one builds lib/c.zig.
6526fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool {
6527 switch (target.os.tag) {
6528 .plan9 => return false,
6529 else => {},
6530 }
6531 switch (target.cpu.arch) {
6532 .spirv, .spirv32, .spirv64 => return false,
6533 else => {},
6534 }
6535 return switch (target_util.zigBackend(target, use_llvm)) {
6536 .stage2_llvm => true,
6537 .stage2_riscv64 => true,
6538 .stage2_x86_64 => if (target.ofmt == .elf or target.ofmt == .macho) true else build_options.have_llvm,
6539 else => build_options.have_llvm,
6540 };
6541}
6542
6543pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend {6531pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend {
6544 const target = comp.root_mod.resolved_target.result;6532 const target = comp.root_mod.resolved_target.result;
6545 return target_util.zigBackend(target, comp.config.use_llvm);6533 return target_util.zigBackend(target, comp.config.use_llvm);
...@@ -6580,6 +6568,7 @@ pub fn updateSubCompilation(...@@ -6580,6 +6568,7 @@ pub fn updateSubCompilation(
6580fn buildOutputFromZig(6568fn buildOutputFromZig(
6581 comp: *Compilation,6569 comp: *Compilation,
6582 src_basename: []const u8,6570 src_basename: []const u8,
6571 root_name: []const u8,
6583 output_mode: std.builtin.OutputMode,6572 output_mode: std.builtin.OutputMode,
6584 allow_lto: bool,6573 allow_lto: bool,
6585 out: *?CrtFile,6574 out: *?CrtFile,
...@@ -6643,7 +6632,6 @@ fn buildOutputFromZig(...@@ -6643,7 +6632,6 @@ fn buildOutputFromZig(
6643 .builtin_mod = null,6632 .builtin_mod = null,
6644 .builtin_modules = null, // there is only one module in this compilation6633 .builtin_modules = null, // there is only one module in this compilation
6645 });6634 });
6646 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];
6647 const target = comp.getTarget();6635 const target = comp.getTarget();
6648 const bin_basename = try std.zig.binNameAlloc(arena, .{6636 const bin_basename = try std.zig.binNameAlloc(arena, .{
6649 .root_name = root_name,6637 .root_name = root_name,
src/link/Coff.zig+6-5
...@@ -2147,6 +2147,12 @@ fn linkWithLLD(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -2147,6 +2147,12 @@ fn linkWithLLD(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
2147 },2147 },
2148 }2148 }
21492149
2150 if (comp.config.link_libc and link_in_crt) {
2151 if (comp.zigc_static_lib) |zigc| {
2152 try argv.append(try zigc.full_object_path.toString(arena));
2153 }
2154 }
2155
2150 // libc++ dep2156 // libc++ dep
2151 if (comp.config.link_libcpp) {2157 if (comp.config.link_libcpp) {
2152 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));2158 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:...@@ -2172,11 +2178,6 @@ fn linkWithLLD(coff: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
2172 }2178 }
21732179
2174 if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {2180 if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {
2175 if (!comp.config.link_libc) {
2176 if (comp.libc_static_lib) |lib| {
2177 try argv.append(try lib.full_object_path.toString(arena));
2178 }
2179 }
2180 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but2181 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
2181 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.2182 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
2182 if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena));2183 if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena));
src/link/Elf.zig+4-10
...@@ -1984,16 +1984,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s...@@ -1984,16 +1984,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
1984 try argv.append(try p.toString(arena));1984 try argv.append(try p.toString(arena));
1985 }1985 }
19861986
1987 // libc
1988 if (is_exe_or_dyn_lib and
1989 !comp.skip_linker_dependencies and
1990 !comp.config.link_libc)
1991 {
1992 if (comp.libc_static_lib) |lib| {
1993 try argv.append(try lib.full_object_path.toString(arena));
1994 }
1995 }
1996
1997 // Shared libraries.1987 // Shared libraries.
1998 if (is_exe_or_dyn_lib) {1988 if (is_exe_or_dyn_lib) {
1999 // Worst-case, we need an --as-needed argument for every lib, as well1989 // 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...@@ -2071,6 +2061,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
2071 } else {2061 } else {
2072 diags.flags.missing_libc = true;2062 diags.flags.missing_libc = true;
2073 }2063 }
2064
2065 if (comp.zigc_static_lib) |zigc| {
2066 try argv.append(try zigc.full_object_path.toString(arena));
2067 }
2074 }2068 }
2075 }2069 }
20762070
src/link/MachO.zig+12
...@@ -454,6 +454,17 @@ pub fn flushModule(...@@ -454,6 +454,17 @@ pub fn flushModule(
454 system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path });454 system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path });
455 }455 }
456456
457 const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or
458 (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic);
459
460 if (comp.config.link_libc and is_exe_or_dyn_lib) {
461 if (comp.zigc_static_lib) |zigc| {
462 const path = zigc.full_object_path;
463 self.classifyInputFile(try link.openArchiveInput(diags, path, false, false)) catch |err|
464 diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)});
465 }
466 }
467
457 // libc/libSystem dep468 // libc/libSystem dep
458 self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) {469 self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) {
459 error.MissingLibSystem => {}, // already reported470 error.MissingLibSystem => {}, // already reported
...@@ -831,6 +842,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -831,6 +842,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
831842
832 try argv.append("-lSystem");843 try argv.append("-lSystem");
833844
845 if (comp.zigc_static_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
834 if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));846 if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
835 if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena));847 if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena));
836 if (comp.ubsan_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));848 if (comp.ubsan_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
src/link/Wasm.zig+18-19
...@@ -4100,10 +4100,11 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -4100,10 +4100,11 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
4100 try argv.append("-mwasm64");4100 try argv.append("-mwasm64");
4101 }4101 }
41024102
4103 if (target.os.tag == .wasi) {4103 const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or
4104 const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or4104 (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic);
4105 (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic);4105
4106 if (is_exe_or_dyn_lib) {4106 if (comp.config.link_libc and is_exe_or_dyn_lib) {
4107 if (target.os.tag == .wasi) {
4107 for (comp.wasi_emulated_libs) |crt_file| {4108 for (comp.wasi_emulated_libs) |crt_file| {
4108 try argv.append(try comp.crtFileAsString(4109 try argv.append(try comp.crtFileAsString(
4109 arena,4110 arena,
...@@ -4111,18 +4112,20 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -4111,18 +4112,20 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
4111 ));4112 ));
4112 }4113 }
41134114
4114 if (comp.config.link_libc) {4115 try argv.append(try comp.crtFileAsString(
4115 try argv.append(try comp.crtFileAsString(4116 arena,
4116 arena,4117 wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),
4117 wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),4118 ));
4118 ));4119 try argv.append(try comp.crtFileAsString(arena, "libc.a"));
4119 try argv.append(try comp.crtFileAsString(arena, "libc.a"));4120 }
4120 }
41214121
4122 if (comp.config.link_libcpp) {4122 if (comp.zigc_static_lib) |zigc| {
4123 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));4123 try argv.append(try zigc.full_object_path.toString(arena));
4124 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));4124 }
4125 }4125
4126 if (comp.config.link_libcpp) {
4127 try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
4128 try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
4126 }4129 }
4127 }4130 }
41284131
...@@ -4157,10 +4160,6 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:...@@ -4157,10 +4160,6 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
4157 try argv.append(p);4160 try argv.append(p);
4158 }4161 }
41594162
4160 if (comp.libc_static_lib) |crt_file| {
4161 try argv.append(try crt_file.full_object_path.toString(arena));
4162 }
4163
4164 if (compiler_rt_path) |p| {4163 if (compiler_rt_path) |p| {
4165 try argv.append(try p.toString(arena));4164 try argv.append(try p.toString(arena));
4166 }4165 }
src/musl.zig-3
...@@ -1852,17 +1852,14 @@ const src_files = [_][]const u8{...@@ -1852,17 +1852,14 @@ const src_files = [_][]const u8{
1852 "musl/src/string/strcat.c",1852 "musl/src/string/strcat.c",
1853 "musl/src/string/strchr.c",1853 "musl/src/string/strchr.c",
1854 "musl/src/string/strchrnul.c",1854 "musl/src/string/strchrnul.c",
1855 "musl/src/string/strcmp.c",
1856 "musl/src/string/strcpy.c",1855 "musl/src/string/strcpy.c",
1857 "musl/src/string/strcspn.c",1856 "musl/src/string/strcspn.c",
1858 "musl/src/string/strdup.c",1857 "musl/src/string/strdup.c",
1859 "musl/src/string/strerror_r.c",1858 "musl/src/string/strerror_r.c",
1860 "musl/src/string/strlcat.c",1859 "musl/src/string/strlcat.c",
1861 "musl/src/string/strlcpy.c",1860 "musl/src/string/strlcpy.c",
1862 "musl/src/string/strlen.c",
1863 "musl/src/string/strncasecmp.c",1861 "musl/src/string/strncasecmp.c",
1864 "musl/src/string/strncat.c",1862 "musl/src/string/strncat.c",
1865 "musl/src/string/strncmp.c",
1866 "musl/src/string/strncpy.c",1863 "musl/src/string/strncpy.c",
1867 "musl/src/string/strndup.c",1864 "musl/src/string/strndup.c",
1868 "musl/src/string/strnlen.c",1865 "musl/src/string/strnlen.c",
src/wasi_libc.zig-3
...@@ -1061,17 +1061,14 @@ const libc_top_half_src_files = [_][]const u8{...@@ -1061,17 +1061,14 @@ const libc_top_half_src_files = [_][]const u8{
1061 "musl/src/string/strcat.c",1061 "musl/src/string/strcat.c",
1062 "musl/src/string/strchr.c",1062 "musl/src/string/strchr.c",
1063 "musl/src/string/strchrnul.c",1063 "musl/src/string/strchrnul.c",
1064 "musl/src/string/strcmp.c",
1065 "musl/src/string/strcpy.c",1064 "musl/src/string/strcpy.c",
1066 "musl/src/string/strcspn.c",1065 "musl/src/string/strcspn.c",
1067 "musl/src/string/strdup.c",1066 "musl/src/string/strdup.c",
1068 "musl/src/string/strerror_r.c",1067 "musl/src/string/strerror_r.c",
1069 "musl/src/string/strlcat.c",1068 "musl/src/string/strlcat.c",
1070 "musl/src/string/strlcpy.c",1069 "musl/src/string/strlcpy.c",
1071 "musl/src/string/strlen.c",
1072 "musl/src/string/strncasecmp.c",1070 "musl/src/string/strncasecmp.c",
1073 "musl/src/string/strncat.c",1071 "musl/src/string/strncat.c",
1074 "musl/src/string/strncmp.c",
1075 "musl/src/string/strncpy.c",1072 "musl/src/string/strncpy.c",
1076 "musl/src/string/strndup.c",1073 "musl/src/string/strndup.c",
1077 "musl/src/string/strnlen.c",1074 "musl/src/string/strnlen.c",
test/tests.zig+3-3
...@@ -145,7 +145,7 @@ const test_targets = blk: {...@@ -145,7 +145,7 @@ const test_targets = blk: {
145 }) catch unreachable,145 }) catch unreachable,
146 .use_llvm = false,146 .use_llvm = false,
147 .use_lld = false,147 .use_lld = false,
148 .skip_modules = &.{ "c-import", "universal-libc", "std" },148 .skip_modules = &.{ "c-import", "zigc", "std" },
149 },149 },
150 // https://github.com/ziglang/zig/issues/13623150 // https://github.com/ziglang/zig/issues/13623
151 //.{151 //.{
...@@ -1443,9 +1443,9 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {...@@ -1443,9 +1443,9 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
1443 test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt"))1443 test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt"))
1444 continue;1444 continue;
14451445
1446 // TODO get universal-libc tests passing for other self-hosted backends.1446 // TODO get zigc tests passing for other self-hosted backends.
1447 if (target.cpu.arch != .x86_64 and1447 if (target.cpu.arch != .x86_64 and
1448 test_target.use_llvm == false and mem.eql(u8, options.name, "universal-libc"))1448 test_target.use_llvm == false and mem.eql(u8, options.name, "zigc"))
1449 continue;1449 continue;
14501450
1451 // TODO get std lib tests passing for other self-hosted backends.1451 // TODO get std lib tests passing for other self-hosted backends.