diff --git a/lib/std/Io/RwLock.zig b/lib/std/Io/RwLock.zig index 7a445033db6b50da7ab31ee8a6cb7b7f6c3ed438..de9bf86f366314f64ed45877c675dd2cbd9eee4a 100644 --- a/lib/std/Io/RwLock.zig +++ b/lib/std/Io/RwLock.zig @@ -284,8 +284,6 @@ test "concurrent access" { } test "lock canceling" { - if (builtin.cpu.arch.isSPARC() and builtin.os.tag == .linux) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35347 - const io = testing.io; var rl: Io.RwLock = .init; diff --git a/lib/std/Io/Threaded/test.zig b/lib/std/Io/Threaded/test.zig index bb49dbc1b5fbc2e502c0368756d838c39a2e0258..392323de9b15c8674b4af0ea7aea21a5f49d6faf 100644 --- a/lib/std/Io/Threaded/test.zig +++ b/lib/std/Io/Threaded/test.zig @@ -149,8 +149,6 @@ test "async with array return type" { } test "cancel blocked read from pipe" { - if (builtin.cpu.arch.isSPARC() and builtin.os.tag == .linux) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35347 - const global = struct { fn readFromPipe(io: Io, pipe: Io.File) !void { var buf: [1]u8 = undefined; diff --git a/lib/std/Io/net/test.zig b/lib/std/Io/net/test.zig index 308dc1a32670e207e72511d0128b634972050e6a..12fa0a846c0b6576e0f0b593bc51290e6f086b14 100644 --- a/lib/std/Io/net/test.zig +++ b/lib/std/Io/net/test.zig @@ -356,8 +356,6 @@ test "decompress compressed DNS name" { } test "cancel accept" { - if (builtin.cpu.arch.isSPARC() and builtin.os.tag == .linux) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35347 - const io = testing.io; const localhost: net.IpAddress = .{ .ip4 = .loopback(0) }; diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index 3d9523da7675c01e8a780e139e886231f1e04617..54d79fceec8e6adebbc4e35867bcea93aa318bf2 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -232,8 +232,6 @@ fn count(a: usize, b: usize, result: *usize) void { } test "Group.cancel" { - if (builtin.cpu.arch.isSPARC() and builtin.os.tag == .linux) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35347 - const global = struct { fn sleep(io: Io, result: *usize) Io.Cancelable!void { defer result.* = 1; @@ -326,8 +324,6 @@ test "Group materializes error.Cancel" { } test "Group task receives cancelation unknowingly" { - if (builtin.cpu.arch.isSPARC() and builtin.os.tag == .linux) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35347 - const S = struct { io: Io, err: ?Io.Cancelable!void, diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 6eb76719a55d2787d04f124e58fa8829cbc7e8e9..05125adbe852a3586b4d77fa2d0f8f7dd4d4bb94 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -1146,6 +1146,13 @@ const LinuxThreadImpl = struct { parent_tid: i32 = undefined, mapped: []align(std.heap.page_size_min) u8, + // On SPARC, the kernel needs to be able to restore the current register window from the + // stack when returning from a syscall. That presents a bit of a problem in `freeAndExit` + // since we're deallocating the stack! The good news is that, since we do not care about + // the contents of the incoming and local registers at that point, we can just tell the + // kernel that our stack is this undefined global buffer. + var sparc_exit_stack: [192]u8 align(16) = undefined; + /// Calls `munmap(mapped.ptr, mapped.len)` then `exit(1)` without touching the stack (which lives in `mapped.ptr`). /// Ported over from musl libc's pthread detached implementation: /// https://github.com/ifduyue/musl/search?q=__unmapself @@ -1365,51 +1372,39 @@ const LinuxThreadImpl = struct { [len] "{r5}" (self.mapped.len), ), .sparc => asm volatile ( - \\ # See sparc64 comments below. - \\ 1: - \\ cmp %%fp, 0 - \\ beq 2f - \\ nop - \\ ba 1b - \\ restore - \\ 2: - \\ mov %%g1, %%o0 // ptr - \\ mov %%g2, %%o1 // len - \\ mov 73, %%g1 // SYS_munmap - \\ t 0x3 // ST_FLUSH_WINDOWS - \\ t 0x10 - \\ mov 1, %%g1 // SYS_exit - \\ mov 0, %%o0 - \\ t 0x10 + \\ // See sparc64 comments below. + \\ t 0x3 // ST_FLUSH_WINDOWS + \\ mov %%g3, %%sp + \\ mov %%g1, %%o0 + \\ mov %%g2, %%o1 + \\ mov 73, %%g1 // SYS_munmap + \\ t 0x10 + \\ mov 1, %%g1 // SYS_exit + \\ mov 0, %%o0 + \\ t 0x10 : : [ptr] "{g1}" (@intFromPtr(self.mapped.ptr)), [len] "{g2}" (self.mapped.len), + [stack] "{g3}" (&sparc_exit_stack), : .{ .memory = true }), .sparc64 => asm volatile ( - \\ # SPARCs really don't like it when active stack frames - \\ # is unmapped (it will result in a segfault), so we - \\ # force-deactivate it by running `restore` until - \\ # all frames are cleared. - \\ 1: - \\ cmp %%fp, 0 - \\ beq 2f - \\ nop - \\ ba 1b - \\ restore - \\ 2: - \\ mov %%g1, %%o0 // ptr - \\ mov %%g2, %%o1 // len - \\ mov 73, %%g1 // SYS_munmap - \\ # Flush register window contents to prevent background - \\ # memory access before unmapping the stack. - \\ flushw - \\ t 0x6d - \\ mov 1, %%g1 // SYS_exit - \\ mov 0, %%o0 - \\ t 0x6d + \\ // Ensure that the kernel only has to flush the current register window. + \\ flushw + \\ // Set up a fake stack for the syscall to restore l/i registers from. Local + \\ // and incoming registers must be treated as effectively garbage past this + \\ // instruction! + \\ sub %%g3, 2047, %%sp + \\ mov %%g1, %%o0 + \\ mov %%g2, %%o1 + \\ mov 73, %%g1 // SYS_munmap + \\ t 0x6d + \\ mov 1, %%g1 // SYS_exit + \\ mov 0, %%o0 + \\ t 0x6d : : [ptr] "{g1}" (@intFromPtr(self.mapped.ptr)), [len] "{g2}" (self.mapped.len), + [stack] "{g3}" (&sparc_exit_stack), : .{ .memory = true }), .loongarch32, .loongarch64 => asm volatile ( \\ ori $a7, $zero, 215 # SYS_munmap diff --git a/lib/std/heap/PageAllocator.zig b/lib/std/heap/PageAllocator.zig index db736036b3f416d34a934b7185ac4a953affa303..1adac776bb9ac7a1d145bc7a5e464eedefd84f29 100644 --- a/lib/std/heap/PageAllocator.zig +++ b/lib/std/heap/PageAllocator.zig @@ -24,6 +24,7 @@ pub const vtable: Allocator.VTable = .{ /// that don't provide a hint (for security reasons, but it serves our needs /// too). const enable_hints = switch (builtin.target.os.tag) { + .linux => !builtin.target.cpu.arch.isSPARC(), // https://bugzilla.kernel.org/show_bug.cgi?id=221820 .openbsd => false, else => true, }; diff --git a/lib/std/os/linux/sparc.zig b/lib/std/os/linux/sparc.zig index 4a2e7188a6b2a9cf582edddf204ce8fc61d4fb95..c8addd36c45bfc08aff71672c2aeef42bb948f06 100644 --- a/lib/std/os/linux/sparc.zig +++ b/lib/std/os/linux/sparc.zig @@ -260,13 +260,16 @@ pub fn clone() callconv(.naked) u32 { pub const restore = restore_rt; -// Need to use C ABI here instead of naked -// to prevent an infinite loop when calling rt_sigreturn. -pub fn restore_rt() callconv(.c) void { - return asm volatile ("t 0x10" +pub fn restore_rt() callconv(.naked) noreturn { + asm volatile ( + \\ nop + \\ nop + ); + asm volatile ( + \\ t 0x10 : : [number] "{g1}" (@backingInt(SYS.rt_sigreturn)), - : .{ .memory = true, .xcc = true, .o0 = true, .o1 = true, .o2 = true, .o3 = true, .o4 = true, .o5 = true, .o7 = true }); + ); } pub const VDSO = struct { diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index f7e859cc72abfe37dfee1f5ab5927979c2625615..955ae477c3f81d1f190ad7cb440108747cff9b56 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -259,13 +259,16 @@ pub fn clone() callconv(.naked) u64 { pub const restore = restore_rt; -// Need to use C ABI here instead of naked -// to prevent an infinite loop when calling rt_sigreturn. -pub fn restore_rt() callconv(.c) void { - return asm volatile ("t 0x6d" +pub fn restore_rt() callconv(.naked) noreturn { + asm volatile ( + \\ nop + \\ nop + ); + asm volatile ( + \\ t 0x6d : : [number] "{g1}" (@backingInt(SYS.rt_sigreturn)), - : .{ .memory = true, .xcc = true, .o0 = true, .o1 = true, .o2 = true, .o3 = true, .o4 = true, .o5 = true, .o7 = true }); + ); } pub const VDSO = struct {