I’m attempting to add backtraces on exceptions in my programming language.
My current implementation generates dwarf information similarly to Rustc’s cranelift backend, but slimmed down a bit.
When calling unw_get_proc_name I always get -6540 (UNW_EUNSPEC). The other libunwind function calls prior seem to work fine.
I first assumed something was wrong with how I was generating dwarf information. However; when I ported the implementation from my own language to an equivalent implementation in Rust I get the same UNW_EUNSPEC (compiled with the musl toolchain which by default links to the necessary libs).
What are the prerequisites to get unw_get_proc_name to work? Am I misunderstanding its purpose?
Perhaps _Unwind_FindEnclosingFunction uses some completely different method to get the function symbol, which would explain why unw_get_proc_name fails in Rust as well (which uses the more high-level _Unwind API’s that may have different prerequisites)
__unw_get_proc_name relies on UnwindCursor::getFunctionName, which in turn relies on LocalAddressSpace::findFunctionName, which then uses dladdr if available, getFuncNameFromTBTable if on AIX or otherwise always fails. Does your platform provide a working dladdr? That’s generally implemented by walking the ELF symbol table (no DWARF needed).
My platform is Void Linux with glibc (or statically linked musl binaries).
Although, as backtraces work in other languages as long as I’m not the one who writes the code to collect them, it seems I’m the one doing something wrong and not the platform.
I’m not familiar at all with AIX traceback tables. Is this something I’m supposed to be generating?
Currently I’m relying on this function in the Cranelift code generator to define unwind information.
As my next step, I’ll test using the Unwind_Backtrace API instead.
If you’re not on AIX then you don’t want to use the AIX-specific interface. Have you put a breakpoint or some other tracing in to see what unw_get_proc_name is actually being called with / what it’s passing to dladdr? Either you have broken unwind information that gives nonsense addresses there or you’re getting correct addresses that dladdr isn’t giving the right result for.
Seems like many languages don’t resolve function symbols through libunwind but rather parse it as dwarf (or similar) from .debug_info using the addresses from libunwind.
I’m still unsure why I can’t get it to work. Entirely possible that Cranelift (and Rustc) simply doesn’t generate the required information since they instead depend on dwarf information. Or perhaps dladdr doesn’t work for statically linked function names.
But since I long-term want filepaths and line numbers in my backtraces I will take the dwarf debuginfo parsing route as well and stop using unw_get_proc_name entirely.
A bit of an update. While I currently am working with using dwarf debug information instead, I have made a peculiar discovery.
Simply switching from LLVM’s libunwind to GNU’s libunwind makes all the code that previously would throw unknown general errors instead just find and give the function symbol.
Even common C online examples of using unw_get_proc_name will seemingly always fail on llvm-libunwind while working with other implementations.
I do not know whether this is a known limitation of LLVM’s implementation or if this is a bug. If the latter then I’m surprised nobody else have reported it.
LLVM’s libunwind has a minimal implementation and tries not to rely on strippable information. https://github.com/libunwind/libunwind’s unw_get_proc_name follows .gnu_debuglink and parses the static symbol table SHT_SYMTAB.
If you do want to rely on strippable information (debug info, SHT_SYMTAB), you probably want to use llvm/lib/DebugInfo/Symbolize instead of LLVM libunwind.