Skip to content

Commit 6452a64

Browse files
committed
Fix support for wasm32
This commit updates the build scripts to fix support for wasm32 so that it links correctly. Currently it compiles correctly but doesn't have a ton of functionality, but afterwards this should have functionality for growing the stack as well as linking correctly.
1 parent 0fd32d4 commit 6452a64

File tree

6 files changed

+113
-66
lines changed

6 files changed

+113
-66
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ test = false
2121
[dependencies]
2222
cfg-if = "0.1.6"
2323
libc = "0.2.45"
24-
psm = "0.1.7"
24+
psm = { path = "psm", version = "0.1.7" }
2525

2626
[target.'cfg(windows)'.dependencies.winapi]
2727
version = "0.3.6"

build.rs

-17
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,7 @@
1-
extern crate cc;
2-
31
use std::env;
4-
use std::path::PathBuf;
52

63
fn main() {
74
let target = env::var("TARGET").unwrap();
8-
if target.starts_with("wasm32") {
9-
// wasm32 auxilary functions are provided as a precompiled object file.
10-
// this is because LLVM with wasm32 support isn't widespread.
11-
let mut link_dir = PathBuf::new();
12-
link_dir.push(env!("CARGO_MANIFEST_DIR"));
13-
link_dir.push("src");
14-
link_dir.push("arch");
15-
link_dir.push("wasm32");
16-
17-
println!("cargo:rustc-link-search={}", link_dir.display());
18-
println!("cargo:rustc-link-lib=stacker");
19-
return;
20-
}
21-
225
let mut cfg = cc::Build::new();
236
if target.contains("windows") {
247
cfg.define("WINDOWS", None);

psm/build.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@ fn find_assembly(arch: &str, endian: &str, os: &str, env: &str) -> Option<(&'sta
2727
("sparc", _, _, _) => Some(("src/arch/sparc_sysv.s", true)),
2828
("riscv32", _, _, _) => Some(("src/arch/riscv.s", true)),
2929
("riscv64", _, _, _) => Some(("src/arch/riscv64.s", true)),
30-
31-
// The implementations exist, but the compilers are unlikely be able to work with this.
32-
// ("wasm32", _, "unknown", _) => Some(("src/arch/wasm32.s", false)),
33-
// ("wasm32", _, "wasi", _) => Some(("src/arch/wasm32.s", false)),
30+
("wasm32", _, _, _) => Some(("src/arch/wasm32.o", true)),
3431
_ => None,
3532
}
3633
}
@@ -63,7 +60,15 @@ fn main() {
6360
cfg.define(&*format!("CFG_TARGET_ARCH_{}", arch), None);
6461
cfg.define(&*format!("CFG_TARGET_ENV_{}", env), None);
6562
}
66-
cfg.file(asm);
63+
64+
// For wasm targets we ship a precompiled `*.o` file so we just pass that
65+
// directly to `ar` to assemble an archive. Otherwise we're actually
66+
// compiling the source assembly file.
67+
if asm.ends_with(".o") {
68+
cfg.object(asm);
69+
} else {
70+
cfg.file(asm);
71+
}
6772

6873
cfg.compile("libpsm_s.a");
6974
}

psm/src/arch/wasm32.o

323 Bytes
Binary file not shown.

psm/src/arch/wasm32.s

+33-10
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,48 @@
11
#include "psm.h"
22

3-
.text
4-
.section .text.rust_psm_stack_direction,"",@
3+
# Note that this function is not compiled when this package is uploaded to
4+
# crates.io, this source is only here as a reference for how the corresponding
5+
# wasm32.o was generated. This file can be compiled with:
6+
#
7+
# cpp psm/src/arch/wasm32.s | llvm-mc -o psm/src/arch/wasm32.o --arch=wasm32 -filetype=obj
8+
#
9+
# where you'll want to ensure that `llvm-mc` is from a relatively recent
10+
# version of LLVM.
11+
12+
.globaltype __stack_pointer, i32
13+
514
.globl rust_psm_stack_direction
615
.type rust_psm_stack_direction,@function
716
rust_psm_stack_direction:
817
.functype rust_psm_stack_direction () -> (i32)
9-
i32.const $STACK_DIRECTION_DESCENDING
18+
i32.const STACK_DIRECTION_DESCENDING
1019
end_function
11-
.rust_psm_stack_direction_end:
12-
.size rust_psm_stack_direction, .rust_psm_stack_direction_end-rust_psm_stack_direction
1320

14-
15-
.section .text.rust_psm_stack_pointer,"",@
1621
.globl rust_psm_stack_pointer
1722
.type rust_psm_stack_pointer,@function
1823
rust_psm_stack_pointer:
1924
.functype rust_psm_stack_pointer () -> (i32)
2025
global.get __stack_pointer
2126
end_function
22-
.rust_psm_stack_pointer_end:
23-
.size rust_psm_stack_pointer, .rust_psm_stack_pointer_end-rust_psm_stack_pointer
2427

25-
.globaltype __stack_pointer, i32
28+
.globl rust_psm_on_stack
29+
.type rust_psm_on_stack,@function
30+
rust_psm_on_stack:
31+
.functype rust_psm_on_stack (i32, i32, i32, i32) -> ()
32+
# get our new stack argument, then save the old stack
33+
# pointer into that local
34+
local.get 3
35+
global.get __stack_pointer
36+
local.set 3
37+
global.set __stack_pointer
38+
39+
# Call our indirect function specified
40+
local.get 0
41+
local.get 1
42+
local.get 2
43+
call_indirect (i32, i32) -> ()
44+
45+
# restore the stack pointer before returning
46+
local.get 3
47+
global.set __stack_pointer
48+
end_function

src/lib.rs

+69-33
Original file line numberDiff line numberDiff line change
@@ -123,42 +123,21 @@ psm_stack_manipulation! {
123123
old_stack_limit: Option<usize>,
124124
}
125125

126-
impl Drop for StackRestoreGuard {
127-
fn drop(&mut self) {
128-
unsafe {
129-
// FIXME: check the error code and decide what to do with it.
130-
// Perhaps a debug_assertion?
131-
libc::munmap(self.new_stack, self.stack_bytes);
126+
impl StackRestoreGuard {
127+
#[cfg(target_arch = "wasm32")]
128+
unsafe fn new(stack_bytes: usize, _page_size: usize) -> StackRestoreGuard {
129+
let layout = std::alloc::Layout::from_size_align(stack_bytes, 16).unwrap();
130+
let ptr = std::alloc::alloc(layout);
131+
assert!(!ptr.is_null(), "unable to allocate stack");
132+
StackRestoreGuard {
133+
new_stack: ptr as *mut _,
134+
stack_bytes,
135+
old_stack_limit: get_stack_limit(),
132136
}
133-
set_stack_limit(self.old_stack_limit);
134137
}
135-
}
136-
137-
fn _grow<F: FnOnce()>(stack_size: usize, callback: F) {
138-
// Calculate a number of pages we want to allocate for the new stack.
139-
// For maximum portability we want to produce a stack that is aligned to a page and has
140-
// a size that’s a multiple of page size. Furthermore we want to allocate two extras pages
141-
// for the stack guard. To achieve that we do our calculations in number of pages and
142-
// convert to bytes last.
143-
// FIXME: consider caching the page size.
144-
let page_size = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) } as usize;
145-
let requested_pages = stack_size
146-
.checked_add(page_size - 1)
147-
.expect("unreasonably large stack requested") / page_size;
148-
let stack_pages = std::cmp::max(1, requested_pages) + 2;
149-
let stack_bytes = stack_pages.checked_mul(page_size)
150-
.expect("unreasonably large stack requesteed");
151138

152-
// Next, there are a couple of approaches to how we allocate the new stack. We take the
153-
// most obvious path and use `mmap`. We also `mprotect` a guard page into our
154-
// allocation.
155-
//
156-
// We use a guard pattern to ensure we deallocate the allocated stack when we leave
157-
// this function and also try to uphold various safety invariants required by `psm`
158-
// (such as not unwinding from the callback we pass to it).
159-
//
160-
// Other than that this code has no meaningful gotchas.
161-
unsafe {
139+
#[cfg(not(target_arch = "wasm32"))]
140+
unsafe fn new(stack_bytes: usize, page_size: usize) -> StackRestoreGuard {
162141
let new_stack = libc::mmap(
163142
std::ptr::null_mut(),
164143
stack_bytes,
@@ -199,6 +178,55 @@ psm_stack_manipulation! {
199178
drop(guard);
200179
panic!("unable to set stack permissions")
201180
}
181+
guard
182+
}
183+
}
184+
185+
impl Drop for StackRestoreGuard {
186+
fn drop(&mut self) {
187+
#[cfg(target_arch = "wasm32")]
188+
unsafe {
189+
std::alloc::dealloc(
190+
self.new_stack as *mut u8,
191+
std::alloc::Layout::from_size_align_unchecked(self.stack_bytes, 16),
192+
);
193+
}
194+
#[cfg(not(target_arch = "wasm32"))]
195+
unsafe {
196+
// FIXME: check the error code and decide what to do with it.
197+
// Perhaps a debug_assertion?
198+
libc::munmap(self.new_stack, self.stack_bytes);
199+
}
200+
set_stack_limit(self.old_stack_limit);
201+
}
202+
}
203+
204+
fn _grow<F: FnOnce()>(stack_size: usize, callback: F) {
205+
// Calculate a number of pages we want to allocate for the new stack.
206+
// For maximum portability we want to produce a stack that is aligned to a page and has
207+
// a size that’s a multiple of page size. Furthermore we want to allocate two extras pages
208+
// for the stack guard. To achieve that we do our calculations in number of pages and
209+
// convert to bytes last.
210+
let page_size = page_size();
211+
let requested_pages = stack_size
212+
.checked_add(page_size - 1)
213+
.expect("unreasonably large stack requested") / page_size;
214+
let stack_pages = std::cmp::max(1, requested_pages) + 2;
215+
let stack_bytes = stack_pages.checked_mul(page_size)
216+
.expect("unreasonably large stack requesteed");
217+
218+
// Next, there are a couple of approaches to how we allocate the new stack. We take the
219+
// most obvious path and use `mmap`. We also `mprotect` a guard page into our
220+
// allocation.
221+
//
222+
// We use a guard pattern to ensure we deallocate the allocated stack when we leave
223+
// this function and also try to uphold various safety invariants required by `psm`
224+
// (such as not unwinding from the callback we pass to it).
225+
//
226+
// Other than that this code has no meaningful gotchas.
227+
unsafe {
228+
let guard = StackRestoreGuard::new(stack_bytes, page_size);
229+
let above_guard_page = guard.new_stack.add(page_size);
202230
set_stack_limit(Some(above_guard_page as usize));
203231
let panic = psm::on_stack(above_guard_page as *mut _, stack_size, move || {
204232
std::panic::catch_unwind(std::panic::AssertUnwindSafe(callback)).err()
@@ -209,6 +237,14 @@ psm_stack_manipulation! {
209237
}
210238
}
211239
}
240+
241+
fn page_size() -> usize {
242+
// FIXME: consider caching the page size.
243+
#[cfg(not(target_arch = "wasm32"))]
244+
unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) as usize }
245+
#[cfg(target_arch = "wasm32")]
246+
{ 65536 }
247+
}
212248
}
213249

214250
no {

0 commit comments

Comments
 (0)