4

I'm trying to recover my program stacktrace after an exception is thrown on an ARM machine using the backward-cpp library. When running a simple program on an AMD64 machine, the following code returns the expected stacktrace:

#include <backward/backward.hpp>

backward::SignalHandling sh{};

int main() {
    throw 1;
}
terminate called after throwing an instance of 'int'
Stack trace (most recent call last):
#9    Object "", at 0xffffffffffffffff, in 
#8    Object "/home/uy/Sandbox/SimpleC++/cmake-build-debug---native/main", at 0x556929e5308d, in 
#7    Object "/lib/x86_64-linux-gnu/libc.so.6", at 0x7fb8a1e8c0b2, in __libc_start_main
#6    Source "/home/uy/Sandbox/SimpleC++/main.cpp", line 7, in int main() [0x556929e53174]
          4: backward::SignalHandling sh{};
          5: 
          6: int main() {
      >   7:     throw 1;
          8: }
#5    Object "/lib/x86_64-linux-gnu/libstdc++.so.6", at 0x7fb8a211c798, in __cxa_throw
#4    Object "/lib/x86_64-linux-gnu/libstdc++.so.6", at 0x7fb8a211c4e6, in std::terminate()
#3    Object "/lib/x86_64-linux-gnu/libstdc++.so.6", at 0x7fb8a211c47b, in 
#2    Object "/lib/x86_64-linux-gnu/libstdc++.so.6", at 0x7fb8a2110950, in 
#1    Object "/lib/x86_64-linux-gnu/libc.so.6", at 0x7fb8a1e8a858, in abort
#0    Object "/lib/x86_64-linux-gnu/libc.so.6", at 0x7fb8a1eab18b, in gsignal
Aborted (Signal sent by tkill() 32939 1000)

However, when running the same code on an ARM machine, the stacktrace given by the program is not particularly useful:

terminate called after throwing an instance of 'int'
Stack trace (most recent call last):
#7    Object "/lib/arm-linux-gnueabihf/libc.so.6", at 0xb6d08e9f, in gsignal
#6    Object "/lib/arm-linux-gnueabihf/libc.so.6", at 0xb6cf9c65, in gnu_get_libc_version
#5    Object "/lib/arm-linux-gnueabihf/libc.so.6", at 0xb6d09cbf, in __default_sa_restorer
#4    Source "/home/uy/Sandbox/SimpleC++/backward-cpp/include/backward/backward.hpp", line 3972, in backward::SignalHandling::sig_handler(int signo, siginfo_t *info, void *_ctx) [0x7f5a2429]
#3    Source "/home/uy/Sandbox/SimpleC++/backward-cpp/include/backward/backward.hpp", line 3947, in backward::SignalHandling::handleSignal(int, siginfo_t *info, void *_ctx) [0x7f5a2371]
#2    Source "/home/uy/Sandbox/SimpleC++/backward-cpp/include/backward/backward.hpp", line 823, in size_t backward::StackTraceImpl<backward::system_tag::linux_tag>::load_from(void *addr, size_t depth) [0x7f59d44f]
#1    Source "/home/uy/Sandbox/SimpleC++/backward-cpp/include/backward/backward.hpp", line 817, in size_t backward::StackTraceImpl<backward::system_tag::linux_tag>::load_here(size_t depth) [0x7f59d3d3]
#0    Source "/home/uy/Sandbox/SimpleC++/backward-cpp/include/backward/backward.hpp", line 802, in size_t backward::details::unwind<backward::StackTraceImpl<backward::system_tag::linux_tag>::callback>(callback f, size_t depth) [0x7f5a2df3]
Aborted (Signal sent by tkill() 8424 1000)
Aborted

How do I get a stacktrace similar to the one I get on the AMD64 machine?

Uy Hà
  • 497
  • 1
  • 4
  • 16

1 Answers1

0

This is doable but needs access to internal details of how libgcc implements the _Unwind_Backtrace function. Fortunately the code is open-source, but depending on such internal details is brittle in that it may break in future versions of armgcc without any notice.

Generally, reading through the source of libgcc doing the backtrace, it creates an inmemory virtual representation of the CPU core registers, then uses this representation to walk up the stack, simulating exception throws. The first thing that _Unwind_Backtrace does is fill in this context from the current CPU registers, then call an internal implementation function.

Creating that context manually from the stacked exception structure is sufficient to fake the backtrace going from handler mode upwards through the call stack in most cases. Here is some example code (from https://github.com/bakerstu/openmrn/blob/62683863e8621cef35e94c9dcfe5abcaf996d7a2/src/freertos_drivers/common/cpu_profile.hxx#L162):

atozcodes
  • 1
  • 2
  • 13