The GNU GDB Debugger and NetBSD (Part 2)
The NetBSD team of developers maintains two copies of GDB:
- One in the base-system with a stack of local patches.
- One in pkgsrc with mostly build fix patches.
The base-system version of GDB (GPLv3) still relies on a set of local patches. I set a goal to reduce the local patches to bare minimum, ideally reaching no local modifications at all.
Over the past month I've reimplemented debugging support for multi-threaded programs and upstreamed the support. It's interesting to note that the old support relied on GDB tracking only a single inferior process. This caused the need to reimplement the support and be agnostic to the number of traced processes. Meanwhile the upstream developers introduced new features for multi-target tracing and a lot of preexisting code broke and needed resurrection. This affected also the code kept in the GDB basesystem version. Additionally over the past 30 days, I've also developed new CPU-independent GDB features that were for a long time on a TODO list for NetBSD.
After the past month NetBSD has now a decent and functional GDB support in the mainline. It's still not as featured as it could and CPU-specific handling will need a dedicated treatment.
Signal conversions
GDB maintains an internal representation of signals and translates e.g. SIGTRAP to GDB_SIGNAL_TRAP. The kernel independent management of signal names is used by the GDB core and requires translation on the border of kernel-specific implementation. So far, the NetBSD support relied on an accidental overlap of signal names between the GDB core and the NetBSD definitions, while this worked for some signals it wasn't matching for others. I've added a patch with appropriate NetBSD->GDB and GDB->NetBSD signal number conversions and enabled it in all NetBSD CPU-specific files.
Later, newly added code respects now these signal conversions in the management of processes.
Threading support
I've implemented the NetBSD specific methods for dealing with threads. Previously the pristine version of GDB was unaware about threading on NetBSD and the basesystem GDB relied on local patches that needed reimplementation to meet the expectations (especially rewrite to C++) of the upstream maintainers.
I have upstreamed this with the following commit:
Implement basic threading support in the NetBSD target Use sysctl(3) as the portable interface to prompt NetBSD threads on all supported NetBSD versions. In future newer versions could switch to PT_LWPSTATUS ptrace(2) API that will be supported on NetBSD 10.0 and newer. Implement as part of nbsd_nat_target: - thread_name() - read descriptive thread name - thread_alive() - check whether a thread is alive - post_attach() - updates the list of threads after attach - update_thread_list() - updates the list of threads - pid_to_str() - translates ptid to a descriptive string There are two local static functions: - nbsd_thread_lister() - generic LWP lister for a specified pid - nbsd_add_threads() - utility to update the list of threads Now, GDB on NetBSD can attach to a multithreaded process, spawn a multithreaded process, list threads, print their LWP+PID numbers and descriptive thread names.
ELF symbol resolver
The NetBSD operating system relies on the ELF file format for native applications.
One of the features in GDB is to skip single-stepping over the internal ELF loader code.
When a user of GDB instructs ther debugger to step a line in a source code,
and whenever we land into an unresolved symbol in the GOT table, we could step internal code of the ELF loader,
for the first usage of a public symbol, which is not necessarily wrong, but could be confusing.
This is typically worked around with a generic code that tries to detect such scenario examining
among others the code sections of the stepped code, but the default fallback is not functional on every
NetBSD port, especially Alpha and ARM.
The new code merged into GDB uses the same logic for all NetBSD ports, trying to detect the
_rtld_bind_start
symbol and act acordingly in case of detecting it.
SVR4 psABI parsers of AUXV entries
The ELF format ships with a mechanism to transfer certain kernel level information to the user process. AUXV is a key-value array located on the stack and available to an ELF loader. While Linux uses a different format than the one specified in SVR4 psABI, NetBSD follows the standard and stores the key value in a 32-bit integer always. This caused breakage on 64-bit CPUs and the NetBSD developers used to patch the Linux AUXV code to be compatible with the NetBSD behavior. I've added a dedicated function for the NetBSD AUXV handling and switched to it all NetBSD CPUs.
Process information (info proc
)
As documented by the
GDB project:
Some operating systems provide interfaces to fetch additional
information about running processes beyond memory and per-thread
register state. If GDB is configured for an operating system with
a supported interface, the command
info proc
is available to report
information about the process running your program, or about any
process running on your system.
Previously the info proc
functionality was implemented only for Linux and FreeBSD.
I've implemented support for the following commands:
info proc
|info proc process-id
- Summarize available information about a process.info proc cmdline
- Show the original command line of the process.info proc cwd
- Show the current working directory of the process.info proc exe
- Show the name of executable of the process.info proc mappings
- Report the memory address space ranges accessible in a process.info proc stat
|info proc status
- Show additional process-related information.info proc all
- Show all the information about the process described under all of the above info proc subcommands.
All of these pieces of information are retrieved with the sysctl(3) interface. Example of an execution of the command is below:
(gdb) info proc all process 26015 cmdline = '/usr/bin/cal' cwd = '/public/binutils-gdb-netbsd' exe = '/usr/bin/cal' Mapped address spaces: Start Addr End Addr Size Offset Flags File 0x200000 0x204000 0x4000 0x0 r-x C-PD /usr/bin/cal 0x404000 0x405000 0x1000 0x4000 r-- C-PD /usr/bin/cal 0x405000 0x406000 0x1000 0x0 rw- C-PD 0x7f7ff6c00000 0x7f7ff6c10000 0x10000 0x0 rw- C-PD 0x7f7ff6c10000 0x7f7ff6db0000 0x1a0000 0x0 rw- CNPD 0x7f7ff6db0000 0x7f7ff6dc0000 0x10000 0x0 rw- C-PD 0x7f7ff6dc0000 0x7f7ff7000000 0x240000 0x0 rw- CNPD 0x7f7ff7000000 0x7f7ff7010000 0x10000 0x0 rw- C-PD 0x7f7ff7010000 0x7f7ff7200000 0x1f0000 0x0 rw- CNPD 0x7f7ff7200000 0x7f7ff7260000 0x60000 0x0 r-x CNPD /lib/libc.so.12.215 0x7f7ff7260000 0x7f7ff7270000 0x10000 0x60000 r-x C-PD /lib/libc.so.12.215 0x7f7ff7270000 0x7f7ff73c6000 0x156000 0x70000 r-x CNPD /lib/libc.so.12.215 0x7f7ff73c6000 0x7f7ff75c6000 0x200000 0x1c6000 --- CNPD /lib/libc.so.12.215 0x7f7ff75c6000 0x7f7ff75d1000 0xb000 0x1c6000 r-- C-PD /lib/libc.so.12.215 0x7f7ff75d1000 0x7f7ff75d7000 0x6000 0x1d1000 rw- C-PD /lib/libc.so.12.215 0x7f7ff75d7000 0x7f7ff75f0000 0x19000 0x0 rw- C-PD 0x7f7ff75f0000 0x7f7ff76e0000 0xf0000 0x0 rw- CNPD 0x7f7ff76e0000 0x7f7ff76f0000 0x10000 0x0 rw- C-PD 0x7f7ff76f0000 0x7f7ff77e0000 0xf0000 0x0 rw- CNPD 0x7f7ff77e0000 0x7f7ff77f8000 0x18000 0x0 rw- C-PD 0x7f7ff7800000 0x7f7ff780e000 0xe000 0x0 r-x CNPD /lib/libterminfo.so.2.0 0x7f7ff780e000 0x7f7ff7a0d000 0x1ff000 0xe000 --- CNPD /lib/libterminfo.so.2.0 0x7f7ff7a0d000 0x7f7ff7a0e000 0x1000 0xd000 r-- C-PD /lib/libterminfo.so.2.0 0x7f7ff7a0e000 0x7f7ff7a0f000 0x1000 0xe000 rw- C-PD /lib/libterminfo.so.2.0 0x7f7ff7c00000 0x7f7ff7c0f000 0xf000 0x0 r-x C-PD /libexec/ld.elf_so 0x7f7ff7c0f000 0x7f7ff7e0f000 0x200000 0x0 --- CNPD 0x7f7ff7e0f000 0x7f7ff7e10000 0x1000 0xf000 rw- C-PD /libexec/ld.elf_so 0x7f7ff7e10000 0x7f7ff7e11000 0x1000 0x0 rw- C-PD 0x7f7ff7eed000 0x7f7ff7eff000 0x12000 0x0 rw- C-PD 0x7f7ff7eff000 0x7f7fffbff000 0x7d00000 0x0 --- CNPD 0x7f7fffbff000 0x7f7fffff0000 0x3f1000 0x0 rw- CNPD 0x7f7fffff0000 0x7f7ffffff000 0xf000 0x0 rw- C-PD Name: cal State: STOP Parent process: 11837 Process group: 26015 Session id: 15656 TTY: 1288 TTY owner process group: 11837 User IDs (real, effective, saved): 1000 1000 1000 Group IDs (real, effective, saved): 100 100 100 Groups: 100 0 5 Minor faults (no memory page): 292 Major faults (memory page faults): 0 utime: 0.000000 stime: 0.003510 utime+stime, children: 0.000000 'nice' value: 20 Start time: 1588600926.724211 Data size: 8 kB Stack size: 8 kB Text size: 16 kB Resident set size: 1264 kB Maximum RSS: 2000 kB Pending Signals: 00000000 00000000 00000000 00000000 Ignored Signals: 98488000 00000000 00000000 00000000 Caught Signals: 00000000 00000000 00000000 00000000
Event handling
I've implemented event handling for the following trap types:
- single step (
TRAP_TRACE
) - software breakpoint (
TRAP_DBREG
) - exec() (
TRAP_EXEC
) - syscall entry/exit (
TRAP_SCE
/TRAP_SCX
)
While there, I have added proper support for ::wait ()
and ::resume ()
methods.
Syscall entry/exit tracing
I've added a support for syscall entry/exit breakpoint traps.
There used to be some support for this mode in the basesystem GDB, however we missed a list of mapping
of syscall number to syscall name.
I've borrowed the script from FreeBSD to generate netbsd.xml with the mapping,
based on definitions in /usr/include/sys/syscall.h
.
This approach of mapping syscalls for NetBSD is imperfect as the internal names are not stable and change whenever
a syscall is versioned. There is no ideal approach to handle this
(e.g. debugging programs on NetBSD 9.0 and NetBSD 10.0 can behave differently), and end-users will need to deal with it.
Threading events
As threading support is mandatory these days, I have implemented and upstreamed support for thread creation and thread exit events.
Other changes
As in general,
I oppose to treating all BSD Operating Systems under the same ifdef in existing software,
as it leads to conditionals like #if defined(AllBSDs) && !defined(ThisBSD)
and/or
a spaghetti of #define
symbol renames.
There was an attempt to define support for all BSDs in a shared file inf-ptrace.c, however in the
end developers pushing for that reimplemented support part of the code for their kernels in private per-OS
files.
This left a lot of shared, sometimes unneeded code in the common layer.
I was kind to fix the build of OpenBSD support in GDB (and only build-tested) and moved OpenBSD-specific code from inf-ptrace.c to obsd-nat.c. Code that was no longer needed for OpenBSD in inf-ptrace.c (as it was reimplemented in obsd-nat.c) and in theory shared with NetBSD was removed.
My intention is to restrict common shared code of GDB to really common parts and whenever kernels differ, implement their specific handling in dedicated files. There are still some hacks left in the GDB shared code (even in inf-ptrace.c) for long removed kernels that obfuscate the support... especially the Gould NP1 support from the 1980s and definitely removed in 2000.
Plan for the next milestone
Finish and upstream operational support of follow-fork, follow-vfork and follow-spawn events. Rewrite the gdbserver support and submit upstream. [0 comments]