From 4889f730c1fa5db8cea2780eaa1d2156c92b298a Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Sun, 19 Jul 2015 22:38:30 +0200 Subject: [PATCH] Reduce stack pressure throughout Cygwin * dcrt0.cc (initial_env): Reduce size of local path buffers to PATH_MAX. Allocate debugger_command from process heap. (init_windows_system_directory): Very early initialize new global variable global_progname. * dll_init.cc (dll_list::alloc): Make path buffer static. Explain why. (dll_list::populate_deps): Use tmp_pathbuf for local path buffer. * exceptions.cc (debugger_command): Convert to PWCHAR. (error_start_init): Allocate debugger_command and fill with wide char strings. Only allocate if NULL. (try_to_debug): Just check if debugger_command is a NULL pointer to return. Drop conversion from char to WCHAR and drop local variable dbg_cmd. * globals.cc (global_progname): New global variable to store Windows application path. * pinfo.cc (pinfo_basic::pinfo_basic): Just copy progname over from global_progname. (pinfo::status_exit): Let path_conv create the POSIX path to avoid local buffer. * pseudo_reloc.cc (__report_error): Utilize global_progname, drop local buffer. * smallprint.cc (__small_vsprintf): Just utilize global_progname for %P format specifier. (__small_vswprintf): Ditto. * strace.cc (PROTECT): Change to reflect x being a pointer. Reformat. (CHECK): Ditto. Reformat. (strace::activate): Utilize global_progname, drop local buffer. Fix formatting. (strace::vsprntf): Reduce size of local progname buffer to NAME_MAX. Copy and, if necessary, convert only the last path component to progname. (strace_buf_guard): New muto. (buf): New static pointer. (strace::vprntf): Use buf under strace_buf_guard lock only. Allocate buffer space for buf on Windows heap. * wow64.cc (wow64_respawn_process): Utilize global_progname, drop local path buffer. Signed-off-by: Corinna Vinschen --- winsup/cygwin/ChangeLog | 39 ++++++++++++++++ winsup/cygwin/dcrt0.cc | 14 ++++-- winsup/cygwin/dll_init.cc | 9 +++- winsup/cygwin/exceptions.cc | 38 ++++++++-------- winsup/cygwin/globals.cc | 1 + winsup/cygwin/pinfo.cc | 12 ++--- winsup/cygwin/pseudo-reloc.cc | 8 ++-- winsup/cygwin/smallprint.cc | 14 ++---- winsup/cygwin/strace.cc | 85 ++++++++++++++++++++--------------- winsup/cygwin/wow64.cc | 6 +-- 10 files changed, 141 insertions(+), 85 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 43429299c..a606cca74 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,42 @@ +2015-07-19 Corinna Vinschen + + * dcrt0.cc (initial_env): Reduce size of local path buffers to + PATH_MAX. Allocate debugger_command from process heap. + (init_windows_system_directory): Very early initialize new global + variable global_progname. + * dll_init.cc (dll_list::alloc): Make path buffer static. Explain why. + (dll_list::populate_deps): Use tmp_pathbuf for local path buffer. + * exceptions.cc (debugger_command): Convert to PWCHAR. + (error_start_init): Allocate debugger_command and fill with wide char + strings. Only allocate if NULL. + (try_to_debug): Just check if debugger_command is a NULL pointer to + return. Drop conversion from char to WCHAR and drop local variable + dbg_cmd. + * globals.cc (global_progname): New global variable to store Windows + application path. + * pinfo.cc (pinfo_basic::pinfo_basic): Just copy progname over from + global_progname. + (pinfo::status_exit): Let path_conv create the POSIX path to + avoid local buffer. + * pseudo_reloc.cc (__report_error): Utilize global_progname, drop local + buffer. + * smallprint.cc (__small_vsprintf): Just utilize global_progname for + %P format specifier. + (__small_vswprintf): Ditto. + * strace.cc (PROTECT): Change to reflect x being a pointer. Reformat. + (CHECK): Ditto. Reformat. + (strace::activate): Utilize global_progname, drop local buffer. + Fix formatting. + (strace::vsprntf): Reduce size of local progname buffer to NAME_MAX. + Copy and, if necessary, convert only the last path component to + progname. + (strace_buf_guard): New muto. + (buf): New static pointer. + (strace::vprntf): Use buf under strace_buf_guard lock only. Allocate + buffer space for buf on Windows heap. + * wow64.cc (wow64_respawn_process): Utilize global_progname, drop + local path buffer. + 2015-07-18 Corinna Vinschen * gendef: Remove unused 64 bit versions of __sjfault and __ljfault. diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 1874fe527..d7f549441 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -509,11 +509,11 @@ initial_env () _cygwin_testing = 1; #ifdef DEBUGGING - char buf[NT_MAX_PATH]; + char buf[PATH_MAX]; if (GetEnvironmentVariableA ("CYGWIN_DEBUG", buf, sizeof (buf) - 1)) { - char buf1[NT_MAX_PATH]; - GetModuleFileName (NULL, buf1, NT_MAX_PATH); + char buf1[PATH_MAX]; + GetModuleFileName (NULL, buf1, PATH_MAX); char *p = strpbrk (buf, ":="); if (!p) p = (char *) "gdb.exe -nw"; @@ -521,6 +521,13 @@ initial_env () *p++ = '\0'; if (strcasestr (buf1, buf)) { + extern PWCHAR debugger_command; + + debugger_command = (PWCHAR) HeapAlloc (GetProcessHeap (), 0, + (2 * NT_MAX_PATH + 20) + * sizeof (WCHAR)); + if (!debugger_command) + return; error_start_init (p); jit_debug = true; try_to_debug (); @@ -736,6 +743,7 @@ void dll_crt0_0 () { wincap.init (); + GetModuleFileNameW (NULL, global_progname, NT_MAX_PATH); child_proc_info = get_cygwin_startup_info (); init_windows_system_directory (); initial_env (); diff --git a/winsup/cygwin/dll_init.cc b/winsup/cygwin/dll_init.cc index 51ef186a3..313b0ff93 100644 --- a/winsup/cygwin/dll_init.cc +++ b/winsup/cygwin/dll_init.cc @@ -25,6 +25,7 @@ details. */ #include #include #include +#include extern void __stdcall check_sanity_and_sync (per_process *); @@ -178,7 +179,9 @@ dll_list::find_by_modname (const PWCHAR modname) dll * dll_list::alloc (HINSTANCE h, per_process *p, dll_type type) { - WCHAR buf[NT_MAX_PATH]; + /* 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)) @@ -264,7 +267,9 @@ dll_list::append (dll* d) void dll_list::populate_deps (dll* d) { - WCHAR wmodname[NT_MAX_PATH]; + tmp_pathbuf tp; + + PWCHAR wmodname = tp.w_get (); pefile* pef = (pefile*) d->handle; PIMAGE_DATA_DIRECTORY dd = pef->idata_dir (IMAGE_DIRECTORY_ENTRY_IMPORT); /* Annoyance: calling crealloc with a NULL pointer will use the diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 0025be6b1..c4b076169 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -46,7 +46,7 @@ details. */ #define CALL_HANDLER_RETRY_OUTER 10 #define CALL_HANDLER_RETRY_INNER 10 -char debugger_command[2 * NT_MAX_PATH + 20]; +PWCHAR debugger_command; extern u_char _sigbe; extern u_char _sigdelayed_end; @@ -112,18 +112,19 @@ extern "C" void error_start_init (const char *buf) { if (!buf || !*buf) - { - debugger_command[0] = '\0'; - return; - } + return; + if (!debugger_command && + !(debugger_command = (PWCHAR) malloc ((2 * NT_MAX_PATH + 20) + * sizeof (WCHAR)))) + return; - char pgm[NT_MAX_PATH]; - if (!GetModuleFileName (NULL, pgm, NT_MAX_PATH)) - strcpy (pgm, "cygwin1.dll"); - for (char *p = strchr (pgm, '\\'); p; p = strchr (p, '\\')) - *p = '/'; - - __small_sprintf (debugger_command, "%s \"%s\"", buf, pgm); + PWCHAR cp = debugger_command + + sys_mbstowcs (debugger_command, NT_MAX_PATH, buf) - 1; + cp = wcpcpy (cp, L" \""); + wcpcpy (cp, global_progname); + for (PWCHAR p = wcschr (cp, L'\\'); p; p = wcschr (p, L'\\')) + *p = L'/'; + wcscat (cp, L"\""); } void @@ -474,9 +475,9 @@ cygwin_stackdump () extern "C" int try_to_debug (bool waitloop) { - debug_printf ("debugger_command '%s'", debugger_command); - if (*debugger_command == '\0') + if (!debugger_command) return 0; + debug_printf ("debugger_command '%W'", debugger_command); if (being_debugged ()) { extern void break_here (); @@ -484,8 +485,8 @@ try_to_debug (bool waitloop) return 0; } - __small_sprintf (strchr (debugger_command, '\0'), " %u", - GetCurrentProcessId ()); + PWCHAR dbg_end = wcschr (debugger_command, L'\0'); + __small_swprintf (dbg_end, L" %u", GetCurrentProcessId ()); LONG prio = GetThreadPriority (GetCurrentThread ()); SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST); @@ -529,10 +530,8 @@ try_to_debug (bool waitloop) console_printf ("*** starting debugger for pid %u, tid %u\n", cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ()); BOOL dbg; - WCHAR dbg_cmd[strlen(debugger_command) + 1]; - sys_mbstowcs (dbg_cmd, strlen(debugger_command) + 1, debugger_command); dbg = CreateProcessW (NULL, - dbg_cmd, + debugger_command, NULL, NULL, FALSE, @@ -542,6 +541,7 @@ try_to_debug (bool waitloop) &si, &pi); + *dbg_end = L'\0'; if (!dbg) system_printf ("Failed to start debugger, %E"); else diff --git a/winsup/cygwin/globals.cc b/winsup/cygwin/globals.cc index 92f9acc8a..389741900 100644 --- a/winsup/cygwin/globals.cc +++ b/winsup/cygwin/globals.cc @@ -31,6 +31,7 @@ UINT windows_system_directory_length; WCHAR system_wow64_directory[MAX_PATH]; UINT system_wow64_directory_length; #endif /* !__x86_64__ */ +WCHAR global_progname[NT_MAX_PATH]; /* program exit the program */ diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc index 038937e15..d0b4cd9a4 100644 --- a/winsup/cygwin/pinfo.cc +++ b/winsup/cygwin/pinfo.cc @@ -38,7 +38,9 @@ public: pinfo_basic::pinfo_basic () { pid = dwProcessId = GetCurrentProcessId (); - GetModuleFileNameW (NULL, progname, sizeof (progname) / sizeof (WCHAR)); + PWCHAR pend = wcpncpy (progname, global_progname, + sizeof (progname) / sizeof (WCHAR) - 1); + *pend = L'\0'; /* Default uid/gid are needed very early to initialize shared user info. */ uid = ILLEGAL_UID; gid = ILLEGAL_GID; @@ -120,20 +122,18 @@ pinfo::status_exit (DWORD x) { case STATUS_DLL_NOT_FOUND: { - char posix_prog[NT_MAX_PATH]; path_conv pc; if (!procinfo) - pc.check ("/dev/null"); + pc.check ("/dev/null", PC_NOWARN | PC_POSIX); else { UNICODE_STRING uc; RtlInitUnicodeString(&uc, procinfo->progname); - pc.check (&uc, PC_NOWARN); + pc.check (&uc, PC_NOWARN | PC_POSIX); } - mount_table->conv_to_posix_path (pc.get_win32 (), posix_prog, 1); small_printf ("%s: error while loading shared libraries: %s: cannot " "open shared object file: No such file or directory\n", - posix_prog, find_first_notloaded_dll (pc)); + pc.get_posix (), find_first_notloaded_dll (pc)); x = 127 << 8; } break; diff --git a/winsup/cygwin/pseudo-reloc.cc b/winsup/cygwin/pseudo-reloc.cc index bdcde2a4b..f374d2260 100644 --- a/winsup/cygwin/pseudo-reloc.cc +++ b/winsup/cygwin/pseudo-reloc.cc @@ -85,20 +85,18 @@ __report_error (const char *msg, ...) * cygwin ptys. */ char buf[128]; - WCHAR module[PATH_MAX]; - char * posix_module = NULL; + char *posix_module = NULL; static const char UNKNOWN_MODULE[] = ": "; static const char CYGWIN_FAILURE_MSG[] = "Cygwin runtime failure: "; HANDLE errh = GetStdHandle (STD_ERROR_HANDLE); - ssize_t modulelen = GetModuleFileNameW (NULL, module, PATH_MAX); va_list args; /* FIXME: cleanup further to avoid old use of cygwin_internal */ if (errh == INVALID_HANDLE_VALUE) cygwin_internal (CW_EXIT_PROCESS, STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION, 1); - if (modulelen > 0) - posix_module = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX, module); + posix_module = (char *) cygwin_create_path (CCP_WIN_W_TO_POSIX, + global_progname); va_start (args, msg); vsnprintf (buf, sizeof (buf), msg, args); diff --git a/winsup/cygwin/smallprint.cc b/winsup/cygwin/smallprint.cc index a1e0f70f1..2a8b1cef2 100644 --- a/winsup/cygwin/smallprint.cc +++ b/winsup/cygwin/smallprint.cc @@ -284,12 +284,6 @@ gen_decimalLL: dst = rnarg (dst, 16, 0, len, pad); #endif break; - case 'P': - if (!GetModuleFileName (NULL, tmp, NT_MAX_PATH)) - s = "cygwin program"; - else - s = tmp; - goto fillin; case '.': n = strtol (fmt, (char **) &fmt, 10); if (*fmt++ != 's') @@ -311,6 +305,9 @@ gen_decimalLL: else *dst++ = *s++; break; + case 'P': + RtlInitUnicodeString (us = &uw, global_progname); + goto wfillin; case 'W': w = va_arg (ap, PWCHAR); RtlInitUnicodeString (us = &uw, w ?: L"(null)"); @@ -613,10 +610,7 @@ gen_decimalLL: #endif break; case L'P': - if (!GetModuleFileNameW (NULL, tmp, NT_MAX_PATH)) - RtlInitUnicodeString (us = &uw, L"cygwin program"); - else - RtlInitUnicodeString (us = &uw, tmp); + RtlInitUnicodeString (us = &uw, global_progname); goto fillin; case L'.': n = wcstoul (fmt, (wchar_t **) &fmt, 10); diff --git a/winsup/cygwin/strace.cc b/winsup/cygwin/strace.cc index 9d1c3c27f..434f92dec 100644 --- a/winsup/cygwin/strace.cc +++ b/winsup/cygwin/strace.cc @@ -21,9 +21,13 @@ details. */ #include "dtable.h" #include "cygheap.h" #include "child_info.h" +#include "sync.h" -#define PROTECT(x) x[sizeof (x)-1] = 0 -#define CHECK(x) if (x[sizeof (x)-1] != 0) { small_printf ("array bound exceeded %d\n", __LINE__); ExitProcess (1); } +#define PROTECT(x) {x[NT_MAX_PATH - 1] = '\0';} +#define CHECK(x) if (x[NT_MAX_PATH - 1] != '\0') \ + { small_printf ("array bound exceeded %d\n", __LINE__); \ + ExitProcess (1); \ + } class strace NO_COPY strace; @@ -35,25 +39,25 @@ strace::activate (bool isfork) if (!_active && being_debugged ()) { char buf[30]; - __small_sprintf (buf, "cYg%8x %lx %d", _STRACE_INTERFACE_ACTIVATE_ADDR, &_active, isfork); + __small_sprintf (buf, "cYg%8x %lx %d", + _STRACE_INTERFACE_ACTIVATE_ADDR, &_active, isfork); OutputDebugString (buf); if (_active) { char pidbuf[80]; - WCHAR progname_buf[NT_MAX_PATH - 512]; - WCHAR *progname; + PWCHAR progname; if (myself) { - __small_sprintf (pidbuf, "(pid %d, ppid %d, windows pid %u)", myself->pid, - myself->ppid ?: 1, GetCurrentProcessId ()); + __small_sprintf (pidbuf, "(pid %d, ppid %d, windows pid %u)", + myself->pid, myself->ppid ?: 1, + GetCurrentProcessId ()); progname = myself->progname; } else { - GetModuleFileNameW (NULL, progname_buf, - sizeof progname_buf / sizeof (WCHAR)); - __small_sprintf (pidbuf, "(windows pid %u)", GetCurrentProcessId ()); - progname = progname_buf; + __small_sprintf (pidbuf, "(windows pid %u)", + GetCurrentProcessId ()); + progname = global_progname; } prntf (1, NULL, "**********************************************"); prntf (1, NULL, "Program name: %W %s", progname, pidbuf); @@ -151,32 +155,32 @@ strace::vsprntf (char *buf, const char *func, const char *infmt, va_list ap) else { PWCHAR pn = NULL; - WCHAR progname[NT_MAX_PATH]; - if (!cygwin_finished_initializing) - pn = (myself) ? myself->progname : NULL; - else if (__progname) - sys_mbstowcs(pn = progname, NT_MAX_PATH, __progname); - - WCHAR empty[1] = {}; - PWCHAR p; - if (!pn) - GetModuleFileNameW (NULL, pn = progname, sizeof (progname)); - if (!pn) - p = empty; - else if ((p = wcsrchr (pn, L'\\')) != NULL) - p++; - else if ((p = wcsrchr (pn, L'/')) != NULL) - p++; + WCHAR progname[NAME_MAX]; + if (cygwin_finished_initializing && __progname) + { + char *p = strrchr (__progname, '/'); + if (p) + ++p; + else + p = __progname; + char *pe = strrchr (p, '.'); + if (!pe || !ascii_strcasematch (pe, ".exe")) + pe = strrchr (p, '\0'); + sys_mbstowcs (pn = progname, NAME_MAX, p, pe - p); + } else - p = pn; - if (p != progname) - wcscpy (progname, p); - if ((p = wcsrchr (progname, '.')) != NULL - && !wcscasecmp (p, L".exe")) - *p = '\000'; - p = progname; + { + PWCHAR p = wcsrchr (global_progname, L'\\'); + ++p; + PWCHAR pe = wcsrchr (p, '.'); + if (!pe || wcscasecmp (pe, L".exe")) + pe = wcsrchr (p, L'\0'); + pe = wcpncpy (progname, p, pe - p); + *pe = L'\0'; + pn = progname; + } char tmpbuf[20]; - count = __small_sprintf (buf, fmt, *p ? p : L"?", mypid (tmpbuf), + count = __small_sprintf (buf, fmt, pn, mypid (tmpbuf), execing ? "!" : ""); if (func) count += getfunc (buf + count, func); @@ -235,14 +239,22 @@ strace::write_childpid (pid_t pid) /* Printf function used when tracing system calls. Warning: DO NOT SET ERRNO HERE! */ +static NO_COPY muto strace_buf_guard; +static NO_COPY char *buf; void strace::vprntf (unsigned category, const char *func, const char *fmt, va_list ap) { DWORD err = GetLastError (); int len; - char buf[NT_MAX_PATH]; + strace_buf_guard.init ("smallprint_buf")->acquire (); + /* Creating buffer on Windows process heap to drop stack pressure and + keeping our .bss small. */ + if (!buf) + buf = (char *) HeapAlloc (GetProcessHeap (), 0, NT_MAX_PATH); + if (!buf) + return; PROTECT (buf); SetLastError (err); @@ -272,6 +284,7 @@ strace::vprntf (unsigned category, const char *func, const char *fmt, va_list ap if (active ()) write (category, buf, len); #endif + strace_buf_guard.release (); SetLastError (err); } diff --git a/winsup/cygwin/wow64.cc b/winsup/cygwin/wow64.cc index 070b9c2c4..098d6c2c2 100644 --- a/winsup/cygwin/wow64.cc +++ b/winsup/cygwin/wow64.cc @@ -192,19 +192,17 @@ wow64_revert_to_original_stack (PVOID &allocationbase) void wow64_respawn_process () { - WCHAR path[PATH_MAX]; PROCESS_INFORMATION pi; STARTUPINFOW si; DWORD ret = 0; - GetModuleFileNameW (NULL, path, PATH_MAX); GetStartupInfoW (&si); - if (!CreateProcessW (path, GetCommandLineW (), NULL, NULL, TRUE, + if (!CreateProcessW (global_progname, GetCommandLineW (), NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (GetCurrentProcess ()), NULL, NULL, &si, &pi)) api_fatal ("Failed to create process <%W> <%W>, %E", - path, GetCommandLineW ()); + global_progname, GetCommandLineW ()); CloseHandle (pi.hThread); if (WaitForSingleObject (pi.hProcess, INFINITE) == WAIT_FAILED) api_fatal ("Waiting for process %u failed, %E", pi.dwProcessId);