]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* dll_init.cc |
2 | ||
bc837d22 | 3 | Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, |
61522196 | 4 | 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. |
1fd5e000 CF |
5 | |
6 | This software is a copyrighted work licensed under the terms of the | |
7 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
8 | details. */ | |
9 | ||
1fd5e000 | 10 | #include "winsup.h" |
9e2baf8d | 11 | #include "cygerrno.h" |
f0338f54 | 12 | #include "perprocess.h" |
71c17c54 | 13 | #include "sync.h" |
f0338f54 CF |
14 | #include "dll_init.h" |
15 | #include "environ.h" | |
1ff9f4b9 | 16 | #include "security.h" |
47063f00 | 17 | #include "path.h" |
7ac61736 | 18 | #include "fhandler.h" |
1ff9f4b9 CF |
19 | #include "dtable.h" |
20 | #include "cygheap.h" | |
6b7cd251 | 21 | #include "pinfo.h" |
44d2fc0a | 22 | #include "child_info.h" |
9aca6a48 | 23 | #include "cygtls.h" |
98a97ac6 | 24 | #include "exception.h" |
69d704be | 25 | #include <wchar.h> |
d5c4cd3f | 26 | #include <sys/reent.h> |
73bcd49b | 27 | #include <assert.h> |
1fd5e000 CF |
28 | |
29 | extern void __stdcall check_sanity_and_sync (per_process *); | |
30 | ||
29649132 CF |
31 | #define fabort fork_info->abort |
32 | ||
71f53a2f | 33 | dll_list dlls; |
1fd5e000 | 34 | |
71c17c54 CF |
35 | muto dll_list::protect; |
36 | ||
dda06573 | 37 | static bool dll_global_dtors_recorded; |
1fd5e000 | 38 | |
2eb392bd | 39 | /* Run destructors for all DLLs on exit. */ |
dda06573 | 40 | void |
f0227ea3 | 41 | dll_global_dtors () |
1fd5e000 | 42 | { |
3521d504 CF |
43 | /* Don't attempt to call destructors if we're still in fork processing |
44 | since that likely means fork is failing and everything will not have been | |
45 | set up. */ | |
46 | if (in_forkee) | |
47 | return; | |
dda06573 CF |
48 | int recorded = dll_global_dtors_recorded; |
49 | dll_global_dtors_recorded = false; | |
53364a1f | 50 | if (recorded && dlls.start.next) |
bee18f45 | 51 | for (dll *d = dlls.end; d != &dlls.start; d = d->prev) |
6282fe16 | 52 | d->run_dtors (); |
1fd5e000 CF |
53 | } |
54 | ||
2eb392bd CF |
55 | /* Run all constructors associated with a dll */ |
56 | void | |
57 | per_module::run_ctors () | |
1fd5e000 | 58 | { |
2eb392bd | 59 | void (**pfunc)() = ctors; |
1fd5e000 CF |
60 | |
61 | /* Run ctors backwards, so skip the first entry and find how many | |
62 | there are, then run them. */ | |
63 | ||
64 | if (pfunc) | |
65 | { | |
66 | int i; | |
67 | for (i = 1; pfunc[i]; i++); | |
68 | ||
f7632549 | 69 | for (int j = i - 1; j > 0; j--) |
1fd5e000 CF |
70 | (pfunc[j]) (); |
71 | } | |
72 | } | |
73 | ||
2eb392bd CF |
74 | /* Run all destructors associated with a dll */ |
75 | void | |
76 | per_module::run_dtors () | |
1fd5e000 | 77 | { |
2eb392bd | 78 | void (**pfunc)() = dtors; |
e7fd0883 CF |
79 | while (*++pfunc) |
80 | (*pfunc) (); | |
1fd5e000 CF |
81 | } |
82 | ||
2eb392bd CF |
83 | /* Initialize an individual DLL */ |
84 | int | |
85 | dll::init () | |
1fd5e000 | 86 | { |
2eb392bd | 87 | int ret = 1; |
1fd5e000 | 88 | |
61522196 | 89 | #ifndef __x86_64__ |
27f564e9 | 90 | /* This should be a no-op. Why didn't we just import this variable? */ |
f3e3e0e7 CF |
91 | if (!p.envptr) |
92 | p.envptr = &__cygwin_environ; | |
8d777a13 | 93 | else if (*(p.envptr) != __cygwin_environ) |
f3e3e0e7 | 94 | *(p.envptr) = __cygwin_environ; |
61522196 | 95 | #endif |
1fd5e000 | 96 | |
2eb392bd CF |
97 | /* Don't run constructors or the "main" if we've forked. */ |
98 | if (!in_forkee) | |
1fd5e000 CF |
99 | { |
100 | /* global contructors */ | |
2eb392bd | 101 | p.run_ctors (); |
1fd5e000 CF |
102 | |
103 | /* entry point of dll (use main of per_process with null args...) */ | |
2eb392bd | 104 | if (p.main) |
27f564e9 | 105 | ret = p.main (0, 0, 0); |
1fd5e000 CF |
106 | } |
107 | ||
108 | return ret; | |
109 | } | |
110 | ||
50124fc0 CV |
111 | /* Look for a dll based on the full path. |
112 | ||
113 | CV, 2012-03-04: Per MSDN, If a DLL with the same module name is already | |
114 | loaded in memory, the system uses the loaded DLL, no matter which directory | |
115 | it is in. The system does not search for the DLL. See | |
116 | http://msdn.microsoft.com/en-us/library/ms682586%28v=vs.85%29.aspx | |
117 | ||
118 | On 2012-02-08 I interpreted "module name" as "basename". So the assumption | |
119 | was that the Windows Loader does not load another DLL with the same basename, | |
120 | if one such DLL is already loaded. Consequentially I changed the code so | |
121 | that DLLs are only compared by basename. | |
122 | ||
123 | This assumption was obviously wrong, as the perl dynaloader proves. It | |
124 | loads multiple DLLs with the same basename into memory, just from different | |
125 | locations. This mechanism is broken when only comparing basenames in the | |
126 | below code. | |
127 | ||
128 | However, the original problem reported on 2012-02-07 was a result of | |
129 | a subtil difference between the paths returned by different calls to | |
130 | GetModuleFileNameW: Sometimes the path is a plain DOS path, sometimes | |
131 | it's preceeded by the long pathname prefix "\\?\". | |
132 | ||
133 | So I reverted the original change from 2012-02-08 and only applied the | |
134 | following fix: Check if the path is preceeded by a long pathname prefix, | |
135 | and, if so, drop it forthwith so that subsequent full path comparisons | |
75effa37 | 136 | work as expected. |
46f5dd59 | 137 | |
75effa37 CV |
138 | At least that was the original idea. In fact there are two case, linked |
139 | and runtime loaded DLLs, which have to be distinguished: | |
46f5dd59 | 140 | |
75effa37 CV |
141 | - Linked DLLs are loaded by only specifying the basename of the DLL and |
142 | searching it using the system DLL search order as given in the | |
143 | aforementioned MSDN URL. | |
144 | ||
145 | - Runtime loaded DLLs are specified with the full path since that's how | |
146 | dlopen works. | |
147 | ||
148 | In effect, we have to be careful not to mix linked and loaded DLLs. | |
149 | For more info how this gets accomplished, see the comments at the start | |
150 | of dll_list::alloc, as well as the comment preceeding the definition of | |
151 | the in_load_after_fork bool later in the file. */ | |
2eb392bd | 152 | dll * |
50124fc0 CV |
153 | dll_list::operator[] (const PWCHAR name) |
154 | { | |
155 | dll *d = &start; | |
156 | while ((d = d->next) != NULL) | |
157 | if (!wcscasecmp (name, d->name)) | |
158 | return d; | |
159 | ||
160 | return NULL; | |
161 | } | |
162 | ||
163 | /* Look for a dll based on the basename. */ | |
164 | dll * | |
165 | dll_list::find_by_modname (const PWCHAR modname) | |
1fd5e000 | 166 | { |
2eb392bd CF |
167 | dll *d = &start; |
168 | while ((d = d->next) != NULL) | |
9eba4de2 | 169 | if (!wcscasecmp (modname, d->modname)) |
2eb392bd | 170 | return d; |
1fd5e000 | 171 | |
2eb392bd | 172 | return NULL; |
1fd5e000 CF |
173 | } |
174 | ||
c43c5c16 | 175 | #define RETRIES 1000 |
2eb392bd | 176 | |
71f53a2f | 177 | /* Allocate space for a dll struct. */ |
2eb392bd CF |
178 | dll * |
179 | dll_list::alloc (HINSTANCE h, per_process *p, dll_type type) | |
1fd5e000 | 180 | { |
50124fc0 CV |
181 | WCHAR buf[NT_MAX_PATH]; |
182 | GetModuleFileNameW (h, buf, sizeof (buf)); | |
183 | PWCHAR name = buf; | |
184 | if (!wcsncmp (name, L"\\\\?\\", 4)) | |
26e60345 CV |
185 | { |
186 | name += 4; | |
187 | if (!wcsncmp (name, L"UNC\\", 4)) | |
188 | { | |
189 | name += 2; | |
190 | *name = L'\\'; | |
191 | } | |
192 | } | |
8f4ea5f0 | 193 | DWORD namelen = wcslen (name); |
f5b0d9d4 | 194 | PWCHAR modname = wcsrchr (name, L'\\') + 1; |
1fd5e000 | 195 | |
71c17c54 | 196 | guard (true); |
f5b0d9d4 CV |
197 | /* Already loaded? For linked DLLs, only compare the basenames. Linked |
198 | DLLs are loaded using just the basename and the default DLL search path. | |
199 | The Windows loader picks up the first one it finds. */ | |
200 | dll *d = (type == DLL_LINK) ? dlls.find_by_modname (modname) : dlls[name]; | |
2eb392bd | 201 | if (d) |
1fd5e000 | 202 | { |
71f53a2f CF |
203 | if (!in_forkee) |
204 | d->count++; /* Yes. Bump the usage count. */ | |
8f4ea5f0 CV |
205 | else if (d->handle != h) |
206 | fabort ("%W: Loaded to different address: parent(%p) != child(%p)", | |
50124fc0 | 207 | name, d->handle, h); |
f5b0d9d4 CV |
208 | /* If this DLL has been linked against, and the full path differs, try |
209 | to sanity check if this is the same DLL, just in another path. */ | |
210 | else if (type == DLL_LINK && wcscasecmp (name, d->name) | |
211 | && (d->p.data_start != p->data_start | |
212 | || d->p.data_start != p->data_start | |
213 | || d->p.bss_start != p->bss_start | |
214 | || d->p.bss_end != p->bss_end | |
215 | || d->p.ctors != p->ctors | |
216 | || d->p.dtors != p->dtors)) | |
217 | fabort ("\nLoaded different DLL with same basename in forked child,\n" | |
218 | "parent loaded: %W\n" | |
219 | " child loaded: %W\n" | |
220 | "The DLLs differ, so it's not safe to run the forked child.\n" | |
221 | "Make sure to remove the offending DLL before trying again.", | |
222 | d->name, name); | |
8978381c | 223 | d->p = p; |
1fd5e000 | 224 | } |
73bcd49b CF |
225 | else |
226 | { | |
227 | /* FIXME: Change this to new at some point. */ | |
228 | d = (dll *) cmalloc (HEAP_2_DLL, sizeof (*d) + (namelen * sizeof (*name))); | |
229 | ||
8f4ea5f0 CV |
230 | /* Now we've allocated a block of information. Fill it in with the |
231 | supplied info about this DLL. */ | |
73bcd49b CF |
232 | d->count = 1; |
233 | wcscpy (d->name, name); | |
f5b0d9d4 | 234 | d->modname = d->name + (modname - name); |
73bcd49b CF |
235 | d->handle = h; |
236 | d->has_dtors = true; | |
237 | d->p = p; | |
977ad543 CF |
238 | d->ndeps = 0; |
239 | d->deps = NULL; | |
6cd2e185 | 240 | d->image_size = ((pefile*)h)->optional_hdr ()->SizeOfImage; |
6642f7da | 241 | d->preferred_base = (void*) ((pefile*)h)->optional_hdr()->ImageBase; |
73bcd49b | 242 | d->type = type; |
977ad543 | 243 | append (d); |
73bcd49b CF |
244 | if (type == DLL_LOAD) |
245 | loaded_dlls++; | |
246 | } | |
71c17c54 | 247 | guard (false); |
61522196 | 248 | #ifndef __x86_64__ |
73bcd49b | 249 | assert (p->envptr != NULL); |
61522196 | 250 | #endif |
2eb392bd | 251 | return d; |
1fd5e000 CF |
252 | } |
253 | ||
977ad543 CF |
254 | void |
255 | dll_list::append (dll* d) | |
256 | { | |
257 | if (end == NULL) | |
258 | end = &start; /* Point to "end" of dll chain. */ | |
259 | end->next = d; /* Standard linked list stuff. */ | |
260 | d->next = NULL; | |
261 | d->prev = end; | |
262 | end = d; | |
977ad543 CF |
263 | } |
264 | ||
265 | void dll_list::populate_deps (dll* d) | |
266 | { | |
267 | WCHAR wmodname[NT_MAX_PATH]; | |
268 | pefile* pef = (pefile*) d->handle; | |
269 | PIMAGE_DATA_DIRECTORY dd = pef->idata_dir (IMAGE_DIRECTORY_ENTRY_IMPORT); | |
270 | /* Annoyance: calling crealloc with a NULL pointer will use the | |
271 | wrong heap and crash, so we have to replicate some code */ | |
272 | long maxdeps = 4; | |
273 | d->deps = (dll**) cmalloc (HEAP_2_DLL, maxdeps*sizeof (dll*)); | |
274 | d->ndeps = 0; | |
275 | for (PIMAGE_IMPORT_DESCRIPTOR id= | |
276 | (PIMAGE_IMPORT_DESCRIPTOR) pef->rva (dd->VirtualAddress); | |
277 | dd->Size && id->Name; | |
278 | id++) | |
279 | { | |
280 | char* modname = pef->rva (id->Name); | |
281 | sys_mbstowcs (wmodname, NT_MAX_PATH, modname); | |
50124fc0 | 282 | if (dll* dep = find_by_modname (wmodname)) |
977ad543 CF |
283 | { |
284 | if (d->ndeps >= maxdeps) | |
285 | { | |
286 | maxdeps = 2*(1+maxdeps); | |
287 | d->deps = (dll**) crealloc (d->deps, maxdeps*sizeof (dll*)); | |
288 | } | |
289 | d->deps[d->ndeps++] = dep; | |
290 | } | |
291 | } | |
6642f7da | 292 | |
977ad543 CF |
293 | /* add one to differentiate no deps from unknown */ |
294 | d->ndeps++; | |
295 | } | |
296 | ||
297 | ||
298 | void | |
299 | dll_list::topsort () | |
300 | { | |
301 | /* Anything to do? */ | |
302 | if (!end) | |
303 | return; | |
6642f7da | 304 | |
977ad543 CF |
305 | /* make sure we have all the deps available */ |
306 | dll* d = &start; | |
307 | while ((d = d->next)) | |
308 | if (!d->ndeps) | |
309 | populate_deps (d); | |
6642f7da | 310 | |
977ad543 CF |
311 | /* unlink head and tail pointers so the sort can rebuild the list */ |
312 | d = start.next; | |
313 | start.next = end = NULL; | |
314 | topsort_visit (d, true); | |
315 | ||
316 | /* clear node markings made by the sort */ | |
317 | d = &start; | |
318 | while ((d = d->next)) | |
319 | { | |
29649132 | 320 | #ifdef DEBUGGING |
9eba4de2 | 321 | paranoid_printf ("%W", d->modname); |
29649132 | 322 | for (int i = 1; i < -d->ndeps; i++) |
9eba4de2 | 323 | paranoid_printf ("-> %W", d->deps[i - 1]->modname); |
29649132 | 324 | #endif |
977ad543 CF |
325 | |
326 | /* It would be really nice to be able to keep this information | |
327 | around for next time, but we don't have an easy way to | |
328 | invalidate cached dependencies when a module unloads. */ | |
329 | d->ndeps = 0; | |
330 | cfree (d->deps); | |
331 | d->deps = NULL; | |
332 | } | |
333 | } | |
334 | ||
335 | /* A recursive in-place topological sort. The result is ordered so that | |
336 | dependencies of a dll appear before it in the list. | |
337 | ||
338 | NOTE: this algorithm is guaranteed to terminate with a "partial | |
339 | order" of dlls but does not do anything smart about cycles: an | |
340 | arbitrary dependent dll will necessarily appear first. Perhaps not | |
341 | surprisingly, Windows ships several dlls containing dependency | |
342 | cycles, including SspiCli/RPCRT4.dll and a lovely tangle involving | |
343 | USP10/LPK/GDI32/USER32.dll). Fortunately, we don't care about | |
344 | Windows DLLs here, and cygwin dlls should behave better */ | |
345 | void | |
346 | dll_list::topsort_visit (dll* d, bool seek_tail) | |
347 | { | |
348 | /* Recurse to the end of the dll chain, then visit nodes as we | |
349 | unwind. We do this because once we start visiting nodes we can no | |
350 | longer trust any _next_ pointers. | |
351 | ||
352 | We "mark" visited nodes (to avoid revisiting them) by negating | |
353 | ndeps (undone once the sort completes). */ | |
354 | if (seek_tail && d->next) | |
355 | topsort_visit (d->next, true); | |
6642f7da | 356 | |
977ad543 CF |
357 | if (d->ndeps > 0) |
358 | { | |
359 | d->ndeps = -d->ndeps; | |
29649132 CF |
360 | for (long i = 1; i < -d->ndeps; i++) |
361 | topsort_visit (d->deps[i - 1], false); | |
977ad543 CF |
362 | |
363 | append (d); | |
364 | } | |
365 | } | |
366 | ||
367 | ||
fc6a0dc8 CF |
368 | dll * |
369 | dll_list::find (void *retaddr) | |
d5c4cd3f | 370 | { |
fc6a0dc8 CF |
371 | MEMORY_BASIC_INFORMATION m; |
372 | if (!VirtualQuery (retaddr, &m, sizeof m)) | |
373 | return NULL; | |
374 | HMODULE h = (HMODULE) m.AllocationBase; | |
375 | ||
376 | dll *d = &start; | |
377 | while ((d = d->next)) | |
378 | if (d->handle == h) | |
379 | break; | |
380 | return d; | |
d5c4cd3f CF |
381 | } |
382 | ||
2eb392bd | 383 | /* Detach a DLL from the chain. */ |
1fd5e000 | 384 | void |
052990e6 | 385 | dll_list::detach (void *retaddr) |
1fd5e000 | 386 | { |
fc6a0dc8 | 387 | dll *d; |
97575769 CF |
388 | /* Don't attempt to call destructors if we're still in fork processing |
389 | since that likely means fork is failing and everything will not have been | |
390 | set up. */ | |
391 | if (!myself || in_forkee) | |
052990e6 | 392 | return; |
71c17c54 CF |
393 | guard (true); |
394 | if ((d = find (retaddr))) | |
fc6a0dc8 | 395 | { |
71c17c54 CF |
396 | if (d->count <= 0) |
397 | system_printf ("WARNING: trying to detach an already detached dll ..."); | |
398 | if (--d->count == 0) | |
399 | { | |
400 | /* Ensure our exception handler is enabled for destructors */ | |
401 | exception protect; | |
402 | /* Call finalize function if we are not already exiting */ | |
403 | if (!exit_state) | |
404 | __cxa_finalize (d); | |
405 | d->run_dtors (); | |
406 | d->prev->next = d->next; | |
407 | if (d->next) | |
408 | d->next->prev = d->prev; | |
409 | if (d->type == DLL_LOAD) | |
410 | loaded_dlls--; | |
411 | if (end == d) | |
412 | end = d->prev; | |
413 | cfree (d); | |
414 | } | |
fc6a0dc8 | 415 | } |
71c17c54 | 416 | guard (false); |
1fd5e000 CF |
417 | } |
418 | ||
c77c4419 | 419 | /* Initialization for all linked DLLs, called by dll_crt0_1. */ |
1fd5e000 | 420 | void |
2eb392bd | 421 | dll_list::init () |
1fd5e000 | 422 | { |
2eb392bd CF |
423 | /* Walk the dll chain, initializing each dll */ |
424 | dll *d = &start; | |
53364a1f | 425 | dll_global_dtors_recorded = d->next != NULL; |
2eb392bd CF |
426 | while ((d = d->next)) |
427 | d->init (); | |
1fd5e000 CF |
428 | } |
429 | ||
430 | #define A64K (64 * 1024) | |
431 | ||
1fd5e000 | 432 | |
6cd2e185 CF |
433 | /* Reserve the chunk of free address space starting _here_ and (usually) |
434 | covering at least _dll_size_ bytes. However, we must take care not | |
435 | to clobber the dll's target address range because it often overlaps. | |
436 | */ | |
61522196 CV |
437 | static PVOID |
438 | reserve_at (const PWCHAR name, PVOID here, PVOID dll_base, DWORD dll_size) | |
8d777a13 CF |
439 | { |
440 | DWORD size; | |
441 | MEMORY_BASIC_INFORMATION mb; | |
442 | ||
61522196 CV |
443 | if (!VirtualQuery (here, &mb, sizeof (mb))) |
444 | fabort ("couldn't examine memory at %p while mapping %W, %E", here, name); | |
8d777a13 CF |
445 | if (mb.State != MEM_FREE) |
446 | return 0; | |
447 | ||
448 | size = mb.RegionSize; | |
6642f7da | 449 | |
6cd2e185 | 450 | // don't clobber the space where we want the dll to land |
61522196 CV |
451 | caddr_t end = (caddr_t) here + size; |
452 | caddr_t dll_end = (caddr_t) dll_base + dll_size; | |
453 | if (dll_base < here && dll_end > (caddr_t) here) | |
454 | here = (PVOID) dll_end; // the dll straddles our left edge | |
455 | else if (dll_base >= here && (caddr_t) dll_base < end) | |
456 | end = (caddr_t) dll_base; // the dll overlaps partly or fully to our right | |
457 | ||
458 | size = end - (caddr_t) here; | |
459 | if (!VirtualAlloc (here, size, MEM_RESERVE, PAGE_NOACCESS)) | |
29649132 CF |
460 | fabort ("couldn't allocate memory %p(%d) for '%W' alignment, %E\n", |
461 | here, size, name); | |
8d777a13 CF |
462 | return here; |
463 | } | |
464 | ||
465 | /* Release the memory previously allocated by "reserve_at" above. */ | |
466 | static void | |
61522196 | 467 | release_at (const PWCHAR name, PVOID here) |
8d777a13 | 468 | { |
61522196 | 469 | if (!VirtualFree (here, 0, MEM_RELEASE)) |
29649132 CF |
470 | fabort ("couldn't release memory %p for '%W' alignment, %E\n", |
471 | here, name); | |
8d777a13 CF |
472 | } |
473 | ||
6642f7da CF |
474 | /* Step 1: Reserve memory for all DLL_LOAD dlls. This is to prevent |
475 | anything else from taking their spot as we compensate for Windows | |
476 | randomly relocating things. | |
477 | ||
478 | NOTE: because we can't depend on LoadLibraryExW to do the right | |
479 | thing, we have to do a vanilla VirtualAlloc instead. One possible | |
480 | optimization might attempt a LoadLibraryExW first, in case it lands | |
481 | in the right place, but then we have to find a way of tracking | |
482 | which dlls ended up needing VirtualAlloc after all. */ | |
483 | void | |
484 | dll_list::reserve_space () | |
485 | { | |
486 | for (dll* d = dlls.istart (DLL_LOAD); d; d = dlls.inext ()) | |
487 | if (!VirtualAlloc (d->handle, d->image_size, MEM_RESERVE, PAGE_NOACCESS)) | |
6a288492 | 488 | fabort ("address space needed by '%W' (%p) is already occupied", |
9eba4de2 | 489 | d->modname, d->handle); |
6642f7da CF |
490 | } |
491 | ||
f5b0d9d4 CV |
492 | /* We need the in_load_after_fork flag so dll_dllcrt0_1 can decide at fork |
493 | time if this is a linked DLL or a dynamically loaded DLL. In either case, | |
494 | both, cygwin_finished_initializing and in_forkee are true, so they are not | |
495 | sufficient to discern the situation. */ | |
496 | static bool NO_COPY in_load_after_fork; | |
497 | ||
1d95e198 | 498 | /* Reload DLLs after a fork. Iterates over the list of dynamically loaded |
71f53a2f CF |
499 | DLLs and attempts to load them in the same place as they were loaded in the |
500 | parent. */ | |
1fd5e000 | 501 | void |
71f53a2f | 502 | dll_list::load_after_fork (HANDLE parent) |
1fd5e000 | 503 | { |
6642f7da CF |
504 | // moved to frok::child for performance reasons: |
505 | // dll_list::reserve_space(); | |
8d777a13 | 506 | |
f5b0d9d4 | 507 | in_load_after_fork = true; |
6642f7da | 508 | load_after_fork_impl (parent, dlls.istart (DLL_LOAD), 0); |
f5b0d9d4 | 509 | in_load_after_fork = false; |
6642f7da | 510 | } |
8d777a13 | 511 | |
6642f7da CF |
512 | static int const DLL_RETRY_MAX = 6; |
513 | void dll_list::load_after_fork_impl (HANDLE parent, dll* d, int retries) | |
514 | { | |
515 | /* Step 2: For each dll which did not map at its preferred base | |
516 | address in the parent, try to coerce it to land at the same spot | |
517 | as before. If not, unload it, reserve the memory around it, and | |
518 | try again. Use recursion to remember blocked regions address | |
519 | space so we can release them later. | |
520 | ||
521 | We DONT_RESOLVE_DLL_REFERENCES at first in case the DLL lands in | |
522 | the wrong spot; | |
523 | ||
524 | NOTE: This step skips DLLs which loaded at their preferred | |
525 | address in the parent because they should behave (we already | |
526 | verified that their preferred address in the child is | |
527 | available). However, this may fail on a Vista/Win7 machine with | |
528 | ASLR active, because the ASLR base address will usually not equal | |
529 | the preferred base recorded in the dll. In this case, we should | |
530 | make the LoadLibraryExW call unconditional. | |
531 | */ | |
532 | for ( ; d; d = dlls.inext ()) | |
533 | if (d->handle != d->preferred_base) | |
534 | { | |
535 | /* See if the DLL will load in proper place. If not, unload it, | |
536 | reserve the memory around it, and try again. | |
537 | ||
538 | If this is the first attempt, we need to release the | |
539 | dll's protective reservation from step 1 | |
540 | */ | |
541 | if (!retries && !VirtualFree (d->handle, 0, MEM_RELEASE)) | |
61522196 | 542 | fabort ("unable to release protective reservation for %W (%p), %E", |
9eba4de2 | 543 | d->modname, d->handle); |
b86f999a | 544 | |
6642f7da CF |
545 | HMODULE h = LoadLibraryExW (d->name, NULL, DONT_RESOLVE_DLL_REFERENCES); |
546 | if (!h) | |
9eba4de2 | 547 | fabort ("unable to create interim mapping for %W, %E", d->name); |
6642f7da CF |
548 | if (h != d->handle) |
549 | { | |
61522196 | 550 | sigproc_printf ("%W loaded in wrong place: %p != %p", |
9eba4de2 | 551 | d->modname, h, d->handle); |
6642f7da | 552 | FreeLibrary (h); |
61522196 CV |
553 | PVOID reservation = reserve_at (d->modname, h, |
554 | d->handle, d->image_size); | |
6642f7da | 555 | if (!reservation) |
29649132 | 556 | fabort ("unable to block off %p to prevent %W from loading there", |
9eba4de2 | 557 | h, d->modname); |
6642f7da CF |
558 | |
559 | if (retries < DLL_RETRY_MAX) | |
560 | load_after_fork_impl (parent, d, retries+1); | |
561 | else | |
61522196 | 562 | fabort ("unable to remap %W to same address as parent (%p) - try running rebaseall", |
9eba4de2 | 563 | d->modname, d->handle); |
6642f7da CF |
564 | |
565 | /* once the above returns all the dlls are mapped; release | |
566 | the reservation and continue unwinding */ | |
61522196 | 567 | sigproc_printf ("releasing blocked space at %p", reservation); |
9eba4de2 | 568 | release_at (d->modname, reservation); |
6642f7da CF |
569 | return; |
570 | } | |
571 | } | |
8d777a13 | 572 | |
6642f7da CF |
573 | /* Step 3: try to load each dll for real after either releasing the |
574 | protective reservation (for well-behaved dlls) or unloading the | |
575 | interim mapping (for rebased dlls) . The dll list is sorted in | |
576 | dependency order, so we shouldn't pull in any additional dlls | |
29649132 | 577 | outside our control. */ |
6642f7da CF |
578 | for (dll *d = dlls.istart (DLL_LOAD); d; d = dlls.inext ()) |
579 | { | |
580 | if (d->handle == d->preferred_base) | |
581 | { | |
582 | if (!VirtualFree (d->handle, 0, MEM_RELEASE)) | |
61522196 | 583 | fabort ("unable to release protective reservation for %W (%p), %E", |
9eba4de2 | 584 | d->modname, d->handle); |
2eb392bd | 585 | } |
6642f7da CF |
586 | else |
587 | { | |
588 | /* Free the library using our parent's handle: it's identical | |
29649132 | 589 | to ours or we wouldn't have gotten this far */ |
6642f7da | 590 | if (!FreeLibrary (d->handle)) |
29649132 | 591 | fabort ("unable to unload interim mapping of %W, %E", |
9eba4de2 | 592 | d->modname); |
6642f7da CF |
593 | } |
594 | HMODULE h = LoadLibraryW (d->name); | |
595 | if (!h) | |
29649132 | 596 | fabort ("unable to map %W, %E", d->name); |
6642f7da | 597 | if (h != d->handle) |
29649132 | 598 | fabort ("unable to map %W to same address as parent: %p != %p", |
9eba4de2 | 599 | d->modname, d->handle, h); |
6642f7da | 600 | } |
1fd5e000 CF |
601 | } |
602 | ||
2346864a CF |
603 | struct dllcrt0_info |
604 | { | |
605 | HMODULE h; | |
606 | per_process *p; | |
61522196 | 607 | PVOID res; |
8d777a13 | 608 | dllcrt0_info (HMODULE h0, per_process *p0): h (h0), p (p0) {} |
2346864a CF |
609 | }; |
610 | ||
61522196 | 611 | extern "C" PVOID |
1fd5e000 CF |
612 | dll_dllcrt0 (HMODULE h, per_process *p) |
613 | { | |
5025bf33 | 614 | if (dynamically_loaded) |
61522196 | 615 | return (PVOID) 1; |
2346864a | 616 | dllcrt0_info x (h, p); |
5025bf33 | 617 | dll_dllcrt0_1 (&x); |
2346864a CF |
618 | return x.res; |
619 | } | |
620 | ||
621 | void | |
622 | dll_dllcrt0_1 (VOID *x) | |
623 | { | |
8d777a13 CF |
624 | HMODULE& h = ((dllcrt0_info *) x)->h; |
625 | per_process*& p = ((dllcrt0_info *) x)->p; | |
61522196 | 626 | PVOID& res = ((dllcrt0_info *) x)->res; |
2346864a | 627 | |
737a86d3 CF |
628 | if (p == NULL) |
629 | p = &__cygwin_user_data; | |
630 | else | |
27f564e9 CF |
631 | { |
632 | *(p->impure_ptr_ptr) = __cygwin_user_data.impure_ptr; | |
633 | _pei386_runtime_relocator (p); | |
634 | } | |
737a86d3 | 635 | |
f5b0d9d4 | 636 | bool linked = !cygwin_finished_initializing && !in_load_after_fork; |
29d1e62e | 637 | |
ce5eb135 CV |
638 | /* Broken DLLs built against Cygwin versions 1.7.0-49 up to 1.7.0-57 |
639 | override the cxx_malloc pointer in their DLL initialization code, | |
640 | when loaded either statically or dynamically. Because this leaves | |
641 | a stale pointer into demapped memory space if the DLL is unloaded | |
642 | by a call to dlclose, we prevent this happening for dynamically | |
643 | loaded DLLS in dlopen by saving and restoring cxx_malloc around | |
644 | the call to LoadLibrary, which invokes the DLL's startup sequence. | |
645 | Modern DLLs won't even attempt to override the pointer when loaded | |
646 | statically, but will write their overrides directly into the | |
647 | struct it points to. With all modern DLLs, this will remain the | |
648 | default_cygwin_cxx_malloc struct in cxx.cc, but if any broken DLLs | |
649 | are in the mix they will have overridden the pointer and subsequent | |
650 | overrides will go into their embedded cxx_malloc structs. This is | |
651 | almost certainly not a problem as they can never be unloaded, but | |
652 | if we ever did want to do anything about it, we could check here to | |
653 | see if the pointer had been altered in the early parts of the DLL's | |
654 | startup, and if so copy back the new overrides and reset it here. | |
655 | However, that's just a note for the record; at the moment, we can't | |
656 | see any need to worry about this happening. */ | |
657 | ||
5025bf33 | 658 | check_sanity_and_sync (p); |
2eb392bd CF |
659 | |
660 | dll_type type; | |
661 | ||
662 | /* If this function is called before cygwin has finished | |
663 | initializing, then the DLL must be a cygwin-aware DLL | |
664 | that was explicitly linked into the program rather than | |
665 | a dlopened DLL. */ | |
2346864a | 666 | if (linked) |
2eb392bd CF |
667 | type = DLL_LINK; |
668 | else | |
669 | { | |
670 | type = DLL_LOAD; | |
671 | dlls.reload_on_fork = 1; | |
672 | } | |
673 | ||
674 | /* Allocate and initialize space for the DLL. */ | |
675 | dll *d = dlls.alloc (h, p, type); | |
676 | ||
677 | /* If d == NULL, then something is broken. | |
678 | Otherwise, if we've finished initializing, it's ok to | |
679 | initialize the DLL. If we haven't finished initializing, | |
680 | it may not be safe to call the dll's "main" since not | |
681 | all of cygwin's internal structures may have been set up. */ | |
2346864a | 682 | if (!d || (!linked && !d->init ())) |
61522196 | 683 | res = (PVOID) -1; |
2346864a | 684 | else |
61522196 | 685 | res = (PVOID) d; |
1fd5e000 CF |
686 | } |
687 | ||
61522196 | 688 | #ifndef __x86_64__ |
2b37c431 | 689 | /* OBSOLETE: This function is obsolete and will go away in the |
1fd5e000 CF |
690 | future. Cygwin can now handle being loaded from a noncygwin app |
691 | using the same entry point. */ | |
2eb392bd | 692 | extern "C" int |
1fd5e000 CF |
693 | dll_noncygwin_dllcrt0 (HMODULE h, per_process *p) |
694 | { | |
61522196 | 695 | return (int) dll_dllcrt0 (h, p); |
1fd5e000 | 696 | } |
61522196 | 697 | #endif /* !__x86_64__ */ |
1fd5e000 | 698 | |
2eb392bd | 699 | extern "C" void |
052990e6 | 700 | cygwin_detach_dll (dll *) |
1fd5e000 | 701 | { |
51f90b2f CF |
702 | HANDLE retaddr; |
703 | if (_my_tls.isinitialized ()) | |
fc6a0dc8 | 704 | retaddr = (void *) _my_tls.retaddr (); |
51f90b2f CF |
705 | else |
706 | retaddr = __builtin_return_address (0); | |
707 | dlls.detach (retaddr); | |
1fd5e000 CF |
708 | } |
709 | ||
2eb392bd | 710 | extern "C" void |
1fd5e000 CF |
711 | dlfork (int val) |
712 | { | |
2eb392bd | 713 | dlls.reload_on_fork = val; |
1fd5e000 CF |
714 | } |
715 | ||
61522196 | 716 | #ifndef __x86_64__ |
2eb392bd CF |
717 | /* Called from various places to update all of the individual |
718 | ideas of the environ block. Explain to me again why we didn't | |
719 | just import __cygwin_environ? */ | |
720 | void __stdcall | |
721 | update_envptrs () | |
722 | { | |
2eb392bd | 723 | for (dll *d = dlls.istart (DLL_ANY); d; d = dlls.inext ()) |
8d777a13 CF |
724 | if (*(d->p.envptr) != __cygwin_environ) |
725 | *(d->p.envptr) = __cygwin_environ; | |
2eb392bd CF |
726 | *main_environ = __cygwin_environ; |
727 | } | |
61522196 | 728 | #endif |