windows-nt/Source/XPSP1/NT/base/win32/fusion/sxsmain/sxsmain.cpp
2020-09-26 16:20:57 +08:00

597 lines
16 KiB
C++

#include "stdinc.h"
#include <windows.h>
#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
);
/*
MEMORY_BASIC_INFORMATION g_SxsDllMemoryBasicInformation = { 0 };
*/
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 DllStartup_CryptoContext(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[] = {
#if FUSION_WIN
MAKE_STARTUP_RECORD(DllStartup_CrtInit),
#endif // FUSION_WIN
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(DllStartup_CryptoContext),
MAKE_STARTUP_RECORD(DllStartup_SetupLog),
MAKE_STARTUP_RECORD(DllStartup_FileHashCriticalSectionInitialization),
MAKE_STARTUP_RECORD(FusionpAreWeInOSSetupModeMain)
};
BYTE g_SxspDllMainStartupStatus[NUMBER_OF(g_SxspDllMainStartupPointers)];
HINSTANCE g_hInstance = NULL;
static SLIST_HEADER sxspAtExitList;
PCWSTR g_AlternateAssemblyStoreRoot = NULL;
#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;
/*
if (!VirtualQueryEx(
GetCurrentProcess(),
hInst,
&g_SxsDllMemoryBasicInformation,
sizeof(g_SxsDllMemoryBasicInformation)))
{
FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"SXS: %s - Failed querying for the dll's memory boundaries: 0x%08x\n",
__FUNCTION__,
::FusionpGetLastWin32Error());
goto Exit;
}
*/
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;
}
BOOL
SxspAtExit(
CCleanupBase* pCleanup
)
{
if (!pCleanup->m_fInAtExitList)
{
SxspInterlockedPushEntrySList(&sxspAtExitList, pCleanup);
pCleanup->m_fInAtExitList = true;
}
return TRUE;
}
BOOL
SxspTryCancelAtExit(
CCleanupBase* pCleanup
)
{
if (!pCleanup->m_fInAtExitList)
return FALSE;
if (::SxspIsSListEmpty(&sxspAtExitList))
{
pCleanup->m_fInAtExitList = false;
return FALSE;
}
PSINGLE_LIST_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_CryptoContext(HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved)
{
BOOL fSuccess = FALSE;
COMMON_HANDLER_PROLOG(dwReason);
switch (dwReason)
{
case DLL_PROCESS_DETACH:
fSuccess = SxspReleaseGlobalCryptContext();
break;
case DLL_PROCESS_ATTACH:
fSuccess = TRUE;
break;
}
Exit:
return fSuccess;
}
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<CCleanupBase*>(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<DWORD>(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);
}
}
#if FUSION_WIN
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
*/
{
BOOL fSuccess = FALSE;
DWORD dwExceptionCode;
__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)
{
//
// Yes, Virginia, there is a fall-through.
//
case DLL_PROCESS_DETACH:
if (g_AlternateAssemblyStoreRoot != NULL)
{
CSxsPreserveLastError ple;
delete[] const_cast<PWSTR>(g_AlternateAssemblyStoreRoot);
ple.Restore();
}
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;
COMMON_HANDLER_PROLOG(dwReason);
switch (dwReason)
{
case DLL_PROCESS_DETACH:
::DeleteCriticalSection(&g_csHashFile);
fSuccess = TRUE;
break;
case DLL_PROCESS_ATTACH:
::InitializeCriticalSection(&g_csHashFile);
fSuccess = TRUE;
break;
}
Exit:
return fSuccess;
}