🦀
Rustのno_stdでHello World!(Linux on aarch64)
no_stdでHello World!をしたい
libcを使わずに直でシステムコールを呼び出したい欲求はプログラマの三大欲求のうちの一つであるため(?)、
欲求に従って手元のmacでこちらの記事を参考にシステムコールを呼び出そうとしたところ、archが違うのだから呼び出し規約も違うという現実に突き当たった(そうだねという感じ)
なんとかなったため、なんとかした方法を紹介する
(mac上でlinuxシステムコールを呼び出す方法は普通にDockerとかdevcontainerをコネコネした)
aarch64のsyscall convention
x86_64ではrax
レジスタに呼び出すsyscall number、rdi
、rsi
に第一引数、第二引数と続き、syscall
命令実行後rax
レジスタに返り値が入る、といった動作をするが、
aarch64ではsvc
命令によってシステムコールの呼び出しを行う。
svc
命令はCPUの実行モードを切り替える割り込み命令で、カーネルがシステムコールのハンドリングやエラーハンドリングなどを行う必要がある場合に使う。
システムコール呼び出しをする場合はsvc #0
といった形で呼び出すことができる。
呼び出す際のレジスタの値は
-
x8
: システムコール番号 -
x0~x5
: システムコールの第6引数〜第6引数
返り値はx0
レジスタに帰る。
システムコール番号の一覧は https://arm64.syscall.sh/ を参考にした。
実際に書くぞ
基本的にプログラムの実行に必要なシンボルなどはx86_64の場合と相違ないため、差分のある二つの関数の実装のみ示す[1]
#[start]
fn rust_main(_argc: isize, _argv: *const *const u8) -> isize {
let buf = "Hello, world!\n";
let ret: isize;
unsafe {
asm!(
"svc 0",
in("x8") 64,
in("x0") 1,
in("x1") buf.as_ptr(),
in("x2") buf.len(),
lateout("x0") ret,
)
}
ret
#[no_mangle]
fn __libc_start_main(main: fn() -> isize) {
let ret = main();
unsafe {
asm!(
"svc 0",
in("x8") 93,
in("x0") ret,
)
}
}
実行した結果は以下のようになった
vscode ➜ /workspaces/nostd_hello (main) $ cargo run
Compiling nostd_hello v0.1.0 (/workspaces/nostd_hello)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s
Running `target/debug/nostd_hello`
Hello, world!
無事Hello World!を出力することができた、やったね!
参考
- https://qiita.com/0yoyoyo/items/9a7c96e1250b50c6b339
- https://www.mztn.org/dragon/arm6408cond.html
- https://arm64.syscall.sh/
- https://elixir.bootlin.com/linux/v6.9.5/source
-
asmマクロはnightlyだと思っていたらだいぶ前にstableに入ってたらしい、びっくり ↩︎
Discussion