← back to home

Setting up crash utility to work on live Arch Linux kernel

12/05/2026 · 20 minutes
kernel-dev debugging

Originally this was going to be a post about writing some basic Linux kernel modules, however, I noticed a gap in information around setting up the crash utility for live kernel inspection, so I have reframed my efforts to explain how to do this.

Recently I have been getting into Linux Kernel programming, and based on my previous post where I tried to set up a local AI to help me dev for it, it quickly became apparent to me that this was the wrong first approach to take, and I should first gain a solid understanding on the concepts and techniques I am trying to get it to help me dev for, before setting this up. So to start with I wanted to explore several kernel networking APIs, specifically what is actually going on when we call kernel_sendmsg.

To get started with I will be working on an old MacBook Air (x86-64) with Arch Linux installed. A couple of the tools I will use are crash and dmesg.

dmesg is a tool that will print the contents of the kernel ring buffer. The kernel ring buffer is where any important kernel level logs get stored, and can be viewed from user space, so tools like dmesg can be used to inspect what is going on in a system. This tool is available on virtually every Linux system by default.

crash "is a tool for interactively analyzing the state of the Linux system while it is running" - what's cool about this is we can inspect data structures, and code in a live running machine.

Setting up crash

First, I am going to update my system, I am on Arch Linux so pacman is my package manager.

sudo pacman -Syu

If you are ever curious about what the flags for pacman command stand for:

-S
Stands for "Sync", which allows pacman to interact with remote repositories
-y
This downloads a master copy of the package database from remote servers
-u
This will compare the version of all the packages on your system with those fetched from remote. Any packages that have a newer version available will be flagged for upgrading.

If you are on Ubuntu, fetching debug symbols for the kernel is relatively easy, and a one liner. To install the crash utility and get debug symbols you would simply do:

sudo apt install crash
sudo apt-get install linux-image-$(uname -r)-dbgsym

However, if you are using Arch Linux like me, this is not as easy. The first thing to understand is what are we trying to do. We are trying to run the crash utility against the running kernel; aka /proc/kcore [for more info on the virtual file /proc/kcore click here.]. We are doing this so that we can look at live kernel data structures, such as task_struct. For this to actually be useful and not just a bunch of unreadable data, we need some sort of file that can describe these structures. This is not currently available in our running kernel, but lives in a separate file called vmlinux which is built with DWARF debug info. More info on DWARF debug info to come so hold tight.

On Arch, you can't just pacman -S vmlinux, there's no package available to install. What this means is the path forward is to rebuild the kernel ourselves with the right config, package the debug information as a separate package, e.g. linux-debug and then install this alongside the regular kernel, and finally point crash at it.

Recap - what are "symbols"

I found this article "What is a debug symbol" by Lizzie Danielson to provide a great explanation.

A debug symbol is basically a kind of metadata, i.e., data that describes data, that acts as a translation unit of binary source code into a human-readable format. When a program is compiled, the final executable binary is in a format known as machine code, that is in a format the computer can run, i.e., in binary instructions or data. This is awesome for the computer because it can read this step by step, and knows exactly what to do. For human developers who are trying to understand what is going on, this is not so awesome, and while certainly possible to gain an understanding of what is going on, it would be nice if we could keep some information from when we were first writing the executable in a high level language, such as variable names, or function signatures, embedded within the binary.

Once we place these symbols back in the binary, we can then use tools such as a debugger, that cross reference the source code and the executable file, presenting the binary in a way that humans can understand easily.

ELF binaries

Quick recap on executable and linkable files, I don't want to go too deep into this. ELF is the standard format for files on Linux, and inside the ELF spec, there are sections defined to host a symbol table, the .symtab and .dynsym sections which map functions and variables to addresses inside the executable.

Some further interesting articles I found on ELF files can be found here:

The ELF Object File Format: Introduction - Eric Youngdale on April 1, 1995
Goes over the different file formats, interesting history, plus explanation of a.out file.
ELF and Its History Explained - The Routinely Interrupts on August 14, 2025
Amazing explanation of the history and specification of ELF, also pointing to ELF's current maintainers, Xinuos.
Linux 101: Understanding the insides of your program - Holden Grissett on May 16, 2018
Goes over the ELF spec and also some pretty useful tools, specifically objdump, readelf and nm, which can all be used for inspecting binaries.

kallsyms - The Linux kernel's built-in symbol table

Much like how each ELF binary has its own symbol table as aforementioned, the kernel also has some built-in symbols. The file for this is /proc/kallsyms - which is used to store kernel exported symbols. This file is also a table, with three columns: [the memory address][the type of the symbol][the name of the symbol]. Shlomi Boutnaru did a pretty good article titled The Linux Concept Journey — /proc/kallsyms (Kernel Exported Symbols).

The important thing to understand from this is that /proc/kallsyms is usually built into the kernel by default, but this alone won't be enough to get crash working properly.

cat /proc/kallsyms img
Running cat against /proc/kallsyms. Note: Need sudo to see the addresses

So as far as I am aware, the reason why DWARF symbols are not shipped is because they balloon the size of vmlinux. So something to keep in mind if you are doing kernel dev and regularly updating your system, is that you will probably have to do this for every update, or pin to a single version lol.

Prerequisite packages - before we begin

Before we begin there are three packages we need (there may be more or fewer depending on your system)

crash
the command line utility (note): at the time of writing this (12 May 2026) you may need to clone the crash repo if you have a kernel version greater than 7.x.
base-devel
a meta package that pulls all the packages in the standard build chain.
devtools
Has pkgctl, which is what I will use for cloning Arch packaging repos and building a clean chroot

sudo pacman -S --needed base-devel devtools crash

note: --needed flag tells pacman to skip any packages we specified that are already installed and up to date. You could probably also just use git to clone linux rather than pkgctl, it doesn't really matter.

Cloning Linux and ensuring we have the version that matches our kernel.

This only applies if you are trying to get symbols for your current running kernel; however, once we have cloned Linux we can build a kernel for any publicly available version on the git tree. I wanted to get symbols for my current version — if you are not doing the same, don't worry about the steps concerning version matching. To clone using pkgctl do:

mkdir -p ~/Projects/kernel_debugging/build && cd ~/Projects/kernel_debugging/build
pkgctl repo clone --protocol=https linux
cd ~/Projects/kernel_debugging/build/linux
pkgtctl clone linux results
cloning the Linux kernel

For this task, I wouldn't worry about the warnings for undefined name, email, and gpg-key. We aren't going to be pushing any changes lol. Here are some quick commands to run to verify what version of Linux you cloned, and what version of Linux you are currently running:

[admin@archmba2013 linux]$ grep -E '^(pkgver|pkgrel)=' PKGBUILD
pkgver=7.0.5.arch1
pkgrel=1
[admin@archmba2013 linux]$ pacman -Q linux
linux 7.0.3.arch1-2
[admin@archmba2013 linux]$ uname -r
7.0.3-arch1-2
[admin@archmba2013 linux]$

At the time of making this, I was up to linux 7.0.3.arch1-2 and the public repo was up to 7.0.5.arch1-1. What we want to do is switch to the commit that contains our exact version, so this will be unique for myself vs whoever is reading. Here's a quick command to grep for your version and switch to it:

[admin@archmba2013 linux]$ git tag --list | grep '7.0.3'
7.0.3.arch1-1
7.0.3.arch1-2
7.0.3.arch1-3
[admin@archmba2013 linux]$ git checkout 7.0.3.arch1-2
Note: switching to '7.0.3.arch1-2'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c 

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at b1ae233 upgpkg: 7.0.3.arch1-2: gcc 16.1.1 rebuild
[admin@archmba2013 linux]$

Again - I chose to checkout 7.0.3.arch1-2 because this is my current version of the Linux kernel. You must ensure this is the exact version of whatever Linux kernel you want to run. You can then verify this with grep:

[admin@archmba2013 linux]$ grep -E '^(pkgver|pkgrel)=' PKGBUILD
pkgver=7.0.3.arch1
pkgrel=2
[admin@archmba2013 linux]$

Alright, finally, before we build, we need to change the PKGBUILD & .config files so they will not strip debug symbols. First, I will check my config file, as this will be unique depending on your computer's architecture.

[admin@archmba2013 linux]$ ls
config.x86_64  keys  LICENSE  LICENSES  PKGBUILD  REUSE.toml
[admin@archmba2013 linux]$ grep -E '^CONFIG_DEBUG_INFO|# CONFIG_DEBUG_INFO)' config.x86_64
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF5=y
CONFIG_DEBUG_INFO_COMPRESSED_NONE=y
CONFIG_DEBUG_INFO_BTF=y
CONFIG_DEBUG_INFO_BTF_MODULES=y
[admin@archmba2013 linux]$

This is pretty handy ~ everything in the config is already set to yes. I am not sure if this is standard when you clone the Linux repo, but it doesn't hurt to verify. Next, make some edits to the PKGBUILD file.

removing the not from debug and strip
removing the ! from debug and strip
removing the line that strips vmlinux
removing the line that strips vmlinux - note you probably also want to comment out the echo, or you might get confused...

The final thing I did was add a long number to the end of pkgrel, e.g. .1919 so we can easily find this file later. This is optional though, and if you do this you will need to install the kernel you just built too, otherwise there will be a version mismatch. Finally, we are ready to build. I used an environment variable to set the max number of processors to be used for the build:

[admin@archmba2013 linux]$ MAKEFLAGS="-j$(nproc)"

And you should be good to make!

[admin@archmba2013 linux]$ makepkg -s

Debugging in make

I only had one issue when I tried to make, and that was a signature issue. For some reason I could not verify some of the co-authors. Anyway, this was my error and solution

Verifying source file signatures with gpg...
    linux-7.0.3.tar ... FAILED (unknown public key 38DBBDC86092693E)
    linux-v7.0.3-arch1.patch.zst ... FAILED (unknown public key B8AC08600F108CDF)
==> ERROR: One or more PGP signatures could not be verified!
[admin@archmba2013 linux]$ ==> Verifying source file signatures with gpg...
    linux-7.0.3.tar ... FAILED (unknown public key 38DBBDC86092693E)
    linux-v7.0.3-arch1.patch.zst ... FAILED (unknown public key B8AC08600F108CDF)
==> ERROR: One or more PGP signatures could not be verified!
[admin@archmba2013 linux]$^C
[admin@archmba2013 linux]$ ^C
[admin@archmba2013 linux]$ grep -A 10 'validpgpkeys' PKGBUILD
validpgpkeys=(
  ABAF11C65A2970B130ABE3C479BE3E4300411886  # Linus Torvalds
  647F28654894E3BD457199BE38DBBDC86092693E  # Greg Kroah-Hartman
  83BC8889351B5DEBBB68416EB8AC08600F108CDF  # Jan Alexander Steffens (heftig)
)
b2sums=('51eebd3aa3c64779308b0781818fd91921c1a7b0c3ffd361dbff01f8853f1cea7d4c70f6ee2ae3b7817aeca7605b63f12b0fa422d22c0a50fb2306553c49eda4'
        'SKIP'
        'ad245fe70556a42c94d6f16b7c276a476bfb1ed5811a5030d7fefa3f5f226dd722f61c55cb9b76f5ff42082a6cbf88e04dc616adecc91131b68fe59cbed59035'
        'SKIP')
b2sums_x86_64=('dafee1f25d231199834869a5ce76a85eebb3c1ceac86f604270e93a40a22f29bcf797822481aff5aa5020c12359b9ad87ad8e0d36727166522510a07539d69d4')
[admin@archmba2013 linux]$ gpg --recv-keys 647F28654894E3BD457199BE38DBBDC86092693E 83BC8889351B5DEBBB68416EB8AC08600F108CDF
Adding the unverified keys
Getting a list of the keys of the authors, and then importing the two keys that failed

After this I was good to go - and it was just waiting for the kernel to build.

Linux kernel built
Linux kernel built

Once this was done, I installed the headers, debug and arch pkg.tar.zst files:

[admin@archmba2013 linux]$ sudo pacman -S linux-7.0.3.arch1-2.1919-x86_64.pkg.tar.zst linux-debug-7.0.3.arch1-2.1919-x86_64.pkg.tar.zst linux-headers-7.0.3.arch1-2.1919-x86_64.pkg.tar.zst

Finally, you can reboot and verify the changes! Unfortunately I had an additional hiccup with using the crash tool:

[[admin@archmba2013 kernel_debugging]$ sudo crash -s --hex
Exception caught while booting Guile.
Error in function "open-file":
No such file or directory: "/usr/share/gdb/guile/gdb/boot.scm"
crash: warning: Could not complete Guile gdb module initialization from:
/usr/share/gdb/guile/gdb/boot.scm.
Limited Guile support is available.
Suggest passing --data-directory=/path/to/gdb/data-directory.

crash: invalid structure member offset: kmem_cache_s_num
       FILE: memory.c  LINE: 9988  FUNCTION: kmem_cache_init()

[/usr/bin/crash] error trace: 556739ae8b18 => 556739b091a8 => 556739b304a8 => 556739c39e09

[admin@archmba2013 kernel_debugging]$ sudo crash

crash 9.0.1
Copyright (C) 2002-2025  Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010  IBM Corporation
Copyright (C) 1999-2006  Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012  Fujitsu Limited
Copyright (C) 2006, 2007  VA Linux Systems Japan K.K.
Copyright (C) 2005, 2011, 2020-2024  NEC Corporation
Copyright (C) 1999, 2002, 2007  Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002  Mission Critical Linux, Inc.
Copyright (C) 2015, 2021  VMware, Inc.
This program is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions.  Enter "help copying" to see the conditions.
This program has absolutely no warranty.  Enter "help warranty" for details.

Exception caught while booting Guile.
Error in function "open-file":
No such file or directory: "/usr/share/gdb/guile/gdb/boot.scm"
crash: warning: Could not complete Guile gdb module initialization from:
/usr/share/gdb/guile/gdb/boot.scm.
Limited Guile support is available.
Suggest passing --data-directory=/path/to/gdb/data-directory.
GNU gdb (GDB) 16.2
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
Find the GDB manual and other documentation resources online at:
    .

For help, type "help".
Type "apropos word" to search for commands related to "word"...


crash: invalid structure member offset: kmem_cache_s_num
       FILE: memory.c  LINE: 9988  FUNCTION: kmem_cache_init()

[/usr/bin/crash] error trace: 55751cdbbb18 => 55751cddc1a8 => 55751ce034a8 => 55751cf0ce09

[admin@archmba2013 kernel_debugging]$

The thing that caused the crash in my case was related to kmem_cache changes. This is a known issue, documented here. At the time of writing this, the fix for this problem wasn't available through the crash-utility package on pacman, so I cloned the git repo, and used make to build it which appeared to work.

Searching init task in crash
Searching init task in crash
Getting address of forkidle using kallsyms
Getting address of forkidle using kallsyms
Verifying crash with fork_idle address
Verifying crash with fork_idle address

That concludes setting up crash :D Hopefully I can begin doing some actual kernel dev soon lol

1