The GNU GDB Debugger and NetBSD (Part 2)

May 04, 2020 posted by Kamil Rytarowski

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/
      0x7f7ff7260000     0x7f7ff7270000    0x10000    0x60000  r-x C-PD /lib/
      0x7f7ff7270000     0x7f7ff73c6000   0x156000    0x70000  r-x CNPD /lib/
      0x7f7ff73c6000     0x7f7ff75c6000   0x200000   0x1c6000  --- CNPD /lib/
      0x7f7ff75c6000     0x7f7ff75d1000     0xb000   0x1c6000  r-- C-PD /lib/
      0x7f7ff75d1000     0x7f7ff75d7000     0x6000   0x1d1000  rw- C-PD /lib/
      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/
      0x7f7ff780e000     0x7f7ff7a0d000   0x1ff000     0xe000  --- CNPD /lib/
      0x7f7ff7a0d000     0x7f7ff7a0e000     0x1000     0xd000  r-- C-PD /lib/
      0x7f7ff7a0e000     0x7f7ff7a0f000     0x1000     0xe000  rw- C-PD /lib/
      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]


Post a Comment:
Comments are closed for this entry.