formatting, elaborating on build-std/cargo unstable
This commit is contained in:
parent
ea5ad2ced0
commit
56aa40fb4c
|
|
@ -1,12 +1,17 @@
|
||||||
* Writing an x86-64 OS (mostly) from scratch in Rust
|
* Writing an x86-64 OS (mostly) from scratch in Rust
|
||||||
|
|
||||||
I’ve always been fascinated by the idea of bootstrapping.
|
I’ve always been fascinated by the idea of bootstrapping.
|
||||||
|
|
||||||
Combined with a curiosity for learning about how things work, this has lead to me often asking the question: if all I had were my hands, how far into the “tech-tree” of humanity could I get? The answer, unfortunately, is likely not very far at all. Certainly, I would never be able to put together any sort of machine that resembles what we today understand to be a computer.
|
Combined with a curiosity for learning about how things work, this has lead to me often asking the question: if all I had were my hands, how far into the “tech-tree” of humanity could I get? The answer, unfortunately, is likely not very far at all. Certainly, I would never be able to put together any sort of machine that resembles what we today understand to be a computer.
|
||||||
|
|
||||||
The term “bootstrapping” famously comes from the saying “pull oneself up by one’s bootstraps”, which is rarely understood for what it actually is: an impossible task, a paradox.
|
The term “bootstrapping” famously comes from the saying “pull oneself up by one’s bootstraps”, which is rarely understood for what it actually is: an impossible task, a paradox.
|
||||||
So, when tackling the problem of bootstrapping anything computer related, it is important to pick ones battles, and know where to start and what the goal is.
|
So, when tackling the problem of bootstrapping anything computer related, it is important to pick ones battles, and know where to start and what the goal is.
|
||||||
For most people, for economic reasons, this means starting with a computer and a good bit of software already in place.
|
For most people, for economic reasons, this means starting with a computer and a good bit of software already in place.
|
||||||
|
|
||||||
Still, there are many paths one can take to either fulfil ones curiosity about how things work, or to feel the satisfaction of having something that are built by yourself.
|
Still, there are many paths one can take to either fulfil ones curiosity about how things work, or to feel the satisfaction of having something that are built by yourself.
|
||||||
|
|
||||||
One might, from there, attempt to bootstrap a compiler (typically a simple C compiler), starting from nothing but the simplest hand-written binary blob that can turn a text file containing hex digits into a binary executable, and then build on that, step by step, every successive tool becoming more complex and more ergonomic, until, ex-nihil, a C compiler has appeared which is capable of building itself, as well as old versions of binutils and the gcc toolchain. From there, it’s possible to build every piece of software found on the modern Linux system.
|
One might, from there, attempt to bootstrap a compiler (typically a simple C compiler), starting from nothing but the simplest hand-written binary blob that can turn a text file containing hex digits into a binary executable, and then build on that, step by step, every successive tool becoming more complex and more ergonomic, until, ex-nihil, a C compiler has appeared which is capable of building itself, as well as old versions of binutils and the gcc toolchain. From there, it’s possible to build every piece of software found on the modern Linux system.
|
||||||
|
|
||||||
The journey that I will hopefully document in this blog, both learning my self and hopefully acting as a reference for others also interested in the same topic, will be the building of a simple operating system for the average modern computer running the x86-64 architecture.
|
The journey that I will hopefully document in this blog, both learning my self and hopefully acting as a reference for others also interested in the same topic, will be the building of a simple operating system for the average modern computer running the x86-64 architecture.
|
||||||
Instead of, how the little purist voice in my head demands, starting, as described above, from nothing and writing my own compiler and language, I will instead use the awesome Rust programming language.
|
Instead of, how the little purist voice in my head demands, starting, as described above, from nothing and writing my own compiler and language, I will instead use the awesome Rust programming language.
|
||||||
This is both because writing an assembler and multiple compilers from scratch is a lot of very tedious work, and because Rust is, in my opinion, the best language available for the task at hand.
|
This is both because writing an assembler and multiple compilers from scratch is a lot of very tedious work, and because Rust is, in my opinion, the best language available for the task at hand.
|
||||||
|
|
@ -84,7 +89,7 @@ cargo new --bin kernel
|
||||||
|
|
||||||
Most software will at some point want to make use of or work together in some way with other software running on the same machine, including most certainly the kernel, the very thing we are trying to replace.
|
Most software will at some point want to make use of or work together in some way with other software running on the same machine, including most certainly the kernel, the very thing we are trying to replace.
|
||||||
In Rust, the mechanisms for interfacing with other software or the kernel are provided by the standard library =std=. =std= itself depends on a number of libraries which are not available to us, in our bare-metal, operating system-less environment.
|
In Rust, the mechanisms for interfacing with other software or the kernel are provided by the standard library =std=. =std= itself depends on a number of libraries which are not available to us, in our bare-metal, operating system-less environment.
|
||||||
To tell the rust compiler that we don’t want to use the standard library, we can use the crate-level attribute =no_std=; we will still have access to a large portion of the =std= library via the =core= and =alloc= libraries, which =std= typically re-exports.
|
To tell the rust compiler that we don’t want to use the standard library, we can use the crate-level attribute =no_std=; we will still have access to a large portion of the =std= library via the =core= and =alloc= libraries, which =std= re-exports.
|
||||||
|
|
||||||
A typical Rust program starts in the =main= function, but this is not actually where the final program itself starts running from. Before the users =main= function, things like environment variables and command line arguments are set up.
|
A typical Rust program starts in the =main= function, but this is not actually where the final program itself starts running from. Before the users =main= function, things like environment variables and command line arguments are set up.
|
||||||
|
|
||||||
|
|
@ -105,7 +110,7 @@ fn _start() -> ! {
|
||||||
}
|
}
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
As is, attempting to build this code will throw linker errors, because cargo will still attempt to link with its own =_start= function and the aforementioned scaffolding around the =main= function. To tell cargo off, we will define our own custom target specification file which will also allow us to conveniently special-case the build for our kernel via cargos configuration later.
|
As is, attempting to build this code will throw linker errors, because cargo will still attempt to link with its own =_start= function and the aforementioned scaffolding around the =main= function. To tell cargo off, we will define our own custom target specification file which will also allow us to conveniently special-case the build for our kernel via cargo's configuration later.
|
||||||
|
|
||||||
#+begin_src json
|
#+begin_src json
|
||||||
{
|
{
|
||||||
|
|
@ -168,6 +173,14 @@ build-std = ["core", "compiler_builtins"]
|
||||||
|
|
||||||
Because we plan on using builtin functions like =memcpy=, we tell cargo to build the =compiler_builtins= crate with the =mem= feature enabled.
|
Because we plan on using builtin functions like =memcpy=, we tell cargo to build the =compiler_builtins= crate with the =mem= feature enabled.
|
||||||
|
|
||||||
|
Unstable fields in =cargo/config.toml= are unstable cargo features that can otherwise be enabled by passing the =-Z= flag to cargo, as we did above for the =json-target-spec= feature.
|
||||||
|
A list of unstable cargo features can be found [[https://doc.rust-lang.org/cargo/reference/unstable.html][here]].
|
||||||
|
=build-std= takes as argument a list of crates to build from the standard library. We only want =core= and =compiler_builtins=, but in the future we may want to add =alloc=. Any crate that is part of the library workspace can be specified here, though some make no sense to specift, or even break cargo.
|
||||||
|
=core= implies =compiler_builtins=, but since =build-std= is an unstable feature and may change in the future, we explicitly specify both to be safe.
|
||||||
|
|
||||||
|
=build-std-features= takes a list of features to pass to the crates specified in =build-std=.
|
||||||
|
For a complete list of crates and features, I recommend exploring the =library= directory of the Rust source code.
|
||||||
|
|
||||||
Rust permits the user to abort the program at any time by calling the =panic!= macro, or a function that “panics” internally. Typically, the mechanism by which this happens, as well as the more complex behaviour of unwinding the stack and catching unwinds, is provided by the =std=. Since we’ve opted out of using =std=, we will need to provide a panic handler for rust to call in case of a panic. Unwinding is already disabled by the =panic-strategy= field in our target specification file.
|
Rust permits the user to abort the program at any time by calling the =panic!= macro, or a function that “panics” internally. Typically, the mechanism by which this happens, as well as the more complex behaviour of unwinding the stack and catching unwinds, is provided by the =std=. Since we’ve opted out of using =std=, we will need to provide a panic handler for rust to call in case of a panic. Unwinding is already disabled by the =panic-strategy= field in our target specification file.
|
||||||
|
|
||||||
For the time being, we will again simply spin in an infinite loop whenever a panic occurs:
|
For the time being, we will again simply spin in an infinite loop whenever a panic occurs:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue