X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=NativeLauncher%2Flauncher%2Fdotnet%2Fdotnet_launcher.cc;h=e6d3bc7374111350602851fd8d78674285ad8eab;hb=d31dd71a0a72bcf3d95bb635d6598d1b874a0752;hp=c0b53008cee9ea16c46b1251d04afd0d55843d10;hpb=01bdc88a025dcd2a01ca444759a27e69f9bb1209;p=platform%2Fcore%2Fdotnet%2Flauncher.git diff --git a/NativeLauncher/launcher/dotnet/dotnet_launcher.cc b/NativeLauncher/launcher/dotnet/dotnet_launcher.cc index c0b5300..e6d3bc7 100644 --- a/NativeLauncher/launcher/dotnet/dotnet_launcher.cc +++ b/NativeLauncher/launcher/dotnet/dotnet_launcher.cc @@ -16,63 +16,326 @@ #include +#include #include #include #include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "injection.h" #include "utils.h" #include "log.h" #include "launcher.h" #include "dotnet_launcher.h" +#include "plugin_manager.h" +#include "path_manager.h" +#include "log_manager.h" + +#define __XSTR(x) #x +#define __STR(x) __XSTR(x) +static const char* __FRAMEWORK_DIR = __STR(FRAMEWORK_DIR); +#undef __STR +#undef __XSTR namespace tizen { namespace runtime { namespace dotnetcore { -CoreRuntime::CoreRuntime() : +#if defined (__aarch64__) +#define ARCHITECTURE_IDENTIFIER "arm64" + +#elif defined (__arm__) +#define ARCHITECTURE_IDENTIFIER "armel" + +#elif defined (__x86_64__) +#define ARCHITECTURE_IDENTIFIER "x64" + +#elif defined (__i386__) +#define ARCHITECTURE_IDENTIFIER "x86" + +#else +#error "Unknown target" +#endif + +static const char* __TIZEN_RID_VERSION_KEY = "db/dotnet/tizen_rid_version"; + +// The sequence of RID_FALLBACK graphs must be: +// 1. Tizen + Version + Architecture +// 2. Tizen + Version +// 3. OS(tizen, linux, unix) + Architecture +// 4. OS(tizen, linux, unix) +// 5. any, base +static std::string getExtraNativeLibDirs(const std::string& appRoot) +{ + std::vector RID_FALLBACK_GRAPH; + std::vector RID_FALLBACK_TIZEN; + char* tizen_rid = vconf_get_str(__TIZEN_RID_VERSION_KEY); + if (tizen_rid) { + std::vector version; + splitPath(tizen_rid, version); + std::reverse(std::begin(version), std::end(version)); + for (unsigned int i = 0; i < version.size(); i++) { + RID_FALLBACK_TIZEN.push_back(std::string("tizen." + version[i] + "-" + ARCHITECTURE_IDENTIFIER)); + RID_FALLBACK_TIZEN.push_back(std::string("tizen." + version[i])); + } + free(tizen_rid); + } + + std::vector RID_FALLBACK_OS = {"tizen", "linux", "unix"}; + for (unsigned int i = 0; i < RID_FALLBACK_OS.size(); i++) { + RID_FALLBACK_GRAPH.push_back(std::string(RID_FALLBACK_OS[i] + "-" + ARCHITECTURE_IDENTIFIER)); + RID_FALLBACK_GRAPH.push_back(std::string(RID_FALLBACK_OS[i])); + } + RID_FALLBACK_GRAPH.push_back("any"); + RID_FALLBACK_GRAPH.push_back("base"); + + std::string candidate; + for (unsigned int i = 0; i < RID_FALLBACK_GRAPH.size(); i++) { + if (!candidate.empty()) { + candidate += ":"; + } + candidate += concatPath(appRoot, "bin/runtimes/" + RID_FALLBACK_GRAPH[i] + "/native"); + } + + candidate = candidate + ":" + concatPath(appRoot, "lib/" ARCHITECTURE_IDENTIFIER); + if (!strncmp(ARCHITECTURE_IDENTIFIER, "arm64", 5)) { + candidate = candidate + ":" + concatPath(appRoot, "lib/aarch64"); + } else if (!strncmp(ARCHITECTURE_IDENTIFIER, "armel", 5)) { + candidate = candidate + ":" + concatPath(appRoot, "lib/arm"); + } + + return candidate; +} + + +static std::vector __envList; + +static void setEnvFromFile() +{ + std::string envList; + std::ifstream inFile(ENV_FILE_PATH); + + __envList.clear(); + + if (inFile) { + _INFO("coreclr_env.list is found"); + + std::string token; + while (std::getline(inFile, token, '\n')) { + if (!token.empty()) { + __envList.push_back(token); + } + } + + for (unsigned int i = 0; i < __envList.size(); i++) { + putenv(const_cast(__envList[i].c_str())); + } + } else { + _INFO("coreclr_env.list file is not found. skip"); + } +} + +#define _unused(x) ((void)(x)) + +struct sigaction sig_abrt_new; +struct sigaction sig_abrt_old; + +static bool checkOnSigabrt = false; +static bool checkOnTerminate = false; + +static void onSigabrt(int signum) +{ + // use unused variable to avoid build warning + ssize_t ret = write(STDERR_FILENO, "onSigabrt called\n", 17); + + if (checkOnTerminate) { + ret = write(STDERR_FILENO, "onSigabrt called while terminate. go to exit\n", 45); + _unused(ret); + exit(0); + } + + if (checkOnSigabrt) { + ret = write(STDERR_FILENO, "onSigabrt called again. go to exit\n", 35); + _unused(ret); + exit(0); + } + + checkOnSigabrt = true; + if (sigaction(SIGABRT, &sig_abrt_old, NULL) == 0) { + if (raise(signum) < 0) { + ret = write(STDERR_FILENO, "Fail to raise SIGABRT\n", 22); + } + } else { + ret = write(STDERR_FILENO, "Fail to set original SIGABRT handler\n", 37); + } + _unused(ret); +} + +static void registerSigHandler() +{ + sig_abrt_new.sa_handler = onSigabrt; + if (sigemptyset(&sig_abrt_new.sa_mask) != 0) { + _ERR("Fail to sigemptyset"); + } + + if (sigaction(SIGABRT, &sig_abrt_new, &sig_abrt_old) < 0) { + _ERR("Fail to add sig handler"); + } +} + +static bool storage_cb(int id, storage_type_e type, storage_state_e state, const char *path, void *user_data) +{ + int* tmp = (int*)user_data; + if (type == STORAGE_TYPE_INTERNAL) + { + *tmp = id; + return false; + } + + return true; +} + +static void initEnvForSpecialFolder() +{ + int storageId; + int error; + char *path = NULL; + + error = storage_foreach_device_supported(storage_cb, &storageId); + if (error != STORAGE_ERROR_NONE) { + return; + } + + error = storage_get_directory(storageId, STORAGE_DIRECTORY_IMAGES, &path); + if (error == STORAGE_ERROR_NONE && path != NULL) { + setenv("XDG_PICTURES_DIR", const_cast(path), 1); + free(path); + path = NULL; + } + + error = storage_get_directory(storageId, STORAGE_DIRECTORY_MUSIC, &path); + if (error == STORAGE_ERROR_NONE && path != NULL) { + setenv("XDG_MUSIC_DIR", const_cast(path), 1); + free(path); + path = NULL; + } + + error = storage_get_directory(storageId, STORAGE_DIRECTORY_VIDEOS, &path); + if (error == STORAGE_ERROR_NONE && path != NULL) { + setenv("XDG_VIDEOS_DIR", const_cast(path), 1); + free(path); + path = NULL; + } +} + +void CoreRuntime::preloadTypes() +{ + const static std::string initDllPath = concatPath(__FRAMEWORK_DIR, "Tizen.Init.dll"); + if (!isFileExist(initDllPath)) { + _ERR("Failed to locate Tizen.Init.dll"); + return; + } + + typedef void (*InitDelegate)(); + InitDelegate initDelegate; + + int ret = createDelegate(__hostHandle, + __domainId, + "Tizen.Init", + "Tizen.Init.TypeLoader", + "PreloadTypes", + (void**)&initDelegate); + + if (ret < 0) { + _ERR("Failed to create delegate for PreloadTypes (0x%08x)", ret); + } else { + initDelegate(); + } +} + +CoreRuntime::CoreRuntime(const char* mode) : initializeClr(nullptr), executeAssembly(nullptr), shutdown(nullptr), createDelegate(nullptr), + setEnvironmentVariable(nullptr), __coreclrLib(nullptr), __hostHandle(nullptr), __domainId(-1), - preparedFunction(nullptr), - launchFunction(nullptr) + fd(0), + __initialized(false), + __isProfileMode(false) { -#define __XSTR(x) #x -#define __STR(x) __XSTR(x) - -#ifdef DEVICE_API_DIR - __deviceAPIDirectory = __STR(DEVICE_API_DIR); -#endif -#ifdef RUNTIME_DIR - __runtimeDirectory = __STR(RUNTIME_DIR); -#endif -#ifdef NATIVE_LIB_DIR - __nativeLibDirectory = __STR(NATIVE_LIB_DIR); -#endif + _INFO("Constructor called!!"); -#ifdef USE_MANAGED_LAUNCHER -#ifdef CORECLR_LAUNCHER_ASSEMBLY_PATH - __launcherAssembly = __STR(CORECLR_LAUNCHER_ASSEMBLY_PATH); -#endif -#endif + // Intiailize ecore first (signal handlers, etc.) before runtime init. + ecore_init(); -#undef __STR -#undef __XSTR + char *env = nullptr; + env = getenv("CORECLR_ENABLE_PROFILING"); + if (env != nullptr && !strcmp(env, "1")) { + _INFO("profiling mode on"); + __isProfileMode = true; + } - _DBG("Constructor called!!"); + // plugin initialize should be called before start loader mainloop. + // In case of VD plugins, attaching secure zone is done in the plugin_initialize(). + // When attaching to a secure zone, if there is a created thread, it will failed. + // So, plugin initialize should be called before mainloop start. + if (initializePluginManager(mode) < 0) { + _ERR("Failed to initialize PluginManager"); + } } CoreRuntime::~CoreRuntime() { + // workaround : to prevent crash while process terminate on profiling mode, + // kill process immediately. + // see https://github.com/dotnet/coreclr/issues/26687 + if (__isProfileMode) { + _INFO("shutdown process immediately."); + _exit(0); + } + dispose(); } -int CoreRuntime::initialize(bool standalone) +int CoreRuntime::initialize(bool standalone, bool useDlog) { + // checkInjection checks dotnet-launcher run mode + // At the moment, this mechanism is used only when the Memory Profiler is started. + int res = checkInjection(); + if (res != 0) { + _ERR("Failed to initnialize Memory Profiler"); + return -1; + } +#define __XSTR(x) #x +#define __STR(x) __XSTR(x) + +#ifdef NATIVE_LIB_DIR + __nativeLibDirectory = __STR(NATIVE_LIB_DIR); +#endif + +#undef __STR +#undef __XSTR + #ifdef __arm__ // libunwind library is used to unwind stack frame, but libunwind for ARM // does not support ARM vfpv3/NEON registers in DWARF format correctly. @@ -87,53 +350,41 @@ int CoreRuntime::initialize(bool standalone) putenv(const_cast("UNW_ARM_UNWIND_METHOD=6")); #endif // __arm__ - if (standalone) { - const char *deviceApiDirectory = getenv("__deviceAPIDirectory"); - const char *runtimeDirectory = getenv("__runtimeDirectory"); - if (deviceApiDirectory != nullptr) - __deviceAPIDirectory = deviceApiDirectory; - if (runtimeDirectory != nullptr) - __runtimeDirectory = runtimeDirectory; - -#ifdef USE_MANAGED_LAUNCHER - const char *launcherAssembly = getenv("__launcherAssembly"); - if (launcherAssembly != nullptr) - __launcherAssembly = launcherAssembly; -#endif - } + // Disable debug pipes and semaphores creation in case of non-standlone mode + if (!standalone) + putenv(const_cast("COMPlus_EnableDiagnostics=0")); - if (__deviceAPIDirectory.empty()) { - _ERR("Empty Device API Directory"); - return 1; - } else { - __deviceAPIDirectory = absolutePath(__deviceAPIDirectory); - } + // Write Debug.WriteLine to stderr + putenv(const_cast("COMPlus_DebugWriteToStdErr=1")); - if (__runtimeDirectory.empty()) { - _ERR("Empty Runtime Directory"); - return 1; - } else { - __runtimeDirectory = absolutePath(__runtimeDirectory); - } +#ifdef USE_DEFAULT_BASE_ADDR + putenv(const_cast("COMPlus_UseDefaultBaseAddr=1")); +#endif // USE_DEFAULT_BASE_ADDR -#ifdef USE_MANAGED_LAUNCHER - if (__launcherAssembly.empty()) { - _ERR("Empty Launcher Assembly"); - return 1; - } else { - __launcherAssembly = absolutePath(__launcherAssembly); + // read string from external file and set them to environment value. + setEnvFromFile(); + + if (initializePathManager(std::string(), std::string(), std::string()) < 0) { + _ERR("Failed to initialize PathManager"); + return -1; } -#endif - std::string libCoreclr(concatPath(__runtimeDirectory, "libcoreclr.so")); + if (useDlog && !pluginHasLogControl()) { + if (initializeLogManager() < 0) { + _ERR("Failed to initnialize LogManager"); + return -1; + } + } - _DBG("libcoreclr : %s", libCoreclr.c_str()); + std::string libCoreclr(concatPath(getRuntimeDir(), "libcoreclr.so")); __coreclrLib = dlopen(libCoreclr.c_str(), RTLD_NOW | RTLD_LOCAL); if (__coreclrLib == nullptr) { char *err = dlerror(); _ERR("dlopen failed to open libcoreclr.so with error %s", err); - return 1; + if (access(libCoreclr.c_str(), R_OK) == -1) + _ERR("access '%s': %s\n", libCoreclr.c_str(), strerror(errno)); + return -1; } #define CORELIB_RETURN_IF_NOSYM(type, variable, name) \ @@ -141,7 +392,7 @@ int CoreRuntime::initialize(bool standalone) variable = (type)dlsym(__coreclrLib, name); \ if (variable == nullptr) { \ _ERR(name " is not found in the libcoreclr.so"); \ - return 1; \ + return -1; \ } \ } while (0) @@ -152,15 +403,55 @@ int CoreRuntime::initialize(bool standalone) #undef CORELIB_RETURN_IF_NOSYM - _DBG("libcoreclr dlopen and dlsym success"); - _DBG("this addr : %x", this); - _DBG("coreclr_initialize : %x", initializeClr); + _INFO("libcoreclr dlopen and dlsym success"); + + if (!standalone) { + pluginPreload(); + } + + // Set environment for System.Environment.SpecialFolder + // Below function creates dbus connection by callging storage API. + // If dbus connection is created bofere fork(), forked process cannot use dbus. + // To avoid gdbus blocking issue, below function should be called after fork() + initEnvForSpecialFolder(); + + fd = open("/proc/self", O_DIRECTORY); + std::string appRoot = std::string("/proc/self/fd/") + std::to_string(fd); + std::string appBin = concatPath(appRoot, "bin"); + std::string appLib = concatPath(appRoot, "lib"); + std::string appTac = concatPath(appBin, TAC_SYMLINK_SUB_DIR); + std::string probePath = appBin + ":" + appLib + ":" + appTac; + std::string NIprobePath = concatPath(appBin, APP_NI_SUB_DIR) + ":" + concatPath(appLib, APP_NI_SUB_DIR) + ":" + appTac; + std::string tpa = getTPA(); + std::string runtimeDir = getRuntimeDir(); + std::string nativeLibPath = getExtraNativeLibDirs(appRoot) + ":" + appBin + ":" + appLib + ":" + __nativeLibDirectory + ":" + runtimeDir; + std::string appName = std::string("dotnet-launcher-") + std::to_string(getpid()); + + if (!initializeCoreClr(appName.c_str(), probePath.c_str(), NIprobePath.c_str(), nativeLibPath.c_str(), tpa.c_str())) { + _ERR("Failed to initialize coreclr"); + return -1; + } + + int st = createDelegate(__hostHandle, __domainId, "Dotnet.Launcher", "Dotnet.Launcher.Environment", "SetEnvironmentVariable", (void**)&setEnvironmentVariable); + if (st < 0 || setEnvironmentVariable == nullptr) { + _ERR("Create delegate for Dotnet.Launcher.dll -> Dotnet.Launcher.Environment -> SetEnvironmentVariable failed (0x%08x)", st); + return -1; + } + + __initialized = true; + + if (!standalone) { + preloadTypes(); // Preload common managed code + } + + _INFO("CoreRuntime initialize success"); return 0; } bool CoreRuntime::initializeCoreClr(const char* appId, const char* assemblyProbePaths, + const char* NIProbePaths, const char* pinvokeProbePaths, const char* tpaList) { @@ -175,7 +466,7 @@ bool CoreRuntime::initializeCoreClr(const char* appId, const char *propertyValues[] = { tpaList, assemblyProbePaths, - assemblyProbePaths, + NIProbePaths, pinvokeProbePaths, "UseLatestBehaviorWhenTFMNotSpecified" }; @@ -195,126 +486,98 @@ bool CoreRuntime::initializeCoreClr(const char* appId, return false; } - _DBG("Initialize core clr success"); + pluginSetCoreclrInfo(__hostHandle, __domainId, createDelegate); + + _INFO("Initialize core clr success"); return true; } -int CoreRuntime::runManagedLauncher(const char* appId, const char* appBase, const char* tpaList) +void CoreRuntime::dispose() { - if (fileNotExist(__launcherAssembly)) { - _ERR("Launcher assembly is not exist in %s", __launcherAssembly.c_str()); - return 1; - } + // call plugin finalize function to notify finalize to plugin + // dlclose shoud be done after coreclr shutdown to avoid breaking signal chain + pluginFinalize(); - if (!initializeCoreClr(appId, appBase, appBase, tpaList)) { - _ERR("Failed to initialize coreclr"); - return 1; - } - -#ifdef USE_MANAGED_LAUNCHER - void *preparedFunctionDelegate; - int st = createDelegate(__hostHandle, __domainId, - "Tizen.Runtime", - "Tizen.Runtime.Coreclr.AssemblyManager", - "Prepared", &preparedFunctionDelegate); - if (st < 0) { - _ERR("Create delegate for Launch prepared function is fail (0x%08x)", st); - return 1; - } - preparedFunction = reinterpret_cast(preparedFunctionDelegate); - - if (preparedFunction != nullptr) - preparedFunction(); - - void *launchFunctionDelegate; - st = createDelegate(__hostHandle, __domainId, - "Tizen.Runtime", - "Tizen.Runtime.Coreclr.AssemblyManager", - "Launch", &launchFunctionDelegate); - if (st < 0) { - _ERR("Create delegate for Launch managed function is fail! (0x%08x)", st); - return 1; - } - launchFunction = reinterpret_cast(launchFunctionDelegate); -#endif - return 0; -} + // ignore the signal generated by an exception that occurred during shutdown + checkOnTerminate = true; -void CoreRuntime::dispose() -{ if (__hostHandle != nullptr) { int st = shutdown(__hostHandle, __domainId); if (st < 0) _ERR("shutdown core clr fail! (0x%08x)", st); + __hostHandle = nullptr; } - if (dlclose(__coreclrLib) != 0) - _ERR("libcoreclr.so close failed"); + if (__coreclrLib != nullptr) { + if (dlclose(__coreclrLib) != 0) { + _ERR("libcoreclr.so close failed"); + } + + __coreclrLib = nullptr; + } - __coreclrLib = nullptr; + finalizePluginManager(); + finalizePathManager(); - _DBG("Dotnet runtime disposed"); + __envList.clear(); + + _INFO("Dotnet runtime disposed"); } int CoreRuntime::launch(const char* appId, const char* root, const char* path, int argc, char* argv[]) { + if (!__initialized) { + _ERR("Runtime is not initialized"); + return -1; + } + if (path == nullptr) { _ERR("executable path is null"); - return 1; + return -1; } - if (fileNotExist(path)) { + if (!isFileExist(path)) { _ERR("File not exist : %s", path); - return 1; + return -1; } - std::string tpa; - std::string appRoot = root; - std::string appBin = concatPath(appRoot, "bin"); - std::string appLib = concatPath(appRoot, "lib"); - std::string probePath = appBin + ":" + appLib + ":" + __nativeLibDirectory; - - std::vector searchDirectories; - searchDirectories.push_back(appBin); - searchDirectories.push_back(appLib); - searchDirectories.push_back(__runtimeDirectory); - searchDirectories.push_back(__deviceAPIDirectory); -#ifdef USE_MANAGED_LAUNCHER - searchDirectories.push_back(baseName(__launcherAssembly)); -#endif - - assembliesInDirectory(searchDirectories, tpa); + // launchpad override stdout and stderr to journalctl before launch application. + // we have to re-override that to input pipe for logging thread. + // if LogManager is not initialized, below redirectFD will return 0; + if (redirectFD() < 0) { + _ERR("Failed to redirect FD"); + return -1; + } -#ifdef USE_MANAGED_LAUNCHER - runManagedLauncher(appId, probePath.c_str(), tpa.c_str()); + // VD has their own signal handler. + if (!pluginHasLogControl()) { + registerSigHandler(); + } - bool success = false; - if (launchFunction != nullptr) { - std::string cppPath(path); + pluginSetAppInfo(appId, path); - if (isManagedAssembly(cppPath) && !isNativeImage(cppPath)) { - size_t extindex = cppPath.size() - 4; - cppPath = cppPath.substr(0, extindex) + ".ni" + cppPath.substr(extindex, 4); - if (!fileNotExist(cppPath)) - path = cppPath.c_str(); - } + int fd2 = open(root, O_DIRECTORY); + dup3(fd2, fd, O_CLOEXEC); + if (fd2 >= 0) + close(fd2); - success = launchFunction(root, path, argc, argv); - if (!success) - _ERR("Failed to launch Application %s", path); - return success ? 0 : 1; - } else { - _ERR("Failed to find launch function"); - return 1; + // set application data path to coreclr environment. + // application data path can be changed by owner. So, we have to set data path just before launching. + char* localDataPath = app_get_data_path(); + if (localDataPath != nullptr) { + setEnvironmentVariable("XDG_DATA_HOME", localDataPath); + free(localDataPath); } -#else - int st = initializeCoreClr(appId, probePath.c_str(), probePath.c_str(), tpa.c_str()); + + pluginBeforeExecute(); + + _INFO("execute assembly : %s", path); + unsigned int ret = 0; - st = executeAssembly(__hostHandle, __domainId, argc, (const char**)argv, path, &ret); + int st = executeAssembly(__hostHandle, __domainId, argc, (const char**)argv, path, &ret); if (st < 0) _ERR("Failed to Execute Assembly %s (0x%08x)", path, st); return ret; -#endif } } // namespace dotnetcore