| /* dcrt0.cc -- essentially the main() for the Cygwin dll |
| |
| This file is part of Cygwin. |
| |
| This software is a copyrighted work licensed under the terms of the |
| Cygwin license. Please consult the file "CYGWIN_LICENSE" for |
| details. */ |
| |
| #include "winsup.h" |
| #include "miscfuncs.h" |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include "glob.h" |
| #include <ctype.h> |
| #include <locale.h> |
| #include <sys/param.h> |
| #include "environ.h" |
| #include "sigproc.h" |
| #include "pinfo.h" |
| #include "cygerrno.h" |
| #define NEED_VFORK |
| #include "perprocess.h" |
| #include "path.h" |
| #include "fhandler.h" |
| #include "dtable.h" |
| #include "cygheap.h" |
| #include "child_info_magic.h" |
| #include "cygtls.h" |
| #include "shared_info.h" |
| #include "cygwin_version.h" |
| #include "dll_init.h" |
| #include "heap.h" |
| #include "tls_pbuf.h" |
| #include "exception.h" |
| #include "cygxdr.h" |
| #include "fenv.h" |
| #include "ntdll.h" |
| |
| #define MAX_AT_FILE_LEVEL 10 |
| |
| #define PREMAIN_LEN (sizeof (user_data->premain) / sizeof (user_data->premain[0])) |
| |
| extern "C" void cygwin_exit (int) __attribute__ ((noreturn)); |
| extern "C" void __sinit (_reent *); |
| |
| static int NO_COPY envc; |
| static char NO_COPY **envp; |
| |
| bool NO_COPY jit_debug; |
| |
| static void |
| do_global_dtors () |
| { |
| void (**pfunc) () = user_data->dtors; |
| if (pfunc) |
| { |
| user_data->dtors = NULL; |
| while (*++pfunc) |
| (*pfunc) (); |
| } |
| } |
| |
| static void __stdcall |
| do_global_ctors (void (**in_pfunc)(), int force) |
| { |
| if (!force && in_forkee) |
| return; // inherit constructed stuff from parent pid |
| |
| /* Run ctors backwards, so skip the first entry and find how many |
| there are, then run them. */ |
| |
| void (**pfunc) () = in_pfunc; |
| |
| while (*++pfunc) |
| ; |
| while (--pfunc > in_pfunc) |
| (*pfunc) (); |
| } |
| |
| /* |
| * Replaces @file in the command line with the contents of the file. |
| * There may be multiple @file's in a single command line |
| * A \@file is replaced with @file so that echo \@foo would print |
| * @foo and not the contents of foo. |
| */ |
| static bool __stdcall |
| insert_file (char *name, char *&cmd) |
| { |
| HANDLE f; |
| DWORD size; |
| tmp_pathbuf tp; |
| |
| PWCHAR wname = tp.w_get (); |
| sys_mbstowcs (wname, NT_MAX_PATH, name + 1); |
| f = CreateFileW (wname, |
| GENERIC_READ, /* open for reading */ |
| FILE_SHARE_VALID_FLAGS, /* share for reading */ |
| &sec_none_nih, /* default security */ |
| OPEN_EXISTING, /* existing file only */ |
| FILE_ATTRIBUTE_NORMAL, /* normal file */ |
| NULL); /* no attr. template */ |
| |
| if (f == INVALID_HANDLE_VALUE) |
| { |
| debug_printf ("couldn't open file '%s', %E", name); |
| return false; |
| } |
| |
| /* This only supports files up to about 4 billion bytes in |
| size. I am making the bold assumption that this is big |
| enough for this feature */ |
| size = GetFileSize (f, NULL); |
| if (size == 0xFFFFFFFF) |
| { |
| CloseHandle (f); |
| debug_printf ("couldn't get file size for '%s', %E", name); |
| return false; |
| } |
| |
| int new_size = strlen (cmd) + size + 2; |
| char *tmp = (char *) malloc (new_size); |
| if (!tmp) |
| { |
| CloseHandle (f); |
| debug_printf ("malloc failed, %E"); |
| return false; |
| } |
| |
| /* realloc passed as it should */ |
| DWORD rf_read; |
| BOOL rf_result; |
| rf_result = ReadFile (f, tmp, size, &rf_read, NULL); |
| CloseHandle (f); |
| if (!rf_result || (rf_read != size)) |
| { |
| free (tmp); |
| debug_printf ("ReadFile failed, %E"); |
| return false; |
| } |
| |
| tmp[size++] = ' '; |
| strcpy (tmp + size, cmd); |
| cmd = tmp; |
| return true; |
| } |
| |
| static inline int |
| isquote (char c) |
| { |
| char ch = c; |
| return ch == '"' || ch == '\''; |
| } |
| |
| /* Step over a run of characters delimited by quotes */ |
| static /*__inline*/ char * |
| quoted (char *cmd, int winshell) |
| { |
| char *p; |
| char quote = *cmd; |
| |
| if (!winshell) |
| { |
| char *p; |
| strcpy (cmd, cmd + 1); |
| if (*(p = strchrnul (cmd, quote))) |
| strcpy (p, p + 1); |
| return p; |
| } |
| |
| const char *s = quote == '\'' ? "'" : "\\\""; |
| /* This must have been run from a Windows shell, so preserve |
| quotes for globify to play with later. */ |
| while (*cmd && *++cmd) |
| if ((p = strpbrk (cmd, s)) == NULL) |
| { |
| cmd = strchr (cmd, '\0'); // no closing quote |
| break; |
| } |
| else if (*p == '\\') |
| cmd = ++p; |
| else if (quote == '"' && p[1] == '"') |
| { |
| *p = '\\'; |
| cmd = ++p; // a quoted quote |
| } |
| else |
| { |
| cmd = p + 1; // point to after end |
| break; |
| } |
| return cmd; |
| } |
| |
| /* Perform a glob on word if it contains wildcard characters. |
| Also quote every character between quotes to force glob to |
| treat the characters literally. */ |
| |
| /* Either X:[...] or \\server\[...] */ |
| #define is_dos_path(s) (isdrive(s) \ |
| || ((s)[0] == '\\' \ |
| && (s)[1] == '\\' \ |
| && isalpha ((s)[2]) \ |
| && strchr ((s) + 3, '\\'))) |
| |
| static int __stdcall |
| globify (char *word, char **&argv, int &argc, int &argvlen) |
| { |
| if (*word != '~' && strpbrk (word, "?*[\"\'(){}") == NULL) |
| return 0; |
| |
| int n = 0; |
| char *p, *s; |
| int dos_spec = is_dos_path (word); |
| if (!dos_spec && isquote (*word) && word[1] && word[2]) |
| dos_spec = is_dos_path (word + 1); |
| |
| /* We'll need more space if there are quoting characters in |
| word. If that is the case, doubling the size of the |
| string should provide more than enough space. */ |
| if (strpbrk (word, "'\"")) |
| n = strlen (word); |
| char pattern[strlen (word) + ((dos_spec + 1) * n) + 1]; |
| |
| /* Fill pattern with characters from word, quoting any |
| characters found within quotes. */ |
| for (p = pattern, s = word; *s != '\000'; s++, p++) |
| if (!isquote (*s)) |
| { |
| if (dos_spec && *s == '\\') |
| *p++ = '\\'; |
| *p = *s; |
| } |
| else |
| { |
| char quote = *s; |
| while (*++s && *s != quote) |
| { |
| if (dos_spec || *s != '\\') |
| /* nothing */; |
| else if (s[1] == quote || s[1] == '\\') |
| s++; |
| *p++ = '\\'; |
| size_t cnt = isascii (*s) ? 1 : mbtowc (NULL, s, MB_CUR_MAX); |
| if (cnt <= 1 || cnt == (size_t)-1) |
| *p++ = *s; |
| else |
| { |
| --s; |
| while (cnt-- > 0) |
| *p++ = *++s; |
| } |
| } |
| if (*s == quote) |
| p--; |
| if (*s == '\0') |
| break; |
| } |
| |
| *p = '\0'; |
| |
| glob_t gl; |
| gl.gl_offs = 0; |
| |
| /* Attempt to match the argument. Return just word (minus quoting) if no match. */ |
| if (glob (pattern, GLOB_TILDE | GLOB_NOCHECK | GLOB_BRACE | GLOB_QUOTE, NULL, &gl) || !gl.gl_pathc) |
| return 0; |
| |
| /* Allocate enough space in argv for the matched filenames. */ |
| n = argc; |
| if ((argc += gl.gl_pathc) > argvlen) |
| { |
| argvlen = argc + 10; |
| argv = (char **) realloc (argv, (1 + argvlen) * sizeof (argv[0])); |
| } |
| |
| /* Copy the matched filenames to argv. */ |
| char **gv = gl.gl_pathv; |
| char **av = argv + n; |
| while (*gv) |
| { |
| debug_printf ("argv[%d] = '%s'", n++, *gv); |
| *av++ = *gv++; |
| } |
| |
| /* Clean up after glob. */ |
| free (gl.gl_pathv); |
| return 1; |
| } |
| |
| /* Build argv, argc from string passed from Windows. */ |
| |
| static void __stdcall |
| build_argv (char *cmd, char **&argv, int &argc, int winshell) |
| { |
| int argvlen = 0; |
| int nesting = 0; // monitor "nesting" from insert_file |
| |
| argc = 0; |
| argvlen = 0; |
| argv = NULL; |
| |
| /* Scan command line until there is nothing left. */ |
| while (*cmd) |
| { |
| /* Ignore spaces */ |
| if (issep (*cmd)) |
| { |
| cmd++; |
| continue; |
| } |
| |
| /* Found the beginning of an argument. */ |
| char *word = cmd; |
| char *sawquote = NULL; |
| while (*cmd) |
| { |
| if (*cmd != '"' && (!winshell || *cmd != '\'')) |
| cmd++; // Skip over this character |
| else |
| /* Skip over characters until the closing quote */ |
| { |
| sawquote = cmd; |
| /* Handle quoting. Only strip off quotes if the parent is |
| a Cygwin process, or if the word starts with a '@'. |
| In this case, the insert_file function needs an unquoted |
| DOS filename and globbing isn't performed anyway. */ |
| cmd = quoted (cmd, winshell && argc > 0 && *word != '@'); |
| } |
| if (issep (*cmd)) // End of argument if space |
| break; |
| } |
| if (*cmd) |
| *cmd++ = '\0'; // Terminate `word' |
| |
| /* Possibly look for @file construction assuming that this isn't |
| the very first argument and the @ wasn't quoted */ |
| if (argc && sawquote != word && *word == '@') |
| { |
| if (++nesting > MAX_AT_FILE_LEVEL) |
| api_fatal ("Too many levels of nesting for %s", word); |
| if (insert_file (word, cmd)) |
| continue; // There's new stuff in cmd now |
| } |
| |
| /* See if we need to allocate more space for argv */ |
| if (argc >= argvlen) |
| { |
| argvlen = argc + 10; |
| argv = (char **) realloc (argv, (1 + argvlen) * sizeof (argv[0])); |
| } |
| |
| /* Add word to argv file after (optional) wildcard expansion. */ |
| if (!winshell || !argc || !globify (word, argv, argc, argvlen)) |
| { |
| debug_printf ("argv[%d] = '%s'", argc, word); |
| argv[argc++] = word; |
| } |
| } |
| |
| if (argv) |
| argv[argc] = NULL; |
| |
| debug_printf ("argc %d", argc); |
| } |
| |
| /* sanity and sync check */ |
| void __stdcall |
| check_sanity_and_sync (per_process *p) |
| { |
| /* Sanity check to make sure developers didn't change the per_process */ |
| /* struct without updating SIZEOF_PER_PROCESS [it makes them think twice */ |
| /* about changing it]. */ |
| if (sizeof (per_process) != SIZEOF_PER_PROCESS) |
| api_fatal ("per_process sanity check failed"); |
| |
| /* Make sure that the app and the dll are in sync. */ |
| |
| /* Complain if older than last incompatible change */ |
| if (p->dll_major < CYGWIN_VERSION_DLL_EPOCH) |
| api_fatal ("cygwin DLL and APP are out of sync -- DLL version mismatch %u < %u", |
| p->dll_major, CYGWIN_VERSION_DLL_EPOCH); |
| |
| /* magic_biscuit != 0 if using the old style version numbering scheme. */ |
| if (p->magic_biscuit != SIZEOF_PER_PROCESS) |
| api_fatal ("Incompatible cygwin .dll -- incompatible per_process info %u != %u", |
| p->magic_biscuit, SIZEOF_PER_PROCESS); |
| |
| /* Complain if incompatible API changes made */ |
| if (p->api_major > cygwin_version.api_major) |
| api_fatal ("cygwin DLL and APP are out of sync -- API version mismatch %u > %u", |
| p->api_major, cygwin_version.api_major); |
| |
| #ifdef __i386__ |
| /* This is a kludge to work around a version of _cygwin_common_crt0 |
| which overwrote the cxx_malloc field with the local DLL copy. |
| Hilarity ensues if the DLL is not loaded while the process |
| is forking. */ |
| __cygwin_user_data.cxx_malloc = &default_cygwin_cxx_malloc; |
| #endif |
| } |
| |
| child_info NO_COPY *child_proc_info; |
| |
| /* Extend the stack prior to fork longjmp. */ |
| void |
| child_info_fork::alloc_stack () |
| { |
| PTEB teb = NtCurrentTeb (); |
| if (teb->Tib.StackBase != stackbase) |
| { |
| void *stack_ptr; |
| size_t stacksize; |
| |
| /* If guardsize is -1, we have been started from a pthread with an |
| application-provided stack, and the stack has just to be used as is. */ |
| if (guardsize == (size_t) -1) |
| return; |
| /* Reserve entire stack. */ |
| stacksize = (PBYTE) stackbase - (PBYTE) stackaddr; |
| if (!VirtualAlloc (stackaddr, stacksize, MEM_RESERVE, PAGE_NOACCESS)) |
| { |
| api_fatal ("fork: can't reserve memory for parent stack " |
| "%p - %p, (child has %p - %p), %E", |
| stackaddr, stackbase, teb->DeallocationStack, |
| teb->Tib.StackBase); |
| } |
| /* Commit the area commited in parent. */ |
| stacksize = (PBYTE) stackbase - (PBYTE) stacklimit; |
| stack_ptr = VirtualAlloc (stacklimit, stacksize, MEM_COMMIT, |
| PAGE_READWRITE); |
| if (!stack_ptr) |
| api_fatal ("can't commit memory for stack %p(%ly), %E", |
| stacklimit, stacksize); |
| /* Set up guardpages. */ |
| ULONG real_guardsize = guardsize |
| ? roundup2 (guardsize, wincap.page_size ()) |
| : wincap.def_guard_page_size (); |
| if (stack_ptr > stackaddr) |
| { |
| stack_ptr = (void *) ((PBYTE) stack_ptr - real_guardsize); |
| if (!VirtualAlloc (stack_ptr, real_guardsize, MEM_COMMIT, |
| PAGE_READWRITE | PAGE_GUARD)) |
| api_fatal ("fork: couldn't allocate new stack guard page %p, %E", |
| stack_ptr); |
| } |
| /* Set thread stack guarantee matching the guardsize. |
| Note that the guardsize is one page bigger than the guarantee. */ |
| if (real_guardsize > wincap.def_guard_page_size ()) |
| { |
| real_guardsize -= wincap.page_size (); |
| SetThreadStackGuarantee (&real_guardsize); |
| } |
| } |
| else |
| { |
| /* Fork has been called from main thread. Simply commit the region |
| of the stack commited in the parent but not yet commited in the |
| child and create new guardpages. */ |
| if (NtCurrentTeb ()->Tib.StackLimit > stacklimit) |
| { |
| SIZE_T commitsize = (PBYTE) NtCurrentTeb ()->Tib.StackLimit |
| - (PBYTE) stacklimit; |
| if (!VirtualAlloc (stacklimit, commitsize, MEM_COMMIT, PAGE_READWRITE)) |
| api_fatal ("can't commit child memory for stack %p(%ly), %E", |
| stacklimit, commitsize); |
| PVOID guardpage = (PBYTE) stacklimit - wincap.def_guard_page_size (); |
| if (!VirtualAlloc (guardpage, wincap.def_guard_page_size (), |
| MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD)) |
| api_fatal ("fork: couldn't allocate new stack guard page %p, %E", |
| guardpage); |
| NtCurrentTeb ()->Tib.StackLimit = stacklimit; |
| } |
| /* This only affects forked children of a process started from a native |
| 64 bit process, but it doesn't hurt to do it unconditionally. Fix |
| StackBase in the child to be the same as in the parent, so that the |
| computation of _my_tls is correct. */ |
| teb->Tib.StackBase = (PVOID) stackbase; |
| } |
| } |
| |
| extern "C" void |
| break_here () |
| { |
| static int NO_COPY sent_break; |
| if (!sent_break++) |
| DebugBreak (); |
| debug_printf ("break here"); |
| } |
| |
| static void |
| initial_env () |
| { |
| if (GetEnvironmentVariableA ("CYGWIN_TESTING", NULL, 0)) |
| _cygwin_testing = 1; |
| |
| #ifdef DEBUGGING |
| char buf[PATH_MAX]; |
| if (GetEnvironmentVariableA ("CYGWIN_DEBUG", buf, sizeof (buf) - 1)) |
| { |
| char buf1[PATH_MAX]; |
| GetModuleFileName (NULL, buf1, PATH_MAX); |
| char *p = strpbrk (buf, ":="); |
| if (!p) |
| p = (char *) "gdb.exe -nw"; |
| else |
| *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 (); |
| console_printf ("*** Sending Break. gdb may issue spurious SIGTRAP message.\n"); |
| break_here (); |
| } |
| } |
| #endif |
| } |
| |
| child_info * |
| get_cygwin_startup_info () |
| { |
| STARTUPINFO si; |
| |
| GetStartupInfo (&si); |
| child_info *res = (child_info *) si.lpReserved2; |
| |
| if (si.cbReserved2 < EXEC_MAGIC_SIZE || !res |
| || res->intro != PROC_MAGIC_GENERIC || res->magic != CHILD_INFO_MAGIC) |
| { |
| strace.activate (false); |
| res = NULL; |
| } |
| else |
| { |
| if ((res->intro & OPROC_MAGIC_MASK) == OPROC_MAGIC_GENERIC) |
| multiple_cygwin_problem ("proc intro", res->intro, 0); |
| else if (res->cygheap != (void *) &_cygheap_start) |
| multiple_cygwin_problem ("cygheap base", (uintptr_t) res->cygheap, |
| (uintptr_t) &_cygheap_start); |
| |
| unsigned should_be_cb = 0; |
| switch (res->type) |
| { |
| case _CH_FORK: |
| in_forkee = true; |
| should_be_cb = sizeof (child_info_fork); |
| /* fall through */; |
| case _CH_SPAWN: |
| case _CH_EXEC: |
| if (!should_be_cb) |
| should_be_cb = sizeof (child_info_spawn); |
| if (should_be_cb != res->cb) |
| multiple_cygwin_problem ("proc size", res->cb, should_be_cb); |
| else if (sizeof (fhandler_union) != res->fhandler_union_cb) |
| multiple_cygwin_problem ("fhandler size", res->fhandler_union_cb, |
| sizeof (fhandler_union)); |
| if (res->isstraced ()) |
| { |
| while (!being_debugged ()) |
| yield (); |
| strace.activate (res->type == _CH_FORK); |
| } |
| break; |
| default: |
| system_printf ("unknown exec type %u", res->type); |
| /* intentionally fall through */ |
| case _CH_WHOOPS: |
| res = NULL; |
| break; |
| } |
| } |
| |
| return res; |
| } |
| |
| #ifdef __x86_64__ |
| #define dll_data_start &__data_start__ |
| #define dll_data_end &__data_end__ |
| #define dll_bss_start &__bss_start__ |
| #define dll_bss_end &__bss_end__ |
| #else |
| #define dll_data_start &_data_start__ |
| #define dll_data_end &_data_end__ |
| #define dll_bss_start &_bss_start__ |
| #define dll_bss_end &_bss_end__ |
| #endif |
| |
| void |
| child_info_fork::handle_fork () |
| { |
| cygheap_fixup_in_child (false); |
| memory_init (); |
| myself.thisproc (NULL); |
| myself->uid = cygheap->user.real_uid; |
| myself->gid = cygheap->user.real_gid; |
| |
| child_copy (parent, false, |
| "dll data", dll_data_start, dll_data_end, |
| "dll bss", dll_bss_start, dll_bss_end, |
| "user heap", cygheap->user_heap.base, cygheap->user_heap.ptr, |
| NULL); |
| |
| /* If my_wr_proc_pipe != NULL then it's a leftover handle from a previously |
| forked process. Close it now or suffer confusion with the parent of our |
| parent. */ |
| if (my_wr_proc_pipe) |
| ForceCloseHandle1 (my_wr_proc_pipe, wr_proc_pipe); |
| |
| /* Setup our write end of the process pipe. Clear the one in the structure. |
| The destructor should never be called for this but, it can't hurt to be |
| safe. */ |
| my_wr_proc_pipe = wr_proc_pipe; |
| rd_proc_pipe = wr_proc_pipe = NULL; |
| /* Do the relocations here. These will actually likely be overwritten by the |
| below child_copy but we do them here in case there is a read-only section |
| which does not get copied by fork. */ |
| _pei386_runtime_relocator (user_data); |
| |
| /* step 2 now that the dll has its heap filled in, we can fill in the |
| user's data and bss since user_data is now filled out. */ |
| child_copy (parent, false, |
| "data", user_data->data_start, user_data->data_end, |
| "bss", user_data->bss_start, user_data->bss_end, |
| NULL); |
| |
| if (fixup_mmaps_after_fork (parent)) |
| api_fatal ("recreate_mmaps_after_fork_failed"); |
| } |
| |
| bool |
| child_info_spawn::get_parent_handle () |
| { |
| parent = OpenProcess (PROCESS_VM_READ, false, parent_winpid); |
| moreinfo->myself_pinfo = NULL; |
| return !!parent; |
| } |
| |
| void |
| child_info_spawn::handle_spawn () |
| { |
| extern void fixup_lockf_after_exec (bool); |
| HANDLE h; |
| if (!dynamically_loaded || get_parent_handle ()) |
| { |
| cygheap_fixup_in_child (true); |
| memory_init (); |
| } |
| if (!moreinfo->myself_pinfo || |
| !DuplicateHandle (GetCurrentProcess (), moreinfo->myself_pinfo, |
| GetCurrentProcess (), &h, 0, |
| FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) |
| h = NULL; |
| |
| /* Setup our write end of the process pipe. Clear the one in the structure. |
| The destructor should never be called for this but, it can't hurt to be |
| safe. */ |
| my_wr_proc_pipe = wr_proc_pipe; |
| rd_proc_pipe = wr_proc_pipe = NULL; |
| |
| myself.thisproc (h); |
| __argc = moreinfo->argc; |
| __argv = moreinfo->argv; |
| envp = moreinfo->envp; |
| envc = moreinfo->envc; |
| if (!dynamically_loaded) |
| cygheap->fdtab.fixup_after_exec (); |
| if (__stdin >= 0) |
| cygheap->fdtab.move_fd (__stdin, 0); |
| if (__stdout >= 0) |
| cygheap->fdtab.move_fd (__stdout, 1); |
| cygheap->user.groups.clear_supp (); |
| |
| /* If we're execing we may have "inherited" a list of children forked by the |
| previous process executing under this pid. Reattach them here so that we |
| can wait for them. */ |
| if (type == _CH_EXEC) |
| reattach_children (); |
| |
| ready (true); |
| |
| /* Keep pointer to parent open if we've execed so that pid will not be reused. |
| Otherwise, we no longer need this handle so close it. |
| Need to do this after debug_fixup_after_fork_exec or DEBUGGING handling of |
| handles might get confused. */ |
| if (type != _CH_EXEC && child_proc_info->parent) |
| { |
| CloseHandle (child_proc_info->parent); |
| child_proc_info->parent = NULL; |
| } |
| |
| signal_fixup_after_exec (); |
| fixup_lockf_after_exec (type == _CH_EXEC); |
| } |
| |
| /* Retrieve and store system directory for later use. Note that the |
| directory is stored with a trailing backslash! */ |
| static void |
| init_windows_system_directory () |
| { |
| if (!windows_system_directory_length) |
| { |
| windows_system_directory_length = |
| GetSystemDirectoryW (windows_system_directory, MAX_PATH); |
| if (windows_system_directory_length == 0) |
| api_fatal ("can't find windows system directory"); |
| windows_system_directory[windows_system_directory_length++] = L'\\'; |
| windows_system_directory[windows_system_directory_length] = L'\0'; |
| #ifdef __i386__ |
| system_wow64_directory_length = |
| GetSystemWow64DirectoryW (system_wow64_directory, MAX_PATH); |
| if (system_wow64_directory_length) |
| { |
| system_wow64_directory[system_wow64_directory_length++] = L'\\'; |
| system_wow64_directory[system_wow64_directory_length] = L'\0'; |
| } |
| #endif /* __i386__ */ |
| } |
| } |
| |
| 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 (); |
| |
| SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); |
| |
| lock_process::init (); |
| _impure_ptr = _GLOBAL_REENT; |
| _impure_ptr->_stdin = &_impure_ptr->__sf[0]; |
| _impure_ptr->_stdout = &_impure_ptr->__sf[1]; |
| _impure_ptr->_stderr = &_impure_ptr->__sf[2]; |
| user_data->impure_ptr = _impure_ptr; |
| user_data->impure_ptr_ptr = &_impure_ptr; |
| |
| DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), |
| GetCurrentProcess (), &hMainThread, |
| 0, false, DUPLICATE_SAME_ACCESS); |
| |
| NtOpenProcessToken (NtCurrentProcess (), MAXIMUM_ALLOWED, &hProcToken); |
| set_cygwin_privileges (hProcToken); |
| |
| device::init (); |
| do_global_ctors (&__CTOR_LIST__, 1); |
| cygthread::init (); |
| |
| if (!child_proc_info) |
| { |
| setup_cygheap (); |
| memory_init (); |
| } |
| else |
| { |
| cygwin_user_h = child_proc_info->user_h; |
| switch (child_proc_info->type) |
| { |
| case _CH_FORK: |
| fork_info->handle_fork (); |
| break; |
| case _CH_SPAWN: |
| case _CH_EXEC: |
| spawn_info->handle_spawn (); |
| break; |
| } |
| } |
| |
| user_data->threadinterface->Init (); |
| |
| _main_tls = &_my_tls; |
| |
| /* Initialize signal processing here, early, in the hopes that the creation |
| of a thread early in the process will cause more predictability in memory |
| layout for the main thread. */ |
| if (!dynamically_loaded) |
| sigproc_init (); |
| |
| #ifdef __x86_64__ |
| /* See comment preceeding myfault_altstack_handler in exception.cc. */ |
| AddVectoredContinueHandler (0, myfault_altstack_handler); |
| #endif |
| |
| debug_printf ("finished dll_crt0_0 initialization"); |
| } |
| |
| static inline void |
| main_thread_sinit () |
| { |
| __sinit (_impure_ptr); |
| /* At this point, _impure_ptr == _global_impure_ptr == _GLOBAL_REENT is |
| initialized, but _REENT == _my_tls.local_clib doesn't know about it. |
| It has been copied over from _GLOBAL_REENT in _cygtls::init_thread |
| *before* the initialization took place. |
| |
| As soon as the main thread calls a stdio function, this would be |
| rectified. But if another thread calls a stdio function on |
| stdin/out/err before the main thread does, all the required |
| initialization of stdin/out/err will be done, but _REENT->__sdidinit |
| is *still* 0. This in turn will result in a call to __sinit in the |
| wrong spot. The input or output buffer will be NULLed and nothing is |
| read or written in the first stdio function call in the main thread. |
| |
| To fix this issue we have to copy over the relevant part of _GLOBAL_REENT |
| to _REENT here again. */ |
| _REENT->__sdidinit = -1; |
| _REENT->__cleanup = _GLOBAL_REENT->__cleanup; |
| } |
| |
| /* Take over from libc's crt0.o and start the application. Note the |
| various special cases when Cygwin DLL is being runtime loaded (as |
| opposed to being link-time loaded by Cygwin apps) from a non |
| cygwin app via LoadLibrary. */ |
| void |
| dll_crt0_1 (void *) |
| { |
| extern void initial_setlocale (); |
| |
| _my_tls.incyg++; |
| /* Inherit "parent" exec'ed process sigmask */ |
| if (spawn_info && !in_forkee) |
| _my_tls.sigmask = spawn_info->moreinfo->sigmask; |
| |
| if (dynamically_loaded) |
| sigproc_init (); |
| |
| check_sanity_and_sync (user_data); |
| |
| /* Initialize malloc and then call user_shared_initialize since it relies |
| on a functioning malloc and it's possible that the user's program may |
| have overridden malloc. We only know about that at this stage, |
| unfortunately. */ |
| malloc_init (); |
| user_shared->initialize (); |
| |
| #ifdef CYGHEAP_DEBUG |
| int i = 0; |
| const int n = 2 * 1024 * 1024; |
| while (i--) |
| { |
| void *p = cmalloc (HEAP_STR, n); |
| if (p) |
| small_printf ("cmalloc returns %p\n", cmalloc (HEAP_STR, n)); |
| else |
| { |
| small_printf ("total allocated %y\n", (i - 1) * n); |
| break; |
| } |
| } |
| #endif |
| |
| ProtectHandle (hMainThread); |
| |
| cygheap->cwd.init (); |
| |
| /* Initialize pthread mainthread when not forked and it is safe to call new, |
| otherwise it is reinitalized in fixup_after_fork */ |
| if (!in_forkee) |
| { |
| pthread::init_mainthread (); |
| _pei386_runtime_relocator (user_data); |
| } |
| |
| #ifdef DEBUGGING |
| strace.microseconds (); |
| #endif |
| |
| /* Initialize debug muto, if DLL is built with --enable-debugging. |
| Need to do this before any helper threads start. */ |
| debug_init (); |
| |
| cygbench ("pre-forkee"); |
| if (in_forkee) |
| { |
| /* Make sure to restore the TEB's stack info. If guardsize is -1 the |
| stack has been provided by the application and must not be deallocated |
| automagically when the thread exits. |
| |
| NOTE: Don't do anything that involves the stack until you've completed |
| this step. */ |
| PTEB teb = NtCurrentTeb (); |
| teb->Tib.StackBase = (PVOID) fork_info->stackbase; |
| teb->Tib.StackLimit = (PVOID) fork_info->stacklimit; |
| teb->DeallocationStack = (fork_info->guardsize == (size_t) -1) |
| ? NULL |
| : (PVOID) fork_info->stackaddr; |
| |
| /* Not resetting _my_tls.incyg here because presumably fork will overwrite |
| it with the value of the forker and all will be good. */ |
| longjmp (fork_info->jmp, true); |
| } |
| |
| main_thread_sinit (); |
| |
| #ifdef DEBUGGING |
| { |
| extern void fork_init (); |
| fork_init (); |
| } |
| #endif |
| pinfo_init (envp, envc); |
| strace.dll_info (); |
| |
| /* Allocate cygheap->fdtab */ |
| dtable_init (); |
| |
| /* Set internal locale to the environment settings. */ |
| initial_setlocale (); |
| |
| uinfo_init (); /* initialize user info */ |
| |
| /* Connect to tty. */ |
| tty::init_session (); |
| |
| if (!__argc) |
| { |
| PWCHAR wline = GetCommandLineW (); |
| size_t size = sys_wcstombs_no_path (NULL, 0, wline) + 1; |
| char *line = (char *) alloca (size); |
| sys_wcstombs_no_path (line, size, wline); |
| |
| /* Scan the command line and build argv. Expand wildcards if not |
| called from another cygwin process. */ |
| build_argv (line, __argv, __argc, |
| NOTSTATE (myself, PID_CYGPARENT) && allow_glob); |
| |
| /* Convert argv[0] to posix rules if it's currently blatantly |
| win32 style. */ |
| if ((strchr (__argv[0], ':')) || (strchr (__argv[0], '\\'))) |
| { |
| char *new_argv0 = (char *) malloc (NT_MAX_PATH); |
| cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, __argv[0], |
| new_argv0, NT_MAX_PATH); |
| __argv[0] = (char *) realloc (new_argv0, strlen (new_argv0) + 1); |
| } |
| } |
| |
| __argc_safe = __argc; |
| if (user_data->premain[0]) |
| for (unsigned int i = 0; i < PREMAIN_LEN / 2; i++) |
| user_data->premain[i] (__argc, __argv, user_data); |
| |
| /* Set up standard fds in file descriptor table. */ |
| cygheap->fdtab.stdio_init (); |
| |
| /* Set up __progname for getopt error call. */ |
| if (__argv[0] && (__progname = strrchr (__argv[0], '/'))) |
| ++__progname; |
| else |
| __progname = __argv[0]; |
| program_invocation_name = __argv[0]; |
| program_invocation_short_name = __progname; |
| if (__progname) |
| { |
| char *cp = strchr (__progname, '\0') - 4; |
| if (cp > __progname && ascii_strcasematch (cp, ".exe")) |
| *cp = '\0'; |
| } |
| SetThreadName (GetCurrentThreadId (), program_invocation_short_name); |
| |
| (void) xdr_set_vprintf (&cygxdr_vwarnx); |
| cygwin_finished_initializing = true; |
| /* Call init of loaded dlls. */ |
| dlls.init (); |
| |
| /* Execute any specified "premain" functions */ |
| if (user_data->premain[PREMAIN_LEN / 2]) |
| for (unsigned int i = PREMAIN_LEN / 2; i < PREMAIN_LEN; i++) |
| user_data->premain[i] (__argc, __argv, user_data); |
| |
| set_errno (0); |
| |
| if (dynamically_loaded) |
| { |
| _setlocale_r (_REENT, LC_CTYPE, "C"); |
| return; |
| } |
| |
| /* Disable case-insensitive globbing */ |
| ignore_case_with_glob = false; |
| |
| cygbench (__progname); |
| |
| ld_preload (); |
| /* Per POSIX set the default application locale back to "C". */ |
| _setlocale_r (_REENT, LC_CTYPE, "C"); |
| |
| if (!user_data->main) |
| { |
| /* Handle any signals which may have arrived */ |
| _my_tls.call_signal_handler (); |
| _my_tls.incyg--; /* Not in Cygwin anymore */ |
| } |
| else |
| { |
| /* Create a copy of Cygwin's version of __argv so that, if the user makes |
| a change to an element of argv[] it does not affect Cygwin's argv. |
| Changing the the contents of what argv[n] points to will still |
| affect Cygwin. This is similar (but not exactly like) Linux. */ |
| char *newargv[__argc + 1]; |
| char **nav = newargv; |
| char **oav = __argv; |
| while ((*nav++ = *oav++) != NULL) |
| continue; |
| /* Handle any signals which may have arrived */ |
| sig_dispatch_pending (false); |
| _my_tls.call_signal_handler (); |
| _my_tls.incyg--; /* Not in Cygwin anymore */ |
| #ifdef __x86_64__ |
| cygwin_exit (user_data->main (__argc, newargv, __cygwin_environ)); |
| #else |
| cygwin_exit (user_data->main (__argc, newargv, *user_data->envptr)); |
| #endif |
| } |
| __asm__ (" \n\ |
| .global _cygwin_exit_return \n\ |
| .global __cygwin_exit_return \n\ |
| _cygwin_exit_return: \n\ |
| __cygwin_exit_return: \n\ |
| nop \n\ |
| "); |
| } |
| |
| extern "C" void __stdcall |
| _dll_crt0 () |
| { |
| #ifdef __x86_64__ |
| /* Starting with Windows 10 rel 1511, the main stack of an application is |
| not reproducible if a 64 bit process has been started from a 32 bit |
| process. Given that we have enough virtual address space on 64 bit |
| anyway, we now always move the main thread stack to the stack area |
| reserved for pthread stacks. This allows a reproducible stack space |
| under our own control and avoids collision with the OS. */ |
| if (!dynamically_loaded) |
| { |
| if (!in_forkee) |
| { |
| /* Must be static since it's referenced after the stack and frame |
| pointer registers have been changed. */ |
| static PVOID allocationbase; |
| SIZE_T commitsize = in_forkee ? (PBYTE) fork_info->stackbase |
| - (PBYTE) fork_info->stacklimit |
| : 0; |
| PVOID stackaddr = create_new_main_thread_stack (allocationbase, |
| commitsize); |
| if (stackaddr) |
| { |
| /* Set stack pointer to new address. Set frame pointer to |
| stack pointer and subtract 32 bytes for shadow space. */ |
| __asm__ ("\n\ |
| movq %[ADDR], %%rsp \n\ |
| movq %%rsp, %%rbp \n\ |
| subq $32,%%rsp \n" |
| : : [ADDR] "r" (stackaddr)); |
| /* We're on the new stack now. Free up space taken by the former |
| main thread stack and set DeallocationStack correctly. */ |
| VirtualFree (NtCurrentTeb ()->DeallocationStack, 0, MEM_RELEASE); |
| NtCurrentTeb ()->DeallocationStack = allocationbase; |
| } |
| } |
| else |
| fork_info->alloc_stack (); |
| } |
| #else |
| main_environ = user_data->envptr; |
| if (in_forkee) |
| fork_info->alloc_stack (); |
| #endif |
| |
| _feinitialise (); |
| _main_tls = &_my_tls; |
| _main_tls->call ((DWORD (*) (void *, void *)) dll_crt0_1, NULL); |
| } |
| |
| void |
| dll_crt0 (per_process *uptr) |
| { |
| /* Set the local copy of the pointer into the user space. */ |
| if (!in_forkee && uptr && uptr != user_data) |
| { |
| memcpy (user_data, uptr, per_process_overwrite); |
| *(user_data->impure_ptr_ptr) = _GLOBAL_REENT; |
| } |
| _dll_crt0 (); |
| } |
| |
| /* This must be called by anyone who uses LoadLibrary to load cygwin1.dll. |
| You must have CYGTLS_PADSIZE bytes reserved at the bottom of the stack |
| calling this function, and that storage must not be overwritten until you |
| unload cygwin1.dll, as it is used for _my_tls. It is best to load |
| cygwin1.dll before spawning any additional threads in your process. |
| |
| See winsup/testsuite/cygload for an example of how to use cygwin1.dll |
| from MSVC and non-cygwin MinGW applications. */ |
| extern "C" void |
| cygwin_dll_init () |
| { |
| #ifdef __i386__ |
| static char **envp; |
| #endif |
| static int _fmode; |
| |
| user_data->magic_biscuit = sizeof (per_process); |
| |
| #ifdef __i386__ |
| user_data->envptr = &envp; |
| #endif |
| user_data->fmode_ptr = &_fmode; |
| |
| _dll_crt0 (); |
| } |
| |
| extern "C" void |
| __main (void) |
| { |
| /* Ordering is critical here. DLL ctors have already been |
| run as they were being loaded, so we should stack the |
| queued call to DLL dtors now. */ |
| atexit (dll_global_dtors); |
| do_global_ctors (user_data->ctors, false); |
| /* Now we have run global ctors, register their dtors. |
| |
| At exit, global dtors will run first, so the app can still |
| use shared library functions while terminating; then the |
| DLLs will be destroyed; finally newlib will shut down stdio |
| and terminate itself. */ |
| atexit (do_global_dtors); |
| sig_dispatch_pending (true); |
| } |
| |
| void __reg1 |
| do_exit (int status) |
| { |
| syscall_printf ("do_exit (%d), exit_state %d", status, exit_state); |
| |
| lock_process until_exit (true); |
| |
| if (exit_state < ES_EVENTS_TERMINATE) |
| exit_state = ES_EVENTS_TERMINATE; |
| |
| if (exit_state < ES_SIGNAL) |
| { |
| exit_state = ES_SIGNAL; |
| signal (SIGCHLD, SIG_IGN); |
| signal (SIGHUP, SIG_IGN); |
| signal (SIGINT, SIG_IGN); |
| signal (SIGQUIT, SIG_IGN); |
| } |
| |
| if (exit_state < ES_CLOSEALL) |
| { |
| exit_state = ES_CLOSEALL; |
| close_all_files (); |
| } |
| |
| UINT n = (UINT) status; |
| if (exit_state < ES_THREADTERM) |
| { |
| exit_state = ES_THREADTERM; |
| cygthread::terminate (); |
| } |
| |
| myself->stopsig = 0; |
| |
| if (exit_state < ES_HUP_PGRP) |
| { |
| exit_state = ES_HUP_PGRP; |
| /* Kill orphaned children on group leader exit */ |
| if (myself->has_pgid_children && myself->pid == myself->pgid) |
| { |
| siginfo_t si = {0}; |
| si.si_signo = -SIGHUP; |
| si.si_code = SI_KERNEL; |
| sigproc_printf ("%u == pgrp %u, send SIG{HUP,CONT} to stopped children", |
| myself->pid, myself->pgid); |
| kill_pgrp (myself->pgid, si); |
| } |
| } |
| |
| if (exit_state < ES_HUP_SID) |
| { |
| exit_state = ES_HUP_SID; |
| /* Kill the foreground process group on session leader exit */ |
| if (getpgrp () > 0 && myself->pid == myself->sid && real_tty_attached (myself)) |
| { |
| tty *tp = cygwin_shared->tty[myself->ctty]; |
| sigproc_printf ("%u == sid %u, send SIGHUP to children", |
| myself->pid, myself->sid); |
| |
| /* CGF FIXME: This can't be right. */ |
| if (tp->getsid () == myself->sid) |
| tp->kill_pgrp (SIGHUP); |
| } |
| |
| } |
| |
| myself.exit (n); |
| } |
| |
| /* When introducing support for -fuse-cxa-atexit with Cygwin 1.7.32 and |
| GCC 4.8.3-3, we defined __dso_value as &ImageBase. This supposedly allowed |
| a reproducible value which could also be easily evaluated in cygwin_atexit. |
| However, when building C++ applications with -fuse-cxa-atexit, G++ creates |
| calls to __cxa_atexit using the *address* of __dso_handle as DSO handle. |
| |
| So what we do here is this: A call to __cxa_atexit from the application |
| actually calls cygwin__cxa_atexit. From dso_handle (which is either |
| &__dso_handle, or __dso_handle == ImageBase or NULL) we fetch the dll |
| structure of the DLL. Then use dll::handle == ImageBase as the actual DSO |
| handle value in calls to __cxa_atexit and __cxa_finalize. |
| Thus, __cxa_atexit becomes entirely independent of the incoming value of |
| dso_handle, as long as it's *some* pointer into the DSO's address space. */ |
| extern "C" int |
| cygwin__cxa_atexit (void (*fn)(void *), void *obj, void *dso_handle) |
| { |
| dll *d = dso_handle ? dlls.find (dso_handle) : NULL; |
| return __cxa_atexit (fn, obj, d ? d->handle : NULL); |
| } |
| |
| /* This function is only called for applications built with Cygwin versions |
| up to API 0.279. Starting with API 0.280 (Cygwin 1.7.33/1.8.6-2), atexit |
| is a statically linked function inside of libcygwin.a. The reason is that |
| the old method to fetch the caller return address is unreliable given GCCs |
| ability to perform tail call elimination. For the details, see the below |
| comment. The atexit replacement is defined in libcygwin.a to allow reliable |
| access to the correct DSO handle. */ |
| extern "C" int |
| cygwin_atexit (void (*fn) (void)) |
| { |
| int res; |
| |
| dll *d = dlls.find ((void *) _my_tls.retaddr ()); |
| #ifdef __x86_64__ |
| /* x86_64 DLLs created with GCC 4.8.3-3 register __gcc_deregister_frame |
| as atexit function using a call to atexit, rather than __cxa_atexit. |
| Due to GCC's tail call optimizing, cygwin_atexit doesn't get the correct |
| return address on the stack. As a result it fails to get the HMODULE of |
| the caller and thus calls atexit rather than __cxa_atexit. Then, if the |
| module gets dlclosed, __cxa_finalize (called from dll_list::detach) can't |
| remove __gcc_deregister_frame from the atexit function chain. So at |
| process exit, __call_exitprocs calls __gcc_deregister_frame while the |
| module is already unloaded and the __gcc_deregister_frame function not |
| available ==> SEGV. |
| |
| This also occurs for other functions. |
| |
| Workaround: If dlls.find fails, try to find the dll entry of the DLL |
| containing fn. If that works, proceed by calling __cxa_atexit, otherwise |
| call atexit. |
| |
| This *should* be sufficiently safe. Ultimately, new applications will |
| use the statically linked atexit function though, as outlined above. */ |
| if (!d) |
| d = dlls.find ((void *) fn); |
| #endif |
| res = d ? __cxa_atexit ((void (*) (void *)) fn, NULL, d->handle) : atexit (fn); |
| return res; |
| } |
| |
| extern "C" void |
| cygwin_exit (int n) |
| { |
| exit_state = ES_EXIT_STARTING; |
| exit (n); |
| } |
| |
| extern "C" void |
| _exit (int n) |
| { |
| do_exit (((DWORD) n & 0xff) << 8); |
| } |
| |
| extern "C" void cygwin_stackdump (); |
| |
| extern "C" void |
| vapi_fatal (const char *fmt, va_list ap) |
| { |
| char buf[4096]; |
| int n = __small_sprintf (buf, "%P: *** fatal error %s- ", in_forkee ? "in forked process " : ""); |
| __small_vsprintf (buf + n, fmt, ap); |
| va_end (ap); |
| strace.prntf (_STRACE_SYSTEM, NULL, "%s", buf); |
| |
| #ifdef DEBUGGING |
| try_to_debug (); |
| #endif |
| cygwin_stackdump (); |
| myself.exit (__api_fatal_exit_val); |
| } |
| |
| extern "C" void |
| api_fatal (const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start (ap, fmt); |
| vapi_fatal (fmt, ap); |
| } |
| |
| void |
| multiple_cygwin_problem (const char *what, uintptr_t magic_version, uintptr_t version) |
| { |
| if (_cygwin_testing && (strstr (what, "proc") || strstr (what, "cygheap"))) |
| { |
| child_proc_info->type = _CH_WHOOPS; |
| return; |
| } |
| |
| if (GetEnvironmentVariableA ("CYGWIN_MISMATCH_OK", NULL, 0)) |
| return; |
| |
| if (CYGWIN_VERSION_MAGIC_VERSION (magic_version) == version) |
| system_printf ("%s magic number mismatch detected - %p/%ly", what, magic_version, version); |
| else |
| api_fatal ("%s mismatch detected - %ly/%ly.\n\ |
| This problem is probably due to using incompatible versions of the cygwin DLL.\n\ |
| Search for cygwin1.dll using the Windows Start->Find/Search facility\n\ |
| and delete all but the most recent version. The most recent version *should*\n\ |
| reside in x:\\cygwin\\bin, where 'x' is the drive on which you have\n\ |
| installed the cygwin distribution. Rebooting is also suggested if you\n\ |
| are unable to find another cygwin DLL.", |
| what, magic_version, version); |
| } |
| |
| #ifdef DEBUGGING |
| void __reg1 |
| cygbench (const char *s) |
| { |
| if (GetEnvironmentVariableA ("CYGWIN_BENCH", NULL, 0)) |
| small_printf ("%05u ***** %s : %10d\n", GetCurrentProcessId (), s, strace.microseconds ()); |
| } |
| #endif |