GDB Debugging

Launching

Is is not necessary use the prefix ./ to load an executable. The corresponding source files are loaded automatically according to information embedded inside the executable, manual loading is possible with the directory command.

Common information

Commands may always be truncated if the abbreviation is unambiguous.

Environment variables can be modified within GDB, which is useful to control behavior of programs.

The handling of signals like SIGINT - which is often invoked by Ctrl+c - and other signals can be modified, shown and even send from within GDB.

Forking and Threading

Process Forking

https://sourceware.org/gdb/current/onlinedocs/gdb/Forks.html

Multi Threading

There are two modes available the all-stop mode - which is the default - and the non-stop mode - when the target supports it. The mode preference only applies when GDB starts or connects to a program. In all-stop mode all threads stop execution when GDB halts the program and all threads continue execution when GDB continues execution - also when stepping. In non-stop mode all threads continue execution while only one or multiple threads are halted by GDB.

In multi-threaded programs breakpoints or some other events in one thread may cause blocking system calls in other threads to return prematurely. Therefore it is important to check and handle the return values of system calls appropriately - anyway a good programming style e.g. use the return of sleep() within a while loop.

https://sourceware.org/gdb/current/onlinedocs/gdb/Thread-Stops.html

Running

Remote debugging

Their are two possible modes remote and remote-extended where the later offers more comfortable features but is sometimes not available. The remote side is refered as target and the debugging side usually as host. While the executable on the target can be stripped, the executable on the host must be fully debuggable i.e. symbol tables and libraries are available. If necessary use:

How-to
  1. on target
    • $ gdbserver hostname_of_host:port_number executable start program
    • $ gdbserver hostname_of_host:port_number --attach process_id attach to already running program
  2. $ gdb on host
  3. target [remote | extended-remote] hostname_of_target:port_number within gdb on host

It is also possible to use STDIO pipes:

https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Debugging.html

Linespec Location

Several commands accept a location as line_spec:

Symbols specific to C++ programming language - like namespaces, classes and scope operators - are usable e.g. [namespace::class::member_function]. When an expression is ambiguous a menu can be displayed, by default this feature is off and all possible matches are used. This behavior can be modified with set multiple-symbols mode.

Navigation

Stepping and Continuing

Breakpoints and Watchpoints

Print, Modify and Evaluate

General Informations

Core dumps

Patching executables

An executable file must be loaded writable and an address within the executable modified with set - afterwards GDB must be quit immediately.

How-to

  1. set write on allows modification of binary files, likely causes messages like text file bussy
  2. file executable
  3. disas/r function show disassembly
  4. set {type} addr = value where type is a variable type, addr is address within the executable - retrieved from the dissassembly and not the from the current program memory like a variable or pointer address.
  5. quit leave gdb, file is written

A binary patch can be created with diff [-a | --text] and applied by patch but diff is not designed for binary patches. The resulting patch likely becomes bigger then the binaries without actual source lines to compare. Therefore dedicated binary patch tools are commended and perform appropriately. Git has also support for creating and applying binary patches. Binary patching also can be done manually with UNIX tools like dd and strings:
https://unix.stackexchange.com/a/214824

Reverse Debugging

Reverse debugging stores a record within a buffer, the record either stops when it reaches the limit of the buffer or overwrites the exiting record when working as circular buffer. * set record stop-at-limit [on | off] when off record log is used as circular/ring-buffer * show record stop-at-limit

https://www.sourceware.org/gdb/wiki/ProcessRecord/Tutorial

How-to

  1. breakpoint [line_spec] program must be running, set break at appropriate location e.g. main() or even _start
  2. run
  3. record
    • following commands can be used interchangeably now:
    • reverse-[next | step | finish | ...] - reverse execute
    • [next | step | finish | ...] - forward execute
    • and so on
    • when all breakpoints are disabled a continue will return the program to the end of the current record and halt
  4. record stop

Workarounds

In case of issues with xsave or similiar set the environment variable LD_BIND_NOW to 1 before running the program.

Quote from the man page:

If set to a nonempty string, causes the dynamic linker to resolve all symbols at program startup instead of deferring function call resolution to the point when they are first referenced. This is useful when using a debugger.

Resources and Links

https://sourceware.org/gdb/