Rework dotnet executable (#204)
[platform/core/dotnet/launcher.git] / NativeLauncher / launcher / exec / corerun.cc
1 /*
2  * Copyright (c) 2020 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 #include <dlfcn.h>
18 #include <string>
19 #include "coreclr_host.h"
20 #include "utils.h"
21
22 static const char* CLR_PATH = "/usr/share/dotnet.tizen/netcoreapp";
23 static const char* TOOL_PATH = "/home/owner/share/.dotnet/tools";
24
25 void DisplayUsage() {
26         fprintf(stdout,
27                 "Execute a .NET application or command.\n\n"
28                 "Usage: dotnet [options] [path-to-executable] [arguments]\n"
29                 "Usage: dotnet [command] [arguments]\n\n"
30                 "Options:\n"
31                 "-h, --help                         show this help message\n"
32                 "--clr-path <path>                  path to libcoreclr.so and runtime assemblies\n"
33                 "--tool-path <path>                 path to the tool installation directory\n"
34                 "--additionalprobingpath <path>     path containing assemblies to probe for\n"
35                 "--globalizationinvariant           run in globalization invariant mode\n\n"
36                 "Commands:\n"
37                 "counters       monitor or collect performance counters\n"
38                 "dump           capture or analyze a coredump\n"
39                 "gcdump         capture a heapdump\n"
40                 "trace          collect or convert a diagnostic event trace\n");
41 }
42
43 int main(int argc, const char* argv[]) {
44 #ifdef __arm__
45         // libunwind library is used to unwind stack frame, but libunwind for ARM
46         // does not support ARM vfpv3/NEON registers in DWARF format correctly.
47         // Therefore let's disable stack unwinding using DWARF information
48         // See https://github.com/dotnet/runtime/issues/6479
49         //
50         // libunwind use following methods to unwind stack frame.
51         // UNW_ARM_METHOD_ALL          0xFF
52         // UNW_ARM_METHOD_DWARF        0x01
53         // UNW_ARM_METHOD_FRAME        0x02
54         // UNW_ARM_METHOD_EXIDX        0x04
55         putenv(const_cast<char*>("UNW_ARM_UNWIND_METHOD=6"));
56 #endif // __arm__
57
58         argv++;
59         argc--;
60
61         if (argc <= 0) {
62                 DisplayUsage();
63                 return -1;
64         }
65
66         std::string clrFilesPath(CLR_PATH);
67         std::string toolDllsPath(TOOL_PATH);
68
69         std::string managedAssemblyPath;
70         std::string additionalProbingPath;
71         bool globalizationInvariant = false;
72
73         while (argc > 0) {
74                 std::string arg(argv[0]);
75
76                 if (arg == "-?" || arg == "-h" || arg == "--help") {
77                         DisplayUsage();
78                         return 0;
79                 } else if (arg == "--clr-path" && argc > 1) {
80                         clrFilesPath = argv[1];
81                         argc--;
82                         argv++;
83                 } else if (arg == "--tool-path" && argc > 1) {
84                         toolDllsPath = argv[1];
85                         argc--;
86                         argv++;
87                 } else if (arg == "--additionalprobingpath" && argc > 1) {
88                         additionalProbingPath = absolutePath(argv[1]);
89                         argc--;
90                         argv++;
91                 } else if (arg == "--globalizationinvariant") {
92                         globalizationInvariant = true;
93                 } else if ((arg == "--runtimeconfig" || arg == "--depsfile") && argc > 1) {
94                         // Just for compatibility with corefx tests.
95                         // See ParseArguments() in coreclr/hosts/unixcorerun/corerun.cpp.
96                         argc--;
97                         argv++;
98                 } else if (arg.at(0) == '-') {
99                         fprintf(stderr, "Unknown option %s.\n", argv[0]);
100                         DisplayUsage();
101                         return -1;
102                 } else if (isManagedAssembly(arg) || isNativeImage(arg)) {
103                         if (!isFileExist(arg)) {
104                                 fprintf(stderr, "The specified file does not exist.\n");
105                                 return -1;
106                         }
107                         managedAssemblyPath = arg;
108                         argc--;
109                         argv++;
110                         break;
111                 } else if (arg == "exec") {
112                         // 'dotnet' and 'dotnet exec' can be alternatively used.
113                 } else if (arg == "tool") {
114                         fprintf(stderr, "This command is not currently supported.\n");
115                         return -1;
116                 } else {
117                         managedAssemblyPath = toolDllsPath + "/dotnet-" + arg;
118
119                         if (isFileExist(managedAssemblyPath + ".ni.dll")) {
120                                 managedAssemblyPath += ".ni.dll";
121                         } else if (isFileExist(managedAssemblyPath + ".dll")) {
122                                 managedAssemblyPath += ".dll";
123                         } else {
124                                 fprintf(stderr,
125                                         "Could not execute because dotnet-%s does not exist.\n"
126                                         "Follow the instructions for tool installation: https://github.com/Samsung/diagnostic-tools\n", argv[0]);
127                                 return -1;
128                         }
129
130                         // Implicit compatibility mode for System.CommandLine.
131                         std::string termValue(getenv("TERM"));
132                         if (termValue == "linux") {
133                                 setenv("TERM", "xterm", 1);
134                         }
135
136                         argc--;
137                         argv++;
138                         break;
139                 }
140
141                 argc--;
142                 argv++;
143         }
144
145         std::string currentExeAbsolutePath = absolutePath("/proc/self/exe");
146         if (currentExeAbsolutePath.empty()) {
147                 fprintf(stderr, "Failed to get the current executable's absolute path.\n");
148                 return -1;
149         }
150
151         std::string clrFilesAbsolutePath = absolutePath(clrFilesPath);
152         if (clrFilesAbsolutePath.empty()) {
153                 fprintf(stderr, "Failed to resolve the full path to the CLR files.\n");
154                 return -1;
155         }
156
157         std::string managedAssemblyAbsolutePath = absolutePath(managedAssemblyPath);
158         if (managedAssemblyAbsolutePath.empty()) {
159                 fprintf(stderr, "Failed to get the managed assembly's absolute path.\n");
160                 return -1;
161         }
162
163         std::string coreclrLibPath(clrFilesAbsolutePath + "/libcoreclr.so");
164         std::string appPath = baseName(managedAssemblyAbsolutePath);
165         std::string nativeDllSearchDirs(appPath);
166         nativeDllSearchDirs += ":" + additionalProbingPath;
167         nativeDllSearchDirs += ":" + clrFilesAbsolutePath;
168
169         std::string tpaList(managedAssemblyAbsolutePath);
170         // For now we don't parse .deps.json file but let application DLLs can override runtime DLLs.
171         std::vector<std::string> tpaDirs = { appPath, additionalProbingPath, clrFilesAbsolutePath };
172         addAssembliesFromDirectories(tpaDirs, tpaList);
173
174         void* coreclrLib = dlopen(coreclrLibPath.c_str(), RTLD_NOW | RTLD_LOCAL);
175         if (coreclrLib == nullptr) {
176                 const char* error = dlerror();
177                 fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error);
178                 return -1;
179         }
180
181         coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)dlsym(coreclrLib, "coreclr_initialize");
182         coreclr_execute_assembly_ptr executeAssembly = (coreclr_execute_assembly_ptr)dlsym(coreclrLib, "coreclr_execute_assembly");
183         coreclr_shutdown_ptr shutdownCoreCLR = (coreclr_shutdown_ptr)dlsym(coreclrLib, "coreclr_shutdown");
184
185         if (initializeCoreCLR == nullptr) {
186                 fprintf(stderr, "Function coreclr_initialize not found in the libcoreclr.so\n");
187                 return -1;
188         } else if (executeAssembly == nullptr) {
189                 fprintf(stderr, "Function coreclr_execute_assembly not found in the libcoreclr.so\n");
190                 return -1;
191         } else if (shutdownCoreCLR == nullptr) {
192                 fprintf(stderr, "Function coreclr_shutdown not found in the libcoreclr.so\n");
193                 return -1;
194         }
195
196         const char* propertyKeys[] = {
197                 "TRUSTED_PLATFORM_ASSEMBLIES",
198                 "APP_PATHS",
199                 "APP_NI_PATHS",
200                 "NATIVE_DLL_SEARCH_DIRECTORIES",
201                 "System.Globalization.Invariant",
202         };
203         const char* propertyValues[] = {
204                 tpaList.c_str(),
205                 appPath.c_str(),
206                 appPath.c_str(),
207                 nativeDllSearchDirs.c_str(),
208                 globalizationInvariant ? "true" : "false",
209         };
210
211         void* hostHandle;
212         unsigned int domainId;
213
214         int st = initializeCoreCLR(
215                 currentExeAbsolutePath.c_str(),
216                 "dotnet",
217                 sizeof(propertyKeys) / sizeof(propertyKeys[0]),
218                 propertyKeys,
219                 propertyValues,
220                 &hostHandle,
221                 &domainId);
222
223         if (st < 0) {
224                 fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st);
225                 return -1;
226         }
227
228         // Set the current process's command name.
229         std::string managedAssemblyName = getFileName(managedAssemblyAbsolutePath);
230         setCmdName(managedAssemblyName);
231
232         int exitCode = -1;
233
234         st = executeAssembly(
235                 hostHandle,
236                 domainId,
237                 argc,
238                 argv,
239                 managedAssemblyAbsolutePath.c_str(),
240                 (unsigned int*)&exitCode);
241
242         if (st < 0) {
243                 fprintf(stderr, "coreclr_execute_assembly failed - status: 0x%08x\n", st);
244                 exitCode = -1;
245         }
246
247         st = shutdownCoreCLR(hostHandle, domainId);
248         if (st < 0) {
249                 fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st);
250                 return -1;
251         }
252
253         return exitCode;
254 }