3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
11 #include <sys/param.h>
20 #define rva(coerce, base, addr) (coerce) ((char *) (base) + (addr))
21 #define rvacyg(coerce, addr) rva (coerce, cygwin_hmodule, addr)
25 const char *name
; // Function name, e.g. "DirectDrawCreateEx".
26 const void *hookfn
; // Address of your function.
27 void *origfn
; // Stored by HookAPICalls, the address of the original function.
30 /* Given an HMODULE, returns a pointer to the PE header. */
31 static PIMAGE_NT_HEADERS
32 PEHeaderFromHModule (HMODULE hModule
)
34 if (PIMAGE_DOS_HEADER (hModule
) ->e_magic
!= IMAGE_DOS_SIGNATURE
)
37 PIMAGE_NT_HEADERS pNTHeader
=
38 PIMAGE_NT_HEADERS (PBYTE (hModule
)
39 + PIMAGE_DOS_HEADER (hModule
) ->e_lfanew
);
40 if (pNTHeader
->Signature
!= IMAGE_NT_SIGNATURE
)
43 /* Return valid PIMAGE_NT_HEADERS only for supported architectures. */
44 switch (pNTHeader
->FileHeader
.Machine
)
46 case IMAGE_FILE_MACHINE_AMD64
:
56 rvadelta (PIMAGE_NT_HEADERS pnt
, DWORD import_rva
, DWORD
&max_size
)
58 PIMAGE_SECTION_HEADER section
= (PIMAGE_SECTION_HEADER
) (pnt
+ 1);
59 for (int i
= 0; i
< pnt
->FileHeader
.NumberOfSections
; i
++)
60 if (section
[i
].VirtualAddress
<= import_rva
61 && (section
[i
].VirtualAddress
+ section
[i
].Misc
.VirtualSize
) > import_rva
)
63 max_size
= section
[i
].SizeOfRawData
64 - (import_rva
- section
[i
].VirtualAddress
);
65 return section
[i
].VirtualAddress
- section
[i
].PointerToRawData
;
70 /* This function is only used for the current architecture.
71 Just the size of the IMAGE_THUNK_DATA Function member differs. */
73 putmem (PIMAGE_THUNK_DATA pi
, const void *hookfn
)
75 #define THUNK_FUNC_TYPE ULONGLONG
78 if (!VirtualProtect (pi
, sizeof (THUNK_FUNC_TYPE
), PAGE_READWRITE
, &ofl
) )
81 void *origfn
= (void *) pi
->u1
.Function
;
82 pi
->u1
.Function
= (THUNK_FUNC_TYPE
) hookfn
;
84 VirtualProtect (pi
, sizeof (THUNK_FUNC_TYPE
), ofl
, &ofl
);
88 /* Builds stubs for and redirects the IAT for one DLL (pImportDesc)
89 This function is only used for the current architecture. */
92 RedirectIAT (function_hook
& fh
, PIMAGE_IMPORT_DESCRIPTOR pImportDesc
,
95 // If no import names table, we can't redirect this, so bail
96 if (pImportDesc
->OriginalFirstThunk
== 0)
99 /* import address table */
100 PIMAGE_THUNK_DATA pt
= rva (PIMAGE_THUNK_DATA
, hm
, pImportDesc
->FirstThunk
);
101 /* import names table */
102 PIMAGE_THUNK_DATA pn
= rva (PIMAGE_THUNK_DATA
, hm
, pImportDesc
->OriginalFirstThunk
);
104 /* Scan through the IAT, completing the stubs and redirecting the IAT
105 entries to point to the stubs. */
106 for (PIMAGE_THUNK_DATA pi
= pt
; pn
->u1
.Ordinal
; pi
++, pn
++)
108 if (IMAGE_SNAP_BY_ORDINAL (pn
->u1
.Ordinal
) )
112 PIMAGE_IMPORT_BY_NAME pimp
= rva (PIMAGE_IMPORT_BY_NAME
, hm
, pn
->u1
.AddressOfData
);
114 if (strcmp (fh
.name
, (char *) pimp
->Name
) == 0)
116 fh
.origfn
= putmem (pi
, fh
.hookfn
);
120 for (hc
= &cygheap
->hooks
; hc
->next
; hc
= hc
->next
)
122 hc
->next
= (hook_chain
*) cmalloc_abort (HEAP_1_HOOK
, sizeof (hook_chain
));
123 hc
->next
->loc
= (void **) pi
;
124 hc
->next
->func
= fh
.hookfn
;
125 hc
->next
->next
= NULL
;
133 /* This function is only used for the current architecture. */
135 get_export (function_hook
& fh
)
137 PIMAGE_DOS_HEADER pdh
= (PIMAGE_DOS_HEADER
) cygwin_hmodule
;
138 if (pdh
->e_magic
!= IMAGE_DOS_SIGNATURE
)
140 PIMAGE_NT_HEADERS pnt
= (PIMAGE_NT_HEADERS
) ((char *) pdh
+ pdh
->e_lfanew
);
141 if (pnt
->Signature
!= IMAGE_NT_SIGNATURE
|| pnt
->FileHeader
.SizeOfOptionalHeader
== 0)
143 PIMAGE_EXPORT_DIRECTORY pexp
=
144 rvacyg (PIMAGE_EXPORT_DIRECTORY
,
145 pnt
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXPORT
].VirtualAddress
);
149 PDWORD pfuncs
= rvacyg (PDWORD
, pexp
->AddressOfFunctions
);
150 PDWORD pnames
= rvacyg (PDWORD
, pexp
->AddressOfNames
);
151 for (DWORD i
= 0; i
< pexp
->NumberOfNames
; i
++)
152 if (strcmp (fh
.name
, rvacyg (char *, pnames
[i
])) == 0)
154 fh
.origfn
= rvacyg (void *, pfuncs
[i
]);
160 makename (const char *name
, char *&buf
, int& i
, int inc
)
163 static const char *testers
[] = {"NOTUSED", "64", "32"};
164 if (i
< 0 || i
>= (int) (sizeof (testers
) / sizeof (testers
[0])))
168 __small_sprintf (buf
, "_%s%s", name
, testers
[i
]);
175 remap (PIMAGE_IMPORT_DESCRIPTOR
&pdfirst
, long &delta
, HANDLE hc
,
176 DWORD importRVA
, DWORD importRVASize
, DWORD importRVAMaxSize
)
178 /* If h is not NULL, the calling function only mapped at most the first
179 64K of the image. The IAT is usually at the end of the image, so
180 what we do here is to map the IAT into our address space if it doesn't
181 reside in the first 64K anyway. The offset must be a multiple of the
182 allocation granularity, though, so we have to map a bit more. */
184 DWORD offset
= rounddown (importRVA
, wincap
.allocation_granularity ());
185 /* But that's not all, unfortunately. Apparently there's a difference
186 between the importRVASize of applications built with gcc and those
187 built with Visual Studio. When built with gcc, importRVASize contains
188 the size of the import RVA table plus the size of the referenced
189 string table with the DLL names. When built with VS, it only contains
190 the size of the naked import RVA table. The following code handles
191 the situation. importRVAMaxSize contains the size of the remainder
192 of the section. If the difference between importRVAMaxSize and
193 importRVASize is less than 64K, we just use importRVAMaxSize to
194 compute the size of the memory map. Otherwise the executable may be
195 very big. In that case we only map the import RVA table and ... */
196 DWORD size
= importRVA
- offset
+ ((importRVAMaxSize
- importRVASize
197 <= wincap
.allocation_granularity ())
198 ? importRVAMaxSize
: importRVASize
);
199 map
= (HMODULE
) MapViewOfFile (hc
, FILE_MAP_READ
, 0, offset
, size
);
202 pdfirst
= rva (PIMAGE_IMPORT_DESCRIPTOR
, map
, importRVA
- offset
);
203 /* ... carefully check the required size to fit the string table into
204 the map as well. Allow NAME_MAX bytes for the DLL name, but don't
205 go beyond the remainder of the section. */
206 if (importRVAMaxSize
- importRVASize
> wincap
.allocation_granularity ())
208 DWORD newsize
= size
;
209 for (PIMAGE_IMPORT_DESCRIPTOR pd
= pdfirst
; pd
->FirstThunk
; pd
++)
210 if (pd
->Name
- delta
- offset
+ (NAME_MAX
+ 1) > newsize
)
211 newsize
= pd
->Name
- delta
- offset
+ (NAME_MAX
+ 1);
214 if (newsize
> importRVA
- offset
+ importRVAMaxSize
)
215 newsize
= importRVA
- offset
+ importRVAMaxSize
;
216 UnmapViewOfFile (map
);
217 map
= (HMODULE
) MapViewOfFile (hc
, FILE_MAP_READ
, 0, offset
, newsize
);
220 pdfirst
= rva (PIMAGE_IMPORT_DESCRIPTOR
, map
, importRVA
- offset
);
227 /* Find first missing dll in a given executable.
228 FIXME: This is not foolproof since it doesn't look for dlls in the
229 same directory as the given executable, like Windows. Instead it
230 searches for dlls in the context of the current executable.
231 It also only finds direct dependencies, not indirect ones. */
233 find_first_notloaded_dll (path_conv
& pc
)
235 const char *res
= "?";
238 OBJECT_ATTRIBUTES attr
;
243 PIMAGE_NT_HEADERS pExeNTHdr
;
244 DWORD importRVA
, importRVASize
, importRVAMaxSize
;
248 status
= NtOpenFile (&h
, SYNCHRONIZE
| GENERIC_READ
,
249 pc
.get_object_attr (attr
, sec_none_nih
),
250 &io
, FILE_SHARE_VALID_FLAGS
,
251 FILE_SYNCHRONOUS_IO_NONALERT
252 | FILE_OPEN_FOR_BACKUP_INTENT
253 | FILE_NON_DIRECTORY_FILE
);
254 if (!NT_SUCCESS (status
))
256 /* Just as in hook_or_detect_cygwin below, we have to take big executables
257 into account. That means, we must not try to map the entire file, since
258 there's no guarantee that the current process has enough VM in one block
259 left for this mapping. The offset computation below ignores very big
260 executables for now. In theory, since the import RVA table appears to
261 be more or less at the end of the data section, independent of the used
262 compiler, that shouldn't matter. */
263 if (!GetFileSizeEx (h
, &size
))
268 if (size
.QuadPart
> (LONGLONG
) wincap
.allocation_granularity ())
269 size
.LowPart
= wincap
.allocation_granularity ();
270 hc
= CreateFileMapping (h
, &sec_none_nih
, PAGE_READONLY
, 0, 0, NULL
);
274 hm
= (HMODULE
) MapViewOfFile(hc
, FILE_MAP_READ
, 0, 0, size
.LowPart
);
278 pExeNTHdr
= PEHeaderFromHModule (hm
);
282 importRVA
= pExeNTHdr
->OptionalHeader
.DataDirectory
283 [IMAGE_DIRECTORY_ENTRY_IMPORT
].VirtualAddress
;
284 importRVASize
= pExeNTHdr
->OptionalHeader
.DataDirectory
285 [IMAGE_DIRECTORY_ENTRY_IMPORT
].Size
;
289 delta
= rvadelta (pExeNTHdr
, importRVA
, importRVAMaxSize
);
295 PIMAGE_IMPORT_DESCRIPTOR pdfirst
;
297 if (importRVA
+ importRVAMaxSize
> wincap
.allocation_granularity ())
299 map
= remap (pdfirst
, delta
, hc
, importRVA
, importRVASize
,
305 pdfirst
= rva (PIMAGE_IMPORT_DESCRIPTOR
, hm
, importRVA
);
307 /* Iterate through each import descriptor, and check if DLL can be loaded. */
308 for (PIMAGE_IMPORT_DESCRIPTOR pd
= pdfirst
; pd
->FirstThunk
; pd
++)
310 const char *lib
= rva (PSTR
, map
?: hm
, pd
->Name
- delta
);
311 if (!LoadLibraryEx (lib
, NULL
, DONT_RESOLVE_DLL_REFERENCES
312 | LOAD_LIBRARY_AS_DATAFILE
))
314 static char buf
[MAX_PATH
];
315 strlcpy (buf
, lib
, MAX_PATH
);
320 UnmapViewOfFile (map
);
324 UnmapViewOfFile (hm
);
331 // Top level routine to find the EXE's imports and redirect them
333 hook_or_detect_cygwin (const char *name
, const void *fn
, WORD
& subsys
, HANDLE h
)
335 HMODULE hm
= fn
? GetModuleHandle (NULL
) : (HMODULE
) name
;
336 PIMAGE_NT_HEADERS pExeNTHdr
= PEHeaderFromHModule (hm
);
338 /* Shortcut. We don't have to do anything further from here, if the
339 executable's architecture doesn't match. */
343 DWORD importRVA
, importRVASize
;
344 subsys
= pExeNTHdr
->OptionalHeader
.Subsystem
;
345 importRVA
= pExeNTHdr
->OptionalHeader
.DataDirectory
346 [IMAGE_DIRECTORY_ENTRY_IMPORT
].VirtualAddress
;
347 importRVASize
= pExeNTHdr
->OptionalHeader
.DataDirectory
348 [IMAGE_DIRECTORY_ENTRY_IMPORT
].Size
;
352 DWORD importRVAMaxSize
= 0;
353 long delta
= fn
? 0 : rvadelta (pExeNTHdr
, importRVA
, importRVAMaxSize
);
358 // Convert imports RVA to a usable pointer
359 PIMAGE_IMPORT_DESCRIPTOR pdfirst
;
361 if (h
&& importRVA
+ importRVAMaxSize
> wincap
.allocation_granularity ())
363 map
= (char *) remap (pdfirst
, delta
, h
, importRVA
, importRVASize
,
369 pdfirst
= rva (PIMAGE_IMPORT_DESCRIPTOR
, hm
, importRVA
);
376 buf
= (char *) alloca (strlen (name
) + sizeof ("_64"));
378 // Iterate through each import descriptor, and redirect if appropriate
379 for (PIMAGE_IMPORT_DESCRIPTOR pd
= pdfirst
; pd
->FirstThunk
; pd
++)
381 if (!ascii_strcasematch (rva (PSTR
, map
?: (char *) hm
, pd
->Name
- delta
),
386 /* Just checking if executable used cygwin1.dll. */
388 UnmapViewOfFile (map
);
389 return (void *) "found it";
392 while (!fh
.origfn
&& (fh
.name
= makename (name
, buf
, i
, 1)))
393 RedirectIAT (fh
, pd
, hm
);
399 UnmapViewOfFile (map
);
403 while (!fh
.origfn
&& (fh
.name
= makename (name
, buf
, i
, -1)))
409 /* Hook a function in any DLL such as kernel32.dll */
410 /* The DLL must be loaded in advance. */
411 /* Used in fhandler_tty.cc */
412 void *hook_api (const char *mname
, const char *name
, const void *fn
)
414 HMODULE hm
= GetModuleHandle (mname
);
415 PIMAGE_NT_HEADERS pExeNTHdr
=
416 rva (PIMAGE_NT_HEADERS
, hm
, PIMAGE_DOS_HEADER (hm
)->e_lfanew
);
417 DWORD importRVA
= pExeNTHdr
->OptionalHeader
.DataDirectory
418 [IMAGE_DIRECTORY_ENTRY_IMPORT
].VirtualAddress
;
419 PIMAGE_IMPORT_DESCRIPTOR pdfirst
=
420 rva (PIMAGE_IMPORT_DESCRIPTOR
, hm
, importRVA
);
421 for (PIMAGE_IMPORT_DESCRIPTOR pd
= pdfirst
; pd
->FirstThunk
; pd
++)
423 if (pd
->OriginalFirstThunk
== 0)
425 PIMAGE_THUNK_DATA pt
= rva (PIMAGE_THUNK_DATA
, hm
, pd
->FirstThunk
);
426 PIMAGE_THUNK_DATA pn
=
427 rva (PIMAGE_THUNK_DATA
, hm
, pd
->OriginalFirstThunk
);
428 for (PIMAGE_THUNK_DATA pi
= pt
; pn
->u1
.Ordinal
; pi
++, pn
++)
430 if (IMAGE_SNAP_BY_ORDINAL (pn
->u1
.Ordinal
))
432 PIMAGE_IMPORT_BY_NAME pimp
=
433 rva (PIMAGE_IMPORT_BY_NAME
, hm
, pn
->u1
.AddressOfData
);
434 if (strcmp (name
, (char *) pimp
->Name
) != 0)
436 void *origfn
= putmem (pi
, fn
);
446 char *p
= getenv ("LD_PRELOAD");
449 char *s
= (char *) alloca (strlen (p
) + 1);
452 for (p
= strtok_r (s
, ":\t\n", &here
); p
; p
= strtok_r (NULL
, ":\t\n", &here
))
455 WCHAR libname
[lib
.get_wide_win32_path_len () + 1];
456 if (!LoadLibraryW (lib
.get_wide_win32_path (libname
)))
459 api_fatal ("error while loading shared libraries: %s: "
460 "cannot open shared object file: %s", p
,
461 strerror (get_errno ()));
467 fixup_hooks_after_fork ()
469 for (hook_chain
*hc
= &cygheap
->hooks
; (hc
= hc
->next
); )
470 putmem ((PIMAGE_THUNK_DATA
) hc
->loc
, hc
->func
);