- Taken largely from this, though you can use your own kernel source. You can also do a cross-compilation, refer to the previous link for details on that
- specify the KERNEL (
kernel_2712in my case). Domake menuconfiginstead of the raspi preset defconfigs - Enable the KGDB options under Kernel Hacking. I don't remember exactly what flags you need, but the following in
.configshould set you up for something atleast -
- CONFIG_DEBUG_INFO=y
-
- CONFIG_GDB_SCRIPTS=y
-
# CONFIG_DEBUG_INFO_REDUCED is not set
- CONFIG_KALLSYMS can also be set
- set a CONFIG_LOCALVERSION to give it a unique version, helpful if you have many kernels and kernel modules. This won't overwrite the existing ones.
make -j6 Image.gz modules dtbs(I assume that we build natively on arm64. If you are cross compiling or building for 32 bit, please consult the guide linked above)sudo make -j6 modules_install- This will install the new kernel modules. Now onto some shenanigans.- Check the version your newly built and installed kernel has by looking for a new directory under
lib/moduleswhich has your LOCALVERSION suffixed. This version string is important for creating the initramfs - Copy the
System.mapand.configfrom your kernel source and copy it to/bootwith the kernel version string suffixed, like/boot/System.map-$VERSIONand/boot/config-$VERSION - Make
boot/dtbs/$VERSION/and copy your Raspberry Pi's dtb in it (bcm2712-rpi-5-b.dtb for a Raspberry Pi 5, which will be inarch/arm64/boot/dts/broadcom/in you kernel source. - Make a symlink
/boot/dtb-$VERSIONwhich points toboot/dtbs/$VERSION/./<your-dtb>.dtb - Make a symlink
/boot/dtbwhich points toboot/dtbs/$VERSION/./<your-dtb>.dtb(most likely this already exists, so just update it) - Take backups of
/boot/vmlinuz*(any current or older vmlinuz, don't update the vmlinux symlink yet, make vmlinuz.old* symlinks to point to older kernels) - Copy vmlinuz from your kernel source to
/bootas/boot/vmlinuz-$VERSION, update the/boot/vmlinuzsymlink to point to/boot/vmlinuz-$VERSION - Copy
<your-dtb>.dtbfromarch/arm64/boot/dts/broadcom/in you kernel source to/etc/flash-kernel/dtbs - Create initramfs by doing
sudo update-initramfs -c -k $VERSION(IMPORTANT: useMODULES=depin/etc/initramfs-tools/initramfs.confor in/etc/initramfs-tools/conf.d/if it overrides the original conf. This will generate a smaller initramfs and will fit in/boot/firmware) - Update
/boot/initrd.img*symlinks accordingly (point/boot/initrd.imgto/boot/initrd.img-$VERSIONand use/boot/initrd.img.oldfor older versions) - Check if
/boot/firmware/vmlinuzis the same as/boot/vmlinuz-$VERSION. If not, take backups of existing/boot/firmware/vmlinuz*and copy the new vmlinuz into/boot/firmware - Check if
/boot/firmware/initrd.imgis the same as/boot/initrd.img-$VERSION. If not, take backups of existing/boot/firmware/initrd.img*and copy the new initrd.img into/boot/firmware - Add
enable_uart=1andenable_jtag_gpio=1to/boot/firmware/config.txtunder[all] - Add
nokaslr rodata=off maxcpus=1to/boot/firmware/cmdline.txt - Reboot and check
uname -rto see if we have the new kernel running.
- I suggest making a new directory.
- Copy the compiled kernel source from the Raspberry Pi to this directory on your main machine (main machine is the computer you run kgdb on, Raspberry PI is your debug target)
- Install gdb-multiarch and openocd
- Take your Debug Probe and connect its D port to the UART port on the Raspberry Pi 5. Connect the other end via USB to your main machine
- I largely followed this blog to get the OpenOCD interface and target scripts. I recommend you do teh same thing, except running GDB for now.
- After this, your Debug Probe might be powered up. Reboot the Raspberry Pi
- on the main machine, run
gdb-multiarch <linux-source-copied-from-pi>/vmlinux - on the openocd console, you will see 4 ports opened for the 4 CPUs. As we had set
maxcpus=1as a boot arg, we will take the port corresponding to CPU0, which is most likely :3333 (you can assume that the other CPUs are not being used. This is done to make debugging easier) - In gdb, enter
tar remote :<CPU port>. For me it wastar remote :3333 - type c (continue)
- Now it you raise SIGINT in GDB (by Ctrl-C), you will observer that the Raspberry Pi has completely halted (the cursor stops blinking if you have a monitor attached to it, or some other sign of a system halt) and on gdb you can see which function the kernel was in, and see the backtrace as well (along with symbols!!)
- Yay!!
- But if you try to debug a kernel module, you will see it can't find the symbols. No worries, we can fix this.
- NOTE: lx-symbols might fix this, but I haven't tried it yet.
- For example, if you are trying to debug the brcmfmac wifi driver (kernel module), what you need to do is look at
/sys/module/<kernel-module-name>/sections/.text - Then locate the associated
*.kofile in your copied kernel source, and in gdb doadd-symbol-file <path-to-kernel-module>.ko <address in .text> - This will load the relevant symbols, and you can then debug the kernel module normally.
- Make sure you had
CONFIG_GDB_SCRIPTS=y - You should have a vmlinux-gdb.py in you kernel source root which is a symlink to scripts/gdb/vmlinux-gdb.py (if you don'e have it, create the symlink)
- You should add the kernel source root as a safe path by adding
add-auto-load-safe-path <kernel-source>/scripts/gdb/vmlinux-gdb.pyto~/.config/gdb/gdbinit - After this, in my case, gdb complained that
linux.constantscan't be imported (scripts/gdb/linux/constants.py.in was not generated) - run
make scripts_gdbfrom your kernel source root, this should generate the constants file. - Reload GDB, connect to remote and then use lx-symbols
- It will load all the kernel modules at the right addresses