Revert "Add signal handler"
[platform/core/dotnet/launcher.git] / NativeLauncher / launcher / dotnet / scd_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 // Licensed to the .NET Foundation under one or more agreements.
18 // The .NET Foundation licenses this file to you under the MIT license.
19 // See the LICENSE file in the project root for more information.
20
21 #include <cstdlib>
22 #include <cstring>
23
24 #include <string>
25 #include <string.h>
26 #include <sys/stat.h>
27
28 #include <assert.h>
29 #include <dlfcn.h>
30 #include <stdlib.h>
31 #include <dirent.h>
32 #include <set>
33
34 // for serive_app
35 #include <tizen.h>
36 #include <service_app.h>
37 #include <app_control.h>
38 #include <dlog/dlog.h>
39
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <vector>
43 #include <unistd.h>
44 #include <errno.h>
45 #include <linux/limits.h>
46
47 #include "dotnet_launcher.h"
48
49 using namespace std;
50
51
52 bool isDebugMode = false;
53 char *coreclrGdbjit[PATH_MAX];
54 char *rootPath;
55
56 bool service_app_create(void *data)
57 {
58         // Todo: add your code here.
59         return true;
60 }
61
62 void service_app_terminate(void *data)
63 {
64         // Todo: add your code here.
65
66         return;
67 }
68
69 void service_app_control(app_control_h appControl, void *data)
70 {
71         // Get _DEBUG_ value
72         char* buf[82];
73         int ret = app_control_get_extra_data(appControl, "_SCD_DEBUG_", buf);
74
75         if (ret == APP_CONTROL_ERROR_NONE)
76                 if (strcmp(*buf, "1") == 0)
77                         isDebugMode = true;
78
79         if (isDebugMode)
80                 ret = app_control_get_extra_data(appControl, "_SCD_CORECLR_GDBJIT_", coreclrGdbjit);
81
82         service_app_exit();
83         return;
84 }
85
86
87 //
88 // A simple CoreCLR host that runs a managed binary with the same name as this executable but with the *.dll extension
89 // The dll binary must contain a main entry point.
90 //
91
92 #ifndef SUCCEEDED
93 #define SUCCEEDED(Status) ((Status) >= 0)
94 #endif  // !SUCCEEDED
95
96 static const char * const __CORECLR_DLL = "libcoreclr.so";
97 // Name of the environment variable controlling server GC.
98 // If set to 1, server GC is enabled on startup. If 0, server GC is
99 // disabled. Server GC is off by default.
100 static const char* __SERVER_GC_VAR = "CORECLR_SERVER_GC";
101 #define symlinkEntrypointExecutable "/proc/self/exe"
102
103 bool getEntrypointExecutableabsolutePath(std::string& entrypointExecutable)
104 {
105         bool result = false;
106
107         entrypointExecutable.clear();
108
109         // Get path to the executable for the current process using
110         // platform specific means.
111
112         // On Linux, fetch the entry point EXE absolute path, inclusive of filename.
113         char exe[PATH_MAX];
114         ssize_t res = readlink(symlinkEntrypointExecutable, exe, PATH_MAX - 1);
115         if (res != -1) {
116                 exe[res] = '\0';
117                 entrypointExecutable.assign(exe);
118                 result = true;
119         } else {
120                 result = false;
121         }
122
123         return result;
124 }
125
126 bool getabsolutePath(const char* path, std::string& absolutePath)
127 {
128         bool result = false;
129
130         char realPath[PATH_MAX];
131         if (realpath(path, realPath) != nullptr && realPath[0] != '\0') {
132                 absolutePath.assign(realPath);
133                 // realpath should return canonicalized path without the trailing slash
134                 assert(absolutePath.back() != '/');
135
136                 result = true;
137         }
138
139         return result;
140 }
141
142 bool getDirectory(const char* absolutePath, std::string& directory)
143 {
144         directory.assign(absolutePath);
145         size_t lastSlash = directory.rfind('/');
146         if (lastSlash != std::string::npos) {
147                 directory.erase(lastSlash);
148                 return true;
149         }
150
151         return false;
152 }
153
154 bool getClrFilesabsolutePath(const char* currentExePath, const char* clrFilesPath, std::string& clrFilesAbsolutePath)
155 {
156         std::string clrFilesRelativePath;
157         const char* clrFilesPathLocal = clrFilesPath;
158         if (clrFilesPathLocal == nullptr) {
159                 // There was no CLR files path specified, use the folder of the corerun/coreconsole
160                 if (!getDirectory(currentExePath, clrFilesRelativePath)) {
161                         perror("Failed to get directory from argv[0]");
162                         return false;
163                 }
164
165                 clrFilesPathLocal = clrFilesRelativePath.c_str();
166
167                 // TODO: consider using an env variable (if defined) as a fall-back.
168                 // The windows version of the corerun uses core_root env variable
169         }
170
171         if (!getabsolutePath(clrFilesPathLocal, clrFilesAbsolutePath)) {
172                 perror("Failed to convert CLR files path to absolute path");
173                 return false;
174         }
175
176         return true;
177 }
178
179 void addFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
180 {
181         const char * const tpaExtensions[] = {
182                 ".ni.dll",  // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
183                 ".dll",
184                 ".ni.exe",
185                 ".exe",
186         };
187
188         DIR* dir = opendir(directory);
189         if (dir == nullptr)
190                 return;
191
192         std::set<std::string> addedAssemblies;
193
194         // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
195         // then files with .dll extension, etc.
196         for (unsigned int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++) {
197                 const char* ext = tpaExtensions[extIndex];
198                 int extLength = strlen(ext);
199
200                 struct dirent* entry;
201
202                 // For all entries in the directory
203                 while ((entry = readdir(dir)) != nullptr) {
204                         // We are interested in files only
205                         switch (entry->d_type) {
206                                 case DT_REG:
207                                         break;
208
209                                         // Handle symlinks and file systems that do not support d_type
210                                 case DT_LNK:
211                                 case DT_UNKNOWN:
212                                 {
213                                         std::string fullFileName;
214
215                                         fullFileName.append(directory);
216                                         fullFileName.append("/");
217                                         fullFileName.append(entry->d_name);
218
219                                         struct stat sb;
220                                         if (stat(fullFileName.c_str(), &sb) == -1)
221                                                 continue;
222
223                                         if (!S_ISREG(sb.st_mode))
224                                                 continue;
225                                 }
226                                         break;
227
228                                 default:
229                                         continue;
230                         }
231
232                         std::string fileName(entry->d_name);
233
234                         // Check if the extension matches the one we are looking for
235                         int extPos = fileName.length() - extLength;
236                         if ((extPos <= 0) || (fileName.compare(extPos, extLength, ext) != 0))
237                                 continue;
238
239                         std::string fileNameWithoutExt(fileName.substr(0, extPos));
240
241                         // Make sure if we have an assembly with multiple extensions present,
242                         // we insert only one version of it.
243                         if (addedAssemblies.find(fileNameWithoutExt) == addedAssemblies.end()) {
244                                 addedAssemblies.insert(fileNameWithoutExt);
245
246                                 tpaList.append(directory);
247                                 tpaList.append("/");
248                                 tpaList.append(fileName);
249                                 tpaList.append(":");
250                         }
251                 }
252
253                 // Rewind the directory stream to be able to iterate over it for the next extension
254                 rewinddir(dir);
255         }
256
257         closedir(dir);
258 }
259
260 int executeManagedAssembly(const char* currentExeAbsolutePath,
261                                                                         const char* clrFilesAbsolutePath,
262                                                                         const char* managedAssemblyAbsolutePath,
263                                                                         int managedAssemblyArgc,
264                                                                         const char** managedAssemblyArgv)
265 {
266         // Indicates failure
267         int exitCode = -1;
268
269 #ifdef __arm__
270         // libunwind library is used to unwind stack frame, but libunwind for ARM
271         // does not support ARM vfpv3/NEON registers in DWARF format correctly.
272         // Therefore let's disable stack unwinding using DWARF information
273         // See https://github.com/dotnet/coreclr/issues/6698
274         //
275         // libunwind use following methods to unwind stack frame.
276         // UNW_ARM_METHOD_ALL                   0xFF
277         // UNW_ARM_METHOD_DWARF                 0x01
278         // UNW_ARM_METHOD_FRAME                 0x02
279         // UNW_ARM_METHOD_EXIDX                 0x04
280         putenv(const_cast<char *>("UNW_ARM_UNWIND_METHOD=6"));
281 #endif  // __arm__
282
283         std::string coreClrDllPath(clrFilesAbsolutePath);
284         coreClrDllPath.append("/");
285         coreClrDllPath.append(__CORECLR_DLL);
286
287         if (coreClrDllPath.length() >= PATH_MAX) {
288                 fprintf(stderr, "Absolute path to libcoreclr.so too long\n");
289                 return -1;
290         }
291
292         // Get just the path component of the managed assembly path
293         std::string appPath;
294         getDirectory(managedAssemblyAbsolutePath, appPath);
295
296         std::string tpaList;
297         // Construct native search directory paths
298         std::string nativeDllSearchDirs(appPath);
299         char *coreLibraries = getenv("CORE_LIBRARIES");
300         if (coreLibraries) {
301                 nativeDllSearchDirs.append(":");
302                 nativeDllSearchDirs.append(coreLibraries);
303                 if (std::strcmp(coreLibraries, clrFilesAbsolutePath) != 0)
304                         addFilesFromDirectoryToTpaList(coreLibraries, tpaList);
305         }
306         nativeDllSearchDirs.append(":");
307         nativeDllSearchDirs.append(clrFilesAbsolutePath);
308
309         addFilesFromDirectoryToTpaList(clrFilesAbsolutePath, tpaList);
310
311         void* __coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW | RTLD_LOCAL);
312         if (__coreclrLib != nullptr) {
313                 coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)dlsym(__coreclrLib, "coreclr_initialize");
314                 coreclr_execute_assembly_ptr executeAssembly = (coreclr_execute_assembly_ptr)dlsym(__coreclrLib, "coreclr_execute_assembly");
315                 coreclr_shutdown_ptr shutdownCoreCLR = (coreclr_shutdown_ptr)dlsym(__coreclrLib, "coreclr_shutdown");
316
317                 if (initializeCoreCLR == nullptr) {
318                         fprintf(stderr, "Function coreclr_initialize not found in the libcoreclr.so\n");
319                 } else if (executeAssembly == nullptr) {
320                         fprintf(stderr, "Function coreclr_execute_assembly not found in the libcoreclr.so\n");
321                 } else if (shutdownCoreCLR == nullptr) {
322                         fprintf(stderr, "Function coreclr_shutdown not found in the libcoreclr.so\n");
323                 } else {
324                         // Check whether we are enabling server GC (off by default)
325                         const char* useServerGc = std::getenv(__SERVER_GC_VAR);
326                         if (useServerGc == nullptr)
327                                 useServerGc = "0";
328
329                         // CoreCLR expects strings "true" and "false" instead of "1" and "0".
330                         useServerGc = std::strcmp(useServerGc, "1") == 0 ? "true" : "false";
331
332                         // Allowed property names:
333                         // APPBASE
334                         // - The base path of the application from which the exe and other assemblies will be loaded
335                         //
336                         // TRUSTED_PLATFORM_ASSEMBLIES
337                         // - The list of complete paths to each of the fully trusted assemblies
338                         //
339                         // APP_PATHS
340                         // - The list of paths which will be probed by the assembly loader
341                         //
342                         // APP_NI_PATHS
343                         // - The list of additional paths that the assembly loader will probe for ngen images
344                         //
345                         // NATIVE_DLL_SEARCH_DIRECTORIES
346                         // - The list of paths that will be probed for native DLLs called by PInvoke
347                         //
348                         const char *propertyKeys[] = {
349                                 "TRUSTED_PLATFORM_ASSEMBLIES",
350                                 "APP_PATHS",
351                                 "APP_NI_PATHS",
352                                 "NATIVE_DLL_SEARCH_DIRECTORIES",
353                                 "System.GC.Server",
354                         };
355                         const char *propertyValues[] = {
356                                 // TRUSTED_PLATFORM_ASSEMBLIES
357                                 tpaList.c_str(),
358                                 // APP_PATHS
359                                 appPath.c_str(),
360                                 // APP_NI_PATHS
361                                 appPath.c_str(),
362                                 // NATIVE_DLL_SEARCH_DIRECTORIES
363                                 nativeDllSearchDirs.c_str(),
364                                 // System.GC.Server
365                                 useServerGc,
366                         };
367
368                         void* hostHandle;
369                         unsigned int domainId;
370
371                         int st = initializeCoreCLR(currentExeAbsolutePath,
372                                                                          "unixcorerun",
373                                                                          sizeof(propertyKeys) / sizeof(propertyKeys[0]),
374                                                                          propertyKeys,
375                                                                          propertyValues,
376                                                                          &hostHandle,
377                                                                          &domainId);
378
379                         if (!SUCCEEDED(st)) {
380                                 fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st);
381                                 exitCode = -1;
382                         } else {
383                                 st = executeAssembly(hostHandle,
384                                                                          domainId,
385                                                                          managedAssemblyArgc,
386                                                                          managedAssemblyArgv,
387                                                                          managedAssemblyAbsolutePath,
388                                                                          (unsigned int*)&exitCode);
389
390                                 if (!SUCCEEDED(st)) {
391                                         fprintf(stderr, "coreclr_execute_assembly failed - status: 0x%08x\n", st);
392                                         exitCode = -1;
393                                 }
394
395                                 st = shutdownCoreCLR(hostHandle, domainId);
396                                 if (!SUCCEEDED(st)) {
397                                         fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st);
398                                         exitCode = -1;
399                                 }
400                         }
401                 }
402
403                 if (dlclose(__coreclrLib) != 0) {
404                         fprintf(stderr, "Warning - dlclose failed\n");
405                 }
406         } else {
407                 const char* error = dlerror();
408                 fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error);
409         }
410
411         return exitCode;
412 }
413
414
415 // Display the help text
416 void displayUsage()
417 {
418         fprintf(stderr,
419                         "Runs executables on CoreCLR\n\n"
420                         "Usage: <program> [OPTIONS] [ARGUMENTS]\n"
421                         "Runs <program>.dll on CoreCLR.\n\n"
422                         "Options:\n"
423                         "-_c    path to libcoreclr.so and the managed CLR assemblies.\n"
424                         "-_h    show this help message. \n");
425 }
426
427 // Parse the command line arguments
428 bool parseArguments(const int argc,
429                                                 const char* argv[],
430                                                 const char** clrFilesPath,
431                                                 int* managedAssemblyArgc,
432                                                 const char*** managedAssemblyArgv)
433 {
434         bool success = true;
435
436         *clrFilesPath = nullptr;
437         *managedAssemblyArgv = nullptr;
438         *managedAssemblyArgc = 0;
439
440         for (int i = 1; i < argc; i++) {
441                 // Check for options. Options to the Unix coreconsole are prefixed with '-_' to match the convention
442                 // used in the Windows version of coreconsole.
443                 if (strncmp(argv[i], "-_", 2) == 0) {
444                         // Path to the libcoreclr.so and the managed CLR assemblies
445                         if (strcmp(argv[i], "-_c") == 0) {
446                                 i++;
447                                 if (i < argc) {
448                                         *clrFilesPath = argv[i];
449                                 } else {
450                                         fprintf(stderr, "Option %s: missing path\n", argv[i - 1]);
451                                         success = false;
452                                         break;
453                                 }
454                         } else if (strcmp(argv[i], "-_h") == 0) {
455                                 displayUsage();
456                                 success = false;
457                                 break;
458                         } else {
459                                 fprintf(stderr, "Unknown option %s\n", argv[i]);
460                                 success = false;
461                                 break;
462                         }
463                 } else {
464                         // We treat everything starting from the first non-option argument as arguments
465                         // to the managed assembly.
466                         *managedAssemblyArgc = argc - i;
467                         if (*managedAssemblyArgc != 0)
468                                 *managedAssemblyArgv = &argv[i];
469
470                         break;
471                 }
472         }
473
474         return success;
475 }
476
477 int main(const int argc, const char* argv[])
478 {
479         /// service_app_begin
480         char *rootPath = getenv("AUL_ROOT_PATH");
481         char *secondPass = getenv("SECONDPASS");
482
483         // This routine check whether _SCD_DEBUG_ flag is set(1) or not.
484         // In second pass, this routine is skipped.
485         if (secondPass == NULL) {
486                 // run service_app routine to extract _SCD_DEBUG_
487                 char ad[50] = {0, };
488                 service_app_lifecycle_callback_s eventCallback;
489                 eventCallback.create = service_app_create;
490                 eventCallback.terminate = service_app_terminate;
491                 eventCallback.app_control = service_app_control;
492                 // FIXME: casting of argv is safe ?
493                 service_app_main(argc, (char**)argv, &eventCallback, ad);
494
495                 char buf[PATH_MAX];
496                 vector<const char*> vargs;
497                 int status = 0;
498
499                 if (isDebugMode) {
500                         dlog_print(DLOG_INFO, "dotnet", "debugmode on\n");
501                         setenv("CORECLR_GDBJIT", *coreclrGdbjit, 1);
502
503                         string curPath(getenv("PATH"));
504                         string newPath("/home/owner/share/tmp/sdk_tools/lldb/bin:");
505                         newPath.append(curPath);
506                         setenv("PATH", newPath.c_str(), 1);
507
508                         vargs.push_back("/home/owner/share/tmp/sdk_tools/lldb/bin/lldb-server");
509                         vargs.push_back("g");
510                         vargs.push_back("--platform=host");
511                         vargs.push_back("*:1234");
512                         vargs.push_back("--");
513                 }
514                 snprintf(buf, sizeof(buf), "%s/bin/%s", rootPath, basename(rootPath));
515                 vargs.push_back(buf);
516
517                 // Pass app argument to lldb-server as it is
518                 for (int i = 1; i < argc; i++)
519                         vargs.push_back(argv[i]);
520
521                 const char** newArgs = &vargs[0];
522                 setenv("SECONDPASS", "1", 1);
523                 status = execvp(newArgs[0], (char *const *)newArgs);
524                 if (status == -1)
525                         dlog_print(DLOG_ERROR, "dotnet", "execvp error");
526                 dlog_print(DLOG_INFO, "dotnet", "something wrong errno: %d\n", errno);
527         }
528         /// service_app_end
529
530         // Make sure we have a full path for argv[0].
531         std::string argv0AbsolutePath;
532         std::string entryPointExecutablePath;
533
534         if (!getEntrypointExecutableabsolutePath(entryPointExecutablePath)) {
535                 perror("Could not get full path to current executable");
536                 return -1;
537         }
538
539         if (!getabsolutePath(entryPointExecutablePath.c_str(), argv0AbsolutePath)) {
540                 perror("Could not normalize full path to current executable");
541                 return -1;
542         }
543
544         // We will try to load the managed assembly with the same name as this executable
545         // but with the .dll extension.
546         std::string programPath(argv0AbsolutePath);
547         programPath.append(".dll");
548         const char* managedAssemblyAbsolutePath = programPath.c_str();
549
550         // Check if the specified managed assembly file exists
551         struct stat sb;
552         if (stat(managedAssemblyAbsolutePath, &sb) == -1) {
553                 perror("Managed assembly not found");
554                 return -1;
555         }
556
557         // Verify that the managed assembly path points to a file
558         if (!S_ISREG(sb.st_mode)) {
559                 fprintf(stderr, "The specified managed assembly is not a file\n");
560                 return -1;
561         }
562
563         const char* clrFilesPath;
564         const char** managedAssemblyArgv;
565         int managedAssemblyArgc;
566         if (!parseArguments(argc, argv, &clrFilesPath, &managedAssemblyArgc, &managedAssemblyArgv))
567                 return -1;
568
569         std::string clrFilesAbsolutePath;
570         if (!getClrFilesabsolutePath(argv0AbsolutePath.c_str(), clrFilesPath, clrFilesAbsolutePath))
571                 return -1;
572
573         int exitCode = executeManagedAssembly(argv0AbsolutePath.c_str(),
574                                                                                         clrFilesAbsolutePath.c_str(),
575                                                                                         managedAssemblyAbsolutePath,
576                                                                                         managedAssemblyArgc,
577                                                                                         managedAssemblyArgv);
578
579         return exitCode;
580 }