研究下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异常提示框

研究下vc++的abort函数

 

点"中止"进程直接退出,点"忽略"后,就判断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:

研究下vc++的abort函数

 

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

研究下vc++的abort函数

 

而调用_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);
    }
}
上一篇:Material Workbench Changes From 11i to R12


下一篇:C语言跨平台时间操作计算时间差