mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 14:27:49 +00:00
Highlight changed lines in code examples + other improvements
This commit is contained in:
@@ -102,7 +102,7 @@ You can compile your crate through `cargo build` and then run the compiled `kern
|
||||
Right now our crate implicitly links the standard library.
|
||||
Let's try to disable this by adding the [`no_std` attribute]:
|
||||
|
||||
```rust
|
||||
```rust,hl_lines=3
|
||||
// main.rs
|
||||
|
||||
#![no_std]
|
||||
@@ -112,7 +112,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
When we try to build it now (by running `cargo build`), the following error occurs:
|
||||
When we try to build it now (by running `cargo build`), the following errors occur:
|
||||
|
||||
```
|
||||
error: cannot find macro `println!` in this scope
|
||||
@@ -127,8 +127,7 @@ error: language item required, but not found: `eh_personality`
|
||||
[...]
|
||||
```
|
||||
|
||||
There are multiple errors.
|
||||
The reason for the first one is that the [`println` macro] is part of the standard library, which we no longer include.
|
||||
The reason for the first error is that the [`println` macro] is part of the standard library, which we no longer include.
|
||||
So we can no longer print things.
|
||||
This makes sense, since `println` writes to [standard output], which is a special file descriptor provided by the operating system.
|
||||
|
||||
@@ -137,7 +136,7 @@ This makes sense, since `println` writes to [standard output], which is a specia
|
||||
|
||||
So let's remove the printing and try again with an empty main function:
|
||||
|
||||
```rust
|
||||
```rust,hl_lines=5
|
||||
// main.rs
|
||||
|
||||
#![no_std]
|
||||
@@ -158,15 +157,19 @@ The `println` error is gone, but the compiler is still missing a `#[panic_handle
|
||||
### Panic Implementation
|
||||
|
||||
The `panic_handler` attribute defines the function that the compiler should invoke when a [panic] occurs.
|
||||
The standard library provides its own panic handler function, but in a `no_std` environment we need to define it ourselves:
|
||||
The standard library provides its own panic handler function, but in a `no_std` environment we need to define one ourselves:
|
||||
|
||||
[panic]: https://doc.rust-lang.org/stable/book/ch09-01-unrecoverable-errors-with-panic.html
|
||||
|
||||
```rust
|
||||
```rust,hl_lines=3 9-13
|
||||
// in main.rs
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#![no_std]
|
||||
|
||||
fn main() {}
|
||||
|
||||
/// This function is called on panic.
|
||||
#[panic_handler]
|
||||
fn panic(_info: &PanicInfo) -> ! {
|
||||
@@ -222,7 +225,7 @@ There are multiple ways to set the panic strategy, the easiest is to use [cargo
|
||||
|
||||
[cargo profiles]: https://doc.rust-lang.org/cargo/reference/profiles.html
|
||||
|
||||
```toml
|
||||
```toml,hl_lines=3-7
|
||||
# in Cargo.toml
|
||||
|
||||
[profile.dev]
|
||||
@@ -235,9 +238,7 @@ panic = "abort"
|
||||
This sets the panic strategy to `abort` for both the `dev` profile (used for `cargo build`) and the `release` profile (used for `cargo build --release`).
|
||||
Now the `eh_personality` language item should no longer be required.
|
||||
|
||||
|
||||
Now we fixed both of the above errors.
|
||||
However, if we try to compile it now, another error occurs:
|
||||
When we try to compile our kernel now, a new error occurs:
|
||||
|
||||
```
|
||||
❯ cargo build
|
||||
@@ -284,13 +285,13 @@ You can see the target triple for your host system by running `rustc --version -
|
||||
[_target triple_]: https://clang.llvm.org/docs/CrossCompilation.html#target-triple
|
||||
|
||||
```
|
||||
rustc 1.66.0 (69f9c33d7 2022-12-12)
|
||||
rustc 1.68.1 (8460ca823 2023-03-20)
|
||||
binary: rustc
|
||||
commit-hash: 69f9c33d71c871fc16ac445211281c6e7a340943
|
||||
commit-date: 2022-12-12
|
||||
commit-hash: 8460ca823e8367a30dda430efda790588b8c84d3
|
||||
commit-date: 2023-03-20
|
||||
host: x86_64-unknown-linux-gnu
|
||||
release: 1.66.0
|
||||
LLVM version: 15.0.2
|
||||
release: 1.68.1
|
||||
LLVM version: 15.0.6
|
||||
```
|
||||
|
||||
The above output is from a `x86_64` Linux system.
|
||||
@@ -339,7 +340,9 @@ We still get the error about a missing `start` language item because we're still
|
||||
|
||||
To tell the Rust compiler that we don't want to use the normal entry point chain, we add the `#![no_main]` attribute.
|
||||
|
||||
```rust
|
||||
```rust,hl_lines=4
|
||||
// main.rs
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
@@ -356,7 +359,9 @@ You might notice that we removed the `main` function.
|
||||
The reason is that a `main` doesn't make sense without an underlying runtime that calls it.
|
||||
Instead, we are now overwriting the operating system entry point with our own `_start` function:
|
||||
|
||||
```rust
|
||||
```rust,hl_lines=3-6
|
||||
// in main.rs
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
loop {}
|
||||
@@ -401,7 +406,8 @@ cargo build --target x86_64-unknown-none --release
|
||||
The compiled executable is placed at `target/x86_64-unknown-none/release/kernel` in this case.
|
||||
|
||||
In the next post we will cover how to turn this kernel into a bootable disk image that can be run in a virtual machine or on real hardware.
|
||||
But before that, we will examine our executable to verify that it looks as expected.
|
||||
In the rest of this post, we will introduce some tools for examining our kernel executable.
|
||||
These tools are very useful for debugging future issues, so it's good to know about them.
|
||||
|
||||
## Useful Tools
|
||||
|
||||
@@ -411,8 +417,8 @@ In this section, we will examine our kernel executable using the [`objdump`], [`
|
||||
[`nm`]: https://man7.org/linux/man-pages/man1/nm.1.html
|
||||
[`size`]: https://man7.org/linux/man-pages/man1/size.1.html
|
||||
|
||||
If you're on a UNIX system, you might already have the `nm`, `objdump`, and `strip` tools installed.
|
||||
Otherwise, you can use the LLVM binutils shipped by `rustup` through the [`cargo-binutils`] crate.
|
||||
If you're on a UNIX system, you might already have the above tools installed.
|
||||
Otherwise (and on Windows), you can use the LLVM binutils shipped by `rustup` through the [`cargo-binutils`] crate.
|
||||
To install it, run **`cargo install cargo-binutils`** and **`rustup component add llvm-tools-preview`**.
|
||||
Afterwards, you can run the tools through `rust-nm`, `rust-objdump`, and `rust-strip`.
|
||||
|
||||
@@ -425,26 +431,26 @@ To verify that it is properly exposed in the executable, we can run `nm` to list
|
||||
|
||||
```
|
||||
❯ rust-nm target/x86_64-unknown-none/debug/kernel
|
||||
0000000000002218 d _DYNAMIC
|
||||
0000000000001210 T _start
|
||||
0000000000201120 T _start
|
||||
```
|
||||
|
||||
If we comment out the `_start` function or if we remove the `#[no_mangle]` attribute, the `_start` symbol is no longer there after recompiling:
|
||||
|
||||
```
|
||||
❯ rust-nm target/x86_64-unknown-none/debug/kernel
|
||||
0000000000002218 d _DYNAMIC
|
||||
```
|
||||
|
||||
This way we can ensure that we set the `_start` function correctly.
|
||||
|
||||
### `objdump`
|
||||
|
||||
The `objdump` tool can inspect different parts of executables that use the [ELF file format].
|
||||
The `objdump` tool can inspect different parts of executables that use the [ELF file format]. This is the file format that the `x86_64-unknown-none` target uses, so we can use `objdump` to inspect our kernel executable.
|
||||
|
||||
[ELF file format]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
||||
|
||||
#### File Headers
|
||||
|
||||
Upon other things, the ELF [file header] specifies the target architecture and the entry point address of the executable files.
|
||||
Among other things, the ELF [file header] specifies the target architecture and the entry point address of the executable files.
|
||||
To print the file header, we can use `objdump -f`:
|
||||
|
||||
[file header]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
|
||||
@@ -462,7 +468,7 @@ The start address specifies the memory address of our `_start` function.
|
||||
Here the function name `_start` becomes important.
|
||||
If we rename the function to something else (e.g., `_start_here`) and recompile, we see that no start address is set in the ELF file anymore:
|
||||
|
||||
```
|
||||
```hl_lines=5
|
||||
❯ rust-objdump -f target/x86_64-unknown-none/debug/kernel
|
||||
|
||||
target/x86_64-unknown-none/debug/kernel: file format elf64-x86-64
|
||||
|
||||
@@ -270,6 +270,15 @@ pre code {
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
}
|
||||
pre mark {
|
||||
// make highlights take the full width
|
||||
display: block;
|
||||
// use the code color (instead of black)
|
||||
color: inherit;
|
||||
// override the background color (instead of black)
|
||||
background-color: rgba(0, 255, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
margin-bottom: 1rem;
|
||||
border-radius: 4px;
|
||||
|
||||
Reference in New Issue
Block a user