OSDN Git Service

* dcrt0.cc (initial_env): Don't attempt stracing if dynamically loaded.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / hookapi.cc
1 #include "winsup.h"
2 #include "cygerrno.h"
3 #include "security.h"
4 #include "path.h"
5 #include "fhandler.h"
6 #include "dtable.h"
7 #include "cygheap.h"
8 #include <stdlib.h>
9 #include <imagehlp.h>
10 #include <alloca.h>
11
12 #define rva(coerce, base, addr) (coerce) ((char *) (base) + (addr))
13 #define rvacyg(coerce, addr) rva (coerce, cygwin_hmodule, addr)
14
15 struct function_hook
16 {
17   const char *name;     // Function name, e.g. "DirectDrawCreateEx".
18   const void *hookfn;   // Address of your function.
19   void *origfn;         // Stored by HookAPICalls, the address of the original function.
20 };
21
22 /* Given an HMODULE, returns a pointer to the PE header */
23 static PIMAGE_NT_HEADERS
24 PEHeaderFromHModule (HMODULE hModule)
25 {
26   PIMAGE_NT_HEADERS pNTHeader;
27
28   if (PIMAGE_DOS_HEADER(hModule) ->e_magic != IMAGE_DOS_SIGNATURE)
29     pNTHeader = NULL;
30   else
31     {
32       pNTHeader = PIMAGE_NT_HEADERS (PBYTE (hModule)
33                                      + PIMAGE_DOS_HEADER (hModule) ->e_lfanew);
34       if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
35         pNTHeader = NULL;
36     }
37
38   return pNTHeader;
39 }
40
41 long
42 rvadelta (PIMAGE_NT_HEADERS pnt, long import_rva)
43 {
44   PIMAGE_SECTION_HEADER section = (PIMAGE_SECTION_HEADER) (pnt + 1);
45   for (int i = 0; i < pnt->FileHeader.NumberOfSections; i++)
46     if (section[i].VirtualAddress <= import_rva
47         && (section[i].VirtualAddress + section[i].Misc.VirtualSize) >= import_rva)
48     // if (strncasematch ((char *) section[i].Name, ".idata", IMAGE_SIZEOF_SHORT_NAME))
49       return section[i].VirtualAddress - section[i].PointerToRawData;
50   return -1;
51 }
52
53 void *
54 putmem (PIMAGE_THUNK_DATA pi, const void *hookfn)
55 {
56
57   DWORD flOldProtect, flNewProtect, flDontCare;
58   MEMORY_BASIC_INFORMATION mbi;
59
60   /* Get the current protection attributes */
61   VirtualQuery (pi, &mbi, sizeof (mbi));
62
63   /* Remove ReadOnly and ExecuteRead attributes, add on ReadWrite flag */
64   flNewProtect = mbi.Protect;
65   flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ);
66   flNewProtect |= PAGE_READWRITE;
67
68   if (!VirtualProtect (pi, sizeof (PVOID), flNewProtect, &flOldProtect) )
69     return NULL;
70
71   void *origfn = (void *) pi->u1.Function;
72   pi->u1.Function = (DWORD) hookfn;
73
74   VirtualProtect (pi, sizeof (PVOID), flOldProtect, &flDontCare);
75   return origfn;
76 }
77
78 /* Builds stubs for and redirects the IAT for one DLL (pImportDesc) */
79
80 static bool
81 RedirectIAT (function_hook& fh, PIMAGE_IMPORT_DESCRIPTOR pImportDesc,
82              HMODULE hm)
83 {
84   // If no import names table, we can't redirect this, so bail
85   if (pImportDesc->OriginalFirstThunk == 0)
86       return false;
87
88   /* import address table */
89   PIMAGE_THUNK_DATA pt = rva (PIMAGE_THUNK_DATA, hm, pImportDesc->FirstThunk);
90   /* import names table */
91   PIMAGE_THUNK_DATA pn = rva (PIMAGE_THUNK_DATA, hm, pImportDesc->OriginalFirstThunk);
92
93   /* Scan through the IAT, completing the stubs and redirecting the IAT
94      entries to point to the stubs. */
95   for (PIMAGE_THUNK_DATA pi = pt; pn->u1.Ordinal; pi++, pn++)
96     {
97       if (IMAGE_SNAP_BY_ORDINAL (pn->u1.Ordinal) )
98         continue;
99
100       /* import by name */
101       PIMAGE_IMPORT_BY_NAME pimp = rva (PIMAGE_IMPORT_BY_NAME, hm, pn->u1.AddressOfData);
102
103       if (strcmp (fh.name, (char *) pimp->Name) == 0)
104         {
105           fh.origfn = putmem (pi, fh.hookfn);
106           if (!fh.origfn)
107             return false;
108           hook_chain *hc;
109           for (hc = &cygheap->hooks; hc->next; hc = hc->next)
110             continue;
111           hc->next = (hook_chain *) cmalloc (HEAP_1_HOOK, sizeof (hook_chain));
112           hc->next->loc = (void **) pi;
113           hc->next->func = fh.hookfn;
114           hc->next->next = NULL;
115           break;
116       }
117   }
118
119   return true;
120 }
121
122 void
123 get_export (function_hook& fh)
124 {
125   extern HMODULE cygwin_hmodule;
126   PIMAGE_DOS_HEADER pdh = (PIMAGE_DOS_HEADER) cygwin_hmodule;
127   if (pdh->e_magic != IMAGE_DOS_SIGNATURE)
128     return;
129   PIMAGE_NT_HEADERS pnt = (PIMAGE_NT_HEADERS) ((char *) pdh + pdh->e_lfanew);
130   if (pnt->Signature != IMAGE_NT_SIGNATURE || pnt->FileHeader.SizeOfOptionalHeader == 0)
131     return;
132   PIMAGE_EXPORT_DIRECTORY pexp =
133     rvacyg (PIMAGE_EXPORT_DIRECTORY,
134          pnt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
135   if (!pexp)
136     return;
137
138   PDWORD pfuncs = rvacyg (PDWORD, pexp->AddressOfFunctions);
139   PDWORD pnames = rvacyg (PDWORD, pexp->AddressOfNames);
140   for (DWORD i = 0; i < pexp->NumberOfNames; i++)
141     if (strcmp (fh.name, rvacyg (char *, pnames[i])) == 0)
142       {
143         fh.origfn = rvacyg (void *, pfuncs[i]);
144         break;
145       }
146 }
147
148 static const char *
149 makename (const char *name, char *&buf, int& i, int inc)
150 {
151   i += inc;
152   static const char *testers[] = {"NOTUSED", "64", "32"};
153   if (i < 0 || i >= (int) (sizeof (testers) / sizeof (testers[0])))
154     return NULL;
155   if (i)
156     {
157       __small_sprintf (buf, "_%s%s", name, testers[i]);
158       name = buf;
159     }
160   return name;
161 }
162
163 // Top level routine to find the EXE's imports, and redirect them
164 void *
165 hook_or_detect_cygwin (const char *name, const void *fn)
166 {
167   HMODULE hm = fn ? GetModuleHandle (NULL) : (HMODULE) name;
168   PIMAGE_NT_HEADERS pExeNTHdr = PEHeaderFromHModule (hm);
169
170   if (!pExeNTHdr)
171     return false;
172
173   DWORD importRVA = pExeNTHdr->OptionalHeader.DataDirectory
174                       [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
175   if (!importRVA)
176     return false;
177
178   long delta = fn ? 0 : rvadelta (pExeNTHdr, importRVA);
179   if (delta < 0)
180     return false;
181
182   // Convert imports RVA to a usable pointer
183   PIMAGE_IMPORT_DESCRIPTOR pdfirst = rva (PIMAGE_IMPORT_DESCRIPTOR, hm, importRVA - delta);
184
185   function_hook fh;
186   fh.origfn = NULL;
187   fh.hookfn = fn;
188   char *buf = fn ? NULL : (char *) alloca (strlen (name) + strlen ("64") + sizeof ("_"));
189   int i;
190   // Iterate through each import descriptor, and redirect if appropriate
191   for (PIMAGE_IMPORT_DESCRIPTOR pd = pdfirst; pd->FirstThunk; pd++)
192     {
193       if (!strcasematch (rva (PSTR, hm, pd->Name - delta), "cygwin1.dll"))
194         continue;
195       if (!fn)
196         return (void *) "found it";     // just checking if executable used cygwin1.dll
197       i = -1;
198       while (!fh.origfn && (fh.name = makename (name, buf, i, 1)))
199         RedirectIAT (fh, pd, hm);
200       if (fh.origfn)
201         break;
202     }
203
204   while (!fh.origfn && (fh.name = makename (name, buf, i, -1)))
205     get_export (fh);
206
207   return fh.origfn;
208 }
209
210 void
211 ld_preload ()
212 {
213   char *p = getenv ("LD_PRELOAD");
214   if (!p)
215     return;
216   char *s = (char *) alloca (strlen (p) + 1);
217   strcpy (s, p);
218   char *here = NULL;
219   for (p = strtok_r (s, ":\t\n", &here); p; p = strtok_r (NULL, ":\t\n", &here))
220     {
221       path_conv lib (p);
222       if (!LoadLibrary (lib))
223         {
224           __seterrno ();
225           api_fatal ("error while loading shared libraries: %s: "
226                      "cannot open shared object file: %s", p,
227                      strerror (get_errno ()));
228         }
229     }
230 }
231
232 void
233 fixup_hooks_after_fork ()
234 {
235   for (hook_chain *hc = &cygheap->hooks; (hc = hc->next); )
236     putmem ((PIMAGE_THUNK_DATA) hc->loc, hc->func);
237 }