One of the major benefits of using a virtual environment is the ability to inspect and modify the state of the whole system under user control. The primary interface to these features is through the TCP-based GDB remote protocol compatible stub.
Important Notes on the Kernel Debug Stub
- The kernel debug stub presents the multiple CPUs in the system as 'threads' of the process being debugged. For instance, on an iPhone 6, there are two threads: 1 and 2, corresponding to CPU 0 and CPU 1. (GDB protocol does not allow for a thread called 0.)
- Only all-stop mode is supported: if one CPU stops, the others stop as well.
- Single-stepping is supported by using AArch64 single-stepping features. In case of vCont packets, if a CPU single-steps and other CPUs do not have actions specified, they do not perform a step.
- Hardware breakpoints and watchpoints are supported. (Up to 4 of each at a time.) Software breakpoint packets issued to the stub are converted into hardware breakpoints.
- Memory addresses from debugger are passed through virtual-to-physical address translation - this is necessary to make it work. Also, only actual RAM is visible to the debugger; access to MMIO regions is ignored / returns 0.
- Only one concurrent debug stub connection is supported per VM.
- High latency can cause the VM to visually “freeze” or “stutter” as the underlying kernel is breakpointing and communicating with your gdb. If you use features like watch / awatch / rwatch with conditions, every breakpoint is sent to your local machine, the condition is calculated, and then resumed if it was meant to ignore. This is how gdb functions, but it is typically not noticeable for local debugging. On the local gdb end, nothing will visually indicate that this is happening unless the condition is met, in which case you’ll receive a prompt.