Linux Kernel Symbol Overview

Debugging Linux kernel modules using gdb is different from debugging normal user-mode applications. Unlike user-mode where gdb can automatically load the symbols for the main executable and all loaded shared libraries, kernel-mode only supports loading the symbols for the kernel itself. Although VisualKernel fully automates the symbol loading process, this page explains what happens “under the hood” and what mechanisms are involved.

Manual symbol loading using GDB

To demonstrate how symbol loading is performed we can create and load a basic kernel module that includes an exit function:

If we load the module and start a kernel debugging session using KGDB or VMWare, gdb will load the kernel symbols, but not the symbols for the module. E.g. we will be able to set breakpoints inside the kernel itself (e.g. the sys_open() function), but not inside TestKernelModule_exit():

Unlike the kernel itself that is always loaded at the same address in memory, kernel modules can be loaded at arbitrary addresses. Before we can load the symbols for a module, we need to find out at which address it was loaded. The easiest way to do it is to set a breakpoint in the do_init_module() function and once it hits, checking the name and module_core fields of the mod variable:

Now once we know the load address of the module, we can load the symbols for it using the add-symbol-file command. The symbols are contained in the file called <module name>.o in the module build directory:

Note that if you now attempt to set a breakpoint in the TestKernelModule_exit() function, gdb will set it in an invalid address:

This happens because the TestKernelModule_exit() function is located in the “.exit.text” section and we have only loaded the symbols for the “.text” section. Use the following command to display all sections of TestKernelModule that has been loaded into the memory:

To load the symbols for those sections as well we need to specify their addresses manually in the add-symbol-file command:

Now the breakpoint was set at the correct address. Note that in order to see the global variables in the debugger, you need to also manually specify the addresses of the .data, .rodata and .bss sections.

You can explore all modules loaded into the kernel by looking through the global ‘modules’ list. Simply use the ‘next’ field to get the pointer to the list item corresponding to the next module and subtract 4 (the offset of the list item inside the module structure) from it to get the address of the module object:

Automatic symbol loading using VisualKernel

When you start your debugging session, VisualKernel will automatically set breakpoints inside the kernel functions responsible for loading and unloading the modules. When your module is loaded or unloaded, VisualKernel will automatically load/unload the symbols for it:

You can use the Modules window in Visual Studio to see the list of currently loaded kernel modules and load symbols for them:

You can configure VisualKernel to automatically load symbols for all or some of the in-tree kernel modules (that ones that are included in the original Linux kernel tree):

See our KGDB tutorial for a step-by-step guide on debugging your kernel with VisualKernel.