Rework dotnet executable (#204)
[platform/core/dotnet/launcher.git] / NativeLauncher / launcher / exec / corerun.cc
index df6cf1f..4842867 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
  *
  * Licensed under the Apache License, Version 2.0 (the License);
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-#include "dotnet_launcher.h"
+#include <dlfcn.h>
+#include <string>
+#include "coreclr_host.h"
 #include "utils.h"
-#include "log.h"
 
-using tizen::runtime::dotnetcore::CoreRuntime;
+static const char* CLR_PATH = "/usr/share/dotnet.tizen/netcoreapp";
+static const char* TOOL_PATH = "/home/owner/share/.dotnet/tools";
 
-int main(int argc, char *argv[])
-{
-       _INFO("##### Run in corerun mode #####");
+void DisplayUsage() {
+       fprintf(stdout,
+               "Execute a .NET application or command.\n\n"
+               "Usage: dotnet [options] [path-to-executable] [arguments]\n"
+               "Usage: dotnet [command] [arguments]\n\n"
+               "Options:\n"
+               "-h, --help                         show this help message\n"
+               "--clr-path <path>                  path to libcoreclr.so and runtime assemblies\n"
+               "--tool-path <path>                 path to the tool installation directory\n"
+               "--additionalprobingpath <path>     path containing assemblies to probe for\n"
+               "--globalizationinvariant           run in globalization invariant mode\n\n"
+               "Commands:\n"
+               "counters       monitor or collect performance counters\n"
+               "dump           capture or analyze a coredump\n"
+               "gcdump         capture a heapdump\n"
+               "trace          collect or convert a diagnostic event trace\n");
+}
+
+int main(int argc, const char* argv[]) {
+#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.
+       // Therefore let's disable stack unwinding using DWARF information
+       // See https://github.com/dotnet/runtime/issues/6479
+       //
+       // libunwind use following methods to unwind stack frame.
+       // UNW_ARM_METHOD_ALL          0xFF
+       // UNW_ARM_METHOD_DWARF        0x01
+       // UNW_ARM_METHOD_FRAME        0x02
+       // UNW_ARM_METHOD_EXIDX        0x04
+       putenv(const_cast<char*>("UNW_ARM_UNWIND_METHOD=6"));
+#endif // __arm__
+
+       argv++;
+       argc--;
+
+       if (argc <= 0) {
+               DisplayUsage();
+               return -1;
+       }
+
+       std::string clrFilesPath(CLR_PATH);
+       std::string toolDllsPath(TOOL_PATH);
+
+       std::string managedAssemblyPath;
+       std::string additionalProbingPath;
+       bool globalizationInvariant = false;
+
+       while (argc > 0) {
+               std::string arg(argv[0]);
+
+               if (arg == "-?" || arg == "-h" || arg == "--help") {
+                       DisplayUsage();
+                       return 0;
+               } else if (arg == "--clr-path" && argc > 1) {
+                       clrFilesPath = argv[1];
+                       argc--;
+                       argv++;
+               } else if (arg == "--tool-path" && argc > 1) {
+                       toolDllsPath = argv[1];
+                       argc--;
+                       argv++;
+               } else if (arg == "--additionalprobingpath" && argc > 1) {
+                       additionalProbingPath = absolutePath(argv[1]);
+                       argc--;
+                       argv++;
+               } else if (arg == "--globalizationinvariant") {
+                       globalizationInvariant = true;
+               } else if ((arg == "--runtimeconfig" || arg == "--depsfile") && argc > 1) {
+                       // Just for compatibility with corefx tests.
+                       // See ParseArguments() in coreclr/hosts/unixcorerun/corerun.cpp.
+                       argc--;
+                       argv++;
+               } else if (arg.at(0) == '-') {
+                       fprintf(stderr, "Unknown option %s.\n", argv[0]);
+                       DisplayUsage();
+                       return -1;
+               } else if (isManagedAssembly(arg) || isNativeImage(arg)) {
+                       if (!isFileExist(arg)) {
+                               fprintf(stderr, "The specified file does not exist.\n");
+                               return -1;
+                       }
+                       managedAssemblyPath = arg;
+                       argc--;
+                       argv++;
+                       break;
+               } else if (arg == "exec") {
+                       // 'dotnet' and 'dotnet exec' can be alternatively used.
+               } else if (arg == "tool") {
+                       fprintf(stderr, "This command is not currently supported.\n");
+                       return -1;
+               } else {
+                       managedAssemblyPath = toolDllsPath + "/dotnet-" + arg;
+
+                       if (isFileExist(managedAssemblyPath + ".ni.dll")) {
+                               managedAssemblyPath += ".ni.dll";
+                       } else if (isFileExist(managedAssemblyPath + ".dll")) {
+                               managedAssemblyPath += ".dll";
+                       } else {
+                               fprintf(stderr,
+                                       "Could not execute because dotnet-%s does not exist.\n"
+                                       "Follow the instructions for tool installation: https://github.com/Samsung/diagnostic-tools\n", argv[0]);
+                               return -1;
+                       }
+
+                       // Implicit compatibility mode for System.CommandLine.
+                       std::string termValue(getenv("TERM"));
+                       if (termValue == "linux") {
+                               setenv("TERM", "xterm", 1);
+                       }
+
+                       argc--;
+                       argv++;
+                       break;
+               }
+
+               argc--;
+               argv++;
+       }
+
+       std::string currentExeAbsolutePath = absolutePath("/proc/self/exe");
+       if (currentExeAbsolutePath.empty()) {
+               fprintf(stderr, "Failed to get the current executable's absolute path.\n");
+               return -1;
+       }
 
-       if (argc < 2) {
-               _SOUT("No parameter");
+       std::string clrFilesAbsolutePath = absolutePath(clrFilesPath);
+       if (clrFilesAbsolutePath.empty()) {
+               fprintf(stderr, "Failed to resolve the full path to the CLR files.\n");
                return -1;
        }
 
-       if (!isManagedAssembly(argv[1]) && !isNativeImage(argv[1])) {
-               _SOUT("first parameter should be assembly file");
+       std::string managedAssemblyAbsolutePath = absolutePath(managedAssemblyPath);
+       if (managedAssemblyAbsolutePath.empty()) {
+               fprintf(stderr, "Failed to get the managed assembly's absolute path.\n");
                return -1;
        }
 
-       // remove executable and assembly path form the arguments and pass that to managed code
-       int vargc = argc - 2;
-       std::vector<char*> vargs;
-       for (int i = 0; i < vargc; i++) {
-               vargs.push_back(argv[2 + i]);
+       std::string coreclrLibPath(clrFilesAbsolutePath + "/libcoreclr.so");
+       std::string appPath = baseName(managedAssemblyAbsolutePath);
+       std::string nativeDllSearchDirs(appPath);
+       nativeDllSearchDirs += ":" + additionalProbingPath;
+       nativeDllSearchDirs += ":" + clrFilesAbsolutePath;
+
+       std::string tpaList(managedAssemblyAbsolutePath);
+       // For now we don't parse .deps.json file but let application DLLs can override runtime DLLs.
+       std::vector<std::string> tpaDirs = { appPath, additionalProbingPath, clrFilesAbsolutePath };
+       addAssembliesFromDirectories(tpaDirs, tpaList);
+
+       void* coreclrLib = dlopen(coreclrLibPath.c_str(), RTLD_NOW | RTLD_LOCAL);
+       if (coreclrLib == nullptr) {
+               const char* error = dlerror();
+               fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error);
+               return -1;
        }
 
-       // set command name to assembly file
-       char* fileName = getFileNameFromPath(argv[1]);
-       setCmdName(fileName);
-       
-       CoreRuntime* runtime = new CoreRuntime("corerun");
+       coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)dlsym(coreclrLib, "coreclr_initialize");
+       coreclr_execute_assembly_ptr executeAssembly = (coreclr_execute_assembly_ptr)dlsym(coreclrLib, "coreclr_execute_assembly");
+       coreclr_shutdown_ptr shutdownCoreCLR = (coreclr_shutdown_ptr)dlsym(coreclrLib, "coreclr_shutdown");
+
+       if (initializeCoreCLR == nullptr) {
+               fprintf(stderr, "Function coreclr_initialize not found in the libcoreclr.so\n");
+               return -1;
+       } else if (executeAssembly == nullptr) {
+               fprintf(stderr, "Function coreclr_execute_assembly not found in the libcoreclr.so\n");
+               return -1;
+       } else if (shutdownCoreCLR == nullptr) {
+               fprintf(stderr, "Function coreclr_shutdown not found in the libcoreclr.so\n");
+               return -1;
+       }
 
-       // get absolute path of input dll file
-       std::string absoluteDllPath = absolutePath(argv[1]);
-       std::string absoluteRootPath = baseName(absoluteDllPath);
+       const char* propertyKeys[] = {
+               "TRUSTED_PLATFORM_ASSEMBLIES",
+               "APP_PATHS",
+               "APP_NI_PATHS",
+               "NATIVE_DLL_SEARCH_DIRECTORIES",
+               "System.Globalization.Invariant",
+       };
+       const char* propertyValues[] = {
+               tpaList.c_str(),
+               appPath.c_str(),
+               appPath.c_str(),
+               nativeDllSearchDirs.c_str(),
+               globalizationInvariant ? "true" : "false",
+       };
 
-       if (runtime->initialize(LaunchMode::corerun, false, absoluteRootPath.c_str()) != 0) {
-               _SOUT("Failed to initialize");
+       void* hostHandle;
+       unsigned int domainId;
+
+       int st = initializeCoreCLR(
+               currentExeAbsolutePath.c_str(),
+               "dotnet",
+               sizeof(propertyKeys) / sizeof(propertyKeys[0]),
+               propertyKeys,
+               propertyValues,
+               &hostHandle,
+               &domainId);
+
+       if (st < 0) {
+               fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st);
                return -1;
        }
 
-       // launch application
-       if (runtime->launch(fileName, absoluteRootPath.c_str(), absoluteDllPath.c_str(), vargc, &vargs[0])) {
-               _SOUT("Failed to launch");
+       // Set the current process's command name.
+       std::string managedAssemblyName = getFileName(managedAssemblyAbsolutePath);
+       setCmdName(managedAssemblyName);
+
+       int exitCode = -1;
+
+       st = executeAssembly(
+               hostHandle,
+               domainId,
+               argc,
+               argv,
+               managedAssemblyAbsolutePath.c_str(),
+               (unsigned int*)&exitCode);
+
+       if (st < 0) {
+               fprintf(stderr, "coreclr_execute_assembly failed - status: 0x%08x\n", st);
+               exitCode = -1;
+       }
+
+       st = shutdownCoreCLR(hostHandle, domainId);
+       if (st < 0) {
+               fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st);
                return -1;
        }
 
-       return 0;
+       return exitCode;
 }
-