1 /* Copyright (C) 2013 Wildfire Games.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining
4 * a copy of this software and associated documentation files (the
5 * "Software"), to deal in the Software without restriction, including
6 * without limitation the rights to use, copy, modify, merge, publish,
7 * distribute, sublicense, and/or sell copies of the Software, and to
8 * permit persons to whom the Software is furnished to do so, subject to
9 * the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 /*
24 * DLL delay loading and notification
25 */
26
27 #include "precompiled.h"
28 #include "lib/sysdep/os/win/wdll_delay_load.h"
29
30 #include "lib/sysdep/cpu.h"
31 #include "lib/sysdep/os/win/win.h"
32 #include "lib/sysdep/os/win/winit.h"
33
34 WINIT_REGISTER_LATE_SHUTDOWN2(wdll_Shutdown); // last - DLLs are unloaded here
35
36 //-----------------------------------------------------------------------------
37 // delay loading (modified from VC7 DelayHlp.cpp and DelayImp.h)
38
39 #if MSC_VERSION && MSC_VERSION >= 1700
40 // FACILITY_VISUALCPP is already defined in winerror.h in VC2012
41 # undef FACILITY_VISUALCPP
42 #endif
43 #define FACILITY_VISUALCPP ((LONG)0x6d)
44 #define VcppException(sev,status) ((sev) | (FACILITY_VISUALCPP<<16) | status)
45
46 typedef IMAGE_THUNK_DATA * PImgThunkData;
47 typedef const IMAGE_THUNK_DATA * PCImgThunkData;
48 typedef DWORD RVA;
49
50 typedef struct ImgDelayDescr {
51 DWORD grAttrs; // attributes
52 RVA rvaDLLName; // RVA to dll name
53 RVA rvaHmod; // RVA of module handle
54 RVA rvaIAT; // RVA of the IAT
55 RVA rvaINT; // RVA of the INT
56 RVA rvaBoundIAT; // RVA of the optional bound IAT
57 RVA rvaUnloadIAT; // RVA of optional copy of original IAT
58 DWORD dwTimeStamp; // 0 if not bound,
59 // O.W. date/time stamp of DLL bound to (Old BIND)
60 } ImgDelayDescr, * PImgDelayDescr;
61
62 typedef const ImgDelayDescr * PCImgDelayDescr;
63
64 enum DLAttr { // Delay Load Attributes
65 dlattrRva = 0x1 // RVAs are used instead of pointers
66 // Having this set indicates a VC7.0
67 // and above delay load descriptor.
68 };
69
70 enum {
71 dliStartProcessing, // used to bypass or note helper only
72 dliNoteStartProcessing = dliStartProcessing,
73
74 dliNotePreLoadLibrary, // called just before LoadLibrary, can
75 // override w/ new HMODULE return val
76 dliNotePreGetProcAddress, // called just before GetProcAddress, can
77 // override w/ new FARPROC return value
78 dliFailLoadLib, // failed to load library, fix it by
79 // returning a valid HMODULE
80 dliFailGetProc, // failed to get proc address, fix it by
81 // returning a valid FARPROC
82 dliNoteEndProcessing // called after all processing is done, no
83 // no bypass possible at this point except
84 // by longjmp()/throw()/RaiseException.
85 };
86
87
88 typedef struct DelayLoadProc {
89 BOOL fImportByName;
90 union {
91 LPCSTR szProcName;
92 DWORD dwOrdinal;
93 };
94 } DelayLoadProc;
95
96
97 typedef struct DelayLoadInfo {
98 DWORD cb; // size of structure
99 PCImgDelayDescr pidd; // raw form of data (everything is there)
100 FARPROC * ppfn; // points to address of function to load
101 LPCSTR szDll; // name of dll
102 DelayLoadProc dlp; // name or ordinal of procedure
103 HMODULE hmodCur; // the hInstance of the library we have loaded
104 FARPROC pfnCur; // the actual function that will be called
105 DWORD dwLastError;// error received (if an error notification)
106 } DelayLoadInfo, * PDelayLoadInfo;
107
108
109 typedef FARPROC (WINAPI *PfnDliHook)(unsigned dliNotify, PDelayLoadInfo pdli);
110
111
112 //-----------------------------------------------------------------------------
113 // load notification
114
115 static WdllLoadNotify* notify_list;
116
wdll_add_notify(WdllLoadNotify * notify)117 void wdll_add_notify(WdllLoadNotify* notify)
118 {
119 notify->next = notify_list;
120 notify_list = notify;
121 }
122
notify_hook(unsigned dliNotify,PDelayLoadInfo pdli)123 static FARPROC WINAPI notify_hook(unsigned dliNotify, PDelayLoadInfo pdli)
124 {
125 if(dliNotify != dliNoteEndProcessing)
126 return 0;
127
128 for(WdllLoadNotify* n = notify_list; n; n = n->next)
129 if(strncasecmp(pdli->szDll, n->dll_name, strlen(n->dll_name)) == 0)
130 n->func();
131
132 return 0;
133 }
134
135
136 //-----------------------------------------------------------------------------
137 // hook
138
139 // The "notify hook" gets called for every call to the
140 // delay load helper. This allows a user to hook every call and
141 // skip the delay load helper entirely.
142 //
143 // dliNotify == {
144 // dliStartProcessing |
145 // dliNotePreLoadLibrary |
146 // dliNotePreGetProc |
147 // dliNoteEndProcessing}
148 // on this call.
149 //
150 EXTERN_C PfnDliHook __pfnDliNotifyHook2 = notify_hook;
151
152 // This is the failure hook, dliNotify = {dliFailLoadLib|dliFailGetProc}
153 EXTERN_C PfnDliHook __pfnDliFailureHook2 = 0;
154
155
156
157
158
159
160
161
162 #if !ICC_VERSION
163 #pragma intrinsic(strlen,memcmp,memcpy)
164 #endif
165
166 // utility function for calculating the index of the current import
167 // for all the tables (INT, BIAT, UIAT, and IAT).
168 inline unsigned
IndexFromPImgThunkData(PCImgThunkData pitdCur,PCImgThunkData pitdBase)169 IndexFromPImgThunkData(PCImgThunkData pitdCur, PCImgThunkData pitdBase) {
170 return (unsigned) (pitdCur - pitdBase);
171 }
172
173 // C++ template utility function for converting RVAs to pointers
174 //
175 extern "C" const IMAGE_DOS_HEADER __ImageBase;
176
177 template <class X>
PFromRva(RVA rva)178 X PFromRva(RVA rva) {
179 return X(PBYTE(&__ImageBase) + rva);
180 }
181
182 // structure definitions for the list of unload records
183 typedef struct UnloadInfo * PUnloadInfo;
184 typedef struct UnloadInfo {
185 PUnloadInfo puiNext;
186 PCImgDelayDescr pidd;
187 } UnloadInfo;
188
189 // utility function for calculating the count of imports given the base
190 // of the IAT. NB: this only works on a valid IAT!
191 inline unsigned
CountOfImports(PCImgThunkData pitdBase)192 CountOfImports(PCImgThunkData pitdBase) {
193 unsigned cRet = 0;
194 PCImgThunkData pitd = pitdBase;
195 while (pitd->u1.Function) {
196 pitd++;
197 cRet++;
198 }
199 return cRet;
200 }
201
202 extern "C" PUnloadInfo __puiHead = 0;
203
204 struct ULI : public UnloadInfo
205 {
ULIULI206 ULI(PCImgDelayDescr pidd_)
207 {
208 pidd = pidd_;
209 Link();
210 }
211
~ULIULI212 ~ULI() { Unlink(); }
213
operator newULI214 void* operator new(size_t cb) { return ::LocalAlloc(LPTR, cb); }
operator deleteULI215 void operator delete(void* pv) { ::LocalFree(pv); }
216
UnlinkULI217 void Unlink()
218 {
219 PUnloadInfo* ppui = &__puiHead;
220
221 while (*ppui && *ppui != this)
222 ppui = &((*ppui)->puiNext);
223 if (*ppui == this)
224 *ppui = puiNext;
225 }
226
LinkULI227 void Link()
228 {
229 puiNext = __puiHead;
230 __puiHead = this;
231 }
232 };
233
234
235 // For our own internal use, we convert to the old
236 // format for convenience.
237 //
238 struct InternalImgDelayDescr {
239 DWORD grAttrs; // attributes
240 LPCSTR szName; // pointer to dll name
241 HMODULE * phmod; // address of module handle
242 PImgThunkData pIAT; // address of the IAT
243 PCImgThunkData pINT; // address of the INT
244 PCImgThunkData pBoundIAT; // address of the optional bound IAT
245 PCImgThunkData pUnloadIAT; // address of optional copy of original IAT
246 DWORD dwTimeStamp; // 0 if not bound,
247 // O.W. date/time stamp of DLL bound to (Old BIND)
248 };
249
250 typedef InternalImgDelayDescr * PIIDD;
251 typedef const InternalImgDelayDescr * PCIIDD;
252
253 static inline PIMAGE_NT_HEADERS WINAPI
PinhFromImageBase(HMODULE hmod)254 PinhFromImageBase(HMODULE hmod) {
255 return PIMAGE_NT_HEADERS(PBYTE(hmod) + PIMAGE_DOS_HEADER(hmod)->e_lfanew);
256 }
257
258 static inline void WINAPI
OverlayIAT(PImgThunkData pitdDst,PCImgThunkData pitdSrc)259 OverlayIAT(PImgThunkData pitdDst, PCImgThunkData pitdSrc) {
260 memcpy(pitdDst, pitdSrc, CountOfImports(pitdDst) * sizeof IMAGE_THUNK_DATA);
261 }
262
263 static inline DWORD WINAPI
TimeStampOfImage(PIMAGE_NT_HEADERS pinh)264 TimeStampOfImage(PIMAGE_NT_HEADERS pinh) {
265 return pinh->FileHeader.TimeDateStamp;
266 }
267
268 static inline bool WINAPI
FLoadedAtPreferredAddress(PIMAGE_NT_HEADERS pinh,HMODULE hmod)269 FLoadedAtPreferredAddress(PIMAGE_NT_HEADERS pinh, HMODULE hmod) {
270 return UINT_PTR(hmod) == pinh->OptionalHeader.ImageBase;
271 }
272
273
__delayLoadHelper2(PCImgDelayDescr pidd,FARPROC * ppfnIATEntry)274 extern "C" FARPROC WINAPI __delayLoadHelper2(PCImgDelayDescr pidd, FARPROC* ppfnIATEntry)
275 {
276 // Set up some data we use for the hook procs but also useful for
277 // our own use
278 //
279 InternalImgDelayDescr idd = {
280 pidd->grAttrs,
281 PFromRva<LPCSTR>(pidd->rvaDLLName),
282 PFromRva<HMODULE*>(pidd->rvaHmod),
283 PFromRva<PImgThunkData>(pidd->rvaIAT),
284 PFromRva<PCImgThunkData>(pidd->rvaINT),
285 PFromRva<PCImgThunkData>(pidd->rvaBoundIAT),
286 PFromRva<PCImgThunkData>(pidd->rvaUnloadIAT),
287 pidd->dwTimeStamp
288 };
289
290 DelayLoadInfo dli = {
291 sizeof(DelayLoadInfo), pidd, ppfnIATEntry, idd.szName,
292 { 0 }, 0, 0, 0
293 };
294
295 if (!(idd.grAttrs & dlattrRva))
296 {
297 PDelayLoadInfo rgpdli[1] = { &dli };
298 RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_INVALID_PARAMETER), 0, 1, PULONG_PTR(rgpdli));
299 return 0;
300 }
301
302 HMODULE hmod = *idd.phmod;
303
304 // Calculate the index for the IAT entry in the import address table
305 // N.B. The INT entries are ordered the same as the IAT entries so
306 // the calculation can be done on the IAT side.
307 //
308 const unsigned iIAT = IndexFromPImgThunkData(PCImgThunkData(ppfnIATEntry), idd.pIAT);
309 const unsigned iINT = iIAT;
310
311 PCImgThunkData pitd = &(idd.pINT[iINT]);
312
313 dli.dlp.fImportByName = !IMAGE_SNAP_BY_ORDINAL(pitd->u1.Ordinal);
314
315 if (dli.dlp.fImportByName)
316 dli.dlp.szProcName = LPCSTR(PFromRva<PIMAGE_IMPORT_BY_NAME>(RVA(UINT_PTR(pitd->u1.AddressOfData)))->Name);
317 else
318 dli.dlp.dwOrdinal = DWORD(IMAGE_ORDINAL(pitd->u1.Ordinal));
319
320 // Call the initial hook. If it exists and returns a function pointer,
321 // abort the rest of the processing and just return it for the call.
322 //
323 FARPROC pfnRet = NULL;
324
325 if (__pfnDliNotifyHook2) {
326 pfnRet = ((*__pfnDliNotifyHook2)(dliStartProcessing, &dli));
327
328 if (pfnRet != NULL)
329 goto HookBypass;
330 }
331
332 // Check to see if we need to try to load the library.
333 //
334 if (hmod == 0) {
335 if (__pfnDliNotifyHook2) {
336 hmod = HMODULE(((*__pfnDliNotifyHook2)(dliNotePreLoadLibrary, &dli)));
337 }
338 if (hmod == 0) {
339 hmod = ::LoadLibraryA(dli.szDll);
340 }
341 if (hmod == 0) {
342 dli.dwLastError = ::GetLastError();
343 if (__pfnDliFailureHook2) {
344 // when the hook is called on LoadLibrary failure, it will
345 // return 0 for failure and an hmod for the lib if it fixed
346 // the problem.
347 //
348 hmod = HMODULE((*__pfnDliFailureHook2)(dliFailLoadLib, &dli));
349 }
350
351 if (hmod == 0) {
352 PDelayLoadInfo rgpdli[1] = { &dli };
353
354 RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND),
355 0, 1, PULONG_PTR(rgpdli));
356
357 // If we get to here, we blindly assume that the handler of the exception
358 // has magically fixed everything up and left the function pointer in
359 // dli.pfnCur.
360 //
361 return dli.pfnCur;
362 }
363 }
364
365 // Store the library handle. If it is already there, we infer
366 // that another thread got there first, and we need to do a
367 // FreeLibrary() to reduce the refcount
368 //
369 HMODULE hmodT = HMODULE(InterlockedExchangePointer((PVOID *) idd.phmod, PVOID(hmod)));
370 if (hmodT != hmod) {
371 // add lib to unload list if we have unload data
372 if (pidd->rvaUnloadIAT) {
373 new ULI(pidd);
374 }
375 }
376 else {
377 ::FreeLibrary(hmod);
378 }
379
380 }
381
382 // Go for the procedure now.
383 //
384 dli.hmodCur = hmod;
385 if (__pfnDliNotifyHook2) {
386 pfnRet = (*__pfnDliNotifyHook2)(dliNotePreGetProcAddress, &dli);
387 }
388 if (pfnRet == 0) {
389 if (pidd->rvaBoundIAT && pidd->dwTimeStamp) {
390 // bound imports exist...check the timestamp from the target image
391 //
392 PIMAGE_NT_HEADERS pinh(PinhFromImageBase(hmod));
393
394 if (pinh->Signature == IMAGE_NT_SIGNATURE &&
395 TimeStampOfImage(pinh) == idd.dwTimeStamp &&
396 FLoadedAtPreferredAddress(pinh, hmod)) {
397
398 // Everything is good to go, if we have a decent address
399 // in the bound IAT!
400 //
401 pfnRet = FARPROC(UINT_PTR(idd.pBoundIAT[iIAT].u1.Function));
402 if (pfnRet != 0) {
403 goto SetEntryHookBypass;
404 }
405 }
406 }
407
408 pfnRet = ::GetProcAddress(hmod, dli.dlp.szProcName);
409 }
410
411 if (pfnRet == 0) {
412 dli.dwLastError = ::GetLastError();
413 if (__pfnDliFailureHook2) {
414 // when the hook is called on GetProcAddress failure, it will
415 // return 0 on failure and a valid proc address on success
416 //
417 pfnRet = (*__pfnDliFailureHook2)(dliFailGetProc, &dli);
418 }
419 if (pfnRet == 0) {
420 PDelayLoadInfo rgpdli[1] = { &dli };
421
422 RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND),
423 0, 1, PULONG_PTR(rgpdli));
424
425 // If we get to here, we blindly assume that the handler of the exception
426 // has magically fixed everything up and left the function pointer in
427 // dli.pfnCur.
428 //
429 pfnRet = dli.pfnCur;
430 }
431 }
432
433 SetEntryHookBypass:
434 *ppfnIATEntry = pfnRet;
435
436 HookBypass:
437 if (__pfnDliNotifyHook2) {
438 dli.dwLastError = 0;
439 dli.hmodCur = hmod;
440 dli.pfnCur = pfnRet;
441 (*__pfnDliNotifyHook2)(dliNoteEndProcessing, &dli);
442 }
443 return pfnRet;
444 }
445
446
UnloadAllDlls()447 static void UnloadAllDlls()
448 {
449 PUnloadInfo pui;
450
451 // free all DLLs (avoid BoundsChecker warning)
452 while((pui = __puiHead) != 0)
453 if(pui->pidd->rvaUnloadIAT)
454 {
455 PCImgDelayDescr pidd = pui->pidd;
456 HMODULE* phmod = PFromRva<HMODULE*>(pidd->rvaHmod);
457 HMODULE hmod = *phmod;
458
459 OverlayIAT(
460 PFromRva<PImgThunkData>(pidd->rvaIAT),
461 PFromRva<PCImgThunkData>(pidd->rvaUnloadIAT)
462 );
463 ::FreeLibrary(hmod);
464 *phmod = NULL;
465
466 delete (ULI*)pui; // changes __puiHead!
467 }
468 }
469
470 //-----------------------------------------------------------------------------
471
wdll_Shutdown()472 static Status wdll_Shutdown()
473 {
474 UnloadAllDlls();
475 return INFO::OK;
476 }
477