OSDN Git Service

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