To build your out-of-tree kernel module on NixOS
you need to do
the following steps:
# Create a copy of the kernel source
nix-build '<nixpkgs>' -A linuxPackages_6_1.kernel.dev
# Developement shell
nix-shell '<nixpkgs>' -A linuxPackages.kernel
Note that you need to build on the exact kernel version or the module won't load.
If the exact kernel version that you are using is not in the packet manager, you need to download that specific version and build it:
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.92.tar.xz
tar -xvf linux*
nix-shell
cd linux*
# Change the settings if needed and save the config
make menuconfig
# Build with the number of avaiable cores
make -j4
If you get compile errors, you need to remove -Werror
from the
linux kernel Makefile.
If you screw up something, you can clean the installation with:
make distclean
You then need to create the module and a make file like so:
obj-m += hello.o
PWD := $(CURDIR)
KVERSION = $(shell uname -r)
all:
make -C linux/ M=$(PWD) modules
clean:
make -C linux/ M=$(PWD) clean
Make sure to use tabs and not spaces. Note that
-
-C
sets the directory of the makefile to execute -
M
tells the kernel makefile that this directory contains a module
Use make
to build the module:
make
And load it with insmod
:
sudo insmod ./hello.ko
Check messages in dmeg
:
dmesg
List loaded modules:
lsmod | grep hello
Remove it with:
sudo rmmod -f hello
Once you have a compiled kernel, you need a filesystem. You can use busybox, debootstrap or other alternatives. I'll use debootstrap to get the stable debian filesystem. You can run the script `create-image.sh:
create-image.sh
Once you have the image, It will need a password to boot. You need to mount
the image, chroot inside it and run passwd
:
sudo mount qemu-image.img tmp
sudo chroot tmp /bin/sh
root> export PATH="$PATH:/usr/sbin:/sbin:/bin"
root> passwd
root> exit
sudo umount tmp
After all of this, you can finally boot the new kernel with the filesystem:
qemu-system-x86_64 -kernel linux/arch/x86_64/boot/bzImage -hda qemu-image.img -append "root=/dev/sda rw console=ttyS0" --enable-kvm
To setup networking inside the vm, the easiest think is to install network-manager
.
You need to chroot inside the mounted image and use apt if you installed debian
or the packet manager of your choice.
sudo mount qemu-image.img /mnt/linux
sudo chroot /mnt/linux /bin/sh
root> export PATH="$PATH:/usr/sbin:/sbin:/bin"
root> apt install network-manager
root> exit
sudo umount /mnt/linux
apt install keyboard-configuration console-setup
If you want to run a nix shell on the VM, tou need to install nix, then update It's channgels:
apt install xz-utils
curl -L https://nixos.org/nix/install > /tmp/install
chmod +x /tmp/install
/tmp/install --daemon
nix-channel --update
nix-shell
Make a defautlt config
make defconfig
Setup linux kernel parameters:
./scripts/config --set-val CONFIG_DEBUG_INFO y
./scripts/config --set-val CONFIG_GDB_SCRIPTS y
You should also disable "CONFIG_RANDOMIZE_BASE".
Now rebuild the kernel. On quemu, use -s
and -S
to listen and wait for gdb.
You shoulw also disable KASLR by adding "nokaslr" to the appened flag int qemu.
You can run gdb like this:
cd linux/
make scripts_gdb
echo "add-auto-load-safe-path `pwd`/scripts/gdb/vmlinux-gdb.py" >> ~/.gdbinit
gdb -ex "target remote :1234" ./vmlinux
(gdb) add-symbol-file ../kernel/av_systable.ko 0xffffffffa0000000
(gdb) i functions <function-name>
(gdb) b <a function>
You can find a more comple guide here.
Useful gdb:
layout asm