Actually found out that it could be even simpler, and as long as the preloader exposes `_dl_debug_state` and fills the DT_DEBUG entry with a `struct r_debug` Gdb will be happy. In addition, with r_debug version >= 2, it's possible to have multiple r_debug chained together and we can simply lookup ld.so's own r_debug and chain it with ours without having to copy it at all.
Without the systemtap probes, Gdb will hook each `r_debug.r_brk` with breakpoints and we can call our own PE-only `r_debug.r_brk`, while ld.so will call theirs for unix libraries and every library load / unload will then be dynamically updated in Gdb.
Awesome. Well at least we get symbols and backtraces.
For now the backtraces are only on either the unix side (with syscall frames unwinding over and skipping PE frames), or on the PE side only (with backtrace stopping on KiUserCallbackDispatcher). In order to cross the boundaries we need a custom unwinder, and because of some Gdb internal heuristics (refusing to unwind to inner frames) it also probably needs some small Gdb patches.