[netcore] Add libc name transition
[mono-project.git] / netcore / corerun / coreruncommon.cpp
blob25007bb3fd945a5e3e10f4db3f98027c8e4195ec
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.
5 //
6 // Code that is used by both the Unix corerun and coreconsole.
7 //
9 #include "config.h"
11 #include <cstdlib>
12 #include <cstring>
13 #include <assert.h>
14 #include <dirent.h>
15 #include <dlfcn.h>
16 #include <limits.h>
17 #include <set>
18 #include <string>
19 #include <string.h>
20 #include <sys/stat.h>
21 #if defined(__FreeBSD__)
22 #include <sys/types.h>
23 #include <sys/param.h>
24 #endif
25 #if HAVE_GETAUXVAL
26 #include <sys/auxv.h>
27 #endif
28 #if defined(HAVE_SYS_SYSCTL_H) || defined(__FreeBSD__)
29 #include <sys/sysctl.h>
30 #endif
31 #include "coreruncommon.h"
32 #include "coreclrhost.h"
33 #include <unistd.h>
34 #ifndef SUCCEEDED
35 #define SUCCEEDED(Status) ((Status) >= 0)
36 #endif // !SUCCEEDED
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"
51 #endif
53 bool GetEntrypointExecutableAbsolutePath(std::string& entrypointExecutable)
55 bool result = false;
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);
74 result = true;
77 #elif defined (__FreeBSD__)
78 static const int name[] = {
79 CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1
81 char path[PATH_MAX];
82 size_t len;
84 len = sizeof(path);
85 if (sysctl(name, 4, path, &len, nullptr, 0) == 0)
87 entrypointExecutable.assign(path);
88 result = true;
90 else
92 // ENOMEM
93 result = false;
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];
100 size_t len;
102 len = sizeof(path);
103 if (sysctl(name, __arraycount(name), path, &len, NULL, 0) != -1)
105 entrypointExecutable.assign(path);
106 result = true;
108 else
110 result = false;
112 #else
114 #if HAVE_GETAUXVAL && defined(AT_EXECFN)
115 const char *execfn = (const char *)getauxval(AT_EXECFN);
117 if (execfn)
119 entrypointExecutable.assign(execfn);
120 result = true;
122 else
123 #endif
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);
127 #endif
129 return result;
132 bool GetAbsolutePath(const char* path, std::string& absolutePath)
134 bool result = false;
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() != '/');
143 result = true;
146 return result;
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);
156 return true;
159 return false;
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]");
172 return false;
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");
184 return false;
187 return true;
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
194 ".dll",
195 ".ni.exe",
196 ".exe",
199 DIR* dir = opendir(directory);
200 if (dir == nullptr)
202 return;
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)
222 case DT_REG:
223 break;
225 // Handle symlinks and file systems that do not support d_type
226 case DT_LNK:
227 case DT_UNKNOWN:
229 std::string fullFilename;
231 fullFilename.append(directory);
232 fullFilename.append("/");
233 fullFilename.append(entry->d_name);
235 struct stat sb;
236 if (stat(fullFilename.c_str(), &sb) == -1)
238 continue;
241 if (!S_ISREG(sb.st_mode))
243 continue;
246 break;
248 default:
249 continue;
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))
258 continue;
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);
270 tpaList.append("/");
271 tpaList.append(filename);
272 tpaList.append(":");
276 // Rewind the directory stream to be able to iterate over it for the next extension
277 rewinddir(dir);
280 closedir(dir);
283 const char* GetEnvValueBoolean(const char* envVariable)
285 const char* envValue = std::getenv(envVariable);
286 if (envValue == nullptr)
288 envValue = "0";
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";
300 #endif
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());
311 return libraryPtr;
314 int ExecuteManagedAssembly(
315 const char* currentExeAbsolutePath,
316 const char* clrFilesAbsolutePath,
317 const char* managedAssemblyAbsolutePath,
318 int managedAssemblyArgc,
319 const char** managedAssemblyArgv)
321 // Indicates failure
322 int exitCode = -1;
324 #ifdef _ARM_
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"));
336 #endif // _ARM_
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");
345 return -1;
348 // Get just the path component of the managed assembly path
349 std::string appPath;
350 GetDirectory(managedAssemblyAbsolutePath, appPath);
352 std::string tpaList;
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;
359 tpaList.append(":");
362 // Construct native search directory paths
363 std::string nativeDllSearchDirs(appPath);
364 char *coreLibraries = getenv("CORE_LIBRARIES");
365 if (coreLibraries)
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)
385 return -1;
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");
410 else
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:
419 // APPBASE
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
425 // APP_PATHS
426 // - The list of paths which will be probed by the assembly loader
428 // APP_NI_PATHS
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",
436 "APP_PATHS",
437 "APP_NI_PATHS",
438 "NATIVE_DLL_SEARCH_DIRECTORIES",
439 "System.GC.Server",
440 "System.Globalization.Invariant",
442 const char *propertyValues[] = {
443 // TRUSTED_PLATFORM_ASSEMBLIES
444 tpaList.c_str(),
445 // APP_PATHS
446 appPath.c_str(),
447 // APP_NI_PATHS
448 appPath.c_str(),
449 // NATIVE_DLL_SEARCH_DIRECTORIES
450 nativeDllSearchDirs.c_str(),
451 // System.GC.Server
452 useServerGc,
453 // System.Globalization.Invariant
454 globalizationInvariant,
457 void* hostHandle;
458 unsigned int domainId;
460 int st = initializeCoreCLR(
461 currentExeAbsolutePath,
462 "unixcorerun",
463 sizeof(propertyKeys) / sizeof(propertyKeys[0]),
464 propertyKeys,
465 propertyValues,
466 &hostHandle,
467 &domainId);
469 if (!SUCCEEDED(st))
471 fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st);
472 exitCode = -1;
474 else
476 st = executeAssembly(
477 hostHandle,
478 domainId,
479 managedAssemblyArgc,
480 managedAssemblyArgv,
481 managedAssemblyAbsolutePath,
482 (unsigned int*)&exitCode);
484 if (!SUCCEEDED(st))
486 fprintf(stderr, "coreclr_execute_assembly failed - status: 0x%08x\n", st);
487 exitCode = -1;
490 int latchedExitCode = 0;
491 st = shutdownCoreCLR(hostHandle, domainId, &latchedExitCode);
492 if (!SUCCEEDED(st))
494 fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st);
495 exitCode = -1;
498 if (exitCode != -1)
500 exitCode = latchedExitCode;
505 else
507 const char* error = dlerror();
508 fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error);
511 if (hostpolicyLib)
513 if(dlclose(hostpolicyLib) != 0)
515 fprintf(stderr, "Warning - dlclose of mock hostpolicy failed.\n");
519 return exitCode;