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