1

In order to generate a stack trace I am using boost::stacktrace. I have a build agent on Debian that is compiling a unix and a windows version of the program. The Unix version is compiled using the native g++ installed and the windows cross compilation is created using mingw-w64. I am using the libbacktrace backend for both compilations. Both boost and libbacktrace are themselves compiled on the Debian machine using the same mingw-w64 compiler.

In my CMakeLists.txt I specify: add_definitions(-DBOOST_STACKTRACE_USE_BACKTRACE)

A stacktrace is generated like this:

namespace foo { class Bar { public: void fooBar() { std::cout << boost::stacktrace::stacktrace() << std::endl; } }; } int main(int argc, char *argv[]) { foo::Bar bar; bar.fooBar(); } 

This results in the following output (with -g and no optimization) when compiled on my computer (elementary OS) and when downloaded from the build server on my unix machine.

 0# foo::Bar::fooBar() in /home/cromon/CLionProjects/test-proj/cmake-build-debug/test-proj 1# main at /home/cromon/CLionProjects/test-proj/main.cpp:31 2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6 3# _start in /home/cromon/CLionProjects/test-proj/cmake-build-debug/test-proj 

This is the output with the binary created on the build server on my unix machine:

 0# foo::Bar::fooBar() in ./test-proj 1# main at /opt/teamcity/2018.2/TeamCity/buildAgent/work/d79789e141c5605f/test-proj/main.cpp:31 2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6 3# _start in ./test-proj 

Now if I am using the binary that was compiled using mingw on Unix on my Windows machine the following output can be observed

0# ZN5boost10stacktrace16basic_stacktraceISaINS0_5frameEEE4initEyy at /opt/teamcity/boost/1_69/windows/include/boost-1_69/boost/stacktrace/stacktrace.hpp:75 1# ZN3foo3Bar6fooBarEv at /opt/teamcity/2018.2/TeamCity/buildAgent/work/eb975d0a928ba129/test_proj/main.cpp:22 2# main at /opt/teamcity/2018.2/TeamCity/buildAgent/work/eb975d0a928ba129/test_proj/main.cpp:31 3# _tmainCRTStartup at ./mingw-w64-crt/crt/crtexe.c:336 4# mainCRTStartup at ./mingw-w64-crt/crt/crtexe.c:214 5# register_frame_ctor in C:\WINDOWS\System32\KERNEL32.DLL 6# register_frame_ctor in C:\WINDOWS\SYSTEM32\ntdll.dll 

I have also tried to loop through the frames and run a boost::core::demangle on them, but it fails to do so.

There is so far only one difference I can see between the compilation environment on unix and the runtime environment on my windows machine. On Windows I have g++ of version 8.2.0 and on unix it is 6.3.0. Is this causing any problems? What else could cause the demangling to fail only on windows when cross compiled?

1
  • 1
    I noticed, it seems to cut away just the first _, for example mainCRTStartup is actually named _mainCRTStartup and _tmainCRTStartup would be __tmainCRTStartup. Also _ZN3foo3Bar6fooBarEv sould properly be demangled online by a GCC demangler... Commented Jan 23, 2019 at 20:12

1 Answer 1

0

TL/DR: libbacktrace for some reason strips leading underscores and boost improperly calls backtrace_pcinfo -> Issue for libbacktrace and boost will be created (fixed in my local compilations)

More detailed response:

I was able to figure out the weird behavior by creating a debug build of libbacktrace and stepping through the code. There is - in my opinion - a bug in both boost and libbacktrace.

Very high level explanation how boost::stacktrace works when the libbacktrace backend is selected:

  • Call backtrace_pcinfo, this will read (on first call) and search through the DWARF debug information (at least the mingw implementation)
  • If the previous call was successful it will return here since it will have gotten more than enough info (function, file, line)
  • If it returned zero, it will try to call backtrace_syminfo. This will (for mingw) search the coff symbol table
  • If it returned zero you will get a raw address printed, otherwise at least the function name (no file/line)

The first bug/unexpected behavior I was able to trace back to libbacktrace (pun intended). The implementation in gcc/pecoff.c#440 for some reason strips the leading underscore if it is present. That is the reason the function names all lack their leading underscore and cannot be demangled. I think this can be considered a bug, or at least I dont see any other reason than a failed attempted at pretty printing symbols.

Now the attentive reader probably remembers from my question that I am using debug information, so it should not need to get into the coff symbol table but instead should use the DWARF debug information. And this is where the bug with boost is located.

backtrace_pcinfo has to be called with two callbacks. The first will receive the resolved symbol info (if any) and the second is an error callback. When a symbol is found the first callback is called and its return value is returned from backtrace_pcinfo or in code:

return of backtrace_pcinfo: return state->fileline_fn (state, pc, callback, error_callback, data);

fileline_fn for mingw is the dwarf implementation dwarf_fileline which returns like this:

ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback, data, &found); if (ret != 0 || found) return ret; 

dwarf_lookup_pc now returns what the callback returned when a symbol is found: return callback (data, pc, filename, lineno, function->name);

Now lets see how the callback looks boost is supplying:

inline int libbacktrace_full_callback(void *data, uintptr_t /*pc*/, const char *filename, int lineno, const char *function) { pc_data& d = *static_cast<pc_data*>(data); if (d.filename && filename) { *d.filename = filename; } if (d.function && function) { *d.function = function; } d.line = lineno; return 0; } 

Since the return value is directly propagated to backtrace_pcinfo this means that this check will always go into the or case no matter if anything was found:

 ::backtrace_pcinfo( state, reinterpret_cast<uintptr_t>(addr), boost::stacktrace::detail::libbacktrace_full_callback, boost::stacktrace::detail::libbacktrace_error_callback, &data ) || ::backtrace_syminfo( state, reinterpret_cast<uintptr_t>(addr), boost::stacktrace::detail::libbacktrace_syminfo_callback, boost::stacktrace::detail::libbacktrace_error_callback, &data ); 

This means it will always use the implementation that somehow strips the leading space. I changed the callback to return 1 and now everything is fine.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.