1éš
3éš
å éšã®C ++äŸå€ïŒããããæ§æ Œ
äŸå€ã®äœæ¥ãæ¢çŽ¢ãããšããé©ãã¹ãæ ãžã®æ ã¯çµããã§ã¯ãããŸãããUnwindã©ã€ãã©ãªãã¹ã¿ãã¯ã®ã¢ã³ã©ãããè¡ãã®ã«åœ¹ç«ã€ãåŒã³åºããã¬ãŒã æ å ±ããšåŒã°ãããã®ããŸã 調æ»ããå¿ èŠããããŸããã¡ãœãããåŠçã§ãããšã©ãŒã決å®ããŸãã ãŸããã»ãšãã©ã®éæ³ã¯ãç§ãã¡ããŸã å®éã«èŠãããšã®ãªãå人çãªæ©èœã§èµ·ããããšãåŠã³ãŸããã 転éãšã©ãŒãšãã£ãããšã©ãŒã«ã€ããŠæ¢ã«ç¥ã£ãŠããããšïŒãŸãã¯ãããæ£ç¢ºã«ã¯ãã¹ããŒããããã®ãã€ã³ã¿ãŒã»ãããããæ¹æ³ãæ¢ã«ç¥ã£ãŠããããšïŒãèŠçŽããŸãããã
- ã³ã³ãã€ã©ã¯throw宣èšãcxa_allocate_exception / xca_throwã®ãã¢ã«å€æããŸã
- __cxa_allocate_exceptionã¯ã¡ã¢ãªå ã§äŸå€ãã¹ããŒããŸã
- __cxa_throwã¯å±éæäœãéå§ããäŸå€ãäœã¬ãã«å±éã©ã€ãã©ãªã«æž¡ãã _Unwind_RaiseExceptionãçºçãããŸã
- ã¹ã¿ãã¯æ¡åŒµã§ã¯ãCFIã䜿çšããŠãçŸåšã¹ã¿ãã¯ã«ããæ©èœãèŠã€ããŸãã
- åé¢æ°ã«ã¯LSDAãããã .gcc_except_tableãšåŒã°ãããã®ãè¿œå ããŸã
- å±éã¯ãçŸåšã®ã¹ã¿ãã¯ãã¬ãŒã ãšLSDAã䜿çšããŠããŒãœãã«é¢æ°ãåŒã³åºããŸããçŸåšã®é¢æ°ã«ãã®ã¿ã€ãã®äŸå€ãã³ãã©ãŒããªãå Žåãã¹ã¿ãã¯ã¯åŒãç¶ãå±éãããŸãã
ãã®ãã¹ãŠããã§ã«åŠãã§ããéããç§ãã¡èªèº«ã®å人çãªæ©èœãæžãæãæ¥ãŸããïŒ ABIã¯ãäŸå€ãã¹ããŒããããšãã«æ¬¡ãå°å·ããããã«äœ¿çšãããŸãã
alloc ex 1 __cxa_throw called no one handled __cxa_throw, terminate!
mycppabi
ã次ã®ãããªãã®ãè¿œå ããŸãããã
void __gxx_personality_v0() { printf("Personality function FTW\n"); }
ç§ã®githubãªããžããªã§çŸåšã®ã³ãŒãã確èªã§ããŸã ã
ãããŠããã¡ãããã¢ããªã±ãŒã·ã§ã³ãèµ·åãããšãå人ã®é¢æ°ãåŒã³åºãããŸãã ç§ãã¡ã¯ãããæ£ããéã§èŠããããŠç§ãã¡ã圌女ã«äœãæãã§ããããç¥ã£ãŠããŸãã æ£ããé¢æ°å®çŸ©ã䜿çšããªãçç±ïŒ
_Unwind_Reason_Code __gxx_personality_v0 ( int version, _Unwind_Action actions, uint64_t exceptionClass, _Unwind_Exception* unwind_exception, _Unwind_Context* context);
ããã
mycppabi.cpp
ã次ã®ããã«ãªããŸãã
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> namespace __cxxabiv1 { struct __class_type_info { virtual void foo() {} } ti; } #define EXCEPTION_BUFF_SIZE 255 char exception_buff[EXCEPTION_BUFF_SIZE]; extern "C" { void* __cxa_allocate_exception(size_t thrown_size) { printf(&"alloc ex %i\n", thrown_size); if (thrown_size > EXCEPTION_BUFF_SIZE) printf("Exception too big"); return &exception_buff; } void __cxa_free_exception(void *thrown_exception); #include <unwind.h> typedef void (*unexpected_handler)(void); typedef void (*terminate_handler)(void); struct __cxa_exception { std::type_info * exceptionType; void (*exceptionDestructor) (void *); unexpected_handler unexpectedHandler; terminate_handler terminateHandler; __cxa_exception * nextException; int handlerCount; int handlerSwitchValue; const char * actionRecord; const char * languageSpecificData; void * catchTemp; void * adjustedPtr; _Unwind_Exception unwindHeader; }; void __cxa_throw(void* thrown_exception, struct type_info *tinfo, void (*dest)(void*)) { printf("__cxa_throw called\n"); __cxa_exception *header = ((__cxa_exception *) thrown_exception - 1); _Unwind_RaiseException(&header->unwindHeader); // __cxa_throw printf("no one handled __cxa_throw, terminate!\n"); exit(0); } void __cxa_begin_catch() { printf("begin FTW\n"); } void __cxa_end_catch() { printf("end FTW\n"); } _Unwind_Reason_Code __gxx_personality_v0 ( int version, _Unwind_Action actions, uint64_t exceptionClass, _Unwind_Exception* unwind_exception, _Unwind_Context* context) { printf("Personality function FTW!\n"); } }
ãã¹ãŠãã³ã³ãã€ã«ããŠãªã³ã¯ããgdbã®å°ãã®å©ããåããŠããã®é¢æ°ã®åãã©ã¡ãŒã¿ãŒã®åæãéå§ããŸãã
Breakpoint 1, __gxx_personality_v0 (version=1, actions=1, exceptionClass=134514792, unwind_exception=0x804a060, context=0xbffff0f0)
- ããŒãžã§ã³ãšexceptionClassã¯ãèšèª/ ABI /ã³ã³ãã€ã©ãããŒã«ãã§ãŒã³/ãã€ãã£ãã§ã¯ãªããã€ãã£ãã®äŸå€ãªã©ã«äŸåããŸãã ããABIã§ã¯ããã«ã€ããŠå¿é ããå¿ èŠã¯ãããŸããããã¹ãŠã®äŸå€ããã£ããããã ãã§ãã
- ã¢ã¯ã·ã§ã³ ïŒ Unwindãå人çãªæ©èœã«äœããã¹ãããäŒããããã«äœ¿çšããŸãïŒããã«ã€ããŠã¯åŸã§èª¬æããŸãïŒ
- unwind_exception ïŒ __cxa_allocate_exceptionã«ãã£ãŠã¹ããŒãããäŸå€ïŒ...ããç¶æ¿ãããŸããå€ãã®ç®è¡æŒç®ããããŸããããã®ãã€ã³ã¿ãŒã«ãããå ã®äŸå€ã«ã¢ã¯ã»ã¹ã§ããŸãïŒ
- context ïŒLSDAãªã©ãçŸåšã®ã¹ã¿ãã¯ãã¬ãŒã ã«é¢ãããã¹ãŠã®æ å ±ãå«ãŸããŸãã ã³ã³ããã¹ãã䜿çšããŠãçŸåšã®ã¹ã¿ãã¯ã§äŸå€ãåŠçã§ãããã®ã決å®ããŸãïŒãã¹ãã©ã¯ã¿ãåŒã³åºããã©ããã決å®ããŸãïŒã
ããŠãç§ãã¡ã¯æ©èœããïŒå°ãªããšããªã³ã¯å¯èœãªïŒå人çãªæ©èœãæã£ãŠããŸãã ããã¯ããŸã圹ã«ç«ããªãã®ã§ãå®éã®æ¯ãèãã§åããŠãäŸå€ãåŠçããããã«ããŸãã
å éšã®C ++äŸå€ïŒ2ãã§ãŒãºãã¹
UnwindãåŒã³åºãããšãã§ããããŒãœãã«é¢æ°ãè¿œå ããŠãåã®ç« ãçµäºããŸããã 圌女ã¯ãäžè¬çã«ããããŸã§äœãããŠããŸããã ABIã¯ãã§ã«äŸå€ãã¹ããŒããŠãã£ããããäœæ¥ã®ååãå®è£ ããŠããŸãããå人çãªæ©èœã¯ãšã©ãŒåŠçã®ããã«ã©ã³ãã£ã³ã°ããããæ£ããéžæããæ¹æ³ãåŠç¿ããå¿ èŠããããŸãã ãã®ç« ã¯ã __ gxx_personality_v0é¢æ°ãåããã©ã¡ãŒã¿ãŒãç解ããå®éã®åäœãè¿œå ããããšããå§ããŸãããã¯ããçŸåšã®ã¹ã¿ãã¯ãããã¯ã¯äŸå€ãåŠçã§ããŸãããåºåããŸãã
ãã§ã«ãããABIã®ããŒãžã§ã³ãŸãã¯äŸå€ã¯ã©ã¹ãåãåºããããšèšã£ãŠããŸããã ã³ã³ããã¹ãã«ã€ããŠãå¿ããŸããããã¹ã¿ãã¯ã®æåã®ãã¬ãŒã ã§ãã¹ãŠã®äŸå€ãåŠçããŸãã ã¡ãœããåŒã³åºãã®ããäžã«ããé¢æ°ã®try / catchãããã¯ã眮ãæããããšãå¿ããªãã§ãã ãããäŸå€ãã¹ããŒãããšããã¹ãŠãå£ããŸãã ãŸããcatchãããã¯ã¯äŸå€ã®ã¿ã€ããç¡èŠããããšãèŠããŠãã䟡å€ããããŸãã äŸå€ãåŠçããããšã
_Unwind_
é¢æ°ã«ç¥ãããã«ã¯ã©ãããã°ããã§ããïŒ
_Unwind_Reason_Code - Unwindé¢æ°ã«éç¥ããå人é¢æ°ã«ããæ»ãå€ïŒãšã©ãŒãåŠçããããã®ã©ã³ãã£ã³ã°ããããèŠã€ãã£ããã©ããã ããŒãœãã«é¢æ°* _URC_HANDLER_FOUNDããæ»ããäœãèµ·ãããèŠãŠã¿ãŸãããã
alloc ex 1 __cxa_throw called Personality function FTW Personality function FTW no one handled __cxa_throw, terminate!
èŠããïŒ ãã³ãã©ãŒãèŠã€ãã£ãããšãã¢ã³ã©ãããŒã«äŒãããããåã³ããŒãœãã«é¢æ°ãåŒã³åºããŸããïŒ ããã§äœãèµ·ãã£ãŠããã®ã§ããïŒ
ã¢ã¯ã·ã§ã³ãã©ã¡ãŒã¿ãŒãèŠããŠããŸããïŒ äŸå€åŠçã¯æ€çŽ¢ãšã¯ãªãŒã³ã¢ããïŒãŸãã¯_UA_SEARCH_PHASEãš_UA_CLEANUP_PHASEïŒã®2段éã§è¡ãããããããããUnwindã圌ãæããã®ãæ£ç¢ºã«äŒããæ¹æ³ã§ãã ãšã©ãŒåŠçã®ã¬ã·ãã«æ»ããŸãããã
- __cxa_throw / __ cxa_allocate_exceptionã¯äŸå€ãçºçããããããã_Unwind_RaiseExceptionãšåŒã°ããäœã¬ãã«ã®ã¹ã¿ãã¯ããã¢ãŒã·ã§ã³ã©ã€ãã©ãªã«æž¡ããŸã
- ã¹ãããŒã¯CFIã䜿çšããŠãçŸåšã¹ã¿ãã¯ã«ããé¢æ°ãèŠã€ããŸãïŒé¢æ°ã®å±éãéå§ããæ¹æ³ãèŠã€ããŸãïŒ
- åé¢æ°ã«ã¯ã.gcc_except_tableãšåŒã°ãããã®ã«è¿œå ãããLSDAããŒãããããŸã
- ã¹ãããŒã¯äŸå€ã®ã©ã³ãã£ã³ã°ããããç¹å®ããããšããŸãã
- ã¢ã³ã¯ã€ã³ããŒã¯ããã©ã¡ãŒã¿ãŒactionïŒ _UA_SEARCH_PHASEããã³ãã©ã¡ãŒã¿ãŒcontexïŒã¹ã¿ãã¯ã®çŸåšã®ãã¬ãŒã ãžã®ãã€ã³ã¿ãŒã䜿çšããŠãããŒãœãã«é¢æ°ãåŒã³åºããŸãã
- ããŒãœãã«é¢æ°ã¯ãLSDAãåæããããšã«ãããçŸåšã®ãã¬ãŒã ã¹ã¿ãã¯ããã®äŸå€ãåŠçã§ãããã©ããããã§ãã¯ããŸãã
- äŸå€ãåŠçã§ããå Žåã_URC_HANDLER_FOUNDãè¿ããŸã
- äŸå€ãåŠçãã人ãããªãå Žåã_URC_CONTINE_UNWINDãè¿ãããã¢ã³ã¯ã€ã³ããŒã¯æ¬¡ã®ã¹ã¿ãã¯ãã¬ãŒã ã§ãã³ãã©ãŒãèŠã€ããããšãç¶ããŸãã
- ã©ã³ãã£ã³ã°ããããèŠã€ãããªãå Žåãããã©ã«ãã®ãã³ãã©ãŒãåŒã³åºãããŸãïŒéåžžã¯std :: terminateïŒã
- ã©ã³ãã£ã³ã°ããããèŠã€ãã£ãå ŽåïŒ
- ã¢ã³ã¯ã€ã³ããŒã¯ãã¹ã¿ãã¯ãå床åŠçãå§ãããã©ã¡ãŒã¿ãŒaction _UA_CLEANUP_PHASEã䜿çšããŠããŒãœãã«é¢æ°ãåŒã³åºããŸãã
- ããŒãœãã«é¢æ°ã¯ãçŸåšã®ã¹ã¿ãã¯ãŠã£ã³ããŠããã®äŸå€ãåŠçã§ãããã©ããããã§ãã¯ããŸãã
- ã§ããªãå Žåã¯ãLSDAã§èª¬æãããŠããã¯ãªãŒã³ã¢ããé¢æ°ãèµ·åãïŒãã¹ãã©ã¯ã¿ãåŒã³åºããŠçŸåšã®ã¹ã¿ãã¯ãã¬ãŒã ã«é 眮ãããŠãããªããžã§ã¯ããã¯ãªã¢ããŸãïŒã次ã®ãã¬ãŒã ã«é²ãããã¢ã³ã¯ã€ã³ãã«æ瀺ããŸãã
- åŠçã§ããå Žåãã¯ãªãŒãã³ã°ã³ãŒãã¯å®è¡ããããå·»ãåãæ©ã«ã©ã³ãã£ã³ã°ãããã§ã®å®è¡ãç¶ç¶ããããšãäŒããã ãã§ãã
次ã®2ã€ã®éèŠãªããšã«æ³šæããŠãã ããã
- äŸå€ããã£ãããã2ãã§ãŒãºã¢ãŒãã§å®è¡ãããšããããšã¯ãå ã®ã¹ã¿ãã¯ãã¬ãŒã¹äŸå€ãšå®å šãªã¹ã¿ãã¯ãã¬ãŒã¹äŸå€ãååŸã§ããããšãæå³ããŸãïŒåé€ãšäžç·ã«1ã€ã®ãã¹ã§ãããã€ããå Žåãã¹ã¿ãã¯ãã¬ãŒã¹ããªããããã®ã³ããŒãä¿åããå¿ èŠããããŸãïŒïŒã
- _UA_CLEANUP_PHASEãèµ·åãããã¬ãŒã ãäŸå€ãåŠçã§ããããšãããã£ãŠããå Žåã§ããåãã¬ãŒã ãç¹°ãè¿ãåŒã³åºãããšãéåžžã«éèŠã§ããããŒãœãã«é¢æ°ã«ã¯ããã®ã¹ã³ãŒãã§äœæããããªããžã§ã¯ãã®ãã¹ãŠã®ãã¹ãã©ã¯ã¿ãåŒã³åºãæ©èœããããŸãã ãããRAIIïŒResource Acquisition Is InitializationïŒäŸå€ãå®å šãªã€ãã£ãªã ã«ãããã®ã§ãã
ããã§ããã³ãã©ãŒæ€çŽ¢ãã§ãŒãºãã©ã®ããã«æ©èœããããç解ããå人çšé¢æ°ãåŒãç¶ãå®è£ ã§ããŸãã
å éšã®C ++äŸå€ïŒæåã®äŸå€ããã£ãããã
åã®ç« ã¯ãã¹ãããŒã«åå¿ããå人çãªæ©èœãæããããšã§çµäºããŸããã __gxx_personality_v9ã®å®éã®åäœãè¿œå ãããšããæ¥ãŸããã2ã€ã®ãã§ãŒãºã§ããã»ãŒãžãåŠçããããã«æããŸãã
ç§ãã¡ã®å人çãªæ©èœã¯æ¬¡ã®åœ¢åŒããšããŸã
_Unwind_Reason_Code __gxx_personality_v0 ( int version, _Unwind_Action actions, uint64_t exceptionClass, _Unwind_Exception* unwind_exception, _Unwind_Context* context) { if (actions &_UA_SEARCH_PHASE) { printf("Personality function, lookup phase\n"); return _URC_HANDLER_FOUND; } else if (actions & _UA_CLEANUP_PHASE) { printf("Personality function, cleanup\n"); return _URC_INSTALL_CONTEXT; } else { printf("Personality function, error\n"); return _URC_FATAL_PHASE1_ERROR; } }
æãåºãããŠãã ããïŒãœãŒã¹ã³ãŒãã¯githubãªããžããªã«ãããŸã ã
å®è¡ããŠäœãèµ·ãããèŠãŠã¿ãŸãããïŒ
alloc ex 1 __cxa_throw called Personality function, lookup phase Personality function, cleanup try_but_dont_catch handled the exception catchit handled the exception
ããã¯æ©èœããŸãããäœããããããã§ãïŒcatch / tryãããã¯å ã®ãã³ãã©ãŒã¯æ±ºããŠèµ·åããŸããïŒ ããã¯ãããŒãœãã«é¢æ°ããã¹ã¯ã©ã³ãã©ãŒãã«ãã³ã³ããã¹ããèšå®ãããïŒã€ãŸããå®è¡ãç¶ç¶ããïŒããã«æ瀺ããããã©ã®ã³ã³ããã¹ããæ瀺ããªãããšããäºå®ã«ãããã®ã§ãã ãã®å Žåãã©ã³ãã£ã³ã°ããããããã¯ã®åŸãå®è¡ãç¶ç¶ããå¯èœæ§ããããŸãããããã¯æªå®çŸ©ã®åäœã§ãã 次ã«ã .gcc_except_table ïŒæ§åã®LSDAïŒã§å©çšå¯èœãªæ å ±ã䜿çšããŠãã³ãŒãïŒã©ã³ãã£ã³ã°ãããïŒã®å®è¡ãç¶ç¶ãããã€ã³ããæå®ããæ¹æ³ã確èªããŸãã
å éšã® C ++äŸå€ïŒçŸåšã®ãã¬ãŒã æ å ±ãã¢ã³ã¯ã€ã³ã
ããABIã«äŸå€ãã¹ããŒã§ããããã«ããŠãããŸããããçŸåšã¯äŸå€ã®ãã£ããã«åãçµãã§ããŸãã äŸå€ãæ€åºããŠãªãã¹ã³ã§ããããŒãœãã«é¢æ°ãå®è£ ããŸãããããŸã çµäºããŠããŸãããã¹ãããŒã«åæ¢ãã¹ãã¿ã€ãã³ã°ãéç¥ã§ããŠãããšã©ãŒãã³ãã©ãŒãããã¯å ã§ã³ãŒããå®è¡ã§ããŸããã ããã¯ãç§ãã¡ãå§ããå Žæããããã§ã«åªããŠããŸãããé©åãªABIãšã©ãŒåŠçã·ã¹ãã ãäœæããæ¹æ³ã¯ãŸã ãŸã ãããŸãã ã³ãŒããæ¹åã§ããŸããïŒ
catchãããã¯å ã§ã³ãŒããå®è¡ãç¶ããããšãã§ããããã«ãã¹ãããŒã«ã©ã³ãã£ã³ã°ãããã®å Žæãã©ã®ããã«äŒããããšãã§ããŸããïŒ ABIä»æ§ã«æ»ããšã圹ç«ã€ã³ã³ããã¹ã管çæ©èœãããã€ããããŸãã
- _Unwind_GetLanguageSpecificDataã䜿çšããŠãçŸåšã®ãã¬ãŒã ã®LSDAãååŸããŸãã ãã®é¢æ°ã䜿çšããŠã©ã³ãã£ã³ã°ããããšãã¹ãã©ã¯ã¿ãæ€çŽ¢ããæ¹æ³ãåŠç¿ããå¿ èŠããããŸãã
- _Unwind_GetRegionStartã¯ãçŸåšã®é¢æ°ã®å
é ã«ããåœä»€ãžã®ãã€ã³ã¿ãŒïŒçŸåšã®ãã¬ãŒã ã«å¯Ÿå¿ããé¢æ°ãžã®ãã€ã³ã¿ãŒïŒãååŸããŸã
- ã¹ã¿ãã¯ã®çŸåšã®ãã¬ãŒã å ã®åœä»€ãã€ã³ã¿ãŒïŒåœä»€ãã€ã³ã¿ãŒïŒãååŸãã_Unwind_GetIP ïŒæ¬¡ã®ãã¬ãŒã ã®é¢æ°åŒã³åºããå®äºããå Žæãžã®ãã€ã³ã¿ãŒã以äžã®äŸã§æ確ã«ãªããŸãïŒã
ãããã®æ©èœãgdbã§èŠãŠã¿ãŸãããã ç§ã®è»ã§ïŒ
Breakpoint 1, __gxx_personality_v0 (version=1, actions=6, exceptionClass=134515400, unwind_exception=0x804a060, context=0xbffff0f0) at mycppabi.cpp:77 84 const uint8_t* lsda = (const uint8_t*)_Unwind_GetLanguageSpecificData(context); 85 uintptr_t ip = _Unwind_GetIP(context) - 1; 86 uintptr_t funcStart = _Unwind_GetRegionStart(context); 87 uintptr_t ipOffset = ip - funcStart;
ãããã®å€æ°ã調ã¹ããšã _Unwind_GetRegionStartãçŸåšã®ã¹ã¿ãã¯ãã¬ãŒã ïŒtry_but_dont_catchïŒãæãã _Unwind_GetIpã次ã®ãã¬ãŒã ãåŒã³åºãããäœçœ®ã®IPã§ããããšãããããŸãã _Unwind_GetRegionStartã¯ãäŸå€ãæåã«ã¹ããŒãããå Žæã瀺ããŸãã説æããã®ã¯å°ãé£ããã®ã§ãåŸã§ãããæ®ããŸãããã ããã«ã¯ãŸã LSDAãã€ã³ã¿ãŒã衚瀺ãããŠããŸãããã_Unwind_GetLanguageSpecificDataãé¢æ°ã®çµäºåŸã®è¡ãçŽæ¥åç §ããŠããéããé¢æ°ã³ãŒãã®ããåŸãã«ãããšæ³å®ã§ããŸãã
_Unwind_GetIP = (void *) 0x804861d _Unwind_GetRegionStart = (void *) 0x8048612 _Unwind_GetLanguageSpecificData = (void *) 0x8048e3c function pointer to try_but_dont_catch = 0x8048612 &<try_but_dont_catch()> (gdb) disassemble /m try_but_dont_catch Dump of assembler code for function try_but_dont_catch(): 10 void try_but_dont_catch() { [...] 11 try { 12 raise(); 0x08048619 <+7>: call 0x80485e8 <raise()> 13 } catch(Fake_Exception&) { 0x08048651 <+63>: call 0x804874a <__cxa_begin_catch()> 0x08048665 <+83>: call 0x804875e <__cxa_end_catch()> 0x0804866a <+88>: jmp 0x804861e <try_but_dont_catch()+12> 14 printf("Caught a Fake_Exception!\n"); 0x08048659 <+71>: movl $0x8048971,(%esp) 0x08048660 <+78>: call 0x80484c0 <puts@plt> 15 } 16 17 printf("try_but_dont_catch handled the exception\n"); 0x0804861e <+12>;: movl $0x8048948,(%esp) 0x08048625 <+19>: call 0x80484c0 <puts@plt> 18 } 0x0804862a <+24>: add $0x24,%esp
Unwindã䜿çšãããšãçŸåšã®ã¹ã¿ãã¯ãã¬ãŒã ã«é¢ããååãªæ å ±ãååŸããŠãäŸå€ãåŠçã§ãããã©ãããšããã®åŠçæ¹æ³ãå€æã§ããŸãã ã©ã³ãã£ã³ã°ãããã決å®ã§ãããã©ãããå€æããåã«å¿ èŠãªæåŸã®æé ã¯ãé¢æ°ã®æåŸã«CFIæ å ±ã解éããããšã§ãã ããã¯DWARFä»æ§ã®äžéšã§ããããã®å®è£ ã¯ããè€éã§ãã ABIãšåæ§ã«ãå¿ èŠãªæå°å€ã䜿çšããŸãã
å éšã®C ++äŸå€ïŒCFIããŒãã«ã®èªã¿åã
äŸå€ãæ£ããåŠçããã«ã¯ãABIã«å®è£ ããå人æ©èœãLSDAãèªã¿åã£ãŠãã©ã®ãã¬ãŒã ïŒã€ãŸããã©ã®æ©èœïŒãäŸå€ãšã©ã®äŸå€ãåŠçã§ããããããã³ã©ã³ãã£ã³ã°ãããïŒãã£ãã-ãããã¯ïŒãèŠã€ãããŸããã LSDAããŒãã«ã¯CFI圢åŒã§å®çŸ©ãããŠããããã®ç« ã§ã¯ãã®èªã¿æ¹ãåŠã³ãŸãã
CFIããŒã¿ã¯éåžžã«ç°¡åã«èªã¿åãããšãã§ããŸãããèæ ®ãã¹ãèœãšãç©Žãããã€ããããŸãã å®éã2ã€ïŒ
- .gcc_except_tableã«é¢ããããã¥ã¡ã³ãã¯ã»ãšãã©ãããŸããïŒå®éãæ°æåããèŠã€ãããŸããã§ããïŒããããã£ãŠãå€ãã®ãœãŒã¹ã³ãŒãã調ã¹ãéã¢ã»ã³ãã«ãããã³ãŒããç解ããå¿ èŠããããŸãã
- 圢åŒèªäœã¯å°çã®ããã«è€éã§ã¯ãªããšããäºå®ã«ãããããããLEBïŒãªãã«ãšã³ãã£ã¢ã³ããŒã¹ïŒã䜿çšããŠããããããã®ããŒãã«ã®èªã¿åãã¯ç¹ã«ç°¡åã§ã¯ãããŸããã
ç§ã®ç¥ãéããã»ãšãã©ã®DWARFããŒã¿ã¯LEBã§ãšã³ã³ãŒããããŸããããã¯ãããã°ã©ããæ··ä¹±ãããä»»æã®é·ãã®intããšã³ã³ãŒãããããã®ã³ãŒãã®ã¹ããŒã¹ãåæžããçŽ æŽãããã¢ã€ãã¢ã§ãã 幞ããªããšã«ãããã§å°ãã«ãŠã³ãã§ããŸããåºæ¬çã«ãLEBã§ãšã³ã³ãŒããããæ°å€ã¯ãåçŽãªuint8_tã§èªã¿åãããŸããããã¯ã倧ããªäŸå€ããŒãã«ãªã©ãåŠçããªãããã§ãã
ãã€ãã®ããã«ããã®ç« ã®çŸåšã®ããŒãžã§ã³ã®ã³ãŒãã¯ãªããžããªã«ãããŸã ã
å解ããçŽæ¥CFIã®åæãéå§ããå人çãªæ©èœã§ãã®ããŒã¿ãèªã¿åãããã«äœããæ§ç¯ã§ãããã©ããã確èªããŸãããã ã¿ã°ã®ååããã人éã«åªãããã®ã«å€æŽããŸããã LSDAã«ã¯3ã€ã®ã»ã¯ã·ã§ã³ããããŸãã以äžã«å®çŸ©ããŠã¿ãŠãã ããã
.local_frame_entry: .globl __gxx_personality_v0 .section .gcc_except_table,"a",@progbits .align 4
ãã¹ãŠãéåžžã«åçŽã§ããã°ããŒãã«ãšããŠ__gxx_personality_v0ã䜿çšãããšããèŠåºãã ãã§ããªã³ã«ãŒã«.gcc_except_tableã»ã¯ã·ã§ã³ãå®çŸ©ããããšãç¥ãããŸãã
次ã«é²ã¿ãŸãïŒ
.local_lsda_1: # . .byte 0xff # landing pads; 0, func's ptr # (_Unwind_GetRegionStart) .byte 0 # LSDA: LLSDATT1 LLSDATTD1 # LSDA, .uleb128 .local_lsda_end - .local_lsda_call_site_table_header
ãã§ã«å€ãã®æ å ±ããããŸãã ãããã®ã©ãã«ã¯éåžžã«ãããŸãã§ããããã¿ãŒã³ã«åŸããŸãã LSDAã¯èšèªåºæã®ããŒã¿ãŸãŒã³ãæå³ããå é ã®Lã¯ãããŒã«ã«ããæå³ããããããããã¯ããŒã«ã«ã§ãïŒç¿»èš³ãããã¢ãžã¥ãŒã«ã.oãã¡ã€ã«ã®å ŽåïŒã ãŸãŒã³ãã³ããŒã¯ã³ããŒã¿ã ä»ã®ããŒã¯ãåããã¿ãŒã³ã«åŸããŸããããããã®èª¬æã¯åŒãåããŸããã§ããã ãããŠãäžè¬çã«ããããã¯ç§ãã¡ã«ãšã£ãŠå¿ èŠã§ã¯ãããŸããã
.local_lsda_call_site_table_header: # Encoding of items in the landing pad table. Again, we don't care. .byte 0x1. # The length of the call site table (ie the landing pads) .uleb128 .local_lsda_call_site_table_end - .local_lsda_call_site_table
å¥ã®éå±ãªèŠåºããããã«é²ãã§ã¿ãŸãããïŒ
.local_lsda_call_site_table: .uleb128 .LEHB0-.LFB1 .uleb128 .LEHE0-.LEHB0 .uleb128 .L8-.LFB1 .uleb128 0x1 .uleb128 .LEHB1-.LFB1 .uleb128 .LEHE1-.LEHB1 .uleb128 0 .uleb128 0 .uleb128 .LEHB2-.LFB1 .uleb128 .LEHE2-.LEHB2 .uleb128 .L9-.LFB1 .uleb128 0 .local_lsda_call_site_table_end:
ããã¯éåžžã«èå³æ·±ããã®ã§ããããã§ã¯ãèªåã®ç®ã§åŒã³åºãããŒãã«ã確èªããŸãã ã©ããããããããããã®ãã¹ãŠã®ãšã³ããªã§ãçéžå°ãèŠã€ããå¿ èŠããããŸãã ã€ã³ã¿ãŒãããäžã®ã©ã³ãã ãªããŒãžã«åŸã£ãŠãåãšã³ããªã®åœ¢åŒã¯æ§é ã«å¯Ÿå¿ããå¿ èŠããããŸãã
struct lsda_call_site_entry { // IP size_t cs_start; // IP size_t cs_len; // Landing pad size_t cs_lp; // size_t cs_action; };
ãŸããç§ãã¡ã¯æ£ããéãé²ãã§ããããã«èŠããŸãããã©ã³ã®ã³ã°ãããã1ã€ã ãç¹å®ãããšãããªã3ã€ã®ãšã³ããªãã€ã³ããããã®ãââãŸã ããããŸããã ãããã«ãããå°ãã«ãŠã³ãã§ããŸããéã¢ã»ã³ãã«ãããã³ãŒããèŠããšããã¹ãŠã®CFIå€ã128æªæºã§ãããšå€æã§ããŸããã€ãŸããLEBãšã³ã³ãŒãã£ã³ã°ã¯ucharãšããŠèªã¿åãããšãã§ããŸãã ããã«ãããCFIã®èªã¿åãã³ãŒããã¯ããã«ç°¡åã«ãªããå人æ©èœã§ããã«äœ¿çšããæ¹æ³ãããããŸããã
å éšã®C ++äŸå€ïŒçªç¶ãC ++ã®ãªãã¬ã¯ã·ã§ã³
ãã§ã«è¡ã£ãããšãæãåºããŠãã ããïŒãšã©ãŒãã¹ããŒããæ¹æ³ãåŠã³ããšã©ãŒãæ€åºããŠåŠçã§ããããŒãœãã«é¢æ°__gxx_personality_v0ãäœæããã¹ã¿ãã«ãŒã«åæ¢ããã¿ã€ãã³ã°ãäŒããŸãããããã§ãå¿ èŠãªãã£ãããããã¯ã®æ±ºå®æ¹æ³ãããããŸããã ãŸããLSDAãèªãããšãåŠã³ãŸããããä»ã§ã¯ããããã¹ãŠãçµã¿åãããããšãã§ããŸãïŒ
ãã®ãããªããšãããŠãæ£ããè»éã«ä¹ã£ãŠãããã©ããã確èªããŸãããïŒãã®ã³ãŒãã¯uint8ã§ããåäœããããããã移æ€æ§ããªãããšã«æ³šæããŠãã ããïŒã
struct LSDA_Header { uint8_t lsda_start_encoding; uint8_t lsda_type_encoding; uint8_t lsda_call_site_table_length; }; struct LSDA_Call_Site_Header { uint8_t encoding; uint8_t length; }; struct LSDA_Call_Site { LSDA_Call_Site(const uint8_t *ptr) { cs_start = ptr[0]; cs_len = ptr[1]; cs_lp = ptr[2]; cs_action = ptr[3]; } uint8_t cs_start; uint8_t cs_len; uint8_t cs_lp; uint8_t cs_action; }; _Unwind_Reason_Code __gxx_personality_v0 ( int version, _Unwind_Action actions, uint64_t exceptionClass, _Unwind_Exception* unwind_exception, _Unwind_Context* context) { if (actions & _UA_SEARCH_PHASE) { printf("Personality function, lookup phase\n"); return _URC_HANDLER_FOUND; } else if (actions & _UA_CLEANUP_PHASE) { printf("Personality function, cleanup\n"); const uint8_t* lsda = (const uint8_t*) _Unwind_GetLanguageSpecificData(context); LSDA_Header *header = (LSDA_Header*)(lsda); LSDA_Call_Site_Header *cs_header = (LSDA_Call_Site_Header*) (lsda + sizeof(LSDA_Header)); size_t cs_in_table = cs_header->length / sizeof(LSDA_Call_Site); // cs_table_base uint8, // const uint8_t *cs_table_base = lsda + sizeof(LSDA_Header) + sizeof(LSDA_Call_Site_Header); // call site for (size_t i=0; i < cs_in_table; ++i) { const uint8_t *offset = &cs_table_base[i * sizeof(LSDA_Call_Site)]; LSDA_Call_Site cs(offset); printf("Found a CS:\n"); printf("\tcs_start: %i\n", cs.cs_start); printf("\tcs_len: %i\n", cs.cs_len); printf("\tcs_lp: %i\n", cs.cs_lp); printf("\tcs_action: %i\n", cs.cs_action); } uintptr_t ip = _Unwind_GetIP(context); uintptr_t funcStart = _Unwind_GetRegionStart(context); uintptr_t ipOffset = ip - funcStart; return _URC_INSTALL_CONTEXT; } else { printf("Personality function, error\n"); return _URC_FATAL_PHASE1_ERROR; } } }
å®éã®ã³ãŒã
ã芧ã®ãšããïŒãã®ã³ãŒããå®è¡ããå ŽåïŒãåŒã³åºãããŒãã«ã®ãã¹ãŠã®ãã€ã³ãã¯çžå¯Ÿçã§ãã äœã®èŠªRelativeïŒ ãã¡ãããæ©èœãéå§ããŸãã ã€ãŸããç¹å®ã®ã©ã³ãã£ã³ã°ãããã®EIPïŒåœä»€ãã€ã³ã¿ãŒïŒãååŸããå Žåãå¿ èŠãªäœæ¥ã¯_Unwind_GetRegionStart + LSDA_Call_Site.cs_IpïŒãè¿œå ããã ãã§ãã
æåŸã«ãåé¡ã解決ã§ããããã«ãªããŸãããæ£ããã©ã³ãã£ã³ã°ããããå®è¡ããããã«å人æ©èœãå€æŽããŸãããã 次ã«ãå¥ã®Unwindé¢æ°ã䜿çšããŠãå®è¡ãç¶ç¶ããå Žæã瀺ãå¿ èŠããããŸãïŒ _Unwind_SetIP ã æåã®çéžå°ãèµ·åããããã«ãå人æ©èœãå床å€æŽããŸãã
const uint8_t *cs_table_base = lsda + sizeof(LSDA_Header) + sizeof(LSDA_Call_Site_Header); for (size_t i=0; i < cs_in_table; ++i) { const uint8_t *offset = &cs_table_base[i * sizeof(LSDA_Call_Site)]; LSDA_Call_Site cs(offset); if (cs.cs_lp) { uintptr_t func_start = _Unwind_GetRegionStart(context); _Unwind_SetIP(context, func_start + cs.cs_lp); break; } } return _URC_INSTALL_CONTEXT;
ãã®ã³ãŒããå®è¡ããŠã¿ãŠãçŸããæ°žä¹ ã«ãŒãã芳å¯ããŠãã ããã äœãééã£ãŠãããšæããŸããïŒ çãã¯æ¬¡ã®ç« ã«ãããŸãïŒ
å éšã®C ++äŸå€ïŒã©ã³ãã£ã³ã°ãããã®ã³ã³ããã¹ãã®èšå®
æåŸã®ç« ã§ãç§ãã¡ã¯ã€ãã«ã»ãšãã©æ©èœããå人çãªæ©èœãæžããŸããã 䜿çšå¯èœãªã©ã³ãã£ã³ã°ãããã§åã¹ã¿ãã¯ãã¬ãŒã ãå®çŸ©ããå®è¡ãããå 容ãUnwindã«æ£ç¢ºã«äŒããããšãã§ããŸãã ãã ããå°ããªåé¡ãçºçããŸãããUnwindã³ã³ããã¹ããèšå®ããŠæ£ããã©ã³ãã£ã³ã°ãããã§å®è¡ãç¶ç¶ããã«ã¯ãçŸåšã®äŸå€ãã¬ãžã¹ã¿ã«èšå®ããå¿ èŠããããŸãã ããã¯åºæ¬çã«ãã©ã³ãã£ã³ã°ãããã¯ã©ã®äŸå€ãåŠçããå¿ èŠãããããç¥ããããªããããããããåŠçã§ããŸããããšã®ã¿è¡šç€ºããŸãã ã¢ã³ã¯ã€ã³ãã¯ã次ã®ã©ã³ãã£ã³ã°ããããè©ŠããŠãã ããããšèšããŸãããABIã¯éåžžã«åçŽãªã®ã§ãå¥ã®ã©ã³ãã£ã³ã°ããããèŠã€ããæ¹æ³ããèãããããåããã®ãæ»ãããããšããŸããäœåãããã°ããã®éãæãäžèªç¶ãªäŸãåºãŠããããã§ãïŒtrueïŒïŒ
ã©ã³ãã£ã³ã°ãããã®ã³ã³ããã¹ããä¿®æ£ããABIãå°ãæ¹åããŸãããã
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> namespace __cxxabiv1 { struct __class_type_info { virtual void foo() {} } ti; } #define EXCEPTION_BUFF_SIZE 255 char exception_buff[EXCEPTION_BUFF_SIZE]; extern "C" { void* __cxa_allocate_exception(size_t thrown_size) { printf("alloc ex %i\n", thrown_size); if (thrown_size > EXCEPTION_BUFF_SIZE) printf("Exception too big"); return &exception_buff; } void __cxa_free_exception(void *thrown_exception); #include <unwind.h> typedef void (*unexpected_handler)(void); typedef void (*terminate_handler)(void); struct __cxa_exception { std::type_info * exceptionType; void (*exceptionDestructor) (void *); unexpected_handler unexpectedHandler; terminate_handler terminateHandler; __cxa_exception * nextException; int handlerCount; int handlerSwitchValue; const char * actionRecord; const char * languageSpecificData; void * catchTemp; void * adjustedPtr; _Unwind_Exception unwindHeader; }; void __cxa_throw(void* thrown_exception, struct type_info *tinfo, void (*dest)(void*)) { printf("__cxa_throw called\n"); __cxa_exception *header = ((__cxa_exception *) thrown_exception - 1); _Unwind_RaiseException(&header->unwindHeader); // __cxa_throw never returns printf("no one handled __cxa_throw, terminate!\n"); exit(0); } void __cxa_begin_catch() { printf("begin FTW\n"); } void __cxa_end_catch() { printf("end FTW\n"); } /**********************************************/ /** * The LSDA is a read only place in memory; we'll create a typedef for * this to avoid a const mess later on; LSDA_ptr refers to readonly and * &LSDA_ptr will be a non-const pointer to a const place in memory */ typedef const uint8_t* LSDA_ptr; struct LSDA_Header { /** * Read the LSDA table into a struct; advances the lsda pointer * as many bytes as read */ LSDA_Header(LSDA_ptr *lsda) { LSDA_ptr read_ptr = *lsda; // Copy the LSDA fields start_encoding = read_ptr[0]; type_encoding = read_ptr[1]; ttype = read_ptr[2]; // Advance the lsda pointer *lsda = read_ptr + sizeof(LSDA_Header); } uint8_t start_encoding; uint8_t type_encoding; uint8_t ttype; }; struct LSDA_CS_Header { // Same as other LSDA constructors LSDA_CS_Header(LSDA_ptr *lsda) { LSDA_ptr read_ptr = *lsda; encoding = read_ptr[0]; length = read_ptr[1]; *lsda = read_ptr + sizeof(LSDA_CS_Header); } uint8_t encoding; uint8_t length; }; struct LSDA_CS { // Same as other LSDA constructors LSDA_CS(LSDA_ptr *lsda) { LSDA_ptr read_ptr = *lsda; start = read_ptr[0]; len = read_ptr[1]; lp = read_ptr[2]; action = read_ptr[3]; *lsda = read_ptr + sizeof(LSDA_CS); } // Note start, len and lp would be void*'s, but they are actually relative // addresses: start and lp are relative to the start of the function, len // is relative to start // Offset into function from which we could handle a throw uint8_t start; // Length of the block that might throw uint8_t len; // Landing pad uint8_t lp; // Offset into action table + 1 (0 means no action) // Used to run destructors uint8_t action; }; /**********************************************/ _Unwind_Reason_Code __gxx_personality_v0 ( int version, _Unwind_Action actions, uint64_t exceptionClass, _Unwind_Exception* unwind_exception, _Unwind_Context* context) { if (actions & _UA_SEARCH_PHASE) { printf("Personality function, lookup phase\n"); return _URC_HANDLER_FOUND; } else if (actions & _UA_CLEANUP_PHASE) { printf("Personality function, cleanup\n"); // Pointer to the beginning of the raw LSDA LSDA_ptr lsda = (uint8_t*)_Unwind_GetLanguageSpecificData(context); // Read LSDA headerfor the LSDA LSDA_Header header(&lsda); // Read the LSDA CS header LSDA_CS_Header cs_header(&lsda); // Calculate where the end of the LSDA CS table is const LSDA_ptr lsda_cs_table_end = lsda + cs_header.length; // Loop through each entry in the CS table while (lsda < lsda_cs_table_end) { LSDA_CS cs(&lsda); if (cs.lp) { int r0 = __builtin_eh_return_data_regno(0); int r1 = __builtin_eh_return_data_regno(1); _Unwind_SetGR(context, r0, (uintptr_t)(unwind_exception)); // Note the following code hardcodes the exception type; // we'll fix that later on _Unwind_SetGR(context, r1, (uintptr_t)(1)); uintptr_t func_start = _Unwind_GetRegionStart(context); _Unwind_SetIP(context, func_start + cs.lp); break; } } return _URC_INSTALL_CONTEXT; } else { printf("Personality function, error\n"); return _URC_FATAL_PHASE1_ERROR; } } }
ã³ãŒãã®çŸåšã®ããŒãžã§ã³ãšLSDAã®ãã詳现ãªèª¬æã
æåŸã«åäœããŸãïŒæ¬¡ã®ãããªãã®ãååŸããå¿ èŠããããŸãã
./app alloc ex 1 __cxa_throw called Personality function, lookup phase Personality function, cleanup begin FTW Caught a Fake_Exception! end FTW try_but_dont_catch handled the exception catchit handled the exception
ãã¡ãããUnwindãå°ãtrickããŸãããæåã®catchãããã¯ã§ããã¹ãŠã®äŸå€ãé£ç¶ããŠåŠçããããã«åœŒã«äŒããŸãããããã«ãããcatchïŒExceptionïŒïŒãcatchïŒ...ïŒã«å€ããããã¬ãŒã å ã®æåã®é¢æ°ã«catchãããã¯ããªãå Žåããã¹ãŠã®å°çãç Žå£ãããŸãããã ããéåžžã«åçŽãªABIãäœæããããã®æåã®ã¹ããããå®è¡ããŸããïŒ
ã³ãŒããæ¹åããæ£ãããã¬ãŒã ã§äŸå€ãæ£ããåŠçããããšãã§ããŸããïŒãã¡ããïŒ
ãã³ãããã®äžã®C ++äŸå€ïŒããã€ãã®ã©ã³ãã£ã³ã°ããããšã°ã«ã®æã
å°é£ãªæ¹æ³ã®åŸãã€ãã«lbstdc ++ã®å©ããåããã«ãšã©ãŒãåŠçã§ããå®çšçãªå人çšé¢æ°ãäœæããŸããããã¹ãŠã®ãšã©ãŒãã©ã³ãã ã«åŠçããŸãããæ©èœããŸãïŒæ£ããäŸå€åŠçã¯ãŸã çããããŠããªã倧ããªè³ªåã§ãããLSDAã«æ»ããšã次ã®ãããªããšãããããŸãã
.local_lsda_call_site_table: .uleb128 .LEHB0-.LFB1 .uleb128 .LEHE0-.LEHB0 .uleb128 .L8-.LFB1 .uleb128 0x1 .uleb128 .LEHB1-.LFB1 .uleb128 .LEHE1-.LEHB1 .uleb128 0 .uleb128 0 .uleb128 .LEHB2-.LFB1 .uleb128 .LEHE2-.LEHB2 .uleb128 .L9-.LFB1 .uleb128 0 .local_lsda_call_site_table_end:
1ã€ã®try / catchãããã¯ãèšè¿°ããå Žåã§ãã3ã€ã®ã©ã³ãã£ã³ã°ãããããããŸãã ããã§äœãèµ·ãã£ãŠããŸããïŒ
åã®ç« ã泚ææ·±ãèŠããšãLSDA_CSæ§é äœã®å®çŸ©ã«ããã€ãã®ã³ã¡ã³ããè¿œå ãããŠããããšãããããŸãã
struct LSDA_CS { // len lp void*'s, // : start and lp , len // // , uint8_t start; // uint8_t len; // Landing pad uint8_t lp; // action table + 1 (0 " ") // uint8_t action; };
ããã«ã¯å€ãã®èå³æ·±ããã®ããããŸãããæåã«ãã®äŸã®ãã£ãŒã«ãããšã«æ§é ãèŠãŠãã ããã
void foo() { L0: try { do_something(); L1: } catch (const Exception1& ex) { ... } catch (const Exception2& ex) { ... } catch (const ExceptionN& ex) { ... } catch (...) { } L2: }
- IpïŒçéžããããå§ãŸãæ©èœã®éå§ããã®ãªãã»ããããã®äŸã®å€ã¯L1-addr_ofïŒfooïŒã§ããå¿ èŠããããŸã
- action: . . , .
- start: try- . L0 â addr_of(foo)
- len: try-. L1-L0
é¢å¿ã®ãããã£ãŒã«ãã¯startãšlenã§ãïŒtry / catchãããã¯ãå€ãé¢æ°ã§ã¯ãçŸåšã®ãã¬ãŒã ã®åœä»€ãã€ã³ã¿ãŒïŒIPïŒãstartãšstart + lenã®éã«ãããã©ããã確èªããããšã§ãäŸå€ãåŠçããå¿ èŠããããã©ãããå€æã§ããŸãã
ããã«ãããè€æ°ã®try / catchãããã¯ãæã€é¢æ°ãè€æ°ã®äŸå€ãåŠçã§ãããšããç¥è©±ãç Žå£ãããŸããã1ã€ã®ã©ã³ãã£ã³ã°ãããã«å¯ŸããŠ3ã€ã®ãªããžã§ã¯ããçæãããã®ã¯ãªãã§ããïŒä»ã®ãªããžã§ã¯ãã¯ã転éå¯èœãªã¯ãªãŒãã³ã°ã¢ã¯ã·ã§ã³ãŸãã¯ã©ã³ãã£ã³ã°ãããã®å ŽæãšããŠé 眮ãããŸãã
ç¶ç¶