2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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.
36 #include <service_app.h>
37 #include <app_control.h>
38 #include <dlog/dlog.h>
45 #include <linux/limits.h>
47 #include "dotnet_launcher.h"
52 bool isDebugMode = false;
53 char *coreclrGdbjit[PATH_MAX];
56 bool service_app_create(void *data)
58 // Todo: add your code here.
62 void service_app_terminate(void *data)
64 // Todo: add your code here.
69 void service_app_control(app_control_h appControl, void *data)
73 int ret = app_control_get_extra_data(appControl, "_SCD_DEBUG_", buf);
75 if (ret == APP_CONTROL_ERROR_NONE)
76 if (strcmp(*buf, "1") == 0)
80 ret = app_control_get_extra_data(appControl, "_SCD_CORECLR_GDBJIT_", coreclrGdbjit);
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.
93 #define SUCCEEDED(Status) ((Status) >= 0)
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"
103 bool getEntrypointExecutableabsolutePath(std::string& entrypointExecutable)
107 entrypointExecutable.clear();
109 // Get path to the executable for the current process using
110 // platform specific means.
112 // On Linux, fetch the entry point EXE absolute path, inclusive of filename.
114 ssize_t res = readlink(symlinkEntrypointExecutable, exe, PATH_MAX - 1);
117 entrypointExecutable.assign(exe);
126 bool getabsolutePath(const char* path, std::string& absolutePath)
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() != '/');
142 bool getDirectory(const char* absolutePath, std::string& directory)
144 directory.assign(absolutePath);
145 size_t lastSlash = directory.rfind('/');
146 if (lastSlash != std::string::npos) {
147 directory.erase(lastSlash);
154 bool getClrFilesabsolutePath(const char* currentExePath, const char* clrFilesPath, std::string& clrFilesAbsolutePath)
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]");
165 clrFilesPathLocal = clrFilesRelativePath.c_str();
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
171 if (!getabsolutePath(clrFilesPathLocal, clrFilesAbsolutePath)) {
172 perror("Failed to convert CLR files path to absolute path");
179 void addFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
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
188 DIR* dir = opendir(directory);
192 std::set<std::string> addedAssemblies;
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);
200 struct dirent* entry;
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) {
209 // Handle symlinks and file systems that do not support d_type
213 std::string fullFileName;
215 fullFileName.append(directory);
216 fullFileName.append("/");
217 fullFileName.append(entry->d_name);
220 if (stat(fullFileName.c_str(), &sb) == -1)
223 if (!S_ISREG(sb.st_mode))
232 std::string fileName(entry->d_name);
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))
239 std::string fileNameWithoutExt(fileName.substr(0, extPos));
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);
246 tpaList.append(directory);
248 tpaList.append(fileName);
253 // Rewind the directory stream to be able to iterate over it for the next extension
260 int executeManagedAssembly(const char* currentExeAbsolutePath,
261 const char* clrFilesAbsolutePath,
262 const char* managedAssemblyAbsolutePath,
263 int managedAssemblyArgc,
264 const char** managedAssemblyArgv)
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
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"));
283 std::string coreClrDllPath(clrFilesAbsolutePath);
284 coreClrDllPath.append("/");
285 coreClrDllPath.append(__CORECLR_DLL);
287 if (coreClrDllPath.length() >= PATH_MAX) {
288 fprintf(stderr, "Absolute path to libcoreclr.so too long\n");
292 // Get just the path component of the managed assembly path
294 getDirectory(managedAssemblyAbsolutePath, appPath);
297 // Construct native search directory paths
298 std::string nativeDllSearchDirs(appPath);
299 char *coreLibraries = getenv("CORE_LIBRARIES");
301 nativeDllSearchDirs.append(":");
302 nativeDllSearchDirs.append(coreLibraries);
303 if (std::strcmp(coreLibraries, clrFilesAbsolutePath) != 0)
304 addFilesFromDirectoryToTpaList(coreLibraries, tpaList);
306 nativeDllSearchDirs.append(":");
307 nativeDllSearchDirs.append(clrFilesAbsolutePath);
309 addFilesFromDirectoryToTpaList(clrFilesAbsolutePath, tpaList);
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");
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");
324 // Check whether we are enabling server GC (off by default)
325 const char* useServerGc = std::getenv(__SERVER_GC_VAR);
326 if (useServerGc == nullptr)
329 // CoreCLR expects strings "true" and "false" instead of "1" and "0".
330 useServerGc = std::strcmp(useServerGc, "1") == 0 ? "true" : "false";
332 // Allowed property names:
334 // - The base path of the application from which the exe and other assemblies will be loaded
336 // TRUSTED_PLATFORM_ASSEMBLIES
337 // - The list of complete paths to each of the fully trusted assemblies
340 // - The list of paths which will be probed by the assembly loader
343 // - The list of additional paths that the assembly loader will probe for ngen images
345 // NATIVE_DLL_SEARCH_DIRECTORIES
346 // - The list of paths that will be probed for native DLLs called by PInvoke
348 const char *propertyKeys[] = {
349 "TRUSTED_PLATFORM_ASSEMBLIES",
352 "NATIVE_DLL_SEARCH_DIRECTORIES",
355 const char *propertyValues[] = {
356 // TRUSTED_PLATFORM_ASSEMBLIES
362 // NATIVE_DLL_SEARCH_DIRECTORIES
363 nativeDllSearchDirs.c_str(),
369 unsigned int domainId;
371 int st = initializeCoreCLR(currentExeAbsolutePath,
373 sizeof(propertyKeys) / sizeof(propertyKeys[0]),
379 if (!SUCCEEDED(st)) {
380 fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st);
383 st = executeAssembly(hostHandle,
387 managedAssemblyAbsolutePath,
388 (unsigned int*)&exitCode);
390 if (!SUCCEEDED(st)) {
391 fprintf(stderr, "coreclr_execute_assembly failed - status: 0x%08x\n", st);
395 st = shutdownCoreCLR(hostHandle, domainId);
396 if (!SUCCEEDED(st)) {
397 fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st);
403 if (dlclose(__coreclrLib) != 0) {
404 fprintf(stderr, "Warning - dlclose failed\n");
407 const char* error = dlerror();
408 fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error);
415 // Display the help text
419 "Runs executables on CoreCLR\n\n"
420 "Usage: <program> [OPTIONS] [ARGUMENTS]\n"
421 "Runs <program>.dll on CoreCLR.\n\n"
423 "-_c path to libcoreclr.so and the managed CLR assemblies.\n"
424 "-_h show this help message. \n");
427 // Parse the command line arguments
428 bool parseArguments(const int argc,
430 const char** clrFilesPath,
431 int* managedAssemblyArgc,
432 const char*** managedAssemblyArgv)
436 *clrFilesPath = nullptr;
437 *managedAssemblyArgv = nullptr;
438 *managedAssemblyArgc = 0;
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) {
448 *clrFilesPath = argv[i];
450 fprintf(stderr, "Option %s: missing path\n", argv[i - 1]);
454 } else if (strcmp(argv[i], "-_h") == 0) {
459 fprintf(stderr, "Unknown option %s\n", argv[i]);
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];
477 int main(const int argc, const char* argv[])
479 /// service_app_begin
480 char *rootPath = getenv("AUL_ROOT_PATH");
481 char *secondPass = getenv("SECONDPASS");
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_
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);
496 vector<const char*> vargs;
500 dlog_print(DLOG_INFO, "dotnet", "debugmode on\n");
501 setenv("CORECLR_GDBJIT", *coreclrGdbjit, 1);
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);
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("--");
514 snprintf(buf, sizeof(buf), "%s/bin/%s", rootPath, basename(rootPath));
515 vargs.push_back(buf);
517 // Pass app argument to lldb-server as it is
518 for (int i = 1; i < argc; i++)
519 vargs.push_back(argv[i]);
521 const char** newArgs = &vargs[0];
522 setenv("SECONDPASS", "1", 1);
523 status = execvp(newArgs[0], (char *const *)newArgs);
525 dlog_print(DLOG_ERROR, "dotnet", "execvp error");
526 dlog_print(DLOG_INFO, "dotnet", "something wrong errno: %d\n", errno);
530 // Make sure we have a full path for argv[0].
531 std::string argv0AbsolutePath;
532 std::string entryPointExecutablePath;
534 if (!getEntrypointExecutableabsolutePath(entryPointExecutablePath)) {
535 perror("Could not get full path to current executable");
539 if (!getabsolutePath(entryPointExecutablePath.c_str(), argv0AbsolutePath)) {
540 perror("Could not normalize full path to current executable");
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();
550 // Check if the specified managed assembly file exists
552 if (stat(managedAssemblyAbsolutePath, &sb) == -1) {
553 perror("Managed assembly not found");
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");
563 const char* clrFilesPath;
564 const char** managedAssemblyArgv;
565 int managedAssemblyArgc;
566 if (!parseArguments(argc, argv, &clrFilesPath, &managedAssemblyArgc, &managedAssemblyArgv))
569 std::string clrFilesAbsolutePath;
570 if (!getClrFilesabsolutePath(argv0AbsolutePath.c_str(), clrFilesPath, clrFilesAbsolutePath))
573 int exitCode = executeManagedAssembly(argv0AbsolutePath.c_str(),
574 clrFilesAbsolutePath.c_str(),
575 managedAssemblyAbsolutePath,
577 managedAssemblyArgv);