diff options
author | Michael Haubenwallner <michael.haubenwallner@ssi-schaefer.com> | 2016-12-07 13:58:25 +0300 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2019-02-07 17:58:02 +0300 |
commit | 2678c4efe1195ffb49d92a899c10d4c9fa168eb0 (patch) | |
tree | bd7c8b77e59806e5448bb26d0cf45491e3af9c08 /winsup/cygwin | |
parent | 9fa22dba558f5e3efd0bb719049491edcd7b5e0b (diff) |
dll_list: Store dll file name as full NT path.
Store loaded dll's file name as full NT path.
* dll_init.h (struct dll): Rename member variable name to ntname.
(struct dll_list): Declare private static member variable
nt_max_path_buffer. Declare public static methods form_ntname,
form_shortname. Define public static methods nt_max_path_buf,
buffered_shortname.
(dll_list::operator []): Use PCWCHAR rather than const PWCHAR.
(dll_list::find_by_modname): Ditto.
* dll_init.cc (in_load_after_fork): Define earlier in file.
(struct dll_list): Rename member variable name to ntname.
Define nt_max_path_buffer variable.
Implement static methods form_ntname, form_shortname.
(dll_list::operator []): Use PCWCHAR rather than const PWCHAR.
(dll_list::find_by_modname): Ditto.
(reserve_at): Ditto.
(release_at): Ditto.
(dll_list::alloc): Use nt_max_path_buf method instead of local
buffer. Store module file name as full NT path, convert using
the form_ntname static method.
(dll_list::load_after_fork): Call load_after_fork_impl only when
reload_on_fork is set.
* fork.cc (frok::child): Call dlls.load_after_fork even without
need to dynamically load dlls.
(frok::parent): Move syscall_printf into the retry loop.
Diffstat (limited to 'winsup/cygwin')
-rw-r--r-- | winsup/cygwin/dll_init.cc | 208 | ||||
-rw-r--r-- | winsup/cygwin/dll_init.h | 20 | ||||
-rw-r--r-- | winsup/cygwin/fork.cc | 28 |
3 files changed, 187 insertions, 69 deletions
diff --git a/winsup/cygwin/dll_init.cc b/winsup/cygwin/dll_init.cc index 549e5cccd..f94c6698d 100644 --- a/winsup/cygwin/dll_init.cc +++ b/winsup/cygwin/dll_init.cc @@ -30,10 +30,129 @@ extern void __stdcall check_sanity_and_sync (per_process *); dll_list dlls; +WCHAR NO_COPY dll_list::nt_max_path_buffer[NT_MAX_PATH]; + muto dll_list::protect; static bool dll_global_dtors_recorded; +/* We need the in_load_after_fork flag so dll_dllcrt0_1 can decide at fork + time if this is a linked DLL or a dynamically loaded DLL. In either case, + both, cygwin_finished_initializing and in_forkee are true, so they are not + sufficient to discern the situation. */ +static bool NO_COPY in_load_after_fork; + +/* Into ntbuf with ntbufsize, prints name prefixed with "\\??\\" + or "\\??\\UNC" as necessary to form the native NT path name. + Returns the end of the resulting string in ntbuf. + Supports using (a substring of) ntbuf as name argument. */ +PWCHAR dll_list::form_ntname (PWCHAR ntbuf, size_t ntbufsize, PCWCHAR name) +{ + while (true) + { + /* avoid using path_conv here: cygheap might not be + initialized when started from non-cygwin process, + or still might be frozen in_forkee */ + if (name[0] == L'\0' || ntbufsize < 8) + break; + if (name[1] == L':') /* short Win32 drive letter path name */ + { + int winlen = min (ntbufsize - 5, wcslen (name)); + if (ntbuf + 4 != name) + memmove (ntbuf + 4, name, sizeof (*ntbuf) * winlen); + wcsncpy (ntbuf, L"\\??\\", 4); + ntbuf += 4 + winlen; + break; + } + if (!wcsncmp (name, L"\\\\?\\", 4)) /* long Win32 path name */ + { + int winlen = min (ntbufsize - 1, wcslen (name)); + if (ntbuf != name) + memmove (ntbuf, name, sizeof (*ntbuf) * winlen); + ntbuf[1] = L'?'; + ntbuf += winlen; + break; + } + if (!wcsncmp (name, L"\\\\", 2)) /* short Win32 UNC path name */ + { + name += 1; /* skip first backslash */ + int winlen = min (ntbufsize - 8, wcslen (name)); + if (ntbuf + 7 != name) + memmove (ntbuf + 7, name, sizeof (*ntbuf) * winlen); + wcsncpy (ntbuf, L"\\??\\UNC", 7); + ntbuf += 7 + winlen; + break; + } + if (!wcsncmp (name, L"\\??\\", 4)) /* already a long NT path name */ + { + int winlen = min (ntbufsize - 1, wcslen (name)); + if (ntbuf != name) + memmove (ntbuf, name, sizeof (*ntbuf) * winlen); + ntbuf += winlen; + break; + } + system_printf ("WARNING: invalid path name '%W'", name); + break; + } + if (ntbufsize) + *ntbuf = L'\0'; + return ntbuf; +} + +/* Into shortbuf with shortbufsize, prints name with "\\??\\" + or "\\??\\UNC" prefix removed/modified as necessary to form + the short Win32 path name. + Returns the end of the resulting string in shortbuf. + Supports using (a substring of) shortbuf as name argument. */ +PWCHAR +dll_list::form_shortname (PWCHAR shortbuf, size_t shortbufsize, PCWCHAR name) +{ + while (true) + { + /* avoid using path_conv here: cygheap might not be + initialized when started from non-cygwin process, + or still might be frozen in_forkee */ + if (name[0] == L'\0' || shortbufsize < 2) + break; + if (name[0] == L'\\' && + (name[1] == L'\\' || name[1] == L'?') && + name[2] == L'?' && + name[3] == L'\\') /* long Win32 or NT path name */ + name += 4; + if (name[1] == L':') /* short Win32 drive letter path name */ + { + int ntlen = min (shortbufsize - 1, wcslen (name)); + if (shortbuf != name) + memmove (shortbuf, name, sizeof (*shortbuf) * ntlen); + shortbuf += ntlen; + break; + } + if (!wcsncmp (name, L"UNC\\", 4)) /* UNC path name */ + { + name += 3; /* skip "UNC" */ + int winlen = min (shortbufsize - 2, wcslen (name)); + if (shortbuf + 1 != name) + memmove (shortbuf + 1, name, sizeof (*shortbuf) * winlen); + shortbuf[0] = L'\\'; + shortbuf += 1 + winlen; + break; + } + if (!wcsncmp (name, L"\\\\", 2)) /* already a short Win32 UNC path name */ + { + int winlen = min (shortbufsize - 1, wcslen (name)); + if (shortbuf != name) + memmove (shortbuf, name, sizeof (*shortbuf) * winlen); + shortbuf += winlen; + break; + } + system_printf ("WARNING: invalid path name '%W'", name); + break; + } + if (shortbufsize) + *shortbuf = L'\0'; + return shortbuf; +} + /* Run destructors for all DLLs on exit. */ void dll_global_dtors () @@ -148,11 +267,11 @@ dll::init () of dll_list::alloc, as well as the comment preceeding the definition of the in_load_after_fork bool later in the file. */ dll * -dll_list::operator[] (const PWCHAR name) +dll_list::operator[] (PCWCHAR ntname) { dll *d = &start; while ((d = d->next) != NULL) - if (!wcscasecmp (name, d->name)) + if (!wcscasecmp (ntname, d->ntname)) return d; return NULL; @@ -160,7 +279,7 @@ dll_list::operator[] (const PWCHAR name) /* Look for a dll based on the basename. */ dll * -dll_list::find_by_modname (const PWCHAR modname) +dll_list::find_by_modname (PCWCHAR modname) { dll *d = &start; while ((d = d->next) != NULL) @@ -177,58 +296,47 @@ dll * dll_list::alloc (HINSTANCE h, per_process *p, dll_type type) { /* Called under loader lock conditions so this function can't be called - multiple times in parallel. A static buffer is safe. */ - static WCHAR buf[NT_MAX_PATH]; - GetModuleFileNameW (h, buf, NT_MAX_PATH); - PWCHAR name = buf; - if (!wcsncmp (name, L"\\\\?\\", 4)) - { - name += 4; - if (!wcsncmp (name, L"UNC\\", 4)) - { - name += 2; - *name = L'\\'; - } - } - DWORD namelen = wcslen (name); - PWCHAR modname = wcsrchr (name, L'\\') + 1; + multiple times in parallel. The static buffer is safe. */ + PWCHAR ntname = nt_max_path_buf (); + GetModuleFileNameW (h, ntname, NT_MAX_PATH); + PWCHAR modname = form_ntname (ntname, NT_MAX_PATH, ntname); + while (modname > ntname && *(modname - 1) != L'\\') + --modname; guard (true); /* Already loaded? For linked DLLs, only compare the basenames. Linked DLLs are loaded using just the basename and the default DLL search path. The Windows loader picks up the first one it finds. */ - dll *d = (type == DLL_LINK) ? dlls.find_by_modname (modname) : dlls[name]; + dll *d = (type == DLL_LINK) ? dlls.find_by_modname (modname) : dlls[ntname]; if (d) { /* We only get here in the forkee. */ if (d->handle != h) fabort ("%W: Loaded to different address: parent(%p) != child(%p)", - name, d->handle, h); + ntname, d->handle, h); /* If this DLL has been linked against, and the full path differs, try to sanity check if this is the same DLL, just in another path. */ - else if (type == DLL_LINK && wcscasecmp (name, d->name) + else if (type == DLL_LINK && wcscasecmp (ntname, d->ntname) && (d->p.data_start != p->data_start || d->p.data_start != p->data_start || d->p.bss_start != p->bss_start || d->p.bss_end != p->bss_end || d->p.ctors != p->ctors || d->p.dtors != p->dtors)) - fabort ("\nLoaded different DLL with same basename in forked child,\n" + fabort ("\nLoaded different DLL with same basename in forked child,\n" "parent loaded: %W\n" " child loaded: %W\n" "The DLLs differ, so it's not safe to run the forked child.\n" "Make sure to remove the offending DLL before trying again.", - d->name, name); + d->ntname, ntname); d->p = p; } else { - d = (dll *) cmalloc (HEAP_2_DLL, - sizeof (*d) + (namelen * sizeof (*name))); /* Now we've allocated a block of information. Fill it in with the supplied info about this DLL. */ - wcscpy (d->name, name); - d->modname = d->name + (modname - name); + wcscpy (d->ntname, ntname); + d->modname = d->ntname + (modname - ntname); d->handle = h; d->count = 0; /* Reference counting performed in dlopen/dlclose. */ d->has_dtors = true; @@ -473,7 +581,7 @@ dll_list::init () to clobber the dll's target address range because it often overlaps. */ static PVOID -reserve_at (const PWCHAR name, PVOID here, PVOID dll_base, DWORD dll_size) +reserve_at (PCWCHAR name, PVOID here, PVOID dll_base, DWORD dll_size) { DWORD size; MEMORY_BASIC_INFORMATION mb; @@ -502,7 +610,7 @@ reserve_at (const PWCHAR name, PVOID here, PVOID dll_base, DWORD dll_size) /* Release the memory previously allocated by "reserve_at" above. */ static void -release_at (const PWCHAR name, PVOID here) +release_at (PCWCHAR name, PVOID here) { if (!VirtualFree (here, 0, MEM_RELEASE)) fabort ("couldn't release memory %p for '%W' alignment, %E\n", @@ -527,12 +635,6 @@ dll_list::reserve_space () d->modname, d->handle); } -/* We need the in_load_after_fork flag so dll_dllcrt0_1 can decide at fork - time if this is a linked DLL or a dynamically loaded DLL. In either case, - both, cygwin_finished_initializing and in_forkee are true, so they are not - sufficient to discern the situation. */ -static bool NO_COPY in_load_after_fork; - /* Reload DLLs after a fork. Iterates over the list of dynamically loaded DLLs and attempts to load them in the same place as they were loaded in the parent. */ @@ -543,7 +645,8 @@ dll_list::load_after_fork (HANDLE parent) // dll_list::reserve_space(); in_load_after_fork = true; - load_after_fork_impl (parent, dlls.istart (DLL_LOAD), 0); + if (reload_on_fork) + load_after_fork_impl (parent, dlls.istart (DLL_LOAD), 0); in_load_after_fork = false; } @@ -576,33 +679,34 @@ void dll_list::load_after_fork_impl (HANDLE parent, dll* d, int retries) dll's protective reservation from step 1 */ if (!retries && !VirtualFree (d->handle, 0, MEM_RELEASE)) - fabort ("unable to release protective reservation for %W (%p), %E", - d->modname, d->handle); + fabort ("unable to release protective reservation (%p) for %W, %E", + d->handle, d->ntname); - HMODULE h = LoadLibraryExW (d->name, NULL, DONT_RESOLVE_DLL_REFERENCES); + HMODULE h = LoadLibraryExW (buffered_shortname (d->ntname), + NULL, DONT_RESOLVE_DLL_REFERENCES); if (!h) - fabort ("unable to create interim mapping for %W, %E", d->name); + fabort ("unable to create interim mapping for %W, %E", d->ntname); if (h != d->handle) { sigproc_printf ("%W loaded in wrong place: %p != %p", - d->modname, h, d->handle); + d->ntname, h, d->handle); FreeLibrary (h); - PVOID reservation = reserve_at (d->modname, h, + PVOID reservation = reserve_at (d->ntname, h, d->handle, d->image_size); if (!reservation) fabort ("unable to block off %p to prevent %W from loading there", - h, d->modname); + h, d->ntname); if (retries < DLL_RETRY_MAX) load_after_fork_impl (parent, d, retries+1); else fabort ("unable to remap %W to same address as parent (%p) - try running rebaseall", - d->modname, d->handle); + d->ntname, d->handle); /* once the above returns all the dlls are mapped; release the reservation and continue unwinding */ sigproc_printf ("releasing blocked space at %p", reservation); - release_at (d->modname, reservation); + release_at (d->ntname, reservation); return; } } @@ -618,7 +722,7 @@ void dll_list::load_after_fork_impl (HANDLE parent, dll* d, int retries) { if (!VirtualFree (d->handle, 0, MEM_RELEASE)) fabort ("unable to release protective reservation for %W (%p), %E", - d->modname, d->handle); + d->ntname, d->handle); } else { @@ -626,17 +730,19 @@ void dll_list::load_after_fork_impl (HANDLE parent, dll* d, int retries) to ours or we wouldn't have gotten this far */ if (!FreeLibrary (d->handle)) fabort ("unable to unload interim mapping of %W, %E", - d->modname); + d->ntname); } - HMODULE h = LoadLibraryW (d->name); + /* cygwin1.dll - as linked dependency - may reuse the shortname + buffer, even in case of failure: don't reuse shortname later */ + HMODULE h = LoadLibraryW (buffered_shortname (d->ntname)); if (!h) - fabort ("unable to map %W, %E", d->name); + fabort ("unable to map %W, %E", d->ntname); if (h != d->handle) fabort ("unable to map %W to same address as parent: %p != %p", - d->modname, d->handle, h); + d->ntname, d->handle, h); /* Fix OS reference count. */ for (int cnt = 1; cnt < d->count; ++cnt) - LoadLibraryW (d->name); + LoadLibraryW (buffered_shortname (d->ntname)); } } diff --git a/winsup/cygwin/dll_init.h b/winsup/cygwin/dll_init.h index 862ad91b2..4897c420f 100644 --- a/winsup/cygwin/dll_init.h +++ b/winsup/cygwin/dll_init.h @@ -58,7 +58,7 @@ struct dll DWORD image_size; void* preferred_base; PWCHAR modname; - WCHAR name[1]; + WCHAR ntname[1]; /* must be the last data member */ void detach (); int init (); void run_dtors () @@ -79,11 +79,25 @@ class dll_list dll *hold; dll_type hold_type; static muto protect; + /* Use this buffer under loader lock conditions only. */ + static WCHAR NO_COPY nt_max_path_buffer[NT_MAX_PATH]; public: + static PWCHAR form_ntname (PWCHAR ntbuf, size_t bufsize, PCWCHAR name); + static PWCHAR form_shortname (PWCHAR shortbuf, size_t bufsize, PCWCHAR name); + static PWCHAR nt_max_path_buf () + { + return nt_max_path_buffer; + } + static PCWCHAR buffered_shortname (PCWCHAR name) + { + form_shortname (nt_max_path_buffer, NT_MAX_PATH, name); + return nt_max_path_buffer; + } + dll start; int loaded_dlls; int reload_on_fork; - dll *operator [] (const PWCHAR name); + dll *operator [] (PCWCHAR ntname); dll *alloc (HINSTANCE, per_process *, dll_type); dll *find (void *); void detach (void *); @@ -91,7 +105,7 @@ public: void load_after_fork (HANDLE); void reserve_space (); void load_after_fork_impl (HANDLE, dll* which, int retries); - dll *find_by_modname (const PWCHAR name); + dll *find_by_modname (PCWCHAR modname); void populate_deps (dll* d); void topsort (); void topsort_visit (dll* d, bool goto_tail); diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 5f775249a..c78e5cf3b 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -175,21 +175,18 @@ frok::child (volatile char * volatile here) if (fixup_shms_after_fork ()) api_fatal ("recreate_shm areas after fork failed"); - /* If we haven't dynamically loaded any dlls, just signal - the parent. Otherwise, load all the dlls, tell the parent - that we're done, and wait for the parent to fill in the. - loaded dlls' data/bss. */ + /* load dynamic dlls, if any */ + dlls.load_after_fork (hParent); + + cygheap->fdtab.fixup_after_fork (hParent); + + /* If we haven't dynamically loaded any dlls, just signal the parent. + Otherwise, tell the parent that we've loaded all the dlls + and wait for the parent to fill in the loaded dlls' data/bss. */ if (!load_dlls) - { - cygheap->fdtab.fixup_after_fork (hParent); - sync_with_parent ("performed fork fixup", false); - } + sync_with_parent ("performed fork fixup", false); else - { - dlls.load_after_fork (hParent); - cygheap->fdtab.fixup_after_fork (hParent); - sync_with_parent ("loaded dlls", true); - } + sync_with_parent ("loaded dlls", true); init_console_handler (myself->ctty > 0); ForceCloseHandle1 (fork_info->forker_finished, forker_finished); @@ -303,8 +300,6 @@ frok::parent (volatile char * volatile stack_here) si.lpReserved2 = (LPBYTE) &ch; si.cbReserved2 = sizeof (ch); - syscall_printf ("CreateProcessW (%W, %W, 0, 0, 1, %y, 0, 0, %p, %p)", - myself->progname, myself->progname, c_flags, &si, &pi); bool locked = __malloc_lock (); /* Remove impersonation */ @@ -315,6 +310,9 @@ frok::parent (volatile char * volatile stack_here) while (1) { + syscall_printf ("CreateProcessW (%W, %W, 0, 0, 1, %y, 0, 0, %p, %p)", + myself->progname, myself->progname, c_flags, &si, &pi); + hchild = NULL; rc = CreateProcessW (myself->progname, /* image to run */ GetCommandLineW (), /* Take same space for command |