 Thank you so much everyone for coming. I was I was worried I wouldn't have a seat with everyone outside Thank you for showing up So this talk is going to be Basically a high-level overview of some of the things that you're going to bump into when you're doing something with Rust and the Linux kernels so Rust is going to give you a lot of benefits as a low-level language and It doesn't work quite out of the box, so we're going to go over some of the gotchas that come about When you're trying to make this happen So essentially we're going to first talk about what is Rust and why are we using it? So that's a big question that comes up a lot when People discuss a new language it there are a lot of people that you know kind of need to be sold first on its usefulness so we'll go over that a little bit then we're going to do a demo of my kernel module and Then we'll talk about leveraging existing kernel tools for actually building Rust and Last we'll talk about the most interesting part, which is some rust specific gotchas so Things that you might not expect to bump into at a lower level and sort of how we can get around So first off what is rust and why are we using it the reason for rust? Tends to be that it's a highly systems oriented programming language. It's very high performance Despite it being a relatively new language started around 2010 then it's actually made a lot of headway in that time and so we got some really nice benefits like No garbage collection or manual man memory management and the allocations and Deallocations really are handled by the compiler for you it has a robust type system that is going to allow you to use unsafe to kind of opt out of those stricter security checks and It's LLVM based so it's going to give you a lot of those really nice optimizations All of that together with kind of the existing ecosystem of out of the box Documentation and built-in testing capabilities kind of make it a really attractive option for a lot of people So we'll talk a little bit about the safety guarantees So rust is strongly typed so it's able to do some pretty intense type validation for you It has ownership validation which at a high level is what scope owns this data at what time? It's also able to do mutability validation Which essentially says when we have references which of these references can mutate at a given time? and Together these provide some pretty attractive guarantees, so we're going to have memory safety, so no dangling pointers in safe rust We have data race safety So we can't access data from two locations at any one time and for right access and These guarantees can all be overridden with the unsafe key word So who here is familiar with what an ABI is raise of hands and Who here needs a review we can go through this? Okay, thank you, so in ABI at a high level is going to essentially be a standard of generated assembly that Basically guarantees that when you link two things together that it will be compatible, so this includes things like consistent assembly instructions Data type sizes and alignment, and it's also going to be calling conventions and register usage, so This is very important when it comes down to interoperability between languages, so a lot of times we'll have Interoperability between new languages and see kind of plagued by a lot of things like you know driving the runtime and garbage Collector from sea will have data type conversions that get pretty painful We'll have to just explicitly write some bindings and see and Kind of link that into our new language or sometimes we even have to write assembly and You know a lot of people like to avoid that so Rust's approach is that you can explicitly coerce functions to really stick to the C ABI at compile time and So we get some major benefits from this one of the things that we're going to get is that We can link directly with C and this is pretty simple compared to some of the other approaches I outlined Rust actually is going to natively support in its build system Linking C directly into the rest program. So even the build system works with it pretty simply and We really don't need any assembly for this whole process So this overall is going to make rust a very good candidate for kernel development Anything that we are not able to offload to rust we're able to still do in C so things like the C preprocessor are really going to need to be done in C there's no kind of type safe way to represent that and Particularly if we're accessing data fields in a struct that Is kind of littered with if deaths then at that point we are really going to want to offload that to see as well So we'll talk a little bit about the type system to rust is strongly typed. So we're going to get validation of data types passed into functions and Validation of pass by reference versus pass by value, but both of those things can already be done by C so What else does it kind of give us that we don't get in C and Some of these things are going to be things like explicit conversions only so we're never going to have sort of a Wiggly kind of type casting system. We actually have to Explicitly say how we're going to convert from one type to another and so this really avoids some undefined behavior that could pop up We're going to have type parameters or generics as they're known in a lot of other languages This is also validated and supported by the compiler We have Mutability checks and what this is going to do for us is we have two rules So we can either have one mutable reference and no immutable references To prevent sort of read-write corruption or we can have as many immutable references as we want And no mutable references. So you can kind of think of it like a read-write lock at the compiler level Ownership we use move semantics. And so Basically mutable and immutable references can be passed into functions But essentially that does not that does not constitute a move on own data types Will be moved when they're passed into a function and they cannot be used in the original scope after they have been moved These guarantee memory safety and data race safety kind of as we talked about before So when it comes to performance we often get into a lot of kind of nitty gritty number Related details and people always want to know which is faster see or rest and I'm going to kind of sidestep all of that because We're instead going to go over some non-controversial points. So Rust is going to produce native binaries There is no garbage collection or VMs. So essentially we're going to get some performance boost from that Rust uses LLVM as a back-end. So anything that Clang has in terms of optimizations. Those are also available to rust Rust is also going to use in intermediate representation and what this allows us to do is rust specific optimizations, so that happens before we compile it down to LLVM so When we're actually writing sort of more performance critical code that really relies on some kind of unsafe pointer magic then Rust also provides unsafe capabilities to drop down to that level and really do what we need to do we can kind of think of rust as generally in the same performance class as C and C++ and Rust's unsafe keyword which we mentioned in the last slide is going to allow us to do a few things We can dereference raw pointers. We can call code that has been marked as unsafe and We can access or modify mutable static variables. So These these kinds of operations are often needed when we're doing some sort of FFI and so This unsafe keyword is really kind of crucial for Building C into the actual nuts and bolts of the language So now we're going to have a little demo of my kernel module so The kernel module itself is pretty silly, but it does do some really important things So it's going to do some Chart of ice creation using the kernel API for that It has some u-text locking in there It's going to even be able to print out to the kernel logs and it even does some buffer operations that are actually marked as safe And sort of encode the length in the buffers So we don't really have to worry about buffer overflows here So it's really just going to create an animation that when you cat The device node then what it's going to do is it's going to display an animation so First I'll give you kind of the steps if you're looking to Reproduce this on your own machine and Now we'll go through it. So here is my project directory Is that good? Okay So we're just going to run make It's going to build this kernel module we're going to insert the module and now we're going to check the logs for the major and minor number and Slash dev slash parrot Here we go So what does this kernel module do we party hard? So How did I do this? Well, I leveraged actually a lot of existing tooling for the kernel So we have here a list of basically all of the tools you'll need for this But the most important one really is k build because that is the native system For the kernel builds and so we're actually able to leverage that because of rusts C ABI Compatibility to actually offload a lot of this So what changes well, we're going to have to do something a little bit more manager manual to basically create our object files and we'll have to do a few Function declaration level changes on the rest side and the way the kernel API methods are handled on the rest side We'll also have to change so cable really is going to be the easiest way to accomplish this task because It's make file based and accept C ABI compatible object files And it's also, you know, going to be semi shell based So we're able to call into different tools that we need in the rust ecosystem We will need to download some develop packages And while the cable documentation is pretty dense usually it will give you everything you could possibly need to know for something like this One note that you should definitely put a bookmark in is that it's going to ignore static libraries So this actually comes into play later On as for rust side changes will have to make two specific changes that are really important here One is going to be an attribute that we attach to the functions for entry points. That is no mangle and One is going to be just declaring this as compatible with the C ABI as an extra function Overall the rule is that we should keep everything Compatible with the C ABI so for example, we're going to want to use pointers instead of references and similar kind of C ABI compatible Data types that map directly to rust data types, but have a little bit less information encoded. So No mangle is really important because by default C is going to assign a label that Essentially just kind of maps from this entry point in the kernel To this label right here. So it's really a one-to-one correlation Rust is actually going to mangle the symbols So we have a knit module and it's going to take it out of the global namespace by mangling it as we see right here The solution is that on kernel entry points. We just attach no mangle to the top of this function and We're going to get the unmangled symbol when we actually talk about extern C function declarations then One note is that this is not going to validate that everything you have done is C ABI compliant So there is some work that you have to do to make sure you're doing everything correctly However, this is because not everything that is in rust is actually Compatible with the C ABI you can force it to become that way But it's not going to be that way by default and this should be relatively intuitive because Ultimately, we're representing things that are just not representable in the the C type system So data types really must conform to the C ABI. So we'll have integer primitives that are acceptable anything in the libc crate and Pointers to any of those acceptable types. We can also mark structs as specifically C ABI compatible and this is going to exclude a lot of things like Vectors and slices and a lot of other data types that we just can't use So slices will not work because they encode length and pointers do not References also encode additional information that pointers do not We're not going to be able to use you 128 because C does not support those at the C ABI level and We're also going to have to mark those structs as explicitly C ABI compatible because of memory optimizations that rust will do for us So we should talk a little bit about libcorp because this is going to be everything that we need from in terms of basically a standard library so you can think of libcorp as The standard library for embedded or systems work That's basically going to allow you to define your own allocation and do a little bit lower level things like that That essentially The standard library itself is going to Take for granted. So we're going to need to include this somehow in our build so It actually gets a little bit more complicated than I was originally thinking Rust can output a number of different output options, but ultimately we're going to want to go with static libraries So as you might remember static libraries are not compatible with k-build So we'll have to take some manual steps to actually make sure that this is going to Properly work with cable So this is a little bit more manual than some people might like but it does work consistently and because Static libraries really are just archived object files What we're going to want to do is basically unarchive that static library and Then with LD we can just link all of those object files together And we're going to get an object file that actually gives us exactly what we need This object file basically can then just be passed directly off to k-build So now we're going to talk about some rust specific gotchas So this is really defined in kind of three different levels. So we're going to have the symbol level We'll have the foreign function interface level and then we'll have global state and mutability So at the symbol level we've already kind of touched on no mangle So I'm not going to go over that again, but we also need some build specific changes for example, if you're doing this all manually then You will have to make sure that your code is compiled with some flags for example to put it in kernel address space and Basically use the procedural lookup table instead of the global offset table because of the nature of the kernel address space Now all of this can kind of be avoided and really has to be avoided because of lib core So when you're building normally with cargo the native build system Then what it's going to do is it's going to link in a pre-compiled lib core for you And when you do that then essentially you don't have any control over what options that's actually been compiled with When you start linking it in your code that you wrote might have the appropriate flags for the compilation however Lib core is not going to have that so really the only way that I found around this is to use xargo Which is a cross compiler for both your code and lib core and Then thankfully they were nice enough to put a Linux kernel target in that for us So this is going to be a lot simpler now at the ffi level. We have a few options So we have bind gen which is a tool that can generate bindings from C headers actually But this is not really going to work for us because bind gen doesn't actually work with the kernel headers Now we can do DIY bindings and some of that is really necessary because this is the only way to Expose kernel methods directly from kernel space into our rust code This gets a little bit messier because we actually have to make sure that our data types match up directly Between the kernel API and our API that we've exposed So this one should be done with caution, but it really is the only way to expose kernel methods directly We can also wrap kernel API calls in kind of safer C functions that have fewer parameters and this is actually really necessary when we're doing things with With the C preprocessor macros because when we get into that area, there's no way to translate that across the ffi boundary So really this is going to be a large part of what you end up having to do a lot of the time now Definitely examine the trade-offs carefully because all of these Options have different places and different kinds of ffi And the rule of thumb that I would say is if you're doing something with the C preprocessor macros Then essentially or anything with structs even that have if deaths in them You'll want to offload that to see because you may you may want to try to win that war But you will so this was kind of the most fun part really what we're going to do here is kind of say How can we get idiomatic rust outside of everything that we've built on top of? so one option that I chose for actually providing a safe interface to global state mutation is that I actually designed a Mutex that kind of has a similar pattern to the standard library that actually wraps the kernel mutex functionality So it's going to really give us that ability to kind of keep the rest promises That the language requires while also Giving us a little bit more idiomatic breast code So here we're going to Take a look through some of this code We essentially have our inner data type and so that's going to house our data that we're protecting We can actually Immutably acquire a mutex locks so when we do that we're calling into C which is going to call the kernel API and Then we get this mutex guard returned this mutex guard basically has a lifetime that means as long as It is in scope it will keep that lock acquired We can get a mutable reference from that mutex guard and then once it goes out of scope That's what drop is for then essentially it will unlock the mutex and so here what we're really getting is that The mutex guard is basically going to clean up after us And so we really don't have to worry about data races here We're essentially guaranteed that rust is going to clean up after us and this is one of the major benefits of the language It's going to protect us against program or error So in summary we can kind of Take a few things away from this there definitely are going to be some additional challenges Using rust and some C is definitely still required But rusts benefits really can be used effectively in the kernel We can kind of get a lot of things for free in terms of resource management and clean up without really a performance hit And ultimately there was a whole lot less unsafe than I thought was initially going to be required So thank you for attending Here is the link for My source code if you want to take a look through that and see more If you want to see just sort of a more in-depth example of this. Thank you very much Any questions? So I've seen a couple other rust and Linux kernel Projects and I'm just curious how many others you have looked at and what you explored before you did this So I actually have I've looked at maybe one when I started this project a while ago I looked around a little bit for this and I was actually unable to find very many when I started this I I'm not usually sure what the approach they take is and the purpose of this one was basically instead of really getting down into the weeds of Um Exactly how we can do everything from scratch. This really was intended to be how can we make this as easy as possible? And that's ultimately why you did this project in the beginning you show Tommel file and some console output And I saw that you use rust nightly does it mean that you cannot build your coat with stable rust? so Xargo is going to require nightly Because of the Linux kernel target you will require nightly for now I'm not sure what the plans are for stabilizing that but right now it does require Any more questions? Well, we'll wrap it up early there then. Thank you all very much