운영체제를 만들기 위해서는 운영체제에 의존하지 않는 코드를 만들어야 한다.
그럼에도 불구하고 rust의 언어 자체 기능은 사용이 가능하다.(Iterator, Option, Result, Pattern Matching, etc...)
이렇게, 운영체제에 의존하지 않는 코드를 만드는 것을 freestanding binary(또는 bare-metal binary)라고 한다.
#![no_std]라는 attribute를 사용한다.#![no_std]
fn main() {
println!("Hello, world!"); // ERROR!
}
#![no_std]
fn main() {}
error: `#[panic_handler]` function required, but not found
error: language item required, but not found: `eh_personality`
|
= note: this can occur when a binary crate with `#![no_std]` is compiled for a target where `eh_personality` is defined in the standard library
= help: you may be able to compile for a target that doesn't need `eh_personality`, specify a target with `--target` or in `.cargo/config`
panic handler는 panic이 발생했을 때, panic을 처리하는 함수이다.
하지만 우리의 코드는 그 정도로 복자하지 않다. 그래서 매우 단순하게 panic handler를 만들어보자.
// in main.rs
use core::panic::PanicInfo;
/// 패닉이 일어날 경우, 이 함수가 호출됩니다.
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
-> !를 사용하여(never type 이라고 한다.) 반환하지 않음을 명시해준다.[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
warning: unused import: `panic::panic`
--> src/main.rs:5:5
|
5 | use panic::panic;
| ^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: crate-level attribute should be in the root module
--> src/panic/mod.rs:1:1
|
1 | #![no_std]
| ^^^^^^^^^^
|
= note: `#[warn(unused_attributes)]` on by default
error: requires `start` lang_item
처음 프로그래밍을 접하는(그리고 그다지 low-level programming을 하지 않는) 사람들은 대부분의 언어의 main 함수가 프로그램의 시작점이라고 생각할 수 있다.
main 함수는 언어가 어떤 프로그램을 실행할 준비를 마쳤을 때 실행되는 함수이지, 프로그램의 시작점이 아니다.crt0라고 불리는 C의 런타임 시스템이다.이렇게 런타임 시스템이 호출이 되고 나면, 런타임 시스템은 main 함수를 호출한다. -> 이는 rust의 start Language Item이다.
#![no_main] attribute를 사용한다.// in main.rs
#![no_std]
#![no_main]
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[no_mangle]
pub extern "C" fn _start() -> ! {
loop {}
}
위의 코드를 잘 보면 main이 사라졌다는 사실을 알 수 있다.
_start라는 함수가 생겼다. → 이는 프로그램의 새로운 시작 지점을 제공하는 함수이다.start Language Item을 구현한 것이 아니다. start Language Item은 main 함수를 호출하는 역할을 하는데, 이 함수는 crt0에서 호출되는 것이다. 따라서 지금 상황과 같이 런타임 시스템이 없는 경우에는 custom start Language Item을 구현한다 하더라도 아무런 의미가 없다.#[no_mangle] attribute는 rustc에게 말해주는 것이다. -> 이 함수의 이름을 _start로 해야 한다는 것을 말해주는 것이다.#[no_mangle] attribute를 사용하여 rustc에게 이름을 변경하지 말라고 말해준다.extern "C"의 경우에는 일반적인 rust의 호출규약이 아니라 C 함수 호출 규약을 사용하겠음을 알리는 코드이다.-> !를 사용하여 반환하지 않음을 명시해준다.이제 다시 빌드해보자.
error: linking with `cc` failed: exit status: 1
|
= note: LC_ALL="C" PATH="/home/bae/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/bae/anaconda3/bin:/home/bae/anaconda3/condabin:/home/bae/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin:/home/bae/.local/share/JetBrains/Toolbox/scripts:/opt/jdk/current/bin:/usr/bin/java:/opt/jdk/current/bin:/usr/bin/java" VSLANG="1033" "cc" "-m64" "/tmp/rustcSpk25h/symbols.o" "/home/bae/Github/rust_os/target/debug/deps/rust_os-cfd2a4d760122e45.3lka4t0njt4elio9.rcgu.o" "/home/bae/Github/rust_os/target/debug/deps/rust_os-cfd2a4d760122e45.46oixk3kh2rypau3.rcgu.o" "-Wl,--as-needed" "-L" "/home/bae/Github/rust_os/target/debug/deps" "-L" "/home/bae/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/home/bae/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-914eb40be05d8663.rlib" "/home/bae/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-27094fcca7e14863.rlib" "/home/bae/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-919e055b306699ae.rlib" "-Wl,-Bdynamic" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/home/bae/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/home/bae/Github/rust_os/target/debug/deps/rust_os-cfd2a4d760122e45" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs"
= note: /usr/bin/ld: /home/bae/Github/rust_os/target/debug/deps/rust_os-cfd2a4d760122e45.3lka4t0njt4elio9.rcgu.o: in function `_start':
/home/bae/Github/rust_os/src/main.rs:8: multiple definition of `_start'; /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o:(.text+0x0): first defined here
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x1b): undefined reference to `main'
/usr/bin/ld: (.text+0x21): undefined reference to `__libc_start_main'
collect2: error: ld returned 1 exit status
= note: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
= note: use the `-l` flag to specify native libraries to link
= note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)
error: could not compile `rust_os` (bin "rust_os") due to previous error
링커는 여러 개의 executable file(컴파일러가 컴파일한 파일)들을 한개의 실행 파일로 변경해주는 역할을 하는 프로그램이다.
이를 위해서는 링커에게 말해야 한다.
bare-metal로 설정한다..exe 파일을 생성할 것이다.(base) bae@bae-Legion-Slim-5-16IRH8:~/Github/rust_os$ rustc --version --verbose
rustc 1.75.0 (82e1608df 2023-12-21)
binary: rustc
commit-hash: 82e1608dfa6e0b5569232559e3d385fea5a93112
commit-date: 2023-12-21
host: x86_64-unknown-linux-gnu
release: 1.75.0
LLVM version: 17.0.6
위의 코드는 rustc --version --verbose의 결과로, 여기서 host에 현재 시스템 환경을 나타내는 문자열이 있다.
x86_64-unknown-linux-gnu라는 문자열은 CPU 아키텍처-제조사-운영체제-ABI를 나타낸다.우리가 목표로 하는 시스템 환경은 운영체제가 없는 시스템이다.
thumbv7em-none-eabihf target triple이 있다.rustup target add thumbv7em-none-eabihf--target flag를 사용하자.cargo build --target thumbv7em-none-eabihf