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