Skip to content

Commit ccc71df

Browse files
committed
Use rustc directly instead of cargo
This is a big PR, but most of it is interdependent to the rest. - Shared Rust infrastructure: `libkernel`, `libmodule`, `libcore`, `liballoc`, `libcompiler_builtins`. + The Rust modules are now much smaller since they do not contain several copies of those libraries. Our example `.ko` on release is just 12 KiB, down from 1.3 MiB. For reference: `vmlinux` on release w/ Rust is 23 MiB (compressed: 2.1 MiB) `vmlinux` on release w/o Rust is 22 MiB (compressed: 1.9 MiB) i.e. the bulk is now shared. + Multiple builtin modules are now supported since their symbols do not collide against each other (fixes #9). + Faster compilation (less crates to compile & less repetition). + We achieve this by compiling all the shared code to `.rlib`s (and the `.so` for the proc macro). For loadable modules, we need to rely on the upcoming v0 Rust mangling scheme, plus we need to export the Rust symbols needed by the `.ko`s. - Simpler, flat file structure: now a small driver may only need a single file like `drivers/char/rust_example.rs`, like in C. + All the `rust/*` and `driver/char/rust_example/*` files moved to fit in the new structure: less files around. - Only `rust-lang/{rust,rust-bindgen,compiler-builtins}` as dependencies. + Also helps with the faster compilation. - Dependency handling integration with `Kbuild`/`fixdep`. + Changes to the Rust standard library, kernel headers (bindings), `rust/` source files, `.rs` changes, command-line changes, flag changes, etc. all trigger recompilation as needed. + Works as expected with parallel support (`-j`). - Automatic generation of the `exports.c` list: + Instead of manually handling the list, all non-local functions available in `core`, `alloc` and `kernel` are exported, so all modules should work, regardless of what they need, and without failing linking due to symbols in the manual list not existing (e.g. due to differences in config options). + They are a lot, though: * ~6k Rust symbols vs. ~4k C symbols in release. * However, 4k of those are `bindings_raw` (i.e. duplicated C ones), which shouldn't be exported. Thus we should look into making `bindings_raw` private to the crate (at the moment, the (first) Rust example requires `<kernel::bindings...::miscdevice as Default>::default`). + Licensing: * `kernel`'s symbols are exported as GPL. * `core`'s and `alloc`'s symbols are exported as non-GPL so that third-parties can build Rust modules as long as they write their own kernel support infrastructure, i.e. without taking advantage of `kernel`. This seemed to make the most sense compared to other exports from the kernel, plus it follows more closely the original licence of the crates. - Support for GCC-compiled kernels: + The generated bindings do not have meaningful differences in our release config, between GCC 10.1 and Clang 11. + Other configs (e.g. our debug one) may add/remove types and functions. That is fine unless we use them form our bindings. + However, there are config options that may not work (e.g. the randstruct GCC plugin if we use one of those structs). - Support for `arm64` architecture: + Added to the CI: BusyBox is cross-compiled on the fly (increased timeout a bit to match). + Requires weakening of a few compiler builtins and adding `copy_{from,to}_user` helpers. - Proper `make clean` support. - Offline builds by default (there is no "online compilation" anymore; fixes #17). - No interleaved Cargo output (fixes #29). - No nightly dependency on Cargo's `build-std`; since now we manage the cross-compilation ourselves (should fix #27). - "Big" kallsyms symbol support: + I already raised ksym names from 128 to 256 back when I wrote the first integration. However, Rust symbols can be huge in debug/non-optimized, so I increased it again to 512; plus the module name from 56 to 248. + In turn, this required tuning the table format to support 2-byte lengths for ksyms. Compression at generation and kernel decompression is covered, although it may be the case that some script/tool also requires changes to understand the new table format. - Since now a kernel can be "Rust-enabled", a new `CONFIG_RUST` option is added to enable/disable it manually, regardless of whether one has `rustc` available or not (`CONFIG_HAS_RUST`). - Improved handling of `rustc` flags (`opt-level`, `debuginfo`, etc.), by default following what the user selected for C, but customizable through a Kconfig menu. As well as options for tweaking overflow checks and debug assertions. - This rewrite of the Kbuild support is cleaner, i.e. less hacks in general handling paths (e.g. no more `shell readlink` for `O=`). - Duplicated the example driver 3 times so that we can test in the CI that 2 builtins and 2 loadables work, all at the same time. - Do not export any helpers' symbols. - Updated the quick start guide. - Updated CI: + Now we always test with 2 builtins and 2 loadables Rust example drivers, removing the matrix test for builtin/loadable. + Added `toolchain` to matrix: now we test building with GCC, Clang or a full LLVM toolchain. + Added `arch` to matrix: now we test both arm64 and x86_64. + Added `rustc` to matrix: now we test with a very recent nightly as well. + Only build `output == build` once to reduce the number of combinations. + Debug x86_64 config: more things enabled (debuginfo, kgdb, unit testing, etc.) that mimic more what a developer would have. Running the CI will be slightly slower, but should be OK. Also enable `-C opt-level=0` to test that such an extreme works and also to see how much bloated everything becomes. + Release x86_64 config: disabled `EXPERT` and changed a few things to make it look more like a normal desktop configuration, although it is still pretty minimal. + The configs for arm64 are `EXPERT` and `EMBEDDED` ones, very minimal, for the particular CPU we are simulating. + Update configs to v5.10. + Use `$GITHUB_ENV` to simplify. - Less `extern crate`s needed since we pass it via `rustc` (closer to idiomatic 2018 edition Rust code). Things to note: - There is two more nightly features used: + The new Rust mangling scheme: we know it will be stable (and the default on, later on). + The binary dep-info output: if we remove all other nightly features, this one can easily go too. - The hack at `exports.c` to export symbols to loadable modules. - The hack at `allocator.rs` to get the `__rust_*()` functions. - The hack to get the proper flags for bindgen on GCC builds. Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
1 parent 1523878 commit ccc71df

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+5317
-579
lines changed

.github/workflows/busybox.config

+1,153
Large diffs are not rendered by default.

.github/workflows/ci.yaml

+149-51
Original file line numberDiff line numberDiff line change
@@ -6,74 +6,172 @@ on:
66
jobs:
77
ci:
88
runs-on: ubuntu-20.04
9-
timeout-minutes: 15
9+
timeout-minutes: 20
1010

1111
strategy:
1212
matrix:
13-
mode: [debug, release]
14-
module: [builtin, loadable]
15-
outputdir: [src, build]
13+
arch: [x86_64, arm64]
14+
toolchain: [gcc, clang, llvm]
15+
config: [debug, release]
16+
rustc: [2021-01-02]
17+
output: [src] # [src, build]
18+
install: [rustup] # [rustup, standalone]
19+
sysroot: [common] # [common, custom]
20+
21+
# A few independent combinations to avoid exploding the matrix:
22+
# - The other option for `output`.
23+
# - Different nightlies for `rustc`.
24+
# - The other three (`install`, `sysroot`) combinations
25+
# (they are interrelated, so the cross-product needs to be tested)
26+
include:
27+
- arch: x86_64
28+
toolchain: gcc
29+
config: debug
30+
rustc: 2021-01-09
31+
output: build
32+
install: rustup
33+
sysroot: custom
34+
35+
- arch: arm64
36+
toolchain: clang
37+
config: release
38+
rustc: 2021-01-16
39+
output: build
40+
install: standalone
41+
sysroot: common
42+
43+
- arch: x86_64
44+
toolchain: llvm
45+
config: debug
46+
rustc: 2021-01-19
47+
output: build
48+
install: standalone
49+
sysroot: custom
1650

1751
steps:
18-
# Setup
52+
# Setup: checkout
1953
- uses: actions/checkout@v2
20-
- run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
54+
55+
# Setup: variables
56+
- if: matrix.arch == 'x86_64'
57+
run: |
58+
echo 'IMAGE_PATH=arch/x86/boot/bzImage' >> $GITHUB_ENV
59+
echo 'QEMU_ARCH=x86_64' >> $GITHUB_ENV
60+
echo 'QEMU_MACHINE=pc' >> $GITHUB_ENV
61+
echo 'QEMU_CPU=Cascadelake-Server' >> $GITHUB_ENV
62+
echo 'QEMU_APPEND=console=ttyS0' >> $GITHUB_ENV
63+
- if: matrix.arch == 'arm64'
64+
run: |
65+
echo 'MAKE_ARCH=ARCH=arm64' >> $GITHUB_ENV
66+
echo 'MAKE_CROSS_COMPILE=CROSS_COMPILE=aarch64-linux-gnu-' >> $GITHUB_ENV
67+
echo 'IMAGE_PATH=arch/arm64/boot/Image.gz' >> $GITHUB_ENV
68+
echo 'QEMU_ARCH=aarch64' >> $GITHUB_ENV
69+
echo 'QEMU_MACHINE=virt' >> $GITHUB_ENV
70+
echo 'QEMU_CPU=cortex-a72' >> $GITHUB_ENV
71+
72+
- if: matrix.toolchain == 'clang'
73+
run: echo 'MAKE_TOOLCHAIN=CC=clang-11' >> $GITHUB_ENV
74+
- if: matrix.toolchain == 'llvm'
75+
run: echo 'MAKE_TOOLCHAIN=LLVM=1' >> $GITHUB_ENV
76+
77+
- if: matrix.output == 'build'
78+
run: |
79+
echo 'MAKE_OUTPUT=O=build' >> $GITHUB_ENV
80+
echo 'BUILD_DIR=build/' >> $GITHUB_ENV
81+
82+
- if: matrix.sysroot == 'custom'
83+
run: |
84+
echo 'RUSTC_SYSROOT=--sysroot=$HOME/sysroot' >> $GITHUB_ENV
85+
echo "MAKE_SYSROOT=KRUSTCFLAGS=--sysroot=$HOME/sysroot" >> $GITHUB_ENV
86+
87+
# Setup: LLVM
88+
- run: curl https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
2189
- run: sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-11 main'
2290
- run: sudo apt-get update -y
23-
- run: sudo apt-get install -y clang-11 libelf-dev qemu-system-x86 busybox-static
24-
- run: rustup default nightly-2021-01-02
25-
- run: rustup component add rustfmt
26-
- run: rustup component add rust-src
91+
- run: sudo apt-get install -y llvm-11 clang-11 lld-11
92+
- run: echo $(llvm-config-11 --bindir) >> $GITHUB_PATH
2793

28-
# Build
29-
- run: cp .github/workflows/kernel-${{ matrix.mode }}.config .config
94+
# Setup: GCC
95+
- if: matrix.arch == 'arm64'
96+
run: sudo apt-get install -y gcc-aarch64-linux-gnu
3097

31-
- if: matrix.module == 'loadable'
32-
run: sed -i -E 's/^(CONFIG_RUST_EXAMPLE=)(y)$/\1m/g' .config
98+
# Setup: libelf
99+
- run: sudo apt-get install -y libelf-dev
33100

34-
- if: matrix.outputdir == 'build'
35-
run: mkdir build && mv .config build/.config
101+
# Setup: QEMU
102+
- if: matrix.arch == 'x86_64'
103+
run: sudo apt-get install -y qemu-system-x86
104+
- if: matrix.arch == 'arm64'
105+
run: sudo apt-get install -y qemu-system-arm
36106

37-
- if: matrix.outputdir == 'src'
38-
run: make CC=clang-11 LLVM_CONFIG_PATH=llvm-config-11 -j3
39-
- if: matrix.outputdir == 'build'
40-
run: make O=build CC=clang-11 LLVM_CONFIG_PATH=llvm-config-11 -j3
107+
# Setup: rustc
108+
- if: matrix.install == 'rustup'
109+
run: |
110+
rustup default nightly-${{ matrix.rustc }}
111+
rustup component add rustfmt
112+
- if: matrix.install == 'standalone'
113+
run: |
114+
curl https://static.rust-lang.org/dist/${{ matrix.rustc }}/rust-nightly-x86_64-unknown-linux-gnu.tar.gz | tar xz
115+
rust-nightly-x86_64-unknown-linux-gnu/install.sh --without=rust-docs --prefix=$HOME/rustc
116+
echo $HOME/rustc/bin >> $GITHUB_PATH
41117
42-
# Run
43-
- if: matrix.module == 'builtin'
44-
run: sed -i '/rust_example/d' .github/workflows/qemu-initramfs.desc
118+
# Setup: rustc native libs
119+
- if: matrix.sysroot == 'custom'
120+
run: |
121+
mkdir $(rustc ${{ env.RUSTC_SYSROOT }} --print sysroot)
122+
ln -s $(rustc --print sysroot)/lib $(rustc ${{ env.RUSTC_SYSROOT }} --print sysroot)/lib
45123
46-
- if: matrix.outputdir == 'build'
47-
run: sed -i 's:drivers/:build/drivers/:' .github/workflows/qemu-initramfs.desc
124+
# Setup: rustc source
125+
- if: matrix.install == 'rustup' && matrix.sysroot == 'common'
126+
run: rustup component add rust-src
127+
- if: matrix.install != 'rustup' || matrix.sysroot != 'common'
128+
run: |
129+
git clone -n https://github.com/rust-lang/rust $(rustc ${{ env.RUSTC_SYSROOT }} --print sysroot)/lib/rustlib/src/rust
130+
cd $(rustc ${{ env.RUSTC_SYSROOT }} --print sysroot)/lib/rustlib/src/rust
131+
git checkout $(rustc -vV | grep -F 'commit-hash' | awk '{print $2}')
132+
git submodule update --init library
48133
49-
- if: matrix.outputdir == 'src'
50-
run: usr/gen_init_cpio .github/workflows/qemu-initramfs.desc > qemu-initramfs.img
51-
- if: matrix.outputdir == 'build'
52-
run: build/usr/gen_init_cpio .github/workflows/qemu-initramfs.desc > qemu-initramfs.img
134+
# Setup: compiler-builtins source
135+
- run: git clone --depth 1 -b 0.1.39 https://github.com/rust-lang/compiler-builtins.git $(rustc ${{ env.RUSTC_SYSROOT }} --print sysroot)/lib/rustlib/src/compiler-builtins
53136

54-
- if: matrix.outputdir == 'src'
55-
run: qemu-system-x86_64 -kernel arch/x86/boot/bzImage -initrd qemu-initramfs.img -cpu Cascadelake-Server -smp 2 -nographic -no-reboot -append "console=ttyS0 ${{ matrix.module == 'builtin' && 'rust_example.my_i32=123321' || '' }}" | tee qemu-stdout.log
56-
- if: matrix.outputdir == 'build'
57-
run: qemu-system-x86_64 -kernel build/arch/x86/boot/bzImage -initrd qemu-initramfs.img -cpu Cascadelake-Server -smp 2 -nographic -no-reboot -append "console=ttyS0 ${{ matrix.module == 'builtin' && 'rust_example.my_i32=123321' || '' }}" | tee qemu-stdout.log
137+
# Setup: bindgen
138+
- run: cargo install --version 0.56.0 bindgen
139+
140+
# Setup: busybox
141+
- run: git clone --depth 1 -b 1_30_1 https://github.com/mirror/busybox
142+
- run: mv .github/workflows/busybox.config busybox/.config
143+
- run: cd busybox && make ${{ env.MAKE_CROSS_COMPILE }} -j3
144+
145+
# Build
146+
- run: mv .github/workflows/kernel-${{ matrix.arch }}-${{ matrix.config }}.config .config
147+
148+
- if: matrix.output == 'build'
149+
run: |
150+
mkdir ${{ env.BUILD_DIR }}
151+
mv .config ${{ env.BUILD_DIR }}.config
152+
sed -i 's:drivers/:${{ env.BUILD_DIR }}drivers/:' .github/workflows/qemu-initramfs.desc
153+
154+
- run: make ${{ env.MAKE_ARCH }} ${{ env.MAKE_CROSS_COMPILE }} ${{ env.MAKE_TOOLCHAIN }} ${{ env.MAKE_OUTPUT }} ${{ env.MAKE_SYSROOT }} -j3
155+
156+
# Run
157+
- run: ${{ env.BUILD_DIR }}usr/gen_init_cpio .github/workflows/qemu-initramfs.desc > qemu-initramfs.img
158+
159+
- run: qemu-system-${{ env.QEMU_ARCH }} -kernel ${{ env.BUILD_DIR }}${{ env.IMAGE_PATH }} -initrd qemu-initramfs.img -M ${{ env.QEMU_MACHINE }} -cpu ${{ env.QEMU_CPU }} -smp 2 -nographic -no-reboot -append '${{ env.QEMU_APPEND }} rust_example.my_i32=123321 rust_example_2.my_i32=234432' | tee qemu-stdout.log
58160

59161
# Check
60-
- run: grep -F 'Rust Example (init)' qemu-stdout.log
61-
- run: "grep 'my_i32: \\+123321' qemu-stdout.log"
62-
- if: matrix.module == 'loadable'
63-
run: grep -F 'Rust Example (exit)' qemu-stdout.log
162+
- run: grep -F '] Rust Example (init)' qemu-stdout.log
163+
- run: grep -F '] [2] Rust Example (init)' qemu-stdout.log
164+
- run: grep -F '] [3] Rust Example (init)' qemu-stdout.log
165+
- run: grep -F '] [4] Rust Example (init)' qemu-stdout.log
166+
167+
- run: "grep -F '] my_i32: 123321' qemu-stdout.log"
168+
- run: "grep -F '] [2] my_i32: 234432' qemu-stdout.log"
169+
- run: "grep -F '] [3] my_i32: 345543' qemu-stdout.log"
170+
- run: "grep -F '] [4] my_i32: 456654' qemu-stdout.log"
171+
172+
- run: grep -F '] [3] Rust Example (exit)' qemu-stdout.log
173+
- run: grep -F '] [4] Rust Example (exit)' qemu-stdout.log
64174

65175
# Report
66-
- if: matrix.outputdir == 'src' && matrix.module == 'loadable'
67-
run: ls -l drivers/char/rust_example/rust_example.ko
68-
- if: matrix.outputdir == 'build' && matrix.module == 'loadable'
69-
run: ls -l build/drivers/char/rust_example/rust_example.ko
70-
71-
- if: matrix.outputdir == 'src'
72-
run: ls -l vmlinux arch/x86/boot/bzImage
73-
- if: matrix.outputdir == 'build'
74-
run: ls -l build/vmlinux build/arch/x86/boot/bzImage
75-
76-
- if: matrix.outputdir == 'src'
77-
run: size vmlinux
78-
- if: matrix.outputdir == 'build'
79-
run: size build/vmlinux
176+
- run: ls -l ${{ env.BUILD_DIR }}drivers/char/rust_example.o ${{ env.BUILD_DIR }}drivers/char/rust_example_3.ko ${{ env.BUILD_DIR }}rust/*.o ${{ env.BUILD_DIR }}vmlinux ${{ env.BUILD_DIR }}${{ env.IMAGE_PATH }}
177+
- run: size ${{ env.BUILD_DIR }}drivers/char/rust_example.o ${{ env.BUILD_DIR }}drivers/char/rust_example_3.ko ${{ env.BUILD_DIR }}rust/*.o ${{ env.BUILD_DIR }}vmlinux

0 commit comments

Comments
 (0)