OSDN Git Service

* select.cc (select_stuff::wait): Temporarily disallow APCS.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / hookapi.cc
1 /* hookapi.cc
2
3    Copyright 2005, 2006, 2007, 2008, 2011 Red Hat, Inc.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include "winsup.h"
12 #include <stdlib.h>
13 #include <sys/param.h>
14 #include "ntdll.h"
15 #include "cygerrno.h"
16 #include "security.h"
17 #include "path.h"
18 #include "fhandler.h"
19 #include "dtable.h"
20 #include "cygheap.h"
21
22 #define rva(coerce, base, addr) (coerce) ((char *) (base) + (addr))
23 #define rvacyg(coerce, addr) rva (coerce, cygwin_hmodule, addr)
24
25 struct function_hook
26 {
27   const char *name;     // Function name, e.g. "DirectDrawCreateEx".
28   const void *hookfn;   // Address of your function.
29   void *origfn;         // Stored by HookAPICalls, the address of the original function.
30 };
31
32 /* Given an HMODULE, returns a pointer to the PE header */
33 static PIMAGE_NT_HEADERS
34 PEHeaderFromHModule (HMODULE hModule)
35 {
36   PIMAGE_NT_HEADERS pNTHeader;
37
38   if (PIMAGE_DOS_HEADER(hModule) ->e_magic != IMAGE_DOS_SIGNATURE)
39     pNTHeader = NULL;
40   else
41     {
42       pNTHeader = PIMAGE_NT_HEADERS (PBYTE (hModule)
43                                      + PIMAGE_DOS_HEADER (hModule) ->e_lfanew);
44       if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
45         pNTHeader = NULL;
46     }
47
48   return pNTHeader;
49 }
50
51 static long
52 rvadelta (PIMAGE_NT_HEADERS pnt, DWORD import_rva)
53 {
54   PIMAGE_SECTION_HEADER section = (PIMAGE_SECTION_HEADER) (pnt + 1);
55   for (int i = 0; i < pnt->FileHeader.NumberOfSections; i++)
56     if (section[i].VirtualAddress <= import_rva
57         && (section[i].VirtualAddress + section[i].Misc.VirtualSize) > import_rva)
58     // if (ascii_strncasematch ((char *) section[i].Name, ".idata", IMAGE_SIZEOF_SHORT_NAME))
59       return section[i].VirtualAddress - section[i].PointerToRawData;
60   return -1;
61 }
62
63 static void *
64 putmem (PIMAGE_THUNK_DATA pi, const void *hookfn)
65 {
66   DWORD ofl;
67   if (!VirtualProtect (pi, sizeof (PVOID), PAGE_READWRITE, &ofl) )
68     return NULL;
69
70   void *origfn = (void *) pi->u1.Function;
71   pi->u1.Function = (DWORD) hookfn;
72
73   VirtualProtect (pi, sizeof (PVOID), ofl, &ofl);
74   return origfn;
75 }
76
77 /* Builds stubs for and redirects the IAT for one DLL (pImportDesc) */
78
79 static bool
80 RedirectIAT (function_hook& fh, PIMAGE_IMPORT_DESCRIPTOR pImportDesc,
81              HMODULE hm)
82 {
83   // If no import names table, we can't redirect this, so bail
84   if (pImportDesc->OriginalFirstThunk == 0)
85       return false;
86
87   /* import address table */
88   PIMAGE_THUNK_DATA pt = rva (PIMAGE_THUNK_DATA, hm, pImportDesc->FirstThunk);
89   /* import names table */
90   PIMAGE_THUNK_DATA pn = rva (PIMAGE_THUNK_DATA, hm, pImportDesc->OriginalFirstThunk);
91
92   /* Scan through the IAT, completing the stubs and redirecting the IAT
93      entries to point to the stubs. */
94   for (PIMAGE_THUNK_DATA pi = pt; pn->u1.Ordinal; pi++, pn++)
95     {
96       if (IMAGE_SNAP_BY_ORDINAL (pn->u1.Ordinal) )
97         continue;
98
99       /* import by name */
100       PIMAGE_IMPORT_BY_NAME pimp = rva (PIMAGE_IMPORT_BY_NAME, hm, pn->u1.AddressOfData);
101
102       if (strcmp (fh.name, (char *) pimp->Name) == 0)
103         {
104           fh.origfn = putmem (pi, fh.hookfn);
105           if (!fh.origfn)
106             return false;
107           hook_chain *hc;
108           for (hc = &cygheap->hooks; hc->next; hc = hc->next)
109             continue;
110           hc->next = (hook_chain *) cmalloc_abort (HEAP_1_HOOK, sizeof (hook_chain));
111           hc->next->loc = (void **) pi;
112           hc->next->func = fh.hookfn;
113           hc->next->next = NULL;
114           break;
115       }
116   }
117
118   return true;
119 }
120
121 static void
122 get_export (function_hook& fh)
123 {
124   PIMAGE_DOS_HEADER pdh = (PIMAGE_DOS_HEADER) cygwin_hmodule;
125   if (pdh->e_magic != IMAGE_DOS_SIGNATURE)
126     return;
127   PIMAGE_NT_HEADERS pnt = (PIMAGE_NT_HEADERS) ((char *) pdh + pdh->e_lfanew);
128   if (pnt->Signature != IMAGE_NT_SIGNATURE || pnt->FileHeader.SizeOfOptionalHeader == 0)
129     return;
130   PIMAGE_EXPORT_DIRECTORY pexp =
131     rvacyg (PIMAGE_EXPORT_DIRECTORY,
132          pnt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
133   if (!pexp)
134     return;
135
136   PDWORD pfuncs = rvacyg (PDWORD, pexp->AddressOfFunctions);
137   PDWORD pnames = rvacyg (PDWORD, pexp->AddressOfNames);
138   for (DWORD i = 0; i < pexp->NumberOfNames; i++)
139     if (strcmp (fh.name, rvacyg (char *, pnames[i])) == 0)
140       {
141         fh.origfn = rvacyg (void *, pfuncs[i]);
142         break;
143       }
144 }
145
146 static const char *
147 makename (const char *name, char *&buf, int& i, int inc)
148 {
149   i += inc;
150   static const char *testers[] = {"NOTUSED", "64", "32"};
151   if (i < 0 || i >= (int) (sizeof (testers) / sizeof (testers[0])))
152     return NULL;
153   if (i)
154     {
155       __small_sprintf (buf, "_%s%s", name, testers[i]);
156       name = buf;
157     }
158   return name;
159 }
160
161 /* Find first missing dll in a given executable.
162    FIXME: This is not foolproof since it doesn't look for dlls in the
163    same directory as the given executable, like Windows.  Instead it
164    searches for dlls in the context of the current executable.  */
165 const char *
166 find_first_notloaded_dll (path_conv& pc)
167 {
168   const char *res = "?";
169   HANDLE hc = NULL;
170   HMODULE hm = NULL;
171   OBJECT_ATTRIBUTES attr;
172   IO_STATUS_BLOCK io;
173   HANDLE h;
174   NTSTATUS status;
175
176   status = NtOpenFile (&h, SYNCHRONIZE | GENERIC_READ,
177                        pc.get_object_attr (attr, sec_none_nih),
178                        &io, FILE_SHARE_VALID_FLAGS,
179                        FILE_SYNCHRONOUS_IO_NONALERT
180                        | FILE_OPEN_FOR_BACKUP_INTENT
181                        | FILE_NON_DIRECTORY_FILE);
182   if (!NT_SUCCESS (status))
183     goto out;
184
185   hc = CreateFileMapping (h, &sec_none_nih, PAGE_READONLY, 0, 0, NULL);
186   NtClose (h);
187   if (!hc)
188     goto out;
189   hm = (HMODULE) MapViewOfFile(hc, FILE_MAP_READ, 0, 0, 0);
190   CloseHandle (hc);
191
192   PIMAGE_NT_HEADERS pExeNTHdr;
193   pExeNTHdr = PEHeaderFromHModule (hm);
194
195   if (pExeNTHdr)
196     {
197       DWORD importRVA;
198       importRVA = pExeNTHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
199       if (importRVA)
200         {
201           long delta = rvadelta (pExeNTHdr, importRVA);
202
203           // Convert imports RVA to a usable pointer
204           PIMAGE_IMPORT_DESCRIPTOR pdfirst;
205           pdfirst = rva (PIMAGE_IMPORT_DESCRIPTOR, hm, importRVA - delta);
206
207           // Iterate through each import descriptor, and redirect if appropriate
208           for (PIMAGE_IMPORT_DESCRIPTOR pd = pdfirst; pd->FirstThunk; pd++)
209             {
210               const char *lib = rva (PSTR, hm, pd->Name - delta);
211               if (!LoadLibraryEx (lib, NULL, DONT_RESOLVE_DLL_REFERENCES
212                                              | LOAD_LIBRARY_AS_DATAFILE))
213                 {
214                   static char buf[NT_MAX_PATH];
215                   res = strcpy (buf, lib);
216                 }
217             }
218         }
219     }
220
221 out:
222   if (hm)
223     UnmapViewOfFile (hm);
224
225   return res;
226 }
227
228 // Top level routine to find the EXE's imports and redirect them
229 void *
230 hook_or_detect_cygwin (const char *name, const void *fn, WORD& subsys, HANDLE h)
231 {
232   HMODULE hm = fn ? GetModuleHandle (NULL) : (HMODULE) name;
233   PIMAGE_NT_HEADERS pExeNTHdr = PEHeaderFromHModule (hm);
234
235   if (!pExeNTHdr)
236     return false;
237
238   subsys =  pExeNTHdr->OptionalHeader.Subsystem;
239
240   DWORD importRVA = pExeNTHdr->OptionalHeader.DataDirectory
241                       [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
242   DWORD importRVASize = pExeNTHdr->OptionalHeader.DataDirectory
243                         [IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
244   if (!importRVA)
245     return false;
246
247   long delta = fn ? 0 : rvadelta (pExeNTHdr, importRVA);
248   if (delta < 0)
249     return false;
250   importRVA -= delta;
251
252   // Convert imports RVA to a usable pointer
253   PIMAGE_IMPORT_DESCRIPTOR pdfirst;
254   char *map = NULL;
255   DWORD offset = 0;
256   if (h && importRVA + importRVASize > wincap.allocation_granularity ())
257     {
258       /* If h is not NULL, the calling function only mapped at most the first
259          64K of the image.  The IAT is usually at the end of the image, so
260          what we do here is to map the IAT into our address space if it doesn't
261          reside in the first 64K anyway.  The offset must be a multiple of the
262          allocation granularity, though, so we have to map a bit more. */
263       offset = rounddown (importRVA, wincap.allocation_granularity ());
264       DWORD size = importRVA - offset + importRVASize;
265       map = (char *) MapViewOfFile (h, FILE_MAP_READ, 0, offset, size);
266       if (!map)
267         return false;
268       pdfirst = rva (PIMAGE_IMPORT_DESCRIPTOR, map, importRVA - offset);
269     }
270   else
271     pdfirst = rva (PIMAGE_IMPORT_DESCRIPTOR, hm, importRVA);
272
273   function_hook fh;
274   fh.origfn = NULL;
275   fh.hookfn = fn;
276   char *buf = (char *) alloca (strlen (name) + sizeof ("_64"));
277   int i = 0;
278   // Iterate through each import descriptor, and redirect if appropriate
279   for (PIMAGE_IMPORT_DESCRIPTOR pd = pdfirst; pd->FirstThunk; pd++)
280     {
281       if (!ascii_strcasematch (rva (PSTR, map ?: (char *) hm,
282                                     pd->Name - delta - offset), "cygwin1.dll"))
283         continue;
284       if (!fn)
285         {
286           if (map)
287             UnmapViewOfFile (map);
288           return (void *) "found it";   // just checking if executable used cygwin1.dll
289         }
290       i = -1;
291       while (!fh.origfn && (fh.name = makename (name, buf, i, 1)))
292         RedirectIAT (fh, pd, hm);
293       if (fh.origfn)
294         break;
295     }
296
297   if (map)
298     UnmapViewOfFile (map);
299
300   while (!fh.origfn && (fh.name = makename (name, buf, i, -1)))
301     get_export (fh);
302
303   return fh.origfn;
304 }
305
306 void
307 ld_preload ()
308 {
309   char *p = getenv ("LD_PRELOAD");
310   if (!p)
311     return;
312   char *s = (char *) alloca (strlen (p) + 1);
313   strcpy (s, p);
314   char *here = NULL;
315   for (p = strtok_r (s, ":\t\n", &here); p; p = strtok_r (NULL, ":\t\n", &here))
316     {
317       path_conv lib (p);
318       WCHAR libname[lib.get_wide_win32_path_len () + 1];
319       if (!LoadLibraryW (lib.get_wide_win32_path (libname)))
320         {
321           __seterrno ();
322           api_fatal ("error while loading shared libraries: %s: "
323                      "cannot open shared object file: %s", p,
324                      strerror (get_errno ()));
325         }
326     }
327 }
328
329 void
330 fixup_hooks_after_fork ()
331 {
332   for (hook_chain *hc = &cygheap->hooks; (hc = hc->next); )
333     putmem ((PIMAGE_THUNK_DATA) hc->loc, hc->func);
334 }