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 init/pal.cpp
12
13Abstract:
14
15 Implementation of PAL exported functions not part of the Win32 API.
16
17
18
19--*/
20
21#include "pal/dbgmsg.h"
22SET_DEFAULT_DEBUG_CHANNEL(PAL); // some headers have code with asserts, so do this first
23
24#include "pal/thread.hpp"
25#include "pal/synchobjects.hpp"
26#include "pal/procobj.hpp"
27#include "pal/cs.hpp"
28#include "pal/file.hpp"
29#include "pal/map.hpp"
30#include "../objmgr/shmobjectmanager.hpp"
31#include "pal/seh.hpp"
32#include "pal/palinternal.h"
33#include "pal/sharedmemory.h"
34#include "pal/shmemory.h"
35#include "pal/process.h"
36#include "../thread/procprivate.hpp"
37#include "pal/module.h"
38#include "pal/virtual.h"
39#include "pal/misc.h"
40#include "pal/environ.h"
41#include "pal/utils.h"
42#include "pal/debug.h"
43#include "pal/locale.h"
44#include "pal/init.h"
45#include "pal/numa.h"
46#include "pal/stackstring.hpp"
47#include "pal/cgroup.h"
48
49#if HAVE_MACH_EXCEPTIONS
50#include "../exception/machexception.h"
51#endif
52
53#include <stdlib.h>
54#include <unistd.h>
55#include <pwd.h>
56#include <errno.h>
57#include <sys/types.h>
58#include <sys/param.h>
59#include <sys/resource.h>
60#include <sys/stat.h>
61#include <limits.h>
62#include <string.h>
63#include <fcntl.h>
64
65#if HAVE_POLL
66#include <poll.h>
67#else
68#include "pal/fakepoll.h"
69#endif // HAVE_POLL
70
71#if defined(__APPLE__)
72#include <sys/sysctl.h>
73int CacheLineSize;
74#endif //__APPLE__
75
76#ifdef __APPLE__
77#include <mach-o/dyld.h>
78#endif // __APPLE__
79
80#ifdef __NetBSD__
81#include <sys/cdefs.h>
82#include <sys/param.h>
83#include <sys/sysctl.h>
84#include <kvm.h>
85#endif
86
87#include <algorithm>
88
89using namespace CorUnix;
90
91//
92// $$TODO The C++ compiler doesn't like pal/cruntime.h so duplicate the
93// necessary prototype here
94//
95
96extern "C" BOOL CRTInitStdStreams( void );
97
98Volatile<INT> init_count = 0;
99Volatile<BOOL> shutdown_intent = 0;
100Volatile<LONG> g_coreclrInitialized = 0;
101static BOOL g_fThreadDataAvailable = FALSE;
102static pthread_mutex_t init_critsec_mutex = PTHREAD_MUTEX_INITIALIZER;
103
104// The default minimum stack size
105SIZE_T g_defaultStackSize = 0;
106
107/* critical section to protect access to init_count. This is allocated on the
108 very first PAL_Initialize call, and is freed afterward. */
109static PCRITICAL_SECTION init_critsec = NULL;
110
111static DWORD g_initializeDLLFlags = PAL_INITIALIZE_DLL;
112
113static int Initialize(int argc, const char *const argv[], DWORD flags);
114static BOOL INIT_IncreaseDescriptorLimit(void);
115static LPWSTR INIT_FormatCommandLine (int argc, const char * const *argv);
116static LPWSTR INIT_ConvertEXEPath(LPCSTR exe_name);
117static BOOL INIT_SharedFilesPath(void);
118
119#ifdef _DEBUG
120extern void PROCDumpThreadList(void);
121#endif
122
123#if defined(__APPLE__)
124static bool RunningNatively()
125{
126 int ret = 0;
127 size_t sz = sizeof(ret);
128 if (sysctlbyname("sysctl.proc_native", &ret, &sz, NULL, 0) != 0)
129 {
130 // if the sysctl failed, we'll assume this OS does not support
131 // binary translation - so we must be running natively.
132 return true;
133 }
134 return ret != 0;
135}
136#endif // __APPLE__
137
138/*++
139Function:
140 PAL_Initialize
141
142Abstract:
143 This function is the first function of the PAL to be called.
144 Internal structure initialization is done here. It could be called
145 several time by the same process, a reference count is kept.
146
147Return:
148 0 if successful
149 -1 if it failed
150
151--*/
152int
153PALAPI
154PAL_Initialize(
155 int argc,
156 const char *const argv[])
157{
158 return Initialize(argc, argv, PAL_INITIALIZE);
159}
160
161/*++
162Function:
163 PAL_InitializeWithFlags
164
165Abstract:
166 This function is the first function of the PAL to be called.
167 Internal structure initialization is done here. It could be called
168 several time by the same process, a reference count is kept.
169
170Return:
171 0 if successful
172 -1 if it failed
173
174--*/
175int
176PALAPI
177PAL_InitializeWithFlags(
178 int argc,
179 const char *const argv[],
180 DWORD flags)
181{
182 return Initialize(argc, argv, flags);
183}
184
185/*++
186Function:
187 PAL_InitializeDLL
188
189Abstract:
190 Initializes the non-runtime DLLs/modules like the DAC and SOS.
191
192Return:
193 0 if successful
194 -1 if it failed
195
196--*/
197int
198PALAPI
199PAL_InitializeDLL()
200{
201 return Initialize(0, NULL, g_initializeDLLFlags);
202}
203
204/*++
205Function:
206 PAL_SetInitializeDLLFlags
207
208Abstract:
209 This sets the global PAL_INITIALIZE flags that PAL_InitializeDLL
210 will use. It needs to be called before any PAL_InitializeDLL call
211 is made so typical it is used in a __attribute__((constructor))
212 function to make sure.
213
214Return:
215 none
216
217--*/
218void
219PALAPI
220PAL_SetInitializeDLLFlags(
221 DWORD flags)
222{
223 g_initializeDLLFlags = flags;
224}
225
226#ifdef ENSURE_PRIMARY_STACK_SIZE
227/*++
228Function:
229 EnsureStackSize
230
231Abstract:
232 This fixes a problem on MUSL where the initial stack size reported by the
233 pthread_attr_getstack is about 128kB, but this limit is not fixed and
234 the stack can grow dynamically. The problem is that it makes the
235 functions ReflectionInvocation::[Try]EnsureSufficientExecutionStack
236 to fail for real life scenarios like e.g. compilation of corefx.
237 Since there is no real fixed limit for the stack, the code below
238 ensures moving the stack limit to a value that makes reasonable
239 real life scenarios work.
240
241--*/
242__attribute__((noinline,optnone))
243void
244EnsureStackSize(SIZE_T stackSize)
245{
246 volatile uint8_t *s = (uint8_t *)_alloca(stackSize);
247 *s = 0;
248}
249#endif // ENSURE_PRIMARY_STACK_SIZE
250
251/*++
252Function:
253 InitializeDefaultStackSize
254
255Abstract:
256 Initializes the default stack size.
257
258--*/
259void
260InitializeDefaultStackSize()
261{
262 char* defaultStackSizeStr = getenv("COMPlus_DefaultStackSize");
263 if (defaultStackSizeStr != NULL)
264 {
265 errno = 0;
266 // Like all numeric values specific by the COMPlus_xxx variables, it is a
267 // hexadecimal string without any prefix.
268 long int size = strtol(defaultStackSizeStr, NULL, 16);
269
270 if (errno == 0)
271 {
272 g_defaultStackSize = std::max(size, (long int)PTHREAD_STACK_MIN);
273 }
274 }
275
276#ifdef ENSURE_PRIMARY_STACK_SIZE
277 if (g_defaultStackSize == 0)
278 {
279 // Set the default minimum stack size for MUSL to the same value as we
280 // use on Windows.
281 g_defaultStackSize = 1536 * 1024;
282 }
283#endif // ENSURE_PRIMARY_STACK_SIZE
284}
285
286/*++
287Function:
288 Initialize
289
290Abstract:
291 Common PAL initialization function.
292
293Return:
294 0 if successful
295 -1 if it failed
296
297--*/
298int
299Initialize(
300 int argc,
301 const char *const argv[],
302 DWORD flags)
303{
304 PAL_ERROR palError = ERROR_GEN_FAILURE;
305 CPalThread *pThread = NULL;
306 CSharedMemoryObjectManager *pshmom = NULL;
307 LPWSTR command_line = NULL;
308 LPWSTR exe_path = NULL;
309 int retval = -1;
310 bool fFirstTimeInit = false;
311
312 /* the first ENTRY within the first call to PAL_Initialize is a special
313 case, since debug channels are not initialized yet. So in that case the
314 ENTRY will be called after the DBG channels initialization */
315 ENTRY_EXTERNAL("PAL_Initialize(argc = %d argv = %p)\n", argc, argv);
316
317 /*Firstly initiate a lastError */
318 SetLastError(ERROR_GEN_FAILURE);
319
320#ifdef __APPLE__
321 if (!RunningNatively())
322 {
323 SetLastError(ERROR_BAD_FORMAT);
324 goto exit;
325 }
326#endif // __APPLE__
327
328 CriticalSectionSubSysInitialize();
329
330 if(NULL == init_critsec)
331 {
332 pthread_mutex_lock(&init_critsec_mutex); // prevents race condition of two threads
333 // initializing the critical section.
334 if(NULL == init_critsec)
335 {
336 static CRITICAL_SECTION temp_critsec;
337
338 // Want this critical section to NOT be internal to avoid the use of unsafe region markers.
339 InternalInitializeCriticalSectionAndSpinCount(&temp_critsec, 0, false);
340
341 if(NULL != InterlockedCompareExchangePointer(&init_critsec, &temp_critsec, NULL))
342 {
343 // Another thread got in before us! shouldn't happen, if the PAL
344 // isn't initialized there shouldn't be any other threads
345 WARN("Another thread initialized the critical section\n");
346 InternalDeleteCriticalSection(&temp_critsec);
347 }
348 }
349 pthread_mutex_unlock(&init_critsec_mutex);
350 }
351
352 InternalEnterCriticalSection(pThread, init_critsec); // here pThread is always NULL
353
354 if (init_count == 0)
355 {
356 // Set our pid and sid.
357 gPID = getpid();
358 gSID = getsid(gPID);
359
360 // The gSharedFilesPath is allocated dynamically so its destructor does not get
361 // called unexpectedly during cleanup
362 gSharedFilesPath = InternalNew<PathCharString>();
363 if (gSharedFilesPath == nullptr)
364 {
365 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
366 goto done;
367 }
368
369 if (INIT_SharedFilesPath() == FALSE)
370 {
371 goto done;
372 }
373
374 fFirstTimeInit = true;
375
376 InitializeDefaultStackSize();
377
378#ifdef ENSURE_PRIMARY_STACK_SIZE
379 if (flags & PAL_INITIALIZE_ENSURE_STACK_SIZE)
380 {
381 EnsureStackSize(g_defaultStackSize);
382 }
383#endif // ENSURE_PRIMARY_STACK_SIZE
384
385 // Initialize the TLS lookaside cache
386 if (FALSE == TLSInitialize())
387 {
388 goto done;
389 }
390
391 InitializeCGroup();
392
393 // Initialize the environment.
394 if (FALSE == EnvironInitialize())
395 {
396 goto CLEANUP0;
397 }
398
399 // Initialize debug channel settings before anything else.
400 // This depends on the environment, so it must come after
401 // EnvironInitialize.
402 if (FALSE == DBG_init_channels())
403 {
404 goto CLEANUP0;
405 }
406
407 if (!INIT_IncreaseDescriptorLimit())
408 {
409 ERROR("Unable to increase the file descriptor limit!\n");
410 // We can continue if this fails; we'll just have problems if
411 // we use large numbers of threads or have many open files.
412 }
413
414 if (!SharedMemoryManager::StaticInitialize())
415 {
416 ERROR("Shared memory static initialization failed!\n");
417 goto CLEANUP0;
418 }
419
420 /* initialize the shared memory infrastructure */
421 if (!SHMInitialize())
422 {
423 ERROR("Shared memory initialization failed!\n");
424 goto CLEANUP0;
425 }
426
427 //
428 // Initialize global process data
429 //
430
431 palError = InitializeProcessData();
432 if (NO_ERROR != palError)
433 {
434 ERROR("Unable to initialize process data\n");
435 goto CLEANUP1;
436 }
437
438#if HAVE_MACH_EXCEPTIONS
439 // Mach exception port needs to be set up before the thread
440 // data or threads are set up.
441 if (!SEHInitializeMachExceptions(flags))
442 {
443 ERROR("SEHInitializeMachExceptions failed!\n");
444 palError = ERROR_GEN_FAILURE;
445 goto CLEANUP1;
446 }
447#endif // HAVE_MACH_EXCEPTIONS
448
449 //
450 // Allocate the initial thread data
451 //
452
453 palError = CreateThreadData(&pThread);
454 if (NO_ERROR != palError)
455 {
456 ERROR("Unable to create initial thread data\n");
457 goto CLEANUP1a;
458 }
459
460 PROCAddThread(pThread, pThread);
461
462 //
463 // Initialize mutex and condition variable used to synchronize the ending threads count
464 //
465
466 palError = InitializeEndingThreadsData();
467 if (NO_ERROR != palError)
468 {
469 ERROR("Unable to create ending threads data\n");
470 goto CLEANUP1b;
471 }
472
473 //
474 // It's now safe to access our thread data
475 //
476
477 g_fThreadDataAvailable = TRUE;
478
479 //
480 // Initialize module manager
481 //
482 if (FALSE == LOADInitializeModules())
483 {
484 ERROR("Unable to initialize module manager\n");
485 palError = ERROR_INTERNAL_ERROR;
486 goto CLEANUP1b;
487 }
488
489 //
490 // Initialize the object manager
491 //
492
493 pshmom = InternalNew<CSharedMemoryObjectManager>();
494 if (NULL == pshmom)
495 {
496 ERROR("Unable to allocate new object manager\n");
497 palError = ERROR_OUTOFMEMORY;
498 goto CLEANUP1b;
499 }
500
501 palError = pshmom->Initialize();
502 if (NO_ERROR != palError)
503 {
504 ERROR("object manager initialization failed!\n");
505 InternalDelete(pshmom);
506 goto CLEANUP1b;
507 }
508
509 g_pObjectManager = pshmom;
510
511 //
512 // Initialize the synchronization manager
513 //
514 g_pSynchronizationManager =
515 CPalSynchMgrController::CreatePalSynchronizationManager();
516
517 if (NULL == g_pSynchronizationManager)
518 {
519 palError = ERROR_NOT_ENOUGH_MEMORY;
520 ERROR("Failure creating synchronization manager\n");
521 goto CLEANUP1c;
522 }
523 }
524 else
525 {
526 pThread = InternalGetCurrentThread();
527 }
528
529 palError = ERROR_GEN_FAILURE;
530
531 if (argc > 0 && argv != NULL)
532 {
533 /* build the command line */
534 command_line = INIT_FormatCommandLine(argc, argv);
535 if (NULL == command_line)
536 {
537 ERROR("Error building command line\n");
538 goto CLEANUP1d;
539 }
540
541 /* find out the application's full path */
542 exe_path = INIT_ConvertEXEPath(argv[0]);
543 if (NULL == exe_path)
544 {
545 ERROR("Unable to find exe path\n");
546 goto CLEANUP1e;
547 }
548
549 if (NULL == command_line || NULL == exe_path)
550 {
551 ERROR("Failed to process command-line parameters!\n");
552 goto CLEANUP2;
553 }
554
555 palError = InitializeProcessCommandLine(
556 command_line,
557 exe_path);
558
559 if (NO_ERROR != palError)
560 {
561 ERROR("Unable to initialize command line\n");
562 goto CLEANUP2;
563 }
564
565 // InitializeProcessCommandLine took ownership of this memory.
566 command_line = NULL;
567
568#ifdef PAL_PERF
569 // Initialize the Profiling structure
570 if(FALSE == PERFInitialize(command_line, exe_path))
571 {
572 ERROR("Performance profiling initial failed\n");
573 goto CLEANUP2;
574 }
575 PERFAllocThreadInfo();
576#endif
577
578 if (!LOADSetExeName(exe_path))
579 {
580 ERROR("Unable to set exe name\n");
581 goto CLEANUP2;
582 }
583
584 // LOADSetExeName took ownership of this memory.
585 exe_path = NULL;
586 }
587
588 if (init_count == 0)
589 {
590 //
591 // Create the initial process and thread objects
592 //
593 palError = CreateInitialProcessAndThreadObjects(pThread);
594 if (NO_ERROR != palError)
595 {
596 ERROR("Unable to create initial process and thread objects\n");
597 goto CLEANUP2;
598 }
599
600 palError = ERROR_GEN_FAILURE;
601
602 if (FALSE == TIMEInitialize())
603 {
604 ERROR("Unable to initialize TIME support\n");
605 goto CLEANUP6;
606 }
607
608 /* Initialize the File mapping critical section. */
609 if (FALSE == MAPInitialize())
610 {
611 ERROR("Unable to initialize file mapping support\n");
612 goto CLEANUP6;
613 }
614
615 /* Initialize the Virtual* functions. */
616 bool initializeExecutableMemoryAllocator = (flags & PAL_INITIALIZE_EXEC_ALLOCATOR) != 0;
617 if (FALSE == VIRTUALInitialize(initializeExecutableMemoryAllocator))
618 {
619 ERROR("Unable to initialize virtual memory support\n");
620 goto CLEANUP10;
621 }
622
623 if (flags & PAL_INITIALIZE_SYNC_THREAD)
624 {
625 //
626 // Tell the synchronization manager to start its worker thread
627 //
628 palError = CPalSynchMgrController::StartWorker(pThread);
629 if (NO_ERROR != palError)
630 {
631 ERROR("Synch manager failed to start worker thread\n");
632 goto CLEANUP13;
633 }
634 }
635
636 /* initialize structured exception handling stuff (signals, etc) */
637 if (FALSE == SEHInitialize(pThread, flags))
638 {
639 ERROR("Unable to initialize SEH support\n");
640 goto CLEANUP13;
641 }
642
643 if (flags & PAL_INITIALIZE_STD_HANDLES)
644 {
645 /* create file objects for standard handles */
646 if (!FILEInitStdHandles())
647 {
648 ERROR("Unable to initialize standard file handles\n");
649 goto CLEANUP14;
650 }
651 }
652
653 if (FALSE == CRTInitStdStreams())
654 {
655 ERROR("Unable to initialize CRT standard streams\n");
656 goto CLEANUP15;
657 }
658
659 if (FALSE == NUMASupportInitialize())
660 {
661 ERROR("Unable to initialize NUMA support\n");
662 goto CLEANUP15;
663 }
664
665 TRACE("First-time PAL initialization complete.\n");
666 init_count++;
667
668 /* Set LastError to a non-good value - functions within the
669 PAL startup may set lasterror to a nonzero value. */
670 SetLastError(NO_ERROR);
671 retval = 0;
672 }
673 else
674 {
675 init_count++;
676
677 // Behave the same wrt entering the PAL independent of whether this
678 // is the first call to PAL_Initialize or not. The first call implied
679 // PAL_Enter by virtue of creating the CPalThread for the current
680 // thread, and its starting state is to be in the PAL.
681 (void)PAL_Enter(PAL_BoundaryTop);
682
683 TRACE("Initialization count increases to %d\n", init_count.Load());
684
685 SetLastError(NO_ERROR);
686 retval = 0;
687 }
688 goto done;
689
690 NUMASupportCleanup();
691 /* No cleanup required for CRTInitStdStreams */
692CLEANUP15:
693 FILECleanupStdHandles();
694CLEANUP14:
695 SEHCleanup();
696CLEANUP13:
697 VIRTUALCleanup();
698CLEANUP10:
699 MAPCleanup();
700CLEANUP6:
701 PROCCleanupInitialProcess();
702CLEANUP2:
703 free(exe_path);
704CLEANUP1e:
705 free(command_line);
706CLEANUP1d:
707 // Cleanup synchronization manager
708CLEANUP1c:
709 // Cleanup object manager
710CLEANUP1b:
711 // Cleanup initial thread data
712CLEANUP1a:
713 // Cleanup global process data
714CLEANUP1:
715 SHMCleanup();
716CLEANUP0:
717 CleanupCGroup();
718 TLSCleanup();
719 ERROR("PAL_Initialize failed\n");
720 SetLastError(palError);
721done:
722#ifdef PAL_PERF
723 if( retval == 0)
724 {
725 PERFEnableProcessProfile();
726 PERFEnableThreadProfile(FALSE);
727 PERFCalibrate("Overhead of PERF entry/exit");
728 }
729#endif
730
731 InternalLeaveCriticalSection(pThread, init_critsec);
732
733 if (fFirstTimeInit && 0 == retval)
734 {
735 _ASSERTE(NULL != pThread);
736 }
737
738 if (retval != 0 && GetLastError() == ERROR_SUCCESS)
739 {
740 ASSERT("returning failure, but last error not set\n");
741 }
742
743#ifdef __APPLE__
744exit :
745#endif // __APPLE__
746 LOGEXIT("PAL_Initialize returns int %d\n", retval);
747 return retval;
748}
749
750
751/*++
752Function:
753 PAL_InitializeCoreCLR
754
755Abstract:
756 A replacement for PAL_Initialize when loading CoreCLR. Instead of taking a command line (which CoreCLR
757 instances aren't given anyway) the path into which the CoreCLR is installed is supplied instead. This is
758 cached so that PAL_GetPALDirectoryW can return it later.
759
760 This routine also makes sure the psuedo dynamic libraries PALRT and mscorwks have their initialization
761 methods called.
762
763Return:
764 ERROR_SUCCESS if successful
765 An error code, if it failed
766
767--*/
768PAL_ERROR
769PALAPI
770PAL_InitializeCoreCLR(const char *szExePath)
771{
772 // Fake up a command line to call PAL initialization with.
773 int result = Initialize(1, &szExePath, PAL_INITIALIZE_CORECLR);
774 if (result != 0)
775 {
776 return GetLastError();
777 }
778
779 // Check for a repeated call (this is a no-op).
780 if (InterlockedIncrement(&g_coreclrInitialized) > 1)
781 {
782 PAL_Enter(PAL_BoundaryTop);
783 return ERROR_SUCCESS;
784 }
785
786 // Now that the PAL is initialized it's safe to call the initialization methods for the code that used to
787 // be dynamically loaded libraries but is now statically linked into CoreCLR just like the PAL, i.e. the
788 // PAL RT and mscorwks.
789 if (!LOADInitializeCoreCLRModule())
790 {
791 return ERROR_DLL_INIT_FAILED;
792 }
793
794 if (!PROCAbortInitialize())
795 {
796 printf("PROCAbortInitialize FAILED %d (%s)\n", errno, strerror(errno));
797 return ERROR_GEN_FAILURE;
798 }
799
800 if (!InitializeFlushProcessWriteBuffers())
801 {
802 return ERROR_GEN_FAILURE;
803 }
804
805 return ERROR_SUCCESS;
806}
807
808/*++
809Function:
810PAL_IsDebuggerPresent
811
812Abstract:
813This function should be used to determine if a debugger is attached to the process.
814--*/
815PALIMPORT
816BOOL
817PALAPI
818PAL_IsDebuggerPresent()
819{
820#if defined(__linux__)
821 BOOL debugger_present = FALSE;
822 char buf[2048];
823
824 int status_fd = open("/proc/self/status", O_RDONLY);
825 if (status_fd == -1)
826 {
827 return FALSE;
828 }
829 ssize_t num_read = read(status_fd, buf, sizeof(buf) - 1);
830
831 if (num_read > 0)
832 {
833 static const char TracerPid[] = "TracerPid:";
834 char *tracer_pid;
835
836 buf[num_read] = '\0';
837 tracer_pid = strstr(buf, TracerPid);
838 if (tracer_pid)
839 {
840 debugger_present = !!atoi(tracer_pid + sizeof(TracerPid) - 1);
841 }
842 }
843
844 close(status_fd);
845
846 return debugger_present;
847#elif defined(__APPLE__)
848 struct kinfo_proc info = {};
849 size_t size = sizeof(info);
850 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
851 int ret = sysctl(mib, sizeof(mib)/sizeof(*mib), &info, &size, NULL, 0);
852
853 if (ret == 0)
854 return ((info.kp_proc.p_flag & P_TRACED) != 0);
855
856 return FALSE;
857#elif defined(__NetBSD__)
858 int traced;
859 kvm_t *kd;
860 int cnt;
861
862 struct kinfo_proc *info;
863
864 kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
865 if (kd == NULL)
866 return FALSE;
867
868 info = kvm_getprocs(kd, KERN_PROC_PID, getpid(), &cnt);
869 if (info == NULL || cnt < 1)
870 {
871 kvm_close(kd);
872 return FALSE;
873 }
874
875 traced = info->kp_proc.p_slflag & PSL_TRACED;
876 kvm_close(kd);
877
878 if (traced != 0)
879 return TRUE;
880 else
881 return FALSE;
882#else
883 return FALSE;
884#endif
885}
886
887/*++
888Function:
889 PAL_EntryPoint
890
891Abstract:
892 This function should be used to wrap code that uses PAL library on thread that was not created by PAL.
893--*/
894PALIMPORT
895DWORD_PTR
896PALAPI
897PAL_EntryPoint(
898 IN LPTHREAD_START_ROUTINE lpStartAddress,
899 IN LPVOID lpParameter)
900{
901 CPalThread *pThread;
902 DWORD_PTR retval = (DWORD) -1;
903
904 ENTRY("PAL_EntryPoint(lpStartAddress=%p, lpParameter=%p)\n", lpStartAddress, lpParameter);
905
906 pThread = InternalGetCurrentThread();
907 if (NULL == pThread)
908 {
909 /* This function works only for thread that called PAL_Initialize for now. */
910 ERROR( "Unable to get the thread object.\n" );
911 goto done;
912 }
913
914 retval = (*lpStartAddress)(lpParameter);
915
916done:
917 LOGEXIT("PAL_EntryPoint returns int %d\n", retval);
918 return retval;
919}
920
921/*++
922Function:
923 PAL_Shutdown
924
925Abstract:
926 This function shuts down the PAL WITHOUT exiting the current process.
927--*/
928void
929PALAPI
930PAL_Shutdown(
931 void)
932{
933 TerminateCurrentProcessNoExit(FALSE /* bTerminateUnconditionally */);
934}
935
936/*++
937Function:
938 PAL_Terminate
939
940Abstract:
941 This function is the called when a thread has finished using the PAL
942 library. It shuts down PAL and exits the current process.
943--*/
944void
945PALAPI
946PAL_Terminate(
947 void)
948{
949 PAL_TerminateEx(0);
950}
951
952/*++
953Function:
954PAL_TerminateEx
955
956Abstract:
957This function is the called when a thread has finished using the PAL
958library. It shuts down PAL and exits the current process with
959the specified exit code.
960--*/
961void
962PALAPI
963PAL_TerminateEx(
964 int exitCode)
965{
966 ENTRY_EXTERNAL("PAL_TerminateEx()\n");
967
968 if (NULL == init_critsec)
969 {
970 /* note that these macros probably won't output anything, since the
971 debug channels haven't been initialized yet */
972 ASSERT("PAL_Initialize has never been called!\n");
973 LOGEXIT("PAL_Terminate returns.\n");
974 }
975
976 // Declare the beginning of shutdown
977 PALSetShutdownIntent();
978
979 LOGEXIT("PAL_TerminateEx is exiting the current process.\n");
980 exit(exitCode);
981}
982
983/*++
984Function:
985 PAL_InitializeDebug
986
987Abstract:
988 This function is the called when cordbg attaches to the process.
989--*/
990void
991PALAPI
992PAL_InitializeDebug(
993 void)
994{
995 PERF_ENTRY(PAL_InitializeDebug);
996 ENTRY("PAL_InitializeDebug()\n");
997#if HAVE_MACH_EXCEPTIONS
998 MachExceptionInitializeDebug();
999#endif
1000 LOGEXIT("PAL_InitializeDebug returns\n");
1001 PERF_EXIT(PAL_InitializeDebug);
1002}
1003
1004/*++
1005Function:
1006 PALIsThreadDataInitialized
1007
1008Returns TRUE if startup has reached a point where thread data is available
1009--*/
1010BOOL PALIsThreadDataInitialized()
1011{
1012 return g_fThreadDataAvailable;
1013}
1014
1015/*++
1016Function:
1017 PALCommonCleanup
1018
1019 Utility function to prepare for shutdown.
1020
1021--*/
1022void
1023PALCommonCleanup()
1024{
1025 static bool cleanupDone = false;
1026
1027 // Declare the beginning of shutdown
1028 PALSetShutdownIntent();
1029
1030 if (!cleanupDone)
1031 {
1032 cleanupDone = true;
1033
1034 //
1035 // Let the synchronization manager know we're about to shutdown
1036 //
1037 CPalSynchMgrController::PrepareForShutdown();
1038
1039 SharedMemoryManager::StaticClose();
1040
1041#ifdef _DEBUG
1042 PROCDumpThreadList();
1043#endif
1044 }
1045
1046 // Mark that the PAL is uninitialized
1047 init_count = 0;
1048}
1049
1050BOOL PALIsShuttingDown()
1051{
1052 /* TODO: This function may be used to provide a reader/writer-like
1053 mechanism (or a ref counting one) to prevent PAL APIs that need to access
1054 PAL runtime data, from working when PAL is shutting down. Each of those API
1055 should acquire a read access while executing. The shutting down code would
1056 acquire a write lock, i.e. suspending any new incoming reader, and waiting
1057 for the current readers to be done. That would allow us to get rid of the
1058 dangerous suspend-all-other-threads at shutdown time */
1059 return shutdown_intent;
1060}
1061
1062void PALSetShutdownIntent()
1063{
1064 /* TODO: See comment in PALIsShuttingDown */
1065 shutdown_intent = TRUE;
1066}
1067
1068/*++
1069Function:
1070 PALInitLock
1071
1072Take the initializaiton critical section (init_critsec). necessary to serialize
1073TerminateProcess along with PAL_Terminate and PAL_Initialize
1074
1075(no parameters)
1076
1077Return value :
1078 TRUE if critical section existed (and was acquired)
1079 FALSE if critical section doens't exist yet
1080--*/
1081BOOL PALInitLock(void)
1082{
1083 if(!init_critsec)
1084 {
1085 return FALSE;
1086 }
1087
1088 CPalThread * pThread =
1089 (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : NULL);
1090
1091 InternalEnterCriticalSection(pThread, init_critsec);
1092 return TRUE;
1093}
1094
1095/*++
1096Function:
1097 PALInitUnlock
1098
1099Release the initialization critical section (init_critsec).
1100
1101(no parameters, no return value)
1102--*/
1103void PALInitUnlock(void)
1104{
1105 if(!init_critsec)
1106 {
1107 return;
1108 }
1109
1110 CPalThread * pThread =
1111 (PALIsThreadDataInitialized() ? InternalGetCurrentThread() : NULL);
1112
1113 InternalLeaveCriticalSection(pThread, init_critsec);
1114}
1115
1116/* Internal functions *********************************************************/
1117
1118/*++
1119Function:
1120 INIT_IncreaseDescriptorLimit [internal]
1121
1122Abstract:
1123 Calls setrlimit(2) to increase the maximum number of file descriptors
1124 this process can open.
1125
1126Return value:
1127 TRUE if the call to setrlimit succeeded; FALSE otherwise.
1128--*/
1129static BOOL INIT_IncreaseDescriptorLimit(void)
1130{
1131#ifndef DONT_SET_RLIMIT_NOFILE
1132 struct rlimit rlp;
1133 int result;
1134
1135 result = getrlimit(RLIMIT_NOFILE, &rlp);
1136 if (result != 0)
1137 {
1138 return FALSE;
1139 }
1140 // Set our soft limit for file descriptors to be the same
1141 // as the max limit.
1142 rlp.rlim_cur = rlp.rlim_max;
1143#ifdef __APPLE__
1144 // Based on compatibility note in setrlimit(2) manpage for OSX,
1145 // trim the limit to OPEN_MAX.
1146 if (rlp.rlim_cur > OPEN_MAX)
1147 {
1148 rlp.rlim_cur = OPEN_MAX;
1149 }
1150#endif
1151 result = setrlimit(RLIMIT_NOFILE, &rlp);
1152 if (result != 0)
1153 {
1154 return FALSE;
1155 }
1156#endif // !DONT_SET_RLIMIT_NOFILE
1157 return TRUE;
1158}
1159
1160/*++
1161Function:
1162 INIT_FormatCommandLine [Internal]
1163
1164Abstract:
1165 This function converts an array of arguments (argv) into a Unicode
1166 command-line for use by GetCommandLineW
1167
1168Parameters :
1169 int argc : number of arguments in argv
1170 char **argv : argument list in an array of NULL-terminated strings
1171
1172Return value :
1173 pointer to Unicode command line. This is a buffer allocated with malloc;
1174 caller is responsible for freeing it with free()
1175
1176Note : not all peculiarities of Windows command-line processing are supported;
1177
1178-what is supported :
1179 -arguments with white-space must be double quoted (we'll just double-quote
1180 all arguments to simplify things)
1181 -some characters must be escaped with \ : particularly, the double-quote,
1182 to avoid confusion with the double-quotes at the start and end of
1183 arguments, and \ itself, to avoid confusion with escape sequences.
1184-what is not supported:
1185 -under Windows, \\ is interpreted as an escaped \ ONLY if it's followed by
1186 an escaped double-quote \". \\\" is passed to argv as \", but \\a is
1187 passed to argv as \\a... there may be other similar cases
1188 -there may be other characters which must be escaped
1189--*/
1190static LPWSTR INIT_FormatCommandLine (int argc, const char * const *argv)
1191{
1192 LPWSTR retval;
1193 LPSTR command_line=NULL, command_ptr;
1194 LPCSTR arg_ptr;
1195 INT length, i,j;
1196 BOOL bQuoted = FALSE;
1197
1198 /* list of characters that need no be escaped with \ when building the
1199 command line. currently " and \ */
1200 LPCSTR ESCAPE_CHARS="\"\\";
1201
1202 /* allocate temporary memory for the string. Play it safe :
1203 double the length of each argument (in case they're composed
1204 exclusively of escaped characters), and add 3 (for the double-quotes
1205 and separating space). This is temporary anyway, we return a LPWSTR */
1206 length=0;
1207 for(i=0; i<argc; i++)
1208 {
1209 TRACE("argument %d is %s\n", i, argv[i]);
1210 length+=3;
1211 length+=strlen(argv[i])*2;
1212 }
1213 command_line = reinterpret_cast<LPSTR>(InternalMalloc(length));
1214
1215 if(!command_line)
1216 {
1217 ERROR("couldn't allocate memory for command line!\n");
1218 return NULL;
1219 }
1220
1221 command_ptr=command_line;
1222 for(i=0; i<argc; i++)
1223 {
1224 /* double-quote at beginning of argument containing at least one space */
1225 for(j = 0; (argv[i][j] != 0) && (!isspace((unsigned char) argv[i][j])); j++);
1226
1227 if (argv[i][j] != 0)
1228 {
1229 *command_ptr++='"';
1230 bQuoted = TRUE;
1231 }
1232 /* process the argument one character at a time */
1233 for(arg_ptr=argv[i]; *arg_ptr; arg_ptr++)
1234 {
1235 /* if character needs to be escaped, prepend a \ to it. */
1236 if( strchr(ESCAPE_CHARS,*arg_ptr))
1237 {
1238 *command_ptr++='\\';
1239 }
1240
1241 /* now we can copy the actual character over. */
1242 *command_ptr++=*arg_ptr;
1243 }
1244 /* double-quote at end of argument; space to separate arguments */
1245 if (bQuoted == TRUE)
1246 {
1247 *command_ptr++='"';
1248 bQuoted = FALSE;
1249 }
1250 *command_ptr++=' ';
1251 }
1252 /* replace the last space with a NULL terminator */
1253 command_ptr--;
1254 *command_ptr='\0';
1255
1256 /* convert to Unicode */
1257 i = MultiByteToWideChar(CP_ACP, 0,command_line, -1, NULL, 0);
1258 if (i == 0)
1259 {
1260 ASSERT("MultiByteToWideChar failure\n");
1261 free(command_line);
1262 return NULL;
1263 }
1264
1265 retval = reinterpret_cast<LPWSTR>(InternalMalloc((sizeof(WCHAR)*i)));
1266 if(retval == NULL)
1267 {
1268 ERROR("can't allocate memory for Unicode command line!\n");
1269 free(command_line);
1270 return NULL;
1271 }
1272
1273 if(!MultiByteToWideChar(CP_ACP, 0,command_line, i, retval, i))
1274 {
1275 ASSERT("MultiByteToWideChar failure\n");
1276 free(retval);
1277 retval = NULL;
1278 }
1279 else
1280 TRACE("Command line is %s\n", command_line);
1281
1282 free(command_line);
1283 return retval;
1284}
1285
1286/*++
1287Function:
1288 INIT_ConvertEXEPath
1289
1290Abstract:
1291 Check whether the executable path is valid, and convert its type (LPCSTR -> LPWSTR)
1292
1293Parameters:
1294 LPCSTR exe_name : full path of the current executable
1295
1296Return:
1297 pointer to buffer containing the full path. This buffer must be released
1298 by the caller using free()
1299
1300Notes :
1301 this function assumes that "exe_name" is in Unix style (no \)
1302--*/
1303static LPWSTR INIT_ConvertEXEPath(LPCSTR exe_path)
1304{
1305 PathCharString real_path;
1306 LPWSTR return_value;
1307 INT return_size;
1308 struct stat theStats;
1309
1310 if (!strchr(exe_path, '/'))
1311 {
1312 ERROR( "The exe path is not fully specified\n" );
1313 return NULL;
1314 }
1315
1316 if (-1 == stat(exe_path, &theStats))
1317 {
1318 ERROR( "The file does not exist\n" );
1319 return NULL;
1320 }
1321
1322 if (!CorUnix::RealPathHelper(exe_path, real_path))
1323 {
1324 ERROR("realpath() failed!\n");
1325 return NULL;
1326 }
1327
1328 return_size = MultiByteToWideChar(CP_ACP, 0, real_path, -1, NULL, 0);
1329 if (0 == return_size)
1330 {
1331 ASSERT("MultiByteToWideChar failure\n");
1332 return NULL;
1333 }
1334
1335 return_value = reinterpret_cast<LPWSTR>(InternalMalloc((return_size*sizeof(WCHAR))));
1336 if (NULL == return_value)
1337 {
1338 ERROR("Not enough memory to create full path\n");
1339 return NULL;
1340 }
1341 else
1342 {
1343 if (!MultiByteToWideChar(CP_ACP, 0, real_path, -1,
1344 return_value, return_size))
1345 {
1346 ASSERT("MultiByteToWideChar failure\n");
1347 free(return_value);
1348 return_value = NULL;
1349 }
1350 else
1351 {
1352 TRACE("full path to executable is %s\n", real_path.GetString());
1353 }
1354 }
1355
1356 return return_value;
1357}
1358
1359/*++
1360Function:
1361 INIT_SharedFilesPath
1362
1363Abstract:
1364 Initializes the shared application
1365--*/
1366static BOOL INIT_SharedFilesPath(void)
1367{
1368#ifdef __APPLE__
1369 // Store application group Id. It will be null if not set
1370 gApplicationGroupId = getenv("DOTNET_SANDBOX_APPLICATION_GROUP_ID");
1371
1372 if (nullptr != gApplicationGroupId)
1373 {
1374 // Verify the length of the application group ID
1375 gApplicationGroupIdLength = strlen(gApplicationGroupId);
1376 if (gApplicationGroupIdLength > MAX_APPLICATION_GROUP_ID_LENGTH)
1377 {
1378 SetLastError(ERROR_BAD_LENGTH);
1379 return FALSE;
1380 }
1381
1382 // In sandbox, all IPC files (locks, pipes) should be written to the application group
1383 // container. There will be no write permissions to TEMP_DIRECTORY_PATH
1384 if (!GetApplicationContainerFolder(*gSharedFilesPath, gApplicationGroupId, gApplicationGroupIdLength))
1385 {
1386 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1387 return FALSE;
1388 }
1389
1390 // Verify the size of the path won't exceed maximum allowed size
1391 if (gSharedFilesPath->GetCount() + SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ > MAX_LONGPATH)
1392 {
1393 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1394 return FALSE;
1395 }
1396
1397 // Check if the path already exists and it's a directory
1398 struct stat statInfo;
1399 int statResult = stat(*gSharedFilesPath, &statInfo);
1400
1401 // If the path exists, check that it's a directory
1402 if (statResult != 0 || !(statInfo.st_mode & S_IFDIR))
1403 {
1404 SetLastError(ERROR_PATH_NOT_FOUND);
1405 return FALSE;
1406 }
1407
1408 return TRUE;
1409 }
1410#endif // __APPLE__
1411
1412 // If we are here, then we are not in sandbox mode, resort to TEMP_DIRECTORY_PATH as shared files path
1413 return gSharedFilesPath->Set(TEMP_DIRECTORY_PATH);
1414
1415 // We can verify statically the non sandboxed case, since the size is known during compile time
1416 static_assert_no_msg(string_countof(TEMP_DIRECTORY_PATH) + SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ <= MAX_LONGPATH);
1417}
1418