Rust에서 #![no_std]의 "Hello, World!"을 시도하십시오.

8761 단어 Rust

환경



Linux Mint 20.1 Ulyssa
rustc 1.53.0 nightly-x86_64-unknown-linux-gnu

동기



낮은 레이어 소녀이라는 YouTube 채널에서 어셈블리에서 놀고있는 것을 보면 Rust asm! 매크로로 어셈블리를 작성하려고합니다. 어셈블리는 초보자이므로 먼저 "Hello, World!"로 입문해 보겠습니다. 그리고, 이쪽의 「리눅스에서 no_std의 Rust on Hello, world!」라고 하는 기사를 참고로 했습니다. 이 기사에서는 참고 기사와는 다른 부분만 씁니다.

진입점



링커에 -nostartfiles 옵션을 전달하고 _start 함수에 어셈블리를 작성합니다.

.cargo/config.toml
[build]
rustflags = ["-Clink-arg=-nostartfiles"]

main.rs
#![no_std]
#![no_main]

#[no_mangle]
fn _start() {
    // ここにアセンブリを書いていく!
}

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

Segmentation fault는 발생하지만, 우선 실행할 수 있는 바이너리가 생겼습니다.

어셈블리 작성



#1: FizzBuzz를 어셈블리 언어로 쓰고 싶다!을 참고로 "Hello, World!"의 어셈블리를 작성해 보았습니다.

main.rs
#[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 에 건네주면 좋을 것 같습니다.

cargo/config.toml
[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 명령으로 어셈블리가 출력됩니다.

.cargo/config.toml
[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

아티팩트



Cargo.toml
[package]
name = "hello_from_asm"
version = "0.1.0"
authors = ["benki"]
edition = "2018"

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"

.cargo/config.toml
[alias]
asm = "rustc --release -- --emit asm -Cllvm-args=-x86-asm-syntax=intel"

[build]
rustflags = ["-Clink-arg=-nostartfiles", "-Crelocation-model=dynamic-no-pic"]

main.rs
#![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 {}
}

좋은 웹페이지 즐겨찾기