How can I print stack trace for caught exceptions in C++ & code injection in C++

前端 未结 4 445
忘掉有多难
忘掉有多难 2020-12-07 15:16

I want to have stack trace not for my exceptions only but also for any descendants of std::exception

As I understand, stack trace is completely lost whe

4条回答
  •  自闭症患者
    2020-12-07 15:42

    Since you mentioned that you're happy with something that is GCC specific I've put together an example of a way you might do this. It's pure evil though, interposing on internals of the C++ support library. I'm not sure I'd want to use this in production code. Anyway:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    namespace {
      void * last_frames[20];
      size_t last_size;
      std::string exception_name;
    
      std::string demangle(const char *name) {
        int status;
        std::unique_ptr realname(abi::__cxa_demangle(name, 0, 0, &status), &std::free);
        return status ? "failed" : &*realname;
      }
    }
    
    extern "C" {
      void __cxa_throw(void *ex, void *info, void (*dest)(void *)) {
        exception_name = demangle(reinterpret_cast(info)->name());
        last_size = backtrace(last_frames, sizeof last_frames/sizeof(void*));
    
        static void (*const rethrow)(void*,void*,void(*)(void*)) __attribute__ ((noreturn)) = (void (*)(void*,void*,void(*)(void*)))dlsym(RTLD_NEXT, "__cxa_throw");
        rethrow(ex,info,dest);
      }
    }
    
    void foo() {
      throw 0;
    }
    
    int main() {
      try {
        foo();
      }
      catch (...) {
        std::cerr << "Caught a: " << exception_name << std::endl;
        // print to stderr
        backtrace_symbols_fd(last_frames, last_size, 2);
      }
    }
    

    We basically steal calls to the internal implementation function that GCC uses for dispatching thrown exceptions. At that point we take a stack trace and save it in a global variable. Then when we come across that exception later on in our try/catch we can work with the stacktrace to print/save or whatever it is you want to do. We use dlsym() to find the real version of __cxa_throw.

    My example throws an int to prove that you can do this with literally any type, not just your own user defined exceptions.

    It uses the type_info to get the name of the type that was thrown and then demangles it.

    You could encapsulate the global variables that store the stacktrace a bit better if you wanted to.

    I compiled and tested this with:

    g++ -Wall -Wextra test.cc -g -O0 -rdynamic -ldl

    Which gave the following when run:

    ./a.out
    Caught a: int
    ./a.out(__cxa_throw+0x74)[0x80499be]
    ./a.out(main+0x0)[0x8049a61]
    ./a.out(main+0x10)[0x8049a71]
    /lib/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xb75c2ca6]
    ./a.out[0x80497e1]
    

    Please don't take this as an example of good advice though - it's an example of what you can do with a little bit of trickery and poking around at the internals!

提交回复
热议问题