Source code of Windows XP (NT5)
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

//+---------------------------------------------------------------------------
//
// 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;
}