2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
32 #include <sys/types.h>
35 #include <linux/limits.h>
39 #include <app_common.h>
40 #include <tzplatform_config.h>
42 #include "injection.h"
45 #include "core_runtime.h"
46 #include "plugin_manager.h"
47 #include "path_manager.h"
51 namespace dotnetcore {
53 static coreclr_initialize_ptr initializeClr = nullptr;
54 static coreclr_execute_assembly_ptr executeAssembly = nullptr;
55 static coreclr_shutdown_ptr shutdown = nullptr;
56 static coreclr_create_delegate_ptr createDelegate = nullptr;
57 static set_environment_variable_ptr setEnvironmentVariable = nullptr;
58 static stop_profile_after_delay_ptr stopProfileAfterDelay = nullptr;
59 static set_switch_ptr setSwitch = nullptr;
60 static void* __coreclrLib = nullptr;
61 static void* __hostHandle = nullptr;
62 static unsigned int __domainId = -1;
63 static bool __initialized = false;
64 static bool __isProfileMode = false;
65 PathManager* CoreRuntime::__pm = nullptr;
67 #define MAX_DELAY_SEC 100
69 static std::vector<std::string> __envList;
71 static void setEnvFromFile()
74 std::ifstream inFile(ENV_FILE_PATH);
79 _INFO("coreclr_env.list is found");
82 while (std::getline(inFile, token, '\n')) {
84 __envList.push_back(token);
88 for (unsigned int i = 0; i < __envList.size(); i++) {
89 putenv(const_cast<char *>(__envList[i].c_str()));
92 _INFO("coreclr_env.list file is not found. skip");
96 #define _unused(x) ((void)(x))
98 static struct sigaction sig_abrt_new;
99 static struct sigaction sig_abrt_old;
101 static bool checkOnSigabrt = false;
102 static bool checkOnTerminate = false;
104 static void onSigabrt(int signum)
106 // use unused variable to avoid build warning
107 ssize_t ret = write(STDERR_FILENO, "onSigabrt called\n", 17);
109 if (checkOnTerminate) {
110 ret = write(STDERR_FILENO, "onSigabrt called while terminate. go to exit\n", 45);
115 if (checkOnSigabrt) {
116 ret = write(STDERR_FILENO, "onSigabrt called again. go to exit\n", 35);
121 checkOnSigabrt = true;
122 if (sigaction(SIGABRT, &sig_abrt_old, NULL) == 0) {
123 if (raise(signum) < 0) {
124 ret = write(STDERR_FILENO, "Fail to raise SIGABRT\n", 22);
127 ret = write(STDERR_FILENO, "Fail to set original SIGABRT handler\n", 37);
132 static void registerSigHandler()
134 sig_abrt_new.sa_handler = onSigabrt;
135 if (sigemptyset(&sig_abrt_new.sa_mask) != 0) {
136 _ERR("Fail to sigemptyset");
139 if (sigaction(SIGABRT, &sig_abrt_new, &sig_abrt_old) < 0) {
140 _ERR("Fail to add sig handler");
144 static void initEnvForSpecialFolder()
146 const char* path = NULL;
147 if (getenv("XDG_PICTURES_DIR") == NULL) {
148 path = tzplatform_getenv(TZ_USER_IMAGES);
150 setenv("XDG_PICTURES_DIR", path, 1);
154 if (getenv("XDG_MUSIC_DIR") == NULL) {
155 path = tzplatform_getenv(TZ_USER_MUSIC);
157 setenv("XDG_MUSIC_DIR", path, 1);
161 if (getenv("XDG_VIDEOS_DIR") == NULL) {
162 path = tzplatform_getenv(TZ_USER_VIDEOS);
164 setenv("XDG_VIDEOS_DIR", path, 1);
169 static void setLang()
171 //To reduce search overhead of libicuuc.so.xx
172 setenv("CLR_ICU_VERSION_OVERRIDE", "build", 1);
174 char* lang = vconf_get_str(VCONFKEY_LANGSET);
176 _ERR("Fail to get language from vconf");
180 // In order to operate ICU (used for globalization) normally, the following
181 // environment variables must be set before using ICU API.
182 // When running Applicaiton, the following environment variables are set by AppFW.
183 // But when preloading the dll in the candidate process, the following environment variables are not set
184 // As a result, CultureInfo is incorrectly generated and malfunctions.
185 // For example, uloc_getDefault() returns en_US_POSIX, CultureInfo is set to invariant mode.
186 setenv("LANG", const_cast<char *>(lang), 1);
187 setlocale(LC_ALL, const_cast<char *>(lang));
192 static std::string readSelfPath()
195 ssize_t len = ::readlink("/proc/self/exe", buff, sizeof(buff)-1);
198 return std::string(buff);
204 static void removeDebugPipe()
207 struct dirent* entry;
208 char debugPipeFiles[PATH_MAX];;
209 sprintf(debugPipeFiles, "/tmp/clr-debug-pipe-%d-", getpid());
211 dir = opendir("/tmp");
212 if (dir == nullptr) {
213 _ERR("Fail to open /tmp directory");
217 while ((entry = readdir(dir)) != nullptr) {
218 std::string path = concatPath("/tmp", entry->d_name);
219 if (path.find(debugPipeFiles) != std::string::npos) {
220 if (!removeFile(path)) {
221 _ERR("Fail to remove file (%s)", path.c_str());
231 typedef void (*PreloadDelegate)();
232 PreloadDelegate preloadDelegate;
234 int ret = createDelegate(__hostHandle,
237 "Tizen.Runtime.Preloader",
239 (void**)&preloadDelegate);
242 _ERR("Failed to create delegate for Tizen.Runtime Preload (0x%08x)", ret);
250 bool initializeCoreClr(PathManager* pm, const std::string& tpa)
252 bool ncdbStartupHook = isNCDBStartupHookProvided();
254 const char *propertyKeys[] = {
255 "TRUSTED_PLATFORM_ASSEMBLIES",
258 "NATIVE_DLL_SEARCH_DIRECTORIES",
259 "AppDomainCompatSwitch",
260 ncdbStartupHook ? "STARTUP_HOOKS" : "" // must be the last one
263 const char *propertyValues[] = {
265 pm->getAppPaths().c_str(),
266 pm->getAppNIPaths().c_str(),
267 pm->getNativeDllSearchingPaths().c_str(),
268 "UseLatestBehaviorWhenTFMNotSpecified",
269 ncdbStartupHook ? getNCDBStartupHook() : "" // must be the last one
272 std::string selfPath = readSelfPath();
274 int st = initializeClr(selfPath.c_str(),
276 sizeof(propertyKeys) / sizeof(propertyKeys[0]) - (ncdbStartupHook ? 0 : 1),
283 _ERR("initialize core clr fail! (0x%08x)", st);
287 pluginSetCoreclrInfo(__hostHandle, __domainId, createDelegate);
289 _INFO("Initialize core clr success");
293 int CoreRuntime::initialize(const char* appType, LaunchMode launchMode)
296 _ERR("CoreRuntime is already initialized");
300 // set language environment to support ICU
304 env = getenv("CORECLR_ENABLE_PROFILING");
305 if (env != nullptr && !strcmp(env, "1")) {
306 _INFO("profiling mode on");
307 __isProfileMode = true;
310 // plugin initialize should be called before creating threads.
311 // In case of VD plugins, attaching secure zone is done in the plugin_initialize().
312 // When attaching to a secure zone, if there is a created thread, it will failed.
313 // So, plugin initialize should be called before creating threads.
314 if (initializePluginManager(appType) < 0) {
315 _ERR("Failed to initialize PluginManager");
319 // checkInjection checks dotnet-launcher run mode
320 // At the moment, this mechanism is used only when the Memory Profiler is started.
321 int res = checkInjection();
323 _ERR("Failed to initnialize Memory Profiler");
328 // libunwind library is used to unwind stack frame, but libunwind for ARM
329 // does not support ARM vfpv3/NEON registers in DWARF format correctly.
330 // Therefore let's disable stack unwinding using DWARF information
331 // See https://github.com/dotnet/coreclr/issues/6698
333 // libunwind use following methods to unwind stack frame.
334 // UNW_ARM_METHOD_ALL 0xFF
335 // UNW_ARM_METHOD_DWARF 0x01
336 // UNW_ARM_METHOD_FRAME 0x02
337 // UNW_ARM_METHOD_EXIDX 0x04
338 putenv(const_cast<char *>("UNW_ARM_UNWIND_METHOD=6"));
341 // Enable diagnostics.
342 // clr create clr-debug-pipe-xxx and dotnet-diagnostics-xxx file under /tmp dir.
343 putenv(const_cast<char *>("COMPlus_EnableDiagnostics=1"));
345 // Write Debug.WriteLine to stderr
346 putenv(const_cast<char *>("COMPlus_DebugWriteToStdErr=1"));
348 #ifdef USE_DEFAULT_BASE_ADDR
349 putenv(const_cast<char *>("COMPlus_UseDefaultBaseAddr=1"));
350 #endif // USE_DEFAULT_BASE_ADDR
352 // Disable config cache to set environment after coreclr_initialize()
353 putenv(const_cast<char *>("COMPlus_DisableConfigCache=1"));
355 // Set environment variable for System.Environment.SpecialFolder
356 initEnvForSpecialFolder();
358 // read string from external file and set them to environment value.
362 __pm = new PathManager();
363 } catch (const std::exception& e) {
364 _ERR("Failed to create PathManager");
368 char* pluginDllPaths = pluginGetDllPath();
369 if (pluginDllPaths && pluginDllPaths[0] != '\0') {
370 __pm->addPlatformAssembliesPaths(pluginDllPaths, true);
373 char* pluginNativePaths = pluginGetNativeDllSearchingPath();
374 if (pluginNativePaths && pluginNativePaths[0] != '\0') {
375 __pm->addNativeDllSearchingPaths(pluginNativePaths, true);
378 char* pluginExtraDllPaths = pluginGetExtraDllPath();
379 if (pluginExtraDllPaths && pluginExtraDllPaths[0] != '\0') {
380 __pm->setExtraDllPaths(pluginExtraDllPaths);
383 std::string libCoreclr(concatPath(__pm->getRuntimePath(), "libcoreclr.so"));
385 __coreclrLib = dlopen(libCoreclr.c_str(), RTLD_NOW | RTLD_LOCAL);
386 if (__coreclrLib == nullptr) {
387 char *err = dlerror();
388 _ERR("dlopen failed to open libcoreclr.so with error %s", err);
392 #define CORELIB_RETURN_IF_NOSYM(type, variable, name) \
394 variable = (type)dlsym(__coreclrLib, name); \
395 if (variable == nullptr) { \
396 _ERR(name " is not found in the libcoreclr.so"); \
401 CORELIB_RETURN_IF_NOSYM(coreclr_initialize_ptr, initializeClr, "coreclr_initialize");
402 CORELIB_RETURN_IF_NOSYM(coreclr_execute_assembly_ptr, executeAssembly, "coreclr_execute_assembly");
403 CORELIB_RETURN_IF_NOSYM(coreclr_shutdown_ptr, shutdown, "coreclr_shutdown");
404 CORELIB_RETURN_IF_NOSYM(coreclr_create_delegate_ptr, createDelegate, "coreclr_create_delegate");
406 #undef CORELIB_RETURN_IF_NOSYM
408 _INFO("libcoreclr dlopen and dlsym success");
411 char* pluginTPA = pluginGetTPA();
412 if (pluginTPA && pluginTPA[0] != '\0') {
413 tpa = std::string(pluginTPA);
415 addAssembliesFromDirectories(__pm->getPlatformAssembliesPaths(), tpa);
418 if (!initializeCoreClr(__pm, tpa)) {
419 _ERR("Failed to initialize coreclr");
423 // VD has their own signal handler.
424 if (!pluginHasLogControl()) {
425 registerSigHandler();
428 int st = createDelegate(__hostHandle, __domainId, "Tizen.Runtime", "Tizen.Runtime.Environment", "SetEnvironmentVariable", (void**)&setEnvironmentVariable);
429 if (st < 0 || setEnvironmentVariable == nullptr) {
430 _ERR("Create delegate for Tizen.Runtime.dll -> Tizen.Runtime.Environment -> SetEnvironmentVariable failed (0x%08x)", st);
434 st = createDelegate(__hostHandle, __domainId, "Tizen.Runtime", "Tizen.Runtime.Profiler", "StopProfileAfterDelay", (void**)&stopProfileAfterDelay);
435 if (st < 0 || stopProfileAfterDelay == nullptr) {
436 _ERR("Create delegate for Tizen.Runtime.dll -> Tizen.Runtime.Profiler -> StopProfileAfterDelay failed (0x%08x)", st);
440 st = createDelegate(__hostHandle, __domainId, "Tizen.Runtime", "Tizen.Runtime.AppSetting", "SetSwitch", (void**)&setSwitch);
441 if (st < 0 || setSwitch == nullptr) {
442 _ERR("Create delegate for Tizen.Runtime.dll -> Tizen.Runtime.AppSetting -> SetSwitch failed (0x%08x)", st);
446 if (launchMode == LaunchMode::loader) {
447 // preload libraries and manage dlls for optimizing startup time
450 // The debug pipe created in the candidate process has a "User" label.
451 // As a result, smack deny occurs when app process try to access the debug pipe.
452 // Also, since debugging is performed only in standalone mode,
453 // the debug pipe doesnot be used in the candidate process.
454 // So, to avoid smack deny error, delete unused debug pipe files.
458 __initialized = true;
460 _INFO("CoreRuntime initialize success");
465 void CoreRuntime::finalize()
467 // call plugin finalize function to notify finalize to plugin
468 // dlclose shoud be done after coreclr shutdown to avoid breaking signal chain
471 // ignore the signal generated by an exception that occurred during shutdown
472 checkOnTerminate = true;
474 // workaround : to prevent crash while process terminate on profiling mode,
475 // kill process immediately.
476 // see https://github.com/dotnet/coreclr/issues/26687
477 if (__isProfileMode) {
478 _INFO("shutdown process immediately.");
482 if (__hostHandle != nullptr) {
483 int st = shutdown(__hostHandle, __domainId);
485 _ERR("shutdown core clr fail! (0x%08x)", st);
486 __hostHandle = nullptr;
489 if (__coreclrLib != nullptr) {
490 if (dlclose(__coreclrLib) != 0) {
491 _ERR("libcoreclr.so close failed");
494 __coreclrLib = nullptr;
497 finalizePluginManager();
504 _INFO("CoreRuntime finalized");
507 int CoreRuntime::launch(const char* appId, const char* root, const char* path, int argc, char* argv[], bool profile)
509 if (!__initialized) {
510 _ERR("Runtime is not initialized");
514 if (path == nullptr) {
515 _ERR("executable path is null");
520 _ERR("File not exist : %s", path);
524 pluginSetAppInfo(appId, path);
526 // temporal root path is overrided to real application root path
527 __pm->setAppRootPath(root);
529 // set application data path to coreclr environment.
530 // application data path can be changed by owner. So, we have to set data path just before launching.
531 char* localDataPath = app_get_data_path();
532 if (localDataPath != nullptr) {
533 setEnvironmentVariable("XDG_DATA_HOME", localDataPath);
535 // set profile.data path and collect/use it if it non-exists/exists.
537 char multiCoreJitProfile[strlen(localDataPath) + strlen(PROFILE_BASENAME) + 1];
538 memcpy(multiCoreJitProfile, localDataPath, strlen(localDataPath) + 1);
539 strncat(multiCoreJitProfile, PROFILE_BASENAME, strlen(PROFILE_BASENAME));
541 setEnvironmentVariable("COMPlus_MultiCoreJitProfile", multiCoreJitProfile);
542 setEnvironmentVariable("COMPlus_MultiCoreJitMinNumCpus", "1");
544 if (exist(multiCoreJitProfile)) {
545 setEnvironmentVariable("COMPlus_MultiCoreJitNoProfileGather", "1");
546 _INFO("MCJ playing start for %s", appId);
548 setEnvironmentVariable("COMPlus_MultiCoreJitNoProfileGather", "0");
549 // stop profiling and write collected data after delay if env value is set.
550 char *env = getenv("CLR_MCJ_PROFILE_WRITE_DELAY");
551 if (env != nullptr) {
552 int delay = std::atoi(env);
553 // To avoid undefined behavior by out-of-range input(atoi), set max delay value to 100.
555 if (delay > MAX_DELAY_SEC) delay = MAX_DELAY_SEC;
556 stopProfileAfterDelay(delay);
559 _INFO("MCJ recording start for %s", appId);
565 if (exist(__pm->getAppRootPath() + "/bin/" + DISABLE_IPV6_FILE)) {
566 setSwitch("System.Net.DisableIPv6", true);
569 setSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true);
571 pluginBeforeExecute();
573 _INFO("execute assembly : %s", path);
575 unsigned int ret = 0;
576 int st = executeAssembly(__hostHandle, __domainId, argc, (const char**)argv, path, &ret);
578 _ERR("Failed to Execute Assembly %s (0x%08x)", path, st);
582 } // namespace dotnetcore
583 } // namespace runtime