1

Is there any way in gcc/linux to have a user-defined function called immediately after any C++ throw statement executes, but before the stack is unwound to the catch? (I want to capture a stacktrace.)

(In gdb I can write catch throw. Anyway to do that programmatically short of a compiler extension?)

Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
  • 1
    I answered a similar question a while back: https://stackoverflow.com/a/11674810/168175, with stack trace and rethrow. – Flexo Jan 17 '21 at 22:16

2 Answers2

2

If you are using libstdc++, you could override __cxa_throw().

For instance:

#include <cstring>

void __cxa_throw(void *, void *, void (*)(void *)) {
    std::puts("bad luck");
}

int main() {
    throw 13;
}
Acorn
  • 24,970
  • 5
  • 40
  • 69
  • @MarshallClow Well, when you are there you can do whatever you want, including calling the proper thrower. – Acorn Jan 17 '21 at 21:08
  • If it's somebody else's program, you can use `LD_LIBRARY_PATH` - I have an example [here](https://github.com/AaronDMarasco/rpm-cookbook#spoofing-rpm-host-name). – Aaron D. Marasco Jan 17 '21 at 21:10
  • A better example [here](https://github.com/AaronDMarasco/file-stealer). – Aaron D. Marasco Jan 17 '21 at 21:12
  • @AaronD.Marasco Indeed! Thanks for those links. – Acorn Jan 17 '21 at 21:13
  • How would you call the "proper thrower" from within __cxa_throw? As Marshall said, once the function is complete I want the exception to be thrown and caught normally. – Andrew Tomazos Jan 17 '21 at 21:14
  • @AndrewTomazos Calling the real `__cxa_throw()`, forwarding the arguments. You just need to save a function pointer with the address of the real `__cxa_throw()` somewhere. – Acorn Jan 17 '21 at 21:22
  • @Acorn: How do I get the address of the real `__cxa_throw`? `&__cxa_throw` will give address of user-defined one won't it? Actually I don't even know how your program links, why doesn't user-defined `__cxa_throw` cause link conflict with the original one? – Andrew Tomazos Jan 17 '21 at 21:27
  • You mean with `dlsym` or what? – Andrew Tomazos Jan 17 '21 at 21:31
  • @AndrewTomazos If you don't know your way around this, it will be quite hard to get a stack trace if you plan on implementing that yourself. Anyway, you can do it in several ways; the easiest being using another compilation unit where you haven't overridden it. – Acorn Jan 17 '21 at 21:33
  • No, you don't need `dlsym`, just create another independent file, save the address there, and call the address in the first file. – Acorn Jan 17 '21 at 21:34
  • FYI you can get a stacktrace with `boost::stacktrace::stacktrace()` – Andrew Tomazos Jan 17 '21 at 22:53
  • The original address can be found with `dlsym(RTLD_NEXT,...)` as shown in this example https://github.com/AaronDMarasco/file-stealer/blob/master/stealer.cpp#L21 – Aaron D. Marasco Jan 18 '21 at 17:07
0

at your throwing site you can do this:

try { throw 13; }
catch (...) { print_stack_trace(); rethrow;}

Of course, if you want this everywhere, then you'll need some automated editing to put it there (or a macro like this): untested code

#define THROW(x) do { \
                   try {throw(x);} \
                   catch(...) { print_stack_trace(); rethrow;} \
                 } while(false)

or even simpler:

#define THROW(x) do { \
                   print_stack_trace(); \
                   throw(x); \
                 } while(false)
Marshall Clow
  • 15,972
  • 2
  • 29
  • 45