Remote Debugging of Loadable Kernel Modules with kgdb: a Knowledge-based Article for Getting Started
In order for us to use kgdb to debug loadable modules, we must understand how the remote kernel communicates with gdb on the development machine. Remember, the mechanism's default communication between gdb on the development machine and the debugging stub on the target machine transpires via a serial driver interface (called gdbserial.o). In order for the debugging process to begin, two things must happen from within this driver. First, the set_debug_traps( ) is initiated. This function is defined in the debugging stub and informs the remote kernel that all breakpoints, error conditions and other exception handling is to be intercepted and handled by gdb. Secondly, the serial driver must call the function breakpoint( ). This function is also defined in the debugging stub and is used to initiate the communication by issuing a breakpoint interrupt:
asm( " int $3");
Since gdb is now configured to intercept such a condition (i.e., the set_debug_traps( ) call), the kernel on the target machine halts and transfers control to gdb on the development machine. It is from this point that the user may begin normal debugging such as single-stepping, issuing of breakpoints, stack tracing, etc. However, if we were to begin stepping from this initial point, the code we would be examining would be in gdbserial immediately following the call to the debugging stub's breakpoint( ) (since this is where program execution has halted).
For example, Listing 2 is the excerpt of gdbserial in which the two calls to the previously explained stub functions are called.
Now, if we initiate another debug session and issue a step command after gdb receives the stub's breakpoint interrupt, we would step to the next line of code in gdbserial after the breakpoint( ) is made (which should be gdb_null in the example in Listing 3).
As was mentioned before, gdb can return the remote kernel to a running state by issuing a continue command. If this is done, gdb patiently waits until the remote kernel returns control by issuing some sort of exception (such as a user-defined breakpoint, segmentation fault, etc.).
For a better understanding of how the entire process works review the debugging stub code found in /usr/src/linux/arch/i386/kernel/gdbstub.c and the serial driver interface, which can be found in /usr/src/linux/drivers/char/gdbserial.c.
We now have almost all the information we need in order begin using the kgdb mechanism to debug loadable modules. Remember, the important information we must retain from kgdb's communication process in order to initiate module debugging is: 1) at the beginning of the debug session, the debugging stub informs gdb on the development machine that it is responsible for intercepting and handling all exceptions from the remote target kernel for example, gdbserial's initial call to set_debug"traps( ); 2) the initial debug process begins with the serial interface's call to the debugging stub's breakpoint( ) function; 3) gdb can return the remote kernel to a running state but will regain control once the remote kernel issues any type of exception.
Additionally, we must consider that because the module will be loaded on the target machine and the gdb session runs on another machine, gdb will have no idea where in the target machine's memory the module code will be loaded. We must therefore determine this location and inform gdb of its whereabouts before the debugging of the module can begin.
During the kernel-building process, the kernel produces a file that maps addresses in memory to function names for the modules/drivers that are built during the compile process. This file is usually placed in the root of the kernel source directory (i.e., /usr/src/linux/System.map) and is used by the kernel to access those compiled devices properly in the correct memory location. However, at the construction of that file the kernel is unaware of where in memory a particular module may be loaded at a later time.
Fortunately, we can determine the memory location for modularized code during its load process. This is accomplished by using insmod with the -m parameter that informs insmod to produce a load map. This map informs us of where in memory the object-code sections reside. Ultimately, we must locate this information in order to inform gdb on our development machine of where our module's object code resides on the target machine. To illustrate, let's consider the module code shown in Listing 4, which we will refer to as the simple module.
As you can see, the simple module contains just enough functionality to to identify its memory address on the target machine when loaded.
On the development machine compile the module:
gcc -c -O2 -g simple.c
Be sure to include the -g option during compilation in order to enable debugging symbols for gdb.
Copy the compiled object code to the target machine.
Install the object code into the kernel using insmod's -m parameter:
insmod -m simple.oThis, of course, loads the module and produces a similar load map as shown in Listing 5.
Record the hex address of the .text section (0xc480004c in our example) for use later. This section represents the beginning of the module code in memory. We will use this value to inform the development machine running gdb of the module's whereabouts in memory.
Unload the module:
Fast/Flexible Linux OS Recovery
On Demand Now
In this live one-hour webinar, learn how to enhance your existing backup strategies for complete disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible full-system recovery solution for UNIX and Linux systems.
Join Linux Journal's Shawn Powers and David Huffman, President/CEO, Storix, Inc.
Free to Linux Journal readers.Register Now!
- The Italian Army Switches to LibreOffice
- Download "Linux Management with Red Hat Satellite: Measuring Business Impact and ROI"
- Petros Koutoupis' RapidDisk
- Linux Mint 18
- Oracle vs. Google: Round 2
- The FBI and the Mozilla Foundation Lock Horns over Known Security Hole
- Varnish Software's Varnish Massive Storage Engine
- Privacy and the New Math
- Ben Rady's Serverless Single Page Apps (The Pragmatic Programmers)
Until recently, IBM’s Power Platform was looked upon as being the system that hosted IBM’s flavor of UNIX and proprietary operating system called IBM i. These servers often are found in medium-size businesses running ERP, CRM and financials for on-premise customers. By enabling the Power platform to run the Linux OS, IBM now has positioned Power to be the platform of choice for those already running Linux that are facing scalability issues, especially customers looking at analytics, big data or cloud computing.
￼Running Linux on IBM’s Power hardware offers some obvious benefits, including improved processing speed and memory bandwidth, inherent security, and simpler deployment and management. But if you look beyond the impressive architecture, you’ll also find an open ecosystem that has given rise to a strong, innovative community, as well as an inventory of system and network management applications that really help leverage the benefits offered by running Linux on Power.Get the Guide