7 ms·
Writing an OS in Rust: Better Exception Messages
- vvanders 10y agoCool stuff, what's the significance of "!" as a return value? Haven't seen that one before.
- webkike 10y agoapparently it indicates divergence, i.e. the function does not return at all. Very interesting, I've never seen the notation before.
- phil-opp 10y agoSee https://doc.rust-lang.org/book/functions.html#diverging-functions https://doc.rust-lang.org/book/functions.html#diverging-func... and https://doc.rust-lang.org/reference.html#diverging-functions https://doc.rust-lang.org/reference.html#diverging-functions
- Gankro 10y agoSwift encodes this with a noreturn attribute, and I think it's a "standard not standard" C attribute as well. (in case you were wondering who else does this)
- alexbock 10y ago_Noreturn is a standard keyword in C11. C++11 has a standard attribute called [[noreturn]].
- pcwalton 10y agoIt means that the function never returns (via exiting the process, exiting the thread, or by panicking). It's useful because the result of a function that returns ! is compatible with any type, so you can write things like: fn usage() -> ! { println!("usage: myapp FILENAME"); process::exit(0) } let filename = if args.len() < 2 { args[1].clone() } else { usage() };
- vvanders 10y agoGood to know, that explains why I've never seen it.
- swsieber 10y agoAnd an RFC for it to become its own type recently got approved: https://github.com/rust-lang/rfcs/pull/1216 https://github.com/rust-lang/rfcs/pull/1216 (The github thread also has a ton of good discussion about it. I learned a lot). Up until now it's been a big edge case. This makes it considerably smaller.
- winter_blue 10y agoIs ! verified or enforced; i.e. does the compiler check to make sure that the functions does terminate in some way (besides returning) ?
- jonesetc 10y agoLooks like it [0]. The author had to use inline assembly, so compiler couldn't enforce it and failed to compile. They had to add an annotation to say that the code after the assembly was unreachable. [0] http://os.phil-opp.com/better-exception-messages.html#intrinsics-unreachable http://os.phil-opp.com/better-exception-messages.html#intrin...
- Gankro 10y agoIt's the exact same analysis as ensuring all paths of a function do return the right type -- you just make sure all paths call a divergent function (typically unconditional panic) or enter a loop {} that never breaks/returns. This is of course a conservative analysis so e.g. "while true { }" is assumed to terminate and is rejected. Failing to perform this verification correctly would trivially lead to memory unsafety as calling code is allowed to assume such a function returns any and all requested types.
- pcwalton 10y agoIt'd be type and memory unsafe if it weren't verified, so yes, it is.
- frutiger 10y agoSee: https://en.wikipedia.org/wiki/Bottom_type https://en.wikipedia.org/wiki/Bottom_type
- tectonic 10y agoThe whole blog, at http://os.phil-opp.com http://os.phil-opp.com, is phenomenal.
- phil-opp 10y agoThanks so much!
- _RPM 10y agoDoes Linux have exceptions?
- pcwalton 10y agoAll CPUs with MMUs use them, so Linux has to handle them.
- emcrazyone 10y agoumm, that's not true. Not all CPUs with MMUs have hardware support for memory based exception handling often the job of a MPU (memory protection unit). The ARM Cortex-M4 says it's optional [1] and as such Ti Jacinto6 processors don't implement it. [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/BIHJJABA.html http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc....
- comex 10y agoThe Cortex-M4 does not have a MMU; there are no page tables or anything. The MPU you mention is a sort of primitive MMU.
- emcrazyone 10y agoIt does on the Ti Jacinto6. In fact there are two MMUs. Cortex-M4 MMUs are implementation specific. The Jacinto6 (aka J6) M4s are housed in what they call an IPU. Each IPU (2 of them) have 2 Cortex-M4 cores for a total of 4. Each IPU has an IPU MMU (called an AMMU) and they also can access memory through the L3 MMU; the same that the A15 cores of that chip use. So when you say the Cortex-M4 does not have an MMU, you're categorically wrong because it can and does have one on Ti J6 chips.
- exDM69 10y agoPretty cool that this was done in pure Rust, although it might have been easier to write a small trampoline in assembler code that calls into Rust code. This is what I did in a hobby kernel project of mine [0]. This was pretty simple to do and easy to do all interrupt handlers at one go, leaving the actual logic to C code. The downside is that it uses unnecessary amount of memory (at least 16 bytes per interrupt vector). The Linux kernel has a similar but more clever solution where the actual interrupt vectors are tightly packed and the number of interrupt is determined with arithmetic from the instruction pointer. I can't find a link right now, if anyone finds it, please post a link. Side note: the use of Intel syntax is an annoyance. You can write inline asm in Intel syntax but it's not exactly the same as nasm/masm style and you still have to read at&t syntax in objdump output (by default at least). I prefer sticking to at&t when working with GNU tools. Even if you prefer Intel, it's still better to have just one syntax. [0] https://github.com/rikusalminen/danjeros/blob/master/src/arch/x86_64/isr.s https://github.com/rikusalminen/danjeros/blob/master/src/arc...
- Animats 10y ago"although it might have been easier to write a small trampoline in assembler code that calls into Rust code." That's what he did; it's just embedded inside Rust code. This is the easy part - capturing exceptions. Handling them, and returning control with the machine state intact, is the messy part. (It's late to be writing an operating system with demand paging. The most paging ever accomplishes is the illusion of 2X the RAM. RAM is cheap today. The price you pay for paging is random large delays in your program. Is it worth it to save some RAM cost? Mobile devices and real-time OSs don't demand-page.)
- phil-opp 10y ago> This is the easy part - capturing exceptions. Handling them, and returning control with the machine state intact, is the messy part. Yeah, I plan to tackle this in the next post: Compiling without red zone, saving/restoring all relevant registers, and disabling SSE instructions in the kernel to speed things up. > (It's late to be writing an operating system with demand paging. The most paging ever accomplishes is the illusion of 2X the RAM. RAM is cheap today. The price you pay for paging is random large delays in your program. Is it worth it to save some RAM cost? Mobile devices and real-time OSs don't demand-page.) I'm not sure if I really want to implement demand paging, I've just used it as an example. In the next post we will only implement lazy page mapping, so that we no longer need to preinitialize the complete kernel heap.