diff options
| -rw-r--r-- | lib/compiler/std-docs.zig | 81 | ||||
| -rw-r--r-- | lib/libc/glibc/csu/init.c | 23 | ||||
| -rw-r--r-- | lib/std/Build.zig | 1 | ||||
| -rw-r--r-- | lib/std/Build/Fuzz/WebServer.zig | 8 | ||||
| -rw-r--r-- | lib/std/Build/Step.zig | 10 | ||||
| -rw-r--r-- | lib/std/Target/Query.zig | 9 | ||||
| -rw-r--r-- | lib/std/bounded_array.zig | 30 | ||||
| -rw-r--r-- | lib/std/c.zig | 4 | ||||
| -rw-r--r-- | lib/std/dynamic_library.zig | 65 | ||||
| -rw-r--r-- | lib/std/fs/test.zig | 1 | ||||
| -rw-r--r-- | lib/std/os/linux.zig | 36 | ||||
| -rw-r--r-- | lib/std/os/linux/bpf/btf.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/test.zig | 17 | ||||
| -rw-r--r-- | lib/std/posix.zig | 3 | ||||
| -rw-r--r-- | lib/std/valgrind.zig | 2 | ||||
| -rw-r--r-- | lib/std/valgrind/cachegrind.zig | 29 | ||||
| -rw-r--r-- | lib/std/valgrind/callgrind.zig | 22 | ||||
| -rw-r--r-- | lib/std/valgrind/memcheck.zig | 46 | ||||
| -rw-r--r-- | src/Zcu/PerThread.zig | 3 | ||||
| -rw-r--r-- | src/glibc.zig | 37 | ||||
| -rw-r--r-- | test/incremental/remove_enum_field | 23 |
21 files changed, 327 insertions, 126 deletions
diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig index 4cfdf9b1e3..0382bbf971 100644 --- a/lib/compiler/std-docs.zig +++ b/lib/compiler/std-docs.zig @@ -4,6 +4,7 @@ const mem = std.mem; const io = std.io; const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const Cache = std.Build.Cache; fn usage() noreturn { io.getStdOut().writeAll( @@ -232,9 +233,18 @@ fn serveWasm( // Do the compilation every request, so that the user can edit the files // and see the changes without restarting the server. - const wasm_binary_path = try buildWasmBinary(arena, context, optimize_mode); + const wasm_base_path = try buildWasmBinary(arena, context, optimize_mode); + const bin_name = try std.zig.binNameAlloc(arena, .{ + .root_name = autodoc_root_name, + .target = std.zig.system.resolveTargetQuery(std.Build.parseTargetQuery(.{ + .arch_os_abi = autodoc_arch_os_abi, + .cpu_features = autodoc_cpu_features, + }) catch unreachable) catch unreachable, + .output_mode = .Exe, + }); // std.http.Server does not have a sendfile API yet. - const file_contents = try std.fs.cwd().readFileAlloc(gpa, wasm_binary_path, 10 * 1024 * 1024); + const bin_path = try wasm_base_path.join(arena, bin_name); + const file_contents = try bin_path.root_dir.handle.readFileAlloc(gpa, bin_path.sub_path, 10 * 1024 * 1024); defer gpa.free(file_contents); try request.respond(file_contents, .{ .extra_headers = &.{ @@ -244,37 +254,42 @@ fn serveWasm( }); } +const autodoc_root_name = "autodoc"; +const autodoc_arch_os_abi = "wasm32-freestanding"; +const autodoc_cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext"; + fn buildWasmBinary( arena: Allocator, context: *Context, optimize_mode: std.builtin.OptimizeMode, -) ![]const u8 { +) !Cache.Path { const gpa = context.gpa; var argv: std.ArrayListUnmanaged([]const u8) = .{}; try argv.appendSlice(arena, &.{ - context.zig_exe_path, - "build-exe", - "-fno-entry", - "-O", - @tagName(optimize_mode), - "-target", - "wasm32-freestanding", - "-mcpu", - "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext", - "--cache-dir", - context.global_cache_path, - "--global-cache-dir", - context.global_cache_path, - "--name", - "autodoc", - "-rdynamic", - "--dep", - "Walk", - try std.fmt.allocPrint(arena, "-Mroot={s}/docs/wasm/main.zig", .{context.zig_lib_directory}), - try std.fmt.allocPrint(arena, "-MWalk={s}/docs/wasm/Walk.zig", .{context.zig_lib_directory}), - "--listen=-", + context.zig_exe_path, // + "build-exe", // + "-fno-entry", // + "-O", @tagName(optimize_mode), // + "-target", autodoc_arch_os_abi, // + "-mcpu", autodoc_cpu_features, // + "--cache-dir", context.global_cache_path, // + "--global-cache-dir", context.global_cache_path, // + "--name", autodoc_root_name, // + "-rdynamic", // + "--dep", "Walk", // + try std.fmt.allocPrint( + arena, + "-Mroot={s}/docs/wasm/main.zig", + .{context.zig_lib_directory}, + ), + try std.fmt.allocPrint( + arena, + "-MWalk={s}/docs/wasm/Walk.zig", + .{context.zig_lib_directory}, + ), + "--listen=-", // }); var child = std.process.Child.init(argv.items, gpa); @@ -293,7 +308,7 @@ fn buildWasmBinary( try sendMessage(child.stdin.?, .exit); const Header = std.zig.Server.Message.Header; - var result: ?[]const u8 = null; + var result: ?Cache.Path = null; var result_error_bundle = std.zig.ErrorBundle.empty; const stdout = poller.fifo(.stdout); @@ -330,13 +345,19 @@ fn buildWasmBinary( .extra = extra_array, }; }, - .emit_bin_path => { - const EbpHdr = std.zig.Server.Message.EmitBinPath; - const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); - if (!ebp_hdr.flags.cache_hit) { + .emit_digest => { + const EmitDigest = std.zig.Server.Message.EmitDigest; + const emit_digest = @as(*align(1) const EmitDigest, @ptrCast(body)); + if (!emit_digest.flags.cache_hit) { std.log.info("source changes detected; rebuilt wasm component", .{}); } - result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); + const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len]; + result = .{ + .root_dir = Cache.Directory.cwd(), + .sub_path = try std.fs.path.join(arena, &.{ + context.global_cache_path, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*), + }), + }; }, else => {}, // ignore other messages } diff --git a/lib/libc/glibc/csu/init.c b/lib/libc/glibc/csu/init.c new file mode 100644 index 0000000000..3ee6148898 --- /dev/null +++ b/lib/libc/glibc/csu/init.c @@ -0,0 +1,23 @@ +/* Special startup support. + Copyright (C) 1997-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +/* Vestigial libio version number. Some code in libio checks whether + this symbol exists in the executable, but nothing looks at its + value anymore; the value it was historically set to has been + preserved out of an abundance of caution. */ +const int _IO_stdin_used = 0x20001; diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 82810bb02f..f8ab20cac1 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1808,6 +1808,7 @@ pub fn runAllowFail( child.stderr_behavior = stderr_behavior; child.env_map = &b.graph.env_map; + try Step.handleVerbose2(b, null, child.env_map, argv); try child.spawn(); const stdout = child.stdout.?.reader().readAllAlloc(b.allocator, max_output_size) catch { diff --git a/lib/std/Build/Fuzz/WebServer.zig b/lib/std/Build/Fuzz/WebServer.zig index fbf6b8dbce..26b25b83d9 100644 --- a/lib/std/Build/Fuzz/WebServer.zig +++ b/lib/std/Build/Fuzz/WebServer.zig @@ -304,13 +304,13 @@ fn buildWasmBinary( }; }, .emit_digest => { - const EbpHdr = std.zig.Server.Message.EmitDigest; - const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); + const EmitDigest = std.zig.Server.Message.EmitDigest; + const ebp_hdr = @as(*align(1) const EmitDigest, @ptrCast(body)); if (!ebp_hdr.flags.cache_hit) { log.info("source changes detected; rebuilt wasm component", .{}); } - const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; - result = Path{ + const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len]; + result = .{ .root_dir = ws.global_cache_directory, .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)), }; diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 346ab2c9b3..15dbd7faed 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -534,11 +534,11 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { } }, .emit_digest => { - const EbpHdr = std.zig.Server.Message.EmitDigest; - const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); - s.result_cached = ebp_hdr.flags.cache_hit; - const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; - result = Path{ + const EmitDigest = std.zig.Server.Message.EmitDigest; + const emit_digest = @as(*align(1) const EmitDigest, @ptrCast(body)); + s.result_cached = emit_digest.flags.cache_hit; + const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len]; + result = .{ .root_dir = b.cache_root, .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)), }; diff --git a/lib/std/Target/Query.zig b/lib/std/Target/Query.zig index 8871e360a5..4241e425ad 100644 --- a/lib/std/Target/Query.zig +++ b/lib/std/Target/Query.zig @@ -588,14 +588,7 @@ test parse { const text = try query.zigTriple(std.testing.allocator); defer std.testing.allocator.free(text); - var buf: [256]u8 = undefined; - const triple = std.fmt.bufPrint( - buf[0..], - "native-native-{s}.2.1.1", - .{@tagName(builtin.target.abi)}, - ) catch unreachable; - - try std.testing.expectEqualSlices(u8, triple, text); + try std.testing.expectEqualSlices(u8, "native-native-gnu.2.1.1", text); } { const query = try Query.parse(.{ diff --git a/lib/std/bounded_array.zig b/lib/std/bounded_array.zig index 85a175003d..b88ba5a907 100644 --- a/lib/std/bounded_array.zig +++ b/lib/std/bounded_array.zig @@ -39,16 +39,14 @@ pub fn BoundedArrayAligned( ) type { return struct { const Self = @This(); - const Len = std.math.IntFittingRange(0, buffer_capacity); - buffer: [buffer_capacity]T align(alignment) = undefined, - len: Len = 0, + len: usize = 0, /// Set the actual length of the slice. /// Returns error.Overflow if it exceeds the length of the backing array. pub fn init(len: usize) error{Overflow}!Self { if (len > buffer_capacity) return error.Overflow; - return Self{ .len = @intCast(len) }; + return Self{ .len = len }; } /// View the internal array as a slice whose size was previously set. @@ -69,7 +67,7 @@ pub fn BoundedArrayAligned( /// Does not initialize added items if any. pub fn resize(self: *Self, len: usize) error{Overflow}!void { if (len > buffer_capacity) return error.Overflow; - self.len = @intCast(len); + self.len = len; } /// Remove all elements from the slice. @@ -178,7 +176,7 @@ pub fn BoundedArrayAligned( /// This operation is O(N). pub fn insertSlice(self: *Self, i: usize, items: []const T) error{Overflow}!void { try self.ensureUnusedCapacity(items.len); - self.len = @intCast(self.len + items.len); + self.len += items.len; mem.copyBackwards(T, self.slice()[i + items.len .. self.len], self.constSlice()[i .. self.len - items.len]); @memcpy(self.slice()[i..][0..items.len], items); } @@ -208,7 +206,7 @@ pub fn BoundedArrayAligned( for (self.constSlice()[after_range..], 0..) |item, i| { self.slice()[after_subrange..][i] = item; } - self.len = @intCast(self.len - len + new_items.len); + self.len -= len - new_items.len; } } @@ -259,7 +257,7 @@ pub fn BoundedArrayAligned( /// enough to store the new items. pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void { const old_len = self.len; - self.len = @intCast(self.len + items.len); + self.len += items.len; @memcpy(self.slice()[old_len..][0..items.len], items); } @@ -275,8 +273,8 @@ pub fn BoundedArrayAligned( /// Asserts the capacity is enough. pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void { const old_len = self.len; - assert(self.len + n <= buffer_capacity); - self.len = @intCast(self.len + n); + self.len += n; + assert(self.len <= buffer_capacity); @memset(self.slice()[old_len..self.len], value); } @@ -406,18 +404,6 @@ test BoundedArray { try testing.expectEqualStrings(s, a.constSlice()); } -test "BoundedArray sizeOf" { - // Just sanity check size on one CPU - if (@import("builtin").cpu.arch != .x86_64) - return; - - try testing.expectEqual(@sizeOf(BoundedArray(u8, 3)), 4); - - // `len` is the minimum required size to hold the maximum capacity - try testing.expectEqual(@TypeOf(@as(BoundedArray(u8, 15), undefined).len), u4); - try testing.expectEqual(@TypeOf(@as(BoundedArray(u8, 16), undefined).len), u5); -} - test "BoundedArrayAligned" { var a = try BoundedArrayAligned(u8, 16, 4).init(0); try a.append(0); diff --git a/lib/std/c.zig b/lib/std/c.zig index d430cf6a83..611f707e15 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -9499,8 +9499,8 @@ pub const LC = enum(c_int) { pub extern "c" fn setlocale(category: LC, locale: ?[*:0]const u8) ?[*:0]const u8; -pub const getcontext = if (builtin.target.isAndroid()) -{} // android bionic libc does not implement getcontext +pub const getcontext = if (builtin.target.isAndroid() or builtin.target.os.tag == .openbsd) +{} // android bionic and openbsd libc does not implement getcontext else if (native_os == .linux and builtin.target.isMusl()) linux.getcontext else diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index ad37777e43..110393d91a 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -147,9 +147,72 @@ pub const ElfDynLib = struct { pub const Error = ElfDynLibError; + fn openPath(path: []const u8) !std.fs.Dir { + if (path.len == 0) return error.NotDir; + var parts = std.mem.tokenizeScalar(u8, path, '/'); + var parent = if (path[0] == '/') try std.fs.cwd().openDir("/", .{}) else std.fs.cwd(); + while (parts.next()) |part| { + const child = try parent.openDir(part, .{}); + parent.close(); + parent = child; + } + return parent; + } + + fn resolveFromSearchPath(search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t { + var paths = std.mem.tokenizeScalar(u8, search_path, delim); + while (paths.next()) |p| { + var dir = openPath(p) catch continue; + defer dir.close(); + const fd = posix.openat(dir.fd, file_name, .{ + .ACCMODE = .RDONLY, + .CLOEXEC = true, + }, 0) catch continue; + return fd; + } + return null; + } + + fn resolveFromParent(dir_path: []const u8, file_name: []const u8) ?posix.fd_t { + var dir = std.fs.cwd().openDir(dir_path, .{}) catch return null; + defer dir.close(); + return posix.openat(dir.fd, file_name, .{ + .ACCMODE = .RDONLY, + .CLOEXEC = true, + }, 0) catch null; + } + + // This implements enough to be able to load system libraries in general + // Places where it differs from dlopen: + // - DT_RPATH of the calling binary is not used as a search path + // - DT_RUNPATH of the calling binary is not used as a search path + // - /etc/ld.so.cache is not read + fn resolveFromName(path_or_name: []const u8) !posix.fd_t { + // If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname + if (std.mem.indexOfScalarPos(u8, path_or_name, 0, '/')) |_| { + return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); + } + + // Only read LD_LIBRARY_PATH if the binary is not setuid/setgid + if (std.os.linux.geteuid() == std.os.linux.getuid() and + std.os.linux.getegid() == std.os.linux.getgid()) + { + if (posix.getenvZ("LD_LIBRARY_PATH")) |ld_library_path| { + if (resolveFromSearchPath(ld_library_path, path_or_name, ':')) |fd| { + return fd; + } + } + } + + // Lastly the directories /lib and /usr/lib are searched (in this exact order) + if (resolveFromParent("/lib", path_or_name)) |fd| return fd; + if (resolveFromParent("/usr/lib", path_or_name)) |fd| return fd; + return error.FileNotFound; + } + /// Trusts the file. Malicious file will be able to execute arbitrary code. pub fn open(path: []const u8) Error!ElfDynLib { - const fd = try posix.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); + const fd = try resolveFromName(path); defer posix.close(fd); const stat = try posix.fstat(fd); diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index f596c52322..b0810f81a5 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -745,6 +745,7 @@ test "directory operations on files" { test "file operations on directories" { // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759 if (native_os == .freebsd) return error.SkipZigTest; + if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/20747 try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index c35059663e..a29b381c40 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -630,12 +630,13 @@ pub fn futex2_waitv( nr_futexes, flags, @intFromPtr(timeout), - @bitCast(@as(isize, clockid)), + @bitCast(@as(isize, @intFromEnum(clockid))), ); } /// Wait on a futex. -/// Identical to `FUTEX.WAIT`, except it is part of the futex2 family of calls. +/// Identical to the traditional `FUTEX.FUTEX_WAIT_BITSET` op, except it is part of the +/// futex2 familiy of calls. pub fn futex2_wait( /// Address of the futex to wait on. uaddr: *const anyopaque, @@ -646,7 +647,7 @@ pub fn futex2_wait( /// `FUTEX2` flags. flags: u32, /// Optional absolute timeout. - timeout: *const timespec, + timeout: ?*const timespec, /// Clock to be used for the timeout, realtime or monotonic. clockid: clockid_t, ) usize { @@ -657,15 +658,16 @@ pub fn futex2_wait( mask, flags, @intFromPtr(timeout), - @bitCast(@as(isize, clockid)), + @bitCast(@as(isize, @intFromEnum(clockid))), ); } /// Wake a number of futexes. -/// Identical to `FUTEX.WAKE`, except it is part of the futex2 family of calls. +/// Identical to the traditional `FUTEX.FUTEX_WAIT_BITSET` op, except it is part of the +/// futex2 family of calls. pub fn futex2_wake( /// Address of the futex(es) to wake. - uaddr: [*]const anyopaque, + uaddr: *const anyopaque, /// Bitmask mask: usize, /// Number of the futexes to wake. @@ -1683,7 +1685,7 @@ pub fn sigaddset(set: *sigset_t, sig: u6) void { pub fn sigismember(set: *const sigset_t, sig: u6) bool { const s = sig - 1; - return ((set.*)[@as(usize, @intCast(s)) / usize_bits] & (@as(usize, @intCast(1)) << (s & (usize_bits - 1)))) != 0; + return ((set.*)[@as(usize, @intCast(s)) / usize_bits] & (@as(usize, @intCast(1)) << @intCast(s & (usize_bits - 1)))) != 0; } pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { @@ -1740,31 +1742,31 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize var next_unsent: usize = 0; for (msgvec[0..kvlen], 0..) |*msg, i| { var size: i32 = 0; - const msg_iovlen = @as(usize, @intCast(msg.msg_hdr.msg_iovlen)); // kernel side this is treated as unsigned - for (msg.msg_hdr.msg_iov[0..msg_iovlen]) |iov| { + const msg_iovlen = @as(usize, @intCast(msg.hdr.iovlen)); // kernel side this is treated as unsigned + for (msg.hdr.iov[0..msg_iovlen]) |iov| { if (iov.len > std.math.maxInt(i32) or @addWithOverflow(size, @as(i32, @intCast(iov.len)))[1] != 0) { // batch-send all messages up to the current message if (next_unsent < i) { const batch_size = i - next_unsent; const r = syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(&msgvec[next_unsent]), batch_size, flags); - if (E.init(r) != 0) return next_unsent; + if (E.init(r) != .SUCCESS) return next_unsent; if (r < batch_size) return next_unsent + r; } // send current message as own packet - const r = sendmsg(fd, &msg.msg_hdr, flags); - if (E.init(r) != 0) return r; + const r = sendmsg(fd, &msg.hdr, flags); + if (E.init(r) != .SUCCESS) return r; // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe. - msg.msg_len = @as(u32, @intCast(r)); + msg.len = @as(u32, @intCast(r)); next_unsent = i + 1; break; } - size += iov.len; + size += @intCast(iov.len); } } if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG.EOR) const batch_size = kvlen - next_unsent; const r = syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(&msgvec[next_unsent]), batch_size, flags); - if (E.init(r) != 0) return r; + if (E.init(r) != .SUCCESS) return r; return next_unsent + r; } return kvlen; @@ -5095,7 +5097,7 @@ pub const epoll_event = extern struct { pub const VFS_CAP_REVISION_MASK = 0xFF000000; pub const VFS_CAP_REVISION_SHIFT = 24; -pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK; +pub const VFS_CAP_FLAGS_MASK = ~@as(u32, VFS_CAP_REVISION_MASK); pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001; pub const VFS_CAP_REVISION_1 = 0x01000000; @@ -5113,7 +5115,7 @@ pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2; pub const vfs_cap_data = extern struct { //all of these are mandated as little endian //when on disk. - const Data = struct { + const Data = extern struct { permitted: u32, inheritable: u32, }; diff --git a/lib/std/os/linux/bpf/btf.zig b/lib/std/os/linux/bpf/btf.zig index 39d25014da..3988fce349 100644 --- a/lib/std/os/linux/bpf/btf.zig +++ b/lib/std/os/linux/bpf/btf.zig @@ -83,13 +83,14 @@ pub const Kind = enum(u5) { /// int kind is followed by this struct pub const IntInfo = packed struct(u32) { bits: u8, - unused: u8, + reserved_1: u8, offset: u8, encoding: enum(u4) { signed = 1 << 0, char = 1 << 1, boolean = 1 << 2, }, + reserved_2: u4, }; test "IntInfo is 32 bits" { diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 8562d4be8e..a8ebec47a5 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -125,6 +125,23 @@ test "fadvise" { try expectEqual(@as(usize, 0), ret); } +test "sigset_t" { + var sigset = linux.empty_sigset; + + try expectEqual(linux.sigismember(&sigset, linux.SIG.USR1), false); + try expectEqual(linux.sigismember(&sigset, linux.SIG.USR2), false); + + linux.sigaddset(&sigset, linux.SIG.USR1); + + try expectEqual(linux.sigismember(&sigset, linux.SIG.USR1), true); + try expectEqual(linux.sigismember(&sigset, linux.SIG.USR2), false); + + linux.sigaddset(&sigset, linux.SIG.USR2); + + try expectEqual(linux.sigismember(&sigset, linux.SIG.USR1), true); + try expectEqual(linux.sigismember(&sigset, linux.SIG.USR2), true); +} + test { _ = linux.IoUring; } diff --git a/lib/std/posix.zig b/lib/std/posix.zig index bc270395cc..665ea17788 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -4746,11 +4746,13 @@ pub fn munmap(memory: []align(mem.page_size) const u8) void { pub const MSyncError = error{ UnmappedMemory, + PermissionDenied, } || UnexpectedError; pub fn msync(memory: []align(mem.page_size) u8, flags: i32) MSyncError!void { switch (errno(system.msync(memory.ptr, memory.len, flags))) { .SUCCESS => return, + .PERM => return error.PermissionDenied, .NOMEM => return error.UnmappedMemory, // Unsuccessful, provided pointer does not point mapped memory .INVAL => unreachable, // Invalid parameters. else => unreachable, @@ -7106,6 +7108,7 @@ pub const MadviseError = error{ pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void { switch (errno(system.madvise(ptr, length, advice))) { .SUCCESS => return, + .PERM => return error.PermissionDenied, .ACCES => return error.AccessDenied, .AGAIN => return error.SystemResources, .BADF => unreachable, // The map exists, but the area maps something that isn't a file. diff --git a/lib/std/valgrind.zig b/lib/std/valgrind.zig index 8590302e9a..241f4c732a 100644 --- a/lib/std/valgrind.zig +++ b/lib/std/valgrind.zig @@ -278,8 +278,10 @@ pub fn monitorCommand(command: [*]u8) bool { pub const memcheck = @import("valgrind/memcheck.zig"); pub const callgrind = @import("valgrind/callgrind.zig"); +pub const cachegrind = @import("valgrind/cachegrind.zig"); test { _ = memcheck; _ = callgrind; + _ = cachegrind; } diff --git a/lib/std/valgrind/cachegrind.zig b/lib/std/valgrind/cachegrind.zig new file mode 100644 index 0000000000..b6aef5391b --- /dev/null +++ b/lib/std/valgrind/cachegrind.zig @@ -0,0 +1,29 @@ +const std = @import("../std.zig"); +const valgrind = std.valgrind; + +pub const ClientRequest = enum(usize) { + StartInstrumentation = valgrind.ToolBase("CG".*), + StopInstrumentation, +}; + +fn doClientRequestExpr(default: usize, request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize { + return valgrind.doClientRequest(default, @as(usize, @intCast(@intFromEnum(request))), a1, a2, a3, a4, a5); +} + +fn doClientRequestStmt(request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) void { + _ = doClientRequestExpr(0, request, a1, a2, a3, a4, a5); +} + +/// Start Cachegrind instrumentation if not already enabled. Use this in +/// combination with `std.valgrind.cachegrind.stopInstrumentation` and +/// `--instr-at-start` to measure only part of a client program's execution. +pub fn startInstrumentation() void { + doClientRequestStmt(.StartInstrumentation, 0, 0, 0, 0, 0); +} + +/// Stop Cachegrind instrumentation if not already disabled. Use this in +/// combination with `std.valgrind.cachegrind.startInstrumentation` and +/// `--instr-at-start` to measure only part of a client program's execution. +pub fn stopInstrumentation() void { + doClientRequestStmt(.StopInstrumentation, 0, 0, 0, 0, 0); +} diff --git a/lib/std/valgrind/callgrind.zig b/lib/std/valgrind/callgrind.zig index 716573e7b0..6718f40643 100644 --- a/lib/std/valgrind/callgrind.zig +++ b/lib/std/valgrind/callgrind.zig @@ -1,7 +1,7 @@ const std = @import("../std.zig"); const valgrind = std.valgrind; -pub const CallgrindClientRequest = enum(usize) { +pub const ClientRequest = enum(usize) { DumpStats = valgrind.ToolBase("CT".*), ZeroStats, ToggleCollect, @@ -10,17 +10,19 @@ pub const CallgrindClientRequest = enum(usize) { StopInstrumentation, }; -fn doCallgrindClientRequestExpr(default: usize, request: CallgrindClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize { +pub const CallgrindClientRequest = @compileError("std.valgrind.callgrind.CallgrindClientRequest renamed to std.valgrind.callgrind.ClientRequest"); + +fn doClientRequestExpr(default: usize, request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize { return valgrind.doClientRequest(default, @as(usize, @intCast(@intFromEnum(request))), a1, a2, a3, a4, a5); } -fn doCallgrindClientRequestStmt(request: CallgrindClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) void { - _ = doCallgrindClientRequestExpr(0, request, a1, a2, a3, a4, a5); +fn doClientRequestStmt(request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) void { + _ = doClientRequestExpr(0, request, a1, a2, a3, a4, a5); } /// Dump current state of cost centers, and zero them afterwards pub fn dumpStats() void { - doCallgrindClientRequestStmt(.DumpStats, 0, 0, 0, 0, 0); + doClientRequestStmt(.DumpStats, 0, 0, 0, 0, 0); } /// Dump current state of cost centers, and zero them afterwards. @@ -28,12 +30,12 @@ pub fn dumpStats() void { /// the dump. This string is written as a description field into the /// profile data dump. pub fn dumpStatsAt(pos_str: [*:0]const u8) void { - doCallgrindClientRequestStmt(.DumpStatsAt, @intFromPtr(pos_str), 0, 0, 0, 0); + doClientRequestStmt(.DumpStatsAt, @intFromPtr(pos_str), 0, 0, 0, 0); } /// Zero cost centers pub fn zeroStats() void { - doCallgrindClientRequestStmt(.ZeroStats, 0, 0, 0, 0, 0); + doClientRequestStmt(.ZeroStats, 0, 0, 0, 0, 0); } /// Toggles collection state. @@ -41,7 +43,7 @@ pub fn zeroStats() void { /// should be noted or if they are to be ignored. Events are noted /// by increment of counters in a cost center pub fn toggleCollect() void { - doCallgrindClientRequestStmt(.ToggleCollect, 0, 0, 0, 0, 0); + doClientRequestStmt(.ToggleCollect, 0, 0, 0, 0, 0); } /// Start full callgrind instrumentation if not already switched on. @@ -49,7 +51,7 @@ pub fn toggleCollect() void { /// this will lead to an artificial cache warmup phase afterwards with /// cache misses which would not have happened in reality. pub fn startInstrumentation() void { - doCallgrindClientRequestStmt(.StartInstrumentation, 0, 0, 0, 0, 0); + doClientRequestStmt(.StartInstrumentation, 0, 0, 0, 0, 0); } /// Stop full callgrind instrumentation if not already switched off. @@ -60,5 +62,5 @@ pub fn startInstrumentation() void { /// To start Callgrind in this mode to ignore the setup phase, use /// the option "--instr-atstart=no". pub fn stopInstrumentation() void { - doCallgrindClientRequestStmt(.StopInstrumentation, 0, 0, 0, 0, 0); + doClientRequestStmt(.StopInstrumentation, 0, 0, 0, 0, 0); } diff --git a/lib/std/valgrind/memcheck.zig b/lib/std/valgrind/memcheck.zig index e66943ee2c..950026afc5 100644 --- a/lib/std/valgrind/memcheck.zig +++ b/lib/std/valgrind/memcheck.zig @@ -2,7 +2,7 @@ const std = @import("../std.zig"); const testing = std.testing; const valgrind = std.valgrind; -pub const MemCheckClientRequest = enum(usize) { +pub const ClientRequest = enum(usize) { MakeMemNoAccess = valgrind.ToolBase("MC".*), MakeMemUndefined, MakeMemDefined, @@ -20,29 +20,31 @@ pub const MemCheckClientRequest = enum(usize) { DisableAddrErrorReportingInRange, }; -fn doMemCheckClientRequestExpr(default: usize, request: MemCheckClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize { +pub const MemCheckClientRequest = @compileError("std.valgrind.memcheck.MemCheckClientRequest renamed to std.valgrind.memcheck.ClientRequest"); + +fn doClientRequestExpr(default: usize, request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize { return valgrind.doClientRequest(default, @as(usize, @intCast(@intFromEnum(request))), a1, a2, a3, a4, a5); } -fn doMemCheckClientRequestStmt(request: MemCheckClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) void { - _ = doMemCheckClientRequestExpr(0, request, a1, a2, a3, a4, a5); +fn doClientRequestStmt(request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) void { + _ = doClientRequestExpr(0, request, a1, a2, a3, a4, a5); } /// Mark memory at qzz.ptr as unaddressable for qzz.len bytes. pub fn makeMemNoAccess(qzz: []const u8) void { - _ = doMemCheckClientRequestExpr(0, // default return + _ = doClientRequestExpr(0, // default return .MakeMemNoAccess, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0); } /// Mark memory at qzz.ptr as addressable but undefined for qzz.len bytes. pub fn makeMemUndefined(qzz: []const u8) void { - _ = doMemCheckClientRequestExpr(0, // default return + _ = doClientRequestExpr(0, // default return .MakeMemUndefined, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0); } /// Mark memory at qzz.ptr as addressable and defined or qzz.len bytes. pub fn makeMemDefined(qzz: []const u8) void { - _ = doMemCheckClientRequestExpr(0, // default return + _ = doClientRequestExpr(0, // default return .MakeMemDefined, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0); } @@ -50,7 +52,7 @@ pub fn makeMemDefined(qzz: []const u8) void { /// not altered: bytes which are addressable are marked as defined, /// but those which are not addressable are left unchanged. pub fn makeMemDefinedIfAddressable(qzz: []const u8) void { - _ = doMemCheckClientRequestExpr(0, // default return + _ = doClientRequestExpr(0, // default return .MakeMemDefinedIfAddressable, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0); } @@ -59,14 +61,14 @@ pub fn makeMemDefinedIfAddressable(qzz: []const u8) void { /// within the specified memory range. Has no other effect on the /// properties of the memory range. pub fn createBlock(qzz: []const u8, desc: [*:0]const u8) usize { - return doMemCheckClientRequestExpr(0, // default return + return doClientRequestExpr(0, // default return .CreateBlock, @intFromPtr(qzz.ptr), qzz.len, @intFromPtr(desc), 0, 0); } /// Discard a block-description-handle. Returns 1 for an /// invalid handle, 0 for a valid handle. pub fn discard(blkindex: usize) bool { - return doMemCheckClientRequestExpr(0, // default return + return doClientRequestExpr(0, // default return .Discard, 0, blkindex, 0, 0, 0) != 0; } @@ -75,7 +77,7 @@ pub fn discard(blkindex: usize) bool { /// error message and returns the address of the first offending byte. /// Otherwise it returns zero. pub fn checkMemIsAddressable(qzz: []const u8) usize { - return doMemCheckClientRequestExpr(0, .CheckMemIsAddressable, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0); + return doClientRequestExpr(0, .CheckMemIsAddressable, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0); } /// Check that memory at qzz.ptr is addressable and defined for @@ -83,31 +85,31 @@ pub fn checkMemIsAddressable(qzz: []const u8) usize { /// established, Valgrind prints an error message and returns the /// address of the first offending byte. Otherwise it returns zero. pub fn checkMemIsDefined(qzz: []const u8) usize { - return doMemCheckClientRequestExpr(0, .CheckMemIsDefined, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0); + return doClientRequestExpr(0, .CheckMemIsDefined, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0); } /// Do a full memory leak check (like --leak-check=full) mid-execution. pub fn doLeakCheck() void { - doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 0, 0, 0, 0, 0); + doClientRequestStmt(.DO_LEAK_CHECK, 0, 0, 0, 0, 0); } /// Same as doLeakCheck() but only showing the entries for /// which there was an increase in leaked bytes or leaked nr of blocks /// since the previous leak search. pub fn doAddedLeakCheck() void { - doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 0, 1, 0, 0, 0); + doClientRequestStmt(.DO_LEAK_CHECK, 0, 1, 0, 0, 0); } /// Same as doAddedLeakCheck() but showing entries with /// increased or decreased leaked bytes/blocks since previous leak /// search. pub fn doChangedLeakCheck() void { - doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 0, 2, 0, 0, 0); + doClientRequestStmt(.DO_LEAK_CHECK, 0, 2, 0, 0, 0); } /// Do a summary memory leak check (like --leak-check=summary) mid-execution. pub fn doQuickLeakCheck() void { - doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 1, 0, 0, 0, 0); + doClientRequestStmt(.DO_LEAK_CHECK, 1, 0, 0, 0, 0); } /// Return number of leaked, dubious, reachable and suppressed bytes found by @@ -126,7 +128,7 @@ pub fn countLeaks() CountResult { .reachable = 0, .suppressed = 0, }; - doMemCheckClientRequestStmt( + doClientRequestStmt( .CountLeaks, @intFromPtr(&res.leaked), @intFromPtr(&res.dubious), @@ -156,7 +158,7 @@ pub fn countLeakBlocks() CountResult { .reachable = 0, .suppressed = 0, }; - doMemCheckClientRequestStmt( + doClientRequestStmt( .CountLeakBlocks, @intFromPtr(&res.leaked), @intFromPtr(&res.dubious), @@ -189,7 +191,7 @@ test countLeakBlocks { /// impossible to segfault your system by using this call. pub fn getVbits(zza: []u8, zzvbits: []u8) u2 { std.debug.assert(zzvbits.len >= zza.len / 8); - return @as(u2, @intCast(doMemCheckClientRequestExpr(0, .GetVbits, @intFromPtr(zza.ptr), @intFromPtr(zzvbits), zza.len, 0, 0))); + return @as(u2, @intCast(doClientRequestExpr(0, .GetVbits, @intFromPtr(zza.ptr), @intFromPtr(zzvbits), zza.len, 0, 0))); } /// Set the validity data for addresses zza, copying it @@ -202,17 +204,17 @@ pub fn getVbits(zza: []u8, zzvbits: []u8) u2 { /// impossible to segfault your system by using this call. pub fn setVbits(zzvbits: []u8, zza: []u8) u2 { std.debug.assert(zzvbits.len >= zza.len / 8); - return @as(u2, @intCast(doMemCheckClientRequestExpr(0, .SetVbits, @intFromPtr(zza.ptr), @intFromPtr(zzvbits), zza.len, 0, 0))); + return @as(u2, @intCast(doClientRequestExpr(0, .SetVbits, @intFromPtr(zza.ptr), @intFromPtr(zzvbits), zza.len, 0, 0))); } /// Disable and re-enable reporting of addressing errors in the /// specified address range. pub fn disableAddrErrorReportingInRange(qzz: []u8) usize { - return doMemCheckClientRequestExpr(0, // default return + return doClientRequestExpr(0, // default return .DisableAddrErrorReportingInRange, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0); } pub fn enableAddrErrorReportingInRange(qzz: []u8) usize { - return doMemCheckClientRequestExpr(0, // default return + return doClientRequestExpr(0, // default return .EnableAddrErrorReportingInRange, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0); } diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 291518f5f0..01063ab2ce 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -3378,7 +3378,6 @@ fn recreateStructType( } else 0; if (captures_len != key.captures.owned.len) return error.AnalysisFail; - if (fields_len != struct_obj.field_types.len) return error.AnalysisFail; // The old type will be unused, so drop its dependency information. ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = struct_obj.cau.unwrap().? })); @@ -3466,7 +3465,6 @@ fn recreateUnionType( } else 0; if (captures_len != key.captures.owned.len) return error.AnalysisFail; - if (fields_len != union_obj.field_types.len) return error.AnalysisFail; // The old type will be unused, so drop its dependency information. ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = union_obj.cau })); @@ -3577,7 +3575,6 @@ fn recreateEnumType( } else 0; if (captures_len != key.captures.owned.len) return error.AnalysisFail; - if (fields_len != enum_obj.names.len) return error.AnalysisFail; extra_index += captures_len; extra_index += decls_len; diff --git a/src/glibc.zig b/src/glibc.zig index 57894f5921..4689805391 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -286,7 +286,11 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progre .owner = undefined, }; }; - var files = [_]Compilation.CSourceFile{ start_o, abi_note_o }; + const init_o: Compilation.CSourceFile = .{ + .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "init.c"), + .owner = undefined, + }; + var files = [_]Compilation.CSourceFile{ start_o, abi_note_o, init_o }; return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &files); }, .libc_nonshared_a => { @@ -682,6 +686,12 @@ pub const BuiltSharedObjects = struct { const all_map_basename = "all.map"; +fn wordDirective(target: std.Target) []const u8 { + // Based on its description in the GNU `as` manual, you might assume that `.word` is sized + // according to the target word size. But no; that would just make too much sense. + return if (target.ptrBitWidth() == 64) ".quad" else ".long"; +} + pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -923,6 +933,31 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi try stubs_asm.appendSlice(".data\n"); + // For some targets, the real `libc.so.6` will contain a weak reference to `_IO_stdin_used`, + // making the linker put the symbol in the dynamic symbol table. We likewise need to emit a + // reference to it here for that effect, or it will not show up, which in turn will cause + // the real glibc to think that the program was built against an ancient `FILE` structure + // (pre-glibc 2.1). + // + // Note that glibc only compiles in the legacy compatibility code for some targets; it + // depends on what is defined in the `shlib-versions` file for the particular architecture + // and ABI. Those files are preprocessed by 2 separate tools during the glibc build to get + // the final `abi-versions.h`, so it would be quite brittle to try to condition our emission + // of the `_IO_stdin_used` reference in the exact same way. The only downside of emitting + // the reference unconditionally is that it ends up being unused for newer targets; it + // otherwise has no negative effect. + // + // glibc uses a weak reference because it has to work with programs compiled against pre-2.1 + // versions where the symbol didn't exist. We only care about modern glibc versions, so use + // a strong reference. + if (std.mem.eql(u8, lib.name, "c")) { + try stubs_asm.writer().print( + \\.globl _IO_stdin_used + \\{s} _IO_stdin_used + \\ + , .{wordDirective(target)}); + } + const obj_inclusions_len = mem.readInt(u16, metadata.inclusions[inc_i..][0..2], .little); inc_i += 2; diff --git a/test/incremental/remove_enum_field b/test/incremental/remove_enum_field new file mode 100644 index 0000000000..64193bb25b --- /dev/null +++ b/test/incremental/remove_enum_field @@ -0,0 +1,23 @@ +#target=x86_64-linux +#update=initial version +#file=main.zig +const MyEnum = enum(u8) { + foo = 1, + bar = 2, +}; +pub fn main() !void { + try std.io.getStdOut().writer().print("{}\n", .{@intFromEnum(MyEnum.foo)}); +} +const std = @import("std"); +#expect_stdout="1\n" +#update=remove enum field +#file=main.zig +const MyEnum = enum(u8) { + //foo = 1, + bar = 2, +}; +pub fn main() !void { + try std.io.getStdOut().writer().print("{}\n", .{@intFromEnum(MyEnum.foo)}); +} +const std = @import("std"); +#expect_error=ignored |
