diff options
| author | Alex Rønne Petersen <alex@alexrp.com> | 2025-04-10 19:17:29 +0200 |
|---|---|---|
| committer | Alex Rønne Petersen <alex@alexrp.com> | 2025-04-11 17:12:31 +0200 |
| commit | 1f896c1bf89aa0e3d2a0dce1f4cf6ba6ce5ae9ed (patch) | |
| tree | 66d5586f636c37b65a1b99de3c0665bab97ec3f0 /lib/c.zig | |
| parent | ee0ff134e9f82bf87751a5174c27b191c04e16c0 (diff) | |
| download | zig-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.zig | 185 |
1 files changed, 19 insertions, 166 deletions
@@ -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); } |
