Supporting gdb was significantly more complicated. Since use of ptrace is essential for the virtualization of system calls, a process can only be under ptrace by one other process, and gdb also requires ptrace, there is a problem attaching gdb to a UML process.
This is fixed by having gdb started and ptraced by the tracing thread and, by intercepting its system calls, faking it into believing that it is attached to UML. gdb's calls to ptrace and a few other system calls are intercepted, emulated by the tracing thread, and the return values imposed on gdb. This works well enough that the user can't tell that gdb isn't really attached to UML.
This support has also been extended to allow external debuggers to be attached. This enables UML to be debugged by an already-running gdb, such as one running under a front-end like emacs or ddd. It also allows debuggers other than gdb, like strace, to be attached to UML.