Given a position-independent, statically-linked, stripped binary, there does not appear to be a way in GDB to set a breakpoint at the entry point without disabling ASLR.

  • break start and similar functions do not work, because there is no symbolic information
  • set stop-on-solib-events 1 does not work as the binary is not dynamically linked
  • break *0xdeadbeef for the entry point does not work, as the entry point is unresolved until the binary starts
  • catch load does not work, as it does not load any libraries
  • start does not work, as main is not defined and no libraries are loaded

Without patching the binary, what mechanism can I use to break at the first instruction executed?


Since a now-deleted response to the question said that a PIE statically-linked binary is impossible, a trivial example is the linker itself.

It is statically linked.

$ ldd /lib/x86_64-linux-gnu/ld-2.19.so
    statically linked

It is executable.

$ strace /lib/x86_64-linux-gnu/ld-2.19.so
execve("/lib/x86_64-linux-gnu/ld-2.19.so", ["/lib/x86_64-linux-gnu/ld-2.19.so"], [/* 96 vars */]) = 0
brk(0)                                  = 0x7ff787b3d000
writev(2, [{"Usage: ld.so [OPTION]... EXECUTA"..., 1373}], 1Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]

It is position-independent.

$ readelf -h /lib/x86_64-linux-gnu/ld-2.19.so | grep DYN
  Type:                              DYN (Shared object file)


It looks like this can be done with Python by utilizing some of the events made available: http://asciinema.org/a/19078

However, I'd like a native-GDB solution.

A successful solution will break at _start in ld.so when executed directly without disabling ASLR. It should look something like this:

sh $ strip -s /lib/x86_64-linux-gnu/ld-2.19.so -o ld.so
sh $ gdb ./ld.so
(gdb) $ set disable-randomization off
(gdb) $ <your magic commands>
(gdb) $ x/i $pc
=> 0x7f9ba515d2d0:     mov    rdi,rsp
(gdb) $ info proc map
process 10432
Mapped address spaces:

        Start Addr           End Addr       Size     Offset objfile
    0x7f9ba515c000     0x7f9ba517f000    0x23000        0x0 /lib/x86_64-linux-gnu/ld-2.19.so 
    0x7f9ba537e000     0x7f9ba5380000     0x2000    0x22000 /lib/x86_64- linux-gnu/ld-2.19.so
    0x7f9ba5380000     0x7f9ba5381000     0x1000        0x0 
    0x7fffc34c7000     0x7fffc38ca000   0x403000        0x0 [stack]
    0x7fffc398b000     0x7fffc398d000     0x2000        0x0 [vdso]
0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]

UPDATE: GDB 8.1 has a starti command, as mentioned below by /u/ruslan

Setting a breakpoint on an unmapped address before starting the target process does this, effectively. It's not correct functionality, but rather a side-effect of the failure to set the breakpoint.

(gdb) break *0
Breakpoint 1 at 0x0
(gdb) r
Starting program: /home/user/ld.so 
Error in re-setting breakpoint 1: Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x0

Cannot insert breakpoint 1.
Cannot access memory at address 0x0

(gdb) x/i $pc
=> 0x7faae3a25cd0:      mov    rdi,rsp

Starting with GDB 8.1, there's a special command for this: starti. Example GDB session:

$ gdb /bin/true
Reading symbols from /bin/true...(no debugging symbols found)...done.
(gdb) starti
Starting program: /bin/true 

Program stopped.
0xf7fdd800 in _start () from /lib/ld-linux.so.2
(gdb) x/5i $pc
=> 0xf7fdd800 <_start>: mov    eax,esp
   0xf7fdd802 <_start+2>:       call   0xf7fe2160 <_dl_start>
   0xf7fdd807 <_dl_start_user>: mov    edi,eax
   0xf7fdd809 <_dl_start_user+2>:       call   0xf7fdd7f0
   0xf7fdd80e <_dl_start_user+7>:       add    ebx,0x1f7e6

Your Answer

