Index
Tue Jan 13 18:39:44 CST 2026
经过仔细探索,似乎所有 rust 编译到 XX 语言的方案都不如直接编译到 wasm/wasi 再编译到目标语言。所以应该对 wasm 的生态做点调查。
https://github.com/turbolent/w2c2
一个把 wasm 编译到 c 的东西。看了下它不支持 socket,决定放弃。
https://www.wingolog.org/archives/2025/10/30/wastrel-a-profligate-implementation-of-webassembly
另一个,说是完整支持 wasip1。最好试试。
>16s< jyi-00-rust-dev 18:52 ~/.c/ta/wasm32-wasip1/debug
0 wastrel rust-hello.wasm
rust-hello.wasm.BTHbhf.c: In function ‘func_392’:
rust-hello.wasm.BTHbhf.c:15277:38: error: expected ‘;’ before ‘indirect_i32_1’
15277 | indirect_i32_0 = i32_const(1055776)
| ^
| ;
15278 | indirect_i32_1 = i32_const(1055792)
| ~~~~~~~~~~~~~~
rust-hello.wasm.BTHbhf.c:15283:14: error: ‘tmp_0’ undeclared (first use in this function); did you mean ‘type_0’?
15283 | local_5 = (tmp_0 & i32_const(1));
| ^~~~~
| type_0
wastrel 好像有点 bug,不过看起来不是什么大问题。我试着改一下……
diff --git a/module/wastrel/backend.scm b/module/wastrel/backend.scm
index b455a26..f76cc3e 100644
--- a/module/wastrel/backend.scm
+++ b/module/wastrel/backend.scm
@@ -701,7 +701,7 @@
(prepare-call sig (map value-expr args)))
(lambda (indirect-args direct-args direct-result indirect-results)
(for-each (match-lambda
- ((name . arg) (emit! "~a = ~a" name arg)))
+ ((name . arg) (emit! "~a = ~a;" name arg)))
indirect-args)
(let ((call (make-call (value-expr operator)
(string-join direct-args ", "))))
@@ -1345,7 +1345,7 @@
(match result-names
((direct-name indirect-local-name ...)
(for-each (lambda (thread-local local)
- (fmt! " ~a = ~a\n" thread-local local))
+ (fmt! " ~a = ~a;\n" thread-local local))
indirect-name indirect-local-name)
(fmt! " return ~a;\n" direct-name))))))
(fmt! "}\n"))))
在 deepseek 的帮助下改好了。现在能用了。llm 真好。
0 wastrel rust-hello.wasm
Hello, world!
至少现在跑起来了。测试一下更复杂的程序看看。
试了下 miniserve,感觉有点坏啊,ring 编译不过去。又是可恶的密码学……用 no-default-features 好了。
error: Socket2 doesn't support the compile target
--> /home/jyi/.cargo/registry/src/mirrors.cernet.edu.cn-0d8da22710581788/socket2-0.6.1/src/lib.rs:184:1
|
184 | compile_error!("Socket2 doesn't support the compile target");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
呃,死掉了。
看来网络编程还为时尚早……
再试试 cli 小工具好了,比如 bat 这种单线程又只有文件 IO 的。
error: Neither cfg(unix) nor cfg(windows) was true, aborting.
--> /home/jyi/.cargo/registry/src/mirrors.cernet.edu.cn-0d8da22710581788/clircle-0.6.1/src/lib.rs:55:9
|
55 | compile_error!("Neither cfg(unix) nor cfg(windows) was true, aborting.");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
啊啊啊啊你又是谁
https://github.com/niklasmohrin/clircle
Detect cyclic file handles with ease.
为什么一个高端的 cat 需要用到这种功能呢??
error[E0432]: unresolved import `sys::position`
--> /home/jyi/.cargo/registry/src/mirrors.cernet.edu.cn-0d8da22710581788/crossterm-0.27.0/src/cursor.rs:52:9
|
52 | pub use sys::position;
| ^^^^^^^^^^^^^ no `position` in `cursor::sys`
|
note: found an item that was configured out
这回是 crossterm 编译不过了……也对毕竟 wasm32-wasi 哪来的终端呢。
我想想还有什么别的项目,又不用网络又没有终端颜色之类的需求……
https://github.com/jyi2ya/simple-calc
这个倒是还算正常,可以跑。
>1m 55s< jyi-00-rust-dev 19:57 (master) ~/sr/util/pixpix
0 ./a.out assets/wh.jpg
thread 'main' (1) panicked at src/main.rs:88:41:
called `Result::unwrap()` on an `Err` value: IoError(Os { code: 44, kind: NotFound, message: "No such file or directory" })
Illegal instruction
哎怎么会这样呢??
我试试能不能就做一个最简单的 cat 程序……
好怪哦,为什么读不出来。
有点搞不懂了,可能和它的沙盒机制有关。但是我没找到可以关掉它的沙盒的方法。
还是试试 w2c2 算了。
[1] jyi-00-rust-dev 20:28 (main) ~/sr/la/w2c2/build
0 gcc -I ../w2c2 pixpix.c -lm
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/14/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x17): undefined reference to `main'
/usr/bin/ld: /tmp/ccsWWtDs.o: in function `f23':
pixpix.c:(.text+0x535): undefined reference to `trap'
/usr/bin/ld: /tmp/ccsWWtDs.o: in function `f76':
pixpix.c:(.text+0xd0c): undefined reference to `trap'
/usr/bin/ld: /tmp/ccsWWtDs.o: in function `f116':
pixpix.c:(.text+0xd48): undefined reference to `trap'
坏了,是不是有什么东西没编译上。
jyi-00-rust-dev 20:39 (main) ~/sr/la/w2c2/build
0 gcc -DHAS_TIMESPEC -DHAS_UNISTD -I../wasi -I../w2c2 ../wasi/wasi.c pixpix.c main.c -lm
CMakeCache.txt Makefile cmake_install.cmake pixpix.c pixpix.wasm* wasi/
CMakeFiles/ a.out* main.c pixpix.h w2c2/
jyi-00-rust-dev 20:39 (main) ~/sr/la/w2c2/build
从它的 ls 示例里偷了个 main 函数拼上终于编译成功了。但是编译出来的 a.out 似乎会把所有路径前面加个 / 变成绝对路径。有点难绷……
real 0m2.389s
user 0m2.290s
sys 0m0.095s
real 0m24.238s
user 0m24.147s
sys 0m0.088s
哦哦,跑出来了,性能是原版的 1/10!
real 0m2.410s
user 0m2.332s
sys 0m0.074s
戳啦,原来是忘记开优化导致的。感觉性能还是挺不错的。
虽然灵车但是感觉还是可以用的。
Tue Jan 13 21:17:57 CST 2026
Benchmark 1: ./a.out ~/src/util/pixpix/assets/8620.png
Time (mean ± σ): 2.331 s ± 0.027 s [User: 2.254 s, System: 0.077 s]
Range (min … max): 2.291 s … 2.367 s 10 runs
Benchmark 2: ~/.cargo/target/release/pixpix ~/src/util/pixpix/assets/8620.png
Time (mean ± σ): 2.385 s ± 0.070 s [User: 2.287 s, System: 0.098 s]
Range (min … max): 2.268 s … 2.550 s 10 runs
Summary
./a.out ~/src/util/pixpix/assets/8620.png ran
1.02 ± 0.03 times faster than ~/.cargo/target/release/pixpix ~/src/util/pixpix/assets/8620.png
补一个 hyperfine,怎么比原版还快两个点。
Wed Jan 14 16:00:09 CST 2026
想了想 wastrel 还是比 w2c2 更好一点,毕竟只生成一个文件,也不用手动组装 main 函数。更阳间一些。
还是研究研究 wastrel 怎么用好了。
[1] jyi-00-rust-dev 16:21 ~/tmp/wast
0 ./a.out cat.c
Error: Os { code: 44, kind: NotFound, message: "No such file or directory" }
wastrel 编译之后为什么会提示打不开文件呢……
怪了,我 system("stat cat.c") 又是好的。
write(2, "IT BEGINS\n", 10IT BEGINS
) = 10
write(2, "Error: ", 7Error: ) = 7
write(2, "Os", 2Os) = 2
write(2, " { ", 3 { ) = 3
write(2, "code", 4code) = 4
write(2, ": ", 2: ) = 2
好怪哦,它完全没做任何系统调用就失败了。
0 gcc cat.c && ./a.out cat.c
IT BEGINS
local_0 is 1048144 at line 16626
func_167
local_0 is 1048144 at line 16644
local_0 is -1 at line 16647
will skip open, jump to label2
Error: Os { code: 44, kind: NotFound, message: "No such file or directory" }
使用古法 printf 调试……
看样子是它在打开文件的时候从内存里读了个数值,发现是 0,就直接返回了。
jyi-00-rust-dev 17:06 (main) ~/sr/la/w2c2/build 0 md5sum dump.txt e3a951a5de90a91ba32136af5717b3eb dump.txt jyi-00-rust-dev 17:07 ~/tmp/wast 0 md5sum dump.txt e3a951a5de90a91ba32136af5717b3eb dump.txt
把 w2c2 和 wastrel 的初始内存映像给存下来了,发现是一样的,那就是后面运行的时候出了问题?
有点奇怪了。可能是 wastrel 的实现有点问题吗
jyi-00-rust-dev 17:48 ~/tmp/wast
0 wasmtime --dir=. cat.wasm cat.c
[1] jyi-00-rust-dev 17:49 ~/tmp/wast
0 wasmtime cat.wasm ./cat.c
Error: Os { code: 44, kind: NotFound, message: "No such file or directory" }
用 wasmtime 跑了一下,发现它必须得先启用沙盒才能正常打开文件。wastrel 会不会也要沙盒才行……
[1] jyi-00-rust-dev 17:51 ~/tmp/wast
0 wastrel run --expose-dir=$PWD cat.wasm cat.c
Error: Os { code: 44, kind: NotFound, message: "No such file or directory" }
好像也不太行
jyi-00-rust-dev 17:54 ~/tmp/wast
0 wastrel run --expose-dir=$PWD cat.wasm $PWD/cat.c
这样就好了。
https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-tutorial.md#executing-in-wasmtime
As a brief aside, note that we used the path . above to grant the program access to the current directory. This is needed because the mapping from paths to associated capabilities is performed by libc, so it's part of the WebAssembly program, and we don't expose the actual current working directory to the WebAssembly program. So providing a full path doesn't work:
神秘,如果指定共享的是绝对路径,wasm 程序就不能用相对路径;如果共享的是相对路径,wasm 程序就不能用绝对路径。wastrel 的 expose-dir 不支持相对路径,只接受绝对路径……
jyi-00-rust-dev 18:02 ~/tmp/wast
0 diff -u cat.c cat.wasm.AV9s8I.c
--- cat.c 2026-01-14 17:59:06.045669963 +0800
+++ cat.wasm.AV9s8I.c 2026-01-14 18:02:16.469412470 +0800
@@ -2677,7 +2677,7 @@
struct access path, struct access stat_out) {
int lookup_flags = 0;
if (!(flags & WASI_LOOKUP_SYMLINK_FOLLOW))
- lookup_flags |= AT_SYMLINK_NOFOLLOW;
+ lookup_flags |= AT_SYMLINK_NOFOLLOW;
char path_buf[1024];
if (path.len > sizeof(path_buf) - 1)
@@ -2701,7 +2701,7 @@
int32_t fst_flags) {
int lookup_flags = 0;
if (!(flags & WASI_LOOKUP_SYMLINK_FOLLOW))
- lookup_flags |= AT_SYMLINK_NOFOLLOW;
+ lookup_flags |= AT_SYMLINK_NOFOLLOW;
char path_buf[1024];
if (path.len > sizeof(path_buf) - 1)
@@ -2921,7 +2921,7 @@
return WASI_ERRNO_INVAL;
}
}
-
+
struct timespec overall_timeout;
struct timespec *overall_timeout_p = NULL;
if (min_timeout < UINT64_MAX) {
@@ -3404,9 +3404,11 @@
return wasip1_sock_shutdown(fd, how);
}
static const struct wasi_preopen wasi_preopens[] = {
+ {3, "/home/jyi/tmp/wast"},
};
static const struct wasi_preopen* wasi_preopen_for_fd(int fd) {
switch (fd) {
+ case 3: return &wasi_preopens[0];
default: return NULL;
}
}
@@ -3482,7 +3484,7 @@
static inline void bind_one(const char *host, const char *guest) {
if (mount(host, guest, NULL, MS_BIND | MS_REC, NULL) == -1) {
- fprintf(stderr, "error: failed to bind mount %s: %s\n",
+ fprintf(stderr, "error: failed to bind mount %s: %s\n",
host, strerror(errno));
exit(1);
}
@@ -17279,7 +17281,13 @@
process_argc = argc;
process_argv = argv;
const char *root = prepare_sandbox();
+ make_empty_dir("home");
+ make_empty_dir("home/jyi");
+ make_empty_dir("home/jyi/tmp");
+ make_empty_dir("home/jyi/tmp/wast");
+ bind_one("/home/jyi/tmp/wast", "home/jyi/tmp/wast");
make_empty_dir("tmp0");
pivot(root, "tmp0");
+ prepare_preopen(3, "/home/jyi/tmp/wast");
func_14();
}
[1] jyi-00-rust-dev 18:02 ~/tmp/wast
0
找了个正确的和错误的对照着看看
好像还真是沙盒的问题……
wastrel compile --no-tracepoints --expose-dir=/ -S -o cat.correct.c cat.wasm
这样就能得到正确的结果了。
0 ./pixpix ~/src/util/pixpix/assets/8620.png
thread 'main' (1) panicked at src/main.rs:88:41:
called `Result::unwrap()` on an `Err` value: Decoding(DecodingError { format: Exact(Png), underlying: Some(Format(FormatError { inner: CorruptFlateStream { err: BadDistanceHuffmanTree } })) })
Illegal instruction
也并非正确啊,这个 illegal instruction 给我整哪来了。
>5m 34s< jyi-00-rust-dev 18:46 ~/tmp/wast
0 ./pixpix ~/src/util/pixpix/assets/8620.png
=================================================================
==497769==ERROR: AddressSanitizer: memcpy-param-overlap: memory ranges [0x772bbdb3d1c0,0x772bbdb3d1de) and [0x772bbdb3d1be, 0x772bbdb3d1dc) overlap
#0 0x772dc0ef264d in memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115
#1 0x5f19f844f52b in memory_copy /home/jyi/tmp/wast/pixpix.c:604
#2 0x5f19f844f52b in func_241 /home/jyi/tmp/wast/pixpix.c:76389
#3 0x5f19f8611af1 in func_739 /home/jyi/tmp/wast/pixpix.c:200067
#4 0x5f19f8699023 in func_804 /home/jyi/tmp/wast/pixpix.c:228561
#5 0x5f19f8787af8 in func_777 /home/jyi/tmp/wast/pixpix.c:211945
#6 0x5f19f878b162 in func_755 /home/jyi/tmp/wast/pixpix.c:203043
#7 0x5f19f87fa7de in func_37 /home/jyi/tmp/wast/pixpix.c:29823
#8 0x5f19f8866276 in func_27 /home/jyi/tmp/wast/pixpix.c:22236
#9 0x5f19f887dae7 in func_88 /home/jyi/tmp/wast/pixpix.c:48870
#10 0x5f19f887dc75 in func_18 /home/jyi/tmp/wast/pixpix.c:20024
#11 0x5f19f887de63 in main /home/jyi/tmp/wast/pixpix.c:268860
#12 0x772dc0c34ca7 (/lib/x86_64-linux-gnu/libc.so.6+0x29ca7) (BuildId: fce446c9d4ad48e2b0c90cce1a11722897805281)
#13 0x772dc0c34d64 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29d64) (BuildId: fce446c9d4ad48e2b0c90cce1a11722897805281)
#14 0x5f19f80373e0 in _start (/home/jyi/tmp/wast/pixpix+0x63e0) (BuildId: cc02f67e84dfc357f5e416c517901d3d424d2b23)
Address 0x772bbdb3d1c0 is a wild pointer inside of access range of size 0x00000000001e.
Address 0x772bbdb3d1be is a wild pointer inside of access range of size 0x00000000001e.
SUMMARY: AddressSanitizer: memcpy-param-overlap ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115 in memcpy
==497769==ABORTING
我超那我感觉这个正确性确实有问题啊,不太敢用了。还是 w2c2 好了。
Wed Jan 14 19:03:59 CST 2026
https://github.com/turbolent/w2c2/issues/1
看起来 w2c2 功能性上比 wasm2c 要差一点。
哦,wasm2c 可以直接用 apt 装 wabt,装完后就能得到 wasm2c 了。
看了几个 example,感觉 wasm2c 用起来比 w2c2 要复杂一点。wasm2c 得手写一堆外围代码
找到了一些早期文章。
https://github.com/vshymanskyy/wasm2native
这个是基于 w2c2 的。
https://kripken.github.io/blog/wasm/2020/07/27/wasmboxc.html
这个是基于 wasm2c 的,我研究一下它怎么处理的 wasi。
https://hacks.mozilla.org/2020/02/securing-firefox-with-webassembly/