You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
463 lines
12 KiB
463 lines
12 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1997.
|
|
//
|
|
// File: D L O A D . C P P
|
|
//
|
|
// Contents: Delay Load Failure Hook
|
|
//
|
|
// Notes: This DLL implements a form of exception handler (a failure
|
|
// hook, really) that is called by the delay-load section of
|
|
// the loader. This DLL implements delay-load handlers for
|
|
// standard APIs by returning an error code specific to the
|
|
// API that failed to load. This allows clients of those APIs
|
|
// to safely delay load the DLLs that implement those APIs
|
|
// without having to worry about the success or failure of the
|
|
// delay load operation. Failures in the delay load operation
|
|
// cause the appropriate stub (implemented in this DLL) to be
|
|
// invoked which simply returns an error code specific to the
|
|
// API being called. (Other interface semantics such as
|
|
// setting output parameters are also performed.)
|
|
//
|
|
// To Use: 1. Add the following line to one of your source modules.
|
|
//
|
|
// PfnDliHook __pfnDliFailureHook = DelayLoadFailureHook;
|
|
//
|
|
// 2. Define a global variable like the following:
|
|
//
|
|
// HANDLE g_hBaseDllHandle;
|
|
//
|
|
// And set it equal to your Dlls instance handle from
|
|
// DLL_PROCESS_ATTACH of DllMain.
|
|
//
|
|
// Author: shaunco 19 May 1998
|
|
//
|
|
// Revised by: pritobla 23 Novemver 1998 (removed C-Runtime calls/RtlAssert
|
|
// and modified the "To Use" section)
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
#if DBG
|
|
VOID
|
|
WINAPI
|
|
AssertDelayLoadFailureMapsAreSorted (
|
|
VOID
|
|
)
|
|
{
|
|
const DLOAD_DLL_ENTRY* pDll;
|
|
const DLOAD_PROCNAME_MAP* pProcNameMap;
|
|
const DLOAD_ORDINAL_MAP* pOrdinalMap;
|
|
|
|
CHAR szMsg[1024];
|
|
UINT iDll, iProcName, iOrdinal;
|
|
INT nRet;
|
|
|
|
for (iDll = 0; iDll < g_DllMap.NumberOfEntries; iDll++)
|
|
{
|
|
if (iDll >= 1)
|
|
{
|
|
nRet = StrCmpIA (
|
|
g_DllMap.pDllEntry[iDll].pszDll,
|
|
g_DllMap.pDllEntry[iDll-1].pszDll);
|
|
|
|
if (nRet <= 0)
|
|
{
|
|
wnsprintfA(szMsg, countof(szMsg),
|
|
"dload: rows %u and %u are out of order in dload!g_DllMap",
|
|
iDll-1, iDll);
|
|
|
|
AssertFailedA(__FILE__, __LINE__, szMsg, TRUE);
|
|
}
|
|
}
|
|
|
|
pDll = g_DllMap.pDllEntry + iDll;
|
|
pProcNameMap = pDll->pProcNameMap;
|
|
pOrdinalMap = pDll->pOrdinalMap;
|
|
|
|
if (pProcNameMap)
|
|
{
|
|
ASSERT (pProcNameMap->NumberOfEntries);
|
|
|
|
for (iProcName = 0;
|
|
iProcName < pProcNameMap->NumberOfEntries;
|
|
iProcName++)
|
|
{
|
|
if (iProcName >= 1)
|
|
{
|
|
nRet = StrCmpA (
|
|
pProcNameMap->pProcNameEntry[iProcName].pszProcName,
|
|
pProcNameMap->pProcNameEntry[iProcName-1].pszProcName);
|
|
|
|
if (nRet <= 0)
|
|
{
|
|
wnsprintfA(szMsg, countof(szMsg),
|
|
"dload: rows %u and %u of pProcNameMap are out "
|
|
"of order in dload!g_DllMap for pszDll=%s",
|
|
iProcName-1, iProcName, pDll->pszDll);
|
|
|
|
AssertFailedA (__FILE__, __LINE__, szMsg, TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pOrdinalMap)
|
|
{
|
|
ASSERT (pOrdinalMap->NumberOfEntries);
|
|
|
|
for (iOrdinal = 0;
|
|
iOrdinal < pOrdinalMap->NumberOfEntries;
|
|
iOrdinal++)
|
|
{
|
|
if (iOrdinal >= 1)
|
|
{
|
|
if (pOrdinalMap->pOrdinalEntry[iOrdinal].dwOrdinal <=
|
|
pOrdinalMap->pOrdinalEntry[iOrdinal-1].dwOrdinal)
|
|
{
|
|
wnsprintfA(szMsg, countof(szMsg),
|
|
"dload: rows %u and %u of pOrdinalMap are out "
|
|
"of order in dload!g_DllMap for pszDll=%s",
|
|
iOrdinal-1, iOrdinal, pDll->pszDll);
|
|
|
|
AssertFailedA(__FILE__, __LINE__, szMsg, TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // DBG
|
|
|
|
|
|
const DLOAD_DLL_ENTRY*
|
|
FindDll (
|
|
LPCSTR pszDll
|
|
)
|
|
{
|
|
const DLOAD_DLL_ENTRY* pDll = NULL;
|
|
|
|
INT nResult;
|
|
|
|
// These must be signed integers for the following binary search
|
|
// to work correctly when iMiddle == 0 and nResult < 0.
|
|
//
|
|
INT iLow;
|
|
INT iMiddle;
|
|
INT iHigh;
|
|
|
|
ASSERT(pszDll);
|
|
ASSERT(StrLenA(pszDll) <= MAX_PATH);
|
|
|
|
iLow = 0;
|
|
iHigh = g_DllMap.NumberOfEntries - 1;
|
|
while (iHigh >= iLow)
|
|
{
|
|
iMiddle = (iLow + iHigh) / 2;
|
|
nResult = StrCmpIA (pszDll, g_DllMap.pDllEntry[iMiddle].pszDll);
|
|
|
|
if (nResult < 0)
|
|
{
|
|
iHigh = iMiddle - 1;
|
|
}
|
|
else if (nResult > 0)
|
|
{
|
|
iLow = iMiddle + 1;
|
|
}
|
|
else
|
|
{
|
|
ASSERT (0 == nResult);
|
|
pDll = &g_DllMap.pDllEntry[iMiddle];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pDll;
|
|
}
|
|
|
|
FARPROC
|
|
LookupHandlerByName (
|
|
LPCSTR pszProcName,
|
|
const DLOAD_PROCNAME_MAP* pMap
|
|
)
|
|
{
|
|
FARPROC pfnHandler = NULL;
|
|
|
|
INT nResult;
|
|
|
|
// These must be signed integers for the following binary search
|
|
// to work correctly when iMiddle == 0 and nResult < 0.
|
|
//
|
|
INT iLow;
|
|
INT iMiddle;
|
|
INT iHigh;
|
|
|
|
ASSERT (pszProcName);
|
|
|
|
iLow = 0;
|
|
iHigh = pMap->NumberOfEntries - 1;
|
|
while (iHigh >= iLow)
|
|
{
|
|
iMiddle = (iLow + iHigh) / 2;
|
|
nResult = StrCmpA (
|
|
pszProcName,
|
|
pMap->pProcNameEntry[iMiddle].pszProcName);
|
|
|
|
if (nResult < 0)
|
|
{
|
|
iHigh = iMiddle - 1;
|
|
}
|
|
else if (nResult > 0)
|
|
{
|
|
iLow = iMiddle + 1;
|
|
}
|
|
else
|
|
{
|
|
ASSERT (0 == nResult);
|
|
pfnHandler = pMap->pProcNameEntry[iMiddle].pfnProc;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pfnHandler;
|
|
}
|
|
|
|
FARPROC
|
|
LookupHandlerByOrdinal (
|
|
DWORD dwOrdinal,
|
|
const DLOAD_ORDINAL_MAP* pMap
|
|
)
|
|
{
|
|
FARPROC pfnHandler = NULL;
|
|
|
|
DWORD dwOrdinalProbe;
|
|
|
|
// These must be signed integers for the following binary search
|
|
// to work correctly when iMiddle == 0 and dwOrdinal < dwOrdinalProbe.
|
|
//
|
|
INT iLow;
|
|
INT iMiddle;
|
|
INT iHigh;
|
|
|
|
iLow = 0;
|
|
iHigh = pMap->NumberOfEntries - 1;
|
|
while (iHigh >= iLow)
|
|
{
|
|
iMiddle = (iLow + iHigh) / 2;
|
|
dwOrdinalProbe = pMap->pOrdinalEntry[iMiddle].dwOrdinal;
|
|
|
|
if (dwOrdinal < dwOrdinalProbe)
|
|
{
|
|
iHigh = iMiddle - 1;
|
|
}
|
|
else if (dwOrdinal > dwOrdinalProbe)
|
|
{
|
|
iLow = iMiddle + 1;
|
|
}
|
|
else
|
|
{
|
|
ASSERT (dwOrdinal == dwOrdinalProbe);
|
|
pfnHandler = pMap->pOrdinalEntry[iMiddle].pfnProc;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pfnHandler;
|
|
}
|
|
|
|
FARPROC
|
|
LookupHandler (
|
|
PDelayLoadInfo pDelayInfo
|
|
)
|
|
{
|
|
FARPROC pfnHandler = NULL;
|
|
const DLOAD_DLL_ENTRY* pDll;
|
|
|
|
ASSERT (pDelayInfo);
|
|
|
|
#if DBG
|
|
AssertDelayLoadFailureMapsAreSorted();
|
|
#endif
|
|
|
|
// Find the DLL record if we have one.
|
|
//
|
|
pDll = FindDll (pDelayInfo->szDll);
|
|
if (pDll)
|
|
{
|
|
// Now find the handler whether it be by name or ordinal.
|
|
//
|
|
if (pDelayInfo->dlp.fImportByName &&
|
|
pDll->pProcNameMap)
|
|
{
|
|
pfnHandler = LookupHandlerByName (
|
|
pDelayInfo->dlp.szProcName,
|
|
pDll->pProcNameMap);
|
|
}
|
|
else if (pDll->pOrdinalMap)
|
|
{
|
|
pfnHandler = LookupHandlerByOrdinal (
|
|
pDelayInfo->dlp.dwOrdinal,
|
|
pDll->pOrdinalMap);
|
|
}
|
|
}
|
|
|
|
return pfnHandler;
|
|
}
|
|
|
|
|
|
#if DBG
|
|
|
|
#define DBG_ERROR 0
|
|
#define DBG_INFO 1
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Trace a message to the debug console. Prefix with who we are so
|
|
// people know who to contact.
|
|
//
|
|
INT
|
|
__cdecl
|
|
DbgTrace (
|
|
INT nLevel,
|
|
PCSTR Format,
|
|
...
|
|
)
|
|
{
|
|
INT cch = 0;
|
|
|
|
if (nLevel >= DBG_INFO)
|
|
{
|
|
CHAR szBuf [1024];
|
|
va_list argptr;
|
|
|
|
va_start(argptr, Format);
|
|
cch = wvnsprintfA(szBuf, countof(szBuf), Format, argptr);
|
|
va_end(argptr);
|
|
|
|
OutputDebugStringA("dload: ");
|
|
OutputDebugStringA(szBuf);
|
|
}
|
|
|
|
return cch;
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
FARPROC
|
|
WINAPI
|
|
DelayLoadFailureHook (
|
|
UINT unReason,
|
|
PDelayLoadInfo pDelayInfo
|
|
)
|
|
{
|
|
FARPROC ReturnValue = NULL;
|
|
|
|
// According to the docs, this parameter is always supplied.
|
|
//
|
|
ASSERT (pDelayInfo);
|
|
|
|
// Trace some potentially useful information about why we were called.
|
|
//
|
|
#if DBG
|
|
if (pDelayInfo->dlp.fImportByName)
|
|
{
|
|
DbgTrace (DBG_INFO,
|
|
"%s: Dll=%s, ProcName=%s\r\n",
|
|
(dliFailLoadLib == unReason) ? "FailLoadLib" : "FailGetProc",
|
|
pDelayInfo->szDll,
|
|
pDelayInfo->dlp.szProcName);
|
|
}
|
|
else
|
|
{
|
|
DbgTrace (DBG_INFO,
|
|
"%s: Dll=%s, Ordinal=%u\r\n",
|
|
(dliFailLoadLib == unReason) ? "FailLoadLib" : "FailGetProc",
|
|
pDelayInfo->szDll,
|
|
pDelayInfo->dlp.dwOrdinal);
|
|
}
|
|
#endif
|
|
|
|
// For a failed LoadLibrary, we will return the HINSTANCE of this DLL.
|
|
// This will cause the loader to try a GetProcAddress on our DLL for the
|
|
// function. This will subsequently fail and then we will be called
|
|
// for dliFailGetProc below.
|
|
//
|
|
if (dliFailLoadLib == unReason)
|
|
{
|
|
ReturnValue = (FARPROC)g_hBaseDllHandle;
|
|
}
|
|
|
|
// The loader is asking us to return a pointer to a procedure.
|
|
// Lookup the handler for this DLL/procedure and, if found, return it.
|
|
// If we don't find it, we'll assert with a message about the missing
|
|
// handler.
|
|
//
|
|
else if (dliFailGetProc == unReason)
|
|
{
|
|
FARPROC pfnHandler;
|
|
|
|
// Try to find an error handler for the DLL/procedure.
|
|
//
|
|
pfnHandler = LookupHandler (pDelayInfo);
|
|
|
|
if (pfnHandler)
|
|
{
|
|
#if DBG
|
|
DbgTrace (DBG_INFO,
|
|
"Returning handler function at address 0x%08x\r\n",
|
|
(LONG_PTR)pfnHandler);
|
|
#endif
|
|
|
|
// Do this on behalf of the handler now that it is about to
|
|
// be called.
|
|
//
|
|
SetLastError (ERROR_MOD_NOT_FOUND);
|
|
}
|
|
|
|
#if DBG
|
|
else
|
|
{
|
|
CHAR szMsg[MAX_PATH];
|
|
|
|
if (pDelayInfo->dlp.fImportByName)
|
|
{
|
|
wnsprintfA(szMsg, countof(szMsg),
|
|
"No delayload handler found for Dll=%s, ProcName=%s\r\n",
|
|
pDelayInfo->szDll,
|
|
pDelayInfo->dlp.szProcName);
|
|
}
|
|
else
|
|
{
|
|
wnsprintfA(szMsg, countof(szMsg),
|
|
"No delayload handler found for Dll=%s, Ordinal=%u\r\n",
|
|
pDelayInfo->szDll,
|
|
pDelayInfo->dlp.dwOrdinal);
|
|
}
|
|
|
|
AssertFailedA(__FILE__, __LINE__, szMsg, TRUE);
|
|
}
|
|
#endif
|
|
|
|
ReturnValue = pfnHandler;
|
|
}
|
|
|
|
#if DBG
|
|
else
|
|
{
|
|
ASSERT (NULL == ReturnValue);
|
|
|
|
DbgTrace (DBG_INFO,
|
|
"Unknown unReason (%u) passed to DelayLoadFailureHook. Ignoring.\r\n",
|
|
unReason);
|
|
}
|
|
#endif
|
|
|
|
return ReturnValue;
|
|
}
|