Hi Ben,
[Maybe read it all first before trying anything. At the end I think I
got the correct solution, I start with some more cumbersome
workarounds...]
On Thu, 2015-09-03 at 22:10 +0200, Ben Gamari wrote:
I now have a few patches [1] adding support for collecting initial
register values from the current thread. While I believe the initial
register collection logic works (it works for GHC), I have encountered
an unfortunate issue while folding this code into libebl. Namely, it
seems that libebl backends are dynamically loaded.
To see why this is problematic, consider this example,
void test() {
Dwfl *dwfl = dwfl_begin (&proc_callbacks);
// Figure out what modules are loaded
dwfl_linux_proc_report(dwfl, getpid());
dwfl_report_end (dwfl, NULL, NULL);
// but the ebl backend, libebl_x86_64.so, only gets loaded here
dwfl_linux_local_attach(dwfl);
// therefore when we begin to unwind we have no debug information
// for libebl, which is where PC sits when we collect the initial
// register values. Unwinding consequently fails
dwfl_getthread_frames (dwfl, getpid(), frame_cb, dwfl) != 0;
}
The issue here is that debug information is not reported to libdwfl as
it is not loaded at the time we report the loaded modules.
Ah, interesting case indeed. I think the easiest workaround for now
would just be to cheat and force the load of the native ebl backend in
some initial setup/initialization function:
static Dwfl *dwfl;
init ()
{
Dwfl_Module *mod;
Dwarf_Addr bias;
Dwarf_CFI *cfi;
dwfl = dwfl_begin (&proc_callbacks);
dwfl_linux_proc_report(dwfl, getpid());
dwfl_report_end (dwfl, NULL, NULL);
pc_here:
mod = dwfl_addrmodule (dwfl, (Dwarf_Addr) &&pc_here);
cfi = dwfl_module_eh_cfi (mod, &bias);
// Don't do this, it will unload the backend again.
// dwfl_end (dwfl);
}
But then checking none of the functions fail of course. The
dwfl_module_eh_cfi () will force the loading of the native backend.
You could even cheat a bit more and just call ebl_openbackend ()
directly yourself. But that isn't officially supported.
At this point I'm a bit unsure of how to proceed. I can think of
a few
options,
* teach dwfl_linux_local_attach to report libebl when it gets
loaded. This would be rather ugly as we would either need to
re-read the /proc map data or somehow extract the necessary
information from the dynamic linker
It might be interesting to have a Dwfl *dwfl_linux_local_report () that
creates a Dwfl using the dynamic linker structures. It could come with a
_refresh function or keep things automagically up to date (although that
might be tricky with threading). But that seems a but more work than
what you signed up for right now.
* Expose an entirely different interface from
dwfl_getthread_frames
for local unwinding such that PC doesn't sit within libebl when
initial register values are collected.
[...[
Unfortunately this is quite a departure from the current interface
and seems to break the nice separation between libebl and the
libdwfl.
Yeah, that seems not great.
* Something I haven't yet thought of...
Maybe you could just do:
dwfl_report_begin (dwfl);
dwfl_linux_proc_report(dwfl, getpid());
dwfl_report_end (dwfl, NULL, NULL);
after dwfl_linux_local_attach (), but before dwfl_getthread_frames ().
That is supported to simply refreshes the Dwfl_Modules into the Dwfl.
The only downside is that you reparse the /proc/pid/maps again, so it
isn't as efficient as it could be.
O, I should have thought of this at the start. Maybe you could just
start with an empty Dwfl. Do you need to call dwfl_linux_proc_report ()
before dwfl_linux_local_attach () at all? dwfl_linux_local_attach ()
should be able to figure out the ebl backend it needs on its own. Then
you could just report the modules as above after you called
dwfl_linux_local_attach ().
Cheers,
Mark