OSDN Git Service

Replace valid memory checks with new myfault class "exception handling", almost
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / dll_init.cc
1 /* dll_init.cc
2
3    Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
4
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. */
8
9 #include "winsup.h"
10 #include <stdlib.h>
11 #include "cygerrno.h"
12 #include "perprocess.h"
13 #include "dll_init.h"
14 #include "environ.h"
15 #include "security.h"
16 #include "path.h"
17 #include "fhandler.h"
18 #include "dtable.h"
19 #include "cygheap.h"
20 #include "pinfo.h"
21
22 extern void __stdcall check_sanity_and_sync (per_process *);
23
24 dll_list NO_COPY dlls;
25
26 static int NO_COPY in_forkee;
27 static bool dll_global_dtors_recorded;
28
29 /* Run destructors for all DLLs on exit. */
30 void
31 dll_global_dtors ()
32 {
33   int recorded = dll_global_dtors_recorded;
34   dll_global_dtors_recorded = false;
35   if (recorded)
36     for (dll *d = dlls.istart (DLL_ANY); d; d = dlls.inext ())
37       d->p.run_dtors ();
38 }
39
40 /* Run all constructors associated with a dll */
41 void
42 per_module::run_ctors ()
43 {
44   void (**pfunc)() = ctors;
45
46   /* Run ctors backwards, so skip the first entry and find how many
47     there are, then run them.  */
48
49   if (pfunc)
50     {
51       int i;
52       for (i = 1; pfunc[i]; i++);
53
54       for (int j = i - 1; j > 0; j--)
55         (pfunc[j]) ();
56     }
57 }
58
59 /* Run all destructors associated with a dll */
60 void
61 per_module::run_dtors ()
62 {
63   void (**pfunc)() = dtors;
64   for (int i = 1; pfunc[i]; i++)
65     (pfunc[i]) ();
66 }
67
68 /* Initialize an individual DLL */
69 int
70 dll::init ()
71 {
72   int ret = 1;
73
74   /* Why didn't we just import this variable? */
75   *(p.envptr) = __cygwin_environ;
76
77   /* Don't run constructors or the "main" if we've forked. */
78   if (!in_forkee)
79     {
80       /* global contructors */
81       p.run_ctors ();
82
83       /* entry point of dll (use main of per_process with null args...) */
84       if (p.main)
85         ret = (*(p.main)) (0, 0, 0);
86     }
87
88   return ret;
89 }
90
91 /* Look for a dll based on name */
92 dll *
93 dll_list::operator[] (const char *name)
94 {
95   dll *d = &start;
96   while ((d = d->next) != NULL)
97     if (strcasematch (name, d->name))
98       return d;
99
100   return NULL;
101 }
102
103 #define RETRIES 1000
104
105 /* Allocate space for a dll struct contiguous with the just-loaded dll. */
106 dll *
107 dll_list::alloc (HINSTANCE h, per_process *p, dll_type type)
108 {
109   char name[CYG_MAX_PATH];
110   DWORD namelen = GetModuleFileName (h, name, sizeof (name));
111
112   /* Already loaded? */
113   dll *d = dlls[name];
114   if (d)
115     {
116       d->count++;       /* Yes.  Bump the usage count. */
117       return d;         /* Return previously allocated pointer. */
118     }
119
120   SYSTEM_INFO s1;
121   GetSystemInfo (&s1);
122
123   int i;
124   void *s = p->bss_end;
125   DWORD n;
126   MEMORY_BASIC_INFORMATION m;
127   /* Search for space after the DLL */
128   for (i = 0; i <= RETRIES; i++, s = (char *) m.BaseAddress + m.RegionSize)
129     {
130       if (!VirtualQuery (s, &m, sizeof (m)))
131         return NULL;    /* Can't do it. */
132       if (m.State == MEM_FREE)
133         {
134           /* Couldn't find any.  Uh oh.  FIXME: Issue an error? */
135           if (i == RETRIES)
136             return NULL;        /* Oh well.  Couldn't locate free space. */
137
138           /* Ensure that this is rounded to the nearest page boundary.
139              FIXME: Should this be ensured by VirtualQuery? */
140           n = (DWORD) m.BaseAddress;
141           DWORD r = n % s1.dwAllocationGranularity;
142
143           if (r)
144             n = ((n - r) + s1.dwAllocationGranularity);
145
146           /* First reserve the area of memory, then commit it. */
147           if (VirtualAlloc ((void *) n, sizeof (dll), MEM_RESERVE, PAGE_READWRITE))
148             d = (dll *) VirtualAlloc ((void *) n, sizeof (dll), MEM_COMMIT,
149                                       PAGE_READWRITE);
150           if (d)
151             break;
152         }
153     }
154
155   /* Did we succeed? */
156   if (d == NULL)
157     {                   /* Nope. */
158 #ifdef DEBUGGING
159       system_printf ("VirtualAlloc failed, %E");
160 #endif
161       __seterrno ();
162       return NULL;
163     }
164
165   /* Now we've allocated a block of information.  Fill it in with the supplied
166      info about this DLL. */
167   d->count = 1;
168   d->namelen = namelen;
169   strcpy (d->name, name);
170   d->handle = h;
171   d->p = p;
172   d->type = type;
173   if (end == NULL)
174     end = &start;       /* Point to "end" of dll chain. */
175   end->next = d;        /* Standard linked list stuff. */
176   d->next = NULL;
177   d->prev = end;
178   end = d;
179   tot++;
180   if (type == DLL_LOAD)
181     loaded_dlls++;
182   return d;
183 }
184
185 /* Detach a DLL from the chain. */
186 void
187 dll_list::detach (void *retaddr)
188 {
189   if (!myself || exit_state)
190     return;
191   MEMORY_BASIC_INFORMATION m;
192   if (!VirtualQuery (retaddr, &m, sizeof m))
193     return;
194   HMODULE h = (HMODULE) m.AllocationBase;
195
196   dll *d = &start;
197   while ((d = d->next))
198     if (d->handle != h)
199       continue;
200     else if (d->count <= 0)
201       system_printf ("WARNING: trying to detach an already detached dll ...");
202     else if (--d->count == 0)
203       {
204         d->p.run_dtors ();
205         d->prev->next = d->next;
206         if (d->next)
207           d->next->prev = d->prev;
208         if (d->type == DLL_LOAD)
209           loaded_dlls--;
210         if (end == d)
211           end = d->prev;
212         VirtualFree (d, 0, MEM_RELEASE);
213         break;
214       }
215 }
216
217 /* Initialization for all linked DLLs, called by dll_crt0_1. */
218 void
219 dll_list::init ()
220 {
221   dll_global_dtors_recorded = true;
222
223   /* Walk the dll chain, initializing each dll */
224   dll *d = &start;
225   while ((d = d->next))
226     d->init ();
227 }
228
229 #define A64K (64 * 1024)
230
231 /* Mark every memory address up to "here" as reserved.  This may force
232    Windows NT to load a DLL in the next available, lowest slot. */
233 static void
234 reserve_upto (const char *name, DWORD here)
235 {
236   DWORD size;
237   MEMORY_BASIC_INFORMATION mb;
238   for (DWORD start = 0x10000; start < here; start += size)
239     if (!VirtualQuery ((void *) start, &mb, sizeof (mb)))
240       size = A64K;
241     else
242       {
243         size = A64K * ((mb.RegionSize + A64K - 1) / A64K);
244         start = A64K * (((DWORD) mb.BaseAddress + A64K - 1) / A64K);
245
246         if (start + size > here)
247           size = here - start;
248         if (mb.State == MEM_FREE &&
249             !VirtualAlloc ((void *) start, size, MEM_RESERVE, PAGE_NOACCESS))
250           api_fatal ("couldn't allocate memory %p(%d) for '%s' alignment, %E\n",
251                      start, size, name);
252       }
253 }
254
255 /* Release all of the memory previously allocated by "upto" above.
256    Note that this may also free otherwise reserved memory.  If that becomes
257    a problem, we'll have to keep track of the memory that we reserve above. */
258 static void
259 release_upto (const char *name, DWORD here)
260 {
261   DWORD size;
262   MEMORY_BASIC_INFORMATION mb;
263   for (DWORD start = 0x10000; start < here; start += size)
264     if (!VirtualQuery ((void *) start, &mb, sizeof (mb)))
265       size = 64 * 1024;
266     else
267       {
268         size = mb.RegionSize;
269         if (!(mb.State == MEM_RESERVE && mb.AllocationProtect == PAGE_NOACCESS &&
270             (((void *) start < cygheap->user_heap.base
271               || (void *) start > cygheap->user_heap.top) &&
272              ((void *) start < (void *) cygheap
273               | (void *) start > (void *) ((char *) cygheap + CYGHEAPSIZE)))))
274           continue;
275         if (!VirtualFree ((void *) start, 0, MEM_RELEASE))
276           api_fatal ("couldn't release memory %p(%d) for '%s' alignment, %E\n",
277                      start, size, name);
278       }
279 }
280
281 /* Reload DLLs after a fork.  Iterates over the list of dynamically loaded DLLs
282    and attempts to load them in the same place as they were loaded in the parent. */
283 void
284 dll_list::load_after_fork (HANDLE parent, dll *first)
285 {
286   in_forkee = 1;
287   int try2 = 0;
288   dll d;
289
290   void *next = first;
291   while (next)
292     {
293       DWORD nb;
294       /* Read the dll structure from the parent. */
295       if (!ReadProcessMemory (parent, next, &d, sizeof (dll), &nb) ||
296           nb != sizeof (dll))
297         return;
298
299       /* We're only interested in dynamically loaded dlls.
300          Hopefully, this function wouldn't even have been called unless
301          the parent had some of those. */
302       if (d.type == DLL_LOAD)
303         {
304           bool unload = true;
305           HMODULE h = LoadLibraryEx (d.name, NULL, DONT_RESOLVE_DLL_REFERENCES);
306
307           if (!h)
308             system_printf ("can't reload %s", d.name);
309           /* See if DLL will load in proper place.  If so, free it and reload
310              it the right way.
311              It sort of stinks that we can't invert the order of the FreeLibrary
312              and LoadLibrary since Microsoft documentation seems to imply that that
313              should do what we want.  However, since the library was loaded above,
314              the second LoadLibrary does not execute it's startup code unless it
315              is first unloaded. */
316           else if (h == d.handle)
317             {
318               if (unload)
319                 {
320                   FreeLibrary (h);
321                   LoadLibrary (d.name);
322                 }
323             }
324           else if (try2)
325             api_fatal ("unable to remap %s to same address as parent(%p) != %p",
326                        d.name, d.handle, h);
327           else
328             {
329               /* It loaded in the wrong place.  Dunno why this happens but it always
330                  seems to happen when there are multiple DLLs attempting to load into
331                  the same address space.  In the "forked" process, the second DLL always
332                  loads into a different location. */
333               FreeLibrary (h);
334               /* Block all of the memory up to the new load address. */
335               reserve_upto (d.name, (DWORD) d.handle);
336               try2 = 1;         /* And try */
337               continue;         /*  again. */
338             }
339           /* If we reached here, and try2 is set, then there is a lot of memory to
340              release. */
341           if (try2)
342             {
343               release_upto (d.name, (DWORD) d.handle);
344               try2 = 0;
345             }
346         }
347       next = d.next;    /* Get the address of the next DLL. */
348     }
349   in_forkee = 0;
350 }
351
352 extern "C" int
353 dll_dllcrt0 (HMODULE h, per_process *p)
354 {
355   if (p == NULL)
356     p = &__cygwin_user_data;
357   else
358     *(p->impure_ptr_ptr) = __cygwin_user_data.impure_ptr;
359   bool initializing = in_forkee || cygwin_finished_initializing;
360
361   /* Partially initialize Cygwin guts for non-cygwin apps. */
362   if (dynamically_loaded && user_data->magic_biscuit == 0)
363     dll_crt0 (p);
364   else
365     check_sanity_and_sync (p);
366
367   dll_type type;
368
369   /* If this function is called before cygwin has finished
370      initializing, then the DLL must be a cygwin-aware DLL
371      that was explicitly linked into the program rather than
372      a dlopened DLL. */
373   if (!initializing)
374     type = DLL_LINK;
375   else
376     {
377       type = DLL_LOAD;
378       dlls.reload_on_fork = 1;
379     }
380
381   /* Allocate and initialize space for the DLL. */
382   dll *d = dlls.alloc (h, p, type);
383
384   /* If d == NULL, then something is broken.
385      Otherwise, if we've finished initializing, it's ok to
386      initialize the DLL.  If we haven't finished initializing,
387      it may not be safe to call the dll's "main" since not
388      all of cygwin's internal structures may have been set up. */
389   if (!d || (initializing && !d->init ()))
390     return -1;
391
392   return (DWORD) d;
393 }
394
395 /* OBSOLETE: This function is obsolescent and will go away in the
396    future.  Cygwin can now handle being loaded from a noncygwin app
397    using the same entry point. */
398
399 extern "C" int
400 dll_noncygwin_dllcrt0 (HMODULE h, per_process *p)
401 {
402   return dll_dllcrt0 (h, p);
403 }
404
405 extern "C" void
406 cygwin_detach_dll (dll *)
407 {
408   dlls.detach (__builtin_return_address (0));
409 }
410
411 extern "C" void
412 dlfork (int val)
413 {
414   dlls.reload_on_fork = val;
415 }
416
417 /* Called from various places to update all of the individual
418    ideas of the environ block.  Explain to me again why we didn't
419    just import __cygwin_environ? */
420 void __stdcall
421 update_envptrs ()
422 {
423   extern char ***main_environ;
424   for (dll *d = dlls.istart (DLL_ANY); d; d = dlls.inext ())
425     {
426         *(d->p.envptr) = __cygwin_environ;
427     }
428   *main_environ = __cygwin_environ;
429 }