gdb and source path substitution

I want to see the source in the source in gdb while debugging.

Prepare the system

Start a cheap ubuntu 16.04 instance:

Edit the source.list file:

root@scw-96c316:~# nano /etc/apt/sources.list

It should contain something like this:

deb xenial main universe
deb xenial-updates main universe
deb xenial-security main universe

Add a line like this:

deb-src xenial main universe

To be able to use apt source, install dpkg-src:

root@scw-96c316:~# apt install dpkg-dev

Install the program

Let's debug gdb itself:

root@scw-96c316:~# apt install gdb
root@scw-96c316:~# apt install gdb-dbg
root@scw-96c316:~# apt source gdb

Source are unpacked in the current directory:

root@scw-96c316:~# ls
gdb-7.11  gdb_7.11-0ubuntu1.debian.tar.xz  gdb_7.11-0ubuntu1.dsc  gdb_7.11.orig.tar.xz


Start gdb:

root@scw-96c316:~# gdb

Load gdb for debugging, set a breakpoint in main, and run the program:

(gdb) file /usr/bin/gdb
Reading symbols from /usr/bin/gdb...Reading symbols from /usr/lib/debug/.build-id/23/b34ad95e5487999606aeede51437f54e5672c6.debug...done.
(gdb) b main
Breakpoint 1 at 0x45eca0: file /build/gdb-9un5Xp/gdb-7.11.1/gdb/gdb.c, line 25.
(gdb) r
Starting program: /usr/bin/gdb
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/".

Breakpoint 1, main (argc=1, argv=0x7fffffffe6a8) at /build/gdb-9un5Xp/gdb-7.11.1/gdb/gdb.c:25
25      /build/gdb-9un5Xp/gdb-7.11.1/gdb/gdb.c: No such file or directory.

As expected, the gdb stops at the breakpoint, and tries to find the matching source file. It fails because the file was located at /build/gdb-9un5Xp/gdb-7.11.1/gdb/gdb.c on the ubuntu build server, while we have the source tree in /root/gdb-7.11/.

The list command to display the source code at the current location also fails:

(gdb) list
20      /build/gdb-9un5Xp/gdb-7.11.1/gdb/gdb.c: No such file or directory.

The magic setting is set substitute-path <from> <to> (see [1]):

(gdb) set substitute-path /build/gdb-9un5Xp/gdb-7.11.1/ /root/gdb-7.11/
(gdb) show substitute-path
List of all source path substitution rules:
  `/build/gdb-9un5Xp/gdb-7.11.1' -> `/root/gdb-7.11'.

Note: shell shortcuts such as ~ for the home directory do not work here.

Let's try the same again:

(gdb) list main
20      #include "main.h"
21      #include "interps.h"
23      int
24      main (int argc, char **argv)
25      {
26        struct captured_main_args args;
28        memset (&args, 0, sizeof args);
29        args.argc = argc;

Disassembly of the current location, mixing assembly and code:

(gdb) disas /m
Dump of assembler code for function main:
25      {
=> 0x000000000045eca0 <+0>:     sub    $0x28,%rsp
   0x000000000045ecb2 <+18>:    mov    %fs:0x28,%rax
   0x000000000045ecbb <+27>:    mov    %rax,0x18(%rsp)
   0x000000000045ecc0 <+32>:    xor    %eax,%eax

26        struct captured_main_args args;
28        memset (&args, 0, sizeof args);
29        args.argc = argc;
   0x000000000045ecac <+12>:    mov    %edi,(%rsp)

30        args.argv = argv;
   0x000000000045ecc2 <+34>:    mov    %rsi,0x8(%rsp)

31        args.interpreter_p = INTERP_CONSOLE;
   0x000000000045ecc7 <+39>:    movq   $0x79ea84,0x10(%rsp)

32        return gdb_main (&args);
   0x000000000045ecaf <+15>:    mov    %rsp,%rdi
   0x000000000045ecd0 <+48>:    callq  0x5ce8f0 <gdb_main>

33      }
   0x000000000045ecd5 <+53>:    mov    0x18(%rsp),%rdx
   0x000000000045ecda <+58>:    xor    %fs:0x28,%rdx
   0x000000000045ece3 <+67>:    jne    0x45ecea <main+74>
   0x000000000045ece5 <+69>:    add    $0x28,%rsp
   0x000000000045ece9 <+73>:    retq
   0x000000000045ecea <+74>:    callq  0x45be10 <__stack_chk_fail@plt>

End of assembler dump.