環境
Linux Mint 20.1 Ulyssa
rustc 1.53.0 nightly-x86_64-unknown-linux-gnu
動機
低レイヤーガールという YouTube チャンネルにてアセンブリで遊んでいるのを見まして、Rust の asm!
マクロでアセンブリを書いてみようということです。アセンブリは初心者なので、まずは "Hello, World!" で入門してみます。あと、こちらの「no_stdのRust on LinuxでHello, world!する」という記事を参考にしました。この記事では参考記事とは違う部分だけ書きます。
エントリポイント
リンカに-nostartfiles
というオプションを渡して、_start
関数内にアセンブリを書いていきます。
[build]
rustflags = ["-Clink-arg=-nostartfiles"]
# ![no_std]
# ![no_main]
# [no_mangle]
fn _start() {
// ここにアセンブリを書いていく!
}
# [panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {}
}
Segmentation fault は発生しますが、とりあえず実行できるバイナリができました。
アセンブリを書く
#1: FizzBuzzをアセンブリ言語で書きたい!を参考に "Hello, World!" のアセンブリを書いてみました。
# [no_mangle]
fn _start() {
unsafe {
asm!(
"mov rdx, 14",
"lea rsi, [string]",
"mov rdi, 1",
"mov rax, 1",
"syscall",
"mov rax, 60",
"syscall",
"string:",
".ascii \"Hello, World!\n\"",
);
}
}
ところがこれはリンカがrelocation R_X86_64_32S against '.text._start' can not be used when making a PIE object; recompile with -fPIE
というエラーを吐いてビルドに失敗しました。PIE
とはなんぞや?
Wiki より引用
位置独立コード(いちどくりつコード、英: position-independent code、PIC)または位置独立実行形式(いちどくりつじっこうけいしき、英: position-independent executable、PIE)とは、主記憶装置内のどこに置かれても絶対アドレスに関わらず正しく実行できる機械語の列である。
データ位置の絶対アドレス、相対アドレス的なものかな?(よくわからん…)とにかくリンカオプションに-pie
が付けてられてるのが原因っぽいのでこれを無効にする必要があります。
そのためにはどうやら-Crelocation-model=dynamic-no-pic
というオプションを rustc に渡せばいいようです。
[build]
rustflags = ["-Clink-arg=-nostartfiles", "-Crelocation-model=dynamic-no-pic"]
これで無事にビルド・実行できました。やったぜ
ちなみに文字列のアドレスを rip 相対lea rsi, [rip + string]
にしてやれば、-Crelocation-model=dynamic-no-pic
は不要でした。
rustc にアセンブリを吐かせる
rustc にアセンブリを吐かせるには以下のようにします。(intel 記法の場合)
cargo rustc --release -- --emit asm -Cllvm-args=-x86-asm-syntax=intel
.cargo/config.toml
に alias として設定しておくと楽ちんだと思います。下のように書いておくとcargo asm
コマンドでアセンブリが出力されます。
[alias]
asm = "rustc --release -- --emit asm -Cllvm-args=-x86-asm-syntax=intel"
アセンブリはtarget/release/deps/*.s
に出力されます。内容を覗いてみると#APP
と#NOAPP
の間にasm!
マクロ内の記述がそのまま出力されていました。
_start:
sub rsp, 8
#APP
mov rdx, 14
lea rsi, [string]
mov rdi, 1
mov rax, 1
syscall
mov rax, 60
syscall
string:
.ascii "Hello, World!\n"
#NO_APP
pop rax
ret
成果物
[package]
name = "hello_from_asm"
version = "0.1.0"
authors = ["benki"]
edition = "2018"
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
[alias]
asm = "rustc --release -- --emit asm -Cllvm-args=-x86-asm-syntax=intel"
[build]
rustflags = ["-Clink-arg=-nostartfiles", "-Crelocation-model=dynamic-no-pic"]
# ![no_std]
# ![no_main]
# ![feature(asm)]
# [no_mangle]
fn _start() {
unsafe {
asm!(
"mov rdx, 14",
"lea rsi, [string]",
"mov rdi, 1",
"mov rax, 1",
"syscall",
"mov rax, 60",
"syscall",
"string:",
".ascii \"Hello, World!\n\"",
);
}
}
# [panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {}
}