-- Leo's gemini proxy

-- Connecting to tilde.pink:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini;

Rust OS with stable toolchain


2024-01-06 @rrobin


One thing that annoys me is that I cannot compile a freestanding operating system kernel without using a nightly rust toolchain or can I?


rustc -V
rustc 1.73.0 (cc66ad468 2023-10-03) (built from a source tarball)

To compile a kernel we need to pass some special options to the rust compiler and arrange our code to:


set the panic strategy to abort

use the right compilation target (x86_64-unknown-none)

use the right linker options to avoid linking to libc

other target options such as no-redzone

cross compiling the core library for our target


This can be done in the nightly toolchain, which is seen is most tutorials out there. Usually core is provided by rustup or built using xargo. The core library uses unstable rust features and so it needs a nightly compiler, but we can work around this.


Building a kernel in Rust


Here is the piece of code we want to build, basically a loop that does nothing.

I have named the function _start since this is the default entry point for linkers, and used extern and no mangle so the symbol is available to the linker too. Finally the no_main and no_str attributes indicate we do not want to link against libstd nor do we have a main function.


#![no_main]
#![no_std]

#[no_mangle]
pub extern "C" fn _start() -> ! {
    loop {}
}

My first attempt to build is:


rustc osrc.rs
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`

error: aborting due to 2 previous errors

The second error can be solved by compiling with the option -Cpanic=abort. The first error requires implementing a panic handler function (which is usually provided by std).


So we include this function, which just runs a loop:


use core::panic::PanicInfo;

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

and try again:


rustc -Cpanic=abort osrs.rs
error: linking with `/gnu/store/f95ljc7bism88x1vrrggnqxa8r9i4llb-gcc-11.3.0/bin/gcc` failed: exit status: 1
  |
  = note: LC_ALL="en_US.UTF-8" PATH="/gnu/store/cqyg66k405whrn5qw5i523y6d06m1lry-rust-1.73.0/lib/rustlib/x86_64-unknown-linux-gnu/bin:/gnu/store/v5aqhqxjqdvh9fhi64ifxnfji562j178-ld-wrapper-0/bin:/gnu/store/cr3r1ry6y8ng1by9bj4nf6k89zk32cra-profile/bin:/gnu/store/cr3r1ry6y8ng1by9bj4nf6k89zk32cra-profile/sbin:/home/user/.guix-home/profile/bin:/home/user/.guix-home/profile/sbin:/home/user/.guix-home/profile/bin:/home/user/.guix-home/profile/sbin:/run/setuid-programs:/home/user/.config/guix/current/bin:/home/user/.guix-profile/bin:/run/current-system/profile/bin:/run/current-system/profile/sbin" VSLANG="1033" "/gnu/store/f95ljc7bism88x1vrrggnqxa8r9i4llb-gcc-11.3.0/bin/gcc" "-m64" "/tmp/rustcFWLjrA/symbols.o" "osrs.osrs.ca943b53624c998a-cgu.0.rcgu.o" "-Wl,--as-needed" "-L" "/gnu/store/cqyg66k405whrn5qw5i523y6d06m1lry-rust-1.73.0/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/gnu/store/cqyg66k405whrn5qw5i523y6d06m1lry-rust-1.73.0/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-28e275f9d01597fc.rlib" "/gnu/store/cqyg66k405whrn5qw5i523y6d06m1lry-rust-1.73.0/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-73d10225083d3008.rlib" "/gnu/store/cqyg66k405whrn5qw5i523y6d06m1lry-rust-1.73.0/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-87dd7e3d4a529231.rlib" "-Wl,-Bdynamic" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/gnu/store/cqyg66k405whrn5qw5i523y6d06m1lry-rust-1.73.0/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "osrs" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs"
  = note: ld: osrs.osrs.ca943b53624c998a-cgu.0.rcgu.o: in function `_start':
          osrs.ca943b53624c998a-cgu.0:(.text._start+0x0): multiple definition of `_start'; /gnu/store/cr3r1ry6y8ng1by9bj4nf6k89zk32cra-profile/lib/Scrt1.o:(.text+0x0): first defined here
          ld: /gnu/store/cr3r1ry6y8ng1by9bj4nf6k89zk32cra-profile/lib/Scrt1.o: in function `_start':
          (.text+0x17): undefined reference to `main'
          ld: (.text+0x1d): 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: aborting due to previous error

The compiler is calling the linker (gcc) and it is failing because it expects a main function to be present as well as __libc_start_main.


A second attempt passes the necessary linker options to allow us to build. This is the default gcc behaviour, we need to pass it options to disable this behaviour (-nostdlib):


rustc -Cpanic=abort -Clink-arg=-nostdlib osrs.rs

This compilation succeeds, but it is not exactly what we want. It generates a static executable that actually runs the infinite loop. That is a Linux executable. We can see it even uses the link loader (using the file command)


file osrs
osrs: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /gnu/store/ln6hxqjvz6m9gdd9s97pivlqck7hzs99-glibc-2.35/lib/ld-linux-x86-64.so.2, not stripped

The correct target for bare metal rust is x86_64-unknown-none. So lets try that


rustc --target=x86_64-unknown-none -Cpanic=abort -Clink-arg=-nostdlib osrs.rs
error[E0463]: can't find crate for `core`
  |
  = note: the `x86_64-unknown-none` target may not be installed
  = help: consider downloading the target with `rustup target add x86_64-unknown-none`

error[E0463]: can't find crate for `compiler_builtins`

error: requires `sized` lang_item

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0463`.

It failed because it needs two dependencies (core, compiler_builtins). The default toolchain installed in our system does not include the core crate for x86_64-unknown-none. The error message suggests using rustup to install it.


The dependency on compiler_builtins on core and compiler_builtins is implicitly added by rustc.


Compiling libcore for our target


core provides intrinsic functions. You can find its source in the rust-src component (under rust-src/library/core).


Building it requires a nightly toolchain because core relies on unstable Rust features. Instead of installing a nightly I'm relying on the stable toolchain I have. This can be done by setting the environment variable RUSTC_BOOTSTRAP=1, a trick used when bootstrapping rustc itself.


From the core source folder you can build it like this:


RUSTC_BOOTSTRAP=1 rustc -Cpanic=abort --crate-type lib --crate-name core --edition=2021 src/lib.rs --target x86_64-unknown-none

which should generate a libcore.rlib.


Compiling compiler_builtins for our target


Sadly I can't find compiler_builtins inside my rust-src folder. But this may be an issue with my own install.


I was able to find it vendored in the release tarball from


https://static.rust-lang.org/dist/rustc-1.73.0-src.tar.gz


compiler_builtins is also available on crates.io but I am using the same version vendored with rust 1.73.0. The vendored compiler_builtins is 0.1.100. While the latest version is 0.1.105.


Attempting to build compiler_builtins will try to find libcore, I will be using the one built earlier (-L argument):


RUSTC_BOOTSTRAP=1 rustc -L /home/user/Code/test/core/rust-src/library/core --target x86_64-unknown-none --crate-name compiler_builtins --edition=2018 src/lib.rs --crate-type lib   -C panic=abort --cfg 'feature="compiler-builtins"' --cfg 'feature="default"' --cfg 'feature="no-asm"' --cfg 'feature="unstable"' --cfg 'feature="mem"' --cfg 'feature="mem-unaligned"

It should be noted here that compiler_builtins has a build.rs build script. And the features will vary with the target. I gathered these via cargo -v.


Building against our core/compiler_builtins


Lets now attempt to build our program by addding the necessary -L flags:


rustc -L ~/Downloads/rustc-1.73.0-src/vendor/compiler_builtins/  -L /home/user/Code/test/core/rust-src/library/core  --target=x86_64-unknown-none -Cpanic=abort -Clink-arg=-nostdlib osrs.rs
error: linker `rust-lld` not found
  |
  = note: No such file or directory (os error 2)

error: aborting due to previous error

This error is funny, it seems like rustc inferred the linker to be rust-lld (based on the target?). But I don't have it installed.


Lets specify the linker (gcc) manually then:


rustc -Clinker=gcc -L ~/Downloads/rustc-1.73.0-src/vendor/compiler_builtins/  -L /home/user/Code/test/core/rust-src/library/core  --target=x86_64-unknown-none -Cpanic=abort -Clink-arg=-nostdlib osrs.rs

This builds, but we don't really want to generate an executable. Instead lets create a static library.


rustc --crate-type=staticlib  -Clinker=gcc -L ~/Downloads/rustc-1.73.0-src/vendor/compiler_builtins/  -L /home/user/Code/test/core/rust-src/library/core  --target=x86_64-unknown-none -Cpanic=abort -Clink-arg=-nostdlib osrs.rs

This will create a libosrc.a static library.


Incidentally, if you ever need rustc to print more information about the linking phase add the following environment variable


RUSTC_LOG=rustc_codegen_ssa::back::link=info

Actually booting this


x86_64 machines are particular in that we have to go through some work before we can run this code.


I wont go into the details here, IntermezzoOS has a really nice write up on to create a bootable ISO with assembly code that boots into long mode. We can use it here to run our _start function in the generated static library.


IntermezzoOS Booting up


References


https://doc.rust-lang.org/rustc/codegen-options/index.html

https://doc.rust-lang.org/rustc/platform-support/x86_64-unknown-none.html

https://os.phil-opp.com/freestanding-rust-binary/

https://doc.rust-lang.org/core/

https://wiki.osdev.org/Entering_Long_Mode_Directly

https://github.com/phil-opp/blog_os/blob/bef5f13560219e4402c995df4cc4d897bf2bca78/Makefile

https://intermezzos.github.io/book/

-- Response ended

-- Page fetched on Sun May 19 08:50:04 2024