zoukankan      html  css  js  c++  java
  • 研究下vc++的abort函数

    最近在调试几个问题时,发现跟abort函数有关,以前只是简单使用,现在却发现不简单,就多留意了下。

    简介

    abort中止当前进程并返回错误代码。异常终止一个进程。中止当前进程,返回一个错误代码。错误代码的缺省值是3。

    代码

    /***
    *abort.c - abort a program by raising SIGABRT
    *
    *       Copyright (c) Microsoft Corporation. All rights reserved.
    *
    *Purpose:
    *       defines abort() - print a message and raise SIGABRT.
    *
    *******************************************************************************/
    
    #include <cruntime.h>
    #include <stdlib.h>
    #include <internal.h>
    #include <awint.h>
    #include <rterr.h>
    #include <signal.h>
    #include <oscalls.h>
    #include <mtdll.h>
    #include <dbgint.h>
    
    #ifdef _DEBUG
    #define _INIT_ABORT_BEHAVIOR _WRITE_ABORT_MSG
    #else  /* _DEBUG */
    #define _INIT_ABORT_BEHAVIOR _CALL_REPORTFAULT
    #endif  /* _DEBUG */
    
    unsigned int __abort_behavior = _INIT_ABORT_BEHAVIOR;
    
    /***
    *void abort() - abort the current program by raising SIGABRT
    *
    *Purpose:
    *   print out an abort message and raise the SIGABRT signal.  If the user
    *   hasn't defined an abort handler routine, terminate the program
    *   with exit status of 3 without cleaning up.
    *
    *   Multi-thread version does not raise SIGABRT -- this isn't supported
    *   under multi-thread.
    *
    *Entry:
    *   None.
    *
    *Exit:
    *   Does not return.
    *
    *Uses:
    *
    *Exceptions:
    *
    *******************************************************************************/
    
    void __cdecl abort (
            void
            )
    {
        _PHNDLR sigabrt_act = SIG_DFL;
    
    #ifdef _DEBUG
        if (__abort_behavior & _WRITE_ABORT_MSG)
        {
            /* write the abort message */
            _NMSG_WRITE(_RT_ABORT);
        }
    #endif  /* _DEBUG */
    
    
        /* Check if the user installed a handler for SIGABRT.
         * We need to read the user handler atomically in the case
         * another thread is aborting while we change the signal
         * handler.
         */
        sigabrt_act = __get_sigabrt();
        if (sigabrt_act != SIG_DFL)
        {
            raise(SIGABRT);
        }
    
        /* If there is no user handler for SIGABRT or if the user
         * handler returns, then exit from the program anyway
         */
    
        if (__abort_behavior & _CALL_REPORTFAULT)
        {
    #if defined (_M_ARM) || defined (_CRT_APP)
            __fastfail(FAST_FAIL_FATAL_APP_EXIT);
    #else  /* defined (_M_ARM) || defined (_CRT_APP) */
            if (IsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE))
                __fastfail(FAST_FAIL_FATAL_APP_EXIT);
    
            _call_reportfault(_CRT_DEBUGGER_ABORT, STATUS_FATAL_APP_EXIT, EXCEPTION_NONCONTINUABLE);
    #endif  /* defined (_M_ARM) || defined (_CRT_APP) */
        }
    
    
        /* If we don't want to call ReportFault, then we call _exit(3), which is the
         * same as invoking the default handler for SIGABRT
         */
    
    
        _exit(3);
    }
    
    /***
    *unsigned int _set_abort_behavior(unsigned int, unsigned int) - set the behavior on abort
    *
    *Purpose:
    *
    *Entry:
    *   unsigned int flags - the flags we want to set
    *   unsigned int mask - mask the flag values
    *
    *Exit:
    *   Return the old behavior flags
    *
    *Exceptions:
    *   None
    *
    *******************************************************************************/
    
    unsigned int __cdecl _set_abort_behavior(unsigned int flags, unsigned int mask)
    {
        unsigned int oldflags = __abort_behavior;
        __abort_behavior = oldflags & (~mask) | flags & mask;
        return oldflags;
    }

    流程

    从上面代码,我可以看到,abort的流程如下:

    如果是调试版,且__abort_behavior为_WRITE_ABORT_MSG,则调用_NMSG_WRITE(_RT_ABORT);弹出crt异常提示框

    点"中止"进程直接退出,点"忽略"后,就判断sigabrt_act的值,不等于SIG_DFL的话调用raise发送信号SIGABRT,然后调用_exit(3)退出

    release版则会检测本机支不支持PF_FASTFAIL_AVAILABLE,支持就走windows快速失败机制,不支持就执行 _call_reportfault(_CRT_DEBUGGER_ABORT, STATUS_FATAL_APP_EXIT, EXCEPTION_NONCONTINUABLE);然后退出。

    隐藏的秘密

    上面讲的那些我相信大家都了解,但是,其实这个函数里,可能会引发多个异常。

    调用_NMSG_WRITE(_RT_ABORT),会引发异常0x4001000A或0x40010006:

    调用__fastfail(FAST_FAIL_FATAL_APP_EXIT);会引发异常0xc0000409子代码为FAST_FAIL_FATAL_APP_EXIT:

    而调用_call_reportfault会引发异常STATUS_FATAL_APP_EXIT(0x40000015):

    #if (defined (_M_IX86) || defined (_M_X64)) && !defined (_CRT_APP)
    void __cdecl _call_reportfault(
        int nDbgHookCode,
        DWORD dwExceptionCode,
        DWORD dwExceptionFlags
        )
    {
        // Notify the debugger if attached.
        if (nDbgHookCode != _CRT_DEBUGGER_IGNORE)
            _CRT_DEBUGGER_HOOK(nDbgHookCode);
    
        /* Fake an exception to call reportfault. */
        EXCEPTION_RECORD   ExceptionRecord = {0};
        CONTEXT ContextRecord;
        EXCEPTION_POINTERS ExceptionPointers = {&ExceptionRecord, &ContextRecord};
        BOOL wasDebuggerPresent = FALSE;
        DWORD ret = 0;
    
    #if defined (_M_IX86)
    
        __asm {
            mov dword ptr [ContextRecord.Eax], eax
            mov dword ptr [ContextRecord.Ecx], ecx
            mov dword ptr [ContextRecord.Edx], edx
            mov dword ptr [ContextRecord.Ebx], ebx
            mov dword ptr [ContextRecord.Esi], esi
            mov dword ptr [ContextRecord.Edi], edi
            mov word ptr [ContextRecord.SegSs], ss
            mov word ptr [ContextRecord.SegCs], cs
            mov word ptr [ContextRecord.SegDs], ds
            mov word ptr [ContextRecord.SegEs], es
            mov word ptr [ContextRecord.SegFs], fs
            mov word ptr [ContextRecord.SegGs], gs
            pushfd
            pop [ContextRecord.EFlags]
        }
    
        ContextRecord.ContextFlags = CONTEXT_CONTROL;
    #pragma warning(push)
    #pragma warning(disable:4311)
        ContextRecord.Eip = (ULONG)_ReturnAddress();
        ContextRecord.Esp = (ULONG)_AddressOfReturnAddress();
    #pragma warning(pop)
        ContextRecord.Ebp = *((ULONG *)_AddressOfReturnAddress()-1);
    
    #elif defined (_M_X64)
        __crtCaptureCurrentContext(&ContextRecord);
        ContextRecord.Rip = (ULONGLONG) _ReturnAddress();
        ContextRecord.Rsp = (ULONGLONG) _AddressOfReturnAddress()+8;
    
    #endif  /* defined (_M_X64) */
    
        ExceptionRecord.ExceptionCode = dwExceptionCode;
        ExceptionRecord.ExceptionFlags    = dwExceptionFlags;
        ExceptionRecord.ExceptionAddress = _ReturnAddress();
    
        wasDebuggerPresent = IsDebuggerPresent();
    
        /* Raises an exception that bypasses all exception handlers. */
        ret = __crtUnhandledException(&ExceptionPointers);
    
        // if no handler found and no debugger previously attached
        // the execution must stop into the debugger hook.
        if (ret == EXCEPTION_CONTINUE_SEARCH && !wasDebuggerPresent && nDbgHookCode != _CRT_DEBUGGER_IGNORE) {
            _CRT_DEBUGGER_HOOK(nDbgHookCode);
        }
    }
  • 相关阅读:
    用MobaXterm_Personal_12.0远程连接ubuntu虚拟机,并解决报错connection refused
    二叉树图解以及二叉树的递归原理
    Source Insight 4.0 安装使用简单粗暴教程
    VScode配置嵌入式linux开发环境,代替source insight
    linux系统下部署DNS正向解析
    【剑指Offer】面试题47. 礼物的最大价值
    【LeetCode】1371. 每个元音包含偶数次的最长子字符串
    【LeetCode】680. 验证回文字符串 Ⅱ
    【剑指Offer】面试题46. 把数字翻译成字符串
    【剑指Offer】面试题45. 把数组排成最小的数
  • 原文地址:https://www.cnblogs.com/yilang/p/12036034.html
Copyright © 2011-2022 走看看