r_debug
I’ve been trying to figure out how to get information about libraries loaded with dlmopen
out of glibc‘s runtime linker and into GDB.
The current interface uses a structure called r_debug
that’s defined in link.h
. If the executable’s dynamic section has a DT_DEBUG
element, the runtime linker sets that element’s value to the address where this structure can be found. I tried to discover where this interface originated, but I didn’t get very far. The only mention of it I found anywhere in any standard is in the System V Application Binary Interface, where it says:
If an object file participates in dynamic linking, its program header table will have an element of type
PT_DYNAMIC
. This “segment” contains the.dynamic
section. A special symbol,_DYNAMIC
, labels the section…
and later:
DT_DEBUG
- This member is used for debugging. Its contents are not specified for the ABI; programs that access this entry are not ABI-conforming.
No help there then. In glibc, r_debug
looks like this:
struct r_debug { int r_version; /* Version number for this protocol. */ struct link_map *r_map; /* Head of the chain of loaded objects. */ /* This is the address of a function internal to the run-time linker, that will always be called when the linker begins to map in a library or unmap it, and again when the mapping change is complete. The debugger can set a breakpoint at this address if it wants to notice shared object mapping changes. */ ElfW(Addr) r_brk; enum { /* This state value describes the mapping change taking place when the `r_brk' address is called. */ RT_CONSISTENT, /* Mapping change is complete. */ RT_ADD, /* Beginning to add a new object. */ RT_DELETE /* Beginning to remove an object mapping. */ } r_state; ElfW(Addr) r_ldbase; /* Base address the linker is loaded at. */ };
With glibc, r_version == 1
. At least some versions of Solaris have r_version == 2
, and when this is the case there are three extra fields, r_ldsomap
, r_rdevent
, r_flags
. GDB uses r_ldsomap
if r_version == 2
; the other two seem to be the interface with librtld_db
. That’s not documented anywhere to my knowledge, and may not even be fixed: applications are supposed to use the external interface to librtld_db
as documented here.
Here is the problem: r_debug
, as it stands, has no way to access more than one namespace. The objects in r_map
are the default namespace, directly linked, or opened with dlopen
, or opened with dlmopen
with lmid
set to LM_ID_BASE
. The r_ldsomap
field in Solaris’s r_debug
gives access to the linker’s namespace, opened with dlmopen
with lmid
set to LM_ID_LDSO
, but you still can’t see any other namespaces.
glibc uses multiple r_debug
structures internally, one per namespace. It would be trivial to add a “next r_debug
” link to r_debug
if it were possible to extend the structure, but to do this you’d need to set r_version > 2
. Applications could arguably expect a runtime linker with r_version > 2
to support the version 2 interface in full, but it wouldn’t be possible to do that in glibc without reverse engineering Solaris’s implementation. glibc is therefore stuck at r_version == 1
, and the r_debug
structure is effectively immutable for all time.