- Debugging kernel and modules via gdb¶
- Requirements¶
- Setup¶
- Examples of using the Linux-provided gdb helpers¶
- List of commands and functions¶
- How to Use GDB to Debug Programs in Ubuntu 20.04
- Prerequisites
- Installing Packages
- C-Program example for debugging
- Code
- Compile C Program using GCC
- Execute the test program
- Debugging an Application on Linux
- Initiate Debugger
- Debug Breakpoints
- Add Breakpoints
- Step through Breakpoints
- Information About Breakpoints
- Delete Breakpoints
- Watch Variables
- Quit Debugger
- Conclusion
- Search
- About This Site
- Latest Tutorials
Debugging kernel and modules via gdb¶
The kernel debugger kgdb, hypervisors like QEMU or JTAG-based hardware interfaces allow to debug the Linux kernel and its modules during runtime using gdb. Gdb comes with a powerful scripting interface for python. The kernel provides a collection of helper scripts that can simplify typical kernel debugging steps. This is a short tutorial about how to enable and use them. It focuses on QEMU/KVM virtual machines as target, but the examples can be transferred to the other gdb stubs as well.
Requirements¶
Setup¶
- Create a virtual Linux machine for QEMU/KVM (see www.linux-kvm.org and www.qemu.org for more details). For cross-development, https://landley.net/aboriginal/bin keeps a pool of machine images and toolchains that can be helpful to start from.
- Build the kernel with CONFIG_GDB_SCRIPTS enabled, but leave CONFIG_DEBUG_INFO_REDUCED off. If your architecture supports CONFIG_FRAME_POINTER, keep it enabled.
- Install that kernel on the guest, turn off KASLR if necessary by adding «nokaslr» to the kernel command line. Alternatively, QEMU allows to boot the kernel directly using -kernel, -append, -initrd command line switches. This is generally only useful if you do not depend on modules. See QEMU documentation for more details on this mode. In this case, you should build the kernel with CONFIG_RANDOMIZE_BASE disabled if the architecture supports KASLR.
- Build the gdb scripts (required on kernels v5.1 and above):
add-auto-load-safe-path /path/to/linux-build
Examples of using the Linux-provided gdb helpers¶
(gdb) lx-symbols loading vmlinux scanning for modules in /home/user/linux/build loading @0xffffffffa0020000: /home/user/linux/build/net/netfilter/xt_tcpudp.ko loading @0xffffffffa0016000: /home/user/linux/build/net/netfilter/xt_pkttype.ko loading @0xffffffffa0002000: /home/user/linux/build/net/netfilter/xt_limit.ko loading @0xffffffffa00ca000: /home/user/linux/build/net/packet/af_packet.ko loading @0xffffffffa003c000: /home/user/linux/build/fs/fuse/fuse.ko . loading @0xffffffffa0000000: /home/user/linux/build/drivers/ata/ata_generic.ko
(gdb) b btrfs_init_sysfs Function "btrfs_init_sysfs" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (btrfs_init_sysfs) pending.
loading @0xffffffffa0034000: /home/user/linux/build/lib/libcrc32c.ko loading @0xffffffffa0050000: /home/user/linux/build/lib/lzo/lzo_compress.ko loading @0xffffffffa006e000: /home/user/linux/build/lib/zlib_deflate/zlib_deflate.ko loading @0xffffffffa01b1000: /home/user/linux/build/fs/btrfs/btrfs.ko Breakpoint 1, btrfs_init_sysfs () at /home/user/linux/fs/btrfs/sysfs.c:36 36 btrfs_kset = kset_create_and_add("btrfs", NULL, fs_kobj);
(gdb) lx-dmesg [ 0.000000] Initializing cgroup subsys cpuset [ 0.000000] Initializing cgroup subsys cpu [ 0.000000] Linux version 3.8.0-rc4-dbg+ (. [ 0.000000] Command line: root=/dev/sda2 resume=/dev/sda1 vga=0x314 [ 0.000000] e820: BIOS-provided physical RAM map: [ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable [ 0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved .
(gdb) p $lx_current().pid $1 = 4998 (gdb) p $lx_current().comm $2 = "modprobe\000\000\000\000\000\000\000"
(gdb) p $lx_per_cpu("runqueues").nr_running $3 = 1 (gdb) p $lx_per_cpu("runqueues", 2).nr_running $4 = 0
(gdb) set $next = $lx_per_cpu("hrtimer_bases").clock_base[0].active.next (gdb) p *$container_of($next, "struct hrtimer", "node") $5 = < node = < node = < __rb_parent_color = 18446612133355256072, rb_right = 0x0 , rb_left = 0x0 >, expires = < tv64 = 1835268000000 >>, _softexpires = < tv64 = 1835268000000 >, function = 0xffffffff81078232 , base = 0xffff88003fd0d6f0, state = 1, start_pid = 0, start_site = 0xffffffff81055c1f , start_comm = "swapper/2\000\000\000\000\000\000" >
List of commands and functions¶
The number of commands and convenience functions may evolve over the time, this is just a snapshot of the initial version:
(gdb) apropos lx function lx_current -- Return current task function lx_module -- Find module by name and return the module variable function lx_per_cpu -- Return per-cpu variable function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable function lx_thread_info -- Calculate Linux thread_info from task variable lx-dmesg -- Print Linux kernel log buffer lx-lsmod -- List currently loaded modules lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules
Detailed help can be obtained via «help » for commands and «help function » for convenience functions.
How to Use GDB to Debug Programs in Ubuntu 20.04
GNU Debugger (GDB) is an open-source debugger for GNU Systems. The debugger is portable and can be used for multiple languages as C/C++ and Fortran. It can be used for debugging programs by observing their states on specific breakpoints and even altering the flow or values for further execution. Usually, the GDB runs in command-line but several GUI has been developed for it as well.
In this article, we explore how to debug C programs using GDB in Ubuntu 20.04 LTS (Focal Fossa).
Prerequisites
Note: The commands discussed in this article have been tested on Ubuntu 20.04 LTS (Focal Fossa).
Installing Packages
Install prerequisite GN packages for compiling and debugging. Run the following command in the terminal:
$ sudo apt install gcc gdb -y
C-Program example for debugging
Code
Before running, a program needs to be compiled. We are going to compile the following C code in the file main.c.
#include int main() < for (int i=0; ireturn 0; >
Compile C Program using GCC
Usually, a C code is compiled in GCC using the following command:
Another argument needs to be provided to include symbols in the binary. These symbols are used by GDB to track and debug the program. Run the following command in terminal to compile the C code:
An executable file named bin will appear.
Execute the test program
The binary file named bin can be executed like any other executable file on a command-line interface. Use the following command to run it in terminal:
The output of the code will appear.
Debugging an Application on Linux
Initiate Debugger
Run the GDB utility using following command in the terminal:
Press enter. The console for GDB terminal will appear. Enter the run command in this console to run the executable provided to the utility as an argument.
Debug Breakpoints
Add Breakpoints
Breakpoints can be added in several ways. We will be adding a breakpoint on the printf function in our code. Run the following command in terminal to add a breakpoint:
Alternatively, a line number can be used to add a breakpoint as well.
Enter the run command and the program will stop at the breakpoint.
Step through Breakpoints
Use the command continue to continue the execution of the program.
There are two other commands for different purposes of continuing the execution of the program:
- Step: steps through the next machine instruction.
- Next: steps to through the next line of code.
Abbreviations of commands can also be used. Like abbreviation of continue command is c.
Information About Breakpoints
Information about breakpoints can be observed using info command of gdb. Run the following command the terminal:
The information about breakpoints will appear.
Note: The number on the left of the breakpoint is used to refer to it by other commands.
Delete Breakpoints
A breakpoint can be deleted using the delete command and by referring to the breakpoint number observed in the output of the info utility.
Now the breakpoint has been deleted and if run, the program will execute straight to the end.
Watch Variables
Variables can be watched using the watch utility. First, we need to enter the scope in which the variable exists. For this purpose, add a breakpoint first using the following command:
Then run the code that hits this breakpoint.
Now we are in the loop where the variable i exists.
The watch command will be used to observe the previous and new value of the variable i in the loop.
Now the breakpoint generated by watch command will appear in the list of breakpoints as well. The list of breakpoints can be shown using the following command:
Moreover, we do not need the breakpoint inserted earlier. It can be easily removed using the following command:
Now if continued, the code will view values whenever the variable has changed the value and show both old and new values.
Further iterations of the program can be observed as well, using the same command.
Quit Debugger
Run the following command in the terminal to exit the debugger.
This close gdb utility and the default command-line prompt will appear.
Conclusion
In this article, we explored how to run and break a program in GDB. Moreover, it was also configured to break itself when the value of a variable has changed. We hope you can easily debug your programs in GDB after following this article.
Search
About This Site
Vitux.com aims to become a Linux compendium with lots of unique and up to date tutorials.