diff --git a/lld/Common/ErrorHandler.cpp b/lld/Common/ErrorHandler.cpp --- a/lld/Common/ErrorHandler.cpp +++ b/lld/Common/ErrorHandler.cpp @@ -63,6 +63,10 @@ if (errorHandler().outputBuffer) errorHandler().outputBuffer->discard(); + // Re-throw a possible signal or exception once/if it was catched by + // safeLldMain(). + CrashRecoveryContext::ThrowIfCrash(val); + // Dealloc/destroy ManagedStatic variables before calling _exit(). // In an LTO build, allows us to get the output of -time-passes. // Ensures that the thread pool for the parallel algorithms is stopped to @@ -75,7 +79,10 @@ lld::outs().flush(); lld::errs().flush(); } - llvm::sys::Process::Exit(val); + // When running inside safeLldMain(), restore the control flow back to the + // CrashRecoveryContext. Otherwise simply use _exit(), meanning no cleanup, + // since we want to avoid further crashes on shutdown. + llvm::sys::Process::Exit(val, /*NoCleanup=*/true); } void lld::diagnosticHandler(const DiagnosticInfo &di) { diff --git a/lld/include/lld/Common/Driver.h b/lld/include/lld/Common/Driver.h --- a/lld/include/lld/Common/Driver.h +++ b/lld/include/lld/Common/Driver.h @@ -21,7 +21,9 @@ // Generic entry point when using LLD as a library, safe for re-entry, supports // crash recovery. Returns a general completion code and a boolean telling // whether it can be called again. In some cases, a crash could corrupt memory -// and re-entry would not be possible anymore. +// and re-entry would not be possible anymore. Use exitLld() in that case to +// properly exit your application and avoid intermittent crashes on exit caused +// by cleanup. SafeReturn safeLldMain(int argc, const char **argv, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); diff --git a/lld/test/COFF/arm-thumb-branch20-error.s b/lld/test/COFF/arm-thumb-branch20-error.s --- a/lld/test/COFF/arm-thumb-branch20-error.s +++ b/lld/test/COFF/arm-thumb-branch20-error.s @@ -1,6 +1,6 @@ // REQUIRES: arm // RUN: llvm-mc -filetype=obj -triple=thumbv7a-windows-gnu %s -o %t.obj -// RUN: not lld-link -entry:_start -subsystem:console %t.obj -out:%t.exe 2>&1 | FileCheck %s +// RUN: env LLD_IN_TEST=1 not lld-link -entry:_start -subsystem:console %t.obj -out:%t.exe 2>&1 | FileCheck %s .syntax unified .globl _start _start: diff --git a/lld/test/COFF/comdat-selection.s b/lld/test/COFF/comdat-selection.s --- a/lld/test/COFF/comdat-selection.s +++ b/lld/test/COFF/comdat-selection.s @@ -32,11 +32,11 @@ # RUN: cp %t.same_size.obj %t.obj && lld-link /dll /noentry /nodefaultlib %t.same_size.obj %t.obj # RUN: cp %t.same_contents.obj %t.obj && lld-link /dll /noentry /nodefaultlib %t.same_contents.obj %t.obj # RUN: cp %t.largest.obj %t.obj && lld-link /dll /noentry /nodefaultlib %t.largest.obj %t.obj -# RUN: cp %t.newest.obj %t.obj && not lld-link /dll /noentry /nodefaultlib %t.newest.obj %t.obj 2>&1 | FileCheck --check-prefix=NEWNEW %s +# RUN: cp %t.newest.obj %t.obj && env LLD_IN_TEST=1 not lld-link /dll /noentry /nodefaultlib %t.newest.obj %t.obj 2>&1 | FileCheck --check-prefix=NEWNEW %s # NEWNEW: lld-link: error: unknown comdat type 7 for symbol # /force doesn't affect errors about unknown comdat types. -# RUN: cp %t.newest.obj %t.obj && not lld-link /force /dll /noentry /nodefaultlib %t.newest.obj %t.obj 2>&1 | FileCheck --check-prefix=NEWNEWFORCE %s +# RUN: cp %t.newest.obj %t.obj && env LLD_IN_TEST=1 not lld-link /force /dll /noentry /nodefaultlib %t.newest.obj %t.obj 2>&1 | FileCheck --check-prefix=NEWNEWFORCE %s # NEWNEWFORCE: lld-link: error: unknown comdat type 7 for symbol # Check that same_size, same_contents, largest do what they're supposed to. diff --git a/lld/test/COFF/delayimports-error.test b/lld/test/COFF/delayimports-error.test --- a/lld/test/COFF/delayimports-error.test +++ b/lld/test/COFF/delayimports-error.test @@ -3,7 +3,8 @@ # RUN: lld-link /out:%t.dir/foo.dll /dll %t1.obj /export:datasym,DATA /noentry # RUN: yaml2obj %s -o %t2.obj -# RUN: not lld-link /out:%t.exe /entry:main %t2.obj %t.dir/foo.lib /delayload:foo.dll \ +# RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:main %t2.obj \ +# RUN: %t.dir/foo.lib /delayload:foo.dll \ # RUN: /alternatename:__delayLoadHelper2=main /opt:noref >& %t.log # RUN: FileCheck %s < %t.log diff --git a/lld/test/COFF/driver-windows.test b/lld/test/COFF/driver-windows.test --- a/lld/test/COFF/driver-windows.test +++ b/lld/test/COFF/driver-windows.test @@ -1,3 +1,3 @@ # REQUIRES: system-windows -# RUN: not LLD-LINK 2>&1 | FileCheck %s +# RUN: env LLD_IN_TEST=1 not LLD-LINK 2>&1 | FileCheck %s CHECK: no input files diff --git a/lld/test/COFF/driver.test b/lld/test/COFF/driver.test --- a/lld/test/COFF/driver.test +++ b/lld/test/COFF/driver.test @@ -13,18 +13,18 @@ # RUN: lld-link /lib /help | FileCheck -check-prefix=LIBHELP %s LIBHELP: OVERVIEW: LLVM Lib -# RUN: not lld-link /WX /lib 2>&1 | FileCheck -check-prefix=LIBBAD %s +# RUN: env LLD_IN_TEST=1 not lld-link /WX /lib 2>&1 | FileCheck -check-prefix=LIBBAD %s LIBBAD: ignoring /lib since it's not the first argument # RUN: yaml2obj %p/Inputs/hello32.yaml -o %t.obj # RUN: not lld-link /out:/ %t.obj 2>&1 | FileCheck -check-prefix=DIR %s DIR: cannot open output file -# RUN: not lld-link -version 2>&1 | FileCheck -check-prefix=SPELLVERSION %s +# RUN: env LLD_IN_TEST=1 not lld-link -version 2>&1 | FileCheck -check-prefix=SPELLVERSION %s SPELLVERSION: ignoring unknown argument '-version', did you mean '--version' SPELLVERSION: no input files -# RUN: not lld-link -nodefaultlibs 2>&1 | FileCheck -check-prefix=SPELLNODEFAULTLIB %s +# RUN: env LLD_IN_TEST=1 not lld-link -nodefaultlibs 2>&1 | FileCheck -check-prefix=SPELLNODEFAULTLIB %s SPELLNODEFAULTLIB: ignoring unknown argument '-nodefaultlibs', did you mean '-nodefaultlib' SPELLNODEFAULTLIB: no input files diff --git a/lld/test/COFF/export-limit.s b/lld/test/COFF/export-limit.s --- a/lld/test/COFF/export-limit.s +++ b/lld/test/COFF/export-limit.s @@ -3,7 +3,7 @@ # RUN: %python %p/Inputs/def-many.py 65536 > %t-65536.def # RUN: llvm-mc -triple x86_64-win32 %s -filetype=obj -o %t.obj # RUN: lld-link -dll -noentry %t.obj -out:%t.dll -def:%t-65535.def -# RUN: not lld-link -dll -noentry %t.obj -out:%t.dll -def:%t-65536.def 2>&1 | FileCheck %s +# RUN: env LLD_IN_TEST=1 not lld-link -dll -noentry %t.obj -out:%t.dll -def:%t-65536.def 2>&1 | FileCheck %s # CHECK: error: too many exported symbols diff --git a/lld/test/COFF/failifmismatch.test b/lld/test/COFF/failifmismatch.test --- a/lld/test/COFF/failifmismatch.test +++ b/lld/test/COFF/failifmismatch.test @@ -9,15 +9,15 @@ RUN: lld-link /entry:main /subsystem:console /out:%t.exe \ RUN: %p/Inputs/ret42.obj /failifmismatch:k1=v1 /failifmismatch:k1=v1 -RUN: not lld-link /entry:main /subsystem:console /out:%t.exe \ +RUN: env LLD_IN_TEST=1 not lld-link /entry:main /subsystem:console /out:%t.exe \ RUN: %p/Inputs/ret42.obj /failifmismatch:k1=v1 /failifmismatch:k1=v2 2>&1 | FileCheck %s RUN: llc < %p/Inputs/failmismatch1.ll -mtriple x86_64-windows-msvc -filetype obj -o %t1.obj RUN: llc < %p/Inputs/failmismatch2.ll -mtriple x86_64-windows-msvc -filetype obj -o %t2.obj -RUN: not lld-link %t1.obj %t2.obj 2>&1 | FileCheck %s -check-prefix OBJ +RUN: env LLD_IN_TEST=1 not lld-link %t1.obj %t2.obj 2>&1 | FileCheck %s -check-prefix OBJ RUN: llvm-lib %t1.obj /out:%t.lib -RUN: not lld-link %t.lib %t2.obj 2>&1 | FileCheck %s -check-prefix LIB +RUN: env LLD_IN_TEST=1 not lld-link %t.lib %t2.obj 2>&1 | FileCheck %s -check-prefix LIB CHECK: lld-link: error: /failifmismatch: mismatch detected for 'k1': CHECK-NEXT: >>> cmd-line has value v1 diff --git a/lld/test/COFF/invalid-obj.test b/lld/test/COFF/invalid-obj.test --- a/lld/test/COFF/invalid-obj.test +++ b/lld/test/COFF/invalid-obj.test @@ -1,5 +1,5 @@ # RUN: yaml2obj %s -o %t.obj -# RUN: not lld-link %t.obj 2>&1 | FileCheck %s +# RUN: env LLD_IN_TEST=1 not lld-link %t.obj 2>&1 | FileCheck %s # CHECK: getSectionName failed: #1: diff --git a/lld/test/COFF/invalid-section-number.test b/lld/test/COFF/invalid-section-number.test --- a/lld/test/COFF/invalid-section-number.test +++ b/lld/test/COFF/invalid-section-number.test @@ -1,5 +1,5 @@ # RUN: yaml2obj %s -o %t.obj -# RUN: not lld-link %t.obj 2>&1 | FileCheck %s +# RUN: env LLD_IN_TEST=1 not lld-link %t.obj 2>&1 | FileCheck %s # CHECK: foo should not refer to special section -10 diff --git a/lld/test/COFF/linkenv.test b/lld/test/COFF/linkenv.test --- a/lld/test/COFF/linkenv.test +++ b/lld/test/COFF/linkenv.test @@ -3,7 +3,7 @@ CHECK: OVERVIEW: LLVM Linker -# RUN: env LINK=-help not lld-link /lldignoreenv 2>&1 | \ +# RUN: env LLD_IN_TEST=1 LINK=-help not lld-link /lldignoreenv 2>&1 | \ # RUN: FileCheck --check-prefix=ERR %s ERR: error: no input files diff --git a/lld/test/COFF/manifestinput-error.test b/lld/test/COFF/manifestinput-error.test --- a/lld/test/COFF/manifestinput-error.test +++ b/lld/test/COFF/manifestinput-error.test @@ -2,7 +2,7 @@ # UNSUPPORTED: libxml2 # RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj -# RUN: not lld-link /out:%t.exe /entry:main \ +# RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:main \ # RUN: /manifest:embed \ # RUN: /manifestuac:"level='requireAdministrator'" \ # RUN: /manifestinput:%p/Inputs/manifestinput.test %t.obj 2>&1 | FileCheck %s diff --git a/lld/test/COFF/merge.test b/lld/test/COFF/merge.test --- a/lld/test/COFF/merge.test +++ b/lld/test/COFF/merge.test @@ -7,17 +7,17 @@ # RUN: /merge:.foo=.bar /merge:.bar=.abc %t.obj /debug # RUN: llvm-readobj --sections %t.exe | FileCheck --check-prefix=CHECK2 %s -# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \ +# RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:main /subsystem:console /force \ # RUN: /merge:.rsrc=.foo %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-RSRC %s -# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \ +# RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:main /subsystem:console /force \ # RUN: /merge:.foo=.rsrc %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-RSRC %s -# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \ +# RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:main /subsystem:console /force \ # RUN: /merge:.reloc=.foo %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-RELOC %s -# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \ +# RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:main /subsystem:console /force \ # RUN: /merge:.foo=.reloc %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-RELOC %s -# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \ +# RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:main /subsystem:console /force \ # RUN: /merge:.foo=.foo1 /merge:.foo1=.foo %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-CYCLE %s -# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \ +# RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:main /subsystem:console /force \ # RUN: /merge:.foo=.foo1 /merge:.foo1=.foo2 /merge:.foo2=.foo1 %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-CYCLE %s # CHECK: Name: .def diff --git a/lld/test/COFF/pdata-arm64-bad.yaml b/lld/test/COFF/pdata-arm64-bad.yaml --- a/lld/test/COFF/pdata-arm64-bad.yaml +++ b/lld/test/COFF/pdata-arm64-bad.yaml @@ -1,5 +1,5 @@ # RUN: yaml2obj %s -o %t.obj -# RUN: not lld-link /out:%t.exe /entry:func1 /subsystem:console %t.obj 2>&1 | FileCheck %s +# RUN: env LLD_IN_TEST=1 not lld-link /out:%t.exe /entry:func1 /subsystem:console %t.obj 2>&1 | FileCheck %s # This file is like pdata-arm64.yaml, except that .pdata has been extended with # 4 bytes. This can happen due to for example bad assembler input. Check that diff --git a/lld/test/COFF/precomp-link.test b/lld/test/COFF/precomp-link.test --- a/lld/test/COFF/precomp-link.test +++ b/lld/test/COFF/precomp-link.test @@ -33,9 +33,9 @@ RUN: yaml2obj precomp-no-objname.yaml -o %t.precomp-no-objname.obj RUN: yaml2obj precomp-zero-sig.yaml -o %t.precomp-zero-sig.obj -RUN: not lld-link %t.precomp-no-objname.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix FAILURE-NO-SIGNATURE +RUN: env LLD_IN_TEST=1 not lld-link %t.precomp-no-objname.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix FAILURE-NO-SIGNATURE -RUN: not lld-link %t.precomp-zero-sig.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix FAILURE-NO-SIGNATURE +RUN: env LLD_IN_TEST=1 not lld-link %t.precomp-zero-sig.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix FAILURE-NO-SIGNATURE FAILURE-NO-SIGNATURE: error: {{.*}}.obj claims to be a PCH object, but does not have a valid signature @@ -43,7 +43,7 @@ RUN: cp %S/Inputs/precomp.obj %t.precomp-dup.obj -RUN: not lld-link %S/Inputs/precomp.obj %t.precomp-dup.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix FAILURE-DUP-SIGNATURE +RUN: env LLD_IN_TEST=1 not lld-link %S/Inputs/precomp.obj %t.precomp-dup.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix FAILURE-DUP-SIGNATURE FAILURE-DUP-SIGNATURE: error: a PCH object with the same signature has already been provided ({{.*precomp.obj and .*precomp-dup.obj.*}}) diff --git a/lld/test/COFF/thin-archive.s b/lld/test/COFF/thin-archive.s --- a/lld/test/COFF/thin-archive.s +++ b/lld/test/COFF/thin-archive.s @@ -17,9 +17,9 @@ # RUN: rm %t.lib.obj # RUN: lld-link /entry:main %t.main.obj %t.lib /out:%t.exe 2>&1 | \ # RUN: FileCheck --allow-empty %s -# RUN: not lld-link /entry:main %t.main.obj %t_thin.lib /out:%t.exe 2>&1 | \ -# RUN: FileCheck --check-prefix=NOOBJ %s -# RUN: not lld-link /entry:main %t.main.obj %t_thin.lib /out:%t.exe \ +# RUN: env LLD_IN_TEST=1 not lld-link /entry:main %t.main.obj %t_thin.lib \ +# RUN: /out:%t.exe 2>&1 | FileCheck --check-prefix=NOOBJ %s +# RUN: env LLD_IN_TEST=1 not lld-link /entry:main %t.main.obj %t_thin.lib /out:%t.exe \ # RUN: /demangle:no 2>&1 | FileCheck --check-prefix=NOOBJNODEMANGLE %s # CHECK-NOT: error: could not get the buffer for the member defining diff --git a/lld/test/COFF/thunk-replace.s b/lld/test/COFF/thunk-replace.s --- a/lld/test/COFF/thunk-replace.s +++ b/lld/test/COFF/thunk-replace.s @@ -3,7 +3,8 @@ # RUN: llvm-mc -triple=x86_64-win32 %s -filetype=obj -o %t.main.obj # RUN: llvm-mc -triple=x86_64-win32 %p/Inputs/otherFunc.s -filetype=obj -o %t.other.obj # RUN: llvm-ar rcs %t.other.lib %t.other.obj -# RUN: not lld-link -out:%t.exe -entry:main %t.main.obj %p/Inputs/std64.lib %t.other.lib -opt:noref 2>&1 | FileCheck %s +# RUN: env LLD_IN_TEST=1 not lld-link -out:%t.exe -entry:main %t.main.obj \ +# RUN: %p/Inputs/std64.lib %t.other.lib -opt:noref 2>&1 | FileCheck %s # CHECK: MessageBoxA was replaced .global main diff --git a/lld/tools/lld/lld.cpp b/lld/tools/lld/lld.cpp --- a/lld/tools/lld/lld.cpp +++ b/lld/tools/lld/lld.cpp @@ -179,7 +179,7 @@ if (!crc.RunSafely([&]() { r = lldMain(argc, argv, stdoutOS, stderrOS, /*exitEarly=*/false); })) - r = crc.RetCode; + return {crc.RetCode, /*canRunAgain=*/false}; } // Cleanup memory and reset everything back in pristine condition. This path @@ -221,7 +221,7 @@ // Execute one iteration. auto r = safeLldMain(argc, argv, llvm::outs(), llvm::errs()); if (!r.canRunAgain) - _exit(r.ret); // Exit now, can't re-execute again. + exitLld(r.ret); // Exit now, can't re-execute again. if (!mainRet) { mainRet = r.ret; @@ -230,14 +230,5 @@ return r.ret; } } -#if LLVM_ON_UNIX - // Re-throw the signal so it can be caught by WIFSIGNALED in - // llvm/lib/Support/Unix/Program.inc. This is required to correctly handle - // usages of `not --crash`. - if (*mainRet > 128) { - llvm::sys::unregisterHandlers(); - raise(*mainRet - 128); - } -#endif return *mainRet; } diff --git a/llvm/include/llvm/Support/CrashRecoveryContext.h b/llvm/include/llvm/Support/CrashRecoveryContext.h --- a/llvm/include/llvm/Support/CrashRecoveryContext.h +++ b/llvm/include/llvm/Support/CrashRecoveryContext.h @@ -102,6 +102,10 @@ LLVM_ATTRIBUTE_NORETURN void HandleExit(int RetCode); + /// Throw again a signal or an exception, after it was catched once by a + /// CrashRecoveryContext. + static bool ThrowIfCrash(int RetCode); + /// In case of a crash, this is the crash identifier. int RetCode = 0; diff --git a/llvm/include/llvm/Support/Process.h b/llvm/include/llvm/Support/Process.h --- a/llvm/include/llvm/Support/Process.h +++ b/llvm/include/llvm/Support/Process.h @@ -213,8 +213,9 @@ /// Equivalent to ::exit(), except when running inside a CrashRecoveryContext. /// In that case, the control flow will resume after RunSafely(), like for a /// crash, rather than exiting the current process. + /// Use \arg NoCleanup for calling _exit() instead of exit(). LLVM_ATTRIBUTE_NORETURN - static void Exit(int RetCode); + static void Exit(int RetCode, bool NoCleanup = false); }; } diff --git a/llvm/lib/Support/CrashRecoveryContext.cpp b/llvm/lib/Support/CrashRecoveryContext.cpp --- a/llvm/lib/Support/CrashRecoveryContext.cpp +++ b/llvm/lib/Support/CrashRecoveryContext.cpp @@ -442,6 +442,26 @@ llvm_unreachable("Most likely setjmp wasn't called!"); } +bool CrashRecoveryContext::ThrowIfCrash(int RetCode) { +#if defined(_WIN32) + // On Windows, the high bits are reserved for kernel return codes. Values + // starting with 0x80000000 are reserved for "warnings"; values of 0xC0000000 + // and up are for "errors". In practice, both are interpreted as a + // non-continuable signal. + unsigned Code = ((unsigned)RetCode & 0xF0000000) >> 28; + if (Code != 0xC && Code != 8) + return false; + ::RaiseException(RetCode, 0, 0, NULL); +#else + // On Unix, signals are represented by return codes of 128 or higher. + if (RetCode <= 128) + return false; + llvm::sys::unregisterHandlers(); + raise(RetCode - 128); +#endif + return true; +} + // FIXME: Portability. static void setThreadBackgroundPriority() { #ifdef __APPLE__ diff --git a/llvm/lib/Support/Process.cpp b/llvm/lib/Support/Process.cpp --- a/llvm/lib/Support/Process.cpp +++ b/llvm/lib/Support/Process.cpp @@ -91,10 +91,14 @@ bool Process::AreCoreFilesPrevented() { return coreFilesPrevented; } LLVM_ATTRIBUTE_NORETURN -void Process::Exit(int RetCode) { +void Process::Exit(int RetCode, bool NoCleanup) { if (CrashRecoveryContext *CRC = CrashRecoveryContext::GetCurrent()) CRC->HandleExit(RetCode); - ::exit(RetCode); + + if (NoCleanup) + _Exit(RetCode); + else + ::exit(RetCode); } // Include the platform-specific parts of this class.