mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Created a Unix version of coreconsole which, like the Windows version, is similar to corerun but runs the managed binary with the same name as the executable (but with the DLL extension). Moved code that is common between corerun and coreconsole to coreruncommon. Remove the workaround for the XCode 6.3 SDK issue wherein some of the system headers that we reference try to include a __debug header which does not exist. The 6.3.1 update fixed that issue so the workaround is no longer required. Fix #747
312 lines
9.6 KiB
C++
312 lines
9.6 KiB
C++
//
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
//
|
|
|
|
//
|
|
// Code that is used by both the Unix corerun and coreconsole.
|
|
//
|
|
|
|
#include <assert.h>
|
|
#include <dirent.h>
|
|
#include <dlfcn.h>
|
|
#include <limits.h>
|
|
#include <set>
|
|
#include <string>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
// The name of the CoreCLR native runtime DLL
|
|
#if defined(__APPLE__)
|
|
static const char * const coreClrDll = "libcoreclr.dylib";
|
|
#else
|
|
static const char * const coreClrDll = "libcoreclr.so";
|
|
#endif
|
|
|
|
// Windows types used by the ExecuteAssembly function
|
|
typedef unsigned int DWORD;
|
|
typedef const char16_t* LPCWSTR;
|
|
typedef const char* LPCSTR;
|
|
typedef int32_t HRESULT;
|
|
|
|
#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0)
|
|
|
|
// Prototype of the ExecuteAssembly function from the libcoreclr.do
|
|
typedef HRESULT (*ExecuteAssemblyFunction)(
|
|
LPCSTR exePath,
|
|
LPCSTR coreClrPath,
|
|
LPCSTR appDomainFriendlyName,
|
|
int propertyCount,
|
|
LPCSTR* propertyKeys,
|
|
LPCSTR* propertyValues,
|
|
int argc,
|
|
LPCSTR* argv,
|
|
LPCSTR managedAssemblyPath,
|
|
LPCSTR entryPointAssemblyName,
|
|
LPCSTR entryPointTypeName,
|
|
LPCSTR entryPointMethodsName,
|
|
DWORD* exitCode);
|
|
|
|
bool GetAbsolutePath(const char* path, std::string& absolutePath)
|
|
{
|
|
bool result = false;
|
|
|
|
char realPath[PATH_MAX];
|
|
if (realpath(path, realPath) != nullptr && realPath[0] != '\0')
|
|
{
|
|
absolutePath.assign(realPath);
|
|
// realpath should return canonicalized path without the trailing slash
|
|
assert(absolutePath.back() != '/');
|
|
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool GetDirectory(const char* absolutePath, std::string& directory)
|
|
{
|
|
directory.assign(absolutePath);
|
|
size_t lastSlash = directory.rfind('/');
|
|
if (lastSlash != std::string::npos)
|
|
{
|
|
directory.erase(lastSlash);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool GetClrFilesAbsolutePath(const char* currentExePath, const char* clrFilesPath, std::string& clrFilesAbsolutePath)
|
|
{
|
|
std::string clrFilesRelativePath;
|
|
const char* clrFilesPathLocal = clrFilesPath;
|
|
if (clrFilesPathLocal == nullptr)
|
|
{
|
|
// There was no CLR files path specified, use the folder of the corerun/coreconsole
|
|
if (!GetDirectory(currentExePath, clrFilesRelativePath))
|
|
{
|
|
perror("Failed to get directory from argv[0]");
|
|
return false;
|
|
}
|
|
|
|
clrFilesPathLocal = clrFilesRelativePath.c_str();
|
|
|
|
// TODO: consider using an env variable (if defined) as a fall-back.
|
|
// The windows version of the corerun uses core_root env variable
|
|
}
|
|
|
|
if (!GetAbsolutePath(clrFilesPathLocal, clrFilesAbsolutePath))
|
|
{
|
|
perror("Failed to convert CLR files path to absolute path");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList)
|
|
{
|
|
const char * const tpaExtensions[] = {
|
|
".ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
|
|
".dll",
|
|
".ni.exe",
|
|
".exe",
|
|
};
|
|
|
|
DIR* dir = opendir(directory);
|
|
if (dir == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::set<std::string> addedAssemblies;
|
|
|
|
// Walk the directory for each extension separately so that we first get files with .ni.dll extension,
|
|
// then files with .dll extension, etc.
|
|
for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
|
|
{
|
|
const char* ext = tpaExtensions[extIndex];
|
|
int extLength = strlen(ext);
|
|
|
|
struct dirent* entry;
|
|
|
|
// For all entries in the directory
|
|
while ((entry = readdir(dir)) != nullptr)
|
|
{
|
|
// We are interested in files only
|
|
switch (entry->d_type)
|
|
{
|
|
case DT_REG:
|
|
break;
|
|
|
|
// Handle symlinks and file systems that do not support d_type
|
|
case DT_LNK:
|
|
case DT_UNKNOWN:
|
|
{
|
|
std::string fullFilename;
|
|
|
|
fullFilename.append(directory);
|
|
fullFilename.append("/");
|
|
fullFilename.append(entry->d_name);
|
|
|
|
struct stat sb;
|
|
if (stat(fullFilename.c_str(), &sb) == -1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!S_ISREG(sb.st_mode))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
std::string filename(entry->d_name);
|
|
|
|
// Check if the extension matches the one we are looking for
|
|
int extPos = filename.length() - extLength;
|
|
if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
std::string filenameWithoutExt(filename.substr(0, extPos));
|
|
|
|
// Make sure if we have an assembly with multiple extensions present,
|
|
// we insert only one version of it.
|
|
if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
|
|
{
|
|
addedAssemblies.insert(filenameWithoutExt);
|
|
|
|
tpaList.append(directory);
|
|
tpaList.append("/");
|
|
tpaList.append(filename);
|
|
tpaList.append(":");
|
|
}
|
|
}
|
|
|
|
// Rewind the directory stream to be able to iterate over it for the next extension
|
|
rewinddir(dir);
|
|
}
|
|
|
|
closedir(dir);
|
|
}
|
|
|
|
int ExecuteManagedAssembly(
|
|
const char* currentExeAbsolutePath,
|
|
const char* clrFilesAbsolutePath,
|
|
const char* managedAssemblyAbsolutePath,
|
|
int managedAssemblyArgc,
|
|
const char** managedAssemblyArgv)
|
|
{
|
|
// Indicates failure
|
|
int exitCode = -1;
|
|
|
|
std::string coreClrDllPath(clrFilesAbsolutePath);
|
|
coreClrDllPath.append("/");
|
|
coreClrDllPath.append(coreClrDll);
|
|
|
|
if (coreClrDllPath.length() >= PATH_MAX)
|
|
{
|
|
fprintf(stderr, "Absolute path to libcoreclr.so too long\n");
|
|
return -1;
|
|
}
|
|
|
|
// Get just the path component of the managed assembly path
|
|
std::string appPath;
|
|
GetDirectory(managedAssemblyAbsolutePath, appPath);
|
|
|
|
std::string nativeDllSearchDirs(appPath);
|
|
nativeDllSearchDirs.append(":");
|
|
nativeDllSearchDirs.append(clrFilesAbsolutePath);
|
|
|
|
std::string tpaList;
|
|
AddFilesFromDirectoryToTpaList(clrFilesAbsolutePath, tpaList);
|
|
|
|
void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW | RTLD_LOCAL);
|
|
if (coreclrLib != nullptr)
|
|
{
|
|
ExecuteAssemblyFunction executeAssembly = (ExecuteAssemblyFunction)dlsym(coreclrLib, "ExecuteAssembly");
|
|
if (executeAssembly != nullptr)
|
|
{
|
|
// Allowed property names:
|
|
// APPBASE
|
|
// - The base path of the application from which the exe and other assemblies will be loaded
|
|
//
|
|
// TRUSTED_PLATFORM_ASSEMBLIES
|
|
// - The list of complete paths to each of the fully trusted assemblies
|
|
//
|
|
// APP_PATHS
|
|
// - The list of paths which will be probed by the assembly loader
|
|
//
|
|
// APP_NI_PATHS
|
|
// - The list of additional paths that the assembly loader will probe for ngen images
|
|
//
|
|
// NATIVE_DLL_SEARCH_DIRECTORIES
|
|
// - The list of paths that will be probed for native DLLs called by PInvoke
|
|
//
|
|
const char *propertyKeys[] = {
|
|
"TRUSTED_PLATFORM_ASSEMBLIES",
|
|
"APP_PATHS",
|
|
"APP_NI_PATHS",
|
|
"NATIVE_DLL_SEARCH_DIRECTORIES",
|
|
"AppDomainCompatSwitch"
|
|
};
|
|
const char *propertyValues[] = {
|
|
// TRUSTED_PLATFORM_ASSEMBLIES
|
|
tpaList.c_str(),
|
|
// APP_PATHS
|
|
appPath.c_str(),
|
|
// APP_NI_PATHS
|
|
appPath.c_str(),
|
|
// NATIVE_DLL_SEARCH_DIRECTORIES
|
|
nativeDllSearchDirs.c_str(),
|
|
// AppDomainCompatSwitch
|
|
"UseLatestBehaviorWhenTFMNotSpecified"
|
|
};
|
|
|
|
HRESULT st = executeAssembly(
|
|
currentExeAbsolutePath,
|
|
coreClrDllPath.c_str(),
|
|
"unixcorerun",
|
|
sizeof(propertyKeys) / sizeof(propertyKeys[0]),
|
|
propertyKeys,
|
|
propertyValues,
|
|
managedAssemblyArgc,
|
|
managedAssemblyArgv,
|
|
managedAssemblyAbsolutePath,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
(DWORD*)&exitCode);
|
|
|
|
if (!SUCCEEDED(st))
|
|
{
|
|
fprintf(stderr, "ExecuteAssembly failed - status: 0x%08x\n", st);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Function ExecuteAssembly not found in the libcoreclr.so\n");
|
|
}
|
|
|
|
if (dlclose(coreclrLib) != 0)
|
|
{
|
|
fprintf(stderr, "Warning - dlclose failed\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char* error = dlerror();
|
|
fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error);
|
|
}
|
|
|
|
return exitCode;
|
|
}
|