1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*++
6
7
8
9Module Name:
10
11 process.cpp
12
13Abstract:
14
15 Implementation of process object and functions related to processes.
16
17
18
19--*/
20
21#include "pal/dbgmsg.h"
22SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so do this first
23
24#include "pal/procobj.hpp"
25#include "pal/thread.hpp"
26#include "pal/file.hpp"
27#include "pal/handlemgr.hpp"
28#include "pal/module.h"
29#include "procprivate.hpp"
30#include "pal/palinternal.h"
31#include "pal/process.h"
32#include "pal/init.h"
33#include "pal/critsect.h"
34#include "pal/debug.h"
35#include "pal/utils.h"
36#include "pal/environ.h"
37#include "pal/virtual.h"
38#include "pal/stackstring.hpp"
39
40#include <errno.h>
41#if HAVE_POLL
42#include <poll.h>
43#else
44#include "pal/fakepoll.h"
45#endif // HAVE_POLL
46
47#include <unistd.h>
48#include <sys/mman.h>
49#include <sys/types.h>
50#include <sys/stat.h>
51#include <signal.h>
52#if HAVE_PRCTL_H
53#include <sys/prctl.h>
54#include <sys/syscall.h>
55#endif
56#include <sys/wait.h>
57#include <sys/time.h>
58#include <sys/resource.h>
59#include <debugmacrosext.h>
60#include <semaphore.h>
61#include <stdint.h>
62#include <dlfcn.h>
63
64#ifdef __linux__
65#include <sys/syscall.h> // __NR_membarrier
66// Ensure __NR_membarrier is defined for portable builds.
67# if !defined(__NR_membarrier)
68# if defined(__amd64__)
69# define __NR_membarrier 324
70# elif defined(__i386__)
71# define __NR_membarrier 375
72# elif defined(__arm__)
73# define __NR_membarrier 389
74# elif defined(__aarch64__)
75# define __NR_membarrier 283
76# elif
77# error Unknown architecture
78# endif
79# endif
80#endif
81
82#ifdef __APPLE__
83#include <sys/sysctl.h>
84#include <sys/posix_sem.h>
85#endif
86
87#ifdef __NetBSD__
88#include <sys/cdefs.h>
89#include <sys/param.h>
90#include <sys/sysctl.h>
91#include <kvm.h>
92#endif
93
94extern char *g_szCoreCLRPath;
95
96using namespace CorUnix;
97
98CObjectType CorUnix::otProcess(
99 otiProcess,
100 NULL, // No cleanup routine
101 NULL, // No initialization routine
102 0, // No immutable data
103 NULL, // No immutable data copy routine
104 NULL, // No immutable data cleanup routine
105 sizeof(CProcProcessLocalData),
106 NULL, // No process local data cleanup routine
107 0, // No shared data
108 PROCESS_ALL_ACCESS,
109 CObjectType::SecuritySupported,
110 CObjectType::SecurityInfoNotPersisted,
111 CObjectType::UnnamedObject,
112 CObjectType::CrossProcessDuplicationAllowed,
113 CObjectType::WaitableObject,
114 CObjectType::SingleTransitionObject,
115 CObjectType::ThreadReleaseHasNoSideEffects,
116 CObjectType::NoOwner
117 );
118
119//
120// Helper membarrier function
121//
122#ifdef __NR_membarrier
123# define membarrier(...) syscall(__NR_membarrier, __VA_ARGS__)
124#else
125# define membarrier(...) -ENOSYS
126#endif
127
128enum membarrier_cmd
129{
130 MEMBARRIER_CMD_QUERY = 0,
131 MEMBARRIER_CMD_GLOBAL = (1 << 0),
132 MEMBARRIER_CMD_GLOBAL_EXPEDITED = (1 << 1),
133 MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED = (1 << 2),
134 MEMBARRIER_CMD_PRIVATE_EXPEDITED = (1 << 3),
135 MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED = (1 << 4),
136 MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE = (1 << 5),
137 MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE = (1 << 6)
138};
139
140//
141// Tracks if the OS supports FlushProcessWriteBuffers using membarrier
142//
143static int s_flushUsingMemBarrier = 0;
144
145//
146// Helper memory page used by the FlushProcessWriteBuffers
147//
148static int* s_helperPage = 0;
149
150//
151// Mutex to make the FlushProcessWriteBuffersMutex thread safe
152//
153pthread_mutex_t flushProcessWriteBuffersMutex;
154
155CAllowedObjectTypes aotProcess(otiProcess);
156
157//
158// The representative IPalObject for this process
159//
160IPalObject* CorUnix::g_pobjProcess;
161
162//
163// Critical section that protects process data (e.g., the
164// list of active threads)/
165//
166CRITICAL_SECTION g_csProcess;
167
168//
169// List and count of active threads
170//
171CPalThread* CorUnix::pGThreadList;
172DWORD g_dwThreadCount;
173
174//
175// The command line and app name for the process
176//
177LPWSTR g_lpwstrCmdLine = NULL;
178LPWSTR g_lpwstrAppDir = NULL;
179
180// Thread ID of thread that has started the ExitProcess process
181Volatile<LONG> terminator = 0;
182
183// Process and session ID of this process.
184DWORD gPID = (DWORD) -1;
185DWORD gSID = (DWORD) -1;
186
187// Application group ID for this process
188#ifdef __APPLE__
189LPCSTR gApplicationGroupId = nullptr;
190int gApplicationGroupIdLength = 0;
191#endif // __APPLE__
192PathCharString* gSharedFilesPath = nullptr;
193
194// The lowest common supported semaphore length, including null character
195// NetBSD-7.99.25: 15 characters
196// MacOSX 10.11: 31 -- Core 1.0 RC2 compatibility
197#if defined(__NetBSD__)
198#define CLR_SEM_MAX_NAMELEN 15
199#elif defined(__APPLE__)
200#define CLR_SEM_MAX_NAMELEN PSEMNAMLEN
201#else
202#define CLR_SEM_MAX_NAMELEN (NAME_MAX - 4)
203#endif
204
205static_assert_no_msg(CLR_SEM_MAX_NAMELEN <= MAX_PATH);
206
207// Function to call during PAL/process shutdown/abort
208Volatile<PSHUTDOWN_CALLBACK> g_shutdownCallback = nullptr;
209
210// Crash dump generating program arguments. Initialized in PROCAbortInitialize().
211char* g_argvCreateDump[8] = { nullptr };
212
213//
214// Key used for associating CPalThread's with the underlying pthread
215// (through pthread_setspecific)
216//
217pthread_key_t CorUnix::thObjKey;
218
219static WCHAR W16_WHITESPACE[]= {0x0020, 0x0009, 0x000D, 0};
220static WCHAR W16_WHITESPACE_DQUOTE[]= {0x0020, 0x0009, 0x000D, '"', 0};
221
222enum FILETYPE
223{
224 FILE_ERROR,/*ERROR*/
225 FILE_UNIX, /*Unix Executable*/
226 FILE_DIR /*Directory*/
227};
228
229#pragma pack(push,1)
230// When creating the semaphore name on Mac running in a sandbox, We reference this structure as a byte array
231// in order to encode its data into a string. Its important to make sure there is no padding between the fields
232// and also at the end of the buffer. Hence, this structure is defined inside a pack(1)
233struct UnambiguousProcessDescriptor
234{
235 UnambiguousProcessDescriptor()
236 {
237 }
238
239 UnambiguousProcessDescriptor(DWORD processId, UINT64 disambiguationKey)
240 {
241 Init(processId, disambiguationKey);
242 }
243
244 void Init(DWORD processId, UINT64 disambiguationKey)
245 {
246 m_processId = processId;
247 m_disambiguationKey = disambiguationKey;
248 }
249 UINT64 m_disambiguationKey;
250 DWORD m_processId;
251};
252#pragma pack(pop)
253
254static
255DWORD
256PALAPI
257StartupHelperThread(
258 LPVOID p);
259
260static
261BOOL
262GetProcessIdDisambiguationKey(
263 IN DWORD processId,
264 OUT UINT64 *disambiguationKey);
265
266PAL_ERROR
267PROCGetProcessStatus(
268 CPalThread *pThread,
269 HANDLE hProcess,
270 PROCESS_STATE *pps,
271 DWORD *pdwExitCode);
272
273static
274void
275CreateSemaphoreName(
276 char semName[CLR_SEM_MAX_NAMELEN],
277 LPCSTR semaphoreName,
278 const UnambiguousProcessDescriptor& unambiguousProcessDescriptor,
279 LPCSTR applicationGroupId);
280
281static BOOL getFileName(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, PathCharString& lpFileName);
282static char ** buildArgv(LPCWSTR lpCommandLine, PathCharString& lpAppPath, UINT *pnArg);
283static BOOL getPath(PathCharString& lpFileName, PathCharString& lpPathFileName);
284static int checkFileType(LPCSTR lpFileName);
285static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUnconditionally);
286
287ProcessModules *GetProcessModulesFromHandle(IN HANDLE hProcess, OUT LPDWORD lpCount);
288ProcessModules *CreateProcessModules(IN DWORD dwProcessId, OUT LPDWORD lpCount);
289void DestroyProcessModules(IN ProcessModules *listHead);
290
291/*++
292Function:
293 GetCurrentProcessId
294
295See MSDN doc.
296--*/
297DWORD
298PALAPI
299GetCurrentProcessId(
300 VOID)
301{
302 PERF_ENTRY(GetCurrentProcessId);
303 ENTRY("GetCurrentProcessId()\n" );
304
305 LOGEXIT("GetCurrentProcessId returns DWORD %#x\n", gPID);
306 PERF_EXIT(GetCurrentProcessId);
307 return gPID;
308}
309
310
311/*++
312Function:
313 GetCurrentSessionId
314
315See MSDN doc.
316--*/
317DWORD
318PALAPI
319GetCurrentSessionId(
320 VOID)
321{
322 PERF_ENTRY(GetCurrentSessionId);
323 ENTRY("GetCurrentSessionId()\n" );
324
325 LOGEXIT("GetCurrentSessionId returns DWORD %#x\n", gSID);
326 PERF_EXIT(GetCurrentSessionId);
327 return gSID;
328}
329
330
331/*++
332Function:
333 GetCurrentProcess
334
335See MSDN doc.
336--*/
337HANDLE
338PALAPI
339GetCurrentProcess(
340 VOID)
341{
342 PERF_ENTRY(GetCurrentProcess);
343 ENTRY("GetCurrentProcess()\n" );
344
345 LOGEXIT("GetCurrentProcess returns HANDLE %p\n", hPseudoCurrentProcess);
346 PERF_EXIT(GetCurrentProcess);
347
348 /* return a pseudo handle */
349 return hPseudoCurrentProcess;
350}
351
352/*++
353Function:
354 CreateProcessA
355
356Note:
357 Only Standard handles need to be inherited.
358 Security attributes parameters are not used.
359
360See MSDN doc.
361--*/
362BOOL
363PALAPI
364CreateProcessA(
365 IN LPCSTR lpApplicationName,
366 IN LPSTR lpCommandLine,
367 IN LPSECURITY_ATTRIBUTES lpProcessAttributes,
368 IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
369 IN BOOL bInheritHandles,
370 IN DWORD dwCreationFlags,
371 IN LPVOID lpEnvironment,
372 IN LPCSTR lpCurrentDirectory,
373 IN LPSTARTUPINFOA lpStartupInfo,
374 OUT LPPROCESS_INFORMATION lpProcessInformation)
375{
376 PAL_ERROR palError = NO_ERROR;
377 CPalThread *pThread;
378 STARTUPINFOW StartupInfoW;
379 LPWSTR CommandLineW = NULL;
380 LPWSTR ApplicationNameW = NULL;
381 LPWSTR CurrentDirectoryW = NULL;
382
383 int n;
384
385 PERF_ENTRY(CreateProcessA);
386 ENTRY("CreateProcessA(lpAppName=%p (%s), lpCmdLine=%p (%s), lpProcessAttr=%p, "
387 "lpThreadAttr=%p, bInherit=%d, dwFlags=%#x, lpEnv=%p, "
388 "lpCurrentDir=%p (%s), lpStartupInfo=%p, lpProcessInfo=%p)\n",
389 lpApplicationName?lpApplicationName:"NULL",
390 lpApplicationName?lpApplicationName:"NULL",
391 lpCommandLine?lpCommandLine:"NULL",
392 lpCommandLine?lpCommandLine:"NULL",
393 lpProcessAttributes, lpThreadAttributes, bInheritHandles,
394 dwCreationFlags, lpEnvironment,
395 lpCurrentDirectory?lpCurrentDirectory:"NULL",
396 lpCurrentDirectory?lpCurrentDirectory:"NULL",
397 lpStartupInfo, lpProcessInformation);
398
399 pThread = InternalGetCurrentThread();
400
401 if(lpStartupInfo == NULL)
402 {
403 ASSERT("lpStartupInfo is NULL!\n");
404 palError = ERROR_INVALID_PARAMETER;
405 goto done;
406 }
407
408 /* convert parameters to Unicode */
409
410 if(lpApplicationName)
411 {
412 n = MultiByteToWideChar(CP_ACP, 0, lpApplicationName, -1, NULL, 0);
413 if(0 == n)
414 {
415 ASSERT("MultiByteToWideChar failed!\n");
416 palError = ERROR_INTERNAL_ERROR;
417 goto done;
418 }
419 ApplicationNameW = (LPWSTR)InternalMalloc(sizeof(WCHAR)*n);
420 if(!ApplicationNameW)
421 {
422 ERROR("malloc() failed!\n");
423 palError = ERROR_NOT_ENOUGH_MEMORY;
424 goto done;
425 }
426 MultiByteToWideChar(CP_ACP, 0, lpApplicationName, -1, ApplicationNameW,
427 n);
428 }
429
430 if(lpCommandLine)
431 {
432 n = MultiByteToWideChar(CP_ACP, 0, lpCommandLine, -1, NULL, 0);
433 if(0 == n)
434 {
435 ASSERT("MultiByteToWideChar failed!\n");
436 palError = ERROR_INTERNAL_ERROR;
437 goto done;
438 }
439 CommandLineW = (LPWSTR)InternalMalloc(sizeof(WCHAR)*n);
440 if(!CommandLineW)
441 {
442 ERROR("malloc() failed!\n");
443 palError = ERROR_NOT_ENOUGH_MEMORY;
444 goto done;
445 }
446 MultiByteToWideChar(CP_ACP, 0, lpCommandLine, -1, CommandLineW, n);
447 }
448
449 if(lpCurrentDirectory)
450 {
451 n = MultiByteToWideChar(CP_ACP, 0, lpCurrentDirectory, -1, NULL, 0);
452 if(0 == n)
453 {
454 ASSERT("MultiByteToWideChar failed!\n");
455 palError = ERROR_INTERNAL_ERROR;
456 goto done;
457 }
458 CurrentDirectoryW = (LPWSTR)InternalMalloc(sizeof(WCHAR)*n);
459 if(!CurrentDirectoryW)
460 {
461 ERROR("malloc() failed!\n");
462 palError = ERROR_NOT_ENOUGH_MEMORY;
463 goto done;
464 }
465 MultiByteToWideChar(CP_ACP, 0, lpCurrentDirectory, -1,
466 CurrentDirectoryW, n);
467 }
468
469 // lpEnvironment should remain ansi on the call to CreateProcessW
470
471 StartupInfoW.cb = sizeof StartupInfoW;
472 StartupInfoW.dwFlags = lpStartupInfo->dwFlags;
473 StartupInfoW.hStdError = lpStartupInfo->hStdError;
474 StartupInfoW.hStdInput = lpStartupInfo->hStdInput;
475 StartupInfoW.hStdOutput = lpStartupInfo->hStdOutput;
476 /* all other members are PAL_Undefined, we can ignore them */
477
478 palError = InternalCreateProcess(
479 pThread,
480 ApplicationNameW,
481 CommandLineW,
482 lpProcessAttributes,
483 lpThreadAttributes,
484 bInheritHandles,
485 dwCreationFlags,
486 lpEnvironment,
487 CurrentDirectoryW,
488 &StartupInfoW,
489 lpProcessInformation
490 );
491done:
492 free(ApplicationNameW);
493 free(CommandLineW);
494 free(CurrentDirectoryW);
495
496 if (NO_ERROR != palError)
497 {
498 pThread->SetLastError(palError);
499 }
500
501 LOGEXIT("CreateProcessA returns BOOL %d\n", NO_ERROR == palError);
502 PERF_EXIT(CreateProcessA);
503 return NO_ERROR == palError;
504}
505
506
507/*++
508Function:
509 CreateProcessW
510
511Note:
512 Only Standard handles need to be inherited.
513 Security attributes parameters are not used.
514
515See MSDN doc.
516--*/
517BOOL
518PALAPI
519CreateProcessW(
520 IN LPCWSTR lpApplicationName,
521 IN LPWSTR lpCommandLine,
522 IN LPSECURITY_ATTRIBUTES lpProcessAttributes,
523 IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
524 IN BOOL bInheritHandles,
525 IN DWORD dwCreationFlags,
526 IN LPVOID lpEnvironment,
527 IN LPCWSTR lpCurrentDirectory,
528 IN LPSTARTUPINFOW lpStartupInfo,
529 OUT LPPROCESS_INFORMATION lpProcessInformation)
530{
531 PAL_ERROR palError = NO_ERROR;
532 CPalThread *pThread;
533
534 PERF_ENTRY(CreateProcessW);
535 ENTRY("CreateProcessW(lpAppName=%p (%S), lpCmdLine=%p (%S), lpProcessAttr=%p,"
536 "lpThreadAttr=%p, bInherit=%d, dwFlags=%#x, lpEnv=%p,"
537 "lpCurrentDir=%p (%S), lpStartupInfo=%p, lpProcessInfo=%p)\n",
538 lpApplicationName?lpApplicationName:W16_NULLSTRING,
539 lpApplicationName?lpApplicationName:W16_NULLSTRING,
540 lpCommandLine?lpCommandLine:W16_NULLSTRING,
541 lpCommandLine?lpCommandLine:W16_NULLSTRING,lpProcessAttributes,
542 lpThreadAttributes, bInheritHandles, dwCreationFlags,lpEnvironment,
543 lpCurrentDirectory?lpCurrentDirectory:W16_NULLSTRING,
544 lpCurrentDirectory?lpCurrentDirectory:W16_NULLSTRING,
545 lpStartupInfo, lpProcessInformation);
546
547 pThread = InternalGetCurrentThread();
548
549 palError = InternalCreateProcess(
550 pThread,
551 lpApplicationName,
552 lpCommandLine,
553 lpProcessAttributes,
554 lpThreadAttributes,
555 bInheritHandles,
556 dwCreationFlags,
557 lpEnvironment,
558 lpCurrentDirectory,
559 lpStartupInfo,
560 lpProcessInformation
561 );
562
563 if (NO_ERROR != palError)
564 {
565 pThread->SetLastError(palError);
566 }
567
568 LOGEXIT("CreateProcessW returns BOOL %d\n", NO_ERROR == palError);
569 PERF_EXIT(CreateProcessW);
570
571 return NO_ERROR == palError;
572}
573
574PAL_ERROR
575PrepareStandardHandle(
576 CPalThread *pThread,
577 HANDLE hFile,
578 IPalObject **ppobjFile,
579 int *piFd
580 )
581{
582 PAL_ERROR palError = NO_ERROR;
583 IPalObject *pobjFile = NULL;
584 IDataLock *pDataLock = NULL;
585 CFileProcessLocalData *pLocalData = NULL;
586 int iError = 0;
587
588 palError = g_pObjectManager->ReferenceObjectByHandle(
589 pThread,
590 hFile,
591 &aotFile,
592 0,
593 &pobjFile
594 );
595
596 if (NO_ERROR != palError)
597 {
598 ERROR("Bad handle passed through CreateProcess\n");
599 goto PrepareStandardHandleExit;
600 }
601
602 palError = pobjFile->GetProcessLocalData(
603 pThread,
604 ReadLock,
605 &pDataLock,
606 reinterpret_cast<void **>(&pLocalData)
607 );
608
609 if (NO_ERROR != palError)
610 {
611 ASSERT("Unable to access file data\n");
612 goto PrepareStandardHandleExit;
613 }
614
615 //
616 // The passed in file needs to be inheritable
617 //
618
619 if (!pLocalData->inheritable)
620 {
621 ERROR("Non-inheritable handle passed through CreateProcess\n");
622 palError = ERROR_INVALID_HANDLE;
623 goto PrepareStandardHandleExit;
624 }
625
626 iError = fcntl(pLocalData->unix_fd, F_SETFD, 0);
627 if (-1 == iError)
628 {
629 ERROR("Unable to remove close-on-exec for file (errno %i)\n", errno);
630 palError = ERROR_INVALID_HANDLE;
631 goto PrepareStandardHandleExit;
632 }
633
634 *piFd = pLocalData->unix_fd;
635 pDataLock->ReleaseLock(pThread, FALSE);
636 pDataLock = NULL;
637
638 //
639 // Transfer pobjFile reference to out parameter
640 //
641
642 *ppobjFile = pobjFile;
643 pobjFile = NULL;
644
645PrepareStandardHandleExit:
646
647 if (NULL != pDataLock)
648 {
649 pDataLock->ReleaseLock(pThread, FALSE);
650 }
651
652 if (NULL != pobjFile)
653 {
654 pobjFile->ReleaseReference(pThread);
655 }
656
657 return palError;
658}
659
660PAL_ERROR
661CorUnix::InternalCreateProcess(
662 CPalThread *pThread,
663 LPCWSTR lpApplicationName,
664 LPWSTR lpCommandLine,
665 LPSECURITY_ATTRIBUTES lpProcessAttributes,
666 LPSECURITY_ATTRIBUTES lpThreadAttributes,
667 BOOL bInheritHandles,
668 DWORD dwCreationFlags,
669 LPVOID lpEnvironment,
670 LPCWSTR lpCurrentDirectory,
671 LPSTARTUPINFOW lpStartupInfo,
672 LPPROCESS_INFORMATION lpProcessInformation
673 )
674{
675 PAL_ERROR palError = NO_ERROR;
676 IPalObject *pobjProcess = NULL;
677 IPalObject *pobjProcessRegistered = NULL;
678 IDataLock *pLocalDataLock = NULL;
679 CProcProcessLocalData *pLocalData;
680 IDataLock *pSharedDataLock = NULL;
681 CPalThread *pDummyThread = NULL;
682 HANDLE hDummyThread = NULL;
683 HANDLE hProcess = NULL;
684 CObjectAttributes oa(NULL, lpProcessAttributes);
685
686 IPalObject *pobjFileIn = NULL;
687 int iFdIn = -1;
688 IPalObject *pobjFileOut = NULL;
689 int iFdOut = -1;
690 IPalObject *pobjFileErr = NULL;
691 int iFdErr = -1;
692
693 pid_t processId;
694 PathCharString lpFileNamePS;
695 char **lppArgv = NULL;
696 UINT nArg;
697 int iRet;
698 char **EnvironmentArray=NULL;
699 int child_blocking_pipe = -1;
700 int parent_blocking_pipe = -1;
701
702 /* Validate parameters */
703
704 /* note : specs indicate lpApplicationName should always
705 be NULL; however support for it is already implemented. Leaving the code
706 in, specs can change; but rejecting non-NULL for now to conform to the
707 spec. */
708 if( NULL != lpApplicationName )
709 {
710 ASSERT("lpApplicationName should be NULL, but is %S instead\n",
711 lpApplicationName);
712 palError = ERROR_INVALID_PARAMETER;
713 goto InternalCreateProcessExit;
714 }
715
716 if (0 != (dwCreationFlags & ~(CREATE_SUSPENDED|CREATE_NEW_CONSOLE)))
717 {
718 ASSERT("Unexpected creation flags (%#x)\n", dwCreationFlags);
719 palError = ERROR_INVALID_PARAMETER;
720 goto InternalCreateProcessExit;
721 }
722
723 /* Security attributes parameters are ignored */
724 if (lpProcessAttributes != NULL &&
725 (lpProcessAttributes->lpSecurityDescriptor != NULL ||
726 lpProcessAttributes->bInheritHandle != TRUE))
727 {
728 ASSERT("lpProcessAttributes is invalid, parameter ignored (%p)\n",
729 lpProcessAttributes);
730 palError = ERROR_INVALID_PARAMETER;
731 goto InternalCreateProcessExit;
732 }
733
734 if (lpThreadAttributes != NULL)
735 {
736 ASSERT("lpThreadAttributes parameter must be NULL (%p)\n",
737 lpThreadAttributes);
738 palError = ERROR_INVALID_PARAMETER;
739 goto InternalCreateProcessExit;
740 }
741
742 /* note : Win32 crashes in this case */
743 if(NULL == lpStartupInfo)
744 {
745 ERROR("lpStartupInfo is NULL\n");
746 palError = ERROR_INVALID_PARAMETER;
747 goto InternalCreateProcessExit;
748 }
749
750 /* Validate lpStartupInfo.cb field */
751 if (lpStartupInfo->cb < sizeof(STARTUPINFOW))
752 {
753 ASSERT("lpStartupInfo parameter structure size is invalid (%u)\n",
754 lpStartupInfo->cb);
755 palError = ERROR_INVALID_PARAMETER;
756 goto InternalCreateProcessExit;
757 }
758
759 /* lpStartupInfo should be either zero or STARTF_USESTDHANDLES */
760 if (lpStartupInfo->dwFlags & ~STARTF_USESTDHANDLES)
761 {
762 ASSERT("lpStartupInfo parameter invalid flags (%#x)\n",
763 lpStartupInfo->dwFlags);
764 palError = ERROR_INVALID_PARAMETER;
765 goto InternalCreateProcessExit;
766 }
767
768 /* validate given standard handles if we have any */
769 if (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES)
770 {
771 palError = PrepareStandardHandle(
772 pThread,
773 lpStartupInfo->hStdInput,
774 &pobjFileIn,
775 &iFdIn
776 );
777
778 if (NO_ERROR != palError)
779 {
780 goto InternalCreateProcessExit;
781 }
782
783 palError = PrepareStandardHandle(
784 pThread,
785 lpStartupInfo->hStdOutput,
786 &pobjFileOut,
787 &iFdOut
788 );
789
790 if (NO_ERROR != palError)
791 {
792 goto InternalCreateProcessExit;
793 }
794
795 palError = PrepareStandardHandle(
796 pThread,
797 lpStartupInfo->hStdError,
798 &pobjFileErr,
799 &iFdErr
800 );
801
802 if (NO_ERROR != palError)
803 {
804 goto InternalCreateProcessExit;
805 }
806 }
807
808 if (!getFileName(lpApplicationName, lpCommandLine, lpFileNamePS))
809 {
810 ERROR("Can't find executable!\n");
811 palError = ERROR_FILE_NOT_FOUND;
812 goto InternalCreateProcessExit;
813 }
814
815 /* check type of file */
816 iRet = checkFileType(lpFileNamePS);
817
818 switch (iRet)
819 {
820 case FILE_ERROR: /* file not found, or not an executable */
821 WARN ("File is not valid (%s)", lpFileNamePS.GetString());
822 palError = ERROR_FILE_NOT_FOUND;
823 goto InternalCreateProcessExit;
824
825 case FILE_UNIX: /* Unix binary file */
826 break; /* nothing to do */
827
828 case FILE_DIR:/*Directory*/
829 WARN ("File is a Directory (%s)", lpFileNamePS.GetString());
830 palError = ERROR_ACCESS_DENIED;
831 goto InternalCreateProcessExit;
832 break;
833
834 default: /* not supposed to get here */
835 ASSERT ("Invalid return type from checkFileType");
836 palError = ERROR_FILE_NOT_FOUND;
837 goto InternalCreateProcessExit;
838 }
839
840 /* build Argument list, lppArgv is allocated in buildArgv function and
841 requires to be freed */
842 lppArgv = buildArgv(lpCommandLine, lpFileNamePS, &nArg);
843
844 /* set the Environment variable */
845 if (lpEnvironment != NULL)
846 {
847 unsigned i;
848 // Since CREATE_UNICODE_ENVIRONMENT isn't supported we know the string is ansi
849 unsigned EnvironmentEntries = 0;
850 // Convert the environment block to array of strings
851 // Count the number of entries
852 // Is it a string that contains null terminated string, the end is delimited
853 // by two null in a row.
854 for (i = 0; ((char *)lpEnvironment)[i]!='\0'; i++)
855 {
856 EnvironmentEntries ++;
857 for (;((char *)lpEnvironment)[i]!='\0'; i++)
858 {
859 }
860 }
861 EnvironmentEntries++;
862 EnvironmentArray = (char **)InternalMalloc(EnvironmentEntries * sizeof(char *));
863
864 EnvironmentEntries = 0;
865 // Convert the environment block to array of strings
866 // Count the number of entries
867 // Is it a string that contains null terminated string, the end is delimited
868 // by two null in a row.
869 for (i = 0; ((char *)lpEnvironment)[i]!='\0'; i++)
870 {
871 EnvironmentArray[EnvironmentEntries] = &((char *)lpEnvironment)[i];
872 EnvironmentEntries ++;
873 for (;((char *)lpEnvironment)[i]!='\0'; i++)
874 {
875 }
876 }
877 EnvironmentArray[EnvironmentEntries] = NULL;
878 }
879
880 //
881 // Allocate and register the process object for the new process
882 //
883
884 palError = g_pObjectManager->AllocateObject(
885 pThread,
886 &otProcess,
887 &oa,
888 &pobjProcess
889 );
890
891 if (NO_ERROR != palError)
892 {
893 ERROR("Unable to allocate object for new proccess\n");
894 goto InternalCreateProcessExit;
895 }
896
897 palError = g_pObjectManager->RegisterObject(
898 pThread,
899 pobjProcess,
900 &aotProcess,
901 PROCESS_ALL_ACCESS,
902 &hProcess,
903 &pobjProcessRegistered
904 );
905
906 //
907 // pobjProcess is invalidated by the above call, so
908 // NULL it out here
909 //
910
911 pobjProcess = NULL;
912
913 if (NO_ERROR != palError)
914 {
915 ERROR("Unable to register new process object\n");
916 goto InternalCreateProcessExit;
917 }
918
919 //
920 // Create a new "dummy" thread object
921 //
922
923 palError = InternalCreateDummyThread(
924 pThread,
925 lpThreadAttributes,
926 &pDummyThread,
927 &hDummyThread
928 );
929
930 if (dwCreationFlags & CREATE_SUSPENDED)
931 {
932 int pipe_descs[2];
933
934 if (-1 == pipe(pipe_descs))
935 {
936 ERROR("pipe() failed! error is %d (%s)\n", errno, strerror(errno));
937 palError = ERROR_NOT_ENOUGH_MEMORY;
938 goto InternalCreateProcessExit;
939 }
940
941 /* [0] is read end, [1] is write end */
942 pDummyThread->suspensionInfo.SetBlockingPipe(pipe_descs[1]);
943 parent_blocking_pipe = pipe_descs[1];
944 child_blocking_pipe = pipe_descs[0];
945 }
946
947 palError = pobjProcessRegistered->GetProcessLocalData(
948 pThread,
949 WriteLock,
950 &pLocalDataLock,
951 reinterpret_cast<void **>(&pLocalData)
952 );
953
954 if (NO_ERROR != palError)
955 {
956 ASSERT("Unable to obtain local data for new process object\n");
957 goto InternalCreateProcessExit;
958 }
959
960
961 /* fork the new process */
962 processId = fork();
963
964 if (processId == -1)
965 {
966 ASSERT("Unable to create a new process with fork()\n");
967 if (-1 != child_blocking_pipe)
968 {
969 close(child_blocking_pipe);
970 close(parent_blocking_pipe);
971 }
972
973 palError = ERROR_INTERNAL_ERROR;
974 goto InternalCreateProcessExit;
975 }
976
977 /* From the time the child process begins running, to when it reaches execve,
978 the child process is not a real PAL process and does not own any PAL
979 resources, although it has access to the PAL resources of its parent process.
980 Thus, while the child process is in this window, it is dangerous for it to affect
981 its parent's PAL resources. As a consequence, no PAL code should be used
982 in this window; all code should make unix calls. Note the use of _exit
983 instead of exit to avoid calling PAL_Terminate and the lack of TRACE's and
984 ASSERT's. */
985
986 if (processId == 0) /* child process */
987 {
988 // At this point, the PAL should be considered uninitialized for this child process.
989
990 // Don't want to enter the init_critsec here since we're trying to avoid
991 // calling PAL functions. Furthermore, nothing should be changing
992 // the init_count in the child process at this point since this is the only
993 // thread executing.
994 init_count = 0;
995
996 sigset_t sm;
997
998 //
999 // Clear out the signal mask for the new process.
1000 //
1001
1002 sigemptyset(&sm);
1003 iRet = sigprocmask(SIG_SETMASK, &sm, NULL);
1004 if (iRet != 0)
1005 {
1006 _exit(EXIT_FAILURE);
1007 }
1008
1009 if (dwCreationFlags & CREATE_SUSPENDED)
1010 {
1011 BYTE resume_code = 0;
1012 ssize_t read_ret;
1013
1014 /* close the write end of the pipe, the child doesn't need it */
1015 close(parent_blocking_pipe);
1016
1017 read_again:
1018 /* block until ResumeThread writes something to the pipe */
1019 read_ret = read(child_blocking_pipe, &resume_code, sizeof(resume_code));
1020 if (sizeof(resume_code) != read_ret)
1021 {
1022 if (read_ret == -1 && EINTR == errno)
1023 {
1024 goto read_again;
1025 }
1026 else
1027 {
1028 /* note : read might return 0 (and return EAGAIN) if the other
1029 end of the pipe gets closed - for example because the parent
1030 process dies (very) abruptly */
1031 _exit(EXIT_FAILURE);
1032 }
1033 }
1034 if (WAKEUPCODE != resume_code)
1035 {
1036 // resume_code should always equal WAKEUPCODE.
1037 _exit(EXIT_FAILURE);
1038 }
1039
1040 close(child_blocking_pipe);
1041 }
1042
1043 /* Set the current directory */
1044 if (lpCurrentDirectory)
1045 {
1046 SetCurrentDirectoryW(lpCurrentDirectory);
1047 }
1048
1049 /* Set the standard handles to the incoming values */
1050 if (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES)
1051 {
1052 /* For each handle, we need to duplicate the incoming unix
1053 fd to the corresponding standard one. The API that I use,
1054 dup2, will copy the source to the destination, automatically
1055 closing the existing destination, in an atomic way */
1056 if (dup2(iFdIn, STDIN_FILENO) == -1)
1057 {
1058 // Didn't duplicate standard in.
1059 _exit(EXIT_FAILURE);
1060 }
1061
1062 if (dup2(iFdOut, STDOUT_FILENO) == -1)
1063 {
1064 // Didn't duplicate standard out.
1065 _exit(EXIT_FAILURE);
1066 }
1067
1068 if (dup2(iFdErr, STDERR_FILENO) == -1)
1069 {
1070 // Didn't duplicate standard error.
1071 _exit(EXIT_FAILURE);
1072 }
1073
1074 /* now close the original FDs, we don't need them anymore */
1075 close(iFdIn);
1076 close(iFdOut);
1077 close(iFdErr);
1078 }
1079
1080 /* execute the new process */
1081
1082 if (EnvironmentArray)
1083 {
1084 execve(lpFileNamePS, lppArgv, EnvironmentArray);
1085 }
1086 else
1087 {
1088 execve(lpFileNamePS, lppArgv, palEnvironment);
1089 }
1090
1091 /* if we get here, it means the execve function call failed so just exit */
1092 _exit(EXIT_FAILURE);
1093 }
1094
1095 /* parent process */
1096
1097 /* close the read end of the pipe, the parent doesn't need it */
1098 close(child_blocking_pipe);
1099
1100 /* Set the process ID */
1101 pLocalData->dwProcessId = processId;
1102 pLocalDataLock->ReleaseLock(pThread, TRUE);
1103 pLocalDataLock = NULL;
1104
1105 //
1106 // Release file handle info; we don't need them anymore. Note that
1107 // this must happen after we've released the data locks, as
1108 // otherwise a deadlock could result.
1109 //
1110
1111 if (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES)
1112 {
1113 pobjFileIn->ReleaseReference(pThread);
1114 pobjFileIn = NULL;
1115 pobjFileOut->ReleaseReference(pThread);
1116 pobjFileOut = NULL;
1117 pobjFileErr->ReleaseReference(pThread);
1118 pobjFileErr = NULL;
1119 }
1120
1121 /* fill PROCESS_INFORMATION strucutre */
1122 lpProcessInformation->hProcess = hProcess;
1123 lpProcessInformation->hThread = hDummyThread;
1124 lpProcessInformation->dwProcessId = processId;
1125 lpProcessInformation->dwThreadId_PAL_Undefined = 0;
1126
1127
1128 TRACE("New process created: id=%#x\n", processId);
1129
1130InternalCreateProcessExit:
1131
1132 if (NULL != pLocalDataLock)
1133 {
1134 pLocalDataLock->ReleaseLock(pThread, FALSE);
1135 }
1136
1137 if (NULL != pSharedDataLock)
1138 {
1139 pSharedDataLock->ReleaseLock(pThread, FALSE);
1140 }
1141
1142 if (NULL != pobjProcess)
1143 {
1144 pobjProcess->ReleaseReference(pThread);
1145 }
1146
1147 if (NULL != pobjProcessRegistered)
1148 {
1149 pobjProcessRegistered->ReleaseReference(pThread);
1150 }
1151
1152 if (NO_ERROR != palError)
1153 {
1154 if (NULL != hProcess)
1155 {
1156 g_pObjectManager->RevokeHandle(pThread, hProcess);
1157 }
1158
1159 if (NULL != hDummyThread)
1160 {
1161 g_pObjectManager->RevokeHandle(pThread, hDummyThread);
1162 }
1163 }
1164
1165 if (EnvironmentArray)
1166 {
1167 free(EnvironmentArray);
1168 }
1169
1170 /* if we still have the file structures at this point, it means we
1171 encountered an error sometime between when we acquired them and when we
1172 fork()ed. We not only have to release them, we have to give them back
1173 their close-on-exec flag */
1174 if (NULL != pobjFileIn)
1175 {
1176 if(-1 == fcntl(iFdIn, F_SETFD, 1))
1177 {
1178 WARN("couldn't restore close-on-exec flag to stdin descriptor! "
1179 "errno is %d (%s)\n", errno, strerror(errno));
1180 }
1181 pobjFileIn->ReleaseReference(pThread);
1182 }
1183
1184 if (NULL != pobjFileOut)
1185 {
1186 if(-1 == fcntl(iFdOut, F_SETFD, 1))
1187 {
1188 WARN("couldn't restore close-on-exec flag to stdout descriptor! "
1189 "errno is %d (%s)\n", errno, strerror(errno));
1190 }
1191 pobjFileOut->ReleaseReference(pThread);
1192 }
1193
1194 if (NULL != pobjFileErr)
1195 {
1196 if(-1 == fcntl(iFdErr, F_SETFD, 1))
1197 {
1198 WARN("couldn't restore close-on-exec flag to stderr descriptor! "
1199 "errno is %d (%s)\n", errno, strerror(errno));
1200 }
1201 pobjFileErr->ReleaseReference(pThread);
1202 }
1203
1204 /* free allocated memory */
1205 if (lppArgv)
1206 {
1207 free(*lppArgv);
1208 free(lppArgv);
1209 }
1210
1211 return palError;
1212}
1213
1214
1215/*++
1216Function:
1217 GetExitCodeProcess
1218
1219See MSDN doc.
1220--*/
1221BOOL
1222PALAPI
1223GetExitCodeProcess(
1224 IN HANDLE hProcess,
1225 IN LPDWORD lpExitCode)
1226{
1227 CPalThread *pThread;
1228 PAL_ERROR palError = NO_ERROR;
1229 DWORD dwExitCode;
1230 PROCESS_STATE ps;
1231
1232 PERF_ENTRY(GetExitCodeProcess);
1233 ENTRY("GetExitCodeProcess(hProcess = %p, lpExitCode = %p)\n",
1234 hProcess, lpExitCode);
1235
1236 pThread = InternalGetCurrentThread();
1237
1238 if(NULL == lpExitCode)
1239 {
1240 WARN("Got NULL lpExitCode\n");
1241 palError = ERROR_INVALID_PARAMETER;
1242 goto done;
1243 }
1244
1245 palError = PROCGetProcessStatus(
1246 pThread,
1247 hProcess,
1248 &ps,
1249 &dwExitCode
1250 );
1251
1252 if (NO_ERROR != palError)
1253 {
1254 ASSERT("Couldn't get process status information!\n");
1255 goto done;
1256 }
1257
1258 if( PS_DONE == ps )
1259 {
1260 *lpExitCode = dwExitCode;
1261 }
1262 else
1263 {
1264 *lpExitCode = STILL_ACTIVE;
1265 }
1266
1267done:
1268
1269 if (NO_ERROR != palError)
1270 {
1271 pThread->SetLastError(palError);
1272 }
1273
1274 LOGEXIT("GetExitCodeProcess returns BOOL %d\n", NO_ERROR == palError);
1275 PERF_EXIT(GetExitCodeProcess);
1276
1277 return NO_ERROR == palError;
1278}
1279
1280/*++
1281Function:
1282 ExitProcess
1283
1284See MSDN doc.
1285--*/
1286PAL_NORETURN
1287VOID
1288PALAPI
1289ExitProcess(
1290 IN UINT uExitCode)
1291{
1292 DWORD old_terminator;
1293
1294 PERF_ENTRY_ONLY(ExitProcess);
1295 ENTRY("ExitProcess(uExitCode=0x%x)\n", uExitCode );
1296
1297 old_terminator = InterlockedCompareExchange(&terminator, GetCurrentThreadId(), 0);
1298
1299 if (GetCurrentThreadId() == old_terminator)
1300 {
1301 // This thread has already initiated termination. This can happen
1302 // in two ways:
1303 // 1) DllMain(DLL_PROCESS_DETACH) triggers a call to ExitProcess.
1304 // 2) PAL_exit() is called after the last PALTerminate().
1305 // If the PAL is still initialized, we go straight through to
1306 // PROCEndProcess. If it isn't, we simply exit.
1307 if (!PALIsInitialized())
1308 {
1309 exit(uExitCode);
1310 ASSERT("exit has returned\n");
1311 }
1312 else
1313 {
1314 WARN("thread re-called ExitProcess\n");
1315 PROCEndProcess(GetCurrentProcess(), uExitCode, FALSE);
1316 }
1317 }
1318 else if (0 != old_terminator)
1319 {
1320 /* another thread has already initiated the termination process. we
1321 could just block on the PALInitLock critical section, but then
1322 PROCSuspendOtherThreads would hang... so sleep forever here, we're
1323 terminating anyway
1324
1325 Update: [TODO] PROCSuspendOtherThreads has been removed. Can this
1326 code be changed? */
1327 WARN("termination already started from another thread; blocking.\n");
1328 poll(NULL, 0, INFTIM);
1329 }
1330
1331 /* ExitProcess may be called even if PAL is not initialized.
1332 Verify if process structure exist
1333 */
1334 if (PALInitLock() && PALIsInitialized())
1335 {
1336 PROCEndProcess(GetCurrentProcess(), uExitCode, FALSE);
1337
1338 /* Should not get here, because we terminate the current process */
1339 ASSERT("PROCEndProcess has returned\n");
1340 }
1341 else
1342 {
1343 exit(uExitCode);
1344
1345 /* Should not get here, because we terminate the current process */
1346 ASSERT("exit has returned\n");
1347 }
1348
1349 /* this should never get executed */
1350 ASSERT("ExitProcess should not return!\n");
1351 for (;;);
1352}
1353
1354/*++
1355Function:
1356 TerminateProcess
1357
1358Note:
1359 hProcess is a handle on the current process.
1360
1361See MSDN doc.
1362--*/
1363BOOL
1364PALAPI
1365TerminateProcess(
1366 IN HANDLE hProcess,
1367 IN UINT uExitCode)
1368{
1369 BOOL ret;
1370
1371 PERF_ENTRY(TerminateProcess);
1372 ENTRY("TerminateProcess(hProcess=%p, uExitCode=%u)\n",hProcess, uExitCode );
1373
1374 ret = PROCEndProcess(hProcess, uExitCode, TRUE);
1375
1376 LOGEXIT("TerminateProcess returns BOOL %d\n", ret);
1377 PERF_EXIT(TerminateProcess);
1378 return ret;
1379}
1380
1381/*++
1382Function:
1383 RaiseFailFastException
1384
1385See MSDN doc.
1386--*/
1387VOID
1388PALAPI
1389RaiseFailFastException(
1390 IN PEXCEPTION_RECORD pExceptionRecord,
1391 IN PCONTEXT pContextRecord,
1392 IN DWORD dwFlags)
1393{
1394 PERF_ENTRY(RaiseFailFastException);
1395 ENTRY("RaiseFailFastException");
1396
1397 TerminateCurrentProcessNoExit(TRUE);
1398 PROCAbort();
1399
1400 LOGEXIT("RaiseFailFastException");
1401 PERF_EXIT(RaiseFailFastException);
1402}
1403
1404/*++
1405Function:
1406 PROCEndProcess
1407
1408 Called from TerminateProcess and ExitProcess. This does the work of
1409 TerminateProcess, but also takes a flag that determines whether we
1410 shut down unconditionally. If the flag is set, the PAL will do very
1411 little extra work before exiting. Most importantly, it won't shut
1412 down any DLLs that are loaded.
1413
1414--*/
1415static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUnconditionally)
1416{
1417 DWORD dwProcessId;
1418 BOOL ret = FALSE;
1419
1420 dwProcessId = PROCGetProcessIDFromHandle(hProcess);
1421 if (dwProcessId == 0)
1422 {
1423 SetLastError(ERROR_INVALID_HANDLE);
1424 }
1425 else if(dwProcessId != GetCurrentProcessId())
1426 {
1427 if (uExitCode != 0)
1428 WARN("exit code 0x%x ignored for external process.\n", uExitCode);
1429
1430 if (kill(dwProcessId, SIGKILL) == 0)
1431 {
1432 ret = TRUE;
1433 }
1434 else
1435 {
1436 switch (errno) {
1437 case ESRCH:
1438 SetLastError(ERROR_INVALID_HANDLE);
1439 break;
1440 case EPERM:
1441 SetLastError(ERROR_ACCESS_DENIED);
1442 break;
1443 default:
1444 // Unexpected failure.
1445 ASSERT(FALSE);
1446 SetLastError(ERROR_INTERNAL_ERROR);
1447 break;
1448 }
1449 }
1450 }
1451 else
1452 {
1453 // WARN/ERROR before starting the termination process and/or leaving the PAL.
1454 if (bTerminateUnconditionally)
1455 {
1456 WARN("exit code 0x%x ignored for terminate.\n", uExitCode);
1457 }
1458 else if ((uExitCode & 0xff) != uExitCode)
1459 {
1460 // TODO: Convert uExitCodes into sysexits(3)?
1461 ERROR("exit() only supports the lower 8-bits of an exit code. "
1462 "status will only see error 0x%x instead of 0x%x.\n", uExitCode & 0xff, uExitCode);
1463 }
1464
1465 TerminateCurrentProcessNoExit(bTerminateUnconditionally);
1466
1467 LOGEXIT("PROCEndProcess will not return\n");
1468
1469 // exit() runs atexit handlers possibly registered by foreign code.
1470 // The right thing to do here is to leave the PAL. If our client
1471 // registered our own PAL_Terminate with atexit(), the latter will
1472 // explicitly re-enter us.
1473 PAL_Leave(PAL_BoundaryBottom);
1474
1475 if (bTerminateUnconditionally)
1476 {
1477 // abort() has the semantics that
1478 // (1) it doesn't run atexit handlers
1479 // (2) can invoke CrashReporter or produce a coredump,
1480 // which is appropriate for TerminateProcess calls
1481 PROCAbort();
1482 }
1483 else
1484 {
1485 exit(uExitCode);
1486 }
1487
1488 ASSERT(FALSE); // we shouldn't get here
1489 }
1490
1491 return ret;
1492}
1493
1494/*++
1495Function:
1496 PAL_SetShutdownCallback
1497
1498Abstract:
1499 Sets a callback that is executed when the PAL is shut down because of
1500 ExitProcess, TerminateProcess or PAL_Shutdown but not PAL_Terminate/Ex.
1501
1502 NOTE: Currently only one callback can be set at a time.
1503--*/
1504PALIMPORT
1505VOID
1506PALAPI
1507PAL_SetShutdownCallback(
1508 IN PSHUTDOWN_CALLBACK callback)
1509{
1510 _ASSERTE(g_shutdownCallback == nullptr);
1511 g_shutdownCallback = callback;
1512}
1513
1514static bool IsCoreClrModule(const char* pModulePath)
1515{
1516 // Strip off everything up to and including the last slash in the path to get name
1517 const char* pModuleName = pModulePath;
1518 while (strchr(pModuleName, '/') != NULL)
1519 {
1520 pModuleName = strchr(pModuleName, '/');
1521 pModuleName++; // pass the slash
1522 }
1523
1524 return _stricmp(pModuleName, MAKEDLLNAME_A("coreclr")) == 0;
1525}
1526
1527// Build the semaphore names using the PID and a value that can be used for distinguishing
1528// between processes with the same PID (which ran at different times). This is to avoid
1529// cases where a prior process with the same PID exited abnormally without having a chance
1530// to clean up its semaphore.
1531// Note to anyone modifying these names in the future: Semaphore names on OS X are limited
1532// to SEM_NAME_LEN characters, including null. SEM_NAME_LEN is 31 (at least on OS X 10.11).
1533// NetBSD limits semaphore names to 15 characters, including null (at least up to 7.99.25).
1534// Keep 31 length for Core 1.0 RC2 compatibility
1535#if defined(__NetBSD__)
1536static const char* RuntimeSemaphoreNameFormat = "/clr%s%08llx";
1537#else
1538static const char* RuntimeSemaphoreNameFormat = "/clr%s%08x%016llx";
1539#endif
1540
1541static const char* RuntimeStartupSemaphoreName = "st";
1542static const char* RuntimeContinueSemaphoreName = "co";
1543
1544#if defined(__NetBSD__)
1545static uint64_t HashSemaphoreName(uint64_t a, uint64_t b)
1546{
1547 return (a ^ b) & 0xffffffff;
1548}
1549#else
1550#define HashSemaphoreName(a,b) a,b
1551#endif
1552
1553static const char* PipeNameFormat = "clr-debug-pipe-%d-%llu-%s";
1554
1555class PAL_RuntimeStartupHelper
1556{
1557 LONG m_ref;
1558 bool m_canceled;
1559 PPAL_STARTUP_CALLBACK m_callback;
1560 PVOID m_parameter;
1561 DWORD m_threadId;
1562 HANDLE m_threadHandle;
1563 DWORD m_processId;
1564#ifdef __APPLE__
1565 char m_applicationGroupId[MAX_APPLICATION_GROUP_ID_LENGTH+1];
1566#endif // __APPLE__
1567 char m_startupSemName[CLR_SEM_MAX_NAMELEN];
1568 char m_continueSemName[CLR_SEM_MAX_NAMELEN];
1569
1570 // A value that, used in conjunction with the process ID, uniquely identifies a process.
1571 // See the format we use for debugger semaphore names for why this is necessary.
1572 UINT64 m_processIdDisambiguationKey;
1573
1574 // Debugger waits on this semaphore and the runtime signals it on startup.
1575 sem_t *m_startupSem;
1576
1577 // Debuggee waits on this semaphore and the debugger signals it after the startup callback
1578 // registered (m_callback) returns.
1579 sem_t *m_continueSem;
1580
1581 LPCSTR GetApplicationGroupId() const
1582 {
1583#ifdef __APPLE__
1584 return m_applicationGroupId[0] == '\0' ? nullptr : m_applicationGroupId;
1585#else // __APPLE__
1586 return nullptr;
1587#endif // __APPLE__
1588 }
1589
1590public:
1591 PAL_RuntimeStartupHelper(DWORD dwProcessId, PPAL_STARTUP_CALLBACK pfnCallback, PVOID parameter) :
1592 m_ref(1),
1593 m_canceled(false),
1594 m_callback(pfnCallback),
1595 m_parameter(parameter),
1596 m_threadId(0),
1597 m_threadHandle(NULL),
1598 m_processId(dwProcessId),
1599 m_startupSem(SEM_FAILED),
1600 m_continueSem(SEM_FAILED)
1601 {
1602 }
1603
1604 ~PAL_RuntimeStartupHelper()
1605 {
1606 if (m_startupSem != SEM_FAILED)
1607 {
1608 sem_close(m_startupSem);
1609 sem_unlink(m_startupSemName);
1610 }
1611
1612 if (m_continueSem != SEM_FAILED)
1613 {
1614 sem_close(m_continueSem);
1615 sem_unlink(m_continueSemName);
1616 }
1617
1618 if (m_threadHandle != NULL)
1619 {
1620 CloseHandle(m_threadHandle);
1621 }
1622 }
1623
1624 LONG AddRef()
1625 {
1626 LONG ref = InterlockedIncrement(&m_ref);
1627 return ref;
1628 }
1629
1630 LONG Release()
1631 {
1632 LONG ref = InterlockedDecrement(&m_ref);
1633 if (ref == 0)
1634 {
1635 InternalDelete(this);
1636 }
1637 return ref;
1638 }
1639
1640 PAL_ERROR GetSemError()
1641 {
1642 PAL_ERROR pe;
1643 switch (errno)
1644 {
1645 case ENOENT:
1646 pe = ERROR_NOT_FOUND;
1647 break;
1648 case EACCES:
1649 pe = ERROR_INVALID_ACCESS;
1650 break;
1651 case EINVAL:
1652 case ENAMETOOLONG:
1653 pe = ERROR_INVALID_NAME;
1654 break;
1655 case ENOMEM:
1656 pe = ERROR_OUTOFMEMORY;
1657 break;
1658 case EEXIST:
1659 pe = ERROR_ALREADY_EXISTS;
1660 break;
1661 case ENOSPC:
1662 pe = ERROR_TOO_MANY_SEMAPHORES;
1663 break;
1664 default:
1665 pe = ERROR_INVALID_PARAMETER;
1666 break;
1667 }
1668 return pe;
1669 }
1670
1671 PAL_ERROR Register(LPCWSTR lpApplicationGroupId)
1672 {
1673 CPalThread *pThread = InternalGetCurrentThread();
1674 PAL_ERROR pe = NO_ERROR;
1675 BOOL ret;
1676 UnambiguousProcessDescriptor unambiguousProcessDescriptor;
1677
1678#ifdef __APPLE__
1679 if (lpApplicationGroupId != NULL)
1680 {
1681 /* Convert to ASCII */
1682 int applicationGroupIdLength = WideCharToMultiByte(CP_ACP, 0, lpApplicationGroupId, -1, m_applicationGroupId, sizeof(m_applicationGroupId), NULL, NULL);
1683 if (applicationGroupIdLength == 0)
1684 {
1685 pe = GetLastError();
1686 TRACE("applicationGroupId: Failed to convert to multibyte string (%u)\n", pe);
1687 if (pe == ERROR_INSUFFICIENT_BUFFER)
1688 {
1689 pe = ERROR_BAD_LENGTH;
1690 }
1691 goto exit;
1692 }
1693 }
1694 else
1695 {
1696 // Indicate that group ID is not being used
1697 m_applicationGroupId[0] = '\0';
1698 }
1699#endif // __APPLE__
1700
1701 // See semaphore name format for details about this value. We store it so that
1702 // it can be used by the cleanup code that removes the semaphore with sem_unlink.
1703 ret = GetProcessIdDisambiguationKey(m_processId, &m_processIdDisambiguationKey);
1704
1705 // If GetProcessIdDisambiguationKey failed for some reason, it should set the value
1706 // to 0. We expect that anyone else opening the semaphore name will also fail and thus
1707 // will also try to use 0 as the value.
1708 _ASSERTE(ret == TRUE || m_processIdDisambiguationKey == 0);
1709
1710 unambiguousProcessDescriptor.Init(m_processId, m_processIdDisambiguationKey);
1711 CreateSemaphoreName(m_startupSemName, RuntimeStartupSemaphoreName, unambiguousProcessDescriptor, GetApplicationGroupId());
1712 CreateSemaphoreName(m_continueSemName, RuntimeContinueSemaphoreName, unambiguousProcessDescriptor, GetApplicationGroupId());
1713
1714 TRACE("PAL_RuntimeStartupHelper.Register creating startup '%s' continue '%s'\n", m_startupSemName, m_continueSemName);
1715
1716 // Create the continue semaphore first so we don't race with PAL_NotifyRuntimeStarted. This open will fail if another
1717 // debugger is trying to attach to this process because the name will already exist.
1718 m_continueSem = sem_open(m_continueSemName, O_CREAT | O_EXCL, S_IRWXU, 0);
1719 if (m_continueSem == SEM_FAILED)
1720 {
1721 TRACE("sem_open(continue) failed: errno is %d (%s)\n", errno, strerror(errno));
1722 pe = GetSemError();
1723 goto exit;
1724 }
1725
1726 // Create the debuggee startup semaphore so the runtime (debuggee) knows to wait for a debugger connection.
1727 m_startupSem = sem_open(m_startupSemName, O_CREAT | O_EXCL, S_IRWXU, 0);
1728 if (m_startupSem == SEM_FAILED)
1729 {
1730 TRACE("sem_open(startup) failed: errno is %d (%s)\n", errno, strerror(errno));
1731 pe = GetSemError();
1732 goto exit;
1733 }
1734
1735 // Add a reference for the thread handler
1736 AddRef();
1737
1738 pe = InternalCreateThread(
1739 pThread,
1740 NULL,
1741 0,
1742 ::StartupHelperThread,
1743 this,
1744 0,
1745 UserCreatedThread,
1746 &m_threadId,
1747 &m_threadHandle);
1748
1749 if (NO_ERROR != pe)
1750 {
1751 TRACE("InternalCreateThread failed %d\n", pe);
1752 Release();
1753 goto exit;
1754 }
1755
1756 exit:
1757 return pe;
1758 }
1759
1760 void Unregister()
1761 {
1762 m_canceled = true;
1763
1764 // Tell the runtime to continue
1765 if (sem_post(m_continueSem) != 0)
1766 {
1767 ASSERT("sem_post(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno));
1768 }
1769
1770 // Tell the worker thread to continue
1771 if (sem_post(m_startupSem) != 0)
1772 {
1773 ASSERT("sem_post(startupSem) failed: errno is %d (%s)\n", errno, strerror(errno));
1774 }
1775
1776 // Don't need to wait for the worker thread if unregister called on it
1777 if (m_threadId != (DWORD)THREADSilentGetCurrentThreadId())
1778 {
1779 // Wait for work thread to exit
1780 if (WaitForSingleObject(m_threadHandle, INFINITE) != WAIT_OBJECT_0)
1781 {
1782 ASSERT("WaitForSingleObject\n");
1783 }
1784 }
1785 }
1786
1787 //
1788 // There are a couple race conditions that need to be considered here:
1789 //
1790 // * On launch, between the fork and execv in the PAL's CreateProcess where the target process
1791 // may contain a coreclr module image if the debugger process is running managed code. This
1792 // makes just checking if the coreclr module exists not enough.
1793 //
1794 // * On launch (after the execv) or attach when the coreclr is loaded but before the DAC globals
1795 // table is initialized where it is too soon to use/initialize the DAC on the debugger side.
1796 //
1797 // They are both fixed by check if the one of transport pipe files has been created.
1798 //
1799 bool IsCoreClrProcessReady()
1800 {
1801 char pipeName[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH];
1802
1803 PAL_GetTransportPipeName(pipeName, m_processId, GetApplicationGroupId(), "in");
1804
1805 struct stat buf;
1806 if (stat(pipeName, &buf) == 0)
1807 {
1808 TRACE("IsCoreClrProcessReady: stat(%s) SUCCEEDED\n", pipeName);
1809 return true;
1810 }
1811 TRACE("IsCoreClrProcessReady: stat(%s) FAILED: errno is %d (%s)\n", pipeName, errno, strerror(errno));
1812 return false;
1813 }
1814
1815 PAL_ERROR InvokeStartupCallback()
1816 {
1817 ProcessModules *listHead = NULL;
1818 PAL_ERROR pe = NO_ERROR;
1819 DWORD count;
1820
1821 if (m_canceled)
1822 {
1823 goto exit;
1824 }
1825
1826 // Enumerate all the modules in the process and invoke the callback
1827 // for the coreclr module if found.
1828 listHead = CreateProcessModules(m_processId, &count);
1829 if (listHead == NULL)
1830 {
1831 TRACE("CreateProcessModules failed for pid %d\n", m_processId);
1832 pe = ERROR_INVALID_PARAMETER;
1833 goto exit;
1834 }
1835
1836 for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
1837 {
1838 if (IsCoreClrModule(entry->Name))
1839 {
1840 PAL_CPP_TRY
1841 {
1842 TRACE("InvokeStartupCallback executing callback %p %s\n", entry->BaseAddress, entry->Name);
1843 m_callback(entry->Name, entry->BaseAddress, m_parameter);
1844 }
1845 PAL_CPP_CATCH_ALL
1846 {
1847 }
1848 PAL_CPP_ENDTRY
1849
1850 // Currently only the first coreclr module in a process is supported
1851 break;
1852 }
1853 }
1854
1855 exit:
1856 // Wake up the runtime
1857 if (sem_post(m_continueSem) != 0)
1858 {
1859 ASSERT("sem_post(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno));
1860 }
1861 if (listHead != NULL)
1862 {
1863 DestroyProcessModules(listHead);
1864 }
1865 return pe;
1866 }
1867
1868 void StartupHelperThread()
1869 {
1870 PAL_ERROR pe = NO_ERROR;
1871
1872 if (IsCoreClrProcessReady())
1873 {
1874 pe = InvokeStartupCallback();
1875 }
1876 else {
1877 TRACE("sem_wait(startup)\n");
1878
1879 // Wait until the coreclr runtime (debuggee) starts up
1880 if (sem_wait(m_startupSem) == 0)
1881 {
1882 pe = InvokeStartupCallback();
1883 }
1884 else
1885 {
1886 TRACE("sem_wait(startup) failed: errno is %d (%s)\n", errno, strerror(errno));
1887 pe = GetSemError();
1888 }
1889 }
1890
1891 // Invoke the callback on errors
1892 if (pe != NO_ERROR && !m_canceled)
1893 {
1894 SetLastError(pe);
1895 m_callback(NULL, NULL, m_parameter);
1896 }
1897 }
1898};
1899
1900static
1901DWORD
1902PALAPI
1903StartupHelperThread(LPVOID p)
1904{
1905 TRACE("PAL's StartupHelperThread starting\n");
1906
1907 PAL_RuntimeStartupHelper *helper = (PAL_RuntimeStartupHelper *)p;
1908 helper->StartupHelperThread();
1909 helper->Release();
1910 return 0;
1911}
1912
1913/*++
1914 PAL_RegisterForRuntimeStartup
1915
1916Parameters:
1917 dwProcessId - process id of runtime process
1918 lpApplicationGroupId - A string representing the application group ID of a sandboxed
1919 process running in Mac. Pass NULL if the process is not
1920 running in a sandbox and other platforms.
1921 pfnCallback - function to callback for coreclr module found
1922 parameter - data to pass to callback
1923 ppUnregisterToken - pointer to put PAL_UnregisterForRuntimeStartup token.
1924
1925Return value:
1926 PAL_ERROR
1927
1928Note:
1929 If the modulePath or hModule is NULL when the callback is invoked, an error occured
1930 and GetLastError() will return the Win32 error code.
1931
1932 The callback is always invoked on a separate thread and this API returns immediately.
1933
1934 Only the first coreclr module is currently supported.
1935
1936--*/
1937DWORD
1938PALAPI
1939PAL_RegisterForRuntimeStartup(
1940 IN DWORD dwProcessId,
1941 IN LPCWSTR lpApplicationGroupId,
1942 IN PPAL_STARTUP_CALLBACK pfnCallback,
1943 IN PVOID parameter,
1944 OUT PVOID *ppUnregisterToken)
1945{
1946 _ASSERTE(pfnCallback != NULL);
1947 _ASSERTE(ppUnregisterToken != NULL);
1948
1949 PAL_RuntimeStartupHelper *helper = InternalNew<PAL_RuntimeStartupHelper>(dwProcessId, pfnCallback, parameter);
1950
1951 // Create the debuggee startup semaphore so the runtime (debuggee) knows to wait for
1952 // a debugger connection.
1953 PAL_ERROR pe = helper->Register(lpApplicationGroupId);
1954 if (NO_ERROR != pe)
1955 {
1956 helper->Release();
1957 helper = NULL;
1958 }
1959
1960 *ppUnregisterToken = helper;
1961 return pe;
1962}
1963
1964/*++
1965 PAL_UnregisterForRuntimeStartup
1966
1967 Stops/cancels startup notification. This API can be called in the startup callback. Otherwise,
1968 it will block until the callback thread finishes and no more callbacks will be initiated after
1969 this API returns.
1970
1971Parameters:
1972 dwUnregisterToken - token from PAL_RegisterForRuntimeStartup or NULL.
1973
1974Return value:
1975 PAL_ERROR
1976--*/
1977DWORD
1978PALAPI
1979PAL_UnregisterForRuntimeStartup(
1980 IN PVOID pUnregisterToken)
1981{
1982 if (pUnregisterToken != NULL)
1983 {
1984 PAL_RuntimeStartupHelper *helper = (PAL_RuntimeStartupHelper *)pUnregisterToken;
1985 helper->Unregister();
1986 helper->Release();
1987 }
1988 return NO_ERROR;
1989}
1990
1991/*++
1992 PAL_NotifyRuntimeStarted
1993
1994 Signals the debugger waiting for runtime startup notification to continue and
1995 waits until the debugger signals us to continue.
1996
1997Parameters:
1998 None
1999
2000Return value:
2001 TRUE - successfully launched by debugger, FALSE - not launched or some failure in the handshake
2002--*/
2003BOOL
2004PALAPI
2005PAL_NotifyRuntimeStarted()
2006{
2007 char startupSemName[CLR_SEM_MAX_NAMELEN];
2008 char continueSemName[CLR_SEM_MAX_NAMELEN];
2009 sem_t *startupSem = SEM_FAILED;
2010 sem_t *continueSem = SEM_FAILED;
2011 BOOL launched = FALSE;
2012
2013 UINT64 processIdDisambiguationKey = 0;
2014 BOOL ret = GetProcessIdDisambiguationKey(gPID, &processIdDisambiguationKey);
2015
2016 // If GetProcessIdDisambiguationKey failed for some reason, it should set the value
2017 // to 0. We expect that anyone else making the semaphore name will also fail and thus
2018 // will also try to use 0 as the value.
2019 _ASSERTE(ret == TRUE || processIdDisambiguationKey == 0);
2020
2021 UnambiguousProcessDescriptor unambiguousProcessDescriptor(gPID, processIdDisambiguationKey);
2022 LPCSTR applicationGroupId =
2023#ifdef __APPLE__
2024 PAL_GetApplicationGroupId();
2025#else
2026 nullptr;
2027#endif
2028 CreateSemaphoreName(startupSemName, RuntimeStartupSemaphoreName, unambiguousProcessDescriptor, applicationGroupId);
2029 CreateSemaphoreName(continueSemName, RuntimeContinueSemaphoreName, unambiguousProcessDescriptor, applicationGroupId);
2030
2031 TRACE("PAL_NotifyRuntimeStarted opening continue '%s' startup '%s'\n", continueSemName, startupSemName);
2032
2033 // Open the debugger startup semaphore. If it doesn't exists, then we do nothing and return
2034 startupSem = sem_open(startupSemName, 0);
2035 if (startupSem == SEM_FAILED)
2036 {
2037 TRACE("sem_open(%s) failed: %d (%s)\n", startupSemName, errno, strerror(errno));
2038 goto exit;
2039 }
2040
2041 continueSem = sem_open(continueSemName, 0);
2042 if (continueSem == SEM_FAILED)
2043 {
2044 ASSERT("sem_open(%s) failed: %d (%s)\n", continueSemName, errno, strerror(errno));
2045 goto exit;
2046 }
2047
2048 // Wake up the debugger waiting for startup
2049 if (sem_post(startupSem) != 0)
2050 {
2051 ASSERT("sem_post(startupSem) failed: errno is %d (%s)\n", errno, strerror(errno));
2052 goto exit;
2053 }
2054
2055 // Now wait until the debugger's runtime startup notification is finished
2056 if (sem_wait(continueSem) != 0)
2057 {
2058 ASSERT("sem_wait(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno));
2059 goto exit;
2060 }
2061
2062 // Returns that the runtime was successfully launched for debugging
2063 launched = TRUE;
2064
2065exit:
2066 if (startupSem != SEM_FAILED)
2067 {
2068 sem_close(startupSem);
2069 }
2070 if (continueSem != SEM_FAILED)
2071 {
2072 sem_close(continueSem);
2073 }
2074 return launched;
2075}
2076
2077#ifdef __APPLE__
2078LPCSTR
2079PALAPI
2080PAL_GetApplicationGroupId()
2081{
2082 return gApplicationGroupId;
2083}
2084
2085// We use 7bits from each byte, so this computes the extra size we need to encode a given byte count
2086constexpr int GetExtraEncodedAreaSize(UINT rawByteCount)
2087{
2088 return (rawByteCount+6)/7;
2089}
2090const int SEMAPHORE_ENCODED_NAME_EXTRA_LENGTH = GetExtraEncodedAreaSize(sizeof(UnambiguousProcessDescriptor));
2091const int SEMAPHORE_ENCODED_NAME_LENGTH =
2092 sizeof(UnambiguousProcessDescriptor) + /* For process ID + disambiguationKey */
2093 SEMAPHORE_ENCODED_NAME_EXTRA_LENGTH; /* For base 255 extra encoding space */
2094
2095static_assert_no_msg(MAX_APPLICATION_GROUP_ID_LENGTH
2096 + 1 /* For / */
2097 + 2 /* For ST/CO name prefix */
2098 + SEMAPHORE_ENCODED_NAME_LENGTH /* For encoded name string */
2099 + 1 /* For null terminator */
2100 <= CLR_SEM_MAX_NAMELEN);
2101
2102// In Apple we are limited by the length of the semaphore name. However, the characters which can be used in the
2103// name can be anything between 1 and 255 (since 0 will terminate the string). Thus, we encode each byte b in
2104// unambiguousProcessDescriptor as b ? b : 1, and mark an additional bit indicating if b is 0 or not. We use 7 bits
2105// out of each extra byte so 1 bit will always be '1'. This will ensure that our extra bytes are never 0 which are
2106// invalid characters. Thus we need an extra byte for each 7 input bytes. Hence, only extra 2 bytes for the name string.
2107void EncodeSemaphoreName(char *encodedSemName, const UnambiguousProcessDescriptor& unambiguousProcessDescriptor)
2108{
2109 const unsigned char *buffer = (const unsigned char *)&unambiguousProcessDescriptor;
2110 char *extraEncodingBits = encodedSemName + sizeof(UnambiguousProcessDescriptor);
2111
2112 // Reset the extra encoding bit area
2113 for (int i=0; i<SEMAPHORE_ENCODED_NAME_EXTRA_LENGTH; i++)
2114 {
2115 extraEncodingBits[i] = 0x80;
2116 }
2117
2118 // Encode each byte in unambiguousProcessDescriptor
2119 for (int i=0; i<sizeof(UnambiguousProcessDescriptor); i++)
2120 {
2121 unsigned char b = buffer[i];
2122 encodedSemName[i] = b ? b : 1;
2123 extraEncodingBits[i/7] |= (b ? 0 : 1) << (i%7);
2124 }
2125}
2126#endif
2127
2128void CreateSemaphoreName(char semName[CLR_SEM_MAX_NAMELEN], LPCSTR semaphoreName, const UnambiguousProcessDescriptor& unambiguousProcessDescriptor, LPCSTR applicationGroupId)
2129{
2130 int length = 0;
2131
2132#ifdef __APPLE__
2133 if (applicationGroupId != nullptr)
2134 {
2135 // We assume here that applicationGroupId has been already tested for length and is less than MAX_APPLICATION_GROUP_ID_LENGTH
2136 length = sprintf_s(semName, CLR_SEM_MAX_NAMELEN, "%s/%s", applicationGroupId, semaphoreName);
2137 _ASSERTE(length > 0 && length < CLR_SEM_MAX_NAMELEN);
2138
2139 EncodeSemaphoreName(semName+length, unambiguousProcessDescriptor);
2140 length += SEMAPHORE_ENCODED_NAME_LENGTH;
2141 semName[length] = 0;
2142 }
2143 else
2144#endif // __APPLE__
2145 {
2146 length = sprintf_s(
2147 semName,
2148 CLR_SEM_MAX_NAMELEN,
2149 RuntimeSemaphoreNameFormat,
2150 semaphoreName,
2151 HashSemaphoreName(unambiguousProcessDescriptor.m_processId, unambiguousProcessDescriptor.m_disambiguationKey));
2152 }
2153
2154 _ASSERTE(length > 0 && length < CLR_SEM_MAX_NAMELEN );
2155}
2156
2157/*++
2158 Function:
2159 GetProcessIdDisambiguationKey
2160
2161 Get a numeric value that can be used to disambiguate between processes with the same PID,
2162 provided that one of them is still running. The numeric value can mean different things
2163 on different platforms, so it should not be used for any other purpose. Under the hood,
2164 it is implemented based on the creation time of the process.
2165--*/
2166BOOL
2167GetProcessIdDisambiguationKey(DWORD processId, UINT64 *disambiguationKey)
2168{
2169 if (disambiguationKey == nullptr)
2170 {
2171 _ASSERTE(!"disambiguationKey argument cannot be null!");
2172 return FALSE;
2173 }
2174
2175 *disambiguationKey = 0;
2176
2177#if defined(__APPLE__)
2178
2179 // On OS X, we return the process start time expressed in Unix time (the number of seconds
2180 // since the start of the Unix epoch).
2181 struct kinfo_proc info = {};
2182 size_t size = sizeof(info);
2183 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, processId };
2184 int ret = ::sysctl(mib, sizeof(mib)/sizeof(*mib), &info, &size, nullptr, 0);
2185
2186 if (ret == 0)
2187 {
2188 timeval procStartTime = info.kp_proc.p_starttime;
2189 long secondsSinceEpoch = procStartTime.tv_sec;
2190
2191 *disambiguationKey = secondsSinceEpoch;
2192 return TRUE;
2193 }
2194 else
2195 {
2196 _ASSERTE(!"Failed to get start time of a process.");
2197 return FALSE;
2198 }
2199
2200#elif defined(__NetBSD__)
2201
2202 // On NetBSD, we return the process start time expressed in Unix time (the number of seconds
2203 // since the start of the Unix epoch).
2204 kvm_t *kd;
2205 int cnt;
2206 struct kinfo_proc2 *info;
2207
2208 kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, "kvm_open");
2209 if (kd == nullptr)
2210 {
2211 _ASSERTE(!"Failed to get start time of a process.");
2212 return FALSE;
2213 }
2214
2215 info = kvm_getproc2(kd, KERN_PROC_PID, processId, sizeof(struct kinfo_proc2), &cnt);
2216 if (info == nullptr || cnt < 1)
2217 {
2218 kvm_close(kd);
2219 _ASSERTE(!"Failed to get start time of a process.");
2220 return FALSE;
2221 }
2222
2223 kvm_close(kd);
2224
2225 long secondsSinceEpoch = info->p_ustart_sec;
2226 *disambiguationKey = secondsSinceEpoch;
2227
2228 return TRUE;
2229
2230#elif HAVE_PROCFS_STAT
2231
2232 // Here we read /proc/<pid>/stat file to get the start time for the process.
2233 // We return this value (which is expressed in jiffies since boot time).
2234
2235 // Making something like: /proc/123/stat
2236 char statFileName[64];
2237
2238 INDEBUG(int chars = )
2239 snprintf(statFileName, sizeof(statFileName), "/proc/%d/stat", processId);
2240 _ASSERTE(chars > 0 && chars <= sizeof(statFileName));
2241
2242 FILE *statFile = fopen(statFileName, "r");
2243 if (statFile == nullptr)
2244 {
2245 TRACE("GetProcessIdDisambiguationKey: fopen() FAILED");
2246 SetLastError(ERROR_INVALID_HANDLE);
2247 return FALSE;
2248 }
2249
2250 char *line = nullptr;
2251 size_t lineLen = 0;
2252 if (getline(&line, &lineLen, statFile) == -1)
2253 {
2254 TRACE("GetProcessIdDisambiguationKey: getline() FAILED");
2255 SetLastError(ERROR_INVALID_HANDLE);
2256 return FALSE;
2257 }
2258
2259 unsigned long long starttime;
2260
2261 // According to `man proc`, the second field in the stat file is the filename of the executable,
2262 // in parentheses. Tokenizing the stat file using spaces as separators breaks when that name
2263 // has spaces in it, so we start using sscanf_s after skipping everything up to and including the
2264 // last closing paren and the space after it.
2265 char *scanStartPosition = strrchr(line, ')') + 2;
2266
2267 // All the format specifiers for the fields in the stat file are provided by 'man proc'.
2268 int sscanfRet = sscanf_s(scanStartPosition,
2269 "%*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %*lu %*lu %*ld %*ld %*ld %*ld %*ld %*ld %llu \n",
2270 &starttime);
2271
2272 if (sscanfRet != 1)
2273 {
2274 _ASSERTE(!"Failed to parse stat file contents with sscanf_s.");
2275 return FALSE;
2276 }
2277
2278 free(line);
2279 fclose(statFile);
2280
2281 *disambiguationKey = starttime;
2282 return TRUE;
2283
2284#else
2285 // If this is not OS X and we don't have /proc, we just return FALSE.
2286 WARN("GetProcessIdDisambiguationKey was called but is not implemented on this platform!");
2287 return FALSE;
2288#endif
2289}
2290
2291/*++
2292 Function:
2293 PAL_GetTransportPipeName
2294
2295 Builds the transport pipe names from the process id.
2296--*/
2297VOID
2298PALAPI
2299PAL_GetTransportPipeName(
2300 OUT char *name,
2301 IN DWORD id,
2302 IN const char *applicationGroupId,
2303 IN const char *suffix)
2304{
2305 *name = '\0';
2306 DWORD dwRetVal = 0;
2307 UINT64 disambiguationKey = 0;
2308 PathCharString formatBufferString;
2309 BOOL ret = GetProcessIdDisambiguationKey(id, &disambiguationKey);
2310 char *formatBuffer = formatBufferString.OpenStringBuffer(MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH-1);
2311 if (formatBuffer == nullptr)
2312 {
2313 ERROR("Out Of Memory");
2314 return;
2315 }
2316
2317 // If GetProcessIdDisambiguationKey failed for some reason, it should set the value
2318 // to 0. We expect that anyone else making the pipe name will also fail and thus will
2319 // also try to use 0 as the value.
2320 _ASSERTE(ret == TRUE || disambiguationKey == 0);
2321#ifdef __APPLE__
2322 if (nullptr != applicationGroupId)
2323 {
2324 // Verify the length of the application group ID
2325 int applicationGroupIdLength = strlen(applicationGroupId);
2326 if (applicationGroupIdLength > MAX_APPLICATION_GROUP_ID_LENGTH)
2327 {
2328 ERROR("The length of applicationGroupId is larger than MAX_APPLICATION_GROUP_ID_LENGTH");
2329 return;
2330 }
2331
2332 // In sandbox, all IPC files (locks, pipes) should be written to the application group
2333 // container. The path returned by GetTempPathA will be unique for each process and cannot
2334 // be used for IPC between two different processes
2335 if (!GetApplicationContainerFolder(formatBufferString, applicationGroupId, applicationGroupIdLength))
2336 {
2337 ERROR("Out Of Memory");
2338 return;
2339 }
2340
2341 // Verify the size of the path won't exceed maximum allowed size
2342 if (formatBufferString.GetCount() >= MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH)
2343 {
2344 ERROR("GetApplicationContainerFolder returned a path that was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH");
2345 return;
2346 }
2347 }
2348 else
2349#endif // __APPLE__
2350 {
2351 // Get a temp file location
2352 dwRetVal = ::GetTempPathA(MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, formatBuffer);
2353 if (dwRetVal == 0)
2354 {
2355 ERROR("GetTempPath failed (0x%08x)", ::GetLastError());
2356 return;
2357 }
2358 if (dwRetVal > MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH)
2359 {
2360 ERROR("GetTempPath returned a path that was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH");
2361 return;
2362 }
2363 }
2364
2365 if (strncat_s(formatBuffer, MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, PipeNameFormat, strlen(PipeNameFormat)) == STRUNCATE)
2366 {
2367 ERROR("TransportPipeName was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH");
2368 return;
2369 }
2370
2371 int chars = snprintf(name, MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, formatBuffer, id, disambiguationKey, suffix);
2372 _ASSERTE(chars > 0 && chars < MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH);
2373}
2374
2375/*++
2376Function:
2377 GetProcessTimes
2378
2379See MSDN doc.
2380--*/
2381BOOL
2382PALAPI
2383GetProcessTimes(
2384 IN HANDLE hProcess,
2385 OUT LPFILETIME lpCreationTime,
2386 OUT LPFILETIME lpExitTime,
2387 OUT LPFILETIME lpKernelTime,
2388 OUT LPFILETIME lpUserTime)
2389{
2390 BOOL retval = FALSE;
2391 struct rusage resUsage;
2392 __int64 calcTime;
2393 const __int64 SECS_TO_NS = 1000000000; /* 10^9 */
2394 const __int64 USECS_TO_NS = 1000; /* 10^3 */
2395
2396
2397 PERF_ENTRY(GetProcessTimes);
2398 ENTRY("GetProcessTimes(hProcess=%p, lpExitTime=%p, lpKernelTime=%p,"
2399 "lpUserTime=%p)\n",
2400 hProcess, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime );
2401
2402 /* Make sure hProcess is the current process, this is the only supported
2403 case */
2404 if(PROCGetProcessIDFromHandle(hProcess)!=GetCurrentProcessId())
2405 {
2406 ASSERT("GetProcessTimes() does not work on a process other than the "
2407 "current process.\n");
2408 SetLastError(ERROR_INVALID_HANDLE);
2409 goto GetProcessTimesExit;
2410 }
2411
2412 /* First, we need to actually retrieve the relevant statistics from the
2413 OS */
2414 if (getrusage (RUSAGE_SELF, &resUsage) == -1)
2415 {
2416 ASSERT("Unable to get resource usage information for the current "
2417 "process\n");
2418 SetLastError(ERROR_INTERNAL_ERROR);
2419 goto GetProcessTimesExit;
2420 }
2421
2422 TRACE ("getrusage User: %ld sec,%ld microsec. Kernel: %ld sec,%ld"
2423 " microsec\n",
2424 resUsage.ru_utime.tv_sec, resUsage.ru_utime.tv_usec,
2425 resUsage.ru_stime.tv_sec, resUsage.ru_stime.tv_usec);
2426
2427 if (lpUserTime)
2428 {
2429 /* Get the time of user mode execution, in 100s of nanoseconds */
2430 calcTime = (__int64)resUsage.ru_utime.tv_sec * SECS_TO_NS;
2431 calcTime += (__int64)resUsage.ru_utime.tv_usec * USECS_TO_NS;
2432 calcTime /= 100; /* Produce the time in 100s of ns */
2433 /* Assign the time into lpUserTime */
2434 lpUserTime->dwLowDateTime = (DWORD)calcTime;
2435 lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
2436 }
2437
2438 if (lpKernelTime)
2439 {
2440 /* Get the time of kernel mode execution, in 100s of nanoseconds */
2441 calcTime = (__int64)resUsage.ru_stime.tv_sec * SECS_TO_NS;
2442 calcTime += (__int64)resUsage.ru_stime.tv_usec * USECS_TO_NS;
2443 calcTime /= 100; /* Produce the time in 100s of ns */
2444 /* Assign the time into lpUserTime */
2445 lpKernelTime->dwLowDateTime = (DWORD)calcTime;
2446 lpKernelTime->dwHighDateTime = (DWORD)(calcTime >> 32);
2447 }
2448
2449 retval = TRUE;
2450
2451
2452GetProcessTimesExit:
2453 LOGEXIT("GetProcessTimes returns BOOL %d\n", retval);
2454 PERF_EXIT(GetProcessTimes);
2455 return (retval);
2456}
2457
2458#define FILETIME_TO_ULONGLONG(f) \
2459 (((ULONGLONG)(f).dwHighDateTime << 32) | ((ULONGLONG)(f).dwLowDateTime))
2460
2461/*++
2462Function:
2463 PAL_GetCPUBusyTime
2464
2465The main purpose of this function is to compute the overall CPU utilization
2466for the CLR thread pool to regulate the number of I/O completion port
2467worker threads.
2468Since there is no consistent API on Unix to get the CPU utilization
2469from a user process, getrusage and gettimeofday are used to
2470compute the current process's CPU utilization instead.
2471This function emulates the ThreadpoolMgr::GetCPUBusyTime_NT function in
2472win32threadpool.cpp of the CLR.
2473
2474See MSDN doc for GetSystemTimes.
2475--*/
2476INT
2477PALAPI
2478PAL_GetCPUBusyTime(
2479 IN OUT PAL_IOCP_CPU_INFORMATION *lpPrevCPUInfo)
2480{
2481 ULONGLONG nLastRecordedCurrentTime = 0;
2482 ULONGLONG nLastRecordedUserTime = 0;
2483 ULONGLONG nLastRecordedKernelTime = 0;
2484 ULONGLONG nKernelTime = 0;
2485 ULONGLONG nUserTime = 0;
2486 ULONGLONG nCurrentTime = 0;
2487 ULONGLONG nCpuBusyTime = 0;
2488 ULONGLONG nCpuTotalTime = 0;
2489 DWORD nReading = 0;
2490 struct rusage resUsage;
2491 struct timeval tv;
2492 static DWORD dwNumberOfProcessors = 0;
2493
2494 if (dwNumberOfProcessors <= 0)
2495 {
2496 SYSTEM_INFO SystemInfo;
2497 GetSystemInfo(&SystemInfo);
2498 dwNumberOfProcessors = SystemInfo.dwNumberOfProcessors;
2499 if (dwNumberOfProcessors <= 0)
2500 {
2501 return 0;
2502 }
2503 }
2504
2505 if (getrusage(RUSAGE_SELF, &resUsage) == -1)
2506 {
2507 ASSERT("getrusage() failed; errno is %d (%s)\n", errno, strerror(errno));
2508 return 0;
2509 }
2510 else
2511 {
2512 nKernelTime = (ULONGLONG)resUsage.ru_stime.tv_sec*tccSecondsTo100NanoSeconds +
2513 resUsage.ru_stime.tv_usec*tccMicroSecondsTo100NanoSeconds;
2514 nUserTime = (ULONGLONG)resUsage.ru_utime.tv_sec*tccSecondsTo100NanoSeconds +
2515 resUsage.ru_utime.tv_usec*tccMicroSecondsTo100NanoSeconds;
2516 }
2517
2518 if (gettimeofday(&tv, NULL) == -1)
2519 {
2520 ASSERT("gettimeofday() failed; errno is %d (%s)\n", errno, strerror(errno));
2521 return 0;
2522 }
2523 else
2524 {
2525 nCurrentTime = (ULONGLONG)tv.tv_sec*tccSecondsTo100NanoSeconds +
2526 tv.tv_usec*tccMicroSecondsTo100NanoSeconds;
2527 }
2528
2529 nLastRecordedCurrentTime = FILETIME_TO_ULONGLONG(lpPrevCPUInfo->LastRecordedTime.ftLastRecordedCurrentTime);
2530 nLastRecordedUserTime = FILETIME_TO_ULONGLONG(lpPrevCPUInfo->ftLastRecordedUserTime);
2531 nLastRecordedKernelTime = FILETIME_TO_ULONGLONG(lpPrevCPUInfo->ftLastRecordedKernelTime);
2532
2533 if (nCurrentTime > nLastRecordedCurrentTime)
2534 {
2535 nCpuTotalTime = (nCurrentTime - nLastRecordedCurrentTime);
2536#if HAVE_THREAD_SELF || HAVE__LWP_SELF || HAVE_VM_READ
2537 // For systems that run multiple threads of a process on multiple processors,
2538 // the accumulated userTime and kernelTime of this process may exceed
2539 // the elapsed time. In this case, the cpuTotalTime needs to be adjusted
2540 // according to number of processors so that the cpu utilization
2541 // will not be greater than 100.
2542 nCpuTotalTime *= dwNumberOfProcessors;
2543#endif // HAVE_THREAD_SELF || HAVE__LWP_SELF || HAVE_VM_READ
2544 }
2545
2546 if (nUserTime >= nLastRecordedUserTime &&
2547 nKernelTime >= nLastRecordedKernelTime)
2548 {
2549 nCpuBusyTime =
2550 (nUserTime - nLastRecordedUserTime)+
2551 (nKernelTime - nLastRecordedKernelTime);
2552 }
2553
2554 if (nCpuTotalTime > 0 && nCpuBusyTime > 0)
2555 {
2556 nReading = (DWORD)((nCpuBusyTime*100)/nCpuTotalTime);
2557 TRACE("PAL_GetCPUBusyTime: nCurrentTime=%lld, nKernelTime=%lld, nUserTime=%lld, nReading=%d\n",
2558 nCurrentTime, nKernelTime, nUserTime, nReading);
2559 }
2560
2561 if (nReading > 100)
2562 {
2563 ERROR("cpu utilization(%d) > 100\n", nReading);
2564 }
2565
2566 lpPrevCPUInfo->LastRecordedTime.ftLastRecordedCurrentTime.dwLowDateTime = (DWORD)nCurrentTime;
2567 lpPrevCPUInfo->LastRecordedTime.ftLastRecordedCurrentTime.dwHighDateTime = (DWORD)(nCurrentTime >> 32);
2568
2569 lpPrevCPUInfo->ftLastRecordedUserTime.dwLowDateTime = (DWORD)nUserTime;
2570 lpPrevCPUInfo->ftLastRecordedUserTime.dwHighDateTime = (DWORD)(nUserTime >> 32);
2571
2572 lpPrevCPUInfo->ftLastRecordedKernelTime.dwLowDateTime = (DWORD)nKernelTime;
2573 lpPrevCPUInfo->ftLastRecordedKernelTime.dwHighDateTime = (DWORD)(nKernelTime >> 32);
2574
2575 return (DWORD)nReading;
2576}
2577
2578/*++
2579Function:
2580 GetCommandLineW
2581
2582See MSDN doc.
2583--*/
2584LPWSTR
2585PALAPI
2586GetCommandLineW(
2587 VOID)
2588{
2589 PERF_ENTRY(GetCommandLineW);
2590 ENTRY("GetCommandLineW()\n");
2591
2592 LPWSTR lpwstr = g_lpwstrCmdLine ? g_lpwstrCmdLine : (LPWSTR)W("");
2593
2594 LOGEXIT("GetCommandLineW returns LPWSTR %p (%S)\n",
2595 g_lpwstrCmdLine,
2596 lpwstr);
2597 PERF_EXIT(GetCommandLineW);
2598
2599 return lpwstr;
2600}
2601
2602/*++
2603Function:
2604 OpenProcess
2605
2606See MSDN doc.
2607
2608Notes :
2609dwDesiredAccess is ignored (all supported operations will be allowed)
2610bInheritHandle is ignored (no inheritance)
2611--*/
2612HANDLE
2613PALAPI
2614OpenProcess(
2615 DWORD dwDesiredAccess,
2616 BOOL bInheritHandle,
2617 DWORD dwProcessId)
2618{
2619 PAL_ERROR palError;
2620 CPalThread *pThread;
2621 IPalObject *pobjProcess = NULL;
2622 IPalObject *pobjProcessRegistered = NULL;
2623 IDataLock *pDataLock;
2624 CProcProcessLocalData *pLocalData;
2625 CObjectAttributes oa;
2626 HANDLE hProcess = NULL;
2627
2628 PERF_ENTRY(OpenProcess);
2629 ENTRY("OpenProcess(dwDesiredAccess=0x%08x, bInheritHandle=%d, "
2630 "dwProcessId = 0x%08x)\n",
2631 dwDesiredAccess, bInheritHandle, dwProcessId );
2632
2633 pThread = InternalGetCurrentThread();
2634
2635 if (0 == dwProcessId)
2636 {
2637 palError = ERROR_INVALID_PARAMETER;
2638 goto OpenProcessExit;
2639 }
2640
2641 palError = g_pObjectManager->AllocateObject(
2642 pThread,
2643 &otProcess,
2644 &oa,
2645 &pobjProcess
2646 );
2647
2648 if (NO_ERROR != palError)
2649 {
2650 goto OpenProcessExit;
2651 }
2652
2653 palError = pobjProcess->GetProcessLocalData(
2654 pThread,
2655 WriteLock,
2656 &pDataLock,
2657 reinterpret_cast<void **>(&pLocalData)
2658 );
2659
2660 if (NO_ERROR != palError)
2661 {
2662 goto OpenProcessExit;
2663 }
2664
2665 pLocalData->dwProcessId = dwProcessId;
2666 pDataLock->ReleaseLock(pThread, TRUE);
2667
2668 palError = g_pObjectManager->RegisterObject(
2669 pThread,
2670 pobjProcess,
2671 &aotProcess,
2672 dwDesiredAccess,
2673 &hProcess,
2674 &pobjProcessRegistered
2675 );
2676
2677 //
2678 // pobjProcess was invalidated by the above call, so NULL
2679 // it out here
2680 //
2681
2682 pobjProcess = NULL;
2683
2684 //
2685 // TODO: check to see if the process actually exists?
2686 //
2687
2688OpenProcessExit:
2689
2690 if (NULL != pobjProcess)
2691 {
2692 pobjProcess->ReleaseReference(pThread);
2693 }
2694
2695 if (NULL != pobjProcessRegistered)
2696 {
2697 pobjProcessRegistered->ReleaseReference(pThread);
2698 }
2699
2700 if (NO_ERROR != palError)
2701 {
2702 pThread->SetLastError(palError);
2703 }
2704
2705 LOGEXIT("OpenProcess returns HANDLE %p\n", hProcess);
2706 PERF_EXIT(OpenProcess);
2707 return hProcess;
2708}
2709
2710/*++
2711Function:
2712 EnumProcessModules
2713
2714Abstract
2715 Returns a process's module list
2716
2717Return
2718 TRUE if it succeeded, FALSE otherwise
2719
2720Notes
2721 This API is tricky because the module handles are never closed/freed so there can't be any
2722 allocations for the module handle or name strings, etc. The "handles" are actually the base
2723 addresses of the modules. The module handles should only be used by GetModuleFileNameExW
2724 below.
2725--*/
2726BOOL
2727PALAPI
2728EnumProcessModules(
2729 IN HANDLE hProcess,
2730 OUT HMODULE *lphModule,
2731 IN DWORD cb,
2732 OUT LPDWORD lpcbNeeded)
2733{
2734 PERF_ENTRY(EnumProcessModules);
2735 ENTRY("EnumProcessModules(hProcess=0x%08x, cb=%d)\n", hProcess, cb);
2736
2737 BOOL result = TRUE;
2738 DWORD count = 0;
2739 ProcessModules *listHead = GetProcessModulesFromHandle(hProcess, &count);
2740 if (listHead != NULL)
2741 {
2742 for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
2743 {
2744 if (cb <= 0)
2745 {
2746 break;
2747 }
2748 cb -= sizeof(HMODULE);
2749 *lphModule = (HMODULE)entry->BaseAddress;
2750 lphModule++;
2751 }
2752 }
2753 else
2754 {
2755 result = FALSE;
2756 }
2757
2758 if (lpcbNeeded)
2759 {
2760 // This return value isn't exactly up to spec because it should return the actual
2761 // number of modules in the process even if "cb" isn't big enough but for our use
2762 // it works just fine.
2763 (*lpcbNeeded) = count * sizeof(HMODULE);
2764 }
2765
2766 LOGEXIT("EnumProcessModules returns %d\n", result);
2767 PERF_EXIT(EnumProcessModules);
2768 return result;
2769}
2770
2771/*++
2772Function:
2773 GetModuleFileNameExW
2774
2775 Used only with module handles returned from EnumProcessModule (for dbgshim).
2776
2777--*/
2778DWORD
2779PALAPI
2780GetModuleFileNameExW(
2781 IN HANDLE hProcess,
2782 IN HMODULE hModule,
2783 OUT LPWSTR lpFilename,
2784 IN DWORD nSize
2785)
2786{
2787 DWORD result = 0;
2788 DWORD count = 0;
2789
2790 ProcessModules *listHead = GetProcessModulesFromHandle(hProcess, &count);
2791 if (listHead != NULL)
2792 {
2793 for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
2794 {
2795 if ((HMODULE)entry->BaseAddress == hModule)
2796 {
2797 // Convert CHAR string into WCHAR string
2798 result = MultiByteToWideChar(CP_ACP, 0, entry->Name, -1, lpFilename, nSize);
2799 break;
2800 }
2801 }
2802 }
2803
2804 return result;
2805}
2806
2807/*++
2808Function:
2809 GetProcessModulesFromHandle
2810
2811Abstract
2812 Returns a process's module list
2813
2814Return
2815 ProcessModules * list
2816
2817--*/
2818ProcessModules *
2819GetProcessModulesFromHandle(
2820 IN HANDLE hProcess,
2821 OUT LPDWORD lpCount)
2822{
2823 CPalThread* pThread = InternalGetCurrentThread();
2824 CProcProcessLocalData *pLocalData = NULL;
2825 ProcessModules *listHead = NULL;
2826 IPalObject *pobjProcess = NULL;
2827 IDataLock *pDataLock = NULL;
2828 PAL_ERROR palError = NO_ERROR;
2829 DWORD dwProcessId = 0;
2830 DWORD count = 0;
2831
2832 _ASSERTE(lpCount != NULL);
2833
2834 if (hPseudoCurrentProcess == hProcess)
2835 {
2836 pobjProcess = g_pobjProcess;
2837 pobjProcess->AddReference();
2838 }
2839 else
2840 {
2841 CAllowedObjectTypes aotProcess(otiProcess);
2842
2843 palError = g_pObjectManager->ReferenceObjectByHandle(
2844 pThread,
2845 hProcess,
2846 &aotProcess,
2847 0,
2848 &pobjProcess);
2849
2850 if (NO_ERROR != palError)
2851 {
2852 pThread->SetLastError(ERROR_INVALID_HANDLE);
2853 goto exit;
2854 }
2855 }
2856
2857 palError = pobjProcess->GetProcessLocalData(
2858 pThread,
2859 WriteLock,
2860 &pDataLock,
2861 reinterpret_cast<void **>(&pLocalData));
2862
2863 _ASSERTE(NO_ERROR == palError);
2864
2865 dwProcessId = pLocalData->dwProcessId;
2866 listHead = pLocalData->pProcessModules;
2867 count = pLocalData->cProcessModules;
2868
2869 // If the module list hasn't been created yet, create it now
2870 if (listHead == NULL)
2871 {
2872 listHead = CreateProcessModules(dwProcessId, &count);
2873 if (listHead == NULL)
2874 {
2875 pThread->SetLastError(ERROR_INVALID_PARAMETER);
2876 goto exit;
2877 }
2878
2879 if (pLocalData != NULL)
2880 {
2881 pLocalData->pProcessModules = listHead;
2882 pLocalData->cProcessModules = count;
2883 }
2884 }
2885
2886exit:
2887 if (NULL != pDataLock)
2888 {
2889 pDataLock->ReleaseLock(pThread, TRUE);
2890 }
2891 if (NULL != pobjProcess)
2892 {
2893 pobjProcess->ReleaseReference(pThread);
2894 }
2895
2896 *lpCount = count;
2897 return listHead;
2898}
2899
2900/*++
2901Function:
2902 CreateProcessModules
2903
2904Abstract
2905 Returns a process's module list
2906
2907Return
2908 ProcessModules * list
2909
2910--*/
2911ProcessModules *
2912CreateProcessModules(
2913 IN DWORD dwProcessId,
2914 OUT LPDWORD lpCount)
2915{
2916 ProcessModules *listHead = NULL;
2917 _ASSERTE(lpCount != NULL);
2918
2919#if defined(__APPLE__)
2920
2921 // For OS X, the "vmmap" command outputs something similar to the /proc/*/maps file so popen the
2922 // command and read the relevant lines:
2923 //
2924 // ...
2925 // ==== regions for process 347 (non-writable and writable regions are interleaved)
2926 // REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
2927 // __TEXT 000000010446d000-0000000104475000 [ 32K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun
2928 // __DATA 0000000104475000-0000000104476000 [ 4K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun
2929 // __LINKEDIT 0000000104476000-000000010447a000 [ 16K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun
2930 // Kernel Alloc Once 000000010447a000-000000010447b000 [ 4K] rw-/rwx SM=PRV
2931 // MALLOC (admin) 000000010447b000-000000010447c000 [ 4K] r--/rwx SM=ZER
2932 // ...
2933 // MALLOC (admin) 00000001044ab000-00000001044ac000 [ 4K] r--/rwx SM=PRV
2934 // __TEXT 00000001044ac000-0000000104c84000 [ 8032K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
2935 // __TEXT 0000000104c84000-0000000104c85000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
2936 // __TEXT 0000000104c85000-000000010513b000 [ 4824K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
2937 // __TEXT 000000010513b000-000000010513c000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
2938 // __TEXT 000000010513c000-000000010516f000 [ 204K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
2939 // __DATA 000000010516f000-00000001051ce000 [ 380K] rw-/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
2940 // __DATA 00000001051ce000-00000001051fa000 [ 176K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
2941 // __LINKEDIT 00000001051fa000-0000000105bac000 [ 9928K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib
2942 // VM_ALLOCATE 0000000105bac000-0000000105bad000 [ 4K] r--/rw- SM=SHM
2943 // MALLOC (admin) 0000000105bad000-0000000105bae000 [ 4K] r--/rwx SM=ZER
2944 // MALLOC 0000000105bae000-0000000105baf000 [ 4K] rw-/rwx SM=ZER
2945
2946 // OS X Sierra (10.12.4 Beta)
2947 // REGION TYPE START - END [ VSIZE RSDNT DIRTY SWAP] PRT/MAX SHRMOD PURGE REGION DETAIL
2948 // Stack 00007fff5a930000-00007fff5b130000 [ 8192K 32K 32K 0K] rw-/rwx SM=PRV thread 0
2949 // __TEXT 00007fffa4a0b000-00007fffa4a0d000 [ 8K 8K 0K 0K] r-x/r-x SM=COW /usr/lib/libSystem.B.dylib
2950 // __TEXT 00007fffa4bbe000-00007fffa4c15000 [ 348K 348K 0K 0K] r-x/r-x SM=COW /usr/lib/libc++.1.dylib
2951
2952 // NOTE: the module path can have spaces in the name
2953 // __TEXT 0000000196220000-00000001965b4000 [ 3664K 2340K 0K 0K] r-x/rwx SM=COW /Volumes/Builds/builds/devmain/rawproduct/debug/build/out/Applications/Microsoft Excel.app/Contents/SharedSupport/PowerQuery/libcoreclr.dylib
2954 char *line = NULL;
2955 size_t lineLen = 0;
2956 int count = 0;
2957 ssize_t read;
2958
2959 char vmmapCommand[100];
2960 int chars = snprintf(vmmapCommand, sizeof(vmmapCommand), "/usr/bin/vmmap -interleaved %d -wide", dwProcessId);
2961 _ASSERTE(chars > 0 && chars <= sizeof(vmmapCommand));
2962
2963 FILE *vmmapFile = popen(vmmapCommand, "r");
2964 if (vmmapFile == NULL)
2965 {
2966 goto exit;
2967 }
2968
2969 // Reading maps file line by line
2970 while ((read = getline(&line, &lineLen, vmmapFile)) != -1)
2971 {
2972 void *startAddress, *endAddress;
2973 char moduleName[PATH_MAX];
2974
2975 if (sscanf_s(line, "__TEXT %p-%p [ %*[0-9K ]] %*[-/rwxsp] SM=%*[A-Z] %[^\n]", &startAddress, &endAddress, moduleName, _countof(moduleName)) == 3)
2976 {
2977 bool dup = false;
2978 for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
2979 {
2980 if (strcmp(moduleName, entry->Name) == 0)
2981 {
2982 dup = true;
2983 break;
2984 }
2985 }
2986
2987 if (!dup)
2988 {
2989 int cbModuleName = strlen(moduleName) + 1;
2990 ProcessModules *entry = (ProcessModules *)InternalMalloc(sizeof(ProcessModules) + cbModuleName);
2991 if (entry == NULL)
2992 {
2993 DestroyProcessModules(listHead);
2994 listHead = NULL;
2995 count = 0;
2996 break;
2997 }
2998 strcpy_s(entry->Name, cbModuleName, moduleName);
2999 entry->BaseAddress = startAddress;
3000 entry->Next = listHead;
3001 listHead = entry;
3002 count++;
3003 }
3004 }
3005 }
3006
3007 *lpCount = count;
3008
3009 free(line); // We didn't allocate line, but as per contract of getline we should free it
3010 pclose(vmmapFile);
3011exit:
3012
3013#elif HAVE_PROCFS_MAPS
3014
3015 // Here we read /proc/<pid>/maps file in order to parse it and figure out what it says
3016 // about a library we are looking for. This file looks something like this:
3017 //
3018 // [address] [perms] [offset] [dev] [inode] [pathname] - HEADER is not preset in an actual file
3019 //
3020 // 35b1800000-35b1820000 r-xp 00000000 08:02 135522 /usr/lib64/ld-2.15.so
3021 // 35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522 /usr/lib64/ld-2.15.so
3022 // 35b1a20000-35b1a21000 rw-p 00020000 08:02 135522 /usr/lib64/ld-2.15.so
3023 // 35b1a21000-35b1a22000 rw-p 00000000 00:00 0 [heap]
3024 // 35b1c00000-35b1dac000 r-xp 00000000 08:02 135870 /usr/lib64/libc-2.15.so
3025 // 35b1dac000-35b1fac000 ---p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so
3026 // 35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so
3027 // 35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870 /usr/lib64/libc-2.15.so
3028
3029 // Making something like: /proc/123/maps
3030 char mapFileName[100];
3031 char *line = NULL;
3032 size_t lineLen = 0;
3033 int count = 0;
3034 ssize_t read;
3035
3036 INDEBUG(int chars = )
3037 snprintf(mapFileName, sizeof(mapFileName), "/proc/%d/maps", dwProcessId);
3038 _ASSERTE(chars > 0 && chars <= sizeof(mapFileName));
3039
3040 FILE *mapsFile = fopen(mapFileName, "r");
3041 if (mapsFile == NULL)
3042 {
3043 goto exit;
3044 }
3045
3046 // Reading maps file line by line
3047 while ((read = getline(&line, &lineLen, mapsFile)) != -1)
3048 {
3049 void *startAddress, *endAddress, *offset;
3050 int devHi, devLo, inode;
3051 char moduleName[PATH_MAX];
3052
3053 if (sscanf_s(line, "%p-%p %*[-rwxsp] %p %x:%x %d %s\n", &startAddress, &endAddress, &offset, &devHi, &devLo, &inode, moduleName, _countof(moduleName)) == 7)
3054 {
3055 if (inode != 0)
3056 {
3057 bool dup = false;
3058 for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next)
3059 {
3060 if (strcmp(moduleName, entry->Name) == 0)
3061 {
3062 dup = true;
3063 break;
3064 }
3065 }
3066
3067 if (!dup)
3068 {
3069 int cbModuleName = strlen(moduleName) + 1;
3070 ProcessModules *entry = (ProcessModules *)InternalMalloc(sizeof(ProcessModules) + cbModuleName);
3071 if (entry == NULL)
3072 {
3073 DestroyProcessModules(listHead);
3074 listHead = NULL;
3075 count = 0;
3076 break;
3077 }
3078 strcpy_s(entry->Name, cbModuleName, moduleName);
3079 entry->BaseAddress = startAddress;
3080 entry->Next = listHead;
3081 listHead = entry;
3082 count++;
3083 }
3084 }
3085 }
3086 }
3087
3088 *lpCount = count;
3089
3090 free(line); // We didn't allocate line, but as per contract of getline we should free it
3091 fclose(mapsFile);
3092exit:
3093
3094#else
3095 _ASSERTE(!"Not implemented on this platform");
3096#endif
3097 return listHead;
3098}
3099
3100/*++
3101Function:
3102 DestroyProcessModules
3103
3104Abstract
3105 Cleans up the process module table.
3106
3107Return
3108 None
3109
3110--*/
3111VOID
3112DestroyProcessModules(IN ProcessModules *listHead)
3113{
3114 for (ProcessModules *entry = listHead; entry != NULL; )
3115 {
3116 ProcessModules *next = entry->Next;
3117 free(entry);
3118 entry = next;
3119 }
3120}
3121
3122/*++
3123Function
3124 PROCNotifyProcessShutdown
3125
3126 Calls the abort handler to do any shutdown cleanup. Call be called
3127 from the unhandled native exception handler.
3128
3129(no return value)
3130--*/
3131__attribute__((destructor))
3132VOID
3133PROCNotifyProcessShutdown()
3134{
3135 // Call back into the coreclr to clean up the debugger transport pipes
3136 PSHUTDOWN_CALLBACK callback = InterlockedExchangePointer(&g_shutdownCallback, NULL);
3137 if (callback != NULL)
3138 {
3139 callback();
3140 }
3141}
3142
3143/*++
3144Function
3145 PROCAbortInitialize()
3146
3147Abstract
3148 Initialize the process abort crash dump program file path and
3149 name. Doing all of this ahead of time so nothing is allocated
3150 or copied in PROCAbort/signal handler.
3151
3152Return
3153 TRUE - succeeds, FALSE - fails
3154
3155--*/
3156BOOL
3157PROCAbortInitialize()
3158{
3159 char* enabled = getenv("COMPlus_DbgEnableMiniDump");
3160 if (enabled != nullptr && _stricmp(enabled, "1") == 0)
3161 {
3162 if (g_szCoreCLRPath == nullptr)
3163 {
3164 return FALSE;
3165 }
3166 const char* DumpGeneratorName = "createdump";
3167 int programLen = strlen(g_szCoreCLRPath) + strlen(DumpGeneratorName) + 1;
3168 char* program = (char*)InternalMalloc(programLen);
3169 if (program == nullptr)
3170 {
3171 return FALSE;
3172 }
3173 if (strcpy_s(program, programLen, g_szCoreCLRPath) != SAFECRT_SUCCESS)
3174 {
3175 return FALSE;
3176 }
3177 char *last = strrchr(program, '/');
3178 if (last != nullptr)
3179 {
3180 *(last + 1) = '\0';
3181 }
3182 else
3183 {
3184 program[0] = '\0';
3185 }
3186 if (strcat_s(program, programLen, DumpGeneratorName) != SAFECRT_SUCCESS)
3187 {
3188 return FALSE;
3189 }
3190 char* pidarg = (char*)InternalMalloc(128);
3191 if (pidarg == nullptr)
3192 {
3193 return FALSE;
3194 }
3195 if (sprintf_s(pidarg, 128, "%d", gPID) == -1)
3196 {
3197 return FALSE;
3198 }
3199 const char** argv = (const char**)g_argvCreateDump;
3200 *argv++ = program;
3201
3202 char* envvar = getenv("COMPlus_DbgMiniDumpName");
3203 if (envvar != nullptr)
3204 {
3205 *argv++ = "--name";
3206 *argv++ = envvar;
3207 }
3208
3209 envvar = getenv("COMPlus_DbgMiniDumpType");
3210 if (envvar != nullptr)
3211 {
3212 if (strcmp(envvar, "1") == 0)
3213 {
3214 *argv++ = "--normal";
3215 }
3216 else if (strcmp(envvar, "2") == 0)
3217 {
3218 *argv++ = "--withheap";
3219 }
3220 else if (strcmp(envvar, "3") == 0)
3221 {
3222 *argv++ = "--triage";
3223 }
3224 else if (strcmp(envvar, "4") == 0)
3225 {
3226 *argv++ = "--full";
3227 }
3228 }
3229
3230 envvar = getenv("COMPlus_CreateDumpDiagnostics");
3231 if (envvar != nullptr && strcmp(envvar, "1") == 0)
3232 {
3233 *argv++ = "--diag";
3234 }
3235
3236 *argv++ = pidarg;
3237 *argv = nullptr;
3238 }
3239 return TRUE;
3240}
3241
3242/*++
3243Function:
3244 PROCCreateCrashDumpIfEnabled
3245
3246 Creates crash dump of the process (if enabled). Can be
3247 called from the unhandled native exception handler.
3248
3249(no return value)
3250--*/
3251VOID
3252PROCCreateCrashDumpIfEnabled()
3253{
3254#if HAVE_PRCTL_H && HAVE_PR_SET_PTRACER
3255 // If enabled, launch the create minidump utility and wait until it completes
3256 if (g_argvCreateDump[0] == nullptr)
3257 return;
3258
3259 // Fork the core dump child process.
3260 pid_t childpid = fork();
3261
3262 // If error, write an error to trace log and abort
3263 if (childpid == -1)
3264 {
3265 ERROR("PROCAbort: fork() FAILED %d (%s)\n", errno, strerror(errno));
3266 }
3267 else if (childpid == 0)
3268 {
3269 // Child process
3270 if (execve(g_argvCreateDump[0], g_argvCreateDump, palEnvironment) == -1)
3271 {
3272 ERROR("PROCAbort: execve FAILED %d (%s)\n", errno, strerror(errno));
3273 }
3274 }
3275 else
3276 {
3277 // Gives the child process permission to use /proc/<pid>/mem and ptrace
3278 if (prctl(PR_SET_PTRACER, childpid, 0, 0, 0) == -1)
3279 {
3280 ERROR("PROCAbort: prctl() FAILED %d (%s)\n", errno, strerror(errno));
3281 }
3282 // Parent waits until the child process is done
3283 int wstatus;
3284 int result = waitpid(childpid, &wstatus, 0);
3285 if (result != childpid)
3286 {
3287 ERROR("PROCAbort: waitpid FAILED result %d wstatus %d errno %d (%s)\n",
3288 result, wstatus, errno, strerror(errno));
3289 }
3290 }
3291#endif // HAVE_PRCTL_H && HAVE_PR_SET_PTRACER
3292}
3293
3294/*++
3295Function:
3296 PROCAbort()
3297
3298 Aborts the process after calling the shutdown cleanup handler. This function
3299 should be called instead of calling abort() directly.
3300
3301 Does not return
3302--*/
3303PAL_NORETURN
3304VOID
3305PROCAbort()
3306{
3307 // Do any shutdown cleanup before aborting or creating a core dump
3308 PROCNotifyProcessShutdown();
3309
3310 PROCCreateCrashDumpIfEnabled();
3311
3312 // Abort the process after waiting for the core dump to complete
3313 abort();
3314}
3315
3316/*++
3317Function:
3318 InitializeFlushProcessWriteBuffers
3319
3320Abstract
3321 This function initializes data structures needed for the FlushProcessWriteBuffers
3322Return
3323 TRUE if it succeeded, FALSE otherwise
3324--*/
3325BOOL
3326InitializeFlushProcessWriteBuffers()
3327{
3328 _ASSERTE(s_helperPage == 0);
3329 _ASSERTE(s_flushUsingMemBarrier == 0);
3330
3331 // Starting with Linux kernel 4.14, process memory barriers can be generated
3332 // using MEMBARRIER_CMD_PRIVATE_EXPEDITED.
3333 int mask = membarrier(MEMBARRIER_CMD_QUERY, 0);
3334 if (mask >= 0 &&
3335 mask & MEMBARRIER_CMD_PRIVATE_EXPEDITED)
3336 {
3337 // Register intent to use the private expedited command.
3338 if (membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0) == 0)
3339 {
3340 s_flushUsingMemBarrier = TRUE;
3341 return TRUE;
3342 }
3343 }
3344
3345 s_helperPage = static_cast<int*>(mmap(0, GetVirtualPageSize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
3346
3347 if(s_helperPage == MAP_FAILED)
3348 {
3349 return FALSE;
3350 }
3351
3352 // Verify that the s_helperPage is really aligned to the GetVirtualPageSize()
3353 _ASSERTE((((SIZE_T)s_helperPage) & (GetVirtualPageSize() - 1)) == 0);
3354
3355 // Locking the page ensures that it stays in memory during the two mprotect
3356 // calls in the FlushProcessWriteBuffers below. If the page was unmapped between
3357 // those calls, they would not have the expected effect of generating IPI.
3358 int status = mlock(s_helperPage, GetVirtualPageSize());
3359
3360 if (status != 0)
3361 {
3362 return FALSE;
3363 }
3364
3365 status = pthread_mutex_init(&flushProcessWriteBuffersMutex, NULL);
3366 if (status != 0)
3367 {
3368 munlock(s_helperPage, GetVirtualPageSize());
3369 }
3370
3371 return status == 0;
3372}
3373
3374#define FATAL_ASSERT(e, msg) \
3375 do \
3376 { \
3377 if (!(e)) \
3378 { \
3379 fprintf(stderr, "FATAL ERROR: " msg); \
3380 PROCAbort(); \
3381 } \
3382 } \
3383 while(0)
3384
3385/*++
3386Function:
3387 FlushProcessWriteBuffers
3388
3389See MSDN doc.
3390--*/
3391VOID
3392PALAPI
3393FlushProcessWriteBuffers()
3394{
3395 if (s_flushUsingMemBarrier)
3396 {
3397 int status = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0);
3398 FATAL_ASSERT(status == 0, "Failed to flush using membarrier");
3399 }
3400 else
3401 {
3402 int status = pthread_mutex_lock(&flushProcessWriteBuffersMutex);
3403 FATAL_ASSERT(status == 0, "Failed to lock the flushProcessWriteBuffersMutex lock");
3404
3405 // Changing a helper memory page protection from read / write to no access
3406 // causes the OS to issue IPI to flush TLBs on all processors. This also
3407 // results in flushing the processor buffers.
3408 status = mprotect(s_helperPage, GetVirtualPageSize(), PROT_READ | PROT_WRITE);
3409 FATAL_ASSERT(status == 0, "Failed to change helper page protection to read / write");
3410
3411 // Ensure that the page is dirty before we change the protection so that
3412 // we prevent the OS from skipping the global TLB flush.
3413 InterlockedIncrement(s_helperPage);
3414
3415 status = mprotect(s_helperPage, GetVirtualPageSize(), PROT_NONE);
3416 FATAL_ASSERT(status == 0, "Failed to change helper page protection to no access");
3417
3418 status = pthread_mutex_unlock(&flushProcessWriteBuffersMutex);
3419 FATAL_ASSERT(status == 0, "Failed to unlock the flushProcessWriteBuffersMutex lock");
3420 }
3421}
3422
3423/*++
3424Function:
3425 PROCGetProcessIDFromHandle
3426
3427Abstract
3428 Return the process ID from a process handle
3429
3430Parameter
3431 hProcess: process handle
3432
3433Return
3434 Return the process ID, or 0 if it's not a valid handle
3435--*/
3436DWORD
3437PROCGetProcessIDFromHandle(
3438 HANDLE hProcess)
3439{
3440 PAL_ERROR palError;
3441 IPalObject *pobjProcess = NULL;
3442 CPalThread *pThread = InternalGetCurrentThread();
3443
3444 DWORD dwProcessId = 0;
3445
3446 if (hPseudoCurrentProcess == hProcess)
3447 {
3448 dwProcessId = gPID;
3449 goto PROCGetProcessIDFromHandleExit;
3450 }
3451
3452
3453 palError = g_pObjectManager->ReferenceObjectByHandle(
3454 pThread,
3455 hProcess,
3456 &aotProcess,
3457 0,
3458 &pobjProcess
3459 );
3460
3461 if (NO_ERROR == palError)
3462 {
3463 IDataLock *pDataLock;
3464 CProcProcessLocalData *pLocalData;
3465
3466 palError = pobjProcess->GetProcessLocalData(
3467 pThread,
3468 ReadLock,
3469 &pDataLock,
3470 reinterpret_cast<void **>(&pLocalData)
3471 );
3472
3473 if (NO_ERROR == palError)
3474 {
3475 dwProcessId = pLocalData->dwProcessId;
3476 pDataLock->ReleaseLock(pThread, FALSE);
3477 }
3478
3479 pobjProcess->ReleaseReference(pThread);
3480 }
3481
3482PROCGetProcessIDFromHandleExit:
3483
3484 return dwProcessId;
3485}
3486
3487PAL_ERROR
3488CorUnix::InitializeProcessData(
3489 void
3490 )
3491{
3492 PAL_ERROR palError = NO_ERROR;
3493 bool fLockInitialized = FALSE;
3494
3495 pGThreadList = NULL;
3496 g_dwThreadCount = 0;
3497
3498 InternalInitializeCriticalSection(&g_csProcess);
3499 fLockInitialized = TRUE;
3500
3501 if (NO_ERROR != palError)
3502 {
3503 if (fLockInitialized)
3504 {
3505 InternalDeleteCriticalSection(&g_csProcess);
3506 }
3507 }
3508
3509 return palError;
3510}
3511
3512/*++
3513Function
3514 InitializeProcessCommandLine
3515
3516Abstract
3517 Initializes (or re-initializes) the saved command line and exe path.
3518
3519Parameter
3520 lpwstrCmdLine
3521 lpwstrFullPath
3522
3523Return
3524 PAL_ERROR
3525
3526Notes
3527 This function takes ownership of lpwstrCmdLine, but not of lpwstrFullPath
3528--*/
3529
3530PAL_ERROR
3531CorUnix::InitializeProcessCommandLine(
3532 LPWSTR lpwstrCmdLine,
3533 LPWSTR lpwstrFullPath
3534)
3535{
3536 PAL_ERROR palError = NO_ERROR;
3537 LPWSTR initial_dir = NULL;
3538
3539 //
3540 // Save the command line and initial directory
3541 //
3542
3543 if (lpwstrFullPath)
3544 {
3545 LPWSTR lpwstr = PAL_wcsrchr(lpwstrFullPath, '/');
3546 lpwstr[0] = '\0';
3547 size_t n = PAL_wcslen(lpwstrFullPath) + 1;
3548
3549 size_t iLen = n;
3550 initial_dir = reinterpret_cast<LPWSTR>(InternalMalloc(iLen*sizeof(WCHAR)));
3551 if (NULL == initial_dir)
3552 {
3553 ERROR("malloc() failed! (initial_dir) \n");
3554 palError = ERROR_NOT_ENOUGH_MEMORY;
3555 goto exit;
3556 }
3557
3558 if (wcscpy_s(initial_dir, iLen, lpwstrFullPath) != SAFECRT_SUCCESS)
3559 {
3560 ERROR("wcscpy_s failed!\n");
3561 free(initial_dir);
3562 palError = ERROR_INTERNAL_ERROR;
3563 goto exit;
3564 }
3565
3566 lpwstr[0] = '/';
3567
3568 free(g_lpwstrAppDir);
3569 g_lpwstrAppDir = initial_dir;
3570 }
3571
3572 free(g_lpwstrCmdLine);
3573 g_lpwstrCmdLine = lpwstrCmdLine;
3574
3575exit:
3576 return palError;
3577}
3578
3579
3580/*++
3581Function:
3582 CreateInitialProcessAndThreadObjects
3583
3584Abstract
3585 Creates the IPalObjects that represent the current process
3586 and the initial thread
3587
3588Parameter
3589 pThread - the initial thread
3590
3591Return
3592 PAL_ERROR
3593--*/
3594
3595PAL_ERROR
3596CorUnix::CreateInitialProcessAndThreadObjects(
3597 CPalThread *pThread
3598 )
3599{
3600 PAL_ERROR palError = NO_ERROR;
3601 HANDLE hThread;
3602 IPalObject *pobjProcess = NULL;
3603 IDataLock *pDataLock;
3604 CProcProcessLocalData *pLocalData;
3605 CObjectAttributes oa;
3606 HANDLE hProcess;
3607
3608 //
3609 // Create initial thread object
3610 //
3611
3612 palError = CreateThreadObject(pThread, pThread, &hThread);
3613 if (NO_ERROR != palError)
3614 {
3615 goto CreateInitialProcessAndThreadObjectsExit;
3616 }
3617
3618 //
3619 // This handle isn't needed
3620 //
3621
3622 (void) g_pObjectManager->RevokeHandle(pThread, hThread);
3623
3624 //
3625 // Create and initialize process object
3626 //
3627
3628 palError = g_pObjectManager->AllocateObject(
3629 pThread,
3630 &otProcess,
3631 &oa,
3632 &pobjProcess
3633 );
3634
3635 if (NO_ERROR != palError)
3636 {
3637 ERROR("Unable to allocate process object");
3638 goto CreateInitialProcessAndThreadObjectsExit;
3639 }
3640
3641 palError = pobjProcess->GetProcessLocalData(
3642 pThread,
3643 WriteLock,
3644 &pDataLock,
3645 reinterpret_cast<void **>(&pLocalData)
3646 );
3647
3648 if (NO_ERROR != palError)
3649 {
3650 ASSERT("Unable to access local data");
3651 goto CreateInitialProcessAndThreadObjectsExit;
3652 }
3653
3654 pLocalData->dwProcessId = gPID;
3655 pLocalData->ps = PS_RUNNING;
3656 pDataLock->ReleaseLock(pThread, TRUE);
3657
3658 palError = g_pObjectManager->RegisterObject(
3659 pThread,
3660 pobjProcess,
3661 &aotProcess,
3662 PROCESS_ALL_ACCESS,
3663 &hProcess,
3664 &g_pobjProcess
3665 );
3666
3667 //
3668 // pobjProcess is invalidated by the call to RegisterObject, so
3669 // NULL it out here to prevent it from being released later
3670 //
3671
3672 pobjProcess = NULL;
3673
3674 if (NO_ERROR != palError)
3675 {
3676 ASSERT("Failure registering process object");
3677 goto CreateInitialProcessAndThreadObjectsExit;
3678 }
3679
3680 //
3681 // There's no need to keep this handle around, so revoke
3682 // it now
3683 //
3684
3685 g_pObjectManager->RevokeHandle(pThread, hProcess);
3686
3687CreateInitialProcessAndThreadObjectsExit:
3688
3689 if (NULL != pobjProcess)
3690 {
3691 pobjProcess->ReleaseReference(pThread);
3692 }
3693
3694 return palError;
3695}
3696
3697
3698/*++
3699Function:
3700 PROCCleanupInitialProcess
3701
3702Abstract
3703 Cleanup all the structures for the initial process.
3704
3705Parameter
3706 VOID
3707
3708Return
3709 VOID
3710
3711--*/
3712VOID
3713PROCCleanupInitialProcess(VOID)
3714{
3715 CPalThread *pThread = InternalGetCurrentThread();
3716
3717 InternalEnterCriticalSection(pThread, &g_csProcess);
3718
3719 /* Free the application directory */
3720 free(g_lpwstrAppDir);
3721
3722 /* Free the stored command line */
3723 free(g_lpwstrCmdLine);
3724
3725 InternalLeaveCriticalSection(pThread, &g_csProcess);
3726
3727 //
3728 // Object manager shutdown will handle freeing the underlying
3729 // thread and process data
3730 //
3731
3732}
3733
3734/*++
3735Function:
3736 PROCAddThread
3737
3738Abstract
3739 Add a thread to the thread list of the current process
3740
3741Parameter
3742 pThread: Thread object
3743
3744--*/
3745VOID
3746CorUnix::PROCAddThread(
3747 CPalThread *pCurrentThread,
3748 CPalThread *pTargetThread
3749 )
3750{
3751 /* protect the access of the thread list with critical section for
3752 mutithreading access */
3753 InternalEnterCriticalSection(pCurrentThread, &g_csProcess);
3754
3755 pTargetThread->SetNext(pGThreadList);
3756 pGThreadList = pTargetThread;
3757 g_dwThreadCount += 1;
3758
3759 TRACE("Thread 0x%p (id %#x) added to the process thread list\n",
3760 pTargetThread, pTargetThread->GetThreadId());
3761
3762 InternalLeaveCriticalSection(pCurrentThread, &g_csProcess);
3763}
3764
3765
3766/*++
3767Function:
3768 PROCRemoveThread
3769
3770Abstract
3771 Remove a thread form the thread list of the current process
3772
3773Parameter
3774 CPalThread *pThread : thread object to remove
3775
3776(no return value)
3777--*/
3778VOID
3779CorUnix::PROCRemoveThread(
3780 CPalThread *pCurrentThread,
3781 CPalThread *pTargetThread
3782 )
3783{
3784 CPalThread *curThread, *prevThread;
3785
3786 /* protect the access of the thread list with critical section for
3787 mutithreading access */
3788 InternalEnterCriticalSection(pCurrentThread, &g_csProcess);
3789
3790 curThread = pGThreadList;
3791
3792 /* if thread list is empty */
3793 if (curThread == NULL)
3794 {
3795 ASSERT("Thread list is empty.\n");
3796 goto EXIT;
3797 }
3798
3799 /* do we remove the first thread? */
3800 if (curThread == pTargetThread)
3801 {
3802 pGThreadList = curThread->GetNext();
3803 TRACE("Thread 0x%p (id %#x) removed from the process thread list\n",
3804 pTargetThread, pTargetThread->GetThreadId());
3805 goto EXIT;
3806 }
3807
3808 prevThread = curThread;
3809 curThread = curThread->GetNext();
3810 /* find the thread to remove */
3811 while (curThread != NULL)
3812 {
3813 if (curThread == pTargetThread)
3814 {
3815 /* found, fix the chain list */
3816 prevThread->SetNext(curThread->GetNext());
3817 g_dwThreadCount -= 1;
3818 TRACE("Thread %p removed from the process thread list\n", pTargetThread);
3819 goto EXIT;
3820 }
3821
3822 prevThread = curThread;
3823 curThread = curThread->GetNext();
3824 }
3825
3826 WARN("Thread %p not removed (it wasn't found in the list)\n", pTargetThread);
3827
3828EXIT:
3829 InternalLeaveCriticalSection(pCurrentThread, &g_csProcess);
3830}
3831
3832
3833/*++
3834Function:
3835 PROCGetNumberOfThreads
3836
3837Abstract
3838 Return the number of threads in the thread list.
3839
3840Parameter
3841 void
3842
3843Return
3844 the number of threads.
3845--*/
3846INT
3847CorUnix::PROCGetNumberOfThreads(
3848 VOID)
3849{
3850 return g_dwThreadCount;
3851}
3852
3853
3854/*++
3855Function:
3856 PROCProcessLock
3857
3858Abstract
3859 Enter the critical section associated to the current process
3860
3861Parameter
3862 void
3863
3864Return
3865 void
3866--*/
3867VOID
3868PROCProcessLock(
3869 VOID)
3870{
3871 CPalThread * pThread =
3872 (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : NULL);
3873
3874 InternalEnterCriticalSection(pThread, &g_csProcess);
3875}
3876
3877
3878/*++
3879Function:
3880 PROCProcessUnlock
3881
3882Abstract
3883 Leave the critical section associated to the current process
3884
3885Parameter
3886 void
3887
3888Return
3889 void
3890--*/
3891VOID
3892PROCProcessUnlock(
3893 VOID)
3894{
3895 CPalThread * pThread =
3896 (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : NULL);
3897
3898 InternalLeaveCriticalSection(pThread, &g_csProcess);
3899}
3900
3901#if USE_SYSV_SEMAPHORES
3902/*++
3903Function:
3904 PROCCleanupThreadSemIds
3905
3906Abstract
3907 Cleanup SysV semaphore ids for all threads
3908
3909(no parameters, no return value)
3910--*/
3911VOID
3912PROCCleanupThreadSemIds(void)
3913{
3914 //
3915 // When using SysV semaphores, the semaphore ids used by PAL threads must be removed
3916 // so they can be used again.
3917 //
3918
3919 PROCProcessLock();
3920
3921 CPalThread *pTargetThread = pGThreadList;
3922 while (NULL != pTargetThread)
3923 {
3924 pTargetThread->suspensionInfo.DestroySemaphoreIds();
3925 pTargetThread = pTargetThread->GetNext();
3926 }
3927
3928 PROCProcessUnlock();
3929
3930}
3931#endif // USE_SYSV_SEMAPHORES
3932
3933/*++
3934Function:
3935 TerminateCurrentProcessNoExit
3936
3937Abstract:
3938 Terminate current Process, but leave the caller alive
3939
3940Parameters:
3941 BOOL bTerminateUnconditionally - If this is set, the PAL will exit as
3942 quickly as possible. In particular, it will not unload DLLs.
3943
3944Return value :
3945 No return
3946
3947Note:
3948 This function is used in ExitThread and TerminateProcess
3949
3950--*/
3951VOID
3952CorUnix::TerminateCurrentProcessNoExit(BOOL bTerminateUnconditionally)
3953{
3954 BOOL locked;
3955 DWORD old_terminator;
3956
3957 old_terminator = InterlockedCompareExchange(&terminator, GetCurrentThreadId(), 0);
3958
3959 if (0 != old_terminator && GetCurrentThreadId() != old_terminator)
3960 {
3961 /* another thread has already initiated the termination process. we
3962 could just block on the PALInitLock critical section, but then
3963 PROCSuspendOtherThreads would hang... so sleep forever here, we're
3964 terminating anyway
3965
3966 Update: [TODO] PROCSuspendOtherThreads has been removed. Can this
3967 code be changed? */
3968
3969 /* note that if *this* thread has already started the termination
3970 process, we want to proceed. the only way this can happen is if a
3971 call to DllMain (from ExitProcess) brought us here (because DllMain
3972 called ExitProcess, or TerminateProcess, or ExitThread);
3973 TerminateProcess won't call DllMain, so there's no danger to get
3974 caught in an infinite loop */
3975 WARN("termination already started from another thread; blocking.\n");
3976 poll(NULL, 0, INFTIM);
3977 }
3978
3979 /* Try to lock the initialization count to prevent multiple threads from
3980 terminating/initializing the PAL simultaneously */
3981
3982 /* note : it's also important to take this lock before the process lock,
3983 because Init/Shutdown take the init lock, and the functions they call
3984 may take the process lock. We must do it in the same order to avoid
3985 deadlocks */
3986
3987 locked = PALInitLock();
3988 if(locked && PALIsInitialized())
3989 {
3990 PROCNotifyProcessShutdown();
3991 PALCommonCleanup();
3992 }
3993}
3994
3995/*++
3996Function:
3997 PROCGetProcessStatus
3998
3999Abstract:
4000 Retrieve process state information (state & exit code).
4001
4002Parameters:
4003 DWORD process_id : PID of process to retrieve state for
4004 PROCESS_STATE *state : state of process (starting, running, done)
4005 DWORD *exit_code : exit code of process (from ExitProcess, etc.)
4006
4007Return value :
4008 TRUE on success
4009--*/
4010PAL_ERROR
4011PROCGetProcessStatus(
4012 CPalThread *pThread,
4013 HANDLE hProcess,
4014 PROCESS_STATE *pps,
4015 DWORD *pdwExitCode
4016 )
4017{
4018 PAL_ERROR palError = NO_ERROR;
4019 IPalObject *pobjProcess = NULL;
4020 IDataLock *pDataLock;
4021 CProcProcessLocalData *pLocalData;
4022 pid_t wait_retval;
4023 int status;
4024
4025 //
4026 // First, check if we already know the status of this process. This will be
4027 // the case if this function has already been called for the same process.
4028 //
4029
4030 palError = g_pObjectManager->ReferenceObjectByHandle(
4031 pThread,
4032 hProcess,
4033 &aotProcess,
4034 0,
4035 &pobjProcess
4036 );
4037
4038 if (NO_ERROR != palError)
4039 {
4040 goto PROCGetProcessStatusExit;
4041 }
4042
4043 palError = pobjProcess->GetProcessLocalData(
4044 pThread,
4045 WriteLock,
4046 &pDataLock,
4047 reinterpret_cast<void **>(&pLocalData)
4048 );
4049
4050 if (PS_DONE == pLocalData->ps)
4051 {
4052 TRACE("We already called waitpid() on process ID %#x; process has "
4053 "terminated, exit code is %d\n",
4054 pLocalData->dwProcessId, pLocalData->dwExitCode);
4055
4056 *pps = pLocalData->ps;
4057 *pdwExitCode = pLocalData->dwExitCode;
4058
4059 pDataLock->ReleaseLock(pThread, FALSE);
4060
4061 goto PROCGetProcessStatusExit;
4062 }
4063
4064 /* By using waitpid(), we can even retrieve the exit code of a non-PAL
4065 process. However, note that waitpid() can only provide the low 8 bits
4066 of the exit code. This is all that is required for the PAL spec. */
4067 TRACE("Looking for status of process; trying wait()");
4068
4069 while(1)
4070 {
4071 /* try to get state of process, using non-blocking call */
4072 wait_retval = waitpid(pLocalData->dwProcessId, &status, WNOHANG);
4073
4074 if ( wait_retval == (pid_t) pLocalData->dwProcessId )
4075 {
4076 /* success; get the exit code */
4077 if ( WIFEXITED( status ) )
4078 {
4079 *pdwExitCode = WEXITSTATUS(status);
4080 TRACE("Exit code was %d\n", *pdwExitCode);
4081 }
4082 else
4083 {
4084 WARN("process terminated without exiting; can't get exit "
4085 "code. faking it.\n");
4086 *pdwExitCode = EXIT_FAILURE;
4087 }
4088 *pps = PS_DONE;
4089 }
4090 else if (0 == wait_retval)
4091 {
4092 // The process is still running.
4093 TRACE("Process %#x is still active.\n", pLocalData->dwProcessId);
4094 *pps = PS_RUNNING;
4095 *pdwExitCode = 0;
4096 }
4097 else if (-1 == wait_retval)
4098 {
4099 // This might happen if waitpid() had already been called, but
4100 // this shouldn't happen - we call waitpid once, store the
4101 // result, and use that afterwards.
4102 // One legitimate cause of failure is EINTR; if this happens we
4103 // have to try again. A second legitimate cause is ECHILD, which
4104 // happens if we're trying to retrieve the status of a currently-
4105 // running process that isn't a child of this process.
4106 if(EINTR == errno)
4107 {
4108 TRACE("waitpid() failed with EINTR; re-waiting");
4109 continue;
4110 }
4111 else if (ECHILD == errno)
4112 {
4113 TRACE("waitpid() failed with ECHILD; calling kill instead");
4114 if (kill(pLocalData->dwProcessId, 0) != 0)
4115 {
4116 if(ESRCH == errno)
4117 {
4118 WARN("kill() failed with ESRCH, i.e. target "
4119 "process exited and it wasn't a child, "
4120 "so can't get the exit code, assuming "
4121 "it was 0.\n");
4122 *pdwExitCode = 0;
4123 }
4124 else
4125 {
4126 ERROR("kill(pid, 0) failed; errno is %d (%s)\n",
4127 errno, strerror(errno));
4128 *pdwExitCode = EXIT_FAILURE;
4129 }
4130 *pps = PS_DONE;
4131 }
4132 else
4133 {
4134 *pps = PS_RUNNING;
4135 *pdwExitCode = 0;
4136 }
4137 }
4138 else
4139 {
4140 // Ignoring unexpected waitpid errno and assuming that
4141 // the process is still running
4142 ERROR("waitpid(pid=%u) failed with unexpected errno=%d (%s)\n",
4143 pLocalData->dwProcessId, errno, strerror(errno));
4144 *pps = PS_RUNNING;
4145 *pdwExitCode = 0;
4146 }
4147 }
4148 else
4149 {
4150 ASSERT("waitpid returned unexpected value %d\n",wait_retval);
4151 *pdwExitCode = EXIT_FAILURE;
4152 *pps = PS_DONE;
4153 }
4154 // Break out of the loop in all cases except EINTR.
4155 break;
4156 }
4157
4158 // Save the exit code for future reference (waitpid will only work once).
4159 if(PS_DONE == *pps)
4160 {
4161 pLocalData->ps = PS_DONE;
4162 pLocalData->dwExitCode = *pdwExitCode;
4163 }
4164
4165 TRACE( "State of process 0x%08x : %d (exit code %d)\n",
4166 pLocalData->dwProcessId, *pps, *pdwExitCode );
4167
4168 pDataLock->ReleaseLock(pThread, TRUE);
4169
4170PROCGetProcessStatusExit:
4171
4172 if (NULL != pobjProcess)
4173 {
4174 pobjProcess->ReleaseReference(pThread);
4175 }
4176
4177 return palError;
4178}
4179
4180#ifdef __APPLE__
4181bool GetApplicationContainerFolder(PathCharString& buffer, const char *applicationGroupId, int applicationGroupIdLength)
4182{
4183 const char *homeDir = getpwuid(getuid())->pw_dir;
4184 int homeDirLength = strlen(homeDir);
4185
4186 // The application group container folder is defined as:
4187 // /user/{loginname}/Library/Group Containers/{AppGroupId}/
4188 return buffer.Set(homeDir, homeDirLength)
4189 && buffer.Append(APPLICATION_CONTAINER_BASE_PATH_SUFFIX)
4190 && buffer.Append(applicationGroupId, applicationGroupIdLength)
4191 && buffer.Append('/');
4192}
4193#endif // __APPLE__
4194
4195#ifdef _DEBUG
4196void PROCDumpThreadList()
4197{
4198 CPalThread *pThread;
4199
4200 PROCProcessLock();
4201
4202 TRACE ("Threads:{\n");
4203
4204 pThread = pGThreadList;
4205 while (NULL != pThread)
4206 {
4207 TRACE (" {pThr=0x%p tid=%#x lwpid=%#x state=%d finsusp=%d}\n",
4208 pThread, (int)pThread->GetThreadId(), (int)pThread->GetLwpId(),
4209 (int)pThread->synchronizationInfo.GetThreadState(),
4210 (int)pThread->suspensionInfo.GetSuspendedForShutdown());
4211
4212 pThread = pThread->GetNext();
4213 }
4214 TRACE ("Threads:}\n");
4215
4216 PROCProcessUnlock();
4217}
4218#endif
4219
4220/* Internal function definitions **********************************************/
4221
4222/*++
4223Function:
4224 getFileName
4225
4226Abstract:
4227 Helper function for CreateProcessW, it retrieves the executable filename
4228 from the application name, and the command line.
4229
4230Parameters:
4231 IN lpApplicationName: first parameter from CreateProcessW (an unicode string)
4232 IN lpCommandLine: second parameter from CreateProcessW (an unicode string)
4233 OUT lpFileName: file to be executed (the new process)
4234
4235Return:
4236 TRUE: if the file name is retrieved
4237 FALSE: otherwise
4238
4239--*/
4240static
4241BOOL
4242getFileName(
4243 LPCWSTR lpApplicationName,
4244 LPWSTR lpCommandLine,
4245 PathCharString& lpPathFileName)
4246{
4247 LPWSTR lpEnd;
4248 WCHAR wcEnd;
4249 char * lpFileName;
4250 PathCharString lpFileNamePS;
4251 char *lpTemp;
4252
4253 if (lpApplicationName)
4254 {
4255 int length = WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1,
4256 NULL, 0, NULL, NULL);
4257
4258 /* if only a file name is specified, prefix it with "./" */
4259 if ((*lpApplicationName != '.') && (*lpApplicationName != '/') &&
4260 (*lpApplicationName != '\\'))
4261 {
4262 length += 2;
4263 lpTemp = lpPathFileName.OpenStringBuffer(length);
4264
4265 if (strcpy_s(lpTemp, length, "./") != SAFECRT_SUCCESS)
4266 {
4267 ERROR("strcpy_s failed!\n");
4268 return FALSE;
4269 }
4270 lpTemp+=2;
4271
4272 }
4273 else
4274 {
4275 lpTemp = lpPathFileName.OpenStringBuffer(length);
4276 }
4277
4278 /* Convert to ASCII */
4279 length = WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1,
4280 lpTemp, length, NULL, NULL);
4281 if (length == 0)
4282 {
4283 lpPathFileName.CloseBuffer(0);
4284 ASSERT("WideCharToMultiByte failure\n");
4285 return FALSE;
4286 }
4287
4288 lpPathFileName.CloseBuffer(length -1);
4289
4290 /* Replace '\' by '/' */
4291 FILEDosToUnixPathA(lpTemp);
4292
4293 return TRUE;
4294 }
4295 else
4296 {
4297 /* use the Command line */
4298
4299 /* filename should be the first token of the command line */
4300
4301 /* first skip all leading whitespace */
4302 lpCommandLine = UTIL_inverse_wcspbrk(lpCommandLine,W16_WHITESPACE);
4303 if(NULL == lpCommandLine)
4304 {
4305 ERROR("CommandLine contains only whitespace!\n");
4306 return FALSE;
4307 }
4308
4309 /* check if it is starting with a quote (") character */
4310 if (*lpCommandLine == 0x0022)
4311 {
4312 lpCommandLine++; /* skip the quote */
4313
4314 /* file name ends with another quote */
4315 lpEnd = PAL_wcschr(lpCommandLine+1, 0x0022);
4316
4317 /* if no quotes found, set lpEnd to the end of the Command line */
4318 if (lpEnd == NULL)
4319 lpEnd = lpCommandLine + PAL_wcslen(lpCommandLine);
4320 }
4321 else
4322 {
4323 /* filename is end out by a whitespace */
4324 lpEnd = PAL_wcspbrk(lpCommandLine, W16_WHITESPACE);
4325
4326 /* if no whitespace found, set lpEnd to end of the Command line */
4327 if (lpEnd == NULL)
4328 {
4329 lpEnd = lpCommandLine + PAL_wcslen(lpCommandLine);
4330 }
4331 }
4332
4333 if (lpEnd == lpCommandLine)
4334 {
4335 ERROR("application name and command line are both empty!\n");
4336 return FALSE;
4337 }
4338
4339 /* replace the last character by a null */
4340 wcEnd = *lpEnd;
4341 *lpEnd = 0x0000;
4342
4343 /* Convert to ASCII */
4344 int size = 0;
4345 int length = (PAL_wcslen(lpCommandLine)+1) * sizeof(WCHAR);
4346 lpFileName = lpFileNamePS.OpenStringBuffer(length);
4347 if (NULL == lpFileName)
4348 {
4349 ERROR("Not Enough Memory!\n");
4350 return FALSE;
4351 }
4352 if (!(size = WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1,
4353 lpFileName, length, NULL, NULL)))
4354 {
4355 ASSERT("WideCharToMultiByte failure\n");
4356 return FALSE;
4357 }
4358
4359 lpFileNamePS.CloseBuffer(size - 1);
4360 /* restore last character */
4361 *lpEnd = wcEnd;
4362
4363 /* Replace '\' by '/' */
4364 FILEDosToUnixPathA(lpFileName);
4365 if (!getPath(lpFileNamePS, lpPathFileName))
4366 {
4367 /* file is not in the path */
4368 return FALSE;
4369 }
4370 }
4371 return TRUE;
4372}
4373
4374/*++
4375Function:
4376 checkFileType
4377
4378Abstract:
4379 Return the type of the file.
4380
4381Parameters:
4382 IN lpFileName: file name
4383
4384Return:
4385 FILE_DIR: Directory
4386 FILE_UNIX: Unix executable file
4387 FILE_ERROR: Error
4388--*/
4389static
4390int
4391checkFileType( LPCSTR lpFileName)
4392{
4393 struct stat stat_data;
4394
4395 /* check if the file exist */
4396 if ( access(lpFileName, F_OK) != 0 )
4397 {
4398 return FILE_ERROR;
4399 }
4400
4401 /* if it's not a PE/COFF file, check if it is executable */
4402 if ( -1 != stat( lpFileName, &stat_data ) )
4403 {
4404 if((stat_data.st_mode & S_IFMT) == S_IFDIR )
4405 {
4406 /*The given file is a directory*/
4407 return FILE_DIR;
4408 }
4409 if ( UTIL_IsExecuteBitsSet( &stat_data ) )
4410 {
4411 return FILE_UNIX;
4412 }
4413 else
4414 {
4415 return FILE_ERROR;
4416 }
4417 }
4418 return FILE_ERROR;
4419
4420}
4421
4422
4423/*++
4424Function:
4425 buildArgv
4426
4427Abstract:
4428 Helper function for CreateProcessW, it builds the array of argument in
4429 a format than can be passed to execve function.lppArgv is allocated
4430 in this function and must be freed by the caller.
4431
4432Parameters:
4433 IN lpCommandLine: second parameter from CreateProcessW (an unicode string)
4434 IN lpAppPath: cannonical name of the application to launched
4435 OUT lppArgv: array of arguments to be passed to the new process
4436
4437Return:
4438 the number of arguments
4439
4440note: this doesn't yet match precisely the behavior of Windows, but should be
4441sufficient.
4442what's here:
44431) stripping nonquoted whitespace
44442) handling of quoted parameters and quoted "parts" of parameters, removal of
4445 doublequotes (<aaaa"b bbb b"ccc> becomes <aaaab bbb bccc>)
44463) \" as an escaped doublequote, both within doublequoted sequences and out
4447what's known missing :
44481) \\ as an escaped backslash, but only if the string of '\'
4449 is followed by a " (escaped or not)
44502) "alternate" escape sequence : double-doublequote within a double-quoted
4451 argument (<"aaa a""aa aaa">) expands to a single-doublequote(<aaa a"aa aaa>)
4452note that there may be other special cases
4453--*/
4454static
4455char **
4456buildArgv(
4457 LPCWSTR lpCommandLine,
4458 PathCharString& lpAppPath,
4459 UINT *pnArg)
4460{
4461 CPalThread *pThread = NULL;
4462 UINT iWlen;
4463 char *lpAsciiCmdLine;
4464 char *pChar;
4465 char **lppArgv;
4466 char **lppTemp;
4467 UINT i,j;
4468
4469 *pnArg = 0;
4470
4471 iWlen = WideCharToMultiByte(CP_ACP,0,lpCommandLine,-1,NULL,0,NULL,NULL);
4472
4473 if(0 == iWlen)
4474 {
4475 ASSERT("Can't determine length of command line\n");
4476 return NULL;
4477 }
4478
4479 pThread = InternalGetCurrentThread();
4480 /* make sure to allocate enough space, up for the worst case scenario */
4481 int iLength = (iWlen + lpAppPath.GetCount() + 2);
4482 lpAsciiCmdLine = (char *) InternalMalloc(iLength);
4483
4484 if (lpAsciiCmdLine == NULL)
4485 {
4486 ERROR("Unable to allocate memory\n");
4487 return NULL;
4488 }
4489
4490 pChar = lpAsciiCmdLine;
4491
4492 /* put the cannonical name of the application as the first parameter */
4493 if ((strcpy_s(lpAsciiCmdLine, iLength, "\"") != SAFECRT_SUCCESS) ||
4494 (strcat_s(lpAsciiCmdLine, iLength, lpAppPath) != SAFECRT_SUCCESS) ||
4495 (strcat_s(lpAsciiCmdLine, iLength, "\"") != SAFECRT_SUCCESS) ||
4496 (strcat_s(lpAsciiCmdLine, iLength, " ") != SAFECRT_SUCCESS))
4497 {
4498 ERROR("strcpy_s/strcat_s failed!\n");
4499 return NULL;
4500 }
4501
4502 pChar = lpAsciiCmdLine + strlen (lpAsciiCmdLine);
4503
4504 /* let's skip the first argument in the command line */
4505
4506 /* strip leading whitespace; function returns NULL if there's only
4507 whitespace, so the if statement below will work correctly */
4508 lpCommandLine = UTIL_inverse_wcspbrk((LPWSTR)lpCommandLine, W16_WHITESPACE);
4509
4510 if (lpCommandLine)
4511 {
4512 LPCWSTR stringstart = lpCommandLine;
4513
4514 do
4515 {
4516 /* find first whitespace or dquote character */
4517 lpCommandLine = PAL_wcspbrk(lpCommandLine,W16_WHITESPACE_DQUOTE);
4518 if(NULL == lpCommandLine)
4519 {
4520 /* no whitespace or dquote found : first arg is only arg */
4521 break;
4522 }
4523 else if('"' == *lpCommandLine)
4524 {
4525 /* got a dquote; skip over it if it's escaped; make sure we
4526 don't try to look before the first character in the
4527 string */
4528 if(lpCommandLine > stringstart && '\\' == lpCommandLine[-1])
4529 {
4530 lpCommandLine++;
4531 continue;
4532 }
4533
4534 /* found beginning of dquoted sequence, run to the end */
4535 /* don't stop if we hit an escaped dquote */
4536 lpCommandLine++;
4537 while( *lpCommandLine )
4538 {
4539 lpCommandLine = PAL_wcschr(lpCommandLine, '"');
4540 if(NULL == lpCommandLine)
4541 {
4542 /* no ending dquote, arg runs to end of string */
4543 break;
4544 }
4545 if('\\' != lpCommandLine[-1])
4546 {
4547 /* dquote is not escaped, dquoted sequence is over*/
4548 break;
4549 }
4550 lpCommandLine++;
4551 }
4552 if(NULL == lpCommandLine || '\0' == *lpCommandLine)
4553 {
4554 /* no terminating dquote */
4555 break;
4556 }
4557
4558 /* step over dquote, keep looking for end of arg */
4559 lpCommandLine++;
4560 }
4561 else
4562 {
4563 /* found whitespace : end of arg. */
4564 lpCommandLine++;
4565 break;
4566 }
4567 }while(lpCommandLine);
4568 }
4569
4570 /* Convert to ASCII */
4571 if (lpCommandLine)
4572 {
4573 if (!WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1,
4574 pChar, iWlen+1, NULL, NULL))
4575 {
4576 ASSERT("Unable to convert to a multibyte string\n");
4577 free(lpAsciiCmdLine);
4578 return NULL;
4579 }
4580 }
4581
4582 pChar = lpAsciiCmdLine;
4583
4584 /* loops through all the arguments, to find out how many arguments there
4585 are; while looping replace whitespace by \0 */
4586
4587 /* skip leading whitespace (and replace by '\0') */
4588 /* note : there shouldn't be any, command starts either with PE loader name
4589 or computed application path, but this won't hurt */
4590 while (*pChar)
4591 {
4592 if (!isspace((unsigned char) *pChar))
4593 {
4594 break;
4595 }
4596 WARN("unexpected whitespace in command line!\n");
4597 *pChar++ = '\0';
4598 }
4599
4600 while (*pChar)
4601 {
4602 (*pnArg)++;
4603
4604 /* find end of current arg */
4605 while(*pChar && !isspace((unsigned char) *pChar))
4606 {
4607 if('"' == *pChar)
4608 {
4609 /* skip over dquote if it's escaped; make sure we don't try to
4610 look before the start of the string for the \ */
4611 if(pChar > lpAsciiCmdLine && '\\' == pChar[-1])
4612 {
4613 pChar++;
4614 continue;
4615 }
4616
4617 /* found leading dquote : look for ending dquote */
4618 pChar++;
4619 while (*pChar)
4620 {
4621 pChar = strchr(pChar,'"');
4622 if(NULL == pChar)
4623 {
4624 /* no ending dquote found : argument extends to the end
4625 of the string*/
4626 break;
4627 }
4628 if('\\' != pChar[-1])
4629 {
4630 /* found a dquote, and it's not escaped : quoted
4631 sequence is over*/
4632 break;
4633 }
4634 /* found a dquote, but it was escaped : skip over it, keep
4635 looking */
4636 pChar++;
4637 }
4638 if(NULL == pChar || '\0' == *pChar)
4639 {
4640 /* reached the end of the string : we're done */
4641 break;
4642 }
4643 }
4644 pChar++;
4645 }
4646 if(NULL == pChar)
4647 {
4648 /* reached the end of the string : we're done */
4649 break;
4650 }
4651 /* reached end of arg; replace trailing whitespace by '\0', to split
4652 arguments into separate strings */
4653 while (isspace((unsigned char) *pChar))
4654 {
4655 *pChar++ = '\0';
4656 }
4657 }
4658
4659 /* allocate lppargv according to the number of arguments
4660 in the command line */
4661 lppArgv = (char **) InternalMalloc((((*pnArg)+1) * sizeof(char *)));
4662
4663 if (lppArgv == NULL)
4664 {
4665 free(lpAsciiCmdLine);
4666 return NULL;
4667 }
4668
4669 lppTemp = lppArgv;
4670
4671 /* at this point all parameters are separated by NULL
4672 we need to fill the array of arguments; we must also remove all dquotes
4673 from arguments (new process shouldn't see them) */
4674 for (i = *pnArg, pChar = lpAsciiCmdLine; i; i--)
4675 {
4676 /* skip NULLs */
4677 while (!*pChar)
4678 {
4679 pChar++;
4680 }
4681
4682 *lppTemp = pChar;
4683
4684 /* go to the next parameter, removing dquotes as we go along */
4685 j = 0;
4686 while (*pChar)
4687 {
4688 /* copy character if it's not a dquote */
4689 if('"' != *pChar)
4690 {
4691 /* if it's the \ of an escaped dquote, skip over it, we'll
4692 copy the " instead */
4693 if( '\\' == pChar[0] && '"' == pChar[1] )
4694 {
4695 pChar++;
4696 }
4697 (*lppTemp)[j++] = *pChar;
4698 }
4699 pChar++;
4700 }
4701 /* re-NULL terminate the argument */
4702 (*lppTemp)[j] = '\0';
4703
4704 lppTemp++;
4705 }
4706
4707 *lppTemp = NULL;
4708
4709 return lppArgv;
4710}
4711
4712
4713/*++
4714Function:
4715 getPath
4716
4717Abstract:
4718 Helper function for CreateProcessW, it looks in the path environment
4719 variable to find where the process to executed is.
4720
4721Parameters:
4722 IN lpFileName: file name to search in the path
4723 OUT lpPathFileName: returned string containing the path and the filename
4724
4725Return:
4726 TRUE if found
4727 FALSE otherwise
4728--*/
4729static
4730BOOL
4731getPath(
4732 PathCharString& lpFileNameString,
4733 PathCharString& lpPathFileName)
4734{
4735 LPSTR lpPath;
4736 LPSTR lpNext;
4737 LPSTR lpCurrent;
4738 LPWSTR lpwstr;
4739 INT n;
4740 INT nextLen;
4741 INT slashLen;
4742 CPalThread *pThread = NULL;
4743 LPCSTR lpFileName = lpFileNameString.GetString();
4744
4745 /* if a path is specified, only look there */
4746 if(strchr(lpFileName, '/'))
4747 {
4748 if (access (lpFileName, F_OK) == 0)
4749 {
4750 if (!lpPathFileName.Set(lpFileNameString))
4751 {
4752 TRACE("Set of StackString failed!\n");
4753 return FALSE;
4754 }
4755
4756 TRACE("file %s exists\n", lpFileName);
4757 return TRUE;
4758 }
4759 else
4760 {
4761 TRACE("file %s doesn't exist.\n", lpFileName);
4762 return FALSE;
4763 }
4764 }
4765
4766 /* first look in directory from which the application loaded */
4767 lpwstr = g_lpwstrAppDir;
4768
4769 if (lpwstr)
4770 {
4771 /* convert path to multibyte, check buffer size */
4772 n = WideCharToMultiByte(CP_ACP, 0, lpwstr, -1, NULL, 0,
4773 NULL, NULL);
4774
4775 if (!lpPathFileName.Reserve(n + lpFileNameString.GetCount() + 1 ))
4776 {
4777 ERROR("StackString Reserve failed!\n");
4778 return FALSE;
4779 }
4780
4781 lpPath = lpPathFileName.OpenStringBuffer(n);
4782
4783 n = WideCharToMultiByte(CP_ACP, 0, lpwstr, -1, lpPath, n,
4784 NULL, NULL);
4785
4786 if (n == 0)
4787 {
4788 lpPathFileName.CloseBuffer(0);
4789 ASSERT("WideCharToMultiByte failure!\n");
4790 return FALSE;
4791 }
4792
4793 lpPathFileName.CloseBuffer(n - 1);
4794
4795 lpPathFileName.Append("/", 1);
4796 lpPathFileName.Append(lpFileNameString);
4797
4798 if (access(lpPathFileName, F_OK) == 0)
4799 {
4800 TRACE("found %s in application directory (%s)\n", lpFileName, lpPathFileName.GetString());
4801 return TRUE;
4802 }
4803 }
4804
4805 /* then try the current directory */
4806 if (!lpPathFileName.Reserve(lpFileNameString.GetCount() + 2))
4807 {
4808 ERROR("StackString Reserve failed!\n");
4809 return FALSE;
4810 }
4811
4812 lpPathFileName.Set("./", 2);
4813 lpPathFileName.Append(lpFileNameString);
4814
4815 if (access (lpPathFileName, R_OK) == 0)
4816 {
4817 TRACE("found %s in current directory.\n", lpFileName);
4818 return TRUE;
4819 }
4820
4821 pThread = InternalGetCurrentThread();
4822
4823 /* Then try to look in the path */
4824 lpPath = EnvironGetenv("PATH");
4825
4826 if (!lpPath)
4827 {
4828 ERROR("EnvironGetenv returned NULL for $PATH\n");
4829 return FALSE;
4830 }
4831
4832 lpNext = lpPath;
4833
4834 /* search in every path directory */
4835 TRACE("looking for file %s in $PATH (%s)\n", lpFileName, lpPath);
4836 while (lpNext)
4837 {
4838 /* skip all leading ':' */
4839 while(*lpNext==':')
4840 {
4841 lpNext++;
4842 }
4843
4844 /* search for ':' */
4845 lpCurrent = strchr(lpNext, ':');
4846 if (lpCurrent)
4847 {
4848 *lpCurrent++ = '\0';
4849 }
4850
4851 nextLen = strlen(lpNext);
4852 slashLen = (lpNext[nextLen-1] == '/') ? 0:1;
4853
4854 if (!lpPathFileName.Reserve(nextLen + lpFileNameString.GetCount() + 1))
4855 {
4856 free(lpPath);
4857 ERROR("StackString ran out of memory for full path\n");
4858 return FALSE;
4859 }
4860
4861 lpPathFileName.Set(lpNext, nextLen);
4862
4863 if( slashLen == 1)
4864 {
4865 /* append a '/' if there's no '/' at the end of the path */
4866 lpPathFileName.Append("/", 1);
4867 }
4868
4869 lpPathFileName.Append(lpFileNameString);
4870
4871 if ( access (lpPathFileName, F_OK) == 0)
4872 {
4873 TRACE("Found %s in $PATH element %s\n", lpFileName, lpNext);
4874 free(lpPath);
4875 return TRUE;
4876 }
4877
4878 lpNext = lpCurrent; /* search in the next directory */
4879 }
4880
4881 free(lpPath);
4882 TRACE("File %s not found in $PATH\n", lpFileName);
4883 return FALSE;
4884}
4885
4886/*++
4887Function:
4888 ~CProcProcessLocalData
4889
4890Process data destructor
4891--*/
4892CorUnix::CProcProcessLocalData::~CProcProcessLocalData()
4893{
4894 if (pProcessModules != NULL)
4895 {
4896 DestroyProcessModules(pProcessModules);
4897 }
4898}
4899
4900