Kernel Debugging: Difference between revisions

m
added instructions for single stepping in qemu, bochs and VBox
[unchecked revision][unchecked revision]
(updated for newer virtualbox versions)
m (added instructions for single stepping in qemu, bochs and VBox)
Line 20:
== Pseudo-Breakpoints ==
 
In places where a full print or logging function is not feasible (such as when trying to isolate a single erroneous assembly language instruction), you can create a kind of 'pseudo-breakpoint' by inserting aan "1: jmp HLT1b" instruction into the code. These can be used to perform a binary space isolation (often referred to as a 'binary chop') through the code. The idea is to place the haltendless instructionloop at a point roughly halfway through the part of the code suspected to be at fault; if the CPU halts before the error occurs, then you know that the error is after the breakpoint, otherwise, it must be in the code before breakpoint. Repeat this procedure until the error is isolated. Unfortunately, this only works if the result of the error can be differentiated from the halt instruction itself, and it does little in the case of a problem occurring more than one repetition into loop, such as an array overrun. But you could use a virtual machine debugger to do single stepping with pseudo-breakpoints (see bellow "Using Debuggers with VMs").
 
IMPORTANT NOTE #1: the HLT instruction is a privileged instruction, and as such it will only work in your kernel. The pseudo-breakpoint "1: jmp 1b" is unprivileged, and works from user mode too.
 
IMPORTANT NOTE #2: gcc thinks it is smarter than the programmer, so if you use "while(1);", then it will falsely assume that everything after that loop is not needed, and it will REMOVE all those code from the binary. You MUST use inline assembly so that gcc will keep your code as-is.
<source lang="C">
asm volatile ("1: jmp 1b");
</source>
 
== Use a virtual machine ==
Line 57 ⟶ 64:
This is however rather tricky, since it requires additional hardware, and special support coded into your kernel. You might want to read the [http://web.archive.org/web/20070415113206/http://www.kernelhacking.org/docs/kernelhacking-HOWTO/indexs09.html kernel hacking how-to] and (at minimum) [http://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Debugging.html#Remote-Debugging chapter 20 of the GDB manual], and chances are likely that your debugger will introduce even more bugs at first.
 
== UseUsing GDBDebuggers with QEMUVMs ==
 
=== Use GDB with QEMU ===
 
You can run QEMU to listen for a "GDB connection" before it starts executing any code to debug it.
Line 116 ⟶ 125:
 
I won't start explaining all the nice things about GDB, but as you can see, it is a very powerful tool for debugging OSes.
 
Alternatively you can force a breakpoint in your code without knowing the name of the function or the address. Place an endless loop pseudo-breakpoint somewhere in your code
<source lang="C">
asm volatile ("1: jmp 1b");
</source>
 
Then on the terminal that's running gdb, when your VM hangs press Ctrl^C to stop execution and drop you at the debugger prompt. There
 
(gdb) set $pc += 2
 
Will step over the endless loop, and you can start single stepping, executing one instruction at a time with
 
(gdb) si
 
=== Use bochs debugger ===
 
The easiest way to trigger a breakpoint in bochs is to place "xor bx, bx" into your code. For example
<source lang="C">
asm volatile ("xchg %bx, %bx");
</source>
 
Then when you run the virtual machine, it will stop execution and drop you at debugger prompt. To single step from there, use
 
bochs:1> s
 
=== Use VirtualBox debugger ===
 
Unfortunately Virtualbox developers are morrons, and they have removed the "--start-dbg" command line option, so there's no way to set up breakpoints before your vm starts execution.
But you can do a similar trick as with GDB, place an endless loop pseudo-breakpoint in your code somewhere:
<source lang="C">
asm volatile ("1: jmp 1b");
</source>
 
Then when the execution hangs, access "Command line..." under "Debug" menu (if you don't have a Debug menu in the Machine window, you'll have to enable the debugger see below). In the debugger command line, the first thing to do is that you MUST stop the VM from running:
 
VBoxDbg> stop
 
This should dump the registers. But if not, then get the current RIP value with:
 
VBoxDbg> r
 
Once you get the current RIP, add 2 to it, and set a new RIP (I couldn't find any way to reference RIP from command line, you have to use constants), for example:
 
VBoxDbg> r rip = 0xfffffffff1000102
 
Check if the current RIP correctly points to the instruction after the endless loop:
 
VBoxDbg> r
 
And you can start single stepping with
 
VBoxDbg> p
 
== GUI frontends ==
Anonymous user