This project will enable gPXE debugging using the GDB remote target feature. Developers will be able to inspect gPXE from a remote machine and control its execution.
Due to the low-level nature of gPXE and the environment it executes in, there is little support for debugging and crash analysis. This can make understanding and fixing errors frustrating.
The GNU Debugger (GDB) can debug remotely using a simple protocol. The code that implements this protocol on the target is called a GDB stub.
Using remote debugging, developers will be able to get much better access to gPXE while it is running or when it has crashed. It will be easier to diagnose crashes and to understand error conditions.
The GNU Debugger (GDB) has support for remote debugging. This lets users debug applications running on other machines or in special environments, like under virtualization. Remote debugging works by speaking a simple protocol that carries checksummed packets of commands and replies.
The protocol is extensible and most commands are optional. A minimal GDB stub must implement the following commands:
Additional GDB protocol commands provide more advanced features, like hardware breakpoints, or optimizations of existing commands, like binary memory dumps for faster transfer.
I will implement the minimal set of commands. If we find that additional commands are useful in practice, I will also implement them.
The GDB stub is interrupt-driven. Control is transferred to the GDB stub when an exception occurs. When not in the interrupt context, the GDB stub is inactive.
Upon entering an interrupt context, the GDB stub notes the state of the registers and the exception that was raised. This information is sent to the remote GDB.
GDB displays its prompt and lets the user invoke commands. In the meantime, the GDB stub holds control of gPXE and blocks on input until the remote GDB sends a command.
Commands are processed in a loop by the GDB stub until a continue or step command is received. These commands pass control back to gPXE by leaving the interrupt context.
Note that this execution model makes the GDB stub a blocking, top-level thread of control in gPXE. After discussion with mcb30, we decided that although this is the anticipated execution model, the GDB stub should not be written to assume blocking. This makes it possible to support other modes of debugging later, like periodic memory dumps while the program runs.
Only 32-bit protected mode support is planned. The bulk of gPXE runs in 32-bit protected mode and gdb/binutils support this mode well.
The GDB stub controls the execution of gPXE, but the GDB stub is part of gPXE. This sounds recursive - and it is! Therefore, care must be taken to isolate the GDB stub from gPXE. If the GDB stub is not isolated, it can hang itself. For example, if the GDB stub calls strcmp()
, then placing a breakpoint inside strcmp()
may lead to recursion in the GDB stub.
The GDB stub must be designed to depend on as few gPXE functions as possible. That way, as much of gPXE as possible remains debuggable. This conflicts with code reuse, so we will have to keep an eye on this during development.
Several transports are supported by GDB including serial, UDP, and TCP. Serial is simple and serves as a good starting point. UDP and TCP are more flexible but also more complex. After discussion with mcb30, it looks like UDP is in scope and should be implemented.
I believe TCP is not a big win if UDP support is already in place. The problem with TCP is that it depends on the TCP/IP stack and therefore blacklists a lot of gPXE code for breakpoints, due to isolation issues discussed above.