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.
6 // Code that is used by both the Unix corerun and coreconsole.
21 #if defined(__FreeBSD__)
22 #include <sys/types.h>
23 #include <sys/param.h>
28 #if defined(HAVE_SYS_SYSCTL_H) || defined(__FreeBSD__)
29 #include <sys/sysctl.h>
31 #include "coreruncommon.h"
32 #include "coreclrhost.h"
35 #define SUCCEEDED(Status) ((Status) >= 0)
38 // Name of the environment variable controlling server GC.
39 // If set to 1, server GC is enabled on startup. If 0, server GC is
40 // disabled. Server GC is off by default.
41 static const char* serverGcVar
= "COMPlus_gcServer";
43 // Name of environment variable to control "System.Globalization.Invariant"
44 // Set to 1 for Globalization Invariant mode to be true. Default is false.
45 static const char* globalizationInvariantVar
= "CORECLR_GLOBAL_INVARIANT";
47 #if defined(__linux__)
48 #define symlinkEntrypointExecutable "/proc/self/exe"
49 #elif !defined(__APPLE__)
50 #define symlinkEntrypointExecutable "/proc/curproc/exe"
53 bool GetEntrypointExecutableAbsolutePath(std::string
& entrypointExecutable
)
57 entrypointExecutable
.clear();
59 // Get path to the executable for the current process using
60 // platform specific means.
61 #if defined(__APPLE__)
63 // On Mac, we ask the OS for the absolute path to the entrypoint executable
64 uint32_t lenActualPath
= 0;
65 if (_NSGetExecutablePath(nullptr, &lenActualPath
) == -1)
67 // OSX has placed the actual path length in lenActualPath,
68 // so re-attempt the operation
69 std::string
resizedPath(lenActualPath
, '\0');
70 char *pResizedPath
= const_cast<char *>(resizedPath
.c_str());
71 if (_NSGetExecutablePath(pResizedPath
, &lenActualPath
) == 0)
73 entrypointExecutable
.assign(pResizedPath
);
77 #elif defined (__FreeBSD__)
78 static const int name
[] = {
79 CTL_KERN
, KERN_PROC
, KERN_PROC_PATHNAME
, -1
85 if (sysctl(name
, 4, path
, &len
, nullptr, 0) == 0)
87 entrypointExecutable
.assign(path
);
95 #elif defined(__NetBSD__) && defined(KERN_PROC_PATHNAME)
96 static const int name
[] = {
97 CTL_KERN
, KERN_PROC_ARGS
, -1, KERN_PROC_PATHNAME
,
99 char path
[MAXPATHLEN
];
103 if (sysctl(name
, __arraycount(name
), path
, &len
, NULL
, 0) != -1)
105 entrypointExecutable
.assign(path
);
114 #if HAVE_GETAUXVAL && defined(AT_EXECFN)
115 const char *execfn
= (const char *)getauxval(AT_EXECFN
);
119 entrypointExecutable
.assign(execfn
);
124 // On other OSs, return the symlink that will be resolved by GetAbsolutePath
125 // to fetch the entrypoint EXE absolute path, inclusive of filename.
126 result
= GetAbsolutePath(symlinkEntrypointExecutable
, entrypointExecutable
);
132 bool GetAbsolutePath(const char* path
, std::string
& absolutePath
)
136 char realPath
[PATH_MAX
];
137 if (realpath(path
, realPath
) != nullptr && realPath
[0] != '\0')
139 absolutePath
.assign(realPath
);
140 // realpath should return canonicalized path without the trailing slash
141 assert(absolutePath
.back() != '/');
149 bool GetDirectory(const char* absolutePath
, std::string
& directory
)
151 directory
.assign(absolutePath
);
152 size_t lastSlash
= directory
.rfind('/');
153 if (lastSlash
!= std::string::npos
)
155 directory
.erase(lastSlash
);
162 bool GetClrFilesAbsolutePath(const char* currentExePath
, const char* clrFilesPath
, std::string
& clrFilesAbsolutePath
)
164 std::string clrFilesRelativePath
;
165 const char* clrFilesPathLocal
= clrFilesPath
;
166 if (clrFilesPathLocal
== nullptr)
168 // There was no CLR files path specified, use the folder of the corerun/coreconsole
169 if (!GetDirectory(currentExePath
, clrFilesRelativePath
))
171 perror("Failed to get directory from argv[0]");
175 clrFilesPathLocal
= clrFilesRelativePath
.c_str();
177 // TODO: consider using an env variable (if defined) as a fall-back.
178 // The windows version of the corerun uses core_root env variable
181 if (!GetAbsolutePath(clrFilesPathLocal
, clrFilesAbsolutePath
))
183 perror("Failed to convert CLR files path to absolute path");
190 void AddFilesFromDirectoryToTpaList(const char* directory
, std::string
& tpaList
)
192 const char * const tpaExtensions
[] = {
193 ".ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
199 DIR* dir
= opendir(directory
);
205 std::set
<std::string
> addedAssemblies
;
207 // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
208 // then files with .dll extension, etc.
209 for (size_t extIndex
= 0; extIndex
< sizeof(tpaExtensions
) / sizeof(tpaExtensions
[0]); extIndex
++)
211 const char* ext
= tpaExtensions
[extIndex
];
212 int extLength
= strlen(ext
);
214 struct dirent
* entry
;
216 // For all entries in the directory
217 while ((entry
= readdir(dir
)) != nullptr)
219 // We are interested in files only
220 switch (entry
->d_type
)
225 // Handle symlinks and file systems that do not support d_type
229 std::string fullFilename
;
231 fullFilename
.append(directory
);
232 fullFilename
.append("/");
233 fullFilename
.append(entry
->d_name
);
236 if (stat(fullFilename
.c_str(), &sb
) == -1)
241 if (!S_ISREG(sb
.st_mode
))
252 std::string
filename(entry
->d_name
);
254 // Check if the extension matches the one we are looking for
255 int extPos
= filename
.length() - extLength
;
256 if ((extPos
<= 0) || (filename
.compare(extPos
, extLength
, ext
) != 0))
261 std::string
filenameWithoutExt(filename
.substr(0, extPos
));
263 // Make sure if we have an assembly with multiple extensions present,
264 // we insert only one version of it.
265 if (addedAssemblies
.find(filenameWithoutExt
) == addedAssemblies
.end())
267 addedAssemblies
.insert(filenameWithoutExt
);
269 tpaList
.append(directory
);
271 tpaList
.append(filename
);
276 // Rewind the directory stream to be able to iterate over it for the next extension
283 const char* GetEnvValueBoolean(const char* envVariable
)
285 const char* envValue
= std::getenv(envVariable
);
286 if (envValue
== nullptr)
290 // CoreCLR expects strings "true" and "false" instead of "1" and "0".
291 return (std::strcmp(envValue
, "1") == 0 || strcasecmp(envValue
, "true") == 0) ? "true" : "false";
294 static void *TryLoadHostPolicy(const char *hostPolicyPath
)
296 #if defined(__APPLE__)
297 static const char LibrarySuffix
[] = ".dylib";
298 #else // Various Linux-related OS-es
299 static const char LibrarySuffix
[] = ".so";
302 std::string
hostPolicyCompletePath(hostPolicyPath
);
303 hostPolicyCompletePath
.append(LibrarySuffix
);
305 void *libraryPtr
= dlopen(hostPolicyCompletePath
.c_str(), RTLD_LAZY
);
306 if (libraryPtr
== nullptr)
308 fprintf(stderr
, "Failed to load mock hostpolicy at path '%s'. Error: %s", hostPolicyCompletePath
.c_str(), dlerror());
314 int ExecuteManagedAssembly(
315 const char* currentExeAbsolutePath
,
316 const char* clrFilesAbsolutePath
,
317 const char* managedAssemblyAbsolutePath
,
318 int managedAssemblyArgc
,
319 const char** managedAssemblyArgv
)
325 // libunwind library is used to unwind stack frame, but libunwind for ARM
326 // does not support ARM vfpv3/NEON registers in DWARF format correctly.
327 // Therefore let's disable stack unwinding using DWARF information
328 // See https://github.com/dotnet/coreclr/issues/6698
330 // libunwind use following methods to unwind stack frame.
331 // UNW_ARM_METHOD_ALL 0xFF
332 // UNW_ARM_METHOD_DWARF 0x01
333 // UNW_ARM_METHOD_FRAME 0x02
334 // UNW_ARM_METHOD_EXIDX 0x04
335 putenv(const_cast<char *>("UNW_ARM_UNWIND_METHOD=6"));
338 std::string
coreClrDllPath(clrFilesAbsolutePath
);
339 coreClrDllPath
.append("/");
340 coreClrDllPath
.append(coreClrDll
);
342 if (coreClrDllPath
.length() >= PATH_MAX
)
344 fprintf(stderr
, "Absolute path to libcoreclr.so too long\n");
348 // Get just the path component of the managed assembly path
350 GetDirectory(managedAssemblyAbsolutePath
, appPath
);
353 if (strlen(managedAssemblyAbsolutePath
) > 0)
355 // Target assembly should be added to the tpa list. Otherwise corerun.exe
356 // may find wrong assembly to execute.
357 // Details can be found at https://github.com/dotnet/coreclr/issues/5631
358 tpaList
= managedAssemblyAbsolutePath
;
362 // Construct native search directory paths
363 std::string
nativeDllSearchDirs(appPath
);
364 char *coreLibraries
= getenv("CORE_LIBRARIES");
367 nativeDllSearchDirs
.append(":");
368 nativeDllSearchDirs
.append(coreLibraries
);
369 if (std::strcmp(coreLibraries
, clrFilesAbsolutePath
) != 0)
371 AddFilesFromDirectoryToTpaList(coreLibraries
, tpaList
);
375 nativeDllSearchDirs
.append(":");
376 nativeDllSearchDirs
.append(clrFilesAbsolutePath
);
378 void* hostpolicyLib
= nullptr;
379 char* mockHostpolicyPath
= getenv("MOCK_HOSTPOLICY");
380 if (mockHostpolicyPath
)
382 hostpolicyLib
= TryLoadHostPolicy(mockHostpolicyPath
);
383 if (hostpolicyLib
== nullptr)
389 AddFilesFromDirectoryToTpaList(clrFilesAbsolutePath
, tpaList
);
391 void* coreclrLib
= dlopen(coreClrDllPath
.c_str(), RTLD_NOW
| RTLD_LOCAL
);
392 if (coreclrLib
!= nullptr)
394 coreclr_initialize_ptr initializeCoreCLR
= (coreclr_initialize_ptr
)dlsym(coreclrLib
, "coreclr_initialize");
395 coreclr_execute_assembly_ptr executeAssembly
= (coreclr_execute_assembly_ptr
)dlsym(coreclrLib
, "coreclr_execute_assembly");
396 coreclr_shutdown_2_ptr shutdownCoreCLR
= (coreclr_shutdown_2_ptr
)dlsym(coreclrLib
, "coreclr_shutdown_2");
398 if (initializeCoreCLR
== nullptr)
400 fprintf(stderr
, "Function coreclr_initialize not found in the libcoreclr.so\n");
402 else if (executeAssembly
== nullptr)
404 fprintf(stderr
, "Function coreclr_execute_assembly not found in the libcoreclr.so\n");
406 else if (shutdownCoreCLR
== nullptr)
408 fprintf(stderr
, "Function coreclr_shutdown_2 not found in the libcoreclr.so\n");
412 // Check whether we are enabling server GC (off by default)
413 const char* useServerGc
= GetEnvValueBoolean(serverGcVar
);
415 // Check Globalization Invariant mode (false by default)
416 const char* globalizationInvariant
= GetEnvValueBoolean(globalizationInvariantVar
);
418 // Allowed property names:
420 // - The base path of the application from which the exe and other assemblies will be loaded
422 // TRUSTED_PLATFORM_ASSEMBLIES
423 // - The list of complete paths to each of the fully trusted assemblies
426 // - The list of paths which will be probed by the assembly loader
429 // - The list of additional paths that the assembly loader will probe for ngen images
431 // NATIVE_DLL_SEARCH_DIRECTORIES
432 // - The list of paths that will be probed for native DLLs called by PInvoke
434 const char *propertyKeys
[] = {
435 "TRUSTED_PLATFORM_ASSEMBLIES",
438 "NATIVE_DLL_SEARCH_DIRECTORIES",
440 "System.Globalization.Invariant",
442 const char *propertyValues
[] = {
443 // TRUSTED_PLATFORM_ASSEMBLIES
449 // NATIVE_DLL_SEARCH_DIRECTORIES
450 nativeDllSearchDirs
.c_str(),
453 // System.Globalization.Invariant
454 globalizationInvariant
,
458 unsigned int domainId
;
460 int st
= initializeCoreCLR(
461 currentExeAbsolutePath
,
463 sizeof(propertyKeys
) / sizeof(propertyKeys
[0]),
471 fprintf(stderr
, "coreclr_initialize failed - status: 0x%08x\n", st
);
476 st
= executeAssembly(
481 managedAssemblyAbsolutePath
,
482 (unsigned int*)&exitCode
);
486 fprintf(stderr
, "coreclr_execute_assembly failed - status: 0x%08x\n", st
);
490 int latchedExitCode
= 0;
491 st
= shutdownCoreCLR(hostHandle
, domainId
, &latchedExitCode
);
494 fprintf(stderr
, "coreclr_shutdown failed - status: 0x%08x\n", st
);
500 exitCode
= latchedExitCode
;
507 const char* error
= dlerror();
508 fprintf(stderr
, "dlopen failed to open the libcoreclr.so with error %s\n", error
);
513 if(dlclose(hostpolicyLib
) != 0)
515 fprintf(stderr
, "Warning - dlclose of mock hostpolicy failed.\n");