From 0eb6cad1ba58a6ccdaf2adedc7c3674b5ad7a9db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Fri, 6 Apr 2018 19:05:38 +0200 Subject: [PATCH] Detect end-of-unwind According to LibUnwind:src/x86_64/Gstep.c:85 (HEAD=c91974f30feac05055621e33ca101a371236c786), the x86_64 ABI states that the end of the call stack is determined by either a null RBP or an undefined return-address column in the DWARF. --- include/dwarfinterpret/DwarfInterpret.hpp | 7 ++++++- src/DwarfInterpret.cpp | 17 +++++++++++++---- test/dump_my_threads.cpp | 6 +++--- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/include/dwarfinterpret/DwarfInterpret.hpp b/include/dwarfinterpret/DwarfInterpret.hpp index 014a1dd..60c6333 100644 --- a/include/dwarfinterpret/DwarfInterpret.hpp +++ b/include/dwarfinterpret/DwarfInterpret.hpp @@ -81,6 +81,8 @@ class DwarfInterpret { /// Thrown when a Dwarf element is not found for a given PC class OF_WHAT_EXCEPTION(NotFound); + /// Thrown when trying to unwind a context with nothing more to unwind + class FirstUnwindFrame: public std::exception {}; /// A Dwarf register typedef dwarf::core::FrameSection::register_def DwarfRegister; @@ -172,7 +174,10 @@ class DwarfInterpret { */ static UnwindContext get_current_unwind_context(); - /// Unwinds once the given context + /** Unwinds once the given context + * + * \throws FirstUnwindFrame when trying to unwind the first frame. + */ UnwindContext unwind_context(const UnwindContext& ctx); private: diff --git a/src/DwarfInterpret.cpp b/src/DwarfInterpret.cpp index cd676b0..796b9ee 100644 --- a/src/DwarfInterpret.cpp +++ b/src/DwarfInterpret.cpp @@ -220,15 +220,24 @@ DwarfInterpret::UnwindContext DwarfInterpret::unwind_context( << cie.get_return_address_register_rule() << " at current IP = " << hex << ctx.rip << endl; - new_context.rip = interpret_dw_register( - cur_row, - cie.get_return_address_register_rule(), - ctx); + try { + new_context.rip = interpret_dw_register( + cur_row, + cie.get_return_address_register_rule(), + ctx); + } catch(const std::out_of_range& e) { + // An undefined RA means we've reached the end of the call stack + throw FirstUnwindFrame(); + } cerr << "Yielding " << hex << new_context.rip << dec << endl; new_context.rbp = interpret_dw_register( cur_row, lib::DWARF_X86_64_RBP, ctx); + if(new_context.rbp == 0) { + // A null rbp means we've reached the end of the call stack + throw FirstUnwindFrame(); + } new_context.rsp = interpret_dw_register( cur_row, diff --git a/test/dump_my_threads.cpp b/test/dump_my_threads.cpp index ffb201f..259b592 100644 --- a/test/dump_my_threads.cpp +++ b/test/dump_my_threads.cpp @@ -30,9 +30,9 @@ void dump_my_stack(int thread_id) { cur_map_entry.pathname.c_str()); fflush(stdout); try { - unw_context = dw.unwind_context(unw_context); - } catch(...) { - // Assume we're reached _start + unw_context = dw.unwind_context(unw_context); + } catch(const DwarfInterpret::FirstUnwindFrame& e) { + // We're reached the end of the stack return; } }