Rework dotnet executable (#204)
author김상욱/Common Platform Lab(SR)/Engineer/삼성전자 <swift.kim@samsung.com>
Mon, 13 Apr 2020 03:02:36 +0000 (12:02 +0900)
committerGitHub Enterprise <noreply-CODE@samsung.com>
Mon, 13 Apr 2020 03:02:36 +0000 (12:02 +0900)
* Re-implement dotnet binary

* Implement additional switches

* Remove corerun-related code from dotnet_launcher.cc

* Update code formatting

* Fix addAssembliesFromDirectories logic and refactor

* Fix the usage of depth parameter

* Organize command line options

* Minor fixes

NativeLauncher/inc/utils.h
NativeLauncher/launcher/exec/corerun.cc
NativeLauncher/launcher/exec/launcher.cc
NativeLauncher/launcher/exec/loader.cc
NativeLauncher/launcher/lib/dotnet_launcher.cc
NativeLauncher/launcher/lib/dotnet_launcher.h
NativeLauncher/tool/ni_common.cc
NativeLauncher/util/path_manager.cc
NativeLauncher/util/utils.cc

index b5c11b8..b8b065f 100644 (file)
@@ -143,24 +143,28 @@ bool isManagedAssembly(const std::string& filePath);
 bool isNativeImage(const std::string& filePath);
 
 /**
- * @brief find assembly files in the directories
- * @param[in] directories
- * @param[out] ":" seperated assembly path list
+ * @brief Resolve assembly files from directories and append their paths to the given list.
+ * @remark If a native image exists for an assembly in the same directory, it will be used.
+ *         If multiple assemblies of the same name exist, the first one will be used.
+ * @param[in]  directories  list of directories
+ * @param[out] list         colon-separated string of assembly paths
  */
-void assembliesInDirectory(const std::vector<std::string>& directories, std::string& tpaList);
+void addAssembliesFromDirectories(const std::vector<std::string>& directories, std::string& list);
 
 /**
- * @brief function pointer for file reader
+ * @brief File search callback
+ * @param[in] path      full path to a resolved file
+ * @param[in] filename  file name
  */
-typedef std::function<void (const std::string&, const char*)> FileReader;
+typedef std::function<void (const std::string& path, const std::string& filename)> FileReader;
 
 /**
- * @brief scan files in the given directory and run file reader function for that
- * @param[in] directory
- * @param[in] file reader function
- * @param[in] scan depth
+ * @brief Scan all files in the given directory.
+ * @param[in] directory path to a root directory
+ * @param[in] reader    callback for iteration
+ * @param[in] depth     recursive search depth
  */
-void scanFilesInDir(const std::string& directory, FileReader reader, unsigned int depth);
+void scanFilesInDirectory(const std::string& directory, FileReader reader, unsigned int depth);
 
 /**
  * @brief copy smack and ownership.
@@ -220,13 +224,14 @@ bool removeAll(const bf::path& path);
  * @brief change command name of the current process for access via ps command
  * @param[in] name
  */
-void setCmdName(const char* name);
+void setCmdName(const std::string& name);
 
 /**
- * @brief get file name from the path
- * @param[in] file path
- * @return return file name if exist otherwise null.
+ * @brief Get the file name from the specified path.
+ * @remark An empty string will be returned if the path string ends with a path separator character.
+ * @param[in] path  path to a file
+ * @return a substring after the path separator character
  */
-char* getFileNameFromPath(char* path);
+std::string getFileName(const std::string& path);
 
 #endif /* __UTILS_H__ */
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;
 }
-
index 5a4ff57..84afd91 100644 (file)
@@ -83,7 +83,7 @@ int main(int argc, char *argv[])
        }
 
        // set command name to assembly file
-       setCmdName(getFileNameFromPath(standalonePath));
+       setCmdName(getFileName(standalonePath));
 
        // change cmdline from dotnet-launcher to executable path
        int cmdlineSize = paddingExist ? APPID_MAX_LENGTH : APPID_MAX_LENGTH - PaddingOption.length();
@@ -91,7 +91,7 @@ int main(int argc, char *argv[])
        snprintf(argv[0], cmdlineSize - 1, "%s", standalonePath);
 
        // initialize CoreRuntime (standalone mode enable, dlog redirection enable, root path NULL)
-       if (runtime->initialize(LaunchMode::launcher, true, appRootPath) != 0) {
+       if (runtime->initialize(LaunchMode::launcher) != 0) {
                _ERR("Failed to initialize");
                return -1;
        }
index c090fd9..dda9368 100644 (file)
@@ -121,7 +121,7 @@ static void __loader_create_cb(bundle *extra, int type, void *user_data)
        }
 
        // initialize CoreRuntime (launchmode, dlog redirection enable, root path NULL)
-       if (runtime->initialize(LaunchMode::loader, true, NULL) != 0) {
+       if (runtime->initialize(LaunchMode::loader) != 0) {
                _ERR("Failed to initialized");
        } else {
                _INFO("Success to initialized");
index 5dc2304..7ed7f39 100644 (file)
@@ -347,7 +347,7 @@ CoreRuntime::~CoreRuntime()
        dispose();
 }
 
-int CoreRuntime::initialize(LaunchMode launchMode, bool useDlog, const char* rootPath)
+int CoreRuntime::initialize(LaunchMode launchMode)
 {
        // checkInjection checks dotnet-launcher run mode
        // At the moment, this mechanism is used only when the Memory Profiler is started.
@@ -399,7 +399,7 @@ int CoreRuntime::initialize(LaunchMode launchMode, bool useDlog, const char* roo
                return -1;
        }
 
-       if (useDlog && !pluginHasLogControl()) {
+       if (!pluginHasLogControl()) {
                if (initializeLogManager() < 0) {
                        _ERR("Failed to initnialize LogManager");
                        return -1;
@@ -449,31 +449,22 @@ int CoreRuntime::initialize(LaunchMode launchMode, bool useDlog, const char* roo
        // To avoid gdbus blocking issue, below function should be called after fork()
        initEnvForSpecialFolder();
 
+       __fd = open("/proc/self", O_DIRECTORY);
+       if (__fd < 0) {
+               _ERR("Failed to open /proc/self");
+               return -1;
+       }
+
        std::string tpa = getTPA();
        std::string runtimeDir = getRuntimeDir();
        std::string appName = std::string("dotnet-launcher-") + std::to_string(getpid());
-       std::string probePath;
-       std::string NIprobePath;
-       std::string nativeLibPath;
-
-       if (launchMode == LaunchMode::corerun) {
-               probePath = rootPath;
-               NIprobePath = rootPath;
-               nativeLibPath = rootPath;
-       } else  {
-               __fd = open("/proc/self", O_DIRECTORY);
-               if (__fd < 0) {
-                       _ERR("Failed to open /proc/self");
-                       return -1;
-               }
-               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);
-               probePath = appRoot + ":" + appBin + ":" + appLib + ":" + appTac;
-               NIprobePath = concatPath(appBin, APP_NI_SUB_DIR) + ":" + concatPath(appLib, APP_NI_SUB_DIR) + ":" + appTac;
-               nativeLibPath = getExtraNativeLibDirs(appRoot) + ":" + appBin + ":" + appLib + ":" + __nativeLibDirectory + ":" + runtimeDir;
-       }
+       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 = appRoot + ":" + appBin + ":" + appLib + ":" + appTac;
+       std::string NIprobePath = concatPath(appBin, APP_NI_SUB_DIR) + ":" + concatPath(appLib, APP_NI_SUB_DIR) + ":" + appTac;
+       std::string nativeLibPath = getExtraNativeLibDirs(appRoot) + ":" + appBin + ":" + appLib + ":" + __nativeLibDirectory + ":" + runtimeDir;
 
        if (!initializeCoreClr(appName.c_str(), probePath.c_str(), NIprobePath.c_str(), nativeLibPath.c_str(), tpa.c_str())) {
                _ERR("Failed to initialize coreclr");
index 8f6c22a..53c8bfb 100644 (file)
@@ -37,7 +37,7 @@ class CoreRuntime
        public:
                CoreRuntime(const char* mode);
                ~CoreRuntime();
-               int initialize(LaunchMode mode, bool useDlog, const char* rootPath);
+               int initialize(LaunchMode mode);
                void dispose();
                int launch(const char* appId, const char* root, const char* path, int argc, char* argv[]);
 
index 05b0cfc..88cc6a1 100644 (file)
@@ -477,7 +477,7 @@ void createNiUnderTAC(std::vector<std::string> nugets, DWORD flags)
                appPaths.pop_back();
        }
 
-       auto convert = [&appPaths, flags](const std::string& path, const char* name) {
+       auto convert = [&appPaths, flags](const std::string& path, const std::string& filename) {
                if (strstr(path.c_str(), TAC_SHA_256_INFO) != NULL)
                        return;
                if (!crossgen(path, appPaths.c_str(), flags)) {
@@ -485,7 +485,7 @@ void createNiUnderTAC(std::vector<std::string> nugets, DWORD flags)
                }
        };
        for (auto& nuget : nugets) {
-               scanFilesInDir(concatPath(__DOTNET_DIR, nuget), convert, -1);
+               scanFilesInDirectory(concatPath(__DOTNET_DIR, nuget), convert, -1);
        }
 }
 
@@ -554,12 +554,12 @@ void createNiUnderDirs(const std::string rootPaths[], int count, DWORD flags)
        std::vector<std::string> tpaAssemblies;
        splitPath(__tpa, tpaAssemblies);
 
-       auto convert = [&appPaths, flags, tpaAssemblies](const std::string& path, const char* name) {
+       auto convert = [&appPaths, flags, tpaAssemblies](const std::string& path, const std::string& filename) {
                bool isAppNI = flags & NI_FLAGS_APPNI;
                if (isAppNI) {
                        for (auto& tpa : tpaAssemblies) {
-                               if (!strcmp(replaceAll(tpa.substr(tpa.rfind('/') + 1), ".ni.dll", ".dll").c_str(), name)) {
-                                       fprintf(stderr, "%s present in the TPA list skips generation of NI file.\n", name);
+                               if (!strcmp(replaceAll(tpa.substr(tpa.rfind('/') + 1), ".ni.dll", ".dll").c_str(), filename.c_str())) {
+                                       fprintf(stderr, "%s present in the TPA list skips generation of NI file.\n", filename.c_str());
                                        return;
                                }
                        }
@@ -570,7 +570,7 @@ void createNiUnderDirs(const std::string rootPaths[], int count, DWORD flags)
        };
 
        for (int i = 0; i < count; i++) {
-               scanFilesInDir(rootPaths[i], convert, 1);
+               scanFilesInDirectory(rootPaths[i], convert, 0);
        }
 
        tpaAssemblies.clear();
@@ -680,7 +680,7 @@ void removeNiPlatform()
 
 void removeNiUnderDirs(const std::string rootPaths[], int count)
 {
-       auto convert = [](const std::string& path, std::string name) {
+       auto convert = [](const std::string& path, const std::string& filename) {
                if (isNativeImage(path)) {
                        if (remove(path.c_str())) {
                                fprintf(stderr, "Failed to remove %s\n", path.c_str());
@@ -689,7 +689,7 @@ void removeNiUnderDirs(const std::string rootPaths[], int count)
        };
 
        for (int i = 0; i < count; i++) {
-               scanFilesInDir(rootPaths[i], convert, -1);
+               scanFilesInDirectory(rootPaths[i], convert, -1);
        }
 }
 
index 5df09ba..a00e247 100644 (file)
@@ -136,11 +136,8 @@ static std::string getPlatformTPA()
                std::getline(cacheFile, platform_tpa);
                cacheFile.close();
        } else {
-               std::vector<std::string> tpaDir;
-               tpaDir.push_back(getRuntimeDir());
-               tpaDir.push_back(getTizenFXDir());
-               tpaDir.push_back(getTizenFXRefDir());
-               assembliesInDirectory(tpaDir, platform_tpa);
+               std::vector<std::string> tpaDirs = { getRuntimeDir(), getTizenFXDir(), getTizenFXRefDir() };
+               addAssembliesFromDirectories(tpaDirs, platform_tpa);
        }
 
        return platform_tpa;
@@ -156,7 +153,7 @@ static std::string getPluginTPA()
                plugin_tpa = plugin_tpa_list;
        } else if (!__dllPath->extra_dirs.empty()){
                _INFO("plugin extra directory found. use plugin extra directroy for TPA");
-               assembliesInDirectory(__dllPath->extra_dirs, plugin_tpa);
+               addAssembliesFromDirectories(__dllPath->extra_dirs, plugin_tpa);
        }
 
        return plugin_tpa;
index 014f760..59a70f3 100644 (file)
@@ -258,54 +258,54 @@ uintptr_t getFileSize(const std::string& path)
        return 0;
 }
 
-std::string stripNiDLL(const std::string& path)
+std::string getAssemblyNameFromPath(const std::string& path)
 {
-       std::string niPath(path);
-       if (path.size() < 5) return niPath;
-       if (!strncasecmp(path.c_str() + path.size() - 4, ".dll", 4))
-               niPath = path.substr(0, path.size()-4);
-       else if (!strncasecmp(path.c_str() + path.size() - 4, ".exe", 4))
-               niPath = path.substr(0, path.size()-4);
+       std::string ret(getFileName(path));
 
-       if (!strncasecmp(niPath.c_str() + niPath.size() - 3, ".ni", 3))
-               return niPath.substr(0, niPath.size()-3);
+       if (ret.find_last_of(".") == std::string::npos)
+               return ret;
+       ret.erase(ret.find_last_of("."));
 
-       return niPath;
+       if (ret.size() > 3 && std::equal(ret.begin() + ret.size() - 3, ret.end(), ".ni"))
+               ret.erase(ret.size() - 3);
+
+       return ret;
 }
 
-void assembliesInDirectory(const std::vector<std::string>& directories, std::string& tpaList)
-{
-       std::map<std::string, std::string> assemblyList;
-       std::map<std::string, std::string> tmpList;
-
-       auto reader = [&assemblyList, &tmpList] (const std::string& path, const char* name) {
-               if (isManagedAssembly(path) || isNativeImage(path)) {
-                       std::string dllName = stripNiDLL(name);
-                       std::pair<std::map<std::string, std::string>::iterator, bool> ret;
-                       ret = tmpList.insert(std::pair<std::string, std::string>(dllName, path));
-                       if (ret.second == false) {
-                               if (isNativeImage(path))
-                                       tmpList[dllName] = path;
+void addAssembliesFromDirectories(const std::vector<std::string>& directories, std::string& list) {
+       std::vector<std::string> assems;
+       std::unordered_map<std::string, std::string> assemPaths;
+
+       auto reader = [&assems, &assemPaths](const std::string& path, const std::string& filename) {
+               if (isManagedAssembly(filename) || isNativeImage(filename)) {
+                       std::string assem = getAssemblyNameFromPath(filename);
+
+                       if (assemPaths.count(assem) == 0) {
+                               assems.push_back(assem);
+                               assemPaths[assem] = path;
+                       } else if (isManagedAssembly(assemPaths[assem]) && isNativeImage(filename)) {
+                               // Update only if a native image is found in the same directory.
+                               // For example, if we have two directories = { X, Y } where X contains A.dll and
+                               // Y contains both A.dll and A.ni.dll, always A.dll in X will be used.
+                               if (baseName(assemPaths[assem]).compare(baseName(path)) == 0)
+                                       assemPaths[assem] = path;
                        }
                }
        };
+       for (auto& directory : directories)
+               scanFilesInDirectory(directory, reader, 0);
 
-       for (auto directory : directories) {
-               scanFilesInDir(directory.c_str(), reader, 1);
-               // merge scaned dll list to tpa list.
-               // if the dll is already exist in the list, that is skipped.
-               assemblyList.insert(tmpList.begin(), tmpList.end());
-       }
+       if (!list.empty() && list.back() != ':')
+               list.push_back(':');
 
-       std::map<std::string, std::string>::iterator it;
-       for (it = assemblyList.begin(); it != assemblyList.end(); it++)
-               tpaList += it->second + ':';
+       for (auto& assem : assems)
+               list += assemPaths[assem] + ":";
 
-       if (tpaList.back() == ':')
-               tpaList.pop_back();
+       if (list.back() == ':')
+               list.pop_back();
 }
 
-void scanFilesInDir(const std::string& directory, FileReader reader, unsigned int depth)
+void scanFilesInDirectory(const std::string& directory, FileReader reader, unsigned int depth)
 {
        DIR *dir;
        struct dirent* entry;
@@ -342,13 +342,13 @@ void scanFilesInDir(const std::string& directory, FileReader reader, unsigned in
                }
                if (!isDir)
                        reader(path, entry->d_name);
-               else if (depth > 1 && strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
+               else if (depth > 0 && strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
                        innerDirectories.push_back(path);
        }
 
-       if (depth != 0)
+       if (depth > 0)
                for (auto& d : innerDirectories)
-                       scanFilesInDir(d.c_str(), reader, depth - 1);
+                       scanFilesInDirectory(d, reader, depth - 1);
 
        closedir(dir);
 }
@@ -598,28 +598,23 @@ bool removeAll(const bf::path& path) {
        return true;
 }
 
-void setCmdName(const char* name)
+void setCmdName(const std::string& name)
 {
        #define PRC_NAME_LENGTH         16
 
        char processName[PRC_NAME_LENGTH] = {0, };
 
-       if (name == NULL || *name == '\0') {
+       if (name.empty())
                return;
-       }
 
        memset(processName, '\0', PRC_NAME_LENGTH);
-       snprintf(processName, PRC_NAME_LENGTH, "%s", name);
+       snprintf(processName, PRC_NAME_LENGTH, "%s", name.c_str());
        prctl(PR_SET_NAME, processName);                
 }
 
-char* getFileNameFromPath(char* path)
+std::string getFileName(const std::string& path)
 {
-       char* fileName = strrchr(path, PATH_SEPARATOR);
-       if (fileName != NULL && *fileName != '\0') {
-               return ++fileName;
-       }
-
-       return NULL;
+       std::string ret(path);
+       size_t index = ret.find_last_of(PATH_SEPARATOR);
+       return index == std::string::npos ? ret : ret.substr(index + 1);
 }
-