Cygwin: sched_setscheduler: allow changes of the priority
[newlib-cygwin.git] / winsup / cygwin / hookapi.cc
blobee2edbafeef6d148e1934f2eb302c6186c131842
1 /* hookapi.cc
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
7 details. */
9 #include "winsup.h"
10 #include <stdlib.h>
11 #include <sys/param.h>
12 #include "ntdll.h"
13 #include "cygerrno.h"
14 #include "security.h"
15 #include "path.h"
16 #include "fhandler.h"
17 #include "dtable.h"
18 #include "cygheap.h"
20 #define rva(coerce, base, addr) (coerce) ((char *) (base) + (addr))
21 #define rvacyg(coerce, addr) rva (coerce, cygwin_hmodule, addr)
23 struct function_hook
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)
35 return NULL;
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)
41 return NULL;
43 /* Return valid PIMAGE_NT_HEADERS only for supported architectures. */
44 switch (pNTHeader->FileHeader.Machine)
46 case IMAGE_FILE_MACHINE_AMD64:
47 break;
48 default:
49 return NULL;
52 return pNTHeader;
55 static long
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;
67 return -1;
70 /* This function is only used for the current architecture.
71 Just the size of the IMAGE_THUNK_DATA Function member differs. */
72 static void *
73 putmem (PIMAGE_THUNK_DATA pi, const void *hookfn)
75 #define THUNK_FUNC_TYPE ULONGLONG
77 DWORD ofl;
78 if (!VirtualProtect (pi, sizeof (THUNK_FUNC_TYPE), PAGE_READWRITE, &ofl) )
79 return NULL;
81 void *origfn = (void *) pi->u1.Function;
82 pi->u1.Function = (THUNK_FUNC_TYPE) hookfn;
84 VirtualProtect (pi, sizeof (THUNK_FUNC_TYPE), ofl, &ofl);
85 return origfn;
88 /* Builds stubs for and redirects the IAT for one DLL (pImportDesc)
89 This function is only used for the current architecture. */
91 static bool
92 RedirectIAT (function_hook& fh, PIMAGE_IMPORT_DESCRIPTOR pImportDesc,
93 HMODULE hm)
95 // If no import names table, we can't redirect this, so bail
96 if (pImportDesc->OriginalFirstThunk == 0)
97 return false;
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) )
109 continue;
111 /* import by name */
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);
117 if (!fh.origfn)
118 return false;
119 hook_chain *hc;
120 for (hc = &cygheap->hooks; hc->next; hc = hc->next)
121 continue;
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;
126 break;
130 return true;
133 /* This function is only used for the current architecture. */
134 static void
135 get_export (function_hook& fh)
137 PIMAGE_DOS_HEADER pdh = (PIMAGE_DOS_HEADER) cygwin_hmodule;
138 if (pdh->e_magic != IMAGE_DOS_SIGNATURE)
139 return;
140 PIMAGE_NT_HEADERS pnt = (PIMAGE_NT_HEADERS) ((char *) pdh + pdh->e_lfanew);
141 if (pnt->Signature != IMAGE_NT_SIGNATURE || pnt->FileHeader.SizeOfOptionalHeader == 0)
142 return;
143 PIMAGE_EXPORT_DIRECTORY pexp =
144 rvacyg (PIMAGE_EXPORT_DIRECTORY,
145 pnt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
146 if (!pexp)
147 return;
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]);
155 break;
159 static const char *
160 makename (const char *name, char *&buf, int& i, int inc)
162 i += inc;
163 static const char *testers[] = {"NOTUSED", "64", "32"};
164 if (i < 0 || i >= (int) (sizeof (testers) / sizeof (testers[0])))
165 return NULL;
166 if (i)
168 __small_sprintf (buf, "_%s%s", name, testers[i]);
169 name = buf;
171 return name;
174 static HMODULE
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. */
183 HMODULE map;
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);
200 if (!map)
201 return NULL;
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);
212 if (newsize > size)
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);
218 if (!map)
219 return NULL;
220 pdfirst = rva (PIMAGE_IMPORT_DESCRIPTOR, map, importRVA - offset);
223 delta += offset;
224 return map;
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. */
232 const char *
233 find_first_notloaded_dll (path_conv& pc)
235 const char *res = "?";
236 HANDLE hc = NULL;
237 HMODULE hm = NULL;
238 OBJECT_ATTRIBUTES attr;
239 IO_STATUS_BLOCK io;
240 HANDLE h;
241 NTSTATUS status;
242 LARGE_INTEGER size;
243 PIMAGE_NT_HEADERS pExeNTHdr;
244 DWORD importRVA, importRVASize, importRVAMaxSize;
245 HMODULE map;
246 long delta;
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))
255 goto out;
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))
265 NtClose (h);
266 goto out;
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);
271 NtClose (h);
272 if (!hc)
273 goto out;
274 hm = (HMODULE) MapViewOfFile(hc, FILE_MAP_READ, 0, 0, size.LowPart);
275 if (!hm)
276 goto out;
278 pExeNTHdr = PEHeaderFromHModule (hm);
279 if (!pExeNTHdr)
280 goto out;
282 importRVA = pExeNTHdr->OptionalHeader.DataDirectory
283 [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
284 importRVASize = pExeNTHdr->OptionalHeader.DataDirectory
285 [IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
286 if (!importRVA)
287 goto out;
289 delta = rvadelta (pExeNTHdr, importRVA, importRVAMaxSize);
290 if (delta < 0)
291 goto out;
292 importRVA -= delta;
293 map = NULL;
295 PIMAGE_IMPORT_DESCRIPTOR pdfirst;
297 if (importRVA + importRVAMaxSize > wincap.allocation_granularity ())
299 map = remap (pdfirst, delta, hc, importRVA, importRVASize,
300 importRVAMaxSize);
301 if (!map)
302 goto out;
304 else
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);
316 res = buf;
319 if (map)
320 UnmapViewOfFile (map);
322 out:
323 if (hm)
324 UnmapViewOfFile (hm);
325 if (hc)
326 CloseHandle (hc);
328 return res;
331 // Top level routine to find the EXE's imports and redirect them
332 void *
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. */
340 if (!pExeNTHdr)
341 return NULL;
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;
349 if (!importRVA)
350 return NULL;
352 DWORD importRVAMaxSize = 0;
353 long delta = fn ? 0 : rvadelta (pExeNTHdr, importRVA, importRVAMaxSize);
354 if (delta < 0)
355 return NULL;
356 importRVA -= delta;
358 // Convert imports RVA to a usable pointer
359 PIMAGE_IMPORT_DESCRIPTOR pdfirst;
360 char *map = NULL;
361 if (h && importRVA + importRVAMaxSize > wincap.allocation_granularity ())
363 map = (char *) remap (pdfirst, delta, h, importRVA, importRVASize,
364 importRVAMaxSize);
365 if (!map)
366 return NULL;
368 else
369 pdfirst = rva (PIMAGE_IMPORT_DESCRIPTOR, hm, importRVA);
371 function_hook fh;
372 fh.origfn = NULL;
373 fh.hookfn = fn;
374 char *buf = NULL;
375 if (fn)
376 buf = (char *) alloca (strlen (name) + sizeof ("_64"));
377 int i = 0;
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),
382 "cygwin1.dll"))
383 continue;
384 if (!fn)
386 /* Just checking if executable used cygwin1.dll. */
387 if (map)
388 UnmapViewOfFile (map);
389 return (void *) "found it";
391 i = -1;
392 while (!fh.origfn && (fh.name = makename (name, buf, i, 1)))
393 RedirectIAT (fh, pd, hm);
394 if (fh.origfn)
395 break;
398 if (map)
399 UnmapViewOfFile (map);
400 if (!fn)
401 return NULL;
403 while (!fh.origfn && (fh.name = makename (name, buf, i, -1)))
404 get_export (fh);
406 return fh.origfn;
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)
424 continue;
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))
431 continue;
432 PIMAGE_IMPORT_BY_NAME pimp =
433 rva (PIMAGE_IMPORT_BY_NAME, hm, pn->u1.AddressOfData);
434 if (strcmp (name, (char *) pimp->Name) != 0)
435 continue;
436 void *origfn = putmem (pi, fn);
437 return origfn;
440 return NULL;
443 void
444 ld_preload ()
446 char *p = getenv ("LD_PRELOAD");
447 if (!p)
448 return;
449 char *s = (char *) alloca (strlen (p) + 1);
450 strcpy (s, p);
451 char *here = NULL;
452 for (p = strtok_r (s, ":\t\n", &here); p; p = strtok_r (NULL, ":\t\n", &here))
454 path_conv lib (p);
455 WCHAR libname[lib.get_wide_win32_path_len () + 1];
456 if (!LoadLibraryW (lib.get_wide_win32_path (libname)))
458 __seterrno ();
459 api_fatal ("error while loading shared libraries: %s: "
460 "cannot open shared object file: %s", p,
461 strerror (get_errno ()));
466 void
467 fixup_hooks_after_fork ()
469 for (hook_chain *hc = &cygheap->hooks; (hc = hc->next); )
470 putmem ((PIMAGE_THUNK_DATA) hc->loc, hc->func);