/*++ Copyright (c) Microsoft Corporation Module Name: sxsmain.cpp Abstract: Author: Revision History: --*/ #include "stdinc.h" #include #include "sxsp.h" #include "fusioneventlog.h" #include "fusiontrace.h" #include "fusionsha1.h" extern CRITICAL_SECTION g_csHashFile; // // This typedef of a function represents a dll-main startup function. These are // called during a DLL_PROCESS_ATTACH call to SxsDllMain in the order they're listed. // typedef BOOL (WINAPI *PFNStartupPointer)( HINSTANCE hDllInstnace, DWORD dwReason, PVOID pvReason ); BOOL WINAPI DllStartup_CrtInit(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved); BOOL WINAPI FusionpEventLogMain(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved); BOOL WINAPI DllStartup_HeapSetup(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved); BOOL WINAPI DllStartup_ActCtxContributors(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved); BOOL WINAPI FusionpCryptoContext_DllMain(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved); BOOL WINAPI DllStartup_AtExitList(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved); BOOL WINAPI DllStartup_AlternateAssemblyStoreRoot(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved); BOOL WINAPI DllStartup_SetupLog(HINSTANCE Module, DWORD Reason, PVOID Reserved); BOOL WINAPI DllStartup_FileHashCriticalSectionInitialization(HINSTANCE Module, DWORD Reason, PVOID Reserved); BOOL WINAPI FusionpAreWeInOSSetupModeMain(HINSTANCE Module, DWORD Reason, PVOID Reserved); #define MAKE_STARTUP_RECORD(f) { &f, L#f } #define SXSP_DLLMAIN_ATTACHED 0x01 const struct StartupFunctionRecord { PFNStartupPointer Handler; PCWSTR Name; } g_SxspDllMainStartupPointers[] = { MAKE_STARTUP_RECORD(DllStartup_CrtInit), MAKE_STARTUP_RECORD(DllStartup_HeapSetup), MAKE_STARTUP_RECORD(FusionpEventLogMain), MAKE_STARTUP_RECORD(DllStartup_AtExitList), MAKE_STARTUP_RECORD(DllStartup_AlternateAssemblyStoreRoot), MAKE_STARTUP_RECORD(DllStartup_ActCtxContributors), MAKE_STARTUP_RECORD(FusionpCryptoContext_DllMain), MAKE_STARTUP_RECORD(DllStartup_SetupLog), MAKE_STARTUP_RECORD(DllStartup_FileHashCriticalSectionInitialization), MAKE_STARTUP_RECORD(FusionpAreWeInOSSetupModeMain) }; BYTE g_SxspDllMainStartupStatus[NUMBER_OF(g_SxspDllMainStartupPointers)]; HINSTANCE g_hInstance; /* NTRAID#NTBUG9-591174-2002/03/31-JayKrell Replace all SLists with criticalsection and CDeque. */ SLIST_HEADER sxspAtExitList; PCWSTR g_AlternateAssemblyStoreRoot; BOOL g_WriteRegistryAnyway; #if DBG PCSTR FusionpDllMainReasonToString(DWORD Reason) { PCSTR String; String = (Reason == DLL_THREAD_ATTACH) ? "DLL_THREAD_ATTACH" : (Reason == DLL_THREAD_DETACH) ? "DLL_THREAD_DETACH" : (Reason == DLL_PROCESS_ATTACH) ? "DLL_PROCESS_ATTACH" : (Reason == DLL_PROCESS_DETACH) ? "DLL_PROCESS_DETACH" : ""; return String; } #endif extern "C" BOOL WINAPI SxsDllMain( HINSTANCE hInst, DWORD dwReason, PVOID pvReserved ) // // We do not call DisableThreadLibraryCalls // because some/all versions of the CRT do work in the thread calls, // allocation and free of the per thread data. // { // // Several "oca" (online crash analysis) bugs show // Sxs.dll in various places in DllMain(process_detach) in hung apps. // We load in many processes for oleaut/typelibrary support. // // When ExitProcess is called, it is impossible to leak memory and kernel handles, // so it is sufficient and preferrable to do nothing quickly than to go through // and free each individual resource. // // The pvReserved parameter is actually documented as having a meaning. // Its NULLness indicates if we are in FreeLibrary or ExitProcess. // if (dwReason == DLL_PROCESS_DETACH && pvReserved != NULL) { // For ExitProcess, do nothing quickly. return TRUE; } BOOL fResult = FALSE; SIZE_T nCounter = 0; #if DBG ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_VERBOSE, "SXS: 0x%lx.0x%lx, %s() %s\n", GetCurrentProcessId(), GetCurrentThreadId(), __FUNCTION__, FusionpDllMainReasonToString(dwReason)); #endif switch (dwReason) { case DLL_THREAD_ATTACH: if (!g_SxspDllMainStartupPointers[0].Handler(hInst, dwReason, pvReserved)) { const DWORD dwLastError = ::FusionpGetLastWin32Error(); ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS: %s - %ls(DLL_THREAD_ATTACH) failed. Last WinError 0x%08x (%d).\n", __FUNCTION__, g_SxspDllMainStartupPointers[0].Name, dwLastError, dwLastError); ::SxsDllMain(hInst, DLL_THREAD_DETACH, pvReserved); ::FusionpSetLastWin32Error(dwLastError); goto Exit; } break; case DLL_THREAD_DETACH: if (!g_SxspDllMainStartupPointers[0].Handler(hInst, dwReason, pvReserved)) { const DWORD dwLastError = ::FusionpGetLastWin32Error(); ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS: %s - %ls(DLL_THREAD_ATTACH) failed. Last WinError 0x%08x (%d).\n", __FUNCTION__, g_SxspDllMainStartupPointers[0].Name, dwLastError, dwLastError); // Eat the error, the loader ignores it. } break; case DLL_PROCESS_ATTACH: ASSERT_NTC(hInst); g_hInstance = hInst; for (nCounter = 0; nCounter != NUMBER_OF(g_SxspDllMainStartupPointers) ; ++nCounter) { const SIZE_T nIndex = nCounter; if (g_SxspDllMainStartupPointers[nIndex].Handler(hInst, dwReason, pvReserved)) { g_SxspDllMainStartupStatus[nIndex] |= SXSP_DLLMAIN_ATTACHED; } else { const DWORD dwLastError = ::FusionpGetLastWin32Error(); // // It's a little iffy to set the bit even upon failure, but // we do this because we assume individual functions do not handle // rollback internally upon attach failure. // g_SxspDllMainStartupStatus[nIndex] |= SXSP_DLLMAIN_ATTACHED; ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS: %s - %ls(DLL_PROCESS_ATTACH) failed. Last WinError 0x%08x (%d).\n", __FUNCTION__, g_SxspDllMainStartupPointers[nIndex].Name, dwLastError, dwLastError); // pvReserved has approximately the same defined meaning for attach and detach ::SxsDllMain(hInst, DLL_PROCESS_DETACH, pvReserved); ::FusionpSetLastWin32Error(dwLastError); goto Exit; } } break; case DLL_PROCESS_DETACH: // // We always succeed DLL_PROCESS_DETACH, and we do not // short circuit it upon failure. The loader in fact // ignores what we return. // for (nCounter = NUMBER_OF(g_SxspDllMainStartupPointers) ; nCounter != 0 ; --nCounter) { const SIZE_T nIndex = nCounter - 1; if ((g_SxspDllMainStartupStatus[nIndex] & SXSP_DLLMAIN_ATTACHED) != 0) { g_SxspDllMainStartupStatus[nIndex] &= ~SXSP_DLLMAIN_ATTACHED; if (!g_SxspDllMainStartupPointers[nIndex].Handler(hInst, dwReason, pvReserved)) { const DWORD dwLastError = ::FusionpGetLastWin32Error(); ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS: %s - %ls(DLL_PROCESS_DETACH) failed. Last WinError 0x%08x (%d).\n", __FUNCTION__, g_SxspDllMainStartupPointers[nIndex].Name, dwLastError, dwLastError); } } } break; } fResult = TRUE; Exit: return fResult; } /* NTRAID#NTBUG9-591174-2002/03/31-JayKrell Replace all SLists with criticalsection and CDeque. */ BOOL SxspAtExit( CCleanupBase* pCleanup ) { if (!pCleanup->m_fInAtExitList) { SxspInterlockedPushEntrySList(&sxspAtExitList, pCleanup); pCleanup->m_fInAtExitList = true; } return TRUE; } /* NTRAID#NTBUG9-591174-2002/03/31-JayKrell Replace all SLists with criticalsection and CDeque. */ BOOL SxspTryCancelAtExit( CCleanupBase* pCleanup ) { if (!pCleanup->m_fInAtExitList) return FALSE; if (::SxspIsSListEmpty(&sxspAtExitList)) { pCleanup->m_fInAtExitList = false; return FALSE; } PSLIST_ENTRY pTop = ::SxspInterlockedPopEntrySList(&sxspAtExitList); if (pTop == pCleanup) { pCleanup->m_fInAtExitList = false; return TRUE; } if (pTop != NULL) ::SxspInterlockedPushEntrySList(&sxspAtExitList, pTop); return FALSE; } #define COMMON_HANDLER_PROLOG(dwReason) \ { \ ASSERT_NTC(\ (dwReason == DLL_PROCESS_ATTACH) || \ (dwReason == DLL_PROCESS_DETACH) \ ); \ if (!(\ (dwReason == DLL_PROCESS_ATTACH) || \ (dwReason == DLL_PROCESS_DETACH) \ )) goto Exit; \ } BOOL WINAPI DllStartup_AtExitList(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved) { BOOL fSuccess = FALSE; COMMON_HANDLER_PROLOG(dwReason); switch (dwReason) { case DLL_PROCESS_DETACH: { CCleanupBase *pCleanup = NULL; while (pCleanup = UNCHECKED_DOWNCAST(SxspPopEntrySList(&sxspAtExitList))) { pCleanup->m_fInAtExitList = false; pCleanup->DeleteYourself(); } fSuccess = TRUE; } break; case DLL_PROCESS_ATTACH: ::SxspInitializeSListHead(&sxspAtExitList); fSuccess = TRUE; break; } Exit: return fSuccess; } extern "C" { BOOL g_fInCrtInit; // // This is the internal CRT routine that does the bulk of // the initialization and uninitialization. // BOOL WINAPI _CRT_INIT( HINSTANCE hDllInstnace, DWORD dwReason, PVOID pvReason ); void SxspCrtRaiseExit( PCSTR pszCaller, int crtError ) // // all the various CRT functions that end up calling ExitProcess end up here // see crt0dat.c // { const static struct { NTSTATUS ntstatus; PCSTR psz; } rgErrors[] = { { STATUS_FATAL_APP_EXIT, "STATUS_FATAL_APP_EXIT" }, { STATUS_DLL_INIT_FAILED, "STATUS_DLL_INIT_FAILED" }, }; const ULONG nInCrtInit = g_fInCrtInit ? 1 : 0; // // if (!g_fInCrtInit), then throwing STATUS_DLL_INIT_FAILED is dubious, // but there no clearly good answer, maybe STATUS_NO_MEMORY, maybe introduce // an NTSTATUS facility to wrap the values in. // ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_ERROR, "SXS: [0x%lx.0x%lx] %s(crtError:%d, g_fInCrtInit:%s) calling RaiseException(%08lx %s)\n", GetCurrentProcessId(), GetCurrentThreadId(), pszCaller, crtError, nInCrtInit ? "true" : "false", rgErrors[nInCrtInit].ntstatus, rgErrors[nInCrtInit].psz ); ::RaiseException( static_cast(rgErrors[nInCrtInit].ntstatus), 0, // flags 0, // number of extra parameters NULL); // extra parameters // // RaiseException returns void, and generally doesn't return, though it // can if you intervene in a debugger. // } extern void (__cdecl * _aexit_rtn)(int); void __cdecl SxsCrtAExitRoutine( int crtError ) // // This is our replacement for an internal CRT routine that otherwise // calls ExitProcess. // { SxspCrtRaiseExit(__FUNCTION__, crtError); } } /* NTRAID#NTBUG9-591174-2002/03/31-JayKrell It'd be nice if we could just use msvcrt.dll without this hacking.. */ BOOL WINAPI DllStartup_CrtInit(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved) /* This mess is because we need destructors to run, even if there is an exception the startup code in msvcrt.dll and libcmt.lib is not very good it tends to call MessageBox and/or ExitProcess upon out of memory we need it to simply propagate an error */ { #if !FUSION_WIN // // having this do-nothing function makes our DLL_THREAD_ATTACH handling simpler. // return TRUE; #else BOOL fSuccess = FALSE; DWORD dwExceptionCode = 0; __try { __try { g_fInCrtInit = TRUE; if (dwReason == DLL_PROCESS_ATTACH) { _aexit_rtn = SxsCrtAExitRoutine; // // __app_type and __error_mode determine if // _CRT_INIT calls MessageBox or WriteFile(GetStdHandle()) upon errors. // MessageBox is a big nono in csrss. // WriteFile we expect to fail, but that's ok, and they don't check // the return value. // // It should be sufficient to set __error_mode. // _set_error_mode(_OUT_TO_STDERR); } fSuccess = _CRT_INIT(hInstance, dwReason, pvReserved); } __finally { g_fInCrtInit = FALSE; } } __except( ( (dwExceptionCode = GetExceptionCode()) == STATUS_DLL_INIT_FAILED || dwExceptionCode == STATUS_FATAL_APP_EXIT ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { } return fSuccess; #endif // FUSION_WIN } BOOL WINAPI DllStartup_HeapSetup(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved) { BOOL fSuccess = FALSE; COMMON_HANDLER_PROLOG(dwReason); switch (dwReason) { case DLL_PROCESS_ATTACH: fSuccess = FusionpInitializeHeap(hInstance); break; case DLL_PROCESS_DETACH: #if defined(FUSION_DEBUG_HEAP) ::FusionpDumpHeap(L""); #endif ::FusionpUninitializeHeap(); fSuccess = TRUE; break; } Exit: return fSuccess; } BOOL WINAPI DllStartup_ActCtxContributors(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved) { BOOL fSuccess = FALSE; COMMON_HANDLER_PROLOG(dwReason); switch (dwReason) { case DLL_PROCESS_ATTACH: fSuccess = SxspInitActCtxContributors(); break; case DLL_PROCESS_DETACH: SxspUninitActCtxContributors(); fSuccess = TRUE; break; } Exit: return fSuccess; } BOOL WINAPI DllStartup_AlternateAssemblyStoreRoot(HINSTANCE, DWORD dwReason, PVOID pvReserved) { BOOL fSuccess = FALSE; COMMON_HANDLER_PROLOG(dwReason); switch (dwReason) { case DLL_PROCESS_DETACH: if (g_AlternateAssemblyStoreRoot != NULL) { CSxsPreserveLastError ple; delete[] const_cast(g_AlternateAssemblyStoreRoot); g_AlternateAssemblyStoreRoot = NULL; ple.Restore(); } fSuccess = TRUE; break; case DLL_PROCESS_ATTACH: g_AlternateAssemblyStoreRoot = NULL; fSuccess = TRUE; break; } Exit: return fSuccess; } BOOL WINAPI DllStartup_FileHashCriticalSectionInitialization( HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved ) { BOOL fSuccess = FALSE; // // We're always called back on disconnect, which isn't really a bug, // but a different contract that this can't uphold. Instead, we'll // keep rollback information here about whether or not the csec was // initialized. // static BOOL s_fCritSecCreated; COMMON_HANDLER_PROLOG(dwReason); switch (dwReason) { case DLL_PROCESS_DETACH: if (s_fCritSecCreated) { ::DeleteCriticalSection(&g_csHashFile); s_fCritSecCreated = FALSE; } fSuccess = TRUE; break; case DLL_PROCESS_ATTACH: if (!s_fCritSecCreated) { if (!::FusionpInitializeCriticalSection(&g_csHashFile)) goto Exit; s_fCritSecCreated = TRUE; } fSuccess = TRUE; break; } Exit: return fSuccess; }