diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5c6b006..0c0d4d0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -74,43 +74,6 @@ jobs: command: test args: --target=${{ matrix.extra_target }} --manifest-path=${{ matrix.manifest }} ${{ matrix.mode }} --examples -- --nocapture - clang-cl-test: - name: Test ${{ matrix.manifest }} on ${{ matrix.rust_target }} with ${{ matrix.clang_cl }} - runs-on: windows-latest - strategy: - fail-fast: false - matrix: - manifest: ["psm/Cargo.toml", "Cargo.toml"] - rust_target: - - x86_64-pc-windows-msvc - - i686-pc-windows-msvc - include: - - rust_target: x86_64-pc-windows-msvc - clang_cl: C:/msys64/mingw64/bin/clang-cl.exe - package: mingw-w64-x86_64-clang - - rust_target: i686-pc-windows-msvc - clang_cl: C:/msys64/mingw32/bin/clang-cl.exe - package: mingw-w64-i686-clang - steps: - - uses: actions/checkout@v4 - - uses: msys2/setup-msys2@v2 - with: - release: false - install: ${{ matrix.package }} - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - default: true - target: ${{ matrix.rust_target }} - - uses: actions-rs/cargo@v1 - with: - command: test - args: --target=${{ matrix.rust_target }} --manifest-path=${{ matrix.manifest }} -- --nocapture - env: - CC: ${{ matrix.clang_cl }} - CFLAGS: "-Werror -Wundef" - windows-gnu-test: name: Test ${{ matrix.manifest }} on ${{ matrix.rust_target }} with ${{ matrix.rust_toolchain }} runs-on: windows-latest diff --git a/psm/build.rs b/psm/build.rs index 28d0d47..df94267 100644 --- a/psm/build.rs +++ b/psm/build.rs @@ -3,37 +3,16 @@ fn find_assembly( endian: &str, os: &str, env: &str, - masm: bool, ) -> Option<(&'static str, bool)> { match (arch, endian, os, env) { - // The implementations for stack switching exist, but, officially, doing so without Fibers - // is not supported in Windows. For x86_64 the implementation actually works locally, - // but failed tests in CI (???). Might want to have a feature for experimental support - // here. - ("x86", _, "windows", _) => { - if masm { - Some(("src/arch/x86_msvc.asm", false)) - } else { - Some(("src/arch/x86_windows_gnu.s", false)) - } - } - ("x86_64", _, "windows", _) => { - if masm { - Some(("src/arch/x86_64_msvc.asm", false)) - } else { - Some(("src/arch/x86_64_windows_gnu.s", false)) - } - } + // Windows: inline asm in src/asm/windows/. Stack switching (canswitch) + // is disabled — officially requires Fibers. + ("x86", _, "windows", _) => None, + ("x86_64", _, "windows", _) => None, + ("aarch64", _, "windows", _) => None, ("x86_64", _, "cygwin", _) => Some(("src/arch/x86_64_windows_gnu.s", false)), ("arm", _, "windows", "msvc") => Some(("src/arch/arm_armasm.asm", false)), ("arm64ec", _, "windows", "msvc") => Some(("src/arch/arm64ec_armasm.asm", false)), - ("aarch64", _, "windows", _) => { - if masm { - Some(("src/arch/aarch64_armasm.asm", false)) - } else { - Some(("src/arch/aarch_aapcs64.s", false)) - } - } ("x86", _, _, _) => Some(("src/arch/x86.s", true)), ("x86_64", _, _, _) => Some(("src/arch/x86_64.s", true)), ("arm", _, _, _) => Some(("src/arch/arm_aapcs.s", true)), @@ -74,22 +53,16 @@ fn main() { let mut cfg = cc::Build::new(); let msvc = cfg.get_compiler().is_like_msvc(); - // If we're targeting msvc, either via regular MS toolchain or clang-cl, we - // will _usually_ want to use the regular Microsoft assembler if it exists, - // which is done for us within cc, however it _probably_ won't exist if - // we're in a cross-compilation context pm a platform that can't natively - // run Windows executables, so in that case we instead use the the equivalent - // GAS assembly file instead. This logic can be removed once LLVM natively - // supports compiling MASM, but that is not stable yet - let masm = msvc && var("HOST").expect("HOST env not set").contains("windows"); - - let asm = if let Some((asm, canswitch)) = find_assembly(&arch, &endian, &os, &env, masm) { + let asm = if let Some((asm, canswitch)) = find_assembly(&arch, &endian, &os, &env) { println!("cargo:rustc-cfg=asm"); println!("cargo:rustc-cfg=link_asm"); if canswitch { println!("cargo:rustc-cfg=switchable_stack") } asm + } else if (arch == "aarch64" || arch == "x86_64" || arch == "x86") && os == "windows" { + println!("cargo:rustc-cfg=asm"); + return; } else { println!( "cargo:warning=Target {}-{}-{} has no assembly files!", diff --git a/psm/src/arch/aarch64_armasm.asm b/psm/src/arch/aarch64_armasm.asm deleted file mode 100644 index 95349f9..0000000 --- a/psm/src/arch/aarch64_armasm.asm +++ /dev/null @@ -1,38 +0,0 @@ - AREA |.text|, CODE, READONLY - - GLOBAL |rust_psm_stack_direction| - ALIGN 4 -|rust_psm_stack_direction| PROC - orr w0, wzr, #2 - ret - ENDP - - - GLOBAL |rust_psm_stack_pointer| - ALIGN 4 -|rust_psm_stack_pointer| PROC - mov x0, sp - ret - ENDP - - - GLOBAL |rust_psm_replace_stack| - ALIGN 4 -|rust_psm_replace_stack| PROC - mov sp, x2 - br x1 - ENDP - - GLOBAL |rust_psm_on_stack| - ALIGN 4 -|rust_psm_on_stack| PROC - stp x29, x30, [sp, #-16]! - mov x29, sp - mov sp, x3 - blr x2 - mov sp, x29 - ldp x29, x30, [sp], #16 - ret - ENDP - - END diff --git a/psm/src/arch/x86_64_msvc.asm b/psm/src/arch/x86_64_msvc.asm deleted file mode 100644 index 67d7283..0000000 --- a/psm/src/arch/x86_64_msvc.asm +++ /dev/null @@ -1,61 +0,0 @@ -PUBLIC rust_psm_stack_direction -PUBLIC rust_psm_stack_pointer -PUBLIC rust_psm_replace_stack -PUBLIC rust_psm_on_stack - -_TEXT SEGMENT - -; extern "sysv64" fn() -> u8 (%al) -rust_psm_stack_direction PROC - mov al, 2 - ret -rust_psm_stack_direction ENDP - -; extern "sysv64" fn() -> *mut u8 (%rax) -rust_psm_stack_pointer PROC - lea rax, [rsp + 8] - ret -rust_psm_stack_pointer ENDP - -; extern "sysv64" fn(%rdi: usize, %rsi: extern "sysv64" fn(usize), %rdx: *mut u8, %rcx: *mut u8) -rust_psm_replace_stack PROC - mov gs:[08h], rdx - mov gs:[10h], rcx - lea rsp, [rdx - 8] - jmp rsi -rust_psm_replace_stack ENDP - -; extern "sysv64" fn(%rdi: usize, %rsi: usize, -; %rdx: extern "sysv64" fn(usize, usize), %rcx: *mut u8, %r8: *mut u8) -; -; NB: on Windows for SEH to work at all, the pointers in TIB, thread information block, need to be -; fixed up. Otherwise, it seems that exception mechanism on Windows will not bother looking for -; exception handlers at *all* if they happen to fall outside the are specified in TIB. -; -; This necessitates an API difference from the usual 4-argument signature used elsewhere. -; -; FIXME: this needs a catch-all exception handler that aborts in case somebody unwinds into here. -rust_psm_on_stack PROC FRAME - push rbp - .pushreg rbp - mov rbp, rsp - .setframe rbp, 0 - .endprolog - - push gs:[08h] - mov gs:[08h], rcx - push gs:[10h] - mov gs:[10h], r8 - mov rsp, rcx - call rdx - lea rsp, [rbp - 010h] - pop gs:[10h] - pop gs:[08h] - - pop rbp - ret -rust_psm_on_stack ENDP - -_TEXT ENDS - -END diff --git a/psm/src/arch/x86_msvc.asm b/psm/src/arch/x86_msvc.asm deleted file mode 100644 index 3838759..0000000 --- a/psm/src/arch/x86_msvc.asm +++ /dev/null @@ -1,70 +0,0 @@ -; FIXME: this is weird, this works locally but not on appveyor?!??! - -.386 -.model flat - -ASSUME FS:NOTHING - -; WTF: PUBLIC conflicts with "SYSCALL" but "SYSCALL" is the only way to stop MASM from manging the -; symbol names? -; -; PUBLIC @rust_psm_stack_direction@0 -; PUBLIC @rust_psm_stack_pointer@0 -; PUBLIC @rust_psm_replace_stack@12 -; PUBLIC @rust_psm_on_stack@16 - -_TEXT SEGMENT - -; extern "fastcall" fn() -> u8 (%al) -@rust_psm_stack_direction@0 PROC SYSCALL - mov al, 2 - ret -@rust_psm_stack_direction@0 ENDP - -; extern "fastcall" fn() -> *mut u8 (%rax) -@rust_psm_stack_pointer@0 PROC SYSCALL - lea eax, [esp + 4] - ret -@rust_psm_stack_pointer@0 ENDP - -; extern "fastcall" fn(%ecx: usize, %edx: extern "fastcall" fn(usize), -; 4(%esp): *mut u8, 8(%esp): *mut u8) -@rust_psm_replace_stack@16 PROC SYSCALL - mov eax, dword ptr [esp + 8] - mov fs:[08h], eax - mov esp, dword ptr [esp + 4] - mov fs:[04h], esp - jmp edx -@rust_psm_replace_stack@16 ENDP - -; extern "fastcall" fn(%ecx: usize, %edx: usize, 4(%esp): extern "fastcall" fn(usize, usize), -; 8(%esp): *mut u8, 12(%esp): *mut u8) -; -; NB: on Windows for SEH to work at all, the pointers in TIB, thread information block, need to be -; fixed up. Otherwise, it seems that exception mechanism on Windows will not bother looking for -; exception handlers at *all* if they happen to fall outside the are specified in TIB. -; -; This necessitates an API difference from the usual 4-argument signature used elsewhere. -@rust_psm_on_stack@20 PROC SYSCALL - push ebp - mov ebp, esp - - push fs:[0E0Ch] - push fs:[08h] - mov eax, dword ptr [ebp + 4 + 12] - mov dword ptr fs:[08h], eax - mov dword ptr fs:[0E0Ch], eax - push fs:[04h] - mov esp, dword ptr [ebp + 4 + 8] - mov dword ptr fs:[04h], esp - call dword ptr [ebp + 4 + 4] - - lea esp, [ebp - 12] - pop fs:[04h] - pop fs:[08h] - pop fs:[0E0Ch] - pop ebp - ret 12 -@rust_psm_on_stack@20 ENDP - -END diff --git a/psm/src/arch/x86_windows_gnu.s b/psm/src/arch/x86_windows_gnu.s deleted file mode 100644 index 474d416..0000000 --- a/psm/src/arch/x86_windows_gnu.s +++ /dev/null @@ -1,94 +0,0 @@ -/* FIXME: this works locally but not on appveyor??!? */ -/* NOTE: fastcall calling convention used on all x86 targets */ -.text - -.def @rust_psm_stack_direction@0 -.scl 2 -.type 32 -.endef -.globl @rust_psm_stack_direction@0 -.p2align 4 -@rust_psm_stack_direction@0: -/* extern "fastcall" fn() -> u8 (%al) */ -.cfi_startproc - movb $2, %al # always descending on x86_64 - retl -.cfi_endproc - - -.def @rust_psm_stack_pointer@0 -.scl 2 -.type 32 -.endef -.globl @rust_psm_stack_pointer@0 -.p2align 4 -@rust_psm_stack_pointer@0: -/* extern "fastcall" fn() -> *mut u8 (%rax) */ -.cfi_startproc - leal 4(%esp), %eax - retl -.cfi_endproc - - -.def @rust_psm_replace_stack@16 -.scl 2 -.type 32 -.endef -.globl @rust_psm_replace_stack@16 -.p2align 4 -@rust_psm_replace_stack@16: -/* extern "fastcall" fn(%ecx: usize, %edx: extern "fastcall" fn(usize), 4(%esp): *mut u8) */ -.cfi_startproc -/* - All we gotta do is set the stack pointer to 4(%esp) & tail-call the callback in %edx - - Note, that the callee expects the stack to be offset by 4 bytes (normally, a return address - would be store there) off the required stack alignment on entry. To offset the stack in such a - way we use the `calll` instruction, however it would also be possible to to use plain `jmpl` but - would require to adjust the stack manually, which cannot be easily done, because the stack - pointer argument is already stored in memory. - */ - movl 8(%esp), %eax - mov %eax, %fs:0x08 - movl 4(%esp), %esp - mov %esp, %fs:0x04 - calll *%edx - ud2 -.cfi_endproc - - -.def @rust_psm_on_stack@16 -.scl 2 -.type 32 -.endef -.globl @rust_psm_on_stack@16 -.p2align 4 -@rust_psm_on_stack@16: -/* extern "fastcall" fn(%ecx: usize, %edx: usize, 4(%esp): extern "fastcall" fn(usize, usize), 8(%esp): *mut u8) */ -.cfi_startproc - pushl %ebp - .cfi_def_cfa %esp, 8 - .cfi_offset %ebp, -8 - pushl %fs:0x04 - .cfi_def_cfa %esp, 12 - pushl %fs:0x08 - .cfi_def_cfa %esp, 16 - movl %esp, %ebp - .cfi_def_cfa_register %ebp - - movl 24(%ebp), %eax - movl %eax, %fs:0x08 - movl 20(%ebp), %esp - movl %esp, %fs:0x04 - calll *16(%ebp) - - movl %ebp, %esp - popl %fs:0x08 - .cfi_def_cfa %esp, 12 - popl %fs:0x04 - .cfi_def_cfa %esp, 8 - popl %ebp - .cfi_def_cfa %esp, 4 - retl $12 -.cfi_endproc - diff --git a/psm/src/asm/mod.rs b/psm/src/asm/mod.rs new file mode 100644 index 0000000..92af08e --- /dev/null +++ b/psm/src/asm/mod.rs @@ -0,0 +1,6 @@ +#[cfg(target_os = "windows")] +mod windows; +#[cfg(target_os = "windows")] +pub(crate) use windows::{rust_psm_stack_direction, rust_psm_stack_pointer}; +#[cfg(all(target_os = "windows", switchable_stack))] +pub(crate) use windows::{rust_psm_on_stack, rust_psm_replace_stack}; diff --git a/psm/src/asm/windows/aarch64.rs b/psm/src/asm/windows/aarch64.rs new file mode 100644 index 0000000..40c6b0b --- /dev/null +++ b/psm/src/asm/windows/aarch64.rs @@ -0,0 +1,57 @@ +/// Always descending on aarch64. +pub(crate) unsafe fn rust_psm_stack_direction() -> u8 { + crate::StackDirection::Descending as u8 +} + +/// Returns an approximate pointer to the current stack position. +/// +/// May be off by one frame without `#[naked]`; safe because stacker only +/// uses this for remaining-stack estimates with large (32KB+) thresholds. +// TODO: use #[unsafe(naked)] when MSRV >= 1.79 for exact results. +pub(crate) unsafe fn rust_psm_stack_pointer() -> *mut u8 { + let sp: *mut u8; + core::arch::asm!("mov {}, sp", out(reg) sp, options(nomem, nostack, preserves_flags)); + sp +} + +/// Sets the stack pointer to `sp` and tail-calls `callback(data)`. +#[cfg(switchable_stack)] +pub(crate) unsafe fn rust_psm_replace_stack( + data: usize, + callback: unsafe extern "C" fn(usize) -> !, + sp: *mut u8, + _stack_base: *mut u8, +) -> ! { + core::arch::asm!( + "mov sp, {sp}", + "br {callback}", + sp = in(reg) sp, + callback = in(reg) callback, + in("x0") data, + options(noreturn), + ) +} + +/// Switches to the provided stack, calls the callback, then restores. +#[cfg(switchable_stack)] +pub(crate) unsafe fn rust_psm_on_stack( + data: usize, + return_ptr: usize, + callback: unsafe extern "C" fn(usize, usize), + sp: *mut u8, + _stack_base: *mut u8, +) { + core::arch::asm!( + "stp x29, x30, [sp, #-16]!", + "mov x29, sp", + "mov sp, {sp}", + "blr {callback}", + "mov sp, x29", + "ldp x29, x30, [sp], #16", + sp = in(reg) sp, + callback = in(reg) callback, + in("x0") data, + in("x1") return_ptr, + clobber_abi("C"), + ) +} diff --git a/psm/src/asm/windows/mod.rs b/psm/src/asm/windows/mod.rs new file mode 100644 index 0000000..bcd6875 --- /dev/null +++ b/psm/src/asm/windows/mod.rs @@ -0,0 +1,14 @@ +#[cfg(target_arch = "aarch64")] +mod aarch64; +#[cfg(target_arch = "aarch64")] +pub(crate) use aarch64::*; + +#[cfg(target_arch = "x86_64")] +mod x86_64; +#[cfg(target_arch = "x86_64")] +pub(crate) use x86_64::*; + +#[cfg(target_arch = "x86")] +mod x86; +#[cfg(target_arch = "x86")] +pub(crate) use x86::*; diff --git a/psm/src/asm/windows/x86.rs b/psm/src/asm/windows/x86.rs new file mode 100644 index 0000000..731657d --- /dev/null +++ b/psm/src/asm/windows/x86.rs @@ -0,0 +1,78 @@ +/// Always descending on x86. +pub(crate) unsafe fn rust_psm_stack_direction() -> u8 { + crate::StackDirection::Descending as u8 +} + +/// Returns an approximate pointer to the current stack position. +/// +/// May be off by one frame without `#[naked]`; safe because stacker only +/// uses this for remaining-stack estimates with large (32KB+) thresholds. +// TODO: use #[unsafe(naked)] when MSRV >= 1.79 for exact results. +#[inline(always)] +pub(crate) unsafe fn rust_psm_stack_pointer() -> *mut u8 { + let sp: *mut u8; + core::arch::asm!("mov {}, esp", out(reg) sp, options(nomem, nostack, preserves_flags)); + sp +} + +/// Stack switching is not currently enabled for i686 Windows (canswitch = false). +/// +/// Officially, switching stacks without Fibers is not supported on Windows. +/// The implementations below are preserved from the original x86_msvc.asm +/// for reference. They manipulate the TIB (Thread Information Block) at +/// fs:04h (StackLimit), fs:08h (StackBase), and fs:0E0Ch (DeallocationStack) +/// for SEH compatibility. +/// +/// Original x86_msvc.asm replace_stack body: +/// ```asm +/// ; extern "fastcall" fn(%ecx: usize, %edx: extern "fastcall" fn(usize), +/// ; 4(%esp): *mut u8, 8(%esp): *mut u8) +/// mov eax, dword ptr [esp + 8] +/// mov fs:[08h], eax +/// mov esp, dword ptr [esp + 4] +/// mov fs:[04h], esp +/// jmp edx +/// ``` +#[cfg(switchable_stack)] +pub(crate) unsafe fn rust_psm_replace_stack( + _data: usize, + _callback: unsafe extern "fastcall" fn(usize) -> !, + _sp: *mut u8, + _stack_base: *mut u8, +) -> ! { + todo!("i686 Windows stack switching requires Fibers; see psm/src/arch/x86_msvc.asm") +} + +/// Original x86_msvc.asm on_stack body: +/// ```asm +/// ; extern "fastcall" fn(%ecx: usize, %edx: usize, +/// ; 4(%esp): extern "fastcall" fn(usize, usize), +/// ; 8(%esp): *mut u8, 12(%esp): *mut u8) +/// push ebp +/// mov ebp, esp +/// push fs:[0E0Ch] +/// push fs:[08h] +/// mov eax, dword ptr [ebp + 4 + 12] +/// mov dword ptr fs:[08h], eax +/// mov dword ptr fs:[0E0Ch], eax +/// push fs:[04h] +/// mov esp, dword ptr [ebp + 4 + 8] +/// mov dword ptr fs:[04h], esp +/// call dword ptr [ebp + 4 + 4] +/// lea esp, [ebp - 12] +/// pop fs:[04h] +/// pop fs:[08h] +/// pop fs:[0E0Ch] +/// pop ebp +/// ret 12 +/// ``` +#[cfg(switchable_stack)] +pub(crate) unsafe fn rust_psm_on_stack( + _data: usize, + _return_ptr: usize, + _callback: unsafe extern "fastcall" fn(usize, usize), + _sp: *mut u8, + _stack_base: *mut u8, +) { + todo!("i686 Windows stack switching requires Fibers; see psm/src/arch/x86_msvc.asm") +} diff --git a/psm/src/asm/windows/x86_64.rs b/psm/src/asm/windows/x86_64.rs new file mode 100644 index 0000000..8eca14b --- /dev/null +++ b/psm/src/asm/windows/x86_64.rs @@ -0,0 +1,81 @@ +/// Always descending on x86_64. +pub(crate) unsafe fn rust_psm_stack_direction() -> u8 { + crate::StackDirection::Descending as u8 +} + +/// Returns an approximate pointer to the current stack position. +/// +/// May be off by one frame without `#[naked]`; safe because stacker only +/// uses this for remaining-stack estimates with large (32KB+) thresholds. +// TODO: use #[unsafe(naked)] when MSRV >= 1.79 for exact results. +#[inline(always)] +pub(crate) unsafe fn rust_psm_stack_pointer() -> *mut u8 { + let sp: *mut u8; + core::arch::asm!("mov {}, rsp", out(reg) sp, options(nomem, nostack, preserves_flags)); + sp +} + +/// Reads the current TIB StackBase (gs:0x08), sets rsp to that minus 8, +/// and tail-calls `callback(data)`. The `sp` and `stack_base` parameters +/// are ignored — the TIB values are used directly, matching the original +/// assembly behavior. +#[cfg(switchable_stack)] +pub(crate) unsafe fn rust_psm_replace_stack( + data: usize, + callback: unsafe extern "sysv64" fn(usize) -> !, + _sp: *mut u8, + _stack_base: *mut u8, +) -> ! { + core::arch::asm!( + "mov rax, gs:[0x08]", + "lea rsp, [rax - 8]", + "jmp {callback}", + callback = in(reg) callback, + in("rdi") data, + out("rax") _, + options(noreturn), + ) +} + +/// Switches to the provided stack, calls the callback, then restores. +/// +/// Saves and restores the Windows TIB stack limits at gs:0x08 (StackBase) +/// and gs:0x10 (StackLimit) so SEH exception handling works on the new stack. +#[cfg(switchable_stack)] +pub(crate) unsafe fn rust_psm_on_stack( + data: usize, + return_ptr: usize, + callback: unsafe extern "sysv64" fn(usize, usize), + sp: *mut u8, + stack_base: *mut u8, +) { + core::arch::asm!( + // Save frame pointer and TIB stack limits + "push rbp", + "mov rax, gs:[0x08]", + "push rax", + "mov rax, gs:[0x10]", + "push rax", + // Switch to new stack, update TIB + "mov rbp, rsp", + "mov gs:[0x08], {sp}", + "mov gs:[0x10], {stack_base}", + "mov rsp, {sp}", + // Call the callback + "call {callback}", + // Restore original stack and TIB + "mov rsp, rbp", + "pop rax", + "mov gs:[0x10], rax", + "pop rax", + "mov gs:[0x08], rax", + "pop rbp", + sp = in(reg) sp, + stack_base = in(reg) stack_base, + callback = in(reg) callback, + in("rdi") data, + in("rsi") return_ptr, + out("rax") _, + clobber_abi("sysv64"), + ) +} diff --git a/psm/src/lib.rs b/psm/src/lib.rs index de74146..a36b2fb 100644 --- a/psm/src/lib.rs +++ b/psm/src/lib.rs @@ -12,6 +12,15 @@ #![allow(unused_macros)] #![no_std] +#[cfg(all(asm, not(link_asm), target_os = "windows"))] +mod asm; +#[cfg(all(asm, not(link_asm), target_os = "windows"))] +use asm::rust_psm_stack_direction; +#[cfg(all(asm, not(link_asm), target_os = "windows"))] +use asm::rust_psm_stack_pointer; +#[cfg(all(switchable_stack, not(link_asm), target_os = "windows"))] +use asm::{rust_psm_on_stack, rust_psm_replace_stack}; + macro_rules! extern_item { (unsafe $($toks: tt)+) => { unsafe extern "C" $($toks)+ @@ -58,9 +67,9 @@ macro_rules! extern_item { extern_item! { { #![cfg_attr(link_asm, link(name="psm_s"))] - #[cfg(asm)] + #[cfg(link_asm)] fn rust_psm_stack_direction() -> u8; - #[cfg(asm)] + #[cfg(link_asm)] fn rust_psm_stack_pointer() -> *mut u8; #[cfg(all(switchable_stack, not(target_os = "windows")))] @@ -78,14 +87,14 @@ extern_item! { { callback: extern_item!(unsafe fn(usize, usize)), sp: *mut u8, ); - #[cfg(all(switchable_stack, target_os = "windows"))] + #[cfg(all(switchable_stack, target_os = "windows", link_asm))] fn rust_psm_replace_stack( data: usize, callback: extern_item!(unsafe fn(usize) -> !), sp: *mut u8, stack_base: *mut u8 ) -> !; - #[cfg(all(switchable_stack, target_os = "windows"))] + #[cfg(all(switchable_stack, target_os = "windows", link_asm))] fn rust_psm_on_stack( data: usize, return_ptr: usize,