I would like to ptrace a PIE on Linux and e.g. break at a given instruction address. From disassembly, I have the relative address of the instruction - how can I find out the location the executable was loaded at so I can get the absolute address?
GDB is able to trace PIEs - how does it deal with this?
how does it deal with this?
On Linux, the dynamic loader ld-linux...
has a special function: _dl_debug_state()
, which the loader calls just before and just after loading shared libraries, and just after setting a global variable: _r_debug.r_state
to RT_ADD
or RT_CONSISTENT
.
A debugger can set a breakpoint on that function, examine .r_state
and _r_debug.r_map
, and in doing so discover which ELF binaries are loaded at which address.
This mechanism was used to discover where e.g. libc.so.6
has been loaded long before PIE
binaries were supported. A PIE
binary is just a special case of a shared library, so the exact same mechanism works for it as well.
This begs the question: how does GDB discover where ld-linux
itself has been loaded (so the breakpoint on _dl_debug_state()
can be set). The kernel tells it via the auxiliary vector, AT_BASE
entry. More info on aux vector here.