Please note, this page may be incomplete or outdated or possibly use incorrect terminology.
When testing, a "bug", or behavior contrary to design or expectation, may be found. Debugging is the process of finding the source of the bug in order to remove it.
As always, there are multiple means by which to debug a program, varying, among other things, in their level of complexity and detail by which information may be examined.
- 1 In Source
- 2 Debugger 32-bit
- 3 Debugger 16-bit Segmented
- 4 Dynamic Debug
Inserting statements like printf() and puts() to watch the progress and/or data of the program is the least technical method.
Both qemu and bochs have the capability of a gdbstub, allowing you to connect the GNU Debugger (gdb) to the qemu/bochs process and examine what the VM sees.
Debugger 16-bit Segmented
(Or the real reason I built this page. --Gene)
16-bit segmented mode, also known as Real Mode, is recognized as addressing with 2 registers, a segment and an offset, each 16 bits long, and staggered to form a 20 bit address (segment * 10h + offset = address). This is the operating mode of the boot sector, the secondary loader (for disk-based variants in Syslinux-4.00 as of 2010-06-21), most (but I believe all) of the Syslinux core assembly code and COMBOOT modules.
Background and Caveats
Unfortunately, gdb is not effective at debugging 16-bit segmented code. Attempting to utilize qemu or bochs with gdb will only lead to frustration.
However, bochs has an internal debugger available. Unfortunately, it's mutually exclusive of the gdbstub at compile time and most distributions compile in the gdbstub instead of the internal debugger (which is probably a good choice _most_ of the time).
Use the source
Quite literally. Grab the source for bochs (on 2010-06-21,
2.4.5 is the current stable),
the needed development libraries
(the configure script will warn you nicely; if it says libraries are missing,
it means development libraries of course).
You'll want to configure using
./configure --enable-debugger --enable-disasm'
to enable the debugger and disassembler.
is helpful in the debugger for editing the command line and history.
You can also add
if you're interested in PXELINUX.
Next, compile it ('
See also Setting Up Bochs.
I've tried to build bfe (bochs debugger front end) but at this time have not figured out what I'm missing.
After launching, it will come up to the "start menu" (unless -q is specified). If it's fully configured, "Begin Simulation". At this point, it will dump you to the debugger prompt. Your first friend here is 'help' (also as 'h') to list the possible commands. 'h b' will list help information for 'b' (breakpoint).
See also the official manual here (although as of 2010-08-01, it appears to be out of date and the internal help is current).
Breakpoints are a way to stop the debugger at an exact code point. The first one to be aware of is 0x7c00 ('b 0x7c00'). This is the code entry point for all variants. If you're debugging a boot sector (for a disk-based variant or for ISOLINUX), this is a great starting breakpoint. This makes it execute through all of the BIOS related initialization code without stopping.
If you don't want to look at the code in an interrupt, try setting a breakpoint at the return point.
As of 2010-06-21, Syslinux 4.00-pre55 disk-based variants used a boot loader to a secondary loader (starting at address 0x8000).
For additional addresses within the Syslinux core, look at the .lst file for the variant you are examining as it has the correct addresses. Please note this requires currently that you compile Syslinux yourself at this time.
For the loading and entry into a COM32 module, search for call com32_entry in the .lst file of the variant you are using.
'c' will tell it to continue on if it's been interrupted (including the initial prompt).
's' or 's #' will step forward a certain number of instructions (default 1). This will include the code you're examining and any interrupt calls.
'u' or 'disasm' will disassemble the current instruction. 'u /count' disassembles count instructions.
'u start' will disassemble the instruction at the location start. 'u start end' will disassemble instructions from location start inclusive to end exclusive.
'r' will dump the state of all of the "standard" registers, eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags.
'sreg' will dump the segment registers es, cs, ss, ds, fs, gs (and information about those segments).
'set reg = expr' will set the register reg to the value of the expression expr. As of 2010-08-01, bochs' manual states it only works on general purpose integer registers, but it does also work on the segment registers.
'xp /nuf addr' is a way to examine the contents of memory. 'xp /4wx 0x7c00' will dump 4 "Words" (32b) in heXadecimal starting at 0x7c00.
From Syslinux-5.11-pre8 and Syslinux-6.02-pre1, all (pre)releases contain a debugging utility in the form of a COM32 file - debug.c32. This utility can be used to enable and disable debug code at runtime and is especially useful for debugging an existing installation when it isn't possible to build Syslinux from source.
The arguments to debug.c32 look like:
debug.c32 [-e|-d] func1[,func2,func3,...]
-e and -d are used to (e)nable and (d)isable debug code, respectively. "func1" is the name of a function inside which you want to configure debug code. For example, to enable the dprintf() statements inside open_file():
debug.c32 -e open_file
debug.c32 can be invoked either from the "boot:" prompt or from within a configuration file,
DEFAULT dbg LABEL dbg COM32 debug.c32 APPEND -e <func>
If using a config file to enable debug, it would be recommended to make the above dbg label the DEFAULT.
If you suspect a bug in Syslinux, some useful functions to enable for debug are:
- Loading kernels
- Memory corruption