1 //
2 // DelayHlp.cpp
3 //
4 //  Copyright (c) Microsoft Corporation.  All rights reserved.
5 //
6 //  Implement the delay load helper routines.
7 //
8
9 // Build instructions
10 // cl -c -O1 -Z7 -Zl -W3 delayhlp.cpp
11 //
12 // For ISOLATION_AWARE_ENABLED calls to LoadLibrary(), you will need to add
13 // a definition for ISOLATION_AWARE_ENABLED to the command line above, eg:
14 // cl -c -O1 -Z7 -Zl -W3 -DISOLATION_AWARE_ENABLED=1 delayhlp.cpp
15 //
16 //
17 // Then, you can either link directly with this new object file, or replace the one in
18 // delayimp.lib with your new one, eg:
19 // lib /out:delayimp.lib delayhlp.obj
20 //
21
22
23 #define WIN32_LEAN_AND_MEAN
24 #define STRICT
25 #include <windows.h>
26
27 #include "DelayImp.h"
28
29 //
30 // Local copies of strlen, memcmp, and memcpy to make sure we do not need the CRT
31 //
32
33 static inline size_t
34 __strlen(const char * sz) {
35       const char *szEnd = sz;
36
37       while( *szEnd++ ) {
38              ;
39              }
40
41       return szEnd - sz - 1;
42       }
43
44 static inline int
45 __memcmp(const void * pv1, const void * pv2, size_t cb) {
46       if (!cb) {
47              return 0;
48              }
49
50       while ( --cb && *(char *)pv1 == *(char *)pv2 ) {
51              pv1 = (char *)pv1 + 1;
52              pv2 = (char *)pv2 + 1;
53              }
54
55       return  *((unsigned char *)pv1) - *((unsigned char *)pv2);
56       }
57
58 static inline void *
59 __memcpy(void * pvDst, const void * pvSrc, size_t cb) {
60
61       void * pvRet = pvDst;
62
63       //
64       // copy from lower addresses to higher addresses
65       //
66       while (cb--) {
67              *(char *)pvDst = *(char *)pvSrc;
68              pvDst = (char *)pvDst + 1;
69              pvSrc = (char *)pvSrc + 1;
70              }
71
72       return pvRet;
73       }
74
75
76 // utility function for calculating the index of the current import
77 // for all the tables (INT, BIAT, UIAT, and IAT).
78 inline unsigned
79 IndexFromPImgThunkData(PCImgThunkData pitdCur, PCImgThunkData pitdBase) {
80       return (unsigned) (pitdCur - pitdBase);
81       }
82
83 // C++ template utility function for converting RVAs to pointers
84 //
85 #if defined(_M_IA64)
86 #pragma section(".base", long, read)
87 extern "C"
88 __declspec(allocate(".base"))
89 const IMAGE_DOS_HEADER __ImageBase;
90 #else
91 extern "C"
92 const IMAGE_DOS_HEADER __ImageBase;
93 #endif
94
95 template <class X>
96 X PFromRva(RVA rva) {
97       return X(PBYTE(&__ImageBase) + rva);
98       }
99
100 // structure definitions for the list of unload records
101 typedef struct UnloadInfo * PUnloadInfo;
102 typedef struct UnloadInfo {
103       PUnloadInfo        puiNext;
104       PCImgDelayDescr pidd;
105       } UnloadInfo;
106
107 // utility function for calculating the count of imports given the base
108 // of the IAT.  NB: this only works on a valid IAT!
109 inline unsigned
110 CountOfImports(PCImgThunkData pitdBase) {
111       unsigned             cRet = 0;
112       PCImgThunkData  pitd = pitdBase;
113       while (pitd->u1.Function) {
114              pitd++;
115              cRet++;
116              }
117       return cRet;
118       }
119
120 extern "C"
121 PUnloadInfo __puiHead = 0;
122
123 struct ULI : public UnloadInfo {
124       ULI(PCImgDelayDescr pidd_) {
125              pidd = pidd_;
126              Link();
127              }
128
129       ~ULI() {
130              Unlink();
131              }
132
133       void *
134       operator new(size_t cb) {
135              return ::LocalAlloc(LPTR, cb);
136              }
137
138       void
139       operator delete(void * pv) {
140              ::LocalFree(pv);
141              }
142
143       void
144       Unlink() {
145              PUnloadInfo *     ppui = &__puiHead;
146
147              while (*ppui && *ppui != this) {
148                     ppui = &((*ppui)->puiNext);
149                     }
150              if (*ppui == this) {
151                     *ppui = puiNext;
152                     }
153              }
154
155       void
156       Link() {
157              puiNext = __puiHead;
158              __puiHead = this;
159              }
160       };
161
162 // For our own internal use, we convert to the old
163 // format for convenience.
164 //
165 struct InternalImgDelayDescr {
166       DWORD                  grAttrs;             // attributes
167       LPCSTR                szName;               // pointer to dll name
168       HMODULE *           phmod;                // address of module handle
169       PImgThunkData     pIAT;                  // address of the IAT
170       PCImgThunkData  pINT;                  // address of the INT
171       PCImgThunkData  pBoundIAT;          // address of the optional bound IAT
172       PCImgThunkData  pUnloadIAT;        // address of optional copy of original IAT
173       DWORD                  dwTimeStamp;      // 0 if not bound,
174                                                             // O.W. date/time stamp of DLL bound to (Old BIND)
175       };
176
177 typedef InternalImgDelayDescr *               PIIDD;
178 typedef const InternalImgDelayDescr *     PCIIDD;
179
180 static inline
181 PIMAGE_NT_HEADERS WINAPI
182 PinhFromImageBase(HMODULE hmod) {
183       return PIMAGE_NT_HEADERS(PBYTE(hmod) + PIMAGE_DOS_HEADER(hmod)->e_lfanew);
184       }
185
186 static inline
187 void WINAPI
188 OverlayIAT(PImgThunkData pitdDst, PCImgThunkData pitdSrc) {
189       __memcpy(pitdDst, pitdSrc, CountOfImports(pitdDst) * sizeof IMAGE_THUNK_DATA);
190       }
191
192 static inline
193 DWORD WINAPI
194 TimeStampOfImage(PIMAGE_NT_HEADERS pinh) {
195       return pinh->FileHeader.TimeDateStamp;
196       }
197
198 static inline
199 bool WINAPI
200 FLoadedAtPreferredAddress(PIMAGE_NT_HEADERS pinh, HMODULE hmod) {
201       return UINT_PTR(hmod) == pinh->OptionalHeader.ImageBase;
202       }
203
204
205 // Do the InterlockedExchange magic
206 //
207 #ifdef  _M_IX86
208
209 #undef  InterlockedExchangePointer
210 #define InterlockedExchangePointer(Target, Value) \
211       (PVOID)(LONG_PTR)InterlockedExchange((PLONG)(Target), (LONG)(LONG_PTR)(Value))
212
213 #if (_MSC_VER >= 1300)
214 typedef __w64 unsigned long *PULONG_PTR;
215 #else
216 typedef unsigned long *PULONG_PTR;
217 #endif
218
219 #endif
220
221 extern "C"
222 FARPROC WINAPI
223 __delayLoadHelper2(
224       PCImgDelayDescr        pidd,
225       FARPROC *                  ppfnIATEntry
226       ) {
227
228       // Set up some data we use for the hook procs but also useful for
229       // our own use
230       //
231       InternalImgDelayDescr     idd = {
232              pidd->grAttrs,
233              PFromRva<LPCSTR>(pidd->rvaDLLName),
234              PFromRva<HMODULE*>(pidd->rvaHmod),
235              PFromRva<PImgThunkData>(pidd->rvaIAT),
236              PFromRva<PCImgThunkData>(pidd->rvaINT),
237              PFromRva<PCImgThunkData>(pidd->rvaBoundIAT),
238              PFromRva<PCImgThunkData>(pidd->rvaUnloadIAT),
239              pidd->dwTimeStamp
240              };
241
242       DelayLoadInfo     dli = {
243              sizeof DelayLoadInfo,
244              pidd,
245              ppfnIATEntry,
246              idd.szName,
247                     { 0 },
248              0,
249              0,
250              0
251              };
252
253       if (0 == (idd.grAttrs & dlattrRva)) {
254              PDelayLoadInfo  rgpdli[1] = { &dli };
255
256              RaiseException(
257                     VcppException(ERROR_SEVERITY_ERROR, ERROR_INVALID_PARAMETER),
258                     0,
259                     1,
260                     PULONG_PTR(rgpdli)
Lines 261 ... 469 are skipped.
470       if (pinh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].Size) {
471              PCImgDelayDescr        pidd;
472
473              pidd = PFromRva<PCImgDelayDescr>(
474                     pinh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress
475                     );
476
477              // Check all of the dlls listed up to the NULL one.
478              //
479              while (pidd->rvaDLLName) {
480                     // Check to see if it is the DLL we want to load.
481                     // Intentionally case sensitive to avoid complication of using the CRT
482                     // for those that don't use the CRT...the user can replace this with
483                     // a variant of a case insenstive comparison routine.
484                     //
485                     LPCSTR  szDllCur = PFromRva<LPCSTR>(pidd->rvaDLLName);
486                     size_t  cchDllCur = __strlen(szDllCur);
487                     if (cchDllCur == __strlen(szDll) && __memcmp(szDll, szDllCur, cchDllCur) == 0) {
488                           // We found it, so break out with pidd and szDllCur set appropriately
489                           //
490                           break;
491                           }
492                     
493                     // Advance to the next delay import descriptor
494                     //
495                     pidd++;
496                     }
497              
498              if (pidd->rvaDLLName) {
499                     // Found a matching DLL name, now process it.
500                     //
501                     // Set up the internal structure
502                     //
503                     FARPROC *     ppfnIATEntry = PFromRva<FARPROC*>(pidd->rvaIAT);
504                     size_t          cpfnIATEntries = CountOfImports(PCImgThunkData(ppfnIATEntry));
505                     FARPROC *     ppfnIATEntryMax = ppfnIATEntry + cpfnIATEntries;
506
507                     for (;ppfnIATEntry < ppfnIATEntryMax; ppfnIATEntry++) {
508                           __delayLoadHelper2(pidd, ppfnIATEntry);
509                           }
510
511                     // Done, indicate some semblance of success
512                     //
513                     hrRet = S_OK;
514                     }
515              }
516       return hrRet;
517 }
518