aboutsummaryrefslogtreecommitdiff
path: root/lib/c.zig
diff options
context:
space:
mode:
authorAlex Rønne Petersen <alex@alexrp.com>2025-04-10 19:17:29 +0200
committerAlex Rønne Petersen <alex@alexrp.com>2025-04-11 17:12:31 +0200
commit1f896c1bf89aa0e3d2a0dce1f4cf6ba6ce5ae9ed (patch)
tree66d5586f636c37b65a1b99de3c0665bab97ec3f0 /lib/c.zig
parentee0ff134e9f82bf87751a5174c27b191c04e16c0 (diff)
downloadzig-1f896c1bf89aa0e3d2a0dce1f4cf6ba6ce5ae9ed.tar.gz
zig-1f896c1bf89aa0e3d2a0dce1f4cf6ba6ce5ae9ed.zip
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.
Diffstat (limited to 'lib/c.zig')
-rw-r--r--lib/c.zig185
1 files changed, 19 insertions, 166 deletions
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 linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .internal else .strong;
+const std = @import("std");
-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.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(),
+ if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {
+ // Files specific to musl and wasi-libc.
+ _ = @import("c/string.zig");
}
-}
-
-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];
+ if (builtin.target.isMuslLibC()) {
+ // Files specific to musl.
}
- 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.isWasiLibC()) {
+ // Files specific to wasi-libc.
}
- 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.isMinGW()) {
+ // Files specific to MinGW-w64.
}
- 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];
- }
- 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);
}