🦀

Rustのno_stdでHello World!(Linux on aarch64)

2024/06/19に公開

no_stdでHello World!をしたい

libcを使わずに直でシステムコールを呼び出したい欲求はプログラマの三大欲求のうちの一つであるため(?)、
欲求に従って手元のmacでこちらの記事を参考にシステムコールを呼び出そうとしたところ、archが違うのだから呼び出し規約も違うという現実に突き当たった(そうだねという感じ)
なんとかなったため、なんとかした方法を紹介する
(mac上でlinuxシステムコールを呼び出す方法は普通にDockerとかdevcontainerをコネコネした)

aarch64のsyscall convention

x86_64ではraxレジスタに呼び出すsyscall number、rdirsiに第一引数、第二引数と続き、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!を出力することができた、やったね!

参考

脚注
  1. asmマクロはnightlyだと思っていたらだいぶ前にstableに入ってたらしい、びっくり ↩︎

Discussion