9e3a0777a5404bca15969d7163c80308143991b5
[platform/core/dotnet/launcher.git] / NativeLauncher / launcher / dotnet / dotnet_launcher.cc
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17
18 #include <dlfcn.h>
19 #include <signal.h>
20
21 #include <string>
22 #include <fstream>
23 #include <vector>
24 #include <sstream>
25
26 #include <locale>
27 #include <codecvt>
28
29 #include <fcntl.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <unistd.h>
34 #include <linux/limits.h>
35
36 #include <storage.h>
37 #include <vconf.h>
38 #include <app_common.h>
39
40 #include "injection.h"
41 #include "utils.h"
42 #include "log.h"
43 #include "launcher.h"
44 #include "dotnet_launcher.h"
45 #include "plugin_manager.h"
46 #include "path_manager.h"
47 #include "log_manager.h"
48
49 #define __XSTR(x) #x
50 #define __STR(x) __XSTR(x)
51 static const char* __FRAMEWORK_DIR = __STR(FRAMEWORK_DIR);
52 #undef __STR
53 #undef __XSTR
54
55 namespace tizen {
56 namespace runtime {
57 namespace dotnetcore {
58
59 #if defined (__aarch64__)
60 #define ARCHITECTURE_IDENTIFIER "arm64"
61
62 #elif defined (__arm__)
63 #define ARCHITECTURE_IDENTIFIER "armel"
64
65 #elif defined (__x86_64__)
66 #define ARCHITECTURE_IDENTIFIER "x64"
67
68 #elif defined (__i386__)
69 #define ARCHITECTURE_IDENTIFIER "x86"
70
71 #else
72 #error "Unknown target"
73 #endif
74
75 static const char* __TIZEN_RID_VERSION_KEY = "db/dotnet/tizen_rid_version";
76
77 // The sequence of RID_FALLBACK graphs must be:
78 // 1. Tizen + Version + Architecture
79 // 2. Tizen + Version
80 // 3. OS(tizen, linux, unix) + Architecture
81 // 4. OS(tizen, linux, unix)
82 // 5. any, base
83 static std::string getExtraNativeLibDirs(const std::string& appRoot)
84 {
85         std::vector<std::string> RID_FALLBACK_GRAPH;
86         std::vector<std::string> RID_FALLBACK_TIZEN;
87         char* tizen_rid = vconf_get_str(__TIZEN_RID_VERSION_KEY);
88         if (tizen_rid) {
89                 std::vector<std::string> version;
90                 splitPath(tizen_rid, version);
91                 std::reverse(std::begin(version), std::end(version));
92                 for (unsigned int i = 0; i < version.size(); i++) {
93                         RID_FALLBACK_TIZEN.push_back(std::string("tizen." + version[i] + "-" + ARCHITECTURE_IDENTIFIER));
94                         RID_FALLBACK_TIZEN.push_back(std::string("tizen." + version[i]));
95                 }
96                 free(tizen_rid);
97         }
98
99         std::vector<std::string> RID_FALLBACK_OS = {"tizen", "linux", "unix"};
100         for (unsigned int i = 0; i < RID_FALLBACK_OS.size(); i++) {
101                 RID_FALLBACK_GRAPH.push_back(std::string(RID_FALLBACK_OS[i] + "-" +  ARCHITECTURE_IDENTIFIER));
102                 RID_FALLBACK_GRAPH.push_back(std::string(RID_FALLBACK_OS[i]));
103         }
104         RID_FALLBACK_GRAPH.push_back("any");
105         RID_FALLBACK_GRAPH.push_back("base");
106
107         std::string candidate;
108         for (unsigned int i = 0; i < RID_FALLBACK_GRAPH.size(); i++) {
109                 if (!candidate.empty()) {
110                         candidate += ":";
111                 }
112                 candidate += concatPath(appRoot, "bin/runtimes/" + RID_FALLBACK_GRAPH[i] + "/native");
113         }
114
115         candidate = candidate + ":" + concatPath(appRoot, "lib/" ARCHITECTURE_IDENTIFIER);
116         if (!strncmp(ARCHITECTURE_IDENTIFIER, "arm64", 5)) {
117                 candidate = candidate + ":" + concatPath(appRoot, "lib/aarch64");
118         } else if (!strncmp(ARCHITECTURE_IDENTIFIER, "armel", 5)) {
119                 candidate = candidate + ":" + concatPath(appRoot, "lib/arm");
120         }
121
122         return candidate;
123 }
124
125
126 static std::vector<std::string> __envList;
127
128 static void setEnvFromFile()
129 {
130         std::string envList;
131         std::ifstream inFile(ENV_FILE_PATH);
132
133         __envList.clear();
134
135         if (inFile) {
136                 _INFO("coreclr_env.list is found");
137
138                 std::string token;
139                 while (std::getline(inFile, token, '\n')) {
140                         if (!token.empty()) {
141                                 __envList.push_back(token);
142                         }
143                 }
144
145                 for (unsigned int i = 0; i < __envList.size(); i++) {
146                         putenv(const_cast<char *>(__envList[i].c_str()));
147                 }
148         } else {
149                 _INFO("coreclr_env.list file is not found. skip");
150         }
151 }
152
153 #define _unused(x) ((void)(x))
154
155 struct sigaction sig_abrt_new;
156 struct sigaction sig_abrt_old;
157
158 static bool checkOnSigabrt = false;
159 static bool checkOnTerminate = false;
160
161 static void onSigabrt(int signum)
162 {
163         // use unused variable to avoid build warning
164         ssize_t ret = write(STDERR_FILENO, "onSigabrt called\n", 17);
165
166         if (checkOnTerminate) {
167                 ret = write(STDERR_FILENO, "onSigabrt called while terminate. go to exit\n", 45);
168                 _unused(ret);
169                 exit(0);
170         }
171
172         if (checkOnSigabrt) {
173                 ret = write(STDERR_FILENO, "onSigabrt called again. go to exit\n", 35);
174                 _unused(ret);
175                 exit(0);
176         }
177
178         checkOnSigabrt = true;
179         if (sigaction(SIGABRT, &sig_abrt_old, NULL) == 0) {
180                 if (raise(signum) < 0) {
181                         ret = write(STDERR_FILENO, "Fail to raise SIGABRT\n", 22);
182                 }
183         } else {
184                 ret = write(STDERR_FILENO, "Fail to set original SIGABRT handler\n", 37);
185         }
186         _unused(ret);
187 }
188
189 static void registerSigHandler()
190 {
191         sig_abrt_new.sa_handler = onSigabrt;
192         if (sigemptyset(&sig_abrt_new.sa_mask) != 0) {
193                 _ERR("Fail to sigemptyset");
194         }
195
196         if (sigaction(SIGABRT, &sig_abrt_new, &sig_abrt_old) < 0) {
197                 _ERR("Fail to add sig handler");
198         }
199 }
200
201 static bool storage_cb(int id, storage_type_e type, storage_state_e state, const char *path, void *user_data)
202 {
203         int* tmp = (int*)user_data;
204         if (type == STORAGE_TYPE_INTERNAL)
205         {
206                 *tmp = id;
207                 return false;
208         }
209
210         return true;
211 }
212
213 static void initEnvForSpecialFolder()
214 {
215         int storageId;
216         int error;
217         char *path = NULL;
218
219         error = storage_foreach_device_supported(storage_cb, &storageId);
220         if (error != STORAGE_ERROR_NONE) {
221                 return;
222         }
223
224         error = storage_get_directory(storageId, STORAGE_DIRECTORY_IMAGES, &path);
225         if (error == STORAGE_ERROR_NONE && path != NULL) {
226                 setenv("XDG_PICTURES_DIR", const_cast<char *>(path), 1);
227                 free(path);
228                 path = NULL;
229         }
230
231         error = storage_get_directory(storageId, STORAGE_DIRECTORY_MUSIC, &path);
232         if (error == STORAGE_ERROR_NONE && path != NULL) {
233                 setenv("XDG_MUSIC_DIR", const_cast<char *>(path), 1);
234                 free(path);
235                 path = NULL;
236         }
237
238         error = storage_get_directory(storageId, STORAGE_DIRECTORY_VIDEOS, &path);
239         if (error == STORAGE_ERROR_NONE && path != NULL) {
240                 setenv("XDG_VIDEOS_DIR", const_cast<char *>(path), 1);
241                 free(path);
242                 path = NULL;
243         }
244 }
245
246 void CoreRuntime::preloadTypes()
247 {
248         const static std::string initDllPath = concatPath(__FRAMEWORK_DIR, "Tizen.Init.dll");
249         if (!isFileExist(initDllPath)) {
250                 _ERR("Failed to locate Tizen.Init.dll");
251                 return;
252         }
253
254         typedef void (*InitDelegate)();
255         InitDelegate initDelegate;
256
257         int ret = createDelegate(__hostHandle,
258                 __domainId,
259                 "Tizen.Init",
260                 "Tizen.Init.TypeLoader",
261                 "PreloadTypes",
262                 (void**)&initDelegate);
263
264         if (ret < 0) {
265                 _ERR("Failed to create delegate for PreloadTypes (0x%08x)", ret);
266         } else {
267                 initDelegate();
268         }
269 }
270
271 CoreRuntime::CoreRuntime(const char* mode) :
272         initializeClr(nullptr),
273         executeAssembly(nullptr),
274         shutdown(nullptr),
275         createDelegate(nullptr),
276         setEnvironmentVariable(nullptr),
277         __coreclrLib(nullptr),
278         __hostHandle(nullptr),
279         __domainId(-1),
280         fd(0),
281         __initialized(false),
282         __isProfileMode(false)
283 {
284         _INFO("Constructor called!!");
285
286         char *env = nullptr;
287         env = getenv("CORECLR_ENABLE_PROFILING");
288         if (env != nullptr && !strcmp(env, "1")) {
289                 _INFO("profiling mode on");
290                 __isProfileMode = true;
291         }
292
293         // plugin initialize should be called before start loader mainloop.
294         // In case of VD plugins, attaching secure zone is done in the plugin_initialize().
295         // When attaching to a secure zone, if there is a created thread, it will failed.
296         // So, plugin initialize should be called before mainloop start.
297         if (initializePluginManager(mode) < 0) {
298                 _ERR("Failed to initialize PluginManager");
299         }
300 }
301
302 CoreRuntime::~CoreRuntime()
303 {
304         // workaround : to prevent crash while process terminate on profiling mode,
305         //              kill process immediately.
306         // see https://github.com/dotnet/coreclr/issues/26687
307         if (__isProfileMode) {
308                 _INFO("shutdown process immediately.");
309                 _exit(0);
310         }
311
312         dispose();
313 }
314
315 int CoreRuntime::initialize(bool standalone, bool useDlog)
316 {
317         // checkInjection checks dotnet-launcher run mode
318         // At the moment, this mechanism is used only when the Memory Profiler is started.
319         int res = checkInjection();
320         if (res != 0) {
321                 _ERR("Failed to initnialize Memory Profiler");
322                 return -1;
323         }
324 #define __XSTR(x) #x
325 #define __STR(x) __XSTR(x)
326
327 #ifdef NATIVE_LIB_DIR
328         __nativeLibDirectory = __STR(NATIVE_LIB_DIR);
329 #endif
330
331 #undef __STR
332 #undef __XSTR
333
334 #ifdef __arm__
335         // libunwind library is used to unwind stack frame, but libunwind for ARM
336         // does not support ARM vfpv3/NEON registers in DWARF format correctly.
337         // Therefore let's disable stack unwinding using DWARF information
338         // See https://github.com/dotnet/coreclr/issues/6698
339         //
340         // libunwind use following methods to unwind stack frame.
341         // UNW_ARM_METHOD_ALL          0xFF
342         // UNW_ARM_METHOD_DWARF        0x01
343         // UNW_ARM_METHOD_FRAME        0x02
344         // UNW_ARM_METHOD_EXIDX        0x04
345         putenv(const_cast<char *>("UNW_ARM_UNWIND_METHOD=6"));
346 #endif // __arm__
347
348         // Disable debug pipes and semaphores creation in case of non-standlone mode
349         if (!standalone)
350                 putenv(const_cast<char *>("COMPlus_EnableDiagnostics=0"));
351
352         // Write Debug.WriteLine to stderr
353         putenv(const_cast<char *>("COMPlus_DebugWriteToStdErr=1"));
354
355 #ifdef USE_DEFAULT_BASE_ADDR
356         putenv(const_cast<char *>("COMPlus_UseDefaultBaseAddr=1"));
357 #endif // USE_DEFAULT_BASE_ADDR
358
359         // read string from external file and set them to environment value.
360         setEnvFromFile();
361
362         if (initializePathManager(std::string(), std::string(), std::string()) < 0) {
363                 _ERR("Failed to initialize PathManager");
364                 return -1;
365         }
366
367         if (useDlog && !pluginHasLogControl()) {
368                 if (initializeLogManager() < 0) {
369                         _ERR("Failed to initnialize LogManager");
370                         return -1;
371                 }
372         }
373
374         std::string libCoreclr(concatPath(getRuntimeDir(), "libcoreclr.so"));
375
376         __coreclrLib = dlopen(libCoreclr.c_str(), RTLD_NOW | RTLD_LOCAL);
377         if (__coreclrLib == nullptr) {
378                 char *err = dlerror();
379                 _ERR("dlopen failed to open libcoreclr.so with error %s", err);
380                 if (access(libCoreclr.c_str(), R_OK) == -1)
381                         _ERR("access '%s': %s\n", libCoreclr.c_str(), strerror(errno));
382                 return -1;
383         }
384
385 #define CORELIB_RETURN_IF_NOSYM(type, variable, name) \
386         do { \
387                 variable = (type)dlsym(__coreclrLib, name); \
388                 if (variable == nullptr) { \
389                         _ERR(name " is not found in the libcoreclr.so"); \
390                         return -1; \
391                 } \
392         } while (0)
393
394         CORELIB_RETURN_IF_NOSYM(coreclr_initialize_ptr, initializeClr, "coreclr_initialize");
395         CORELIB_RETURN_IF_NOSYM(coreclr_execute_assembly_ptr, executeAssembly, "coreclr_execute_assembly");
396         CORELIB_RETURN_IF_NOSYM(coreclr_shutdown_ptr, shutdown, "coreclr_shutdown");
397         CORELIB_RETURN_IF_NOSYM(coreclr_create_delegate_ptr, createDelegate, "coreclr_create_delegate");
398
399 #undef CORELIB_RETURN_IF_NOSYM
400
401         _INFO("libcoreclr dlopen and dlsym success");
402
403         if (!standalone) {
404                 pluginPreload();
405         }
406
407         // Set environment for System.Environment.SpecialFolder
408         // Below function creates dbus connection by callging storage API.
409         // If dbus connection is created bofere fork(), forked process cannot use dbus.
410         // To avoid gdbus blocking issue, below function should be called after fork()
411         initEnvForSpecialFolder();
412
413         fd = open("/proc/self", O_DIRECTORY);
414         std::string appRoot = std::string("/proc/self/fd/") + std::to_string(fd);
415         std::string appBin = concatPath(appRoot, "bin");
416         std::string appLib = concatPath(appRoot, "lib");
417         std::string appTac = concatPath(appBin, TAC_SYMLINK_SUB_DIR);
418         std::string probePath = appBin + ":" + appLib + ":" + appTac;
419         std::string NIprobePath = concatPath(appBin, APP_NI_SUB_DIR) + ":" + concatPath(appLib, APP_NI_SUB_DIR) + ":" + appTac;
420         std::string tpa = getTPA();
421         std::string runtimeDir = getRuntimeDir();
422         std::string nativeLibPath = getExtraNativeLibDirs(appRoot) + ":" + appBin + ":" + appLib + ":" + __nativeLibDirectory + ":" + runtimeDir;
423         std::string appName = std::string("dotnet-launcher-") + std::to_string(getpid());
424
425         if (!initializeCoreClr(appName.c_str(), probePath.c_str(), NIprobePath.c_str(), nativeLibPath.c_str(), tpa.c_str())) {
426                 _ERR("Failed to initialize coreclr");
427                 return -1;
428         }
429
430         int st = createDelegate(__hostHandle, __domainId, "Dotnet.Launcher", "Dotnet.Launcher.Environment", "SetEnvironmentVariable", (void**)&setEnvironmentVariable);
431         if (st < 0 || setEnvironmentVariable == nullptr) {
432                 _ERR("Create delegate for Dotnet.Launcher.dll -> Dotnet.Launcher.Environment -> SetEnvironmentVariable failed (0x%08x)", st);
433                 return -1;
434         }
435
436         __initialized = true;
437
438         if (!standalone) {
439                 preloadTypes();         // Preload common managed code
440         }
441
442         _INFO("CoreRuntime initialize success");
443
444         return 0;
445 }
446
447 bool CoreRuntime::initializeCoreClr(const char* appId,
448                                                                          const char* assemblyProbePaths,
449                                                                          const char* NIProbePaths,
450                                                                          const char* pinvokeProbePaths,
451                                                                          const char* tpaList)
452 {
453         const char *propertyKeys[] = {
454                 "TRUSTED_PLATFORM_ASSEMBLIES",
455                 "APP_PATHS",
456                 "APP_NI_PATHS",
457                 "NATIVE_DLL_SEARCH_DIRECTORIES",
458                 "AppDomainCompatSwitch"
459         };
460
461         const char *propertyValues[] = {
462                 tpaList,
463                 assemblyProbePaths,
464                 NIProbePaths,
465                 pinvokeProbePaths,
466                 "UseLatestBehaviorWhenTFMNotSpecified"
467         };
468
469         std::string selfPath = readSelfPath();
470
471         int st = initializeClr(selfPath.c_str(),
472                                                         appId,
473                                                         sizeof(propertyKeys) / sizeof(propertyKeys[0]),
474                                                         propertyKeys,
475                                                         propertyValues,
476                                                         &__hostHandle,
477                                                         &__domainId);
478
479         if (st < 0) {
480                 _ERR("initialize core clr fail! (0x%08x)", st);
481                 return false;
482         }
483
484         pluginSetCoreclrInfo(__hostHandle, __domainId, createDelegate);
485
486         _INFO("Initialize core clr success");
487         return true;
488 }
489
490 void CoreRuntime::dispose()
491 {
492         // call plugin finalize function to notify finalize to plugin
493         // dlclose shoud be done after coreclr shutdown to avoid breaking signal chain
494         pluginFinalize();
495
496         // ignore the signal generated by an exception that occurred during shutdown
497         checkOnTerminate = true;
498
499         if (__hostHandle != nullptr) {
500                 int st = shutdown(__hostHandle, __domainId);
501                 if (st < 0)
502                         _ERR("shutdown core clr fail! (0x%08x)", st);
503                 __hostHandle = nullptr;
504         }
505
506         if (__coreclrLib != nullptr) {
507                 if (dlclose(__coreclrLib) != 0) {
508                         _ERR("libcoreclr.so close failed");
509                 }
510
511                 __coreclrLib = nullptr;
512         }
513
514         finalizePluginManager();
515         finalizePathManager();
516
517         __envList.clear();
518
519         _INFO("Dotnet runtime disposed");
520 }
521
522 int CoreRuntime::launch(const char* appId, const char* root, const char* path, int argc, char* argv[])
523 {
524         if (!__initialized) {
525                 _ERR("Runtime is not initialized");
526                 return -1;
527         }
528
529         if (path == nullptr) {
530                 _ERR("executable path is null");
531                 return -1;
532         }
533
534         if (!isFileExist(path)) {
535                 _ERR("File not exist : %s", path);
536                 return -1;
537         }
538
539         // launchpad override stdout and stderr to journalctl before launch application.
540         // we have to re-override that to input pipe for logging thread.
541         // if LogManager is not initialized, below redirectFD will return 0;
542         if (redirectFD() < 0) {
543                 _ERR("Failed to redirect FD");
544                 return -1;
545         }
546
547         // VD has their own signal handler.
548         if (!pluginHasLogControl()) {
549                 registerSigHandler();
550         }
551
552         pluginSetAppInfo(appId, path);
553
554         int fd2 = open(root, O_DIRECTORY);
555         dup3(fd2, fd, O_CLOEXEC);
556         if (fd2 >= 0)
557                 close(fd2);
558
559         // set application data path to coreclr environment.
560         // application data path can be changed by owner. So, we have to set data path just before launching.
561         char* localDataPath = app_get_data_path();
562         if (localDataPath != nullptr) {
563                 setEnvironmentVariable("XDG_DATA_HOME", localDataPath);
564                 free(localDataPath);
565         }
566
567         pluginBeforeExecute();
568
569         _INFO("execute assembly : %s", path);
570
571         unsigned int ret = 0;
572         int st = executeAssembly(__hostHandle, __domainId, argc, (const char**)argv, path, &ret);
573         if (st < 0)
574                 _ERR("Failed to Execute Assembly %s (0x%08x)", path, st);
575         return ret;
576 }
577
578 }  // namespace dotnetcore
579 }  // namespace runtime
580 }  // namespace tizen