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.
31 #include <sys/types.h>
34 #include <linux/limits.h>
38 #include <app_common.h>
42 #include "injection.h"
45 #include "core_runtime.h"
46 #include "plugin_manager.h"
47 #include "path_manager.h"
48 #include "log_manager.h"
52 namespace dotnetcore {
54 static coreclr_initialize_ptr initializeClr = nullptr;
55 static coreclr_execute_assembly_ptr executeAssembly = nullptr;
56 static coreclr_shutdown_ptr shutdown = nullptr;
57 static coreclr_create_delegate_ptr createDelegate = nullptr;
58 static set_environment_variable_ptr setEnvironmentVariable = nullptr;
59 static void* __coreclrLib = nullptr;
60 static void* __hostHandle = nullptr;
61 static unsigned int __domainId = -1;
62 static bool __initialized = false;
63 static bool __isProfileMode = false;
64 PathManager* CoreRuntime::__pm = nullptr;
66 static std::vector<std::string> __envList;
68 static void setEnvFromFile()
71 std::ifstream inFile(ENV_FILE_PATH);
76 _INFO("coreclr_env.list is found");
79 while (std::getline(inFile, token, '\n')) {
81 __envList.push_back(token);
85 for (unsigned int i = 0; i < __envList.size(); i++) {
86 putenv(const_cast<char *>(__envList[i].c_str()));
89 _INFO("coreclr_env.list file is not found. skip");
93 #define _unused(x) ((void)(x))
95 struct sigaction sig_abrt_new;
96 struct sigaction sig_abrt_old;
98 static bool checkOnSigabrt = false;
99 static bool checkOnTerminate = false;
101 static void onSigabrt(int signum)
103 // use unused variable to avoid build warning
104 ssize_t ret = write(STDERR_FILENO, "onSigabrt called\n", 17);
106 if (checkOnTerminate) {
107 ret = write(STDERR_FILENO, "onSigabrt called while terminate. go to exit\n", 45);
112 if (checkOnSigabrt) {
113 ret = write(STDERR_FILENO, "onSigabrt called again. go to exit\n", 35);
118 checkOnSigabrt = true;
119 if (sigaction(SIGABRT, &sig_abrt_old, NULL) == 0) {
120 if (raise(signum) < 0) {
121 ret = write(STDERR_FILENO, "Fail to raise SIGABRT\n", 22);
124 ret = write(STDERR_FILENO, "Fail to set original SIGABRT handler\n", 37);
129 static void registerSigHandler()
131 sig_abrt_new.sa_handler = onSigabrt;
132 if (sigemptyset(&sig_abrt_new.sa_mask) != 0) {
133 _ERR("Fail to sigemptyset");
136 if (sigaction(SIGABRT, &sig_abrt_new, &sig_abrt_old) < 0) {
137 _ERR("Fail to add sig handler");
141 static bool storage_cb(int id, storage_type_e type, storage_state_e state, const char *path, void *user_data)
143 int* tmp = (int*)user_data;
144 if (type == STORAGE_TYPE_INTERNAL)
153 static void initEnvForSpecialFolder()
159 error = storage_foreach_device_supported(storage_cb, &storageId);
160 if (error != STORAGE_ERROR_NONE) {
164 error = storage_get_directory(storageId, STORAGE_DIRECTORY_IMAGES, &path);
165 if (error == STORAGE_ERROR_NONE && path != NULL) {
166 setenv("XDG_PICTURES_DIR", const_cast<char *>(path), 1);
171 error = storage_get_directory(storageId, STORAGE_DIRECTORY_MUSIC, &path);
172 if (error == STORAGE_ERROR_NONE && path != NULL) {
173 setenv("XDG_MUSIC_DIR", const_cast<char *>(path), 1);
178 error = storage_get_directory(storageId, STORAGE_DIRECTORY_VIDEOS, &path);
179 if (error == STORAGE_ERROR_NONE && path != NULL) {
180 setenv("XDG_VIDEOS_DIR", const_cast<char *>(path), 1);
186 // terminate candidate process when language changed
187 // icu related data (CultureInfo, etc) should be recreated.
188 static void langChangedCB(keynode_t *key, void* data)
190 _INFO("terminiate candidate process to update language.");
194 static void setLang()
196 char* lang = vconf_get_str(VCONFKEY_LANGSET);
198 _ERR("Fail to get language from vconf");
202 // In order to operate ICU (used for globalization) normally, the following
203 // environment variables must be set before using ICU API.
204 // When running Applicaiton, the following environment variables are set by AppFW.
205 // But when preloading the dll in the candidate process, the following environment variables are not set
206 // As a result, CultureInfo is incorrectly generated and malfunctions.
207 // For example, uloc_getDefault() returns en_US_POSIX, CultureInfo is set to invariant mode.
208 setenv("LANG", const_cast<char *>(lang), 1);
209 setlocale(LC_ALL, const_cast<char *>(lang));
214 static std::string readSelfPath()
217 ssize_t len = ::readlink("/proc/self/exe", buff, sizeof(buff)-1);
220 return std::string(buff);
228 typedef void (*PreloadDelegate)();
229 PreloadDelegate preloadDelegate;
231 int ret = createDelegate(__hostHandle,
234 "Tizen.Runtime.Preloader",
236 (void**)&preloadDelegate);
239 _ERR("Failed to create delegate for Tizen.Runtime Preload (0x%08x)", ret);
247 bool initializeCoreClr(PathManager* pm, const std::string& tpa)
249 const char *propertyKeys[] = {
250 "TRUSTED_PLATFORM_ASSEMBLIES",
253 "NATIVE_DLL_SEARCH_DIRECTORIES",
254 "AppDomainCompatSwitch"
257 const char *propertyValues[] = {
259 pm->getAppPaths().c_str(),
260 pm->getAppNIPaths().c_str(),
261 pm->getNativeDllSearchingPaths().c_str(),
262 "UseLatestBehaviorWhenTFMNotSpecified"
265 std::string selfPath = readSelfPath();
267 int st = initializeClr(selfPath.c_str(),
269 sizeof(propertyKeys) / sizeof(propertyKeys[0]),
276 _ERR("initialize core clr fail! (0x%08x)", st);
280 pluginSetCoreclrInfo(__hostHandle, __domainId, createDelegate);
282 _INFO("Initialize core clr success");
286 int CoreRuntime::initialize(const char* appType, LaunchMode launchMode)
289 _ERR("CoreRuntime is already initialized");
293 // Intiailize ecore first (signal handlers, etc.) before runtime init.
296 // set language environment to support ICU
300 env = getenv("CORECLR_ENABLE_PROFILING");
301 if (env != nullptr && !strcmp(env, "1")) {
302 _INFO("profiling mode on");
303 __isProfileMode = true;
306 // plugin initialize should be called before creating threads.
307 // In case of VD plugins, attaching secure zone is done in the plugin_initialize().
308 // When attaching to a secure zone, if there is a created thread, it will failed.
309 // So, plugin initialize should be called before creating threads.
310 if (initializePluginManager(appType) < 0) {
311 _ERR("Failed to initialize PluginManager");
314 // checkInjection checks dotnet-launcher run mode
315 // At the moment, this mechanism is used only when the Memory Profiler is started.
316 int res = checkInjection();
318 _ERR("Failed to initnialize Memory Profiler");
323 // libunwind library is used to unwind stack frame, but libunwind for ARM
324 // does not support ARM vfpv3/NEON registers in DWARF format correctly.
325 // Therefore let's disable stack unwinding using DWARF information
326 // See https://github.com/dotnet/coreclr/issues/6698
328 // libunwind use following methods to unwind stack frame.
329 // UNW_ARM_METHOD_ALL 0xFF
330 // UNW_ARM_METHOD_DWARF 0x01
331 // UNW_ARM_METHOD_FRAME 0x02
332 // UNW_ARM_METHOD_EXIDX 0x04
333 putenv(const_cast<char *>("UNW_ARM_UNWIND_METHOD=6"));
336 // Enable diagnostics.
337 // clr create clr-debug-pipe-xxx and dotnet-diagnostics-xxx file under /tmp dir.
338 putenv(const_cast<char *>("COMPlus_EnableDiagnostics=1"));
340 // Write Debug.WriteLine to stderr
341 putenv(const_cast<char *>("COMPlus_DebugWriteToStdErr=1"));
343 #ifdef USE_DEFAULT_BASE_ADDR
344 putenv(const_cast<char *>("COMPlus_UseDefaultBaseAddr=1"));
345 #endif // USE_DEFAULT_BASE_ADDR
347 // read string from external file and set them to environment value.
351 __pm = new PathManager();
352 } catch (const std::exception& e) {
353 _ERR("Failed to create PathManager");
357 char* pluginPath = pluginGetDllPath();
359 __pm->addPlatformAssembliesPaths(pluginPath);
362 if (!pluginHasLogControl()) {
363 if (initializeLogManager() < 0) {
364 _ERR("Failed to initnialize LogManager");
369 std::string libCoreclr(concatPath(__pm->getRuntimePath(), "libcoreclr.so"));
371 __coreclrLib = dlopen(libCoreclr.c_str(), RTLD_NOW | RTLD_LOCAL);
372 if (__coreclrLib == nullptr) {
373 char *err = dlerror();
374 _ERR("dlopen failed to open libcoreclr.so with error %s", err);
378 #define CORELIB_RETURN_IF_NOSYM(type, variable, name) \
380 variable = (type)dlsym(__coreclrLib, name); \
381 if (variable == nullptr) { \
382 _ERR(name " is not found in the libcoreclr.so"); \
387 CORELIB_RETURN_IF_NOSYM(coreclr_initialize_ptr, initializeClr, "coreclr_initialize");
388 CORELIB_RETURN_IF_NOSYM(coreclr_execute_assembly_ptr, executeAssembly, "coreclr_execute_assembly");
389 CORELIB_RETURN_IF_NOSYM(coreclr_shutdown_ptr, shutdown, "coreclr_shutdown");
390 CORELIB_RETURN_IF_NOSYM(coreclr_create_delegate_ptr, createDelegate, "coreclr_create_delegate");
392 #undef CORELIB_RETURN_IF_NOSYM
394 _INFO("libcoreclr dlopen and dlsym success");
396 // Set environment for System.Environment.SpecialFolder
397 // Below function creates dbus connection by callging storage API.
398 // If dbus connection is created bofere fork(), forked process cannot use dbus.
399 // To avoid gdbus blocking issue, below function should be called after fork()
400 initEnvForSpecialFolder();
403 char* pluginTPA = pluginGetTPA();
405 tpa = std::string(pluginTPA);
407 addAssembliesFromDirectories(__pm->getPlatformAssembliesPaths(), tpa);
410 if (!initializeCoreClr(__pm, tpa)) {
411 _ERR("Failed to initialize coreclr");
415 int st = createDelegate(__hostHandle, __domainId, "Tizen.Runtime", "Tizen.Runtime.Environment", "SetEnvironmentVariable", (void**)&setEnvironmentVariable);
416 if (st < 0 || setEnvironmentVariable == nullptr) {
417 _ERR("Create delegate for Tizen.Runtime.dll -> Tizen.Runtime.Environment -> SetEnvironmentVariable failed (0x%08x)", st);
421 if (launchMode == LaunchMode::loader) {
422 // terminate candidate process if language is changed.
423 // CurrentCulture created for preloaded dlls should be updated.
424 vconf_notify_key_changed(VCONFKEY_LANGSET, langChangedCB, NULL);
426 // preload libraries and manage dlls for optimizing startup time
430 __initialized = true;
432 _INFO("CoreRuntime initialize success");
437 void CoreRuntime::finalize()
439 // call plugin finalize function to notify finalize to plugin
440 // dlclose shoud be done after coreclr shutdown to avoid breaking signal chain
443 // ignore the signal generated by an exception that occurred during shutdown
444 checkOnTerminate = true;
446 // workaround : to prevent crash while process terminate on profiling mode,
447 // kill process immediately.
448 // see https://github.com/dotnet/coreclr/issues/26687
449 if (__isProfileMode) {
450 _INFO("shutdown process immediately.");
454 if (__hostHandle != nullptr) {
455 int st = shutdown(__hostHandle, __domainId);
457 _ERR("shutdown core clr fail! (0x%08x)", st);
458 __hostHandle = nullptr;
461 if (__coreclrLib != nullptr) {
462 if (dlclose(__coreclrLib) != 0) {
463 _ERR("libcoreclr.so close failed");
466 __coreclrLib = nullptr;
469 finalizePluginManager();
476 _INFO("CoreRuntime finalized");
479 int CoreRuntime::launch(const char* appId, const char* root, const char* path, int argc, char* argv[])
481 if (!__initialized) {
482 _ERR("Runtime is not initialized");
486 if (path == nullptr) {
487 _ERR("executable path is null");
492 _ERR("File not exist : %s", path);
496 // launchpad override stdout and stderr to journalctl before launch application.
497 // we have to re-override that to input pipe for logging thread.
498 // if LogManager is not initialized, below redirectFD will return 0;
499 if (redirectFD() < 0) {
500 _ERR("Failed to redirect FD");
504 // VD has their own signal handler.
505 if (!pluginHasLogControl()) {
506 registerSigHandler();
509 pluginSetAppInfo(appId, path);
511 // temporal root path is overrided to real application root path
512 __pm->setAppRootPath(root);
514 // set application data path to coreclr environment.
515 // application data path can be changed by owner. So, we have to set data path just before launching.
516 char* localDataPath = app_get_data_path();
517 if (localDataPath != nullptr) {
518 setEnvironmentVariable("XDG_DATA_HOME", localDataPath);
522 vconf_ignore_key_changed(VCONFKEY_LANGSET, langChangedCB);
524 pluginBeforeExecute();
526 _INFO("execute assembly : %s", path);
528 unsigned int ret = 0;
529 int st = executeAssembly(__hostHandle, __domainId, argc, (const char**)argv, path, &ret);
531 _ERR("Failed to Execute Assembly %s (0x%08x)", path, st);
535 } // namespace dotnetcore
536 } // namespace runtime