/// Called by __cxa_throw. Only returns if there is a fatal error. _LIBUNWIND_EXPORT _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception *exception_object) { _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)", (void *)exception_object); unw_context_t uc; unw_cursor_t cursor; __unw_getcontext(&uc);
// Mark that this is a non-forced unwind, so _Unwind_Resume() // can do the right thing. exception_object->private_1 = 0; exception_object->private_2 = 0;
// phase 1: the search phase _Unwind_Reason_Code phase1 = unwind_phase1(&uc, &cursor, exception_object); if (phase1 != _URC_NO_REASON) return phase1;
// phase 2: the clean up phase returnunwind_phase2(&uc, &cursor, exception_object); }
struct _Unwind_Exception { _Unwind_Exception_Class exception_class; void (*exception_cleanup)(_Unwind_Reason_Code reason, _Unwind_Exception *exc); #if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) uintptr_t private_[6]; #else uintptr_t private_1; // non-zero means forced unwind uintptr_t private_2; // holds sp that phase1 found for phase2 to use #endif #if __SIZEOF_POINTER__ == 4 // The implementation of _Unwind_Exception uses an attribute mode on the // above fields which has the side effect of causing this whole struct to // round up to 32 bytes in size (48 with SEH). To be more explicit, we add // pad fields added for binary compatibility. uint32_t reserved[3]; #endif // The Itanium ABI requires that _Unwind_Exception objects are "double-word // aligned". GCC has interpreted this to mean "use the maximum useful // alignment for the target"; so do we. } __attribute__((__aligned__));
// Walk each frame looking for a place to stop. while (true) { // Ask libunwind to get next frame (skip over first which is // _Unwind_RaiseException). int stepResult = __unw_step(cursor); if (stepResult == 0) { (void *)exception_object); return _URC_END_OF_STACK; } elseif (stepResult < 0) { (void *)exception_object); return _URC_FATAL_PHASE1_ERROR; }
// See if frame has code to run (has personality routine). unw_proc_info_t frameInfo; unw_word_t sp; if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { (void *)exception_object); return _URC_FATAL_PHASE1_ERROR; }
// If there is a personality routine, ask it if it will want to stop at // this frame. if (frameInfo.handler != 0) { _Unwind_Personality_Fn p = (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler); _Unwind_Reason_Code personalityResult = (*p)(1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object, (struct _Unwind_Context *)(cursor)); switch (personalityResult) { case _URC_HANDLER_FOUND: // found a catch clause or locals that need destructing in this frame // stop search and remember stack pointer at the frame __unw_get_reg(cursor, UNW_REG_SP, &sp); exception_object->private_2 = (uintptr_t)sp; return _URC_NO_REASON;
case _URC_CONTINUE_UNWIND: // continue unwinding break;
/// Create a cursor of a thread in this process given 'context' recorded by /// __unw_getcontext(). _LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor, unw_context_t *context) { _LIBUNWIND_TRACE_API("__unw_init_local(cursor=%p, context=%p)", static_cast<void *>(cursor), static_cast<void *>(context)); #if defined(__i386__) # define REGISTER_KIND Registers_x86 ... #endif // Use "placement new" to allocate UnwindCursor in the cursor buffer. new (reinterpret_cast<UnwindCursor<LocalAddressSpace, REGISTER_KIND> *>(cursor)) UnwindCursor<LocalAddressSpace, REGISTER_KIND>( context, LocalAddressSpace::sThisAddressSpace); #undef REGISTER_KIND AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; co->setInfoBasedOnIPRegister();
// libunwind does not and should not depend on C++ library which means that we // need our own definition of inline placement new. staticvoid *operatornew(size_t, UnwindCursor<A, R> *p){ return p; }
/// UnwindCursor contains all state (including all register values) during /// an unwind. This is normally stack allocated inside a unw_cursor_t. template <typename A, typename R> classUnwindCursor :public AbstractUnwindCursor{ typedeftypename A::pint_tpint_t; public: UnwindCursor(unw_context_t *context, A &as); UnwindCursor(A &as, void *threadArg); ... A &_addressSpace; R _registers; unw_proc_info_t _info; bool _unwindInfoMissing; // 是否有调试信息 bool _isSignalFrame; };
template <typename A, typename R> UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as) : _addressSpace(as), _registers(context), _unwindInfoMissing(false), _isSignalFrame(false) { static_assert((check_fit<UnwindCursor<A, R>, unw_cursor_t>::does_fit), "UnwindCursor<> does not fit in unw_cursor_t"); static_assert((alignof(UnwindCursor<A, R>) <= alignof(unw_cursor_t)), "UnwindCursor<> requires more alignment than unw_cursor_t"); memset(&_info, 0, sizeof(_info)); }
structunw_proc_info_t { unw_word_t start_ip; /* start address of function */ unw_word_t end_ip; /* address after end of function */ unw_word_t lsda; /* address of language specific data area, */ /* or zero if not used */ unw_word_t handler; /* personality routine, or zero if not used */ unw_word_t gp; /* not used */ unw_word_t flags; /* not used */ uint32_t format; /* compact unwind encoding, or zero if none */ uint32_t unwind_info_size; /* size of DWARF unwind info, or zero if none */ unw_word_t unwind_info; /* address of DWARF unwind info, or zero */ unw_word_t extra; /* mach_header of mach-o image containing func */ }; typedefstructunw_proc_info_tunw_proc_info_t;
template <typename A, typename R> void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) { pint_t pc = static_cast<pint_t>(this->getReg(UNW_REG_IP)); // Exit early if at the top of the stack. if (pc == 0) { _unwindInfoMissing = true; return; } // If the last line of a function is a "throw" the compiler sometimes // emits no instructions after the call to __cxa_throw. This means // the return address is actually the start of the next function. // To disambiguate this, back up the pc when we know it is a return // address. if (isReturnAddress) --pc; // Ask address space object to find unwind sections for this pc. UnwindInfoSections sects; if (_addressSpace.findUnwindSections(pc, sects)) { .... #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) // If there is dwarf unwind info, look there next. if (sects.dwarf_section != 0) { if (this->getInfoFromDwarfSection(pc, sects)) { // found info in dwarf, done return; } } #endif ... } // no unwind info, flag that we can't reliably unwind _unwindInfoMissing = true; }
/// Move cursor to next frame. _LIBUNWIND_HIDDEN int __unw_step(unw_cursor_t *cursor) { _LIBUNWIND_TRACE_API("__unw_step(cursor=%p)", static_cast<void *>(cursor)); AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; return co->step(); }
template <typename A, typename R> int UnwindCursor<A, R>::step(bool stage2) { (void)stage2; // Bottom of stack is defined is when unwind info cannot be found. if (_unwindInfoMissing) return UNW_STEP_END;
template <typename A, typename R> int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart, R ®isters, bool &isSignalFrame) { FDE_Info fdeInfo; CIE_Info cieInfo; if (CFI_Parser<A>::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL) { PrologInfo prolog; if (CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, R::getArch(), &prolog)) { // get pointer to cfa (architecture specific) pint_t cfa = getCFA(addressSpace, prolog, registers);
// restore registers that DWARF says were saved R newRegisters = registers;
// Typically, the CFA is the stack pointer at the call site in // the previous frame. However, there are scenarios in which this is not // true. For example, if we switched to a new stack. In that case, the // value of the previous SP might be indicated by a CFI directive. // // We set the SP here to the CFA, allowing for it to be overridden // by a CFI directive later on. newRegisters.setSP(cfa);
pint_t returnAddress = 0; constint lastReg = R::lastDwarfRegNum(); assert(static_cast<int>(CFI_Parser<A>::kMaxRegisterNumber) >= lastReg && "register range too large"); assert(lastReg >= (int)cieInfo.returnAddressRegister && "register range does not contain return address register"); for (int i = 0; i <= lastReg; ++i) { if (prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterUnused) { if (registers.validFloatRegister(i)) newRegisters.setFloatRegister( i, getSavedFloatRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); elseif (registers.validVectorRegister(i)) newRegisters.setVectorRegister( i, getSavedVectorRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); elseif (i == (int)cieInfo.returnAddressRegister) returnAddress = getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]); elseif (registers.validRegister(i)) newRegisters.setRegister( i, getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); else return UNW_EBADREG; } elseif (i == (int)cieInfo.returnAddressRegister) { // Leaf function keeps the return address in register and there is no // explicit intructions how to restore it. returnAddress = registers.getRegister(cieInfo.returnAddressRegister); } }
isSignalFrame = cieInfo.isSignalFrame;
...
// Return address is address after call site instruction, so setting IP to // that does simualates a return. newRegisters.setIP(returnAddress);
// Simulate the step by replacing the register set with the new ones. registers = newRegisters;
// uc is initialized by __unw_getcontext in the parent frame. The first stack // frame walked is unwind_phase2. unsigned framesWalked = 1; // Walk each frame until we reach where search phase said to stop. while (true) {
// Ask libunwind to get next frame (skip over first which is // _Unwind_RaiseException). int stepResult = __unw_step(cursor); if (stepResult == 0) { return _URC_END_OF_STACK; } elseif (stepResult < 0) { return _URC_FATAL_PHASE2_ERROR; }
// Get info about this frame. unw_word_t sp; unw_proc_info_t frameInfo; __unw_get_reg(cursor, UNW_REG_SP, &sp); if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { return _URC_FATAL_PHASE2_ERROR; }
++framesWalked; // If there is a personality routine, tell it we are unwinding. if (frameInfo.handler != 0) { _Unwind_Personality_Fn p = (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler); _Unwind_Action action = _UA_CLEANUP_PHASE; if (sp == exception_object->private_2) { // Tell personality this was the frame it marked in phase 1. action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME); } _Unwind_Reason_Code personalityResult = (*p)(1, action, exception_object->exception_class, exception_object, (struct _Unwind_Context *)(cursor)); switch (personalityResult) { case _URC_CONTINUE_UNWIND: // Continue unwinding if (sp == exception_object->private_2) { // Phase 1 said we would stop at this frame, but we did not... _LIBUNWIND_ABORT("during phase1 personality function said it would " "stop here, but now in phase2 it did not stop here"); } break; case _URC_INSTALL_CONTEXT: // Personality routine says to transfer control to landing pad. // We may get control back if landing pad calls _Unwind_Resume(). __unw_phase2_resume(cursor, framesWalked); // __unw_phase2_resume() only returns if there was an error. return _URC_FATAL_PHASE2_ERROR; default: // Personality routine returned an unknown result code. return _URC_FATAL_PHASE2_ERROR; } } }
// Clean up phase did not resume at the frame that the search phase // said it would... return _URC_FATAL_PHASE2_ERROR; }
找到next_frame
获取proc_info
frame的handler不为空的时候
根据private_2的情况设置不同的action,之后再执行personality操作
查看结果
_URC_INSTALL_CONTEXT会执行resume,transfer control to landing pad
// When CET is enabled, each "call" instruction will push return address to // CET shadow stack, each "ret" instruction will pop current CET shadow stack // top and compare it with target address which program will return. // In exception handing, **some stack frames will be skipped before jumping to // landing pad and we must adjust CET shadow stack accordingly.** // _LIBUNWIND_POP_CET_SSP is used to adjust CET shadow stack pointer and we // directly jump to __libunwind_Registerts_x86/x86_64_jumpto instead of using // a regular function call to avoid pushing to CET shadow stack again. #if !defined(_LIBUNWIND_USE_CET) #define __unw_phase2_resume(cursor, fn) __unw_resume((cursor)) #elif defined(_LIBUNWIND_TARGET_I386) #define __unw_phase2_resume(cursor, fn) \ do { \ _LIBUNWIND_POP_CET_SSP((fn)); \ void *cetRegContext = __libunwind_cet_get_registers((cursor)); \ void *cetJumpAddress = __libunwind_cet_get_jump_target(); \ __asm__ volatile("push %%edi\n\t" \ "sub $4, %%esp\n\t" \ "jmp *%%edx\n\t" :: "D"(cetRegContext), \ "d"(cetJumpAddress)); \ } while (0) #elif defined(_LIBUNWIND_TARGET_X86_64) #define __unw_phase2_resusme(cursor, fn) \ do { \ _LIBUNWIND_POP_CET_SSP((fn)); \ void *cetRegContext = __libunwind_cet_get_registers((cursor)); \ void *cetJumpAddress = __libunwind_cet_get_jump_target(); \ __asm__ volatile("jmpq *%%rdx\n\t" :: "D"(cetRegContext), \ "d"(cetJumpAddress)); \ } while (0) #endif
some stack frames will be skipped before jumping to landing pad and we must adjust CET shadow stack accordingly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/// Resume execution at cursor position (aka longjump). _LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) { _LIBUNWIND_TRACE_API("__unw_resume(cursor=%p)", static_cast<void *>(cursor)); #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) // Inform the ASan runtime that now might be a good time to clean stuff up. __asan_handle_no_return(); #endif AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; co->jumpto(); return UNW_EUNSPEC; }