summaryrefslogtreecommitdiff
path: root/src/dlls/mscoree/delayload.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dlls/mscoree/delayload.cpp')
-rw-r--r--src/dlls/mscoree/delayload.cpp456
1 files changed, 456 insertions, 0 deletions
diff --git a/src/dlls/mscoree/delayload.cpp b/src/dlls/mscoree/delayload.cpp
new file mode 100644
index 0000000000..0226f7da2e
--- /dev/null
+++ b/src/dlls/mscoree/delayload.cpp
@@ -0,0 +1,456 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// DelayLoad.cpp
+//
+// This code defines the dealy load helper notification routines that will be
+// invoked when a dll marked for delay load is processed. A DLL is marked as
+// delay load by using the DELAYLOAD=foo.dll directive in your sources file.
+// This tells the linker to generate helpers for the imports of this dll instead
+// of loading it directly. If your application never touches those functions,
+// the the dll is never loaded. This improves (a) startup time each time the
+// app runs, and (b) overall working set size in the case you never use the
+// functionality.
+//
+//
+//
+// This module provides a hook helper and exception handler. The hook helper
+// is used primarily in debug mode right now to determine what call stacks
+// force a delay load of a dll. If these call stacks are very common, then
+// you should reconsider using a delay load.
+//
+// The exception handler is used to catch fatal errors like library not found
+// or entry point missing. If this happens you are dead and need to fail
+// gracefully.
+//
+//*****************************************************************************
+#include "stdafx.h" // Standard header.
+
+#if !defined(FEATURE_CORESYSTEM)
+
+#include "delayimp.h" // Delay load header file.
+#include "winwrap.h" // Wrappers for Win32 api's.
+#include "utilcode.h" // Debug helpers.
+#include "corerror.h" // Error codes from this EE.
+#include "shimload.h"
+#include "ex.h"
+#include "strsafe.h"
+
+//********** Locals. **********************************************************
+static DWORD _FormatMessage(__out_ecount(chMsg) __out_z LPWSTR szMsg, DWORD chMsg, DWORD dwLastError, ...);
+static void _FailLoadLib(unsigned dliNotify, DelayLoadInfo *pdli);
+static void _FailGetProc(unsigned dliNotify, DelayLoadInfo *pdli);
+
+#if defined (_DEBUG) || defined (__delay_load_trace__)
+static void _DbgPreLoadLibrary(int bBreak, DelayLoadInfo *pdli);
+#endif
+
+
+//********** Globals. *********************************************************
+
+// Override __pfnDllFailureHook. This will give the delay code a callback
+// for when a load failure occurs. This failure hook is implemented below.
+FARPROC __stdcall CorDelayErrorHook(unsigned dliNotify, DelayLoadInfo *pdli);
+ExternC extern PfnDliHook __pfnDliFailureHook = CorDelayErrorHook;
+
+// In trace mode, override the delay load hook. Our hook does nothing but
+// provide some diagnostic information for debugging.
+FARPROC __stdcall CorDelayLoadHook(unsigned dliNotify, DelayLoadInfo *pdli);
+ExternC extern PfnDliHook __pfnDliNotifyHook = CorDelayLoadHook;
+
+
+//********** Code. ************************************************************
+
+#undef ExitProcess
+
+extern void DECLSPEC_NORETURN ThrowOutOfMemory();
+
+//*****************************************************************************
+// Called for errors that might have occured.
+//*****************************************************************************
+FARPROC __stdcall CorDelayErrorHook( // Always 0.
+ unsigned dliNotify, // What event has occured, dli* flag.
+ DelayLoadInfo *pdli) // Description of the event.
+{
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ // Chose operation to perform based on operation.
+ switch (dliNotify)
+ {
+ // Failed to load the library. Need to fail gracefully.
+ case dliFailLoadLib:
+ //_FailLoadLib(dliNotify, pdli);
+ break;
+
+ // Failed to get the address of the given function, fail gracefully.
+ case dliFailGetProc:
+#ifndef FEATURE_CORECLR
+ _FailGetProc(dliNotify, pdli);
+#endif // !FEATURE_CORECLR
+ break;
+
+ // Unknown failure code.
+ default:
+ _ASSERTE(!"Unknown delay load failure code.");
+ break;
+ }
+
+#ifndef FEATURE_CORECLR
+ if (_stricmp(pdli->szDll, "ole32.dll") == 0)
+ {
+ // TODO: after interop team fixes delayload related to ole32.dll, we can throw OOM instead.
+ // For now, SQL preloads ole32.dll before starting CLR, so OOM for ole32 is not a concern.
+ ExitProcess(pdli->dwLastError);
+ }
+ else
+#endif // !FEATURE_CORECLR
+#ifdef MSDIS_DLL
+ // MSDIS_DLL is a macro defined in SOURCES.INC
+ if (_stricmp(pdli->szDll, MSDIS_DLL) == 0)
+ {
+ // msdisxxx.dll is used in GCStress 4 on chk/dbg builds, if it fails to load then the
+ // process will stack-overflow or terminate with no obvious reason of the root cause.
+ _ASSERTE(!"Failed to delay load " MSDIS_DLL);
+ }
+ else
+#endif // MSDIS_DLL
+ {
+#ifndef FEATURE_CORECLR
+ // We do not own the process. ExitProcess is bad.
+ // We will try to recover next time.
+ ThrowWin32 (pdli->dwLastError);
+#endif // !FEATURE_CORECLR
+ }
+
+ return (0);
+}
+
+
+//*****************************************************************************
+// Format an error message using a system error (supplied through GetLastError)
+// and any subtitution values required.
+//*****************************************************************************
+DWORD _FormatMessage( // How many characters written.
+ __out_ecount(chMsg) __out_z LPWSTR szMsg, // Buffer for formatted data.
+ DWORD chMsg, // How big is the buffer.
+ DWORD dwLastError, // The last error code we got.
+ ...) // Substitution values.
+{
+ WRAPPER_NO_CONTRACT;
+
+ DWORD iRtn;
+ va_list marker;
+
+ va_start(marker, dwLastError);
+ iRtn = WszFormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM, // Flags.
+ 0, // No source, use system.
+ dwLastError, // Error code.
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Use default langauge.
+ szMsg, // Output buffer.
+ dwLastError, // Size of buffer.
+ &marker); // Substitution text.
+ va_end(marker);
+ return (iRtn);
+}
+
+
+//*****************************************************************************
+// A library failed to load. This is always a bad thing.
+//*****************************************************************************
+void _FailLoadLib(
+ unsigned dliNotify, // What event has occured, dli* flag.
+ DelayLoadInfo *pdli) // Description of the event.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ // We're allocating strings for the purposes of putting up a critical error box.
+ // Obviously, OOM's aren't going to be passed up to the caller.
+ FAULT_NOT_FATAL();
+
+
+ WCHAR rcMessage[_MAX_PATH+500]; // Message for display.
+ WCHAR rcFmt[500]; // 500 is the number used by excep.cpp for mscorrc resources.
+ HRESULT hr;
+
+ // Load a detailed error message from the resource file.
+ if (SUCCEEDED(hr = UtilLoadStringRC(MSEE_E_LOADLIBFAILED, rcFmt, NumItems(rcFmt))))
+ {
+ StringCchPrintf(rcMessage, COUNTOF(rcMessage), rcFmt, pdli->szDll, pdli->dwLastError);
+ }
+ else
+ {
+ // Foramt the Windows error first.
+ if (!_FormatMessage(rcMessage, NumItems(rcMessage), pdli->dwLastError, pdli->szDll))
+ {
+ // Default to a hard coded error otherwise.
+ StringCchPrintf(rcMessage, COUNTOF(rcMessage), W("ERROR! Failed to delay load library %hs, Win32 error %d, Delay error: %d\n"),
+ pdli->szDll, pdli->dwLastError, dliNotify);
+ }
+ }
+
+#ifndef _ALPHA_
+ // for some bizarre reason, calling OutputDebugString during delay load in non-debug mode on Alpha
+ // kills program, so only do it when in debug mode ()
+#if defined (_DEBUG) || defined (__delay_load_trace__)
+ // Give some feedback to the developer.
+ wprintf(W("%s\n"), rcMessage);
+ WszOutputDebugString(rcMessage);
+#endif
+#endif
+
+ // Inform the user that we cannot continue execution anymore.
+ UtilMessageBoxCatastrophicNonLocalized(rcMessage, W("MSCOREE.DLL"), MB_ICONERROR | MB_OK, TRUE);
+ _ASSERTE(!"Failed to delay load library");
+}
+
+
+//*****************************************************************************
+// A library failed to load. This is always a bad thing.
+//*****************************************************************************
+void _FailGetProc(
+ unsigned dliNotify, // What event has occured, dli* flag.
+ DelayLoadInfo *pdli) // Description of the event.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ // We're allocating strings for the purposes of putting up a critical error box.
+ // Obviously, OOM's aren't going to be passed up to the caller.
+ FAULT_NOT_FATAL();
+
+ WCHAR rcMessage[_MAX_PATH+756]; // Message for display.
+ WCHAR rcProc[257] = {0}; // Name of procedure with error.
+ WCHAR rcFmt[500]; // 500 is the number used by excep.cpp for mscorrc resources.
+ HRESULT hr;
+
+ // Get a display name for debugging information.
+ if (pdli->dlp.fImportByName)
+ Wsz_mbstowcs(rcProc, pdli->dlp.szProcName, sizeof(rcProc)/sizeof(rcProc[0])-1);
+ else
+ StringCchPrintf(rcProc, COUNTOF(rcProc), W("Ordinal: %d"), pdli->dlp.dwOrdinal);
+
+ // Load a detailed error message from the resource file.
+ if (SUCCEEDED(hr = UtilLoadStringRC(MSEE_E_GETPROCFAILED, rcFmt, NumItems(rcFmt))))
+ {
+ StringCchPrintf(rcMessage, COUNTOF(rcMessage), rcFmt, rcProc, pdli->szDll, pdli->dwLastError);
+ }
+ else
+ {
+ if (!_FormatMessage(rcMessage, NumItems(rcMessage), pdli->dwLastError, pdli->szDll))
+ {
+ // Default to a hard coded error otherwise.
+ StringCchPrintf(rcMessage, COUNTOF(rcMessage), W("ERROR! Failed GetProcAddress() for %s, Win32 error %d, Delay error %d\n"),
+ rcProc, pdli->dwLastError, dliNotify);
+ }
+ }
+
+#ifndef ALPHA
+ // for some bizarre reason, calling OutputDebugString during delay load in non-debug mode on Alpha
+ // kills program, so only do it when in debug mode ()
+#if defined (_DEBUG) || defined (__delay_load_trace__)
+ // Give some feedback to the developer.
+ wprintf(W("%s"),rcMessage);
+ WszOutputDebugString(rcMessage);
+#endif
+#endif
+
+ {
+ // We are already in a catastrophic situation so we can tolerate faults as well as SO & GC mode violations to keep going.
+ CONTRACT_VIOLATION(FaultNotFatal | GCViolation | ModeViolation | SOToleranceViolation);
+
+ // Inform the user that we cannot continue execution anymore.
+ UtilMessageBoxCatastrophicNonLocalized(rcMessage, W("MSCOREE.DLL"), MB_ICONERROR | MB_OK, TRUE);
+ }
+ _ASSERTE(!"Failed to delay load GetProcAddress()");
+}
+
+
+
+HMODULE DoPreloadLibraryThrowing(LPCSTR szLibrary)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ HMODULE result=NULL;
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(ThrowHR(COR_E_STACKOVERFLOW));
+ DWORD dwLength = _MAX_PATH;
+ WCHAR pName[_MAX_PATH];
+ IfFailThrow(GetInternalSystemDirectory(pName, &dwLength));
+
+ MAKE_WIDEPTR_FROMANSI_NOTHROW(pwLibrary, szLibrary);
+ if ((pwLibrary == NULL) || ovadd_ge(dwLength, __lpwLibrary, _MAX_PATH-1))
+ ThrowHR(E_INVALIDARG);
+
+ wcscpy_s(pName+dwLength-1, COUNTOF(pName) - dwLength + 1, pwLibrary);
+ result = CLRLoadLibraryEx(pName, NULL, GetLoadWithAlteredSearchPathFlag());
+ END_SO_INTOLERANT_CODE;
+ return result;
+}
+
+//
+//********** Tracing code. ****************************************************
+//
+
+//*****************************************************************************
+// This routine is our Delay Load Helper. It will get called for every delay
+// load event that occurs while the application is running.
+//*****************************************************************************
+FARPROC __stdcall CorDelayLoadHook( // Always 0.
+ unsigned dliNotify, // What event has occured, dli* flag.
+ DelayLoadInfo *pdli) // Description of the event.
+{
+#ifdef _DEBUG
+ if (dliNotify == dliStartProcessing)
+ {
+ BOOL fThrows = TRUE;
+ if (_stricmp(pdli->szDll, "ole32.dll") == 0)
+ {
+ // SQL loads ole32.dll before starting CLR. For Whidbey release,
+ // we do not have time to get ole32.dll delay load cleaned.
+ fThrows = FALSE;
+ }
+ else if (_stricmp(pdli->szDll, "oleaut32.dll") == 0)
+ {
+ extern BOOL DelayLoadOleaut32CheckDisabled();
+ if (DelayLoadOleaut32CheckDisabled())
+ {
+ fThrows = FALSE;
+ }
+ else if ((!pdli->dlp.fImportByName && pdli->dlp.dwOrdinal == 6) ||
+ (pdli->dlp.fImportByName && strcmp(pdli->dlp.szProcName, "SysFreeString") == 0))
+ {
+ // BSTR has been created, which means oleaut32 should have been loaded.
+ // Delay load will not fail.
+ _ASSERTE (GetModuleHandleA("oleaut32.dll") != NULL);
+ fThrows = FALSE;
+ }
+ }
+ else if (_stricmp(pdli->szDll, "mscoree.dll") == 0) // If we are attempting to delay load mscoree.dll
+ {
+ if (GetModuleHandleA("mscoree.dll") != NULL) // and mscoree.dll has already been loaded
+ fThrows = FALSE; // then the delay load will not fail (and hence will not throw).
+ }
+ if (fThrows)
+ {
+ CONTRACTL
+ {
+ SO_TOLERANT;
+ THROWS;
+ }
+ CONTRACTL_END;
+ }
+ }
+#endif
+
+ //STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ // We're allocating strings for the purposes of putting up a critical error box.
+ // Obviously, OOM's aren't going to be passed up to the caller.
+ HMODULE result = NULL;
+ CONTRACT_VIOLATION(FaultNotFatal);
+
+
+ switch(dliNotify) {
+ case dliNotePreLoadLibrary:
+ if(pdli->szDll) {
+ result=DoPreloadLibraryThrowing(pdli->szDll);
+ }
+ break;
+ default:
+ break;
+ }
+
+#if defined (_DEBUG) || defined (__delay_load_trace__)
+ SO_NOT_MAINLINE_FUNCTION;
+ static int bBreak = false; // true to break on events.
+ static int bInit = false; // true after we've checked environment.
+ // If we've not yet looked at our environment, then do so.
+ if (!bInit)
+ {
+ WCHAR rcBreak[16];
+
+ // set DelayLoadBreak=[0|1]
+ if (WszGetEnvironmentVariable(W("DelayLoadBreak"), rcBreak, NumItems(rcBreak)))
+ {
+ // "1" means to break hard and display errors.
+ if (*rcBreak == '1')
+ bBreak = 1;
+ // "2" means no break, but display errors.
+ else if (*rcBreak == '2')
+ bBreak = 2;
+ else
+ bBreak = false;
+ }
+ bInit = true;
+ }
+
+ // Chose operation to perform based on operation.
+ switch (dliNotify)
+ {
+ // Called just before a load library takes place. Use this opportunity
+ // to display a debug trace message, and possible break if desired.
+ case dliNotePreLoadLibrary:
+ _DbgPreLoadLibrary(bBreak, pdli);
+ break;
+ }
+#endif
+ return (FARPROC) result;
+}
+
+
+#if defined (_DEBUG) || defined (__delay_load_trace__)
+
+//*****************************************************************************
+// Display a debug message so we know what's going on. Offer to break in
+// debugger if you want to see what call stack forced this library to load.
+//*****************************************************************************
+void _DbgPreLoadLibrary(
+ int bBreak, // true to break in debugger.
+ DelayLoadInfo *pdli) // Description of the event.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ // We're allocating strings for the purposes of putting up a critical error box.
+ // Obviously, OOM's aren't going to be passed up to the caller.
+ FAULT_NOT_FATAL();
+
+
+#ifdef _ALPHA_
+ // for some bizarre reason, calling OutputDebugString during delay load in non-debug mode on Alpha
+ // kills program, so only do it when in debug mode ()
+ if (! IsDebuggerPresent())
+ return;
+#endif
+
+ WCHAR rcMessage[_MAX_PATH*2]; // Message for display.
+
+ // Give some feedback to the developer.
+ StringCchPrintf(rcMessage, COUNTOF(rcMessage), W("Delay loading %hs\n"), pdli->szDll);
+ WszOutputDebugString(rcMessage);
+
+ if (bBreak)
+ {
+ wprintf(W("%s"), rcMessage);
+
+ if (bBreak == 1)
+ {
+ _ASSERTE(!"fyi - Delay loading library. Set DelayLoadBreak=0 to disable this assert.");
+ }
+ }
+}
+
+
+#endif // _DEBUG
+
+#endif // !FEATURE_CORESYSTEM