From 2636d277cefb9a73ae497e7ff8aa30f79b213c6b Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Thu, 30 Apr 2026 08:26:56 +0000 Subject: [PATCH 01/11] Add minipal_create_process and reuse --- src/coreclr/pal/src/include/pal/procobj.hpp | 14 - src/coreclr/pal/src/thread/process.cpp | 1529 ++--------------- src/coreclr/pal/tests/palsuite/CMakeLists.txt | 2 - .../pal/tests/palsuite/compilableTests.txt | 1 - .../pal/tests/palsuite/paltestlist.txt | 1 - .../CreateProcessW/test2/childprocess.cpp | 77 - .../CreateProcessW/test2/parentprocess.cpp | 253 --- .../threading/CreateProcessW/test2/test2.h | 30 - src/coreclr/vm/autotrace.cpp | 25 +- src/native/minipal/CMakeLists.txt | 1 + src/native/minipal/process.c | 376 ++++ src/native/minipal/process.h | 67 + 12 files changed, 607 insertions(+), 1769 deletions(-) delete mode 100644 src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/childprocess.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/test2.h create mode 100644 src/native/minipal/process.c create mode 100644 src/native/minipal/process.h diff --git a/src/coreclr/pal/src/include/pal/procobj.hpp b/src/coreclr/pal/src/include/pal/procobj.hpp index 80f9aa327a1058..a412afea1ca3ad 100644 --- a/src/coreclr/pal/src/include/pal/procobj.hpp +++ b/src/coreclr/pal/src/include/pal/procobj.hpp @@ -74,20 +74,6 @@ namespace CorUnix LONG lAttachCount; }; - PAL_ERROR - InternalCreateProcess( - CPalThread *pThread, - LPCWSTR lpApplicationName, - LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation - ); - PAL_ERROR InitializeProcessCommandLine( LPWSTR lpwstrCmdLine, diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 31996bb4c3e78c..4a9cdafee437b9 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -32,6 +32,8 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d #include "pal/stackstring.hpp" #include "pal/signal.hpp" +#include + #include #include @@ -196,16 +198,6 @@ const char* g_argvCreateDump[MAX_ARGV_ENTRIES] = { nullptr }; // pthread_key_t CorUnix::thObjKey; -static WCHAR W16_WHITESPACE[]= {0x0020, 0x0009, 0x000D, 0}; -static WCHAR W16_WHITESPACE_DQUOTE[]= {0x0020, 0x0009, 0x000D, '"', 0}; - -enum FILETYPE -{ - FILE_ERROR,/*ERROR*/ - FILE_UNIX, /*Unix Executable*/ - FILE_DIR /*Directory*/ -}; - #pragma pack(push,1) // When creating the semaphore name on Mac running in a sandbox, We reference this structure as a byte array // in order to encode its data into a string. Its important to make sure there is no padding between the fields @@ -257,10 +249,6 @@ CreateSemaphoreName( const UnambiguousProcessDescriptor& unambiguousProcessDescriptor, LPCSTR applicationGroupId); -static BOOL getFileName(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, PathCharString& lpFileName); -static char ** buildArgv(LPCWSTR lpCommandLine, PathCharString& lpAppPath, UINT *pnArg); -static BOOL getPath(PathCharString& lpFileName, PathCharString& lpPathFileName); -static int checkFileType(LPCSTR lpFileName); static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUnconditionally); /*++ @@ -329,8 +317,8 @@ GetCurrentProcess( CreateProcessW Note: - Only Standard handles need to be inherited. - Security attributes parameters are not used. + This is a thin wrapper that delegates to the minipal process API. + Security attributes and most creation flags are not supported. See MSDN doc. --*/ @@ -348,773 +336,245 @@ CreateProcessW( IN LPSTARTUPINFOW lpStartupInfo, OUT LPPROCESS_INFORMATION lpProcessInformation) { - PAL_ERROR palError = NO_ERROR; - CPalThread *pThread; - PERF_ENTRY(CreateProcessW); - ENTRY("CreateProcessW(lpAppName=%p (%S), lpCmdLine=%p (%S), lpProcessAttr=%p," - "lpThreadAttr=%p, bInherit=%d, dwFlags=%#x, lpEnv=%p," - "lpCurrentDir=%p (%S), lpStartupInfo=%p, lpProcessInfo=%p)\n", + ENTRY("CreateProcessW(lpAppName=%p (%S), lpCmdLine=%p (%S), dwFlags=%#x)\n", lpApplicationName?lpApplicationName:W16_NULLSTRING, lpApplicationName?lpApplicationName:W16_NULLSTRING, lpCommandLine?lpCommandLine:W16_NULLSTRING, - lpCommandLine?lpCommandLine:W16_NULLSTRING,lpProcessAttributes, - lpThreadAttributes, bInheritHandles, dwCreationFlags,lpEnvironment, - lpCurrentDirectory?lpCurrentDirectory:W16_NULLSTRING, - lpCurrentDirectory?lpCurrentDirectory:W16_NULLSTRING, - lpStartupInfo, lpProcessInformation); - - pThread = InternalGetCurrentThread(); - - palError = InternalCreateProcess( - pThread, - lpApplicationName, - lpCommandLine, - lpProcessAttributes, - lpThreadAttributes, - dwCreationFlags, - lpEnvironment, - lpCurrentDirectory, - lpStartupInfo, - lpProcessInformation - ); - - if (NO_ERROR != palError) - { - pThread->SetLastError(palError); - } - - LOGEXIT("CreateProcessW returns BOOL %d\n", NO_ERROR == palError); - PERF_EXIT(CreateProcessW); - - return NO_ERROR == palError; -} - -PAL_ERROR -PrepareStandardHandle( - CPalThread *pThread, - HANDLE hFile, - IPalObject **ppobjFile, - int *piFd - ) -{ - PAL_ERROR palError = NO_ERROR; - IPalObject *pobjFile = NULL; - IDataLock *pDataLock = NULL; - CFileProcessLocalData *pLocalData = NULL; - int iError = 0; - - palError = g_pObjectManager->ReferenceObjectByHandle( - pThread, - hFile, - &aotFile, - &pobjFile - ); - - if (NO_ERROR != palError) - { - ERROR("Bad handle passed through CreateProcess\n"); - goto PrepareStandardHandleExit; - } - - palError = pobjFile->GetProcessLocalData( - pThread, - ReadLock, - &pDataLock, - reinterpret_cast(&pLocalData) - ); - - if (NO_ERROR != palError) - { - ASSERT("Unable to access file data\n"); - goto PrepareStandardHandleExit; - } - - // - // The passed in file needs to be inheritable - // - - if (!pLocalData->inheritable) - { - ERROR("Non-inheritable handle passed through CreateProcess\n"); - palError = ERROR_INVALID_HANDLE; - goto PrepareStandardHandleExit; - } - - iError = fcntl(pLocalData->unix_fd, F_SETFD, 0); - if (-1 == iError) - { - ERROR("Unable to remove close-on-exec for file (errno %i)\n", errno); - palError = ERROR_INVALID_HANDLE; - goto PrepareStandardHandleExit; - } - - *piFd = pLocalData->unix_fd; - pDataLock->ReleaseLock(pThread, FALSE); - pDataLock = NULL; - - // - // Transfer pobjFile reference to out parameter - // - - *ppobjFile = pobjFile; - pobjFile = NULL; - -PrepareStandardHandleExit: - - if (NULL != pDataLock) - { - pDataLock->ReleaseLock(pThread, FALSE); - } - - if (NULL != pobjFile) - { - pobjFile->ReleaseReference(pThread); - } - - return palError; -} + lpCommandLine?lpCommandLine:W16_NULLSTRING, + dwCreationFlags); -PAL_ERROR -CorUnix::InternalCreateProcess( - CPalThread *pThread, - LPCWSTR lpApplicationName, - LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation - ) -{ -#if defined(TARGET_TVOS) || defined(TARGET_WASM) - return ERROR_NOT_SUPPORTED; -#else PAL_ERROR palError = NO_ERROR; + CPalThread *pThread = InternalGetCurrentThread(); IPalObject *pobjProcess = NULL; IPalObject *pobjProcessRegistered = NULL; - IDataLock *pLocalDataLock = NULL; - CProcProcessLocalData *pLocalData; - IDataLock *pSharedDataLock = NULL; - CPalThread *pDummyThread = NULL; - HANDLE hDummyThread = NULL; + IDataLock *pDataLock = NULL; + CProcProcessLocalData *pLocalData = NULL; + CObjectAttributes oa; HANDLE hProcess = NULL; - CObjectAttributes oa(NULL, lpProcessAttributes); - - IPalObject *pobjFileIn = NULL; - int iFdIn = -1; - IPalObject *pobjFileOut = NULL; - int iFdOut = -1; - IPalObject *pobjFileErr = NULL; - int iFdErr = -1; - - pid_t processId; - PathCharString lpFileNamePS; - char **lppArgv = NULL; - UINT nArg; - int iRet; - char **EnvironmentArray=NULL; - int child_blocking_pipe = -1; - int parent_blocking_pipe = -1; - - /* Validate parameters */ - - /* note : specs indicate lpApplicationName should always - be NULL; however support for it is already implemented. Leaving the code - in, specs can change; but rejecting non-NULL for now to conform to the - spec. */ - if( NULL != lpApplicationName ) - { - ASSERT("lpApplicationName should be NULL, but is %S instead\n", - lpApplicationName); - palError = ERROR_INVALID_PARAMETER; - goto InternalCreateProcessExit; - } - - if (0 != (dwCreationFlags & ~(CREATE_SUSPENDED|CREATE_NEW_CONSOLE))) - { - ASSERT("Unexpected creation flags (%#x)\n", dwCreationFlags); - palError = ERROR_INVALID_PARAMETER; - goto InternalCreateProcessExit; - } - - /* Security attributes parameters are ignored */ - if (lpProcessAttributes != NULL && - (lpProcessAttributes->lpSecurityDescriptor != NULL || - lpProcessAttributes->bInheritHandle != TRUE)) - { - ASSERT("lpProcessAttributes is invalid, parameter ignored (%p)\n", - lpProcessAttributes); - palError = ERROR_INVALID_PARAMETER; - goto InternalCreateProcessExit; - } - - if (lpThreadAttributes != NULL) - { - ASSERT("lpThreadAttributes parameter must be NULL (%p)\n", - lpThreadAttributes); - palError = ERROR_INVALID_PARAMETER; - goto InternalCreateProcessExit; - } + HANDLE hDummyThread = NULL; + BOOL bRet = FALSE; + LPCWSTR lpCmd = lpCommandLine ? lpCommandLine : lpApplicationName; - /* note : Win32 crashes in this case */ - if(NULL == lpStartupInfo) + if (lpCmd == NULL) { - ERROR("lpStartupInfo is NULL\n"); + ERROR("both lpApplicationName and lpCommandLine are NULL\n"); palError = ERROR_INVALID_PARAMETER; - goto InternalCreateProcessExit; + goto done; } - /* Validate lpStartupInfo.cb field */ - if (lpStartupInfo->cb < sizeof(STARTUPINFOW)) { - ASSERT("lpStartupInfo parameter structure size is invalid (%u)\n", - lpStartupInfo->cb); - palError = ERROR_INVALID_PARAMETER; - goto InternalCreateProcessExit; - } + minipal_process_info info; + bool ok = minipal_create_process_w( + (const CHAR16_T*)lpCmd, + (const CHAR16_T*)lpCurrentDirectory, + bInheritHandles != FALSE, + &info); - /* lpStartupInfo should be either zero or STARTF_USESTDHANDLES */ - if (lpStartupInfo->dwFlags & ~STARTF_USESTDHANDLES) - { - ASSERT("lpStartupInfo parameter invalid flags (%#x)\n", - lpStartupInfo->dwFlags); - palError = ERROR_INVALID_PARAMETER; - goto InternalCreateProcessExit; - } + if (!ok) + { + ERROR("minipal_create_process_w failed\n"); + palError = ERROR_ACCESS_DENIED; + goto done; + } - /* validate given standard handles if we have any */ - if (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) - { - palError = PrepareStandardHandle( + // Register the child process in the PAL object manager so that + // WaitForSingleObject, GetExitCodeProcess, etc. work correctly. + palError = g_pObjectManager->AllocateObject( pThread, - lpStartupInfo->hStdInput, - &pobjFileIn, - &iFdIn - ); + &otProcess, + &oa, + &pobjProcess); if (NO_ERROR != palError) { - goto InternalCreateProcessExit; + ERROR("Unable to allocate object for new process\n"); + goto done; } - palError = PrepareStandardHandle( + palError = g_pObjectManager->RegisterObject( pThread, - lpStartupInfo->hStdOutput, - &pobjFileOut, - &iFdOut - ); + pobjProcess, + &aotProcess, + &hProcess, + &pobjProcessRegistered); + + // pobjProcess is invalidated by the above call + pobjProcess = NULL; if (NO_ERROR != palError) { - goto InternalCreateProcessExit; + ERROR("Unable to register new process object\n"); + goto done; } - palError = PrepareStandardHandle( + palError = pobjProcessRegistered->GetProcessLocalData( pThread, - lpStartupInfo->hStdError, - &pobjFileErr, - &iFdErr - ); + WriteLock, + &pDataLock, + reinterpret_cast(&pLocalData)); if (NO_ERROR != palError) { - goto InternalCreateProcessExit; + ERROR("Unable to get process local data\n"); + goto done; } - } - if (!getFileName(lpApplicationName, lpCommandLine, lpFileNamePS)) - { - ERROR("Can't find executable!\n"); - palError = ERROR_FILE_NOT_FOUND; - goto InternalCreateProcessExit; - } + pLocalData->dwProcessId = (DWORD)info.process_id; + pDataLock->ReleaseLock(pThread, TRUE); + pDataLock = NULL; - /* check type of file */ - iRet = checkFileType(lpFileNamePS); + CPalThread *pDummyThread = NULL; + palError = InternalCreateDummyThread( + pThread, + lpThreadAttributes, + &pDummyThread, + &hDummyThread); - switch (iRet) - { - case FILE_ERROR: /* file not found, or not an executable */ - WARN ("File is not valid (%s)", lpFileNamePS.GetString()); - palError = ERROR_FILE_NOT_FOUND; - goto InternalCreateProcessExit; + if (NO_ERROR != palError) + { + ERROR("Unable to create dummy thread handle\n"); + goto done; + } - case FILE_UNIX: /* Unix binary file */ - break; /* nothing to do */ + if (lpProcessInformation != NULL) + { + lpProcessInformation->hProcess = hProcess; + lpProcessInformation->hThread = hDummyThread; + lpProcessInformation->dwProcessId = (DWORD)info.process_id; + lpProcessInformation->dwThreadId_PAL_Undefined = 0; + } + else + { + g_pObjectManager->RevokeHandle(pThread, hProcess); + g_pObjectManager->RevokeHandle(pThread, hDummyThread); + } - case FILE_DIR:/*Directory*/ - WARN ("File is a Directory (%s)", lpFileNamePS.GetString()); - palError = ERROR_ACCESS_DENIED; - goto InternalCreateProcessExit; - break; + // minipal handles are not needed; the PAL manages lifetime via its own handles. + minipal_close_process_handle(info.process_handle); + minipal_close_process_handle(info.thread_handle); - default: /* not supposed to get here */ - ASSERT ("Invalid return type from checkFileType"); - palError = ERROR_FILE_NOT_FOUND; - goto InternalCreateProcessExit; + bRet = TRUE; } - /* build Argument list, lppArgv is allocated in buildArgv function and - requires to be freed */ - lppArgv = buildArgv(lpCommandLine, lpFileNamePS, &nArg); +done: - /* set the Environment variable */ - if (lpEnvironment != NULL) + if (NO_ERROR != palError && palError != ERROR_INVALID_PARAMETER) { - unsigned i; - // Since CREATE_UNICODE_ENVIRONMENT isn't supported we know the string is ansi - unsigned EnvironmentEntries = 0; - // Convert the environment block to array of strings - // Count the number of entries - // Is it a string that contains null terminated string, the end is delimited - // by two null in a row. - for (i = 0; ((char *)lpEnvironment)[i]!='\0'; i++) + if (NULL != hDummyThread) { - EnvironmentEntries ++; - for (;((char *)lpEnvironment)[i]!='\0'; i++) - { - } + g_pObjectManager->RevokeHandle(pThread, hDummyThread); } - EnvironmentEntries++; - EnvironmentArray = (char **)malloc(EnvironmentEntries * sizeof(char *)); - - EnvironmentEntries = 0; - // Convert the environment block to array of strings - // Count the number of entries - // Is it a string that contains null terminated string, the end is delimited - // by two null in a row. - for (i = 0; ((char *)lpEnvironment)[i]!='\0'; i++) + if (NULL != hProcess) { - EnvironmentArray[EnvironmentEntries] = &((char *)lpEnvironment)[i]; - EnvironmentEntries ++; - for (;((char *)lpEnvironment)[i]!='\0'; i++) - { - } + g_pObjectManager->RevokeHandle(pThread, hProcess); } - EnvironmentArray[EnvironmentEntries] = NULL; } - // - // Allocate and register the process object for the new process - // + if (NULL != pobjProcess) + { + pobjProcess->ReleaseReference(pThread); + } - palError = g_pObjectManager->AllocateObject( - pThread, - &otProcess, - &oa, - &pobjProcess - ); + if (NULL != pobjProcessRegistered) + { + pobjProcessRegistered->ReleaseReference(pThread); + } if (NO_ERROR != palError) { - ERROR("Unable to allocate object for new process\n"); - goto InternalCreateProcessExit; + pThread->SetLastError(palError); } - palError = g_pObjectManager->RegisterObject( - pThread, - pobjProcess, - &aotProcess, - &hProcess, - &pobjProcessRegistered - ); + LOGEXIT("CreateProcessW returns BOOL %d\n", bRet); + PERF_EXIT(CreateProcessW); + return bRet; +} - // - // pobjProcess is invalidated by the above call, so - // NULL it out here - // - pobjProcess = NULL; +/*++ +Function: + GetExitCodeProcess - if (NO_ERROR != palError) - { - ERROR("Unable to register new process object\n"); - goto InternalCreateProcessExit; - } +See MSDN doc. +--*/ +BOOL +PALAPI +GetExitCodeProcess( + IN HANDLE hProcess, + IN LPDWORD lpExitCode) +{ + CPalThread *pThread; + PAL_ERROR palError = NO_ERROR; + DWORD dwExitCode; + PROCESS_STATE ps; - // - // Create a new "dummy" thread object - // + PERF_ENTRY(GetExitCodeProcess); + ENTRY("GetExitCodeProcess(hProcess = %p, lpExitCode = %p)\n", + hProcess, lpExitCode); - palError = InternalCreateDummyThread( - pThread, - lpThreadAttributes, - &pDummyThread, - &hDummyThread - ); + pThread = InternalGetCurrentThread(); - if (dwCreationFlags & CREATE_SUSPENDED) + if(NULL == lpExitCode) { - int pipe_descs[2]; - - if (-1 == pipe(pipe_descs)) - { - ERROR("pipe() failed! error is %d (%s)\n", errno, strerror(errno)); - palError = ERROR_NOT_ENOUGH_MEMORY; - goto InternalCreateProcessExit; - } - - /* [0] is read end, [1] is write end */ - pDummyThread->suspensionInfo.SetBlockingPipe(pipe_descs[1]); - parent_blocking_pipe = pipe_descs[1]; - child_blocking_pipe = pipe_descs[0]; + WARN("Got NULL lpExitCode\n"); + palError = ERROR_INVALID_PARAMETER; + goto done; } - palError = pobjProcessRegistered->GetProcessLocalData( + palError = PROCGetProcessStatus( pThread, - WriteLock, - &pLocalDataLock, - reinterpret_cast(&pLocalData) + hProcess, + &ps, + &dwExitCode ); if (NO_ERROR != palError) { - ASSERT("Unable to obtain local data for new process object\n"); - goto InternalCreateProcessExit; + ASSERT("Couldn't get process status information!\n"); + goto done; } - - /* fork the new process */ - processId = fork(); - - if (processId == -1) + if( PS_DONE == ps ) { - ASSERT("Unable to create a new process with fork()\n"); - if (-1 != child_blocking_pipe) - { - close(child_blocking_pipe); - close(parent_blocking_pipe); - } - - palError = ERROR_INTERNAL_ERROR; - goto InternalCreateProcessExit; + *lpExitCode = dwExitCode; + } + else + { + *lpExitCode = STILL_ACTIVE; } - /* From the time the child process begins running, to when it reaches execve, - the child process is not a real PAL process and does not own any PAL - resources, although it has access to the PAL resources of its parent process. - Thus, while the child process is in this window, it is dangerous for it to affect - its parent's PAL resources. As a consequence, no PAL code should be used - in this window; all code should make unix calls. Note the use of _exit - instead of exit to avoid calling PAL_Terminate and the lack of TRACE's and - ASSERT's. */ +done: - if (processId == 0) /* child process */ + if (NO_ERROR != palError) { - // At this point, the PAL should be considered uninitialized for this child process. + pThread->SetLastError(palError); + } - // Don't want to enter the init_critsec here since we're trying to avoid - // calling PAL functions. Furthermore, nothing should be changing - // the init_count in the child process at this point since this is the only - // thread executing. - init_count = 0; + LOGEXIT("GetExitCodeProcess returns BOOL %d\n", NO_ERROR == palError); + PERF_EXIT(GetExitCodeProcess); - sigset_t sm; + return NO_ERROR == palError; +} - // - // Clear out the signal mask for the new process. - // +/*++ +Function: + ExitProcess - sigemptyset(&sm); - iRet = sigprocmask(SIG_SETMASK, &sm, NULL); - if (iRet != 0) - { - _exit(EXIT_FAILURE); - } +See MSDN doc. +--*/ +PAL_NORETURN +VOID +PALAPI +ExitProcess( + IN UINT uExitCode) +{ + DWORD old_terminator; - if (dwCreationFlags & CREATE_SUSPENDED) - { - BYTE resume_code = 0; - ssize_t read_ret; + PERF_ENTRY_ONLY(ExitProcess); + ENTRY("ExitProcess(uExitCode=0x%x)\n", uExitCode ); - /* close the write end of the pipe, the child doesn't need it */ - close(parent_blocking_pipe); - - read_again: - /* block until ResumeThread writes something to the pipe */ - read_ret = read(child_blocking_pipe, &resume_code, sizeof(resume_code)); - if (sizeof(resume_code) != read_ret) - { - if (read_ret == -1 && EINTR == errno) - { - goto read_again; - } - else - { - /* note : read might return 0 (and return EAGAIN) if the other - end of the pipe gets closed - for example because the parent - process dies (very) abruptly */ - _exit(EXIT_FAILURE); - } - } - if (WAKEUPCODE != resume_code) - { - // resume_code should always equal WAKEUPCODE. - _exit(EXIT_FAILURE); - } - - close(child_blocking_pipe); - } - - /* Set the current directory */ - if (lpCurrentDirectory) - { - SetCurrentDirectoryW(lpCurrentDirectory); - } - - /* Set the standard handles to the incoming values */ - if (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) - { - /* For each handle, we need to duplicate the incoming unix - fd to the corresponding standard one. The API that I use, - dup2, will copy the source to the destination, automatically - closing the existing destination, in an atomic way */ - if (dup2(iFdIn, STDIN_FILENO) == -1) - { - // Didn't duplicate standard in. - _exit(EXIT_FAILURE); - } - - if (dup2(iFdOut, STDOUT_FILENO) == -1) - { - // Didn't duplicate standard out. - _exit(EXIT_FAILURE); - } - - if (dup2(iFdErr, STDERR_FILENO) == -1) - { - // Didn't duplicate standard error. - _exit(EXIT_FAILURE); - } - - /* now close the original FDs, we don't need them anymore */ - close(iFdIn); - close(iFdOut); - close(iFdErr); - } - - /* execute the new process */ - - if (EnvironmentArray) - { - execve(lpFileNamePS, lppArgv, EnvironmentArray); - } - else - { - execve(lpFileNamePS, lppArgv, palEnvironment); - } - - /* if we get here, it means the execve function call failed so just exit */ - _exit(EXIT_FAILURE); - } - - /* parent process */ - - /* close the read end of the pipe, the parent doesn't need it */ - close(child_blocking_pipe); - - /* Set the process ID */ - pLocalData->dwProcessId = processId; - pLocalDataLock->ReleaseLock(pThread, TRUE); - pLocalDataLock = NULL; - - // - // Release file handle info; we don't need them anymore. Note that - // this must happen after we've released the data locks, as - // otherwise a deadlock could result. - // - - if (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) - { - pobjFileIn->ReleaseReference(pThread); - pobjFileIn = NULL; - pobjFileOut->ReleaseReference(pThread); - pobjFileOut = NULL; - pobjFileErr->ReleaseReference(pThread); - pobjFileErr = NULL; - } - - /* fill PROCESS_INFORMATION structure */ - lpProcessInformation->hProcess = hProcess; - lpProcessInformation->hThread = hDummyThread; - lpProcessInformation->dwProcessId = processId; - lpProcessInformation->dwThreadId_PAL_Undefined = 0; - - - TRACE("New process created: id=%#x\n", processId); - -InternalCreateProcessExit: - - if (NULL != pLocalDataLock) - { - pLocalDataLock->ReleaseLock(pThread, FALSE); - } - - if (NULL != pSharedDataLock) - { - pSharedDataLock->ReleaseLock(pThread, FALSE); - } - - if (NULL != pobjProcess) - { - pobjProcess->ReleaseReference(pThread); - } - - if (NULL != pobjProcessRegistered) - { - pobjProcessRegistered->ReleaseReference(pThread); - } - - if (NO_ERROR != palError) - { - if (NULL != hProcess) - { - g_pObjectManager->RevokeHandle(pThread, hProcess); - } - - if (NULL != hDummyThread) - { - g_pObjectManager->RevokeHandle(pThread, hDummyThread); - } - } - - if (EnvironmentArray) - { - free(EnvironmentArray); - } - - /* if we still have the file structures at this point, it means we - encountered an error sometime between when we acquired them and when we - fork()ed. We not only have to release them, we have to give them back - their close-on-exec flag */ - if (NULL != pobjFileIn) - { - if(-1 == fcntl(iFdIn, F_SETFD, 1)) - { - WARN("couldn't restore close-on-exec flag to stdin descriptor! " - "errno is %d (%s)\n", errno, strerror(errno)); - } - pobjFileIn->ReleaseReference(pThread); - } - - if (NULL != pobjFileOut) - { - if(-1 == fcntl(iFdOut, F_SETFD, 1)) - { - WARN("couldn't restore close-on-exec flag to stdout descriptor! " - "errno is %d (%s)\n", errno, strerror(errno)); - } - pobjFileOut->ReleaseReference(pThread); - } - - if (NULL != pobjFileErr) - { - if(-1 == fcntl(iFdErr, F_SETFD, 1)) - { - WARN("couldn't restore close-on-exec flag to stderr descriptor! " - "errno is %d (%s)\n", errno, strerror(errno)); - } - pobjFileErr->ReleaseReference(pThread); - } - - /* free allocated memory */ - if (lppArgv) - { - free(*lppArgv); - free(lppArgv); - } - - return palError; -#endif // !TARGET_TVOS && !TARGET_WASM -} - - -/*++ -Function: - GetExitCodeProcess - -See MSDN doc. ---*/ -BOOL -PALAPI -GetExitCodeProcess( - IN HANDLE hProcess, - IN LPDWORD lpExitCode) -{ - CPalThread *pThread; - PAL_ERROR palError = NO_ERROR; - DWORD dwExitCode; - PROCESS_STATE ps; - - PERF_ENTRY(GetExitCodeProcess); - ENTRY("GetExitCodeProcess(hProcess = %p, lpExitCode = %p)\n", - hProcess, lpExitCode); - - pThread = InternalGetCurrentThread(); - - if(NULL == lpExitCode) - { - WARN("Got NULL lpExitCode\n"); - palError = ERROR_INVALID_PARAMETER; - goto done; - } - - palError = PROCGetProcessStatus( - pThread, - hProcess, - &ps, - &dwExitCode - ); - - if (NO_ERROR != palError) - { - ASSERT("Couldn't get process status information!\n"); - goto done; - } - - if( PS_DONE == ps ) - { - *lpExitCode = dwExitCode; - } - else - { - *lpExitCode = STILL_ACTIVE; - } - -done: - - if (NO_ERROR != palError) - { - pThread->SetLastError(palError); - } - - LOGEXIT("GetExitCodeProcess returns BOOL %d\n", NO_ERROR == palError); - PERF_EXIT(GetExitCodeProcess); - - return NO_ERROR == palError; -} - -/*++ -Function: - ExitProcess - -See MSDN doc. ---*/ -PAL_NORETURN -VOID -PALAPI -ExitProcess( - IN UINT uExitCode) -{ - DWORD old_terminator; - - PERF_ENTRY_ONLY(ExitProcess); - ENTRY("ExitProcess(uExitCode=0x%x)\n", uExitCode ); - - old_terminator = InterlockedCompareExchange(&terminator, GetCurrentThreadId(), 0); + old_terminator = InterlockedCompareExchange(&terminator, GetCurrentThreadId(), 0); if (GetCurrentThreadId() == old_terminator) { @@ -2334,7 +1794,7 @@ PROCBuildCreateDumpCommandLine( { return FALSE; } - + int argc = 0; argv[argc++] = program; @@ -3470,670 +2930,3 @@ bool GetApplicationContainerFolder(PathCharString& buffer, const char *applicati && buffer.Append('/'); } #endif // __APPLE__ - -/* Internal function definitions **********************************************/ - -/*++ -Function: - getFileName - -Abstract: - Helper function for CreateProcessW, it retrieves the executable filename - from the application name, and the command line. - -Parameters: - IN lpApplicationName: first parameter from CreateProcessW (an unicode string) - IN lpCommandLine: second parameter from CreateProcessW (an unicode string) - OUT lpFileName: file to be executed (the new process) - -Return: - TRUE: if the file name is retrieved - FALSE: otherwise - ---*/ -static -BOOL -getFileName( - LPCWSTR lpApplicationName, - LPWSTR lpCommandLine, - PathCharString& lpPathFileName) -{ - LPWSTR lpEnd; - WCHAR wcEnd; - char * lpFileName; - PathCharString lpFileNamePS; - char *lpTemp; - - if (lpApplicationName) - { - int length = WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1, - NULL, 0, NULL, NULL); - - /* if only a file name is specified, prefix it with "./" */ - if ((*lpApplicationName != '.') && (*lpApplicationName != '/')) - { - length += 2; - lpTemp = lpPathFileName.OpenStringBuffer(length); - - if (strcpy_s(lpTemp, length, "./") != SAFECRT_SUCCESS) - { - ERROR("strcpy_s failed!\n"); - return FALSE; - } - lpTemp+=2; - - } - else - { - lpTemp = lpPathFileName.OpenStringBuffer(length); - } - - /* Convert to ASCII */ - length = WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1, - lpTemp, length, NULL, NULL); - if (length == 0) - { - lpPathFileName.CloseBuffer(0); - ASSERT("WideCharToMultiByte failure\n"); - return FALSE; - } - - lpPathFileName.CloseBuffer(length -1); - - return TRUE; - } - else - { - /* use the Command line */ - - /* filename should be the first token of the command line */ - - /* first skip all leading whitespace */ - lpCommandLine = UTIL_inverse_wcspbrk(lpCommandLine,W16_WHITESPACE); - if(NULL == lpCommandLine) - { - ERROR("CommandLine contains only whitespace!\n"); - return FALSE; - } - - /* check if it is starting with a quote (") character */ - if (*lpCommandLine == 0x0022) - { - lpCommandLine++; /* skip the quote */ - - /* file name ends with another quote */ - lpEnd = PAL_wcschr(lpCommandLine+1, 0x0022); - - /* if no quotes found, set lpEnd to the end of the Command line */ - if (lpEnd == NULL) - lpEnd = lpCommandLine + PAL_wcslen(lpCommandLine); - } - else - { - /* filename is end out by a whitespace */ - lpEnd = PAL_wcspbrk(lpCommandLine, W16_WHITESPACE); - - /* if no whitespace found, set lpEnd to end of the Command line */ - if (lpEnd == NULL) - { - lpEnd = lpCommandLine + PAL_wcslen(lpCommandLine); - } - } - - if (lpEnd == lpCommandLine) - { - ERROR("application name and command line are both empty!\n"); - return FALSE; - } - - /* replace the last character by a null */ - wcEnd = *lpEnd; - *lpEnd = 0x0000; - - /* Convert to UTF-8 */ - int size = 0; - int length = WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, NULL, 0, NULL, NULL); - if (length == 0) - { - ERROR("Failed to calculate the required buffer length.\n"); - return FALSE; - }; - - lpFileName = lpFileNamePS.OpenStringBuffer(length - 1); - if (NULL == lpFileName) - { - ERROR("Not Enough Memory!\n"); - return FALSE; - } - if (!(size = WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, - lpFileName, length, NULL, NULL))) - { - ASSERT("WideCharToMultiByte failure\n"); - return FALSE; - } - - lpFileNamePS.CloseBuffer(size - 1); - /* restore last character */ - *lpEnd = wcEnd; - - if (!getPath(lpFileNamePS, lpPathFileName)) - { - /* file is not in the path */ - return FALSE; - } - } - return TRUE; -} - -/*++ -Function: - checkFileType - -Abstract: - Return the type of the file. - -Parameters: - IN lpFileName: file name - -Return: - FILE_DIR: Directory - FILE_UNIX: Unix executable file - FILE_ERROR: Error ---*/ -static -int -checkFileType( LPCSTR lpFileName) -{ - struct stat stat_data; - - /* check if the file exist */ - if ( access(lpFileName, F_OK) != 0 ) - { - return FILE_ERROR; - } - - /* if it's not a PE/COFF file, check if it is executable */ - if ( -1 != stat( lpFileName, &stat_data ) ) - { - if((stat_data.st_mode & S_IFMT) == S_IFDIR ) - { - /*The given file is a directory*/ - return FILE_DIR; - } - if ( UTIL_IsExecuteBitsSet( &stat_data ) ) - { - return FILE_UNIX; - } - else - { - return FILE_ERROR; - } - } - return FILE_ERROR; - -} - - -/*++ -Function: - buildArgv - -Abstract: - Helper function for CreateProcessW, it builds the array of argument in - a format than can be passed to execve function.lppArgv is allocated - in this function and must be freed by the caller. - -Parameters: - IN lpCommandLine: second parameter from CreateProcessW (an unicode string) - IN lpAppPath: canonical name of the application to launched - OUT lppArgv: array of arguments to be passed to the new process - -Return: - the number of arguments - -note: this doesn't yet match precisely the behavior of Windows, but should be -sufficient. -what's here: -1) stripping nonquoted whitespace -2) handling of quoted parameters and quoted "parts" of parameters, removal of - doublequotes ( becomes ) -3) \" as an escaped doublequote, both within doublequoted sequences and out -what's known missing : -1) \\ as an escaped backslash, but only if the string of '\' - is followed by a " (escaped or not) -2) "alternate" escape sequence : double-doublequote within a double-quoted - argument (<"aaa a""aa aaa">) expands to a single-doublequote() -note that there may be other special cases ---*/ -static -char ** -buildArgv( - LPCWSTR lpCommandLine, - PathCharString& lpAppPath, - UINT *pnArg) -{ - CPalThread *pThread = NULL; - UINT iWlen; - char *lpAsciiCmdLine; - char *pChar; - char **lppArgv; - char **lppTemp; - UINT i,j; - - *pnArg = 0; - - iWlen = WideCharToMultiByte(CP_ACP,0,lpCommandLine,-1,NULL,0,NULL,NULL); - - if(0 == iWlen) - { - ASSERT("Can't determine length of command line\n"); - return NULL; - } - - pThread = InternalGetCurrentThread(); - /* make sure to allocate enough space, up for the worst case scenario */ - int iLength = (iWlen + lpAppPath.GetCount() + 2); - lpAsciiCmdLine = (char *) malloc(iLength); - - if (lpAsciiCmdLine == NULL) - { - ERROR("Unable to allocate memory\n"); - return NULL; - } - - pChar = lpAsciiCmdLine; - - /* put the canonical name of the application as the first parameter */ - if ((strcpy_s(lpAsciiCmdLine, iLength, "\"") != SAFECRT_SUCCESS) || - (strcat_s(lpAsciiCmdLine, iLength, lpAppPath) != SAFECRT_SUCCESS) || - (strcat_s(lpAsciiCmdLine, iLength, "\"") != SAFECRT_SUCCESS) || - (strcat_s(lpAsciiCmdLine, iLength, " ") != SAFECRT_SUCCESS)) - { - ERROR("strcpy_s/strcat_s failed!\n"); - free(lpAsciiCmdLine); - return NULL; - } - - pChar = lpAsciiCmdLine + strlen (lpAsciiCmdLine); - - /* let's skip the first argument in the command line */ - - /* strip leading whitespace; function returns NULL if there's only - whitespace, so the if statement below will work correctly */ - lpCommandLine = UTIL_inverse_wcspbrk((LPWSTR)lpCommandLine, W16_WHITESPACE); - - if (lpCommandLine) - { - LPCWSTR stringstart = lpCommandLine; - - do - { - /* find first whitespace or dquote character */ - lpCommandLine = PAL_wcspbrk(lpCommandLine,W16_WHITESPACE_DQUOTE); - if(NULL == lpCommandLine) - { - /* no whitespace or dquote found : first arg is only arg */ - break; - } - else if('"' == *lpCommandLine) - { - /* got a dquote; skip over it if it's escaped; make sure we - don't try to look before the first character in the - string */ - if(lpCommandLine > stringstart && '\\' == lpCommandLine[-1]) - { - lpCommandLine++; - continue; - } - - /* found beginning of dquoted sequence, run to the end */ - /* don't stop if we hit an escaped dquote */ - lpCommandLine++; - while( *lpCommandLine ) - { - lpCommandLine = PAL_wcschr(lpCommandLine, '"'); - if(NULL == lpCommandLine) - { - /* no ending dquote, arg runs to end of string */ - break; - } - if('\\' != lpCommandLine[-1]) - { - /* dquote is not escaped, dquoted sequence is over*/ - break; - } - lpCommandLine++; - } - if(NULL == lpCommandLine || '\0' == *lpCommandLine) - { - /* no terminating dquote */ - break; - } - - /* step over dquote, keep looking for end of arg */ - lpCommandLine++; - } - else - { - /* found whitespace : end of arg. */ - lpCommandLine++; - break; - } - }while(lpCommandLine); - } - - /* Convert to ASCII */ - if (lpCommandLine) - { - if (!WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, - pChar, iWlen+1, NULL, NULL)) - { - ASSERT("Unable to convert to a multibyte string\n"); - free(lpAsciiCmdLine); - return NULL; - } - } - - pChar = lpAsciiCmdLine; - - /* loops through all the arguments, to find out how many arguments there - are; while looping replace whitespace by \0 */ - - /* skip leading whitespace (and replace by '\0') */ - /* note : there shouldn't be any, command starts either with PE loader name - or computed application path, but this won't hurt */ - while (*pChar) - { - if (!isspace((unsigned char) *pChar)) - { - break; - } - WARN("unexpected whitespace in command line!\n"); - *pChar++ = '\0'; - } - - while (*pChar) - { - (*pnArg)++; - - /* find end of current arg */ - while(*pChar && !isspace((unsigned char) *pChar)) - { - if('"' == *pChar) - { - /* skip over dquote if it's escaped; make sure we don't try to - look before the start of the string for the \ */ - if(pChar > lpAsciiCmdLine && '\\' == pChar[-1]) - { - pChar++; - continue; - } - - /* found leading dquote : look for ending dquote */ - pChar++; - while (*pChar) - { - pChar = strchr(pChar,'"'); - if(NULL == pChar) - { - /* no ending dquote found : argument extends to the end - of the string*/ - break; - } - if('\\' != pChar[-1]) - { - /* found a dquote, and it's not escaped : quoted - sequence is over*/ - break; - } - /* found a dquote, but it was escaped : skip over it, keep - looking */ - pChar++; - } - if(NULL == pChar || '\0' == *pChar) - { - /* reached the end of the string : we're done */ - break; - } - } - pChar++; - } - if(NULL == pChar) - { - /* reached the end of the string : we're done */ - break; - } - /* reached end of arg; replace trailing whitespace by '\0', to split - arguments into separate strings */ - while (isspace((unsigned char) *pChar)) - { - *pChar++ = '\0'; - } - } - - /* allocate lppargv according to the number of arguments - in the command line */ - lppArgv = (char **) malloc((((*pnArg)+1) * sizeof(char *))); - - if (lppArgv == NULL) - { - free(lpAsciiCmdLine); - return NULL; - } - - lppTemp = lppArgv; - - /* at this point all parameters are separated by NULL - we need to fill the array of arguments; we must also remove all dquotes - from arguments (new process shouldn't see them) */ - for (i = *pnArg, pChar = lpAsciiCmdLine; i; i--) - { - /* skip NULLs */ - while (!*pChar) - { - pChar++; - } - - *lppTemp = pChar; - - /* go to the next parameter, removing dquotes as we go along */ - j = 0; - while (*pChar) - { - /* copy character if it's not a dquote */ - if('"' != *pChar) - { - /* if it's the \ of an escaped dquote, skip over it, we'll - copy the " instead */ - if( '\\' == pChar[0] && '"' == pChar[1] ) - { - pChar++; - } - (*lppTemp)[j++] = *pChar; - } - pChar++; - } - /* re-NULL terminate the argument */ - (*lppTemp)[j] = '\0'; - - lppTemp++; - } - - *lppTemp = NULL; - - return lppArgv; -} - - -/*++ -Function: - getPath - -Abstract: - Helper function for CreateProcessW, it looks in the path environment - variable to find where the process to executed is. - -Parameters: - IN lpFileName: file name to search in the path - OUT lpPathFileName: returned string containing the path and the filename - -Return: - TRUE if found - FALSE otherwise ---*/ -static -BOOL -getPath( - PathCharString& lpFileNameString, - PathCharString& lpPathFileName) -{ - LPSTR lpPath; - LPSTR lpNext; - LPSTR lpCurrent; - LPWSTR lpwstr; - INT n; - INT nextLen; - INT slashLen; - CPalThread *pThread = NULL; - LPCSTR lpFileName = lpFileNameString.GetString(); - - /* if a path is specified, only look there */ - if(strchr(lpFileName, '/')) - { - if (access (lpFileName, F_OK) == 0) - { - if (!lpPathFileName.Set(lpFileNameString)) - { - TRACE("Set of StackString failed!\n"); - return FALSE; - } - - TRACE("file %s exists\n", lpFileName); - return TRUE; - } - else - { - TRACE("file %s doesn't exist.\n", lpFileName); - return FALSE; - } - } - - /* first look in directory from which the application loaded */ - lpwstr = g_lpwstrAppDir; - - if (lpwstr) - { - /* convert path to multibyte, check buffer size */ - n = WideCharToMultiByte(CP_ACP, 0, lpwstr, -1, NULL, 0, - NULL, NULL); - - if (!lpPathFileName.Reserve(n + lpFileNameString.GetCount() + 1 )) - { - ERROR("StackString Reserve failed!\n"); - return FALSE; - } - - lpPath = lpPathFileName.OpenStringBuffer(n); - - n = WideCharToMultiByte(CP_ACP, 0, lpwstr, -1, lpPath, n, - NULL, NULL); - - if (n == 0) - { - lpPathFileName.CloseBuffer(0); - ASSERT("WideCharToMultiByte failure!\n"); - return FALSE; - } - - lpPathFileName.CloseBuffer(n - 1); - - lpPathFileName.Append("/", 1); - lpPathFileName.Append(lpFileNameString); - - if (access(lpPathFileName, F_OK) == 0) - { - TRACE("found %s in application directory (%s)\n", lpFileName, lpPathFileName.GetString()); - return TRUE; - } - } - - /* then try the current directory */ - if (!lpPathFileName.Reserve(lpFileNameString.GetCount() + 2)) - { - ERROR("StackString Reserve failed!\n"); - return FALSE; - } - - lpPathFileName.Set("./", 2); - lpPathFileName.Append(lpFileNameString); - - if (access (lpPathFileName, R_OK) == 0) - { - TRACE("found %s in current directory.\n", lpFileName); - return TRUE; - } - - pThread = InternalGetCurrentThread(); - - /* Then try to look in the path */ - lpPath = EnvironGetenv("PATH"); - - if (!lpPath) - { - ERROR("EnvironGetenv returned NULL for $PATH\n"); - return FALSE; - } - - lpNext = lpPath; - - /* search in every path directory */ - TRACE("looking for file %s in $PATH (%s)\n", lpFileName, lpPath); - while (lpNext) - { - /* skip all leading ':' */ - while(*lpNext==':') - { - lpNext++; - } - - /* search for ':' */ - lpCurrent = strchr(lpNext, ':'); - if (lpCurrent) - { - *lpCurrent++ = '\0'; - } - - nextLen = strlen(lpNext); - slashLen = (lpNext[nextLen-1] == '/') ? 0:1; - - if (!lpPathFileName.Reserve(nextLen + lpFileNameString.GetCount() + 1)) - { - free(lpPath); - ERROR("StackString ran out of memory for full path\n"); - return FALSE; - } - - lpPathFileName.Set(lpNext, nextLen); - - if( slashLen == 1) - { - /* append a '/' if there's no '/' at the end of the path */ - lpPathFileName.Append("/", 1); - } - - lpPathFileName.Append(lpFileNameString); - - if ( access (lpPathFileName, F_OK) == 0) - { - TRACE("Found %s in $PATH element %s\n", lpFileName, lpNext); - free(lpPath); - return TRUE; - } - - lpNext = lpCurrent; /* search in the next directory */ - } - - free(lpPath); - TRACE("File %s not found in $PATH\n", lpFileName); - return FALSE; -} diff --git a/src/coreclr/pal/tests/palsuite/CMakeLists.txt b/src/coreclr/pal/tests/palsuite/CMakeLists.txt index 1bb9d98676da2e..aa3adc896eb339 100644 --- a/src/coreclr/pal/tests/palsuite/CMakeLists.txt +++ b/src/coreclr/pal/tests/palsuite/CMakeLists.txt @@ -363,8 +363,6 @@ add_executable_clr(paltests samples/test2/test.cpp threading/CreateProcessW/test1/childProcess.cpp threading/CreateProcessW/test1/parentProcess.cpp - threading/CreateProcessW/test2/childprocess.cpp - threading/CreateProcessW/test2/parentprocess.cpp threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.cpp threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.cpp threading/CreateSemaphoreW_ReleaseSemaphore/test3/createsemaphore.cpp diff --git a/src/coreclr/pal/tests/palsuite/compilableTests.txt b/src/coreclr/pal/tests/palsuite/compilableTests.txt index 2b781f28d32adc..de078cf2a276be 100644 --- a/src/coreclr/pal/tests/palsuite/compilableTests.txt +++ b/src/coreclr/pal/tests/palsuite/compilableTests.txt @@ -270,7 +270,6 @@ pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test2_neg/paltest_reg_unreg samples/test1/paltest_samples_test1 samples/test2/paltest_samples_test2 threading/CreateProcessW/test1/paltest_createprocessw_test1 -threading/CreateProcessW/test2/paltest_createprocessw_test2 threading/CreateSemaphoreW_ReleaseSemaphore/test1/paltest_createsemaphorew_releasesemaphore_test1 threading/CreateSemaphoreW_ReleaseSemaphore/test2/paltest_createsemaphorew_releasesemaphore_test2 threading/CreateSemaphoreW_ReleaseSemaphore/test3/paltest_createsemaphorew_releasesemaphore_test3 diff --git a/src/coreclr/pal/tests/palsuite/paltestlist.txt b/src/coreclr/pal/tests/palsuite/paltestlist.txt index 111ef1d8fee380..388fede3a397b8 100644 --- a/src/coreclr/pal/tests/palsuite/paltestlist.txt +++ b/src/coreclr/pal/tests/palsuite/paltestlist.txt @@ -240,7 +240,6 @@ pal_specific/PAL_Initialize_Terminate/test1/paltest_pal_initialize_terminate_tes pal_specific/PAL_Initialize_Terminate/test2/paltest_pal_initialize_terminate_test2 samples/test1/paltest_samples_test1 threading/CreateProcessW/test1/paltest_createprocessw_test1 -threading/CreateProcessW/test2/paltest_createprocessw_test2 threading/CreateSemaphoreW_ReleaseSemaphore/test1/paltest_createsemaphorew_releasesemaphore_test1 threading/CreateSemaphoreW_ReleaseSemaphore/test2/paltest_createsemaphorew_releasesemaphore_test2 threading/CreateThread/test1/paltest_createthread_test1 diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/childprocess.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/childprocess.cpp deleted file mode 100644 index 72ce2a4e67e1bf..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/childprocess.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: createprocessw/test2/childprocess.c -** -** Purpose: This child process reads a string from stdin -** and writes it out to stdout & stderr -** -** Dependencies: memset -** fgets -** gputs -** - -** -**=========================================================*/ - -#define UNICODE -#include -#include "test2.h" - - -PALTEST(threading_CreateProcessW_test2_paltest_createprocessw_test2_child, "threading/CreateProcessW/test2/paltest_createprocessw_test2_child") -{ - int iRetCode = EXIT_OK_CODE; /* preset exit code to OK */ - char szBuf[BUF_LEN]; - - WCHAR *swzParam1, *swzParam2, *swzParam3 = NULL; - - - if(0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - if (argc != 4) - { - return EXIT_ERR_CODE3; - } - - swzParam1 = convert(argv[1]); - swzParam2 = convert(argv[2]); - swzParam3 = convert(argv[3]); - - if (wcscmp(swzParam1, szArg1) != 0 - || wcscmp(swzParam2, szArg2) != 0 - || wcscmp(swzParam3, szArg3) != 0) - { - return EXIT_ERR_CODE4; - } - - free(swzParam1); - free(swzParam2); - free(swzParam3); - - memset(szBuf, 0, BUF_LEN); - - /* Read the string that was written by the parent */ - if (fgets(szBuf, BUF_LEN, stdin) == NULL) - { - return EXIT_ERR_CODE1; - } - - - /* Write the string out to the stdout & stderr pipes */ - if (fputs(szBuf, stdout) == EOF - || fputs(szBuf, stderr) == EOF) - { - return EXIT_ERR_CODE2; - } - - /* The exit code will indicate success or failure */ - PAL_TerminateEx(iRetCode); - - /* Return special exit code to indicate success or failure */ - return iRetCode; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.cpp deleted file mode 100644 index 7c580a9ea9bd91..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/parentprocess.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: createprocessw/test2/parentprocess.c -** -** Purpose: Test the following features of CreateProcessW: -** - Check to see if hProcess & hThread are set in -** return PROCESS_INFORMATION structure -** - Check to see if stdin, stdout, & stderr handles -** are used when STARTF_USESTDHANDLES is specified -** in STARUPINFO flags and bInheritHandles = TRUE -** - Check to see that proper arguments are passed to -** child process -** -** Dependencies: CreatePipe -** strcpy, strlen, strncmp, memset -** WaitForSingleObject -** WriteFile, ReadFile -** GetExitCodeProcess -** - -** -**=========================================================*/ - -#define UNICODE -#include -#include "test2.h" - - - -PALTEST(threading_CreateProcessW_test2_paltest_createprocessw_test2, "threading/CreateProcessW/test2/paltest_createprocessw_test2") -{ - - /******************************************* - * Declarations - *******************************************/ - STARTUPINFO si; - PROCESS_INFORMATION pi; - - HANDLE hTestStdInR = NULL; - HANDLE hTestStdInW = NULL; - HANDLE hTestStdOutR = NULL; - HANDLE hTestStdOutW = NULL; - HANDLE hTestStdErrR = NULL; - HANDLE hTestStdErrW = NULL; - - BOOL bRetVal = FALSE; - DWORD dwBytesWritten = 0; - DWORD dwBytesRead = 0; - DWORD dwExitCode = 0; - - SECURITY_ATTRIBUTES pipeAttributes; - - char szStdOutBuf[BUF_LEN]; - char szStdErrBuf[BUF_LEN]; - WCHAR szFullPathNameW[MAX_PATH]; - - - /******************************************* - * Initialization - *******************************************/ - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - /*Setup SECURITY_ATTRIBUTES structure for CreatePipe*/ - pipeAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); - pipeAttributes.lpSecurityDescriptor = NULL; - pipeAttributes.bInheritHandle = TRUE; - - - /*Create a StdIn pipe for child*/ - bRetVal = CreatePipe(&hTestStdInR, /* read handle*/ - &hTestStdInW, /* write handle */ - &pipeAttributes, /* security attributes*/ - 1024); /* pipe size*/ - - if (bRetVal == FALSE) - { - Fail("ERROR: %ld :Unable to create stdin pipe\n", GetLastError()); - } - - - /*Create a StdOut pipe for child*/ - bRetVal = CreatePipe(&hTestStdOutR, /* read handle*/ - &hTestStdOutW, /* write handle */ - &pipeAttributes, /* security attributes*/ - 0); /* pipe size*/ - - if (bRetVal == FALSE) - { - Fail("ERROR: %ld :Unable to create stdout pipe\n", GetLastError()); - } - - - /*Create a StdErr pipe for child*/ - bRetVal = CreatePipe(&hTestStdErrR, /* read handle*/ - &hTestStdErrW, /* write handle */ - &pipeAttributes, /* security attributes*/ - 0); /* pipe size*/ - - if (bRetVal == FALSE) - { - Fail("ERROR: %ld :Unable to create stderr pipe\n", GetLastError()); - } - - /* Zero the data structure space */ - ZeroMemory ( &pi, sizeof(pi) ); - ZeroMemory ( &si, sizeof(si) ); - - /* Set the process flags and standard io handles */ - si.cb = sizeof(si); - si.dwFlags = STARTF_USESTDHANDLES; - si.hStdInput = hTestStdInR; - si.hStdOutput = hTestStdOutW; - si.hStdError = hTestStdErrW; - - int mbwcResult = MultiByteToWideChar(CP_ACP, 0, argv[0], -1, szFullPathNameW, sizeof(szFullPathNameW)); - - if (0 == mbwcResult) - { - Fail ("Palsuite Code: MultiByteToWideChar() call failed. Exiting.\n"); - } - - wcscat(szFullPathNameW, u" "); - wcscat(szFullPathNameW, szChildFileW); - - wcscat(szFullPathNameW, szArgs); - - /******************************************* - * Start Testing - *******************************************/ - - /* Launch the child */ - if ( !CreateProcess (NULL, szFullPathNameW, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi )) - { - Fail("ERROR: CreateProcess call failed. GetLastError returned %d\n", - GetLastError() ); - } - - /* Check the returned process information for validity */ - if (pi.hProcess == 0 || pi.hThread == 0) - { - Fail("ERROR: CreateProcess Error: Process Handle = %u, Thread Handle = %u\n", - pi.hProcess, pi.hThread); - } - - - /* Write the Constructed string to stdin pipe for the child process */ - if (WriteFile(hTestStdInW, szTestString, strlen(szTestString), &dwBytesWritten, NULL) == FALSE - || WriteFile(hTestStdInW, "\n", strlen("\n"), &dwBytesWritten, NULL) == FALSE) - { - Fail("ERROR: %ld :unable to write to write pipe handle " - "hTestStdInW=0x%lx\n", GetLastError(), hTestStdInW); - } - - /* Wait for the child to finish, Max 20 seconds */ - dwExitCode = WaitForSingleObject(pi.hProcess, 20000); - - /* If the child failed then whole thing fails */ - if (dwExitCode != WAIT_OBJECT_0) - { - TerminateProcess(pi.hProcess, 0); - Fail("ERROR: The child failed to run properly.\n"); - } - - /* Check for problems in the child process */ - if (GetExitCodeProcess(pi.hProcess, &dwExitCode) == FALSE) - { - Fail("ERROR: Call to GetExitCodeProcess failed.\n"); - } - else if (dwExitCode == EXIT_ERR_CODE1) - { - Fail("ERROR: The Child process could not reead the string " - "written to the stdin pipe.\n"); - } - else if (dwExitCode == EXIT_ERR_CODE2) - { - Fail("ERROR: The Child process could not write the string " - "the stdout pipe or stderr pipe.\n"); - } - else if (dwExitCode == EXIT_ERR_CODE3) - { - Fail("ERROR: The Child received the wrong number of " - "command line arguments.\n"); - } - else if (dwExitCode == EXIT_ERR_CODE4) - { - Fail("ERROR: The Child received the wrong " - "command line arguments.\n"); - } - else if (dwExitCode != EXIT_OK_CODE) - { - Fail("ERROR: Unexpected exit code returned: %u. Child process " - "did not complete its part of the test.\n", dwExitCode); - } - - - /* The child ran ok, so check to see if we received the proper */ - /* strings through the pipes. */ - - /* clear our buffers */ - memset(szStdOutBuf, 0, BUF_LEN); - memset(szStdErrBuf, 0, BUF_LEN); - - /* Read the data back from the child process stdout */ - bRetVal = ReadFile(hTestStdOutR, /* handle to read pipe*/ - szStdOutBuf, /* buffer to write to*/ - BUF_LEN, /* number of bytes to read*/ - &dwBytesRead, /* number of bytes read*/ - NULL); /* overlapped buffer*/ - - /*Read the data back from the child process stderr */ - bRetVal = ReadFile(hTestStdErrR, /* handle to read pipe*/ - szStdErrBuf, /* buffer to write to*/ - BUF_LEN, /* number of bytes to read*/ - &dwBytesRead, /* number of bytes read*/ - NULL); /* overlapped buffer*/ - - - /* Confirm that we received the same string that we originally */ - /* wrote to the child and was received on both stdout & stderr.*/ - if (strncmp(szTestString, szStdOutBuf, strlen(szTestString)) != 0 - || strncmp(szTestString, szStdErrBuf, strlen(szTestString)) != 0) - { - Fail("ERROR: The data read back from child does not match " - "what was written. STDOUT: %s STDERR: %s\n", - szStdOutBuf, szStdErrBuf); - } - - - /******************************************* - * Clean Up - *******************************************/ - - /* Close process and thread handle */ - CloseHandle ( pi.hProcess ); - CloseHandle ( pi.hThread ); - - CloseHandle(hTestStdInR); - CloseHandle(hTestStdInW); - CloseHandle(hTestStdOutR); - CloseHandle(hTestStdOutW); - CloseHandle(hTestStdErrR); - CloseHandle(hTestStdErrW); - - PAL_Terminate(); - return ( PASS ); -} diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/test2.h b/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/test2.h deleted file mode 100644 index 3036e7dae9414c..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test2/test2.h +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: test2.h -** -** - -** -**=========================================================*/ - - -const WCHAR szChildFileW[] = u"threading/CreateProcessW/test2/paltest_createprocessw_test2_child"; -const WCHAR szArgs[] = {' ',0x41,' ','B',' ','C','\0'}; -const WCHAR szArg1[] = {0x41,'\0'}; -const WCHAR szArg2[] = {'B','\0'}; -const WCHAR szArg3[] = {'C','\0'}; - -#define szTestString "An uninteresting test string (it works though)" - -const DWORD EXIT_OK_CODE = 100; -const DWORD EXIT_ERR_CODE1 = 101; -const DWORD EXIT_ERR_CODE2 = 102; -const DWORD EXIT_ERR_CODE3 = 103; -const DWORD EXIT_ERR_CODE4 = 104; -const DWORD EXIT_ERR_CODE5 = 105; - -#define BUF_LEN 128 - diff --git a/src/coreclr/vm/autotrace.cpp b/src/coreclr/vm/autotrace.cpp index c41d55537f5f70..4c900377ff35a8 100644 --- a/src/coreclr/vm/autotrace.cpp +++ b/src/coreclr/vm/autotrace.cpp @@ -24,6 +24,7 @@ #ifdef TARGET_UNIX #include "pal.h" #endif // TARGET_UNIX +#include HANDLE auto_trace_event; static size_t g_n_tracers = 1; @@ -76,29 +77,7 @@ void auto_trace_init() void auto_trace_launch_internal() { - DWORD currentProcessId = GetCurrentProcessId(); - STARTUPINFO si; - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(STARTUPINFO); -#ifndef TARGET_UNIX - si.dwFlags = STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; -#endif - - PROCESS_INFORMATION result; - - BOOL code = CreateProcessW( - /* lpApplicationName = */ nullptr, - /* lpCommandLine = */ command, - /* lpCommandLine = */ nullptr, - /* lpThreadAttributes = */ nullptr, - /* bInheritHandles = */ false, - /* dwCreationFlags = */ CREATE_NEW_CONSOLE, - /* lpEnvironment = */ nullptr, - /* lpCurrentDirectory = */ nullptr, - /* lpStartupInfo = */ &si, - /* lpProcessInformation = */ &result - ); + minipal_create_process_w((const CHAR16_T*)command, nullptr, false, nullptr); } void auto_trace_launch() diff --git a/src/native/minipal/CMakeLists.txt b/src/native/minipal/CMakeLists.txt index d7f9ad5e2ab78a..1769423042e9ed 100644 --- a/src/native/minipal/CMakeLists.txt +++ b/src/native/minipal/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCES memorybarrierprocesswide.c mutex.c guid.c + process.c random.c debugger.c strings.c diff --git a/src/native/minipal/process.c b/src/native/minipal/process.c new file mode 100644 index 00000000000000..d0be13b74705de --- /dev/null +++ b/src/native/minipal/process.c @@ -0,0 +1,376 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +#include +#include +#include +#include + +#ifdef _WIN32 + +#include + +bool minipal_create_process( + const char* command_line, + const char* working_dir, + bool inherit_handles, + minipal_process_info* out_info) +{ + if (command_line == NULL) + return false; + + // Convert command_line to UTF-16. + size_t cmd_len = strlen(command_line); + size_t cmd_wide_len = minipal_get_length_utf8_to_utf16(command_line, cmd_len, 0); + if (cmd_wide_len == 0) + return false; + + // +1 for null terminator + WCHAR* cmd_wide = (WCHAR*)malloc((cmd_wide_len + 1) * sizeof(WCHAR)); + if (cmd_wide == NULL) + return false; + + minipal_convert_utf8_to_utf16(command_line, cmd_len, (CHAR16_T*)cmd_wide, cmd_wide_len + 1, 0); + cmd_wide[cmd_wide_len] = L'\0'; + + // Convert working_dir if provided. + WCHAR* dir_wide = NULL; + if (working_dir != NULL) + { + size_t dir_len = strlen(working_dir); + size_t dir_wide_len = minipal_get_length_utf8_to_utf16(working_dir, dir_len, 0); + if (dir_wide_len == 0) + { + free(cmd_wide); + return false; + } + dir_wide = (WCHAR*)malloc((dir_wide_len + 1) * sizeof(WCHAR)); + if (dir_wide == NULL) + { + free(cmd_wide); + return false; + } + minipal_convert_utf8_to_utf16(working_dir, dir_len, (CHAR16_T*)dir_wide, dir_wide_len + 1, 0); + dir_wide[dir_wide_len] = L'\0'; + } + + STARTUPINFOW si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + + PROCESS_INFORMATION pi; + memset(&pi, 0, sizeof(pi)); + + BOOL result = CreateProcessW( + NULL, + cmd_wide, + NULL, + NULL, + inherit_handles ? TRUE : FALSE, + 0, + NULL, + dir_wide, + &si, + &pi); + + free(cmd_wide); + free(dir_wide); + + if (!result) + return false; + + if (out_info != NULL) + { + out_info->process_handle = (intptr_t)pi.hProcess; + out_info->thread_handle = (intptr_t)pi.hThread; + out_info->process_id = (int32_t)pi.dwProcessId; + } + else + { + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + + return true; +} + +void minipal_close_process_handle(intptr_t handle) +{ + if (handle != 0) + CloseHandle((HANDLE)handle); +} + +bool minipal_create_process_w( + const CHAR16_T* command_line, + const CHAR16_T* working_dir, + bool inherit_handles, + minipal_process_info* out_info) +{ + if (command_line == NULL) + return false; + + // On Windows, CHAR16_T is wchar_t so we can pass directly to CreateProcessW. + // CreateProcessW may modify the command line buffer, so make a mutable copy. + size_t cmd_len = wcslen((const WCHAR*)command_line); + WCHAR* cmd_copy = (WCHAR*)malloc((cmd_len + 1) * sizeof(WCHAR)); + if (cmd_copy == NULL) + return false; + memcpy(cmd_copy, command_line, (cmd_len + 1) * sizeof(WCHAR)); + + STARTUPINFOW si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + + PROCESS_INFORMATION pi; + memset(&pi, 0, sizeof(pi)); + + BOOL result = CreateProcessW( + NULL, + cmd_copy, + NULL, + NULL, + inherit_handles ? TRUE : FALSE, + 0, + NULL, + (LPCWSTR)working_dir, + &si, + &pi); + + free(cmd_copy); + + if (!result) + return false; + + if (out_info != NULL) + { + out_info->process_handle = (intptr_t)pi.hProcess; + out_info->thread_handle = (intptr_t)pi.hThread; + out_info->process_id = (int32_t)pi.dwProcessId; + } + else + { + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + + return true; +} + +#else // !_WIN32 + +#include +#include +#include +#include +#include +#include + +// Command line splitting matching Win32 CreateProcessW semantics: +// 1) Whitespace splits arguments (space, tab) +// 2) Double quotes group text (whitespace inside quotes doesn't split) +// 3) \" is an escaped double quote (produces literal " in output) +// 4) Backslash followed by anything other than " is literal (kept as-is) +// 5) Bare double quotes are stripped from output +// Returns heap-allocated argv array (caller frees each element and the array). +static char** split_command_line(const char* cmd, int* out_argc) +{ + int capacity = 8; + int count = 0; + char** argv = (char**)malloc(capacity * sizeof(char*)); + if (argv == NULL) + return NULL; + + const char* p = cmd; + while (*p != '\0') + { + // Skip whitespace + while (*p == ' ' || *p == '\t') + p++; + if (*p == '\0') + break; + + // Find the end of this argument (first pass: determine boundaries) + const char* arg_start = p; + bool in_quotes = false; + while (*p != '\0') + { + if (!in_quotes && (*p == ' ' || *p == '\t')) + break; + + if (*p == '"') + { + // Check for escaped quote: \" + if (p > arg_start && *(p - 1) == '\\') + { + // This is an escaped quote, not a real quote toggle + p++; + continue; + } + in_quotes = !in_quotes; + p++; + } + else + { + p++; + } + } + + // Second pass: copy the argument, stripping bare quotes and handling \" + size_t arg_len = (size_t)(p - arg_start); + char* buf = (char*)malloc(arg_len + 1); + if (buf == NULL) + { + for (int i = 0; i < count; i++) free(argv[i]); + free(argv); + return NULL; + } + + size_t j = 0; + const char* s = arg_start; + while (s < p) + { + if (*s == '"') + { + // Skip bare double quotes (they're grouping characters) + s++; + } + else if (*s == '\\' && (s + 1) < p && *(s + 1) == '"') + { + // Escaped double quote: \\" -> produce literal " + buf[j++] = '"'; + s += 2; + } + else + { + buf[j++] = *s++; + } + } + buf[j] = '\0'; + + if (count + 2 > capacity) + { + capacity *= 2; + argv = (char**)realloc(argv, capacity * sizeof(char*)); + } + argv[count++] = buf; + } + + argv[count] = NULL; + *out_argc = count; + return argv; +} + +bool minipal_create_process( + const char* command_line, + const char* working_dir, + bool inherit_handles, + minipal_process_info* out_info) +{ + if (command_line == NULL) + return false; + + int argc = 0; + char** argv = split_command_line(command_line, &argc); + if (argv == NULL || argc == 0) + { + free(argv); + return false; + } + + pid_t pid = fork(); + if (pid < 0) + { + for (int i = 0; i < argc; i++) free(argv[i]); + free(argv); + return false; + } + + if (pid == 0) + { + // Child process + if (working_dir != NULL) + { + if (chdir(working_dir) != 0) + _exit(127); + } + + if (!inherit_handles) + { + // Close file descriptors > 2 + int max_fd = (int)sysconf(_SC_OPEN_MAX); + if (max_fd < 0) max_fd = 1024; + for (int fd = 3; fd < max_fd; fd++) + close(fd); + } + + execvp(argv[0], argv); + _exit(127); // exec failed + } + + // Parent + for (int i = 0; i < argc; i++) free(argv[i]); + free(argv); + + if (out_info != NULL) + { + out_info->process_handle = (intptr_t)pid; + out_info->thread_handle = 0; + out_info->process_id = (int32_t)pid; + } + + return true; +} + +void minipal_close_process_handle(intptr_t handle) +{ + // On Unix, PIDs don't need to be "closed". No-op. + (void)handle; +} + +bool minipal_create_process_w( + const CHAR16_T* command_line, + const CHAR16_T* working_dir, + bool inherit_handles, + minipal_process_info* out_info) +{ + if (command_line == NULL) + return false; + + // Convert command_line from UTF-16 to UTF-8. + size_t cmd_u16_len = minipal_u16_strlen(command_line); + size_t cmd_u8_len = minipal_get_length_utf16_to_utf8(command_line, cmd_u16_len, 0); + if (cmd_u8_len == 0) + return false; + + char* cmd_utf8 = (char*)malloc(cmd_u8_len + 1); + if (cmd_utf8 == NULL) + return false; + minipal_convert_utf16_to_utf8(command_line, cmd_u16_len, cmd_utf8, cmd_u8_len + 1, 0); + cmd_utf8[cmd_u8_len] = '\0'; + + // Convert working_dir if provided. + char* dir_utf8 = NULL; + if (working_dir != NULL) + { + size_t dir_u16_len = minipal_u16_strlen(working_dir); + size_t dir_u8_len = minipal_get_length_utf16_to_utf8(working_dir, dir_u16_len, 0); + if (dir_u8_len > 0) + { + dir_utf8 = (char*)malloc(dir_u8_len + 1); + if (dir_utf8 != NULL) + { + minipal_convert_utf16_to_utf8(working_dir, dir_u16_len, dir_utf8, dir_u8_len + 1, 0); + dir_utf8[dir_u8_len] = '\0'; + } + } + } + + bool ok = minipal_create_process(cmd_utf8, dir_utf8, inherit_handles, out_info); + free(cmd_utf8); + free(dir_utf8); + return ok; +} + +#endif // !_WIN32 diff --git a/src/native/minipal/process.h b/src/native/minipal/process.h new file mode 100644 index 00000000000000..9d0eba27067581 --- /dev/null +++ b/src/native/minipal/process.h @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef HAVE_MINIPAL_PROCESS_H +#define HAVE_MINIPAL_PROCESS_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + intptr_t process_handle; // Caller must close via minipal_close_process_handle. + intptr_t thread_handle; // Caller must close via minipal_close_process_handle. Always 0 on Unix. + int32_t process_id; +} minipal_process_info; + +/** + * Launch a child process from a UTF-8 command line. + * + * @param command_line The command line to execute (UTF-8, null-terminated). + * @param working_dir Working directory for the child process, or NULL to inherit. + * @param inherit_handles Whether the child inherits parent's handles. + * @param out_info Optional. Receives process handle/PID on success. Caller must + * close process_handle and thread_handle when done. + * @return true on success, false on failure. + */ +bool minipal_create_process( + const char* command_line, + const char* working_dir, + bool inherit_handles, + minipal_process_info* out_info); + +/** + * Launch a child process from a UTF-16 command line. + * Handles the UTF-16 to UTF-8 conversion internally on Unix. + * On Windows, passes through to CreateProcessW directly. + * + * @param command_line The command line to execute (UTF-16/CHAR16_T, null-terminated). + * @param working_dir Working directory for the child process, or NULL to inherit. + * @param inherit_handles Whether the child inherits parent's handles. + * @param out_info Optional. Receives process handle/PID on success. Caller must + * close process_handle and thread_handle when done. + * @return true on success, false on failure. + */ +bool minipal_create_process_w( + const CHAR16_T* command_line, + const CHAR16_T* working_dir, + bool inherit_handles, + minipal_process_info* out_info); + +/** + * Close a process or thread handle returned by minipal_create_process. + * + * @param handle The handle to close. 0 is a no-op. + */ +void minipal_close_process_handle(intptr_t handle); + +#ifdef __cplusplus +} +#endif + +#endif // HAVE_MINIPAL_PROCESS_H From 897bf4182dcb82d9c7a721d1b9498bc053f2b323 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:54:06 +0300 Subject: [PATCH 02/11] Update process.c --- src/native/minipal/process.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/native/minipal/process.c b/src/native/minipal/process.c index d0be13b74705de..08e7baa4c6aeeb 100644 --- a/src/native/minipal/process.c +++ b/src/native/minipal/process.c @@ -8,7 +8,7 @@ #include #include -#ifdef _WIN32 +#ifdef TARGET_WINDOWS #include @@ -158,7 +158,35 @@ bool minipal_create_process_w( return true; } -#else // !_WIN32 +#elif defined(TARGET_WASM) + +// wasm does not support process creation. +bool minipal_create_process( + const char* command_line, + const char* working_dir, + bool inherit_handles, + minipal_process_info* out_info) +{ + (void)command_line; (void)working_dir; (void)inherit_handles; (void)out_info; + return false; +} + +void minipal_close_process_handle(intptr_t handle) +{ + (void)handle; +} + +bool minipal_create_process_w( + const CHAR16_T* command_line, + const CHAR16_T* working_dir, + bool inherit_handles, + minipal_process_info* out_info) +{ + (void)command_line; (void)working_dir; (void)inherit_handles; (void)out_info; + return false; +} + +#else #include #include @@ -373,4 +401,4 @@ bool minipal_create_process_w( return ok; } -#endif // !_WIN32 +#endif From 8b1809987ad8d493b015eee17a770d45918c8eac Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Thu, 30 Apr 2026 17:38:21 +0300 Subject: [PATCH 03/11] Address review: keep process creation out of minipal --- .../dlls/mscordac/mscordac_unixexports.src | 1 - src/coreclr/pal/inc/pal.h | 17 - src/coreclr/pal/src/thread/process.cpp | 181 ------- src/coreclr/pal/tests/palsuite/CMakeLists.txt | 16 - .../pal/tests/palsuite/compilableTests.txt | 8 - .../OutputDebugStringA/test1/helper.cpp | 34 -- .../OutputDebugStringA/test1/test1.cpp | 95 ---- .../pal/tests/palsuite/paltestlist.txt | 1 - .../CreateProcessW/test1/childProcess.cpp | 109 ---- .../CreateProcessW/test1/parentProcess.cpp | 168 ------ .../ExitThread/test2/childprocess.cpp | 40 -- .../threading/ExitThread/test2/myexitcode.h | 13 - .../threading/ExitThread/test2/test2.cpp | 133 ----- .../GetExitCodeProcess/test1/childProcess.cpp | 31 -- .../GetExitCodeProcess/test1/myexitcode.h | 13 - .../GetExitCodeProcess/test1/test1.cpp | 128 ----- .../OpenEventW/test3/childprocess.cpp | 80 --- .../threading/OpenEventW/test3/test3.cpp | 191 ------- .../OpenProcess/test1/childProcess.cpp | 44 -- .../threading/OpenProcess/test1/myexitcode.h | 13 - .../threading/OpenProcess/test1/test1.cpp | 198 ------- .../test5/commonconsts.h | 41 -- .../WaitForMultipleObjectsEx/test5/helper.cpp | 121 ----- .../WaitForMultipleObjectsEx/test5/test5.cpp | 505 ------------------ .../WFSOProcessTest/ChildProcess.cpp | 49 -- .../WFSOProcessTest/WFSOProcessTest.cpp | 115 ---- src/coreclr/utilcode/winfix.cpp | 93 ++++ src/coreclr/vm/autotrace.cpp | 39 +- src/native/minipal/CMakeLists.txt | 1 - src/native/minipal/process.c | 404 -------------- src/native/minipal/process.h | 67 --- 31 files changed, 130 insertions(+), 2819 deletions(-) delete mode 100644 src/coreclr/pal/tests/palsuite/debug_api/OutputDebugStringA/test1/helper.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/debug_api/OutputDebugStringA/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test1/childProcess.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test1/parentProcess.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/childprocess.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/myexitcode.h delete mode 100644 src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/test2.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/childProcess.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/myexitcode.h delete mode 100644 src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/OpenEventW/test3/childprocess.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/OpenEventW/test3/test3.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/myexitcode.h delete mode 100644 src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/commonconsts.h delete mode 100644 src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/helper.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.cpp delete mode 100644 src/native/minipal/process.c delete mode 100644 src/native/minipal/process.h diff --git a/src/coreclr/dlls/mscordac/mscordac_unixexports.src b/src/coreclr/dlls/mscordac/mscordac_unixexports.src index 849f9e945fcf17..ebbe8c166a92af 100644 --- a/src/coreclr/dlls/mscordac/mscordac_unixexports.src +++ b/src/coreclr/dlls/mscordac/mscordac_unixexports.src @@ -65,7 +65,6 @@ nativeStringResourceTable_mscorrc #CreateFileW #CreateEventW #CreateEventExW -#CreateProcessW #CreateSemaphoreExW #CreateThread #CloseHandle diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 9fb95fffdd5385..3f74cf3ec21b28 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -730,23 +730,6 @@ typedef struct _PROCESS_INFORMATION { DWORD dwThreadId_PAL_Undefined; } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION; -PALIMPORT -BOOL -PALAPI -CreateProcessW( - IN LPCWSTR lpApplicationName, - IN LPWSTR lpCommandLine, - IN LPSECURITY_ATTRIBUTES lpProcessAttributes, - IN LPSECURITY_ATTRIBUTES lpThreadAttributes, - IN BOOL bInheritHandles, - IN DWORD dwCreationFlags, - IN LPVOID lpEnvironment, - IN LPCWSTR lpCurrentDirectory, - IN LPSTARTUPINFOW lpStartupInfo, - OUT LPPROCESS_INFORMATION lpProcessInformation); - -#define CreateProcess CreateProcessW - PALIMPORT PAL_NORETURN VOID diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 4a9cdafee437b9..4586b7c8114691 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -32,8 +32,6 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d #include "pal/stackstring.hpp" #include "pal/signal.hpp" -#include - #include #include @@ -312,185 +310,6 @@ GetCurrentProcess( return hPseudoCurrentProcess; } -/*++ -Function: - CreateProcessW - -Note: - This is a thin wrapper that delegates to the minipal process API. - Security attributes and most creation flags are not supported. - -See MSDN doc. ---*/ -BOOL -PALAPI -CreateProcessW( - IN LPCWSTR lpApplicationName, - IN LPWSTR lpCommandLine, - IN LPSECURITY_ATTRIBUTES lpProcessAttributes, - IN LPSECURITY_ATTRIBUTES lpThreadAttributes, - IN BOOL bInheritHandles, - IN DWORD dwCreationFlags, - IN LPVOID lpEnvironment, - IN LPCWSTR lpCurrentDirectory, - IN LPSTARTUPINFOW lpStartupInfo, - OUT LPPROCESS_INFORMATION lpProcessInformation) -{ - PERF_ENTRY(CreateProcessW); - ENTRY("CreateProcessW(lpAppName=%p (%S), lpCmdLine=%p (%S), dwFlags=%#x)\n", - lpApplicationName?lpApplicationName:W16_NULLSTRING, - lpApplicationName?lpApplicationName:W16_NULLSTRING, - lpCommandLine?lpCommandLine:W16_NULLSTRING, - lpCommandLine?lpCommandLine:W16_NULLSTRING, - dwCreationFlags); - - PAL_ERROR palError = NO_ERROR; - CPalThread *pThread = InternalGetCurrentThread(); - IPalObject *pobjProcess = NULL; - IPalObject *pobjProcessRegistered = NULL; - IDataLock *pDataLock = NULL; - CProcProcessLocalData *pLocalData = NULL; - CObjectAttributes oa; - HANDLE hProcess = NULL; - HANDLE hDummyThread = NULL; - BOOL bRet = FALSE; - LPCWSTR lpCmd = lpCommandLine ? lpCommandLine : lpApplicationName; - - if (lpCmd == NULL) - { - ERROR("both lpApplicationName and lpCommandLine are NULL\n"); - palError = ERROR_INVALID_PARAMETER; - goto done; - } - - { - minipal_process_info info; - bool ok = minipal_create_process_w( - (const CHAR16_T*)lpCmd, - (const CHAR16_T*)lpCurrentDirectory, - bInheritHandles != FALSE, - &info); - - if (!ok) - { - ERROR("minipal_create_process_w failed\n"); - palError = ERROR_ACCESS_DENIED; - goto done; - } - - // Register the child process in the PAL object manager so that - // WaitForSingleObject, GetExitCodeProcess, etc. work correctly. - palError = g_pObjectManager->AllocateObject( - pThread, - &otProcess, - &oa, - &pobjProcess); - - if (NO_ERROR != palError) - { - ERROR("Unable to allocate object for new process\n"); - goto done; - } - - palError = g_pObjectManager->RegisterObject( - pThread, - pobjProcess, - &aotProcess, - &hProcess, - &pobjProcessRegistered); - - // pobjProcess is invalidated by the above call - pobjProcess = NULL; - - if (NO_ERROR != palError) - { - ERROR("Unable to register new process object\n"); - goto done; - } - - palError = pobjProcessRegistered->GetProcessLocalData( - pThread, - WriteLock, - &pDataLock, - reinterpret_cast(&pLocalData)); - - if (NO_ERROR != palError) - { - ERROR("Unable to get process local data\n"); - goto done; - } - - pLocalData->dwProcessId = (DWORD)info.process_id; - pDataLock->ReleaseLock(pThread, TRUE); - pDataLock = NULL; - - CPalThread *pDummyThread = NULL; - palError = InternalCreateDummyThread( - pThread, - lpThreadAttributes, - &pDummyThread, - &hDummyThread); - - if (NO_ERROR != palError) - { - ERROR("Unable to create dummy thread handle\n"); - goto done; - } - - if (lpProcessInformation != NULL) - { - lpProcessInformation->hProcess = hProcess; - lpProcessInformation->hThread = hDummyThread; - lpProcessInformation->dwProcessId = (DWORD)info.process_id; - lpProcessInformation->dwThreadId_PAL_Undefined = 0; - } - else - { - g_pObjectManager->RevokeHandle(pThread, hProcess); - g_pObjectManager->RevokeHandle(pThread, hDummyThread); - } - - // minipal handles are not needed; the PAL manages lifetime via its own handles. - minipal_close_process_handle(info.process_handle); - minipal_close_process_handle(info.thread_handle); - - bRet = TRUE; - } - -done: - - if (NO_ERROR != palError && palError != ERROR_INVALID_PARAMETER) - { - if (NULL != hDummyThread) - { - g_pObjectManager->RevokeHandle(pThread, hDummyThread); - } - if (NULL != hProcess) - { - g_pObjectManager->RevokeHandle(pThread, hProcess); - } - } - - if (NULL != pobjProcess) - { - pobjProcess->ReleaseReference(pThread); - } - - if (NULL != pobjProcessRegistered) - { - pobjProcessRegistered->ReleaseReference(pThread); - } - - if (NO_ERROR != palError) - { - pThread->SetLastError(palError); - } - - LOGEXIT("CreateProcessW returns BOOL %d\n", bRet); - PERF_EXIT(CreateProcessW); - return bRet; -} - /*++ Function: diff --git a/src/coreclr/pal/tests/palsuite/CMakeLists.txt b/src/coreclr/pal/tests/palsuite/CMakeLists.txt index aa3adc896eb339..c32f205182c5e0 100644 --- a/src/coreclr/pal/tests/palsuite/CMakeLists.txt +++ b/src/coreclr/pal/tests/palsuite/CMakeLists.txt @@ -140,8 +140,6 @@ add_executable_clr(paltests c_runtime/_wcsnicmp/test1/test1.cpp c_runtime/_wtoi/test1/test1.cpp #debug_api/DebugBreak/test1/test1.cpp - debug_api/OutputDebugStringA/test1/helper.cpp - debug_api/OutputDebugStringA/test1/test1.cpp debug_api/OutputDebugStringW/test1/test1.cpp #debug_api/WriteProcessMemory/test1/helper.cpp #debug_api/WriteProcessMemory/test1/test1.cpp @@ -361,8 +359,6 @@ add_executable_clr(paltests # pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test2_neg/reg_unreg_libraryw_neg.cpp samples/test1/test.cpp samples/test2/test.cpp - threading/CreateProcessW/test1/childProcess.cpp - threading/CreateProcessW/test1/parentProcess.cpp threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.cpp threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.cpp threading/CreateSemaphoreW_ReleaseSemaphore/test3/createsemaphore.cpp @@ -381,22 +377,14 @@ add_executable_clr(paltests threading/ExitProcess/test2/test2.cpp threading/ExitProcess/test3/test3.cpp threading/ExitThread/test1/test1.cpp - threading/ExitThread/test2/childprocess.cpp - threading/ExitThread/test2/test2.cpp threading/GetCurrentProcess/test1/process.cpp threading/GetCurrentProcessId/test1/processId.cpp threading/GetCurrentThread/test1/thread.cpp threading/GetCurrentThread/test2/test2.cpp threading/GetCurrentThreadId/test1/threadId.cpp - threading/GetExitCodeProcess/test1/childProcess.cpp - threading/GetExitCodeProcess/test1/test1.cpp threading/OpenEventW/test1/test1.cpp threading/OpenEventW/test2/test2.cpp - threading/OpenEventW/test3/childprocess.cpp - threading/OpenEventW/test3/test3.cpp threading/OpenEventW/test5/test5.cpp - threading/OpenProcess/test1/childProcess.cpp - threading/OpenProcess/test1/test1.cpp threading/QueryThreadCycleTime/test1/test1.cpp threading/releasesemaphore/test1/test.cpp threading/ResetEvent/test1/test1.cpp @@ -416,11 +404,7 @@ add_executable_clr(paltests threading/ThreadPriority/test1/ThreadPriority.cpp threading/WaitForMultipleObjects/test1/test1.cpp threading/WaitForMultipleObjectsEx/test1/test1.cpp - threading/WaitForMultipleObjectsEx/test5/helper.cpp - threading/WaitForMultipleObjectsEx/test5/test5.cpp threading/WaitForSingleObject/test1/test1.cpp - threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.cpp - threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.cpp threading/WaitForSingleObject/WFSOSemaphoreTest/WFSOSemaphoreTest.cpp threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.cpp threading/YieldProcessor/test1/test1.cpp diff --git a/src/coreclr/pal/tests/palsuite/compilableTests.txt b/src/coreclr/pal/tests/palsuite/compilableTests.txt index de078cf2a276be..5553e413c6cccb 100644 --- a/src/coreclr/pal/tests/palsuite/compilableTests.txt +++ b/src/coreclr/pal/tests/palsuite/compilableTests.txt @@ -92,7 +92,6 @@ c_runtime/_wcsicmp/test1/paltest_wcsicmp_test1 c_runtime/_wcslwr_s/test1/paltest_wcslwr_s_test1 c_runtime/_wcsnicmp/test1/paltest_wcsnicmp_test1 c_runtime/_wtoi/test1/paltest_wtoi_test1 -debug_api/OutputDebugStringA/test1/paltest_outputdebugstringa_test1 debug_api/OutputDebugStringW/test1/paltest_outputdebugstringw_test1 exception_handling/RaiseException/test1/paltest_raiseexception_test1 exception_handling/RaiseException/test2/paltest_raiseexception_test2 @@ -269,7 +268,6 @@ pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test1/paltest_pal_registerl pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test2_neg/paltest_reg_unreg_libraryw_neg samples/test1/paltest_samples_test1 samples/test2/paltest_samples_test2 -threading/CreateProcessW/test1/paltest_createprocessw_test1 threading/CreateSemaphoreW_ReleaseSemaphore/test1/paltest_createsemaphorew_releasesemaphore_test1 threading/CreateSemaphoreW_ReleaseSemaphore/test2/paltest_createsemaphorew_releasesemaphore_test2 threading/CreateSemaphoreW_ReleaseSemaphore/test3/paltest_createsemaphorew_releasesemaphore_test3 @@ -288,18 +286,14 @@ threading/ExitProcess/test1/paltest_exitprocess_test1 threading/ExitProcess/test2/paltest_exitprocess_test2 threading/ExitProcess/test3/paltest_exitprocess_test3 threading/ExitThread/test1/paltest_exitthread_test1 -threading/ExitThread/test2/paltest_exitthread_test2 threading/GetCurrentProcess/test1/paltest_getcurrentprocess_test1 threading/GetCurrentProcessId/test1/paltest_getcurrentprocessid_test1 threading/GetCurrentThread/test1/paltest_getcurrentthread_test1 threading/GetCurrentThread/test2/paltest_getcurrentthread_test2 threading/GetCurrentThreadId/test1/paltest_getcurrentthreadid_test1 -threading/GetExitCodeProcess/test1/paltest_getexitcodeprocess_test1 threading/OpenEventW/test1/paltest_openeventw_test1 threading/OpenEventW/test2/paltest_openeventw_test2 -threading/OpenEventW/test3/paltest_openeventw_test3 threading/OpenEventW/test5/paltest_openeventw_test5 -threading/OpenProcess/test1/paltest_openprocess_test1 threading/QueryThreadCycleTime/test1/paltest_querythreadcycletime_test1 threading/releasesemaphore/test1/paltest_releasesemaphore_test1 threading/ResetEvent/test1/paltest_resetevent_test1 @@ -319,9 +313,7 @@ threading/TerminateProcess/test1/paltest_terminateprocess_test1 threading/ThreadPriority/test1/paltest_threadpriority_test1 threading/WaitForMultipleObjects/test1/paltest_waitformultipleobjects_test1 threading/WaitForMultipleObjectsEx/test1/paltest_waitformultipleobjectsex_test1 -threading/WaitForMultipleObjectsEx/test5/paltest_waitformultipleobjectsex_test5 threading/WaitForSingleObject/test1/paltest_waitforsingleobject_test1 -threading/WaitForSingleObject/WFSOProcessTest/paltest_waitforsingleobject_wfsoprocesstest threading/WaitForSingleObject/WFSOSemaphoreTest/paltest_waitforsingleobject_wfsosemaphoretest threading/WaitForSingleObject/WFSOThreadTest/paltest_waitforsingleobject_wfsothreadtest threading/YieldProcessor/test1/paltest_yieldprocessor_test1 diff --git a/src/coreclr/pal/tests/palsuite/debug_api/OutputDebugStringA/test1/helper.cpp b/src/coreclr/pal/tests/palsuite/debug_api/OutputDebugStringA/test1/helper.cpp deleted file mode 100644 index e47029fa7865c4..00000000000000 --- a/src/coreclr/pal/tests/palsuite/debug_api/OutputDebugStringA/test1/helper.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================= -** -** Source: helper.c -** -** Purpose: Intended to be the child process of a debugger. Calls -** OutputDebugStringA once with a normal string, once with an empty -** string -** -** -**============================================================*/ - -#include - -PALTEST(debug_api_OutputDebugStringA_test1_paltest_outputdebugstringa_test1_helper, "debug_api/OutputDebugStringA/test1/paltest_outputdebugstringa_test1_helper") -{ - if(0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - OutputDebugStringA("Foo!\n"); - - OutputDebugStringA(""); - - /* give a chance to the debugger process to read the debug string before - exiting */ - Sleep(1000); - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/debug_api/OutputDebugStringA/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/debug_api/OutputDebugStringA/test1/test1.cpp deleted file mode 100644 index a8f55d7f9c04c6..00000000000000 --- a/src/coreclr/pal/tests/palsuite/debug_api/OutputDebugStringA/test1/test1.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================= -** -** Source: test1.c -** -** Purpose: Debugs the helper application. Checks that certain events, in -** particular the OUTPUT_DEBUG_STRING_EVENT, is generated correctly -** and gives the correct values. -** -** -**============================================================*/ - -#include - -const int DELAY_MS = 2000; - -struct OutputCheck -{ - DWORD ExpectedEventCode; - DWORD ExpectedUnicode; - char *ExpectedStr; -}; - -PALTEST(debug_api_OutputDebugStringA_test1_paltest_outputdebugstringa_test1, "debug_api/OutputDebugStringA/test1/paltest_outputdebugstringa_test1") -{ - - PROCESS_INFORMATION pi; - STARTUPINFO si; - - if(0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof(si); - ZeroMemory( &pi, sizeof(pi) ); - - WCHAR name[] = {'h','e','l', 'p', 'e', 'r', '\0'}; - /* Create a new process. This is the process to be Debugged */ - if(!CreateProcessW( NULL, name, NULL, NULL, - FALSE, 0, NULL, NULL, &si, &pi)) - { - DWORD dwError = GetLastError(); - Fail("ERROR: CreateProcess failed to load executable 'helper'. " - "GetLastError() returned %d.\n", dwError); - } - - /* This is the main loop. It exits when the process which is being - debugged is finished executing. - */ - - while(1) - { - DWORD dwRet = 0; - dwRet = WaitForSingleObject(pi.hProcess, - DELAY_MS /* Wait for 2 seconds max*/ - ); - - if (dwRet != WAIT_OBJECT_0) - { - Trace("WaitForSingleObjectTest:WaitForSingleObject " - "failed (%x) after waiting %d seconds for the helper\n", - GetLastError(), DELAY_MS / 1000); - } - else - { - DWORD dwExitCode; - - /* check the exit code from the process */ - if( ! GetExitCodeProcess( pi.hProcess, &dwExitCode ) ) - { - DWORD dwError; - - dwError = GetLastError(); - CloseHandle ( pi.hProcess ); - CloseHandle ( pi.hThread ); - Fail( "GetExitCodeProcess call failed with error code %d\n", - dwError ); - } - - if(dwExitCode != STILL_ACTIVE) { - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - break; - } - Trace("still executing %d..\n", dwExitCode); - } - } - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/paltestlist.txt b/src/coreclr/pal/tests/palsuite/paltestlist.txt index 388fede3a397b8..650e28f0e2d00f 100644 --- a/src/coreclr/pal/tests/palsuite/paltestlist.txt +++ b/src/coreclr/pal/tests/palsuite/paltestlist.txt @@ -239,7 +239,6 @@ miscellaneous/SetLastError/test1/paltest_setlasterror_test1 pal_specific/PAL_Initialize_Terminate/test1/paltest_pal_initialize_terminate_test1 pal_specific/PAL_Initialize_Terminate/test2/paltest_pal_initialize_terminate_test2 samples/test1/paltest_samples_test1 -threading/CreateProcessW/test1/paltest_createprocessw_test1 threading/CreateSemaphoreW_ReleaseSemaphore/test1/paltest_createsemaphorew_releasesemaphore_test1 threading/CreateSemaphoreW_ReleaseSemaphore/test2/paltest_createsemaphorew_releasesemaphore_test2 threading/CreateThread/test1/paltest_createthread_test1 diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test1/childProcess.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test1/childProcess.cpp deleted file mode 100644 index dd0e35f5311f35..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test1/childProcess.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: CreateProcessW/test1/childprocess.c -** -** Purpose: Test to ensure CreateProcessW starts a new process. This test -** launches a child process, and examines a file written by the child. -** This code is the child code. -** -** Dependencies: GetTempPath -** MultiByteToWideChar -** wcslen -** strlen -** WideCharToMultiByte -** fopen -** fclose -** fprintf -** - -** -**=========================================================*/ - -#define UNICODE -#include - -const WCHAR szCommonFileW[] = - {'c','h','i','l','d','d','a','t','a','.','t','m','p','\0'}; - - -#define szCommonStringA "058d2d057111a313aa82401c2e856002\0" - -PALTEST(threading_CreateProcessW_test1_paltest_createprocessw_test1_child, "threading/CreateProcessW/test1/paltest_createprocessw_test1_child") -{ - - static FILE * fp; - - DWORD dwFileLength; - DWORD dwDirLength; - DWORD dwSize; - - char *szAbsPathNameA; - WCHAR szDirNameW[_MAX_DIR]; - WCHAR szAbsPathNameW[MAX_PATH]; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - dwDirLength = GetTempPath(MAX_PATH, szDirNameW); - - if (0 == dwDirLength) - { - Fail ("GetTempPath call failed. Could not get " - "temp directory\n. Exiting.\n"); - } - - dwFileLength = wcslen( szCommonFileW ); - - dwSize = mkAbsoluteFilenameW( szDirNameW, dwDirLength, szCommonFileW, - dwFileLength, szAbsPathNameW ); - - if (0 == dwSize) - { - Fail ("Palsuite Code: mkAbsoluteFilename() call failed. Could " - "not build absolute path name to file\n. Exiting.\n"); - } - - /* set the string length for the open call */ - szAbsPathNameA = (char*)malloc(dwSize +1); - - if (NULL == szAbsPathNameA) - { - Fail ("Unable to malloc (%d) bytes. Exiting\n", (dwSize +1) ); - } - - WideCharToMultiByte (CP_ACP, 0, szAbsPathNameW, -1, szAbsPathNameA, - (dwSize + 1), NULL, NULL); - - if ( NULL == ( fp = fopen ( szAbsPathNameA , "w+" ) ) ) - { - /* - * A return value of NULL indicates an error condition or an - * EOF condition - */ - Fail ("%s unable to open %s for writing. Exiting.\n", argv[0] - , szAbsPathNameA ); - } - - free (szAbsPathNameA); - - if ( 0 >= ( fprintf ( fp, "%s", szCommonStringA ))) - { - Fail("%s unable to write to %s. Exiting.\n", argv[0] - , szAbsPathNameA ); - } - - if (0 != (fclose ( fp ))) - { - Fail ("%s unable to close file %s. Pid may not be " - "written to file. Exiting.\n", argv[0], szAbsPathNameA ); - } - - PAL_Terminate(); - return ( PASS ); - -} diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test1/parentProcess.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test1/parentProcess.cpp deleted file mode 100644 index 00e6f2c30147ea..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/CreateProcessW/test1/parentProcess.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: CreateProcessW/test1/parentprocess.c -** -** Purpose: Test to ensure CreateProcessW starts a new process. This test -** launches a child process, and examines a file written by the child. -** This process (the parent process) reads the file created by the child and -** compares the value the child wrote to the file. (a const char *) -** -** Dependencies: GetTempPath -** MultiByteToWideChar -** wcslen -** strlen -** WideCharToMultiByte -** WaitForSingleObject -** fopen -** fclose -** Fail -** - -** -**=========================================================*/ - -#define UNICODE -#include - -const WCHAR szCommonFileW[] = - {'c','h','i','l','d','d','a','t','a','.','t','m','p','\0'}; - -const WCHAR szChildFileW[] = u"threading/CreateProcessW/test1/paltest_createprocessw_test1_child"; - -#define szCommonStringA "058d2d057111a313aa82401c2e856002\0" - -PALTEST(threading_CreateProcessW_test1_paltest_createprocessw_test1, "threading/CreateProcessW/test1/paltest_createprocessw_test1") - -{ - - STARTUPINFOW si; - PROCESS_INFORMATION pi; - - static FILE * fp; - - DWORD dwFileLength; - DWORD dwDirLength; - DWORD dwSize; - - size_t cslen; - - char szReadStringA[256]; - - char szAbsPathNameA[MAX_PATH]; - WCHAR szDirNameW[_MAX_DIR]; - WCHAR absPathBuf[MAX_PATH]; - WCHAR *szAbsPathNameW; - - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - ZeroMemory ( &si, sizeof(si) ); - si.cb = sizeof(si); - ZeroMemory ( &pi, sizeof(pi) ); - - szAbsPathNameW=&absPathBuf[0]; - - dwDirLength = GetTempPath(MAX_PATH, szDirNameW); - - if (0 == dwDirLength) - { - Fail ("GetTempPath call failed. Could not get " - "temp directory\n. Exiting.\n"); - } - - int mbwcResult = MultiByteToWideChar(CP_ACP, 0, argv[0], -1, szAbsPathNameW, sizeof(absPathBuf)); - - if (0 == mbwcResult) - { - Fail ("Palsuite Code: MultiByteToWideChar() call failed. Exiting.\n"); - } - - wcscat(szAbsPathNameW, u" "); - wcscat(szAbsPathNameW, szChildFileW); - - if ( !CreateProcessW ( NULL, - szAbsPathNameW, - NULL, - NULL, - FALSE, - CREATE_NEW_CONSOLE, - NULL, - NULL, - &si, - &pi ) - ) - { - Fail ( "CreateProcess call failed. GetLastError returned %d\n", - GetLastError() ); - } - - WaitForSingleObject ( pi.hProcess, INFINITE ); - - szAbsPathNameW=&absPathBuf[0]; - - dwFileLength = wcslen( szCommonFileW ); - - dwSize = mkAbsoluteFilenameW( szDirNameW, dwDirLength, szCommonFileW, - dwFileLength, szAbsPathNameW ); - - /* set the string length for the open call*/ - - if (0 == dwSize) - { - Fail ("Palsuite Code: mkAbsoluteFilename() call failed. Could " - "not build absolute path name to file\n. Exiting.\n"); - } - - WideCharToMultiByte (CP_ACP, 0, szAbsPathNameW, -1, szAbsPathNameA, - (dwSize + 1), NULL, NULL); - - if ( NULL == ( fp = fopen ( szAbsPathNameA , "r" ) ) ) - { - Fail ("%s\nunable to open %s\nfor reading. Exiting.\n", argv[0], - szAbsPathNameA ); - } - - cslen = strlen ( szCommonStringA ); - - if ( NULL == fgets( szReadStringA, (cslen + 1), fp )) - { - /* - * A return value of NULL indicates an error condition or an - * EOF condition - */ - Fail ("%s\nunable to read file\n%s\nszReadStringA is %s\n" - "Exiting.\n", argv[0], szAbsPathNameA, - szReadStringA ); - } - - if ( 0 != strncmp( szReadStringA, szCommonStringA, cslen )) - { - Fail ("string comparison failed.\n szReadStringA is %s and\n" - "szCommonStringA is %s\n", szReadStringA, - szCommonStringA ); - } - else - { - Trace ("string comparison passed.\n"); - } - - if (0 != (fclose ( fp ))) - { - Trace ("%s unable to close file %s. This may cause a file pointer " - "leak. Continuing.\n", argv[0], szAbsPathNameA ); - } - - /* Close process and thread handle */ - CloseHandle ( pi.hProcess ); - CloseHandle ( pi.hThread ); - - PAL_Terminate(); - return ( PASS ); - -} diff --git a/src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/childprocess.cpp b/src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/childprocess.cpp deleted file mode 100644 index 2cd743fbd37721..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/childprocess.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: childprocess.c -** -** Purpose: Test to ensure ExitThread returns the right -** value when shutting down the last thread of a process. -** All this program does is call ExitThread() with a predefined -** value. -** -** Dependencies: none -** - -** -**=========================================================*/ - -#include -#include "myexitcode.h" - -PALTEST(threading_ExitThread_test2_paltest_exitthread_test2_child, "threading/ExitThread/test2/paltest_exitthread_test2_child") -{ - /* initialize the PAL */ - if( PAL_Initialize(argc, argv) != 0 ) - { - return( FAIL ); - } - - /* exit the current thread with a magic test value -- it should */ - /* terminate the process and return that test value from this */ - /* program. */ - ExitThread( TEST_EXIT_CODE ); - - /* technically we should never get here */ - PAL_Terminate(); - - /* return failure */ - return FAIL; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/myexitcode.h b/src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/myexitcode.h deleted file mode 100644 index f753316e23bebb..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/myexitcode.h +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================ -** -** Source: myexitcode.h -** -** Purpose: Define an exit code. -** -** -**==========================================================================*/ - -#define TEST_EXIT_CODE 316 diff --git a/src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/test2.cpp deleted file mode 100644 index c14bd6ae731379..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/ExitThread/test2/test2.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================= -** -** Source: test2.c -** -** Purpose: Test to ensure ExitThread() called from the last thread of -** a process shuts down that process and returns the proper -** exit code as specified in the ExitThread() call. -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** Fail -** ZeroMemory -** GetCurrentDirectoryW -** CreateProcessW -** WaitForSingleObject -** GetLastError -** strlen -** strncpy -** - -** -**===========================================================================*/ -#include -#include "myexitcode.h" - -PALTEST(threading_ExitThread_test2_paltest_exitthread_test2, "threading/ExitThread/test2/paltest_exitthread_test2") -{ - const char* rgchChildFile = "childprocess"; - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - DWORD dwError; - DWORD dwExitCode; - DWORD dwFileLength; - DWORD dwDirLength; - DWORD dwSize; - DWORD dwExpected = TEST_EXIT_CODE; - - char rgchDirName[_MAX_DIR]; - char absPathBuf[MAX_PATH]; - char* rgchAbsPathName; - - /* initialize the PAL */ - if( PAL_Initialize(argc, argv) != 0 ) - { - return( FAIL ); - } - - /* zero our process and startup info structures */ - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof( si ); - ZeroMemory( &pi, sizeof(pi) ); - - /* build the absolute path to the child process */ - rgchAbsPathName = &absPathBuf[0]; - dwFileLength = strlen( rgchChildFile ); - - strcpy(rgchDirName, ".\\"); - dwDirLength = strlen(rgchDirName); - - dwSize = mkAbsoluteFilename( rgchDirName, - dwDirLength, - rgchChildFile, - dwFileLength, - rgchAbsPathName ); - if( dwSize == 0 ) - { - Fail( "Palsuite Code: mkAbsoluteFilename() call failed. Could ", - "not build absolute path name to file\n. Exiting.\n" ); - } - - LPWSTR rgchAbsPathNameW = convert(rgchAbsPathName); - /* launch the child process */ - if( !CreateProcess( NULL, /* module name to execute */ - rgchAbsPathNameW, /* command line */ - NULL, /* process handle not */ - /* inheritable */ - NULL, /* thread handle not */ - /* inheritable */ - FALSE, /* handle inheritance */ - CREATE_NEW_CONSOLE, /* dwCreationFlags */ - NULL, /* use parent's environment */ - NULL, /* use parent's starting */ - /* directory */ - &si, /* startup info struct */ - &pi ) /* process info struct */ - ) - { - dwError = GetLastError(); - free(rgchAbsPathNameW); - Fail( "CreateProcess call failed with error code %d\n", - dwError ); - } - - free(rgchAbsPathNameW); - - /* wait for the child process to complete */ - WaitForSingleObject ( pi.hProcess, INFINITE ); - - /* check the exit code from the process */ - if( ! GetExitCodeProcess( pi.hProcess, &dwExitCode ) ) - { - dwError = GetLastError(); - CloseHandle ( pi.hProcess ); - CloseHandle ( pi.hThread ); - Fail( "GetExitCodeProcess call failed with error code %d\n", - dwError ); - } - - /* close process and thread handle */ - CloseHandle ( pi.hProcess ); - CloseHandle ( pi.hThread ); - - /* check for the expected exit code */ - /* exit code for some systems is as small as a char, so that's all */ - /* we'll compare for checking success */ - if( LOBYTE(LOWORD(dwExitCode)) != LOBYTE(LOWORD(dwExpected)) ) - { - Fail( "GetExitCodeProcess returned an incorrect exit code %d, " - "expected value is %d\n", - LOWORD(dwExitCode), dwExpected ); - } - - /* terminate the PAL */ - PAL_Terminate(); - - /* return success */ - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/childProcess.cpp b/src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/childProcess.cpp deleted file mode 100644 index 4848bb499804a0..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/childProcess.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: childprocess.c -** -** Purpose: Test to ensure GetExitCodeProcess returns the right -** value. All this program does is return a predefined value. -** -** Dependencies: none -** - -** -**=========================================================*/ - -#include -#include "myexitcode.h" -#include - -PALTEST(threading_GetExitCodeProcess_test1_paltest_getexitcodeprocess_test1_child, "threading/GetExitCodeProcess/test1/paltest_getexitcodeprocess_test1_child") -{ - int i; - - // simulate some activity - for( i=0; i<10000; i++ ) - ; - - // return the predefined exit code - return TEST_EXIT_CODE; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/myexitcode.h b/src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/myexitcode.h deleted file mode 100644 index ddf0fb2a050b40..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/myexitcode.h +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================ -** -** Source: myexitcode.h -** -** Purpose: Define an exit code. -** -** -**==========================================================================*/ - -#define TEST_EXIT_CODE 104 diff --git a/src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/test1.cpp deleted file mode 100644 index 7fa6ddc993a08d..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/GetExitCodeProcess/test1/test1.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================= -** -** Source: test1.c -** -** Purpose: Test to ensure GetExitCodeProcess works properly. -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** Fail -** ZeroMemory -** GetCurrentDirectoryW -** CreateProcessW -** WaitForSingleObject -** GetLastError -** strlen -** strncpy -** - -** -**===========================================================================*/ -#include -#include "myexitcode.h" - -PALTEST(threading_GetExitCodeProcess_test1_paltest_getexitcodeprocess_test1, "threading/GetExitCodeProcess/test1/paltest_getexitcodeprocess_test1") -{ - const char* rgchChildFile = "childprocess"; - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - DWORD dwError; - DWORD dwExitCode; - DWORD dwFileLength; - DWORD dwDirLength; - DWORD dwSize; - - char rgchDirName[_MAX_DIR]; - char absPathBuf[MAX_PATH]; - char* rgchAbsPathName; - - /* initialize the PAL */ - if( PAL_Initialize(argc, argv) != 0 ) - { - return( FAIL ); - } - - /* zero our process and startup info structures */ - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof( si ); - ZeroMemory( &pi, sizeof(pi) ); - - /* build the absolute path to the child process */ - rgchAbsPathName = &absPathBuf[0]; - dwFileLength = strlen( rgchChildFile ); - - strcpy(rgchDirName, ".\\"); - dwDirLength = strlen(rgchDirName); - - dwSize = mkAbsoluteFilename( rgchDirName, - dwDirLength, - rgchChildFile, - dwFileLength, - rgchAbsPathName ); - if( dwSize == 0 ) - { - Fail( "Palsuite Code: mkAbsoluteFilename() call failed. Could ", - "not build absolute path name to file\n. Exiting.\n" ); - } - - LPWSTR rgchAbsPathNameW = convert(rgchAbsPathName); - /* launch the child process */ - if( !CreateProcess( NULL, /* module name to execute */ - rgchAbsPathNameW, /* command line */ - NULL, /* process handle not */ - /* inheritable */ - NULL, /* thread handle not */ - /* inheritable */ - FALSE, /* handle inheritance */ - CREATE_NEW_CONSOLE, /* dwCreationFlags */ - NULL, /* use parent's environment */ - NULL, /* use parent's starting */ - /* directory */ - &si, /* startup info struct */ - &pi ) /* process info struct */ - ) - { - dwError = GetLastError(); - free(rgchAbsPathNameW); - Fail( "CreateProcess call failed with error code %d\n", - dwError ); - } - - free(rgchAbsPathNameW); - - /* wait for the child process to complete */ - WaitForSingleObject ( pi.hProcess, INFINITE ); - - /* check the exit code from the process */ - if( ! GetExitCodeProcess( pi.hProcess, &dwExitCode ) ) - { - dwError = GetLastError(); - CloseHandle ( pi.hProcess ); - CloseHandle ( pi.hThread ); - Fail( "GetExitCodeProcess call failed with error code %d\n", - dwError ); - } - - /* close process and thread handle */ - CloseHandle ( pi.hProcess ); - CloseHandle ( pi.hThread ); - - /* check for the expected exit code */ - if( dwExitCode != TEST_EXIT_CODE ) - { - Fail( "GetExitCodeProcess returned an incorrect exit code %d, " - "expected value is %d\n", - dwExitCode, TEST_EXIT_CODE ); - } - - /* terminate the PAL */ - PAL_Terminate(); - - /* return success */ - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/OpenEventW/test3/childprocess.cpp b/src/coreclr/pal/tests/palsuite/threading/OpenEventW/test3/childprocess.cpp deleted file mode 100644 index a868b7da4d69da..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/OpenEventW/test3/childprocess.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: childprocess.c -** -** Purpose: Test to ensure that OpenEventW() works when -** opening an event created by another process. The test -** program launches this program as a child, which creates -** a named, initially-unset event. The child waits up to -** 10 seconds for the parent process to open that event -** and set it, and returns PASS if the event was set or FAIL -** otherwise. The parent process checks the return value -** from the child to verify that the opened event was -** properly used across processes. -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** CreateEventW -** WaitForSingleObject -** CloseHandle -** -** -**=========================================================*/ - -#include - -PALTEST(threading_OpenEventW_test3_paltest_openeventw_test3_child, "threading/OpenEventW/test3/paltest_openeventw_test3_child") -{ - /* local variables */ - HANDLE hEvent = NULL; - WCHAR wcName[] = {'P','A','L','R','o','c','k','s','\0'}; - LPWSTR lpName = wcName; - - int result = PASS; - - /* initialize the PAL */ - if( PAL_Initialize(argc, argv) != 0 ) - { - return( FAIL ); - } - - - /* open a handle to the event created in the child process */ - hEvent = OpenEventW( EVENT_ALL_ACCESS, /* we want all rights */ - FALSE, /* no inherit */ - lpName ); - - if( hEvent == NULL ) - { - /* ERROR */ - Trace( "ERROR:%lu:OpenEventW() call failed\n", GetLastError() ); - result = FAIL; - goto parentwait; - } - - /* set the event -- should take effect in the child process */ - if( ! SetEvent( hEvent ) ) - { - /* ERROR */ - Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() ); - result = FAIL; - } - -parentwait: - /* close the event handle */ - if( ! CloseHandle( hEvent ) ) - { - /* ERROR */ - Fail( "ERROR:%lu:CloseHandle() call failed in child\n", - GetLastError()); - } - - /* terminate the PAL */ - PAL_TerminateEx(result); - - /* return success or failure */ - return result; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/OpenEventW/test3/test3.cpp b/src/coreclr/pal/tests/palsuite/threading/OpenEventW/test3/test3.cpp deleted file mode 100644 index 58e21465f9a25f..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/OpenEventW/test3/test3.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================= -** -** Source: test3.c -** -** Purpose: Test to ensure that OpenEventW() works when -** opening an event created by another process. This test -** program launches a child process which creates a -** named, initially-unset event. The child waits up to -** 10 seconds for the parent process to open that event -** and set it, and returns PASS if the event was set or FAIL -** otherwise. The parent process checks the return value -** from the child to verify that the opened event was -** properly used across processes. -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** Fail -** ZeroMemory -** GetCurrentDirectoryW -** CreateProcessW -** WaitForSingleObject -** GetExitCodeProcess -** GetLastError -** strlen -** strncpy -** -** -**===========================================================================*/ -#include - -#define TIMEOUT 60000 - -PALTEST(threading_OpenEventW_test3_paltest_openeventw_test3, "threading/OpenEventW/test3/paltest_openeventw_test3") -{ - BOOL ret = FAIL; - LPSECURITY_ATTRIBUTES lpEventAttributes = NULL; - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - DWORD dwExitCode; - - DWORD dwRet = 0; - HANDLE hEvent = NULL; - WCHAR wcName[] = {'P','A','L','R','o','c','k','s','\0'}; - LPWSTR lpName = wcName; - char lpCommandLine[MAX_PATH] = ""; - - /* initialize the PAL */ - if( PAL_Initialize(argc, argv) != 0 ) - { - return( FAIL ); - } - - /* zero our process and startup info structures */ - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof( si ); - ZeroMemory( &pi, sizeof(pi) ); - - /* create an event which we can use with SetEvent */ - hEvent = CreateEventW( lpEventAttributes, - TRUE, /* manual reset */ - FALSE, /* unsignalled */ - lpName ); - - if( hEvent == NULL ) - { - /* ERROR */ - Fail( "ERROR:%lu:CreateEventW() call failed in child\n", - GetLastError()); - } - - ZeroMemory( lpCommandLine, MAX_PATH ); - if ( sprintf_s( lpCommandLine, MAX_PATH-1, "childprocess ") < 0 ) - { - Fail ("Error: Insufficient lpCommandline for\n"); - } - - LPWSTR lpCommandLineW = convert(lpCommandLine); - /* launch the child process */ - if( !CreateProcess( NULL, /* module name to execute */ - lpCommandLineW, /* command line */ - NULL, /* process handle not */ - /* inheritable */ - NULL, /* thread handle not */ - /* inheritable */ - FALSE, /* handle inheritance */ - CREATE_NEW_CONSOLE, /* dwCreationFlags */ - NULL, /* use parent's environment */ - NULL, /* use parent's starting */ - /* directory */ - &si, /* startup info struct */ - &pi ) /* process info struct */ - ) - { - DWORD dwError = GetLastError(); - free(lpCommandLineW); - Fail( "ERROR:%lu:CreateProcess call failed\n", - dwError); - } - - free(lpCommandLineW); - - /* verify that the event is signalled by the child process */ - dwRet = WaitForSingleObject( hEvent, TIMEOUT ); - if( dwRet != WAIT_OBJECT_0 ) - { - ret = FAIL; - /* ERROR */ - Trace( "ERROR:WaitForSingleObject() call returned %lu, " - "expected WAIT_TIMEOUT\n", - "expected WAIT_OBJECT_0\n", - dwRet ); - - goto cleanup; - - if( !CloseHandle( hEvent ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed in child\n", - GetLastError()); - } - goto cleanup; - } - - /* wait for the child process to complete */ - dwRet = WaitForSingleObject ( pi.hProcess, TIMEOUT ); - if( dwRet != WAIT_OBJECT_0 ) - { - ret = FAIL; - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected %lu\n", - dwRet, - WAIT_OBJECT_0 ); - goto cleanup; - } - - /* check the exit code from the process */ - if( ! GetExitCodeProcess( pi.hProcess, &dwExitCode ) ) - { - ret = FAIL; - Trace( "ERROR:%lu:GetExitCodeProcess call failed\n", - GetLastError() ); - goto cleanup; - } - - /* check for success */ - ret = (dwExitCode == PASS) ? PASS : FAIL; - -cleanup: - if( hEvent != NULL ) - { - if( ! CloseHandle ( hEvent ) ) - { - Trace( "ERROR:%lu:CloseHandle call failed on event handle\n", - GetLastError() ); - ret = FAIL; - } - } - - - /* close process and thread handle */ - if( ! CloseHandle ( pi.hProcess ) ) - { - Trace( "ERROR:%lu:CloseHandle call failed on process handle\n", - GetLastError() ); - ret = FAIL; - } - - if( ! CloseHandle ( pi.hThread ) ) - { - Trace( "ERROR:%lu:CloseHandle call failed on thread handle\n", - GetLastError() ); - ret = FAIL; - } - - /* output a convenient error message and exit if we failed */ - if( ret == FAIL ) - { - Fail( "test failed\n" ); - } - - - /* terminate the PAL */ - PAL_Terminate(); - - /* return success */ - return ret; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.cpp b/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.cpp deleted file mode 100644 index 9c12e981ec3663..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: childprocess.c -** -** Purpose: Test to ensure OpenProcess works properly. -** All this program does is return a predefined value. -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** CreateMutexW -** WaitForSingleObject -** CloseHandle -** -** -**=========================================================*/ - -#include -#include "myexitcode.h" - - -PALTEST(threading_OpenProcess_test1_paltest_openprocess_test1_child, "threading/OpenProcess/test1/paltest_openprocess_test1_child") -{ - DWORD dwRet; - int i; - - /* initialize the PAL */ - if( PAL_Initialize(argc, argv) != 0 ) - { - return( FAIL ); - } - - /* simulate some activity */ - for( i=0; i<50000; i++ ) - ; - - /* terminate the PAL */ - PAL_Terminate(); - - /* return the predefined exit code */ - return TEST_EXIT_CODE; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/myexitcode.h b/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/myexitcode.h deleted file mode 100644 index 89d0be7cb32a8d..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/myexitcode.h +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================ -** -** Source: myexitcode.h -** -** Purpose: Define an exit code. -** -** -**==========================================================================*/ - -#define TEST_EXIT_CODE 317 diff --git a/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/test1.cpp deleted file mode 100644 index 5c5e2d3e5d19bf..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/test1.cpp +++ /dev/null @@ -1,198 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================= -** -** Source: test1.c -** -** Purpose: Test to ensure OpenProcess works properly. -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** Fail -** ZeroMemory -** GetCurrentDirectoryW -** CreateProcessW -** WaitForSingleObject -** CreateMutexW -** ReleaseMutex -** CloseHandle -** GetLastError -** strlen -** strncpy -** -** -**===========================================================================*/ -#include -#include "myexitcode.h" - -PALTEST(threading_OpenProcess_test1_paltest_openprocess_test1, "threading/OpenProcess/test1/paltest_openprocess_test1") -{ - const char* rgchChildFile = "childprocess"; - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - DWORD dwError; - DWORD dwExitCode; - DWORD dwFileLength; - DWORD dwDirLength; - DWORD dwSize; - DWORD dwRet; - - HANDLE hChildProcess; - - char rgchDirName[_MAX_DIR]; - char absPathBuf[MAX_PATH]; - char* rgchAbsPathName; - - BOOL ret = FAIL; - BOOL bChildDone = FALSE; - - /* initialize the PAL */ - if( PAL_Initialize(argc, argv) != 0 ) - { - return( FAIL ); - } - - /* zero our process and startup info structures */ - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof( si ); - ZeroMemory( &pi, sizeof(pi) ); - - /* build the absolute path to the child process */ - rgchAbsPathName = &absPathBuf[0]; - dwFileLength = strlen( rgchChildFile ); - - strcpy(rgchDirName, ".\\"); - dwDirLength = strlen(rgchDirName); - - dwSize = mkAbsoluteFilename( rgchDirName, - dwDirLength, - rgchChildFile, - dwFileLength, - rgchAbsPathName ); - if( dwSize == 0 ) - { - Fail( "Palsuite Code: mkAbsoluteFilename() call failed. Could ", - "not build absolute path name to file\n. Exiting.\n" ); - } - - LPWSTR rgchAbsPathNameW = convert(rgchAbsPathName); - /* launch the child process */ - if( !CreateProcess( NULL, /* module name to execute */ - rgchAbsPathNameW, /* command line */ - NULL, /* process handle not */ - /* inheritable */ - NULL, /* thread handle not */ - /*inheritable */ - FALSE, /* handle inheritance */ - CREATE_NEW_CONSOLE, /* dwCreationFlags */ - NULL, /* use parent's environment */ - NULL, /* use parent's starting */ - /* directory */ - &si, /* startup info struct */ - &pi ) /* process info struct */ - ) - { - dwError = GetLastError(); - free(rgchAbsPathNameW); - Fail( "CreateProcess call failed with error code %d\n", - dwError ); - } - - free(rgchAbsPathNameW); - - /* open another handle to the child process */ - hChildProcess = OpenProcess( PROCESS_ALL_ACCESS, /* access */ - FALSE, /* inheritable */ - pi.dwProcessId /* process id */ - ); - if( hChildProcess == NULL ) - { - dwError = GetLastError(); - Trace( "ERROR:%lu:OpenProcess call failed\n", dwError ); - goto cleanup2; - } - - /* wait for the child process to complete, using the new handle */ - dwRet = WaitForSingleObject( hChildProcess, 10000 ); - if( dwRet != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject call returned %lu, " - "expected WAIT_OBJECT_0", - dwRet ); - goto cleanup; - } - - /* remember that we waited until the child was finished */ - bChildDone = TRUE; - - /* check the exit code from the process -- this is a bit of an */ - /* extra verification that we opened the correct process handle */ - if( ! GetExitCodeProcess( hChildProcess, &dwExitCode ) ) - { - Trace( "ERROR:%lu:GetExitCodeProcess call failed\n", GetLastError() ); - goto cleanup; - } - - /* verification */ - if( (dwExitCode & 0xFF) != (TEST_EXIT_CODE & 0xFF) ) - { - Trace( "GetExitCodeProcess returned an incorrect exit code %d, " - "expected value is %d\n", - (dwExitCode & 0xFF), - (TEST_EXIT_CODE & 0xFF)); - goto cleanup; - } - - /* success if we get here */ - ret = PASS; - - -cleanup: - /* wait on the child process to complete if necessary */ - if( ! bChildDone ) - { - dwRet = WaitForSingleObject( hChildProcess, 10000 ); - if( dwRet != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject call returned %lu, " - "expected WAIT_OBJECT_0", - dwRet ); - ret = FAIL; - } - } - - /* close all our handles */ - if( CloseHandle ( hChildProcess ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - ret = FAIL; - } - -cleanup2: - if( CloseHandle ( pi.hProcess ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - ret = FAIL; - } - if( CloseHandle ( pi.hThread ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - ret = FAIL; - } - - if( ret == FAIL ) - { - Fail( "test failed\n" ); - } - - - - /* terminate the PAL */ - PAL_Terminate(); - - /* return success */ - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/commonconsts.h b/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/commonconsts.h deleted file mode 100644 index d4e26e4d478307..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/commonconsts.h +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================= -** -** Source: commonconsts.h -** -** -**============================================================*/ - -#ifndef _COMMONCONSTS_H_ -#define _COMMONCONSTS_H_ - -#include - -const int TIMEOUT = 60 * 5 * 1000; - -#define szcHelperProcessStartEvName "start" -#define szcHelperProcessReadyEvName "ready" -#define szcHelperProcessFinishEvName "finish" - -/* PEDANTIC and PEDANTIC0 is a helper macro that just grumps about any - * zero return codes in a generic way. with little typing */ -#define PEDANTIC(function, parameters) \ -{ \ - if (! (function parameters) ) \ - { \ - Trace("%s: NonFatal failure of %s%s for reasons %u and %u\n", \ - __FILE__, #function, #parameters, GetLastError(), errno); \ - } \ -} -#define PEDANTIC1(function, parameters) \ -{ \ - if ( (function parameters) ) \ - { \ - Trace("%s: NonFatal failure of %s%s for reasons %u and %u\n", \ - __FILE__, #function, #parameters, GetLastError(), errno); \ - } \ -} - -#endif diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/helper.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/helper.cpp deleted file mode 100644 index de39bad98a3c7b..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/helper.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================= -** -** Source: helper.c -** -** Purpose: This helper process sets up signals to communicate -** with the test thread in the parent process, and let the test -** thread signal this process when to exit. -** -** -**============================================================*/ - -#include "commonconsts.h" - -#include - -HANDLE hProcessStartEvent_WFMO_test5_helper; -HANDLE hProcessReadyEvent; -HANDLE hProcessFinishEvent; -HANDLE hProcessCleanupEvent; - - -PALTEST(threading_WaitForMultipleObjectsEx_test5_paltest_waitformultipleobjectsex_test5_helper, "threading/WaitForMultipleObjectsEx/test5/paltest_waitformultipleobjectsex_test5_helper") -{ - - BOOL success = TRUE; /* assume success */ - DWORD dwRet; - DWORD dwProcessId; - char szEventName[MAX_LONGPATH]; - PWCHAR uniString; - - if(0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - /* Open the event to let test thread tell us to get started. */ - uniString = convert(szcHelperProcessStartEvName); - hProcessStartEvent_WFMO_test5_helper = OpenEventW(EVENT_ALL_ACCESS, 0, uniString); - free(uniString); - if (!hProcessStartEvent_WFMO_test5_helper) - { - Fail("helper.main: OpenEvent of '%S' failed (%u). " - "(the event should already exist!)\n", - szcHelperProcessStartEvName, GetLastError()); - } - - /* Wait for signal from test thread. */ - dwRet = WaitForSingleObject(hProcessStartEvent_WFMO_test5_helper, TIMEOUT); - if (dwRet != WAIT_OBJECT_0) - { - Fail("helper.main: WaitForSingleObject '%s' failed\n" - "LastError:(%u)\n", szcHelperProcessStartEvName, GetLastError()); - } - - dwProcessId = GetCurrentProcessId(); - - if ( 0 >= dwProcessId ) - { - Fail ("helper.main: %s has invalid pid %d\n", argv[0], dwProcessId ); - } - - /* Open the event to tell test thread we are ready. */ - if (sprintf_s(szEventName, MAX_LONGPATH-1, "%s%d", szcHelperProcessReadyEvName, dwProcessId) < 0) - { - Fail ("helper.main: Insufficient event name string length for pid=%d\n", dwProcessId); - } - - uniString = convert(szEventName); - - hProcessReadyEvent = OpenEventW(EVENT_ALL_ACCESS, 0, uniString); - free(uniString); - if (!hProcessReadyEvent) - { - Fail("helper.main: OpenEvent of '%s' failed (%u). " - "(the event should already exist!)\n", - szEventName, GetLastError()); - } - - /* Open the event to let test thread tell us to exit. */ - if (sprintf_s(szEventName, MAX_LONGPATH-1, "%s%d", szcHelperProcessFinishEvName, dwProcessId) < 0) - { - Fail ("helper.main: Insufficient event name string length for pid=%d\n", dwProcessId); - } - - uniString = convert(szEventName); - - hProcessFinishEvent = OpenEventW(EVENT_ALL_ACCESS, 0, uniString); - free(uniString); - if (!hProcessFinishEvent) - { - Fail("helper.main: OpenEvent of '%s' failed LastError:(%u).\n", - szEventName, GetLastError()); - } - - /* Tell the test thread we are ready. */ - if (!SetEvent(hProcessReadyEvent)) - { - Fail("helper.main: SetEvent '%s' failed LastError:(%u)\n", - hProcessReadyEvent, GetLastError()); - } - - /* Wait for signal from test thread before exit. */ - dwRet = WaitForSingleObject(hProcessFinishEvent, TIMEOUT); - if (WAIT_OBJECT_0 != dwRet) - { - Fail("helper.main: WaitForSingleObject '%s' failed pid=%d\n" - "LastError:(%u)\n", - szcHelperProcessFinishEvName, dwProcessId, GetLastError()); - } - - PEDANTIC(CloseHandle, (hProcessStartEvent_WFMO_test5_helper)); - PEDANTIC(CloseHandle, (hProcessReadyEvent)); - PEDANTIC(CloseHandle, (hProcessFinishEvent)); - - PAL_TerminateEx(success ? PASS : FAIL); - - return success ? PASS : FAIL; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.cpp deleted file mode 100644 index ab943b11a17060..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test5/test5.cpp +++ /dev/null @@ -1,505 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================= -** -** Source: test5.c -** -** Purpose: Test the functionality of simultaneously waiting -** on multiple processes. Create the same number of helper -** processes and helper threads. -** Helper threads wait on helper processes to finish. -** Helper processes wait on the event signal from test -** thread before exit. -** The test thread can wake up one helper -** thread at a time by signaling the corresponding helper -** process to finish. -** The test thread can also wake up all helper threads at once -** by signaling help process 0 to exit. -** -** -**============================================================*/ - -#define UNICODE - -#include "commonconsts.h" - -#include - -/* The maximum number of objects a thread can wait is MAXIMUM_WAIT_OBJECTS. - The last helper thread in this test case will wait on all helper processes - plus a thread finish event so the maximum number of helper processes - can be created in this test case is (MAXIMUM_WAIT_OBJECTS-1). */ -#define MAX_HELPER_PROCESS (MAXIMUM_WAIT_OBJECTS-1) - -int MaxNumHelperProcess = MAX_HELPER_PROCESS; - -/* indicate how the test thread wake up helper thread. */ -typedef enum _TestCaseType { - WakeUpOneThread, /* wake up one helper thread at a time. */ - WakeUpAllThread /* wake up all helper threads at once */ -} TestCaseType; - -TestCaseType TestCase = WakeUpOneThread; - -/* When the test thread wakes up one thread at a time, - ThreadIndexOfThreadFinishEvent specifies the index of the thread that - should be waked up using hThreadFinishEvent instead of helper process. */ -DWORD ThreadIndexOfThreadFinishEvent = 0; - -struct helper_process_t -{ - PROCESS_INFORMATION pi; - HANDLE hProcessReadyEvent; - HANDLE hProcessFinishEvent; -} helper_process[MAX_HELPER_PROCESS]; - -HANDLE hProcessStartEvent_WFMO_test5; - -struct helper_thread_t -{ - HANDLE hThread; - DWORD dwThreadId; - HANDLE hThreadReadyEvent; - HANDLE hThreadFinishEvent; -} helper_thread[MAX_HELPER_PROCESS]; - -/* - * Entry Point for helper thread. - */ -DWORD PALAPI WaitForProcess(LPVOID lpParameter) -{ - DWORD index, i; - DWORD dwRet; - HANDLE handles[MAX_HELPER_PROCESS+1]; - - index = (DWORD)(SIZE_T) lpParameter; - - /* The helper thread 0 will wait for helper process 0, helper thread 1 will - wait for helper process 0 and 1, helper thread 2 will wait for helper - process 0, 1, and 2, and so on ..., and the last helper thread will wait - on all helper processes. - Each helper thread also waits on hThreadFinishEvent so that - it can exit without waiting on any process to finish. */ - - for (i = 0; i <= index; i++) - { - handles[i] = helper_process[i].pi.hProcess; - } - - handles[index+1] = helper_thread[index].hThreadFinishEvent; - - if(!SetEvent(helper_thread[index].hThreadReadyEvent)) - { - Fail("test5.WaitProcess: SetEvent of hThreadReadyEvent failed for thread %d. " - "GetLastError() returned %d.\n", index, - GetLastError()); - } - - dwRet = WaitForMultipleObjectsEx(index+2, &handles[0], FALSE, TIMEOUT, TRUE); - if (WakeUpAllThread == TestCase) - { - /* If the test thread signals helper process 0 to exit, all threads will be waked up, - and the return value must be (WAIT_OBJECT_0+0) because the handle of helper process 0 - is in handle[0]. */ - if (dwRet != (WAIT_OBJECT_0+0)) - { - Fail("test5.WaitForProcess: invalid return value %d for WakupAllThread from WaitForMultipleObjectsEx for thread %d\n" - "LastError:(%u)\n", - dwRet, index, - GetLastError()); - } - } - else if (WakeUpOneThread == TestCase) - { - /* If the test thread wakes up one helper thread at a time, - the return value must be either (WAIT_OBJECT_0+index) if the helper thread - wakes up because the corresponding help process exits, - or (index+1) if the helper thread wakes up because of hThreadReadyEvent. */ - if ((index != ThreadIndexOfThreadFinishEvent && dwRet != (WAIT_OBJECT_0+index)) || - (index == ThreadIndexOfThreadFinishEvent && dwRet != (index+1))) - { - Fail("test5.WaitForProcess: invalid return value %d for WakupOneThread from WaitForMultipleObjectsEx for thread %d\n" - "LastError:(%u)\n", - dwRet, index, - GetLastError()); - } - } - else - { - Fail("Unknown TestCase %d\n", TestCase); - } - return 0; -} - -/* - * Setup the helper processes and helper threads. - */ -void -Setup() -{ - - STARTUPINFO si; - DWORD dwRet; - int i; - - char szEventName[MAX_PATH]; - PWCHAR uniStringHelper; - PWCHAR uniString; - - /* Create the event to start helper process after it was created. */ - uniString = convert(szcHelperProcessStartEvName); - hProcessStartEvent_WFMO_test5 = CreateEvent(NULL, TRUE, FALSE, uniString); - free(uniString); - if (!hProcessStartEvent_WFMO_test5) - { - Fail("test5.Setup: CreateEvent of '%s' failed. " - "GetLastError() returned %d.\n", szcHelperProcessStartEvName, - GetLastError()); - } - - /* Create the helper processes. */ - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof(si); - uniStringHelper = convert("helper"); - for (i = 0; i < MaxNumHelperProcess; i++) - { - ZeroMemory( &helper_process[i].pi, sizeof(PROCESS_INFORMATION)); - - if(!CreateProcess( NULL, uniStringHelper, NULL, NULL, - FALSE, 0, NULL, NULL, &si, &helper_process[i].pi)) - { - Fail("test5.Setup: CreateProcess failed to load executable for helper process %d. " - "GetLastError() returned %u.\n", - i, GetLastError()); - } - - /* Create the event to let helper process tell us it is ready. */ - if (sprintf_s(szEventName, MAX_PATH-1, "%s%d", - szcHelperProcessReadyEvName, helper_process[i].pi.dwProcessId) < 0) - { - Fail ("test5.Setup: Insufficient event name string length for %s\n", szcHelperProcessReadyEvName); - } - - uniString = convert(szEventName); - - helper_process[i].hProcessReadyEvent = CreateEvent(NULL, FALSE, FALSE, uniString); - free(uniString); - if (!helper_process[i].hProcessReadyEvent) - { - Fail("test5.Setup: CreateEvent of '%s' failed. " - "GetLastError() returned %d.\n", szEventName, - GetLastError()); - } - - /* Create the event to tell helper process to exit. */ - if (sprintf_s(szEventName, MAX_PATH-1, "%s%d", - szcHelperProcessFinishEvName, helper_process[i].pi.dwProcessId) < 0) - { - Fail ("test5.Setup: Insufficient event name string length for %s\n", szcHelperProcessFinishEvName); - } - - uniString = convert(szEventName); - - helper_process[i].hProcessFinishEvent = CreateEvent(NULL, TRUE, FALSE, uniString); - free(uniString); - if (!helper_process[i].hProcessFinishEvent) - { - Fail("test5.Setup: CreateEvent of '%s' failed. " - "GetLastError() returned %d.\n", szEventName, - GetLastError()); - } - - } - free(uniStringHelper); - - /* Signal all helper processes to start. */ - if (!SetEvent(hProcessStartEvent_WFMO_test5)) - { - Fail("test5.Setup: SetEvent '%s' failed\n", - "LastError:(%u)\n", - szcHelperProcessStartEvName, GetLastError()); - } - - /* Wait for ready signals from all helper processes. */ - for (i = 0; i < MaxNumHelperProcess; i++) - { - dwRet = WaitForSingleObject(helper_process[i].hProcessReadyEvent, TIMEOUT); - if (dwRet != WAIT_OBJECT_0) - { - Fail("test5.Setup: WaitForSingleObject %s failed for helper process %d\n" - "LastError:(%u)\n", - szcHelperProcessReadyEvName, i, GetLastError()); - } - } - - /* Create the same number of helper threads as helper processes. */ - for (i = 0; i < MaxNumHelperProcess; i++) - { - /* Create the event to let helper thread tell us it is ready. */ - helper_thread[i].hThreadReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!helper_thread[i].hThreadReadyEvent) - { - Fail("test5.Setup: CreateEvent of hThreadReadyEvent failed for thread %d\n" - "LastError:(%u)\n", i, GetLastError()); - } - - /* Create the event to tell helper thread to exit without waiting for helper process. */ - helper_thread[i].hThreadFinishEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!helper_thread[i].hThreadFinishEvent) - { - Fail("test5.Setup: CreateEvent of hThreadFinishEvent failed for thread %d\n" - "LastError:(%u)\n", i, GetLastError()); - } - - /* Create the helper thread. */ - helper_thread[i].hThread = CreateThread( NULL, - 0, - (LPTHREAD_START_ROUTINE)WaitForProcess, - (LPVOID)i, - 0, - &helper_thread[i].dwThreadId); - if (NULL == helper_thread[i].hThread) - { - Fail("test5.Setup: Unable to create the helper thread %d\n" - "LastError:(%u)\n", i, GetLastError()); - } - } - - /* Wait for ready signals from all helper threads. */ - for (i = 0; i < MaxNumHelperProcess; i++) - { - dwRet = WaitForSingleObject(helper_thread[i].hThreadReadyEvent, TIMEOUT); - if (dwRet != WAIT_OBJECT_0) - { - Fail("test5.Setup: WaitForSingleObject hThreadReadyEvent for thread %d\n" - "LastError:(%u)\n", i, GetLastError()); - } - } -} - -/* - * Cleanup the helper processes and helper threads. - */ -DWORD -Cleanup_WFMO_test5() -{ - DWORD dwExitCode; - DWORD dwRet; - int i; - - /* Wait for all helper process to finish and close their handles - and associated events. */ - for (i = 0; i < MaxNumHelperProcess; i++) - { - - /* wait for the child process to complete */ - dwRet = WaitForSingleObject ( helper_process[i].pi.hProcess, TIMEOUT ); - if (WAIT_OBJECT_0 != dwRet) - { - Fail("test5.Cleanup: WaitForSingleObject hThreadReadyEvent failed for thread %d\n" - "LastError:(%u)\n", i, GetLastError()); - } - - /* check the exit code from the process */ - if (!GetExitCodeProcess(helper_process[i].pi.hProcess, &dwExitCode)) - { - Trace( "test5.Cleanup: GetExitCodeProcess %d call failed LastError:(%u)\n", - i, GetLastError()); - dwExitCode = FAIL; - } - PEDANTIC(CloseHandle, (helper_process[i].pi.hThread)); - PEDANTIC(CloseHandle, (helper_process[i].pi.hProcess)); - PEDANTIC(CloseHandle, (helper_process[i].hProcessReadyEvent)); - PEDANTIC(CloseHandle, (helper_process[i].hProcessFinishEvent)); - } - - /* Close all helper threads' handles */ - for (i = 0; i < MaxNumHelperProcess; i++) - { - PEDANTIC(CloseHandle, (helper_thread[i].hThread)); - PEDANTIC(CloseHandle, (helper_thread[i].hThreadReadyEvent)); - PEDANTIC(CloseHandle, (helper_thread[i].hThreadFinishEvent)); - } - - /* Close all process start event. */ - PEDANTIC(CloseHandle, (hProcessStartEvent_WFMO_test5)); - - return dwExitCode; -} - -/* - * In this test case, the test thread will signal one helper - * process to exit at a time starting from the last helper - * process and then wait for the corresponding helper thread to exit. - * The ThreadIndexOfThreadFinishEvent specifies the index of the thread that - * should be waked up using hThreadFinishEvent instead of helper process. - */ -void -TestWakeupOneThread() -{ - DWORD dwRet; - int i; - - TestCase = WakeUpOneThread; - - if (((LONG)ThreadIndexOfThreadFinishEvent) < 0 || - ThreadIndexOfThreadFinishEvent >= MAX_HELPER_PROCESS) - Fail("test5.TestWaitOnOneThread: Invalid ThreadIndexOfThreadFinishEvent %d\n", ThreadIndexOfThreadFinishEvent); - - /* Since helper thread 0 waits on helper process 0, - thread 1 waits on process 0, and 1, - thread 2 waits on process 0, 1, and 2, and so on ..., - and the last helper thread will wait on all helper processes, - the helper thread can be waked up one at a time by - waking up the help process one at a time starting from the - last helper process. */ - for (i = MaxNumHelperProcess-1; i >= 0; i--) - { - /* make sure the helper thread has not exited yet. */ - dwRet = WaitForSingleObject(helper_thread[i].hThread, 0); - if (WAIT_TIMEOUT != dwRet) - { - Fail("test5.TestWaitOnOneThread: helper thread %d already exited %d\n", i); - } - - /* Decide how to wakeup the helper thread: - using event or using helper process. */ - if (i == ThreadIndexOfThreadFinishEvent) - { - if (!SetEvent(helper_thread[i].hThreadFinishEvent)) - { - Fail("test5.TestWaitOnOneThread: SetEvent hThreadFinishEvent failed for thread %d\n", - "LastError:(%u)\n", i, GetLastError()); - } - } - else - { - if (!SetEvent(helper_process[i].hProcessFinishEvent)) - { - Fail("test5.TestWaitOnOneThread: SetEvent %s%d failed for helper process %d\n", - "LastError:(%u)\n", - szcHelperProcessFinishEvName, helper_process[i].pi.dwProcessId, i, - GetLastError()); - } - } - - dwRet = WaitForSingleObject(helper_thread[i].hThread, TIMEOUT); - if (WAIT_OBJECT_0 != dwRet) - { - Fail("test5.TestWaitOnOneThread: WaitForSingleObject helper thread %d" - "LastError:(%u)\n", - i, GetLastError()); - } - } - - /* Finally, need to wake up the helper process which the test thread - skips waking up in the last loop. */ - if (!SetEvent(helper_process[ThreadIndexOfThreadFinishEvent].hProcessFinishEvent)) - { - Fail("test5.TestWaitOnOneThread: SetEvent %s%d failed\n", - "LastError:(%u)\n", - szcHelperProcessFinishEvName, helper_process[ThreadIndexOfThreadFinishEvent].pi.dwProcessId, - GetLastError()); - } -} - -/* - * In this test case, the test thread will signal the helper - * process 0 to exit. Since all helper threads wait on process 0, - * all helper threads will wake up and exit, and the test thread - * will wait for all of them to exit. - */ -void -TestWakeupAllThread() -{ - DWORD dwRet; - int i; - - TestCase = WakeUpAllThread; - - /* make sure none of the helper thread exits. */ - for (i = 0; i < MaxNumHelperProcess; i++) - { - dwRet = WaitForSingleObject(helper_thread[i].hThread, 0); - if (WAIT_TIMEOUT != dwRet) - { - Fail("test5.TestWaitOnAllThread: helper thread %d already exited %d\n", i); - } - } - - /* Signal helper process 0 to exit. */ - if (!SetEvent(helper_process[0].hProcessFinishEvent)) - { - Fail("test5.TestWaitOnAllThread: SetEvent %s%d failed\n", - "LastError:(%u)\n", - szcHelperProcessFinishEvName, helper_process[0].pi.dwProcessId, - GetLastError()); - } - - /* Wait for all helper threads to exit. */ - for (i = 0; i < MaxNumHelperProcess; i++) - { - - dwRet = WaitForSingleObject(helper_thread[i].hThread, TIMEOUT); - if (WAIT_OBJECT_0 != dwRet) - { - Fail("test5.TestWaitOnAllThread: WaitForSingleObject failed for helper thread %d\n" - "LastError:(%u)\n", - i, GetLastError()); - } - } - - /* Signal the rest of helper processes to exit. */ - for (i = 1; i < MaxNumHelperProcess; i++) - { - if (!SetEvent(helper_process[i].hProcessFinishEvent)) - { - Fail("test5.TestWaitOnAllThread: SetEvent %s%d failed\n", - "LastError:(%u)\n", - szcHelperProcessFinishEvName, helper_process[i].pi.dwProcessId, - GetLastError()); - } - } -} - -PALTEST(threading_WaitForMultipleObjectsEx_test5_paltest_waitformultipleobjectsex_test5, "threading/WaitForMultipleObjectsEx/test5/paltest_waitformultipleobjectsex_test5") -{ - DWORD dwExitCode; - - if(0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - switch (argc) - { - case 1: - MaxNumHelperProcess = MAX_HELPER_PROCESS; - break; - case 2: - MaxNumHelperProcess = atoi(argv[1]); - break; - default: - Fail("Invalid number of arguments\n"); - } - - if (MaxNumHelperProcess < 1 || - MaxNumHelperProcess > MAX_HELPER_PROCESS) - Fail("test5.main: Invalid MaxNumHelperProcess %d\n", MaxNumHelperProcess); - - Setup(); - ThreadIndexOfThreadFinishEvent = 3; - TestWakeupOneThread(); - dwExitCode = Cleanup_WFMO_test5(); - - if (PASS == dwExitCode) - { - Setup(); - TestWakeupAllThread(); - dwExitCode = Cleanup_WFMO_test5(); - } - - PAL_TerminateEx(dwExitCode); - return dwExitCode; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.cpp deleted file mode 100644 index b8c15d07a4aa4d..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: ChildProcess.c -** -** Purpose: Dummy Process which does some work on which the Main Test case waits -** -** - -** -**=========================================================*/ - - - -#include - -PALTEST(threading_WaitForSingleObject_WFSOProcessTest_paltest_waitforsingleobject_wfsoprocesstest_child, "threading/WaitForSingleObject/WFSOProcessTest/paltest_waitforsingleobject_wfsoprocesstest_child") -{ - -//Declare local variables -int i =0; - - - -//Initialize PAL -if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - -//Do some work -for (i=0; i<100000; i++); - -Trace("Counter Value was incremented to %d \n ",i); - -PAL_Terminate(); -return ( PASS ); - -} - - - - - - - - diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.cpp deleted file mode 100644 index f850973a4cdea2..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: WFSOProcessTest.c -** -** Purpose: Test for WaitForSingleObjectTest. -** Create One Process and do some work -** Use WFSO For the Process to finish -** -** Test Passes if the above operations are successful -** -** -** -**=========================================================*/ - - - -#include - -PALTEST(threading_WaitForSingleObject_WFSOProcessTest_paltest_waitforsingleobject_wfsoprocesstest, "threading/WaitForSingleObject/WFSOProcessTest/paltest_waitforsingleobject_wfsoprocesstest") -{ - -//Declare local variables -STARTUPINFO si; -PROCESS_INFORMATION pi; - -DWORD dwWaitResult=0; - -//Initialize PAL -if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - -ZeroMemory( &si, sizeof(si) ); -si.cb = sizeof(si); -ZeroMemory( &pi, sizeof(pi) ); - -LPWSTR nameW = convert("childprocess"); -// Start the child process. -if( !CreateProcess( NULL, // No module name (use command line). - nameW, // Command line. - NULL, // Process handle not inheritable. - NULL, // Thread handle not inheritable. - FALSE, // Set handle inheritance to FALSE. - 0, // No creation flags. - NULL, // Use parent's environment block. - NULL, // Use parent's starting directory. - &si, // Pointer to STARTUPINFO structure. - &pi ) // Pointer to PROCESS_INFORMATION structure. -) - -{ -DWORD dwError = GetLastError(); -free(nameW); -Fail ( "Create Process Failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); -} - -free(nameW); - -// Wait until child process exits. - dwWaitResult = WaitForSingleObject( pi.hProcess, INFINITE ); -switch (dwWaitResult) - { - // The Process wait was successful - case WAIT_OBJECT_0: - { - - Trace("Wait for Process was successful\n"); - break; - } - - // Time-out. - case WAIT_TIMEOUT: - { - Fail ( "Time -out. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - return FALSE; - } - - //Error condition - case WAIT_FAILED: - { - Fail ( "Wait for Process Failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - return FALSE; - } - -} - - - -// Close process handle -if (0==CloseHandle(pi.hProcess)) - { - Trace("Could not close process handle\n"); - Fail ( "GetLastError returned %d\n", GetLastError()); - } - - -PAL_Terminate(); -return ( PASS ); - -} - - - - - - - diff --git a/src/coreclr/utilcode/winfix.cpp b/src/coreclr/utilcode/winfix.cpp index 9738475350c94b..b9da37663e8ed7 100644 --- a/src/coreclr/utilcode/winfix.cpp +++ b/src/coreclr/utilcode/winfix.cpp @@ -27,6 +27,98 @@ WszCreateProcess( LPPROCESS_INFORMATION lpProcessInformation ) { +#ifdef HOST_UNIX + (void)lpApplicationName; + (void)lpProcessAttributes; + (void)lpThreadAttributes; + (void)dwCreationFlags; + (void)lpEnvironment; + (void)lpStartupInfo; + + if (lpCommandLine == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + int commandLineLength = WideCharToMultiByte(CP_UTF8, 0, lpCommandLine, -1, NULL, 0, NULL, NULL); + if (commandLineLength == 0) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + NewArrayHolder commandLineUtf8 = new (nothrow) char[commandLineLength]; + if (commandLineUtf8 == NULL) + { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + if (WideCharToMultiByte(CP_UTF8, 0, lpCommandLine, -1, commandLineUtf8, commandLineLength, NULL, NULL) == 0) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + NewArrayHolder currentDirectoryUtf8; + if (lpCurrentDirectory != NULL) + { + int currentDirectoryLength = WideCharToMultiByte(CP_UTF8, 0, lpCurrentDirectory, -1, NULL, 0, NULL, NULL); + if (currentDirectoryLength == 0) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + currentDirectoryUtf8 = new (nothrow) char[currentDirectoryLength]; + if (currentDirectoryUtf8 == NULL) + { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + if (WideCharToMultiByte(CP_UTF8, 0, lpCurrentDirectory, -1, currentDirectoryUtf8, currentDirectoryLength, NULL, NULL) == 0) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + } + + pid_t pid = fork(); + if (pid < 0) + { + SetLastError(ERROR_INTERNAL_ERROR); + return FALSE; + } + + if (pid == 0) + { + if (currentDirectoryUtf8 != NULL && chdir(currentDirectoryUtf8) != 0) + { + _exit(127); + } + + execl("/bin/sh", "sh", "-c", commandLineUtf8.GetValue(), (char*)NULL); + _exit(127); + } + + if (lpProcessInformation != NULL) + { + HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, bInheritHandles, static_cast(pid)); + if (processHandle == NULL) + { + return FALSE; + } + + lpProcessInformation->hProcess = processHandle; + lpProcessInformation->hThread = NULL; + lpProcessInformation->dwProcessId = static_cast(pid); + lpProcessInformation->dwThreadId_PAL_Undefined = 0; + } + + return TRUE; +#else BOOL fResult; DWORD err; { @@ -58,6 +150,7 @@ WszCreateProcess( SetLastError(err); return fResult; +#endif } #ifndef HOST_UNIX diff --git a/src/coreclr/vm/autotrace.cpp b/src/coreclr/vm/autotrace.cpp index 4c900377ff35a8..330dd8c654786a 100644 --- a/src/coreclr/vm/autotrace.cpp +++ b/src/coreclr/vm/autotrace.cpp @@ -23,8 +23,9 @@ #ifdef FEATURE_AUTO_TRACE #ifdef TARGET_UNIX #include "pal.h" +#include +#include #endif // TARGET_UNIX -#include HANDLE auto_trace_event; static size_t g_n_tracers = 1; @@ -77,7 +78,41 @@ void auto_trace_init() void auto_trace_launch_internal() { - minipal_create_process_w((const CHAR16_T*)command, nullptr, false, nullptr); +#ifdef TARGET_UNIX + MAKE_UTF8PTR_FROMWIDE(commandUtf8, command); + if (commandUtf8 == nullptr) + { + return; + } + + pid_t pid = fork(); + if (pid == 0) + { + execl("/bin/sh", "sh", "-c", commandUtf8, (char*)nullptr); + _exit(EXIT_FAILURE); + } +#else + STARTUPINFO si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + PROCESS_INFORMATION result; + + BOOL code = CreateProcessW( + /* lpApplicationName = */ nullptr, + /* lpCommandLine = */ command, + /* lpCommandLine = */ nullptr, + /* lpThreadAttributes = */ nullptr, + /* bInheritHandles = */ false, + /* dwCreationFlags = */ CREATE_NEW_CONSOLE, + /* lpEnvironment = */ nullptr, + /* lpCurrentDirectory = */ nullptr, + /* lpStartupInfo = */ &si, + /* lpProcessInformation = */ &result + ); +#endif } void auto_trace_launch() diff --git a/src/native/minipal/CMakeLists.txt b/src/native/minipal/CMakeLists.txt index 1769423042e9ed..d7f9ad5e2ab78a 100644 --- a/src/native/minipal/CMakeLists.txt +++ b/src/native/minipal/CMakeLists.txt @@ -6,7 +6,6 @@ set(SOURCES memorybarrierprocesswide.c mutex.c guid.c - process.c random.c debugger.c strings.c diff --git a/src/native/minipal/process.c b/src/native/minipal/process.c deleted file mode 100644 index 08e7baa4c6aeeb..00000000000000 --- a/src/native/minipal/process.c +++ /dev/null @@ -1,404 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include - -#include -#include -#include -#include - -#ifdef TARGET_WINDOWS - -#include - -bool minipal_create_process( - const char* command_line, - const char* working_dir, - bool inherit_handles, - minipal_process_info* out_info) -{ - if (command_line == NULL) - return false; - - // Convert command_line to UTF-16. - size_t cmd_len = strlen(command_line); - size_t cmd_wide_len = minipal_get_length_utf8_to_utf16(command_line, cmd_len, 0); - if (cmd_wide_len == 0) - return false; - - // +1 for null terminator - WCHAR* cmd_wide = (WCHAR*)malloc((cmd_wide_len + 1) * sizeof(WCHAR)); - if (cmd_wide == NULL) - return false; - - minipal_convert_utf8_to_utf16(command_line, cmd_len, (CHAR16_T*)cmd_wide, cmd_wide_len + 1, 0); - cmd_wide[cmd_wide_len] = L'\0'; - - // Convert working_dir if provided. - WCHAR* dir_wide = NULL; - if (working_dir != NULL) - { - size_t dir_len = strlen(working_dir); - size_t dir_wide_len = minipal_get_length_utf8_to_utf16(working_dir, dir_len, 0); - if (dir_wide_len == 0) - { - free(cmd_wide); - return false; - } - dir_wide = (WCHAR*)malloc((dir_wide_len + 1) * sizeof(WCHAR)); - if (dir_wide == NULL) - { - free(cmd_wide); - return false; - } - minipal_convert_utf8_to_utf16(working_dir, dir_len, (CHAR16_T*)dir_wide, dir_wide_len + 1, 0); - dir_wide[dir_wide_len] = L'\0'; - } - - STARTUPINFOW si; - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - - PROCESS_INFORMATION pi; - memset(&pi, 0, sizeof(pi)); - - BOOL result = CreateProcessW( - NULL, - cmd_wide, - NULL, - NULL, - inherit_handles ? TRUE : FALSE, - 0, - NULL, - dir_wide, - &si, - &pi); - - free(cmd_wide); - free(dir_wide); - - if (!result) - return false; - - if (out_info != NULL) - { - out_info->process_handle = (intptr_t)pi.hProcess; - out_info->thread_handle = (intptr_t)pi.hThread; - out_info->process_id = (int32_t)pi.dwProcessId; - } - else - { - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } - - return true; -} - -void minipal_close_process_handle(intptr_t handle) -{ - if (handle != 0) - CloseHandle((HANDLE)handle); -} - -bool minipal_create_process_w( - const CHAR16_T* command_line, - const CHAR16_T* working_dir, - bool inherit_handles, - minipal_process_info* out_info) -{ - if (command_line == NULL) - return false; - - // On Windows, CHAR16_T is wchar_t so we can pass directly to CreateProcessW. - // CreateProcessW may modify the command line buffer, so make a mutable copy. - size_t cmd_len = wcslen((const WCHAR*)command_line); - WCHAR* cmd_copy = (WCHAR*)malloc((cmd_len + 1) * sizeof(WCHAR)); - if (cmd_copy == NULL) - return false; - memcpy(cmd_copy, command_line, (cmd_len + 1) * sizeof(WCHAR)); - - STARTUPINFOW si; - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - - PROCESS_INFORMATION pi; - memset(&pi, 0, sizeof(pi)); - - BOOL result = CreateProcessW( - NULL, - cmd_copy, - NULL, - NULL, - inherit_handles ? TRUE : FALSE, - 0, - NULL, - (LPCWSTR)working_dir, - &si, - &pi); - - free(cmd_copy); - - if (!result) - return false; - - if (out_info != NULL) - { - out_info->process_handle = (intptr_t)pi.hProcess; - out_info->thread_handle = (intptr_t)pi.hThread; - out_info->process_id = (int32_t)pi.dwProcessId; - } - else - { - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } - - return true; -} - -#elif defined(TARGET_WASM) - -// wasm does not support process creation. -bool minipal_create_process( - const char* command_line, - const char* working_dir, - bool inherit_handles, - minipal_process_info* out_info) -{ - (void)command_line; (void)working_dir; (void)inherit_handles; (void)out_info; - return false; -} - -void minipal_close_process_handle(intptr_t handle) -{ - (void)handle; -} - -bool minipal_create_process_w( - const CHAR16_T* command_line, - const CHAR16_T* working_dir, - bool inherit_handles, - minipal_process_info* out_info) -{ - (void)command_line; (void)working_dir; (void)inherit_handles; (void)out_info; - return false; -} - -#else - -#include -#include -#include -#include -#include -#include - -// Command line splitting matching Win32 CreateProcessW semantics: -// 1) Whitespace splits arguments (space, tab) -// 2) Double quotes group text (whitespace inside quotes doesn't split) -// 3) \" is an escaped double quote (produces literal " in output) -// 4) Backslash followed by anything other than " is literal (kept as-is) -// 5) Bare double quotes are stripped from output -// Returns heap-allocated argv array (caller frees each element and the array). -static char** split_command_line(const char* cmd, int* out_argc) -{ - int capacity = 8; - int count = 0; - char** argv = (char**)malloc(capacity * sizeof(char*)); - if (argv == NULL) - return NULL; - - const char* p = cmd; - while (*p != '\0') - { - // Skip whitespace - while (*p == ' ' || *p == '\t') - p++; - if (*p == '\0') - break; - - // Find the end of this argument (first pass: determine boundaries) - const char* arg_start = p; - bool in_quotes = false; - while (*p != '\0') - { - if (!in_quotes && (*p == ' ' || *p == '\t')) - break; - - if (*p == '"') - { - // Check for escaped quote: \" - if (p > arg_start && *(p - 1) == '\\') - { - // This is an escaped quote, not a real quote toggle - p++; - continue; - } - in_quotes = !in_quotes; - p++; - } - else - { - p++; - } - } - - // Second pass: copy the argument, stripping bare quotes and handling \" - size_t arg_len = (size_t)(p - arg_start); - char* buf = (char*)malloc(arg_len + 1); - if (buf == NULL) - { - for (int i = 0; i < count; i++) free(argv[i]); - free(argv); - return NULL; - } - - size_t j = 0; - const char* s = arg_start; - while (s < p) - { - if (*s == '"') - { - // Skip bare double quotes (they're grouping characters) - s++; - } - else if (*s == '\\' && (s + 1) < p && *(s + 1) == '"') - { - // Escaped double quote: \\" -> produce literal " - buf[j++] = '"'; - s += 2; - } - else - { - buf[j++] = *s++; - } - } - buf[j] = '\0'; - - if (count + 2 > capacity) - { - capacity *= 2; - argv = (char**)realloc(argv, capacity * sizeof(char*)); - } - argv[count++] = buf; - } - - argv[count] = NULL; - *out_argc = count; - return argv; -} - -bool minipal_create_process( - const char* command_line, - const char* working_dir, - bool inherit_handles, - minipal_process_info* out_info) -{ - if (command_line == NULL) - return false; - - int argc = 0; - char** argv = split_command_line(command_line, &argc); - if (argv == NULL || argc == 0) - { - free(argv); - return false; - } - - pid_t pid = fork(); - if (pid < 0) - { - for (int i = 0; i < argc; i++) free(argv[i]); - free(argv); - return false; - } - - if (pid == 0) - { - // Child process - if (working_dir != NULL) - { - if (chdir(working_dir) != 0) - _exit(127); - } - - if (!inherit_handles) - { - // Close file descriptors > 2 - int max_fd = (int)sysconf(_SC_OPEN_MAX); - if (max_fd < 0) max_fd = 1024; - for (int fd = 3; fd < max_fd; fd++) - close(fd); - } - - execvp(argv[0], argv); - _exit(127); // exec failed - } - - // Parent - for (int i = 0; i < argc; i++) free(argv[i]); - free(argv); - - if (out_info != NULL) - { - out_info->process_handle = (intptr_t)pid; - out_info->thread_handle = 0; - out_info->process_id = (int32_t)pid; - } - - return true; -} - -void minipal_close_process_handle(intptr_t handle) -{ - // On Unix, PIDs don't need to be "closed". No-op. - (void)handle; -} - -bool minipal_create_process_w( - const CHAR16_T* command_line, - const CHAR16_T* working_dir, - bool inherit_handles, - minipal_process_info* out_info) -{ - if (command_line == NULL) - return false; - - // Convert command_line from UTF-16 to UTF-8. - size_t cmd_u16_len = minipal_u16_strlen(command_line); - size_t cmd_u8_len = minipal_get_length_utf16_to_utf8(command_line, cmd_u16_len, 0); - if (cmd_u8_len == 0) - return false; - - char* cmd_utf8 = (char*)malloc(cmd_u8_len + 1); - if (cmd_utf8 == NULL) - return false; - minipal_convert_utf16_to_utf8(command_line, cmd_u16_len, cmd_utf8, cmd_u8_len + 1, 0); - cmd_utf8[cmd_u8_len] = '\0'; - - // Convert working_dir if provided. - char* dir_utf8 = NULL; - if (working_dir != NULL) - { - size_t dir_u16_len = minipal_u16_strlen(working_dir); - size_t dir_u8_len = minipal_get_length_utf16_to_utf8(working_dir, dir_u16_len, 0); - if (dir_u8_len > 0) - { - dir_utf8 = (char*)malloc(dir_u8_len + 1); - if (dir_utf8 != NULL) - { - minipal_convert_utf16_to_utf8(working_dir, dir_u16_len, dir_utf8, dir_u8_len + 1, 0); - dir_utf8[dir_u8_len] = '\0'; - } - } - } - - bool ok = minipal_create_process(cmd_utf8, dir_utf8, inherit_handles, out_info); - free(cmd_utf8); - free(dir_utf8); - return ok; -} - -#endif diff --git a/src/native/minipal/process.h b/src/native/minipal/process.h deleted file mode 100644 index 9d0eba27067581..00000000000000 --- a/src/native/minipal/process.h +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef HAVE_MINIPAL_PROCESS_H -#define HAVE_MINIPAL_PROCESS_H - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct -{ - intptr_t process_handle; // Caller must close via minipal_close_process_handle. - intptr_t thread_handle; // Caller must close via minipal_close_process_handle. Always 0 on Unix. - int32_t process_id; -} minipal_process_info; - -/** - * Launch a child process from a UTF-8 command line. - * - * @param command_line The command line to execute (UTF-8, null-terminated). - * @param working_dir Working directory for the child process, or NULL to inherit. - * @param inherit_handles Whether the child inherits parent's handles. - * @param out_info Optional. Receives process handle/PID on success. Caller must - * close process_handle and thread_handle when done. - * @return true on success, false on failure. - */ -bool minipal_create_process( - const char* command_line, - const char* working_dir, - bool inherit_handles, - minipal_process_info* out_info); - -/** - * Launch a child process from a UTF-16 command line. - * Handles the UTF-16 to UTF-8 conversion internally on Unix. - * On Windows, passes through to CreateProcessW directly. - * - * @param command_line The command line to execute (UTF-16/CHAR16_T, null-terminated). - * @param working_dir Working directory for the child process, or NULL to inherit. - * @param inherit_handles Whether the child inherits parent's handles. - * @param out_info Optional. Receives process handle/PID on success. Caller must - * close process_handle and thread_handle when done. - * @return true on success, false on failure. - */ -bool minipal_create_process_w( - const CHAR16_T* command_line, - const CHAR16_T* working_dir, - bool inherit_handles, - minipal_process_info* out_info); - -/** - * Close a process or thread handle returned by minipal_create_process. - * - * @param handle The handle to close. 0 is a no-op. - */ -void minipal_close_process_handle(intptr_t handle); - -#ifdef __cplusplus -} -#endif - -#endif // HAVE_MINIPAL_PROCESS_H From 3261ce61d37eeed6c4f99d1098cd469f065ac1c2 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sat, 2 May 2026 20:28:31 +0300 Subject: [PATCH 04/11] Delete DbgTransportTarget::CreateProcess --- src/coreclr/debug/di/dbgtransportmanager.cpp | 31 ----- src/coreclr/debug/di/dbgtransportmanager.h | 15 +-- src/coreclr/debug/di/dbgtransportpipeline.cpp | 115 ------------------ src/coreclr/debug/di/nativepipeline.h | 14 --- src/coreclr/debug/di/process.cpp | 14 +-- src/coreclr/debug/di/rsmain.cpp | 7 +- src/coreclr/debug/di/windowspipeline.cpp | 51 -------- 7 files changed, 3 insertions(+), 244 deletions(-) diff --git a/src/coreclr/debug/di/dbgtransportmanager.cpp b/src/coreclr/debug/di/dbgtransportmanager.cpp index 828af8928724f6..7c17adadde2784 100644 --- a/src/coreclr/debug/di/dbgtransportmanager.cpp +++ b/src/coreclr/debug/di/dbgtransportmanager.cpp @@ -160,37 +160,6 @@ void DbgTransportTarget::ReleaseTransport(DbgTransportSession *pTransport) pTransport->Shutdown(); } -HRESULT DbgTransportTarget::CreateProcess(LPCWSTR lpApplicationName, - LPCWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation) -{ - - BOOL result = WszCreateProcess(lpApplicationName, - lpCommandLine, - lpProcessAttributes, - lpThreadAttributes, - bInheritHandles, - dwCreationFlags, - lpEnvironment, - lpCurrentDirectory, - lpStartupInfo, - lpProcessInformation); - - if (!result) - { - return HRESULT_FROM_GetLastError(); - } - - return S_OK; -} - // Kill the process identified by PID. void DbgTransportTarget::KillProcess(DWORD dwPID) { diff --git a/src/coreclr/debug/di/dbgtransportmanager.h b/src/coreclr/debug/di/dbgtransportmanager.h index f03dd9a401fecf..d7f2273b171949 100644 --- a/src/coreclr/debug/di/dbgtransportmanager.h +++ b/src/coreclr/debug/di/dbgtransportmanager.h @@ -14,8 +14,7 @@ // It also handles things like creating and killing a process. // Usual lifecycle looks like this: -// Debug a new process: -// * CreateProcess(&pid) +// Debug an existing process: // * On Mac, Optionally obtain an application group ID from a user // * Create a ProcessDescriptor pd // * GetTransportForProcess(&pd, &transport) @@ -42,18 +41,6 @@ class DbgTransportTarget // connection at this point). void ReleaseTransport(DbgTransportSession *pTransport); - // When and if the process starts the runtime will be told to halt and wait for a debugger attach. - HRESULT CreateProcess(LPCWSTR lpApplicationName, - LPCWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation); - // Kill the process identified by PID. void KillProcess(DWORD dwPID); diff --git a/src/coreclr/debug/di/dbgtransportpipeline.cpp b/src/coreclr/debug/di/dbgtransportpipeline.cpp index 90564df07a0b19..b42f7c4534e080 100644 --- a/src/coreclr/debug/di/dbgtransportpipeline.cpp +++ b/src/coreclr/debug/di/dbgtransportpipeline.cpp @@ -75,20 +75,6 @@ class DbgTransportPipeline : virtual BOOL DebugSetProcessKillOnExit(bool fKillOnExit); - // Create - virtual HRESULT CreateProcessUnderDebugger( - MachineInfo machineInfo, - LPCWSTR lpApplicationName, - LPCWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation); - // Attach virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor); @@ -182,107 +168,6 @@ BOOL DbgTransportPipeline::DebugSetProcessKillOnExit(bool fKillOnExit) return TRUE; } -// Create an process under the debugger. -HRESULT DbgTransportPipeline::CreateProcessUnderDebugger( - MachineInfo machineInfo, - LPCWSTR lpApplicationName, - LPCWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation) -{ - // INativeEventPipeline has a 1:1 relationship with CordbProcess. - _ASSERTE(!IsTransportRunning()); - - // We don't support interop-debugging on the Mac. - _ASSERTE(!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))); - - // When we're using a transport we can't deal with creating a suspended process (we need the process to - // startup in order that it can start up a transport thread and reply to our messages). - _ASSERTE(!(dwCreationFlags & CREATE_SUSPENDED)); - - // Connect to the debugger proxy on the remote machine and ask it to create a process for us. - HRESULT hr = E_FAIL; - - m_pProxy = &g_DbgTransportTarget; - hr = m_pProxy->CreateProcess(lpApplicationName, - lpCommandLine, - lpProcessAttributes, - lpThreadAttributes, - bInheritHandles, - dwCreationFlags, - lpEnvironment, - lpCurrentDirectory, - lpStartupInfo, - lpProcessInformation); - - if (SUCCEEDED(hr)) - { - ProcessDescriptor processDescriptor = ProcessDescriptor::Create(lpProcessInformation->dwProcessId, NULL); - - // Establish a connection to the actual runtime to be debugged. - hr = m_pProxy->GetTransportForProcess(&processDescriptor, - &m_pTransport, - &m_hProcess); - if (SUCCEEDED(hr)) - { - // Wait for the connection to become usable (or time out). - if (!m_pTransport->WaitForSessionToOpen(10000)) - { - hr = CORDBG_E_TIMEOUT; - } - else - { - if (!m_pTransport->UseAsDebugger(&m_ticket)) - { - hr = CORDBG_E_DEBUGGER_ALREADY_ATTACHED; - } - } - } - } - - if (SUCCEEDED(hr)) - { - _ASSERTE((m_hProcess != NULL) && (m_hProcess != INVALID_HANDLE_VALUE)); - - m_dwProcessId = lpProcessInformation->dwProcessId; - - // For Mac remote debugging, we don't actually have a process handle to hand back to the debugger. - // Instead, we return a handle to an event as the "process handle". The Win32 event thread also waits - // on this event handle, and the event will be signaled when the proxy notifies us that the process - // on the remote machine is terminated. However, normally the debugger calls CloseHandle() immediately - // on the "process handle" after CreateProcess() returns. Doing so causes the Win32 event thread to - // continue waiting on a closed event handle, and so it will never wake up. - // (In fact, in Whidbey, we also duplicate the process handle in code:CordbProcess::Init.) - if (!DuplicateHandle(GetCurrentProcess(), - m_hProcess, - GetCurrentProcess(), - &(lpProcessInformation->hProcess), - 0, // ignored since we are going to pass DUPLICATE_SAME_ACCESS - FALSE, - DUPLICATE_SAME_ACCESS)) - { - hr = HRESULT_FROM_GetLastError(); - } - } - - if (SUCCEEDED(hr)) - { - m_fRunning = TRUE; - } - else - { - Dispose(); - } - - return hr; -} - // Attach the debugger to this process. HRESULT DbgTransportPipeline::DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor) { diff --git a/src/coreclr/debug/di/nativepipeline.h b/src/coreclr/debug/di/nativepipeline.h index 8076830e5607d1..442ab32138b5ff 100644 --- a/src/coreclr/debug/di/nativepipeline.h +++ b/src/coreclr/debug/di/nativepipeline.h @@ -50,20 +50,6 @@ class INativeEventPipeline virtual BOOL DebugSetProcessKillOnExit(bool fKillOnExit) = 0; - // Create - virtual HRESULT CreateProcessUnderDebugger( - MachineInfo machineInfo, - LPCWSTR lpApplicationName, - LPCWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation) = 0; - // Attach virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor) = 0; diff --git a/src/coreclr/debug/di/process.cpp b/src/coreclr/debug/di/process.cpp index 25495d6974b86a..3a126e446e03fd 100644 --- a/src/coreclr/debug/di/process.cpp +++ b/src/coreclr/debug/di/process.cpp @@ -13987,19 +13987,7 @@ void CordbWin32EventThread::CreateProcess() false; // Interop not supported. #endif - // Have Win32 create the process... - hr = m_pNativePipeline->CreateProcessUnderDebugger( - m_actionData.createData.machineInfo, - m_actionData.createData.programName, - m_actionData.createData.programArgs, - m_actionData.createData.lpProcessAttributes, - m_actionData.createData.lpThreadAttributes, - m_actionData.createData.bInheritHandles, - dwCreationFlags, - m_actionData.createData.lpEnvironment, - m_actionData.createData.lpCurrentDirectory, - m_actionData.createData.lpStartupInfo, - m_actionData.createData.lpProcessInformation); + hr = E_NOTIMPL; if (SUCCEEDED(hr)) { diff --git a/src/coreclr/debug/di/rsmain.cpp b/src/coreclr/debug/di/rsmain.cpp index 2daafa9e75be81..dea4f3f9c8991d 100644 --- a/src/coreclr/debug/di/rsmain.cpp +++ b/src/coreclr/debug/di/rsmain.cpp @@ -1499,15 +1499,10 @@ HRESULT Cordb::SetUnmanagedHandler(ICorDebugUnmanagedCallback *pCallback) return S_OK; } -// CreateProcess() isn't supported on Windows CoreCLR. -// It is currently supported on Mac CoreCLR, but that may change. +// CreateProcess() isn't supported on CoreCLR. bool Cordb::IsCreateProcessSupported() { -#if !defined(FEATURE_DBGIPC_TRANSPORT_DI) return false; -#else - return true; -#endif } // Given everything we know about our configuration, can we support interop-debugging diff --git a/src/coreclr/debug/di/windowspipeline.cpp b/src/coreclr/debug/di/windowspipeline.cpp index 56b8312d3d39fd..44c675a2c48f46 100644 --- a/src/coreclr/debug/di/windowspipeline.cpp +++ b/src/coreclr/debug/di/windowspipeline.cpp @@ -56,20 +56,6 @@ class WindowsNativePipeline : virtual BOOL DebugSetProcessKillOnExit(bool fKillOnExit); - // Create - virtual HRESULT CreateProcessUnderDebugger( - MachineInfo machineInfo, - LPCWSTR lpApplicationName, - LPCWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation); - // Attach virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor); @@ -125,43 +111,6 @@ BOOL WindowsNativePipeline::DebugSetProcessKillOnExit(bool fKillOnExit) return TRUE; } -// Create an process under the debugger. -HRESULT WindowsNativePipeline::CreateProcessUnderDebugger( - MachineInfo machineInfo, - LPCWSTR lpApplicationName, - LPCWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - LPVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation) -{ - // This is always doing Native-debugging at the OS-level. - dwCreationFlags |= (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS); - - BOOL ret = ::WszCreateProcess( - lpApplicationName, - lpCommandLine, - lpProcessAttributes, - lpThreadAttributes, - bInheritHandles, - dwCreationFlags, - lpEnvironment, - lpCurrentDirectory, - lpStartupInfo, - lpProcessInformation); - if (!ret) - { - return HRESULT_FROM_GetLastError(); - } - - m_dwProcessId = lpProcessInformation->dwProcessId; - return S_OK; -} - // Attach the debugger to this process. HRESULT WindowsNativePipeline::DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor) { From 470d7caf77818e90f14d8a7d58aeee3e7d7afc72 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Sat, 2 May 2026 20:57:11 +0300 Subject: [PATCH 05/11] Bring back command line splitting for Unix --- src/coreclr/utilcode/winfix.cpp | 108 +++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/src/coreclr/utilcode/winfix.cpp b/src/coreclr/utilcode/winfix.cpp index b9da37663e8ed7..7430b554647d9b 100644 --- a/src/coreclr/utilcode/winfix.cpp +++ b/src/coreclr/utilcode/winfix.cpp @@ -10,6 +10,105 @@ #include "utilcode.h" #include "holder.h" +#ifdef HOST_UNIX + +// Command line splitting matching Win32 CreateProcessW semantics: +// 1) Whitespace splits arguments (per isspace()) +// 2) Double quotes group text (whitespace inside quotes doesn't split) +// 3) \" is an escaped double quote (produces literal " in output) +// 4) Backslash followed by anything other than " is literal (kept as-is) +// 5) Bare double quotes are stripped from output +// Returns heap-allocated argv array (caller frees each element and the array). +static char** SplitCommandLine(const char* cmd, int* out_argc) +{ + int capacity = 8; + int count = 0; + char** argv = (char**)malloc(capacity * sizeof(char*)); + if (argv == NULL) + return NULL; + + const char* p = cmd; + while (*p != '\0') + { + // Skip whitespace + while (*p != '\0' && isspace((unsigned char)*p)) + p++; + if (*p == '\0') + break; + + // Find the end of this argument (first pass: determine boundaries) + const char* arg_start = p; + bool in_quotes = false; + while (*p != '\0') + { + if (!in_quotes && isspace((unsigned char)*p)) + break; + + if (*p == '"') + { + // Check for escaped quote: \" + if (p > arg_start && *(p - 1) == '\\') + { + // This is an escaped quote, not a real quote toggle + p++; + continue; + } + in_quotes = !in_quotes; + p++; + } + else + { + p++; + } + } + + // Second pass: copy the argument, stripping bare quotes and handling \" + size_t arg_len = (size_t)(p - arg_start); + char* buf = (char*)malloc(arg_len + 1); + if (buf == NULL) + { + for (int i = 0; i < count; i++) free(argv[i]); + free(argv); + return NULL; + } + + size_t j = 0; + const char* s = arg_start; + while (s < p) + { + if (*s == '"') + { + // Skip bare double quotes (they're grouping characters) + s++; + } + else if (*s == '\\' && (s + 1) < p && *(s + 1) == '"') + { + // Escaped double quote: \" -> produce literal " + buf[j++] = '"'; + s += 2; + } + else + { + buf[j++] = *s++; + } + } + buf[j] = '\0'; + + if (count + 2 > capacity) + { + capacity *= 2; + argv = (char**)realloc(argv, capacity * sizeof(char*)); + } + argv[count++] = buf; + } + + argv[count] = NULL; + *out_argc = count; + return argv; +} + +#endif // HOST_UNIX + // The only purpose of this function is to make a local copy of lpCommandLine. // Because windows implementation of CreateProcessW can actually change lpCommandLine, // but we'd like to keep it const. @@ -99,7 +198,14 @@ WszCreateProcess( _exit(127); } - execl("/bin/sh", "sh", "-c", commandLineUtf8.GetValue(), (char*)NULL); + int argc = 0; + char** argv = SplitCommandLine(commandLineUtf8.GetValue(), &argc); + if (argv == NULL || argc == 0) + { + _exit(127); + } + + execvp(argv[0], argv); _exit(127); } From 7a10c15eefd5869c7bea26a04f9c8a59a1b35a11 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Sun, 3 May 2026 00:11:22 +0300 Subject: [PATCH 06/11] Tailor impl to only Unix caller --- src/coreclr/utilcode/winfix.cpp | 53 ++++++++------------------------- 1 file changed, 13 insertions(+), 40 deletions(-) diff --git a/src/coreclr/utilcode/winfix.cpp b/src/coreclr/utilcode/winfix.cpp index 7430b554647d9b..a64573bb10aeeb 100644 --- a/src/coreclr/utilcode/winfix.cpp +++ b/src/coreclr/utilcode/winfix.cpp @@ -1,5 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. + //***************************************************************************** // WinWrap.cpp // @@ -127,12 +128,16 @@ WszCreateProcess( ) { #ifdef HOST_UNIX + // Only caller on Unix is LaunchCreateDump (excep.cpp); other parameters are ignored. (void)lpApplicationName; (void)lpProcessAttributes; (void)lpThreadAttributes; (void)dwCreationFlags; (void)lpEnvironment; + (void)lpCurrentDirectory; (void)lpStartupInfo; + _ASSERTE(lpCurrentDirectory == NULL); + _ASSERTE(lpProcessInformation != NULL); if (lpCommandLine == NULL) { @@ -160,30 +165,6 @@ WszCreateProcess( return FALSE; } - NewArrayHolder currentDirectoryUtf8; - if (lpCurrentDirectory != NULL) - { - int currentDirectoryLength = WideCharToMultiByte(CP_UTF8, 0, lpCurrentDirectory, -1, NULL, 0, NULL, NULL); - if (currentDirectoryLength == 0) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - currentDirectoryUtf8 = new (nothrow) char[currentDirectoryLength]; - if (currentDirectoryUtf8 == NULL) - { - SetLastError(ERROR_OUTOFMEMORY); - return FALSE; - } - - if (WideCharToMultiByte(CP_UTF8, 0, lpCurrentDirectory, -1, currentDirectoryUtf8, currentDirectoryLength, NULL, NULL) == 0) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - } - pid_t pid = fork(); if (pid < 0) { @@ -193,11 +174,6 @@ WszCreateProcess( if (pid == 0) { - if (currentDirectoryUtf8 != NULL && chdir(currentDirectoryUtf8) != 0) - { - _exit(127); - } - int argc = 0; char** argv = SplitCommandLine(commandLineUtf8.GetValue(), &argc); if (argv == NULL || argc == 0) @@ -209,20 +185,17 @@ WszCreateProcess( _exit(127); } - if (lpProcessInformation != NULL) + HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, bInheritHandles, static_cast(pid)); + if (processHandle == NULL) { - HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, bInheritHandles, static_cast(pid)); - if (processHandle == NULL) - { - return FALSE; - } - - lpProcessInformation->hProcess = processHandle; - lpProcessInformation->hThread = NULL; - lpProcessInformation->dwProcessId = static_cast(pid); - lpProcessInformation->dwThreadId_PAL_Undefined = 0; + return FALSE; } + lpProcessInformation->hProcess = processHandle; + lpProcessInformation->hThread = NULL; + lpProcessInformation->dwProcessId = static_cast(pid); + lpProcessInformation->dwThreadId_PAL_Undefined = 0; + return TRUE; #else BOOL fResult; From cae1fb993613f5f230d52f152e7344f0e9f035d0 Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sun, 3 May 2026 01:39:59 +0300 Subject: [PATCH 07/11] Address CR feedback --- src/coreclr/utilcode/winfix.cpp | 172 -------------------------------- 1 file changed, 172 deletions(-) diff --git a/src/coreclr/utilcode/winfix.cpp b/src/coreclr/utilcode/winfix.cpp index a64573bb10aeeb..9738475350c94b 100644 --- a/src/coreclr/utilcode/winfix.cpp +++ b/src/coreclr/utilcode/winfix.cpp @@ -1,6 +1,5 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - //***************************************************************************** // WinWrap.cpp // @@ -11,105 +10,6 @@ #include "utilcode.h" #include "holder.h" -#ifdef HOST_UNIX - -// Command line splitting matching Win32 CreateProcessW semantics: -// 1) Whitespace splits arguments (per isspace()) -// 2) Double quotes group text (whitespace inside quotes doesn't split) -// 3) \" is an escaped double quote (produces literal " in output) -// 4) Backslash followed by anything other than " is literal (kept as-is) -// 5) Bare double quotes are stripped from output -// Returns heap-allocated argv array (caller frees each element and the array). -static char** SplitCommandLine(const char* cmd, int* out_argc) -{ - int capacity = 8; - int count = 0; - char** argv = (char**)malloc(capacity * sizeof(char*)); - if (argv == NULL) - return NULL; - - const char* p = cmd; - while (*p != '\0') - { - // Skip whitespace - while (*p != '\0' && isspace((unsigned char)*p)) - p++; - if (*p == '\0') - break; - - // Find the end of this argument (first pass: determine boundaries) - const char* arg_start = p; - bool in_quotes = false; - while (*p != '\0') - { - if (!in_quotes && isspace((unsigned char)*p)) - break; - - if (*p == '"') - { - // Check for escaped quote: \" - if (p > arg_start && *(p - 1) == '\\') - { - // This is an escaped quote, not a real quote toggle - p++; - continue; - } - in_quotes = !in_quotes; - p++; - } - else - { - p++; - } - } - - // Second pass: copy the argument, stripping bare quotes and handling \" - size_t arg_len = (size_t)(p - arg_start); - char* buf = (char*)malloc(arg_len + 1); - if (buf == NULL) - { - for (int i = 0; i < count; i++) free(argv[i]); - free(argv); - return NULL; - } - - size_t j = 0; - const char* s = arg_start; - while (s < p) - { - if (*s == '"') - { - // Skip bare double quotes (they're grouping characters) - s++; - } - else if (*s == '\\' && (s + 1) < p && *(s + 1) == '"') - { - // Escaped double quote: \" -> produce literal " - buf[j++] = '"'; - s += 2; - } - else - { - buf[j++] = *s++; - } - } - buf[j] = '\0'; - - if (count + 2 > capacity) - { - capacity *= 2; - argv = (char**)realloc(argv, capacity * sizeof(char*)); - } - argv[count++] = buf; - } - - argv[count] = NULL; - *out_argc = count; - return argv; -} - -#endif // HOST_UNIX - // The only purpose of this function is to make a local copy of lpCommandLine. // Because windows implementation of CreateProcessW can actually change lpCommandLine, // but we'd like to keep it const. @@ -127,77 +27,6 @@ WszCreateProcess( LPPROCESS_INFORMATION lpProcessInformation ) { -#ifdef HOST_UNIX - // Only caller on Unix is LaunchCreateDump (excep.cpp); other parameters are ignored. - (void)lpApplicationName; - (void)lpProcessAttributes; - (void)lpThreadAttributes; - (void)dwCreationFlags; - (void)lpEnvironment; - (void)lpCurrentDirectory; - (void)lpStartupInfo; - _ASSERTE(lpCurrentDirectory == NULL); - _ASSERTE(lpProcessInformation != NULL); - - if (lpCommandLine == NULL) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - int commandLineLength = WideCharToMultiByte(CP_UTF8, 0, lpCommandLine, -1, NULL, 0, NULL, NULL); - if (commandLineLength == 0) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - NewArrayHolder commandLineUtf8 = new (nothrow) char[commandLineLength]; - if (commandLineUtf8 == NULL) - { - SetLastError(ERROR_OUTOFMEMORY); - return FALSE; - } - - if (WideCharToMultiByte(CP_UTF8, 0, lpCommandLine, -1, commandLineUtf8, commandLineLength, NULL, NULL) == 0) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - pid_t pid = fork(); - if (pid < 0) - { - SetLastError(ERROR_INTERNAL_ERROR); - return FALSE; - } - - if (pid == 0) - { - int argc = 0; - char** argv = SplitCommandLine(commandLineUtf8.GetValue(), &argc); - if (argv == NULL || argc == 0) - { - _exit(127); - } - - execvp(argv[0], argv); - _exit(127); - } - - HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, bInheritHandles, static_cast(pid)); - if (processHandle == NULL) - { - return FALSE; - } - - lpProcessInformation->hProcess = processHandle; - lpProcessInformation->hThread = NULL; - lpProcessInformation->dwProcessId = static_cast(pid); - lpProcessInformation->dwThreadId_PAL_Undefined = 0; - - return TRUE; -#else BOOL fResult; DWORD err; { @@ -229,7 +58,6 @@ WszCreateProcess( SetLastError(err); return fResult; -#endif } #ifndef HOST_UNIX From f2036070796eb5f2206e73ffdd8869a4e25db8cb Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sun, 3 May 2026 01:49:49 +0300 Subject: [PATCH 08/11] Add ifdef --- src/coreclr/utilcode/winfix.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/coreclr/utilcode/winfix.cpp b/src/coreclr/utilcode/winfix.cpp index 9738475350c94b..1a34a25872c847 100644 --- a/src/coreclr/utilcode/winfix.cpp +++ b/src/coreclr/utilcode/winfix.cpp @@ -1,9 +1,5 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -//***************************************************************************** -// WinWrap.cpp -// -//***************************************************************************** #include "stdafx.h" // Precompiled header key. #include "winwrap.h" // Header for macros and functions. @@ -13,6 +9,7 @@ // The only purpose of this function is to make a local copy of lpCommandLine. // Because windows implementation of CreateProcessW can actually change lpCommandLine, // but we'd like to keep it const. +#ifndef HOST_UNIX BOOL WszCreateProcess( LPCWSTR lpApplicationName, @@ -59,6 +56,7 @@ WszCreateProcess( SetLastError(err); return fResult; } +#endif // !HOST_UNIX #ifndef HOST_UNIX From 98f2328526354f89fc2d12f8bdb06ab6c7d9ca46 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Sun, 3 May 2026 02:20:20 +0300 Subject: [PATCH 09/11] Apply suggestions from code review Co-authored-by: Jan Kotas --- src/coreclr/utilcode/winfix.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/coreclr/utilcode/winfix.cpp b/src/coreclr/utilcode/winfix.cpp index 1a34a25872c847..2c5290b750acd8 100644 --- a/src/coreclr/utilcode/winfix.cpp +++ b/src/coreclr/utilcode/winfix.cpp @@ -6,10 +6,11 @@ #include "utilcode.h" #include "holder.h" +#ifndef HOST_UNIX + // The only purpose of this function is to make a local copy of lpCommandLine. // Because windows implementation of CreateProcessW can actually change lpCommandLine, // but we'd like to keep it const. -#ifndef HOST_UNIX BOOL WszCreateProcess( LPCWSTR lpApplicationName, @@ -56,10 +57,6 @@ WszCreateProcess( SetLastError(err); return fResult; } -#endif // !HOST_UNIX - -#ifndef HOST_UNIX - #include "psapi.h" #include "winnls.h" From 9152262db7c10dba723b915786d4e59cac54520c Mon Sep 17 00:00:00 2001 From: Adeel <3840695+am11@users.noreply.github.com> Date: Sun, 3 May 2026 02:27:41 +0300 Subject: [PATCH 10/11] Cleanups --- src/coreclr/debug/di/process.cpp | 258 +------------------------------ src/coreclr/debug/di/rsmain.cpp | 191 ++++------------------- src/coreclr/debug/di/rspriv.h | 46 ------ src/coreclr/debug/di/shimpriv.h | 18 +-- 4 files changed, 32 insertions(+), 481 deletions(-) diff --git a/src/coreclr/debug/di/process.cpp b/src/coreclr/debug/di/process.cpp index 3a126e446e03fd..b47cc24cf3b563 100644 --- a/src/coreclr/debug/di/process.cpp +++ b/src/coreclr/debug/di/process.cpp @@ -1065,95 +1065,6 @@ CordbProcess::~CordbProcess() // Set this to mark that we really did cleanup. } -//----------------------------------------------------------------------------- -// Static build helper. -// This will create a process under the pCordb root, and add it to the list. -// We don't return the process - caller gets the pid and looks it up under -// the Cordb object. -// -// Arguments: -// pCordb - Pointer to the implementation of the owning Cordb object implementing the -// owning ICD interface. -// szProgramName - Name of the program to execute. -// szProgramArgs - Command line arguments for the process. -// lpProcessAttributes - OS-specific attributes for process creation. -// lpThreadAttributes - OS-specific attributes for thread creation. -// fInheritFlags - OS-specific flag for child process inheritance. -// dwCreationFlags - OS-specific creation flags. -// lpEnvironment - OS-specific environmental strings. -// szCurrentDirectory - OS-specific string for directory to run in. -// lpStartupInfo - OS-specific info on startup. -// lpProcessInformation - OS-specific process information buffer. -// corDebugFlags - What type of process to create, currently always managed. -//----------------------------------------------------------------------------- -HRESULT ShimProcess::CreateProcess( - Cordb * pCordb, - ICorDebugRemoteTarget * pRemoteTarget, - LPCWSTR szProgramName, - _In_z_ LPWSTR szProgramArgs, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL fInheritHandles, - DWORD dwCreationFlags, - PVOID lpEnvironment, - LPCWSTR szCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation, - CorDebugCreateProcessFlags corDebugFlags -) -{ - _ASSERTE(pCordb != NULL); - -#if defined(FEATURE_DBGIPC_TRANSPORT_DI) - // The transport cannot deal with creating a suspended process (it needs the debugger to start up and - // listen for connections). - _ASSERTE((dwCreationFlags & CREATE_SUSPENDED) == 0); -#endif // FEATURE_DBGIPC_TRANSPORT_DI - - HRESULT hr = S_OK; - - RSExtSmartPtr pShim; - EX_TRY - { - pShim.Assign(new ShimProcess()); - - // Indicate that this process was started under the debugger as opposed to attaching later. - pShim->m_attached = false; - - hr = pShim->CreateAndStartWin32ET(pCordb); - IfFailThrow(hr); - - // Call out to newly created Win32-event Thread to create the process. - // If this succeeds, new CordbProcess will add a ref to the ShimProcess - hr = pShim->GetWin32EventThread()->SendCreateProcessEvent(pShim->GetMachineInfo(), - szProgramName, - szProgramArgs, - lpProcessAttributes, - lpThreadAttributes, - fInheritHandles, - dwCreationFlags, - lpEnvironment, - szCurrentDirectory, - lpStartupInfo, - lpProcessInformation, - corDebugFlags); - IfFailThrow(hr); - } - EX_CATCH_HRESULT(hr); - - // If this succeeds, then process takes ownership of thread. Else we need to kill it. - if (FAILED(hr)) - { - if (pShim != NULL) - { - pShim->Dispose(); - } - } - // Always release our ref to ShimProcess. If the Process was created, then it takes a reference. - - return hr; -} - //----------------------------------------------------------------------------- // Static build helper for the attach case. // On success, this will add the process to the pCordb list, and then @@ -10583,12 +10494,11 @@ HRESULT CordbRCEventThread::Stop() enum { W32ETA_NONE = 0, - W32ETA_CREATE_PROCESS = 1, - W32ETA_ATTACH_PROCESS = 2, - W32ETA_CONTINUE = 3, - W32ETA_DETACH = 4, + W32ETA_ATTACH_PROCESS = 1, + W32ETA_CONTINUE = 2, + W32ETA_DETACH = 3, #ifdef OUT_OF_PROCESS_SETTHREADCONTEXT - W32ETA_CAN_DETACH = 5 + W32ETA_CAN_DETACH = 4 #endif // OUT_OF_PROCESS_SETTHREADCONTEXT }; @@ -11737,11 +11647,6 @@ void CordbWin32EventThread::Win32EventLoop() ExitProcess(false); // not detach fEventAvailable = false; } - // Should we create a process? - else if (m_action == W32ETA_CREATE_PROCESS) - { - CreateProcess(); - } // Should we attach to a process? else if (m_action == W32ETA_ATTACH_PROCESS) { @@ -13890,161 +13795,6 @@ void CordbWin32EventThread::ForceDbgContinue(CordbProcess *pProcess, CordbUnmana } -// -// Send a CreateProcess event to the Win32 thread to have it create us -// a new process. -// -HRESULT CordbWin32EventThread::SendCreateProcessEvent( - MachineInfo machineInfo, - LPCWSTR programName, - _In_z_ LPWSTR programArgs, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - PVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation, - CorDebugCreateProcessFlags corDebugFlags) -{ - HRESULT hr = S_OK; - - LockSendToWin32EventThreadMutex(); - LOG((LF_CORDB, LL_EVERYTHING, "CordbWin32EventThread::SCPE Called\n")); - m_actionData.createData.machineInfo = machineInfo; - m_actionData.createData.programName = programName; - m_actionData.createData.programArgs = programArgs; - m_actionData.createData.lpProcessAttributes = lpProcessAttributes; - m_actionData.createData.lpThreadAttributes = lpThreadAttributes; - m_actionData.createData.bInheritHandles = bInheritHandles; - m_actionData.createData.dwCreationFlags = dwCreationFlags; - m_actionData.createData.lpEnvironment = lpEnvironment; - m_actionData.createData.lpCurrentDirectory = lpCurrentDirectory; - m_actionData.createData.lpStartupInfo = lpStartupInfo; - m_actionData.createData.lpProcessInformation = lpProcessInformation; - m_actionData.createData.corDebugFlags = corDebugFlags; - - // m_action is set last so that the win32 event thread can inspect - // it and take action without actually having to take any - // locks. The lock around this here is simply to prevent multiple - // threads from making requests at the same time. - m_action = W32ETA_CREATE_PROCESS; - - BOOL succ = SetEvent(m_threadControlEvent); - - if (succ) - { - DWORD ret = WaitForSingleObject(m_actionTakenEvent, INFINITE); - - LOG((LF_CORDB, LL_EVERYTHING, "Process Handle is: %x, m_threadControlEvent is %x\n", - (UINT_PTR)m_actionData.createData.lpProcessInformation->hProcess, (UINT_PTR)m_threadControlEvent)); - - if (ret == WAIT_OBJECT_0) - hr = m_actionResult; - else - hr = HRESULT_FROM_GetLastError(); - } - else - hr = HRESULT_FROM_GetLastError(); - - UnlockSendToWin32EventThreadMutex(); - - return hr; -} - - -//--------------------------------------------------------------------------------------- -// -// Create a process -// -// Assumptions: -// This occurs on the win32 event thread. It is invokved via -// a message sent from code:CordbWin32EventThread::SendCreateProcessEvent -// -// Notes: -// Create a new process. This is called in the context of the Win32 -// event thread to ensure that if we're Win32 debugging the process -// that the same thread that waits for debugging events will be the -// thread that creates the process. -// -//--------------------------------------------------------------------------------------- -void CordbWin32EventThread::CreateProcess() -{ - m_action = W32ETA_NONE; - HRESULT hr = S_OK; - - DWORD dwCreationFlags = m_actionData.createData.dwCreationFlags; - - // If the creation flags has DEBUG_PROCESS in them, then we're - // Win32 debugging this process. Otherwise, we have to create - // suspended to give us time to setup up our side of the IPC - // channel. - BOOL fInteropDebugging = -#if defined(FEATURE_INTEROP_DEBUGGING) - (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)); -#else - false; // Interop not supported. -#endif - - hr = E_NOTIMPL; - - if (SUCCEEDED(hr)) - { - // Process ID is filled in after process is successfully created. - DWORD dwProcessId = m_actionData.createData.lpProcessInformation->dwProcessId; - ProcessDescriptor pd = ProcessDescriptor::FromPid(dwProcessId); - - RSUnsafeExternalSmartPtr pProcess; - hr = m_pShim->InitializeDataTarget(&pd); - - if (SUCCEEDED(hr)) - { - // To emulate V2 semantics, we pass 0 for the clrInstanceID into - // OpenVirtualProcess. This will then connect to the first CLR - // loaded. - const ULONG64 cFirstClrLoaded = 0; - hr = CordbProcess::OpenVirtualProcess(cFirstClrLoaded, m_pShim->GetDataTarget(), NULL, m_cordb, &pd, m_pShim, &pProcess); - } - - // Shouldn't happen on a create, only an attach - _ASSERTE(hr != CORDBG_E_DEBUGGER_ALREADY_ATTACHED); - - // Remember the process in the global list of processes. - if (SUCCEEDED(hr)) - { - EX_TRY - { - // Mark if we're interop-debugging - if (fInteropDebugging) - { - pProcess->EnableInteropDebugging(); - } - - m_cordb->AddProcess(pProcess); // will take ref if it succeeds - } - EX_CATCH_HRESULT(hr); - } - - // If we're Win32 attached to this process, then increment the - // proper count, otherwise add this process to the wait set - // and resume the process's main thread. - if (SUCCEEDED(hr)) - { - _ASSERTE(m_pProcess == NULL); - m_pProcess.Assign(pProcess); - } - } - - - // - // Signal the hr to the caller. - // - m_actionResult = hr; - SetEvent(m_actionTakenEvent); -} - - // // Send a DebugActiveProcess event to the Win32 thread to have it attach to // a new process. diff --git a/src/coreclr/debug/di/rsmain.cpp b/src/coreclr/debug/di/rsmain.cpp index dea4f3f9c8991d..45ae339ad17152 100644 --- a/src/coreclr/debug/di/rsmain.cpp +++ b/src/coreclr/debug/di/rsmain.cpp @@ -1499,12 +1499,6 @@ HRESULT Cordb::SetUnmanagedHandler(ICorDebugUnmanagedCallback *pCallback) return S_OK; } -// CreateProcess() isn't supported on CoreCLR. -bool Cordb::IsCreateProcessSupported() -{ - return false; -} - // Given everything we know about our configuration, can we support interop-debugging bool Cordb::IsInteropDebuggingSupported() { @@ -1557,152 +1551,20 @@ HRESULT Cordb::CreateProcess(LPCWSTR lpApplicationName, CorDebugCreateProcessFlags debuggingFlags, ICorDebugProcess **ppProcess) { - return CreateProcessCommon(NULL, - lpApplicationName, - lpCommandLine, - lpProcessAttributes, - lpThreadAttributes, - bInheritHandles, - dwCreationFlags, - lpEnvironment, - lpCurrentDirectory, - lpStartupInfo, - lpProcessInformation, - debuggingFlags, - ppProcess); -} - -HRESULT Cordb::CreateProcessCommon(ICorDebugRemoteTarget * pRemoteTarget, - LPCWSTR lpApplicationName, - _In_z_ LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - PVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation, - CorDebugCreateProcessFlags debuggingFlags, - ICorDebugProcess ** ppProcess) -{ - // If you hit this assert, it means that you are attempting to create a process without specifying the version - // number. - _ASSERTE(CorDebugInvalidVersion != m_debuggerSpecifiedVersion); - - PUBLIC_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - VALIDATE_POINTER_TO_OBJECT(ppProcess, ICorDebugProcess**); - - HRESULT hr = S_OK; - - EX_TRY - { - if (!m_initialized) - { - ThrowHR(E_FAIL); - } - - // Check that we support the debugger version - CheckCompatibility(); - - #ifdef FEATURE_INTEROP_DEBUGGING - // DEBUG_PROCESS (=0x1) means debug this process & all future children. - // DEBUG_ONLY_THIS_PROCESS =(0x2) means just debug the immediate process. - // If we want to support DEBUG_PROCESS, then we need to have the RS sniff for new CREATE_PROCESS - // events and spawn new CordbProcess for them. - switch(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) - { - // 1) managed-only debugging - case 0: - break; - - // 2) failure - returns E_NOTIMPL. (as this would involve debugging all of our children processes). - case DEBUG_PROCESS: - ThrowHR(E_NOTIMPL); - - // 3) Interop-debugging. - // Note that MSDN (at least as of Jan 2003) is wrong about this flag. MSDN claims - // DEBUG_ONLY_THIS_PROCESS w/o DEBUG_PROCESS should be ignored. - // But it really should do launch as a debuggee (but not auto-attach to child processes). - case DEBUG_ONLY_THIS_PROCESS: - // Emprically, this is the common case for native / interop-debugging. - break; - - // 4) Interop. - // The spec for ICorDebug::CreateProcess says this is the one to use for interop-debugging. - case DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS: - // Win2k does not honor these flags properly. So we just use - // It treats (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS) as if it were DEBUG_PROCESS. - // We'll just always touch up the flags, even though WinXP and above is fine here. - // Per win2k issue, strip off DEBUG_PROCESS, so that we're just left w/ DEBUG_ONLY_THIS_PROCESS. - dwCreationFlags &= ~(DEBUG_PROCESS); - break; - - default: - UNREACHABLE(); - } - - #endif // FEATURE_INTEROP_DEBUGGING - - // Must have a managed-callback by now. - if ((m_managedCallback == NULL) || (m_managedCallback2 == NULL) || (m_managedCallback3 == NULL) || (m_managedCallback4 == NULL)) - { - ThrowHR(E_FAIL); - } - - if (!IsCreateProcessSupported()) - { - ThrowHR(E_NOTIMPL); - } - - if (!IsInteropDebuggingSupported() && - ((dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) != 0)) - { - ThrowHR(CORDBG_E_INTEROP_NOT_SUPPORTED); - } + (void)lpApplicationName; + (void)lpCommandLine; + (void)lpProcessAttributes; + (void)lpThreadAttributes; + (void)bInheritHandles; + (void)dwCreationFlags; + (void)lpEnvironment; + (void)lpCurrentDirectory; + (void)lpStartupInfo; + (void)lpProcessInformation; + (void)debuggingFlags; + (void)ppProcess; - // Check that we can even accept another debuggee before trying anything. - EnsureAllowAnotherProcess(); - - } EX_CATCH_HRESULT(hr); - if (FAILED(hr)) - { - return hr; - } - - hr = ShimProcess::CreateProcess(this, - pRemoteTarget, - lpApplicationName, - lpCommandLine, - lpProcessAttributes, - lpThreadAttributes, - bInheritHandles, - dwCreationFlags, - lpEnvironment, - lpCurrentDirectory, - lpStartupInfo, - lpProcessInformation, - debuggingFlags - ); - - LOG((LF_CORDB, LL_EVERYTHING, "Handle in Cordb::CreateProcess is: %.I64x\n", lpProcessInformation->hProcess)); - - if (SUCCEEDED(hr)) - { - LockProcessList(); - - CordbProcess * pProcess = GetProcessList()->GetBase(lpProcessInformation->dwProcessId); - - UnlockProcessList(); - - _ASSERTE(pProcess != NULL); - - pProcess->ExternalAddRef(); - *ppProcess = (ICorDebugProcess *)pProcess; - } - - return hr; + return E_NOTIMPL; } @@ -1725,19 +1587,20 @@ HRESULT Cordb::CreateProcessEx(ICorDebugRemoteTarget * pRemoteTarget, return E_INVALIDARG; } - return CreateProcessCommon(pRemoteTarget, - lpApplicationName, - lpCommandLine, - lpProcessAttributes, - lpThreadAttributes, - bInheritHandles, - dwCreationFlags, - lpEnvironment, - lpCurrentDirectory, - lpStartupInfo, - lpProcessInformation, - debuggingFlags, - ppProcess); + (void)lpApplicationName; + (void)lpCommandLine; + (void)lpProcessAttributes; + (void)lpThreadAttributes; + (void)bInheritHandles; + (void)dwCreationFlags; + (void)lpEnvironment; + (void)lpCurrentDirectory; + (void)lpStartupInfo; + (void)lpProcessInformation; + (void)debuggingFlags; + (void)ppProcess; + + return E_NOTIMPL; } diff --git a/src/coreclr/debug/di/rspriv.h b/src/coreclr/debug/di/rspriv.h index 0933ddd4fbacfa..69f2876a8c1650 100644 --- a/src/coreclr/debug/di/rspriv.h +++ b/src/coreclr/debug/di/rspriv.h @@ -2226,20 +2226,6 @@ class Cordb : public CordbBase, public ICorDebug, public ICorDebugRemote // Methods not exposed via a COM interface. //----------------------------------------------------------- - HRESULT CreateProcessCommon(ICorDebugRemoteTarget * pRemoteTarget, - LPCWSTR lpApplicationName, - _In_z_ LPWSTR lpCommandLine, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - PVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation, - CorDebugCreateProcessFlags debuggingFlags, - ICorDebugProcess **ppProcess); - HRESULT DebugActiveProcessCommon(ICorDebugRemoteTarget * pRemoteTarget, DWORD id, BOOL win32Attach, ICorDebugProcess **ppProcess); void EnsureCanLaunchOrAttach(BOOL fWin32DebuggingEnabled); @@ -2291,7 +2277,6 @@ class Cordb : public CordbBase, public ICorDebug, public ICorDebugRemote HMODULE GetTargetCLR() { return m_targetCLR; } private: - bool IsCreateProcessSupported(); bool IsInteropDebuggingSupported(); void CheckCompatibility(); @@ -10007,19 +9992,6 @@ class CordbWin32EventThread HRESULT Start(); HRESULT Stop(); - HRESULT SendCreateProcessEvent(MachineInfo machineInfo, - LPCWSTR programName, - _In_z_ LPWSTR programArgs, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - PVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation, - CorDebugCreateProcessFlags corDebugFlags); - HRESULT SendDebugActiveProcessEvent(MachineInfo machineInfo, const ProcessDescriptor *pProcessDescriptor, bool fWin32Attach, @@ -10070,8 +10042,6 @@ class CordbWin32EventThread void ThreadProc(); static DWORD WINAPI ThreadProc(LPVOID parameter); - void CreateProcess(); - INativeEventPipeline * m_pNativePipeline; @@ -10110,22 +10080,6 @@ class CordbWin32EventThread HRESULT m_actionResult; union { - struct - { - MachineInfo machineInfo; - LPCWSTR programName; - LPWSTR programArgs; - LPSECURITY_ATTRIBUTES lpProcessAttributes; - LPSECURITY_ATTRIBUTES lpThreadAttributes; - BOOL bInheritHandles; - DWORD dwCreationFlags; - PVOID lpEnvironment; - LPCWSTR lpCurrentDirectory; - LPSTARTUPINFOW lpStartupInfo; - LPPROCESS_INFORMATION lpProcessInformation; - CorDebugCreateProcessFlags corDebugFlags; - } createData; - struct { MachineInfo machineInfo; diff --git a/src/coreclr/debug/di/shimpriv.h b/src/coreclr/debug/di/shimpriv.h index 2bd2fd5b45bd9a..acbf2b5da4b522 100644 --- a/src/coreclr/debug/di/shimpriv.h +++ b/src/coreclr/debug/di/shimpriv.h @@ -352,7 +352,7 @@ class ShimProcess // Initialization phases. // 1. allocate new ShimProcess(). This lets us spin up a Win32 EventThread, which can then // be used to - // 2. Call ShimProcess::CreateProcess/DebugActiveProcess. This will call CreateAndStartWin32ET to + // 2. Call ShimProcess::DebugActiveProcess. This will call CreateAndStartWin32ET to // craete the w32et. // 3. Create OS-debugging pipeline. This establishes the physical OS process and gets us a pid/handle // 4. pShim->InitializeDataTarget - this creates a reader/writer abstraction around the OS process. @@ -364,22 +364,6 @@ class ShimProcess // Creation //----------------------------------------------------------- - static HRESULT CreateProcess( - Cordb * pCordb, - ICorDebugRemoteTarget * pRemoteTarget, - LPCWSTR programName, - _In_z_ LPWSTR programArgs, - LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, - BOOL bInheritHandles, - DWORD dwCreationFlags, - PVOID lpEnvironment, - LPCWSTR lpCurrentDirectory, - LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation, - CorDebugCreateProcessFlags corDebugFlags - ); - static HRESULT DebugActiveProcess( Cordb * pCordb, ICorDebugRemoteTarget * pRemoteTarget, From ed480ee2540905ecd55425cf31675b704738378b Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 4 May 2026 11:26:14 -0700 Subject: [PATCH 11/11] Apply suggestions from code review Co-authored-by: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com> --- src/coreclr/debug/di/rsmain.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/debug/di/rsmain.cpp b/src/coreclr/debug/di/rsmain.cpp index 45ae339ad17152..c7d5c5a9e24e49 100644 --- a/src/coreclr/debug/di/rsmain.cpp +++ b/src/coreclr/debug/di/rsmain.cpp @@ -1551,6 +1551,8 @@ HRESULT Cordb::CreateProcess(LPCWSTR lpApplicationName, CorDebugCreateProcessFlags debuggingFlags, ICorDebugProcess **ppProcess) { + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); (void)lpApplicationName; (void)lpCommandLine; (void)lpProcessAttributes; @@ -1587,6 +1589,8 @@ HRESULT Cordb::CreateProcessEx(ICorDebugRemoteTarget * pRemoteTarget, return E_INVALIDARG; } + PUBLIC_API_ENTRY(this); + FAIL_IF_NEUTERED(this); (void)lpApplicationName; (void)lpCommandLine; (void)lpProcessAttributes;