Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Plugins/DotNET/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
add_shared_plugin(DotNET
"DotNET.cpp"
"DotNETExports.cpp")

target_compile_definitions(DotNET PUBLIC NETHOST_USE_AS_STATIC)
target_link_libraries(DotNET "${CMAKE_CURRENT_SOURCE_DIR}/External/Microsoft.NETCore.App.Host.linux-x64/libnethost.a")
101 changes: 13 additions & 88 deletions Plugins/DotNET/DotNET.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
// https://docs.microsoft.com/en-us/dotnet/core/tutorials/netcore-hosting
// https://github.com/dotnet/samples/blob/main/core/hosting/src/NativeHost/nativehost.cpp
#include "nwnx.hpp"
#include "DotNET.hpp"

#include <algorithm>
#include <string>
#include <vector>

#include "sdk/coreclr_delegates.h"
#include "sdk/hostfxr.h"
#include "External/Microsoft.NETCore.App.Host.linux-x64/nethost.h"
#include "External/Microsoft.NETCore.App.Host.linux-x64/coreclr_delegates.h"
#include "External/Microsoft.NETCore.App.Host.linux-x64/hostfxr.h"

#include <dirent.h>
#include <dlfcn.h>

Expand All @@ -19,8 +22,7 @@ namespace DotNET {
extern std::vector<void*> GetExports();
extern AllHandlers s_handlers;

static void* LoadNetHost();
static bool LoadHostFxr(void* nethost);
static bool LoadHostFxr();
static void LoadNetCore(const std::string& assembly);
static void CoreMessageHandler(const std::vector<std::string>& message);
static void Bootstrap();
Expand All @@ -34,17 +36,10 @@ static void DotNET() __attribute__((constructor));

static void DotNET()
{
void* nethost = LoadNetHost();
if (!nethost)
{
LOG_ERROR("Unable to load libnethost.so. .NET plugin will be unavailable.");
LOG_ERROR("If you're not using the .NET plugin, you can disable this message with 'NWNX_DOTNET_SKIP=y'");
return;
}

if (!LoadHostFxr(nethost))
if (!LoadHostFxr())
{
LOG_ERROR("Unable to load hostfxr.so. .NET plugin will be unavailable.");
LOG_ERROR("If you're not using the .NET plugin, you can disable this message with 'NWNX_DOTNET_SKIP=y'");
return;
}

Expand All @@ -60,86 +55,16 @@ static void DotNET()
MessageBus::Subscribe("NWNX_CORE_SIGNAL", CoreMessageHandler);
}

void* LoadNetHost()
bool LoadHostFxr()
{
void* nethost = nullptr;
if (auto nethost_path = Config::Get<std::string>("NETHOST_PATH"))
{
nethost = dlopen(nethost_path->c_str(), RTLD_LAZY);
ASSERT_MSG(nethost, "NETHOST_PATH specified ('%s') but failed to open libnethost.so at that path", nethost_path->c_str());
}

if (!nethost)
{
const char *paths[] = {
"libnethost.so",
"./libnethost.so",
"lib/libnethost.so"
};
for (auto& path : paths)
{
nethost = dlopen(path, RTLD_LAZY);
if (nethost)
{
LOG_INFO("Loaded libnethost.so from: %s (autodetected)", path);
break;
}
}
}
char buffer[PATH_MAX];
size_t buffer_size = PATH_MAX;

if (!nethost)
if (get_hostfxr_path(buffer, &buffer_size, nullptr) != 0)
{
const auto hostBaseDir = "/usr/share/dotnet/packs/Microsoft.NETCore.App.Host.linux-x64/";
const auto hostLibSuffix = "/runtimes/linux-x64/native/libnethost.so";

auto dir = opendir(hostBaseDir);
if (dir != nullptr)
{
auto* directoryEntry = readdir(dir);
std::vector<std::string> paths;

while (directoryEntry != nullptr)
{
if (directoryEntry->d_type == DT_DIR)
{
const auto path = (std::string(hostBaseDir) + directoryEntry->d_name + hostLibSuffix);
paths.push_back(path);
}

directoryEntry = readdir(dir);
}

closedir(dir);

if (!paths.empty())
{
std::sort(paths.begin(), paths.end(), std::greater<std::string>());
for (const std::string& path : paths)
{
nethost = dlopen(path.c_str(), RTLD_LAZY);
if (nethost)
{
LOG_INFO("Loaded libnethost.so from: %s (autodetected)", path);
break;
}
}
}
}
return false;
}

return nethost;
}

bool LoadHostFxr(void* nethost)
{
auto get_hostfxr_path = (int(*)(char*,size_t*,const void*))dlsym(nethost, "get_hostfxr_path");
ASSERT_OR_RETURN(get_hostfxr_path != nullptr, false);

char buffer[PATH_MAX];
size_t buffer_size = PATH_MAX;
ASSERT_OR_RETURN(get_hostfxr_path(buffer, &buffer_size, nullptr) == 0, false);
dlclose(nethost);

void *hostfxr = dlopen(buffer, RTLD_LAZY);
ASSERT_OR_RETURN(hostfxr != nullptr, false);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
The MIT License (MIT)

Copyright (c) .NET Foundation and Contributors

All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// https://github.com/dotnet/runtime/blob/main/src/native/corehost/coreclr_delegates.h
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#ifndef __CORECLR_DELEGATES_H__
#define __CORECLR_DELEGATES_H__
#ifndef HAVE_CORECLR_DELEGATES_H
#define HAVE_CORECLR_DELEGATES_H

#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C"
{
#endif

#if defined(_WIN32)
#define CORECLR_DELEGATE_CALLTYPE __stdcall
#ifdef _WCHAR_T_DEFINED
Expand Down Expand Up @@ -45,4 +50,21 @@ typedef int (CORECLR_DELEGATE_CALLTYPE *get_function_pointer_fn)(
void *reserved /* Extensibility parameter (currently unused and must be 0) */,
/*out*/ void **delegate /* Pointer where to store the function pointer result */);

#endif // __CORECLR_DELEGATES_H__
typedef int (CORECLR_DELEGATE_CALLTYPE *load_assembly_fn)(
const char_t *assembly_path /* Fully qualified path to assembly */,
void *load_context /* Extensibility parameter (currently unused and must be 0) */,
void *reserved /* Extensibility parameter (currently unused and must be 0) */);

typedef int (CORECLR_DELEGATE_CALLTYPE *load_assembly_bytes_fn)(
const void *assembly_bytes /* Bytes of the assembly to load */,
size_t assembly_bytes_len /* Byte length of the assembly to load */,
const void *symbols_bytes /* Optional. Bytes of the symbols for the assembly */,
size_t symbols_bytes_len /* Optional. Byte length of the symbols for the assembly */,
void *load_context /* Extensibility parameter (currently unused and must be 0) */,
void *reserved /* Extensibility parameter (currently unused and must be 0) */);

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // HAVE_CORECLR_DELEGATES_H
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
// https://github.com/dotnet/runtime/blob/main/src/native/corehost/hostfxr.h
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#ifndef __HOSTFXR_H__
#define __HOSTFXR_H__
#ifndef HAVE_HOSTFXR_H
#define HAVE_HOSTFXR_H

#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus

#if defined(_WIN32)
#define HOSTFXR_CALLTYPE __cdecl
#ifdef _WCHAR_T_DEFINED
Expand All @@ -29,6 +33,8 @@ enum hostfxr_delegate_type
hdt_com_unregister,
hdt_load_assembly_and_get_function_pointer,
hdt_get_function_pointer,
hdt_load_assembly,
hdt_load_assembly_bytes,
};

typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_main_fn)(const int argc, const char_t **argv);
Expand Down Expand Up @@ -65,7 +71,7 @@ typedef void(HOSTFXR_CALLTYPE *hostfxr_error_writer_fn)(const char_t *message);
// By default no callback is registered in which case the errors are written to stderr.
//
// Each call to the error writer is sort of like writing a single line (the EOL character is omitted).
// Multiple calls to the error writer may occure for one failure.
// Multiple calls to the error writer may occur for one failure.
//
// If the hostfxr invokes functions in hostpolicy as part of its operation, the error writer
// will be propagated to hostpolicy for the duration of the call. This means that errors from
Expand Down Expand Up @@ -129,7 +135,7 @@ typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_initialize_for_dotnet_command_line_fn)
// Success - Hosting components were successfully initialized
// Success_HostAlreadyInitialized - Config is compatible with already initialized hosting components
// Success_DifferentRuntimeProperties - Config has runtime properties that differ from already initialized hosting components
// CoreHostIncompatibleConfig - Config is incompatible with already initialized hosting components
// HostIncompatibleConfig - Config is incompatible with already initialized hosting components
//
// This function will process the .runtimeconfig.json to resolve frameworks and prepare everything needed
// to load the runtime. It will only process the .deps.json from frameworks (not any app/component that
Expand Down Expand Up @@ -295,10 +301,6 @@ struct hostfxr_dotnet_environment_sdk_info
const char_t* path;
};

typedef void(HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_result_fn)(
const struct hostfxr_dotnet_environment_info* info,
void* result_context);

struct hostfxr_dotnet_environment_framework_info
{
size_t size;
Expand All @@ -315,10 +317,111 @@ struct hostfxr_dotnet_environment_info
const char_t* hostfxr_commit_hash;

size_t sdk_count;
const hostfxr_dotnet_environment_sdk_info* sdks;
const struct hostfxr_dotnet_environment_sdk_info* sdks;

size_t framework_count;
const hostfxr_dotnet_environment_framework_info* frameworks;
const struct hostfxr_dotnet_environment_framework_info* frameworks;
};

#endif //__HOSTFXR_H__
typedef void(HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_result_fn)(
const struct hostfxr_dotnet_environment_info* info,
void* result_context);

//
// Returns available SDKs and frameworks.
//
// Resolves the existing SDKs and frameworks from a dotnet root directory (if
// any), or the global default location. If multi-level lookup is enabled and
// the dotnet root location is different than the global location, the SDKs and
// frameworks will be enumerated from both locations.
//
// The SDKs are sorted in ascending order by version, multi-level lookup
// locations are put before private ones.
//
// The frameworks are sorted in ascending order by name followed by version,
// multi-level lookup locations are put before private ones.
//
// Parameters:
// dotnet_root
// The path to a directory containing a dotnet executable.
//
// reserved
// Reserved for future parameters.
//
// result
// Callback invoke to return the list of SDKs and frameworks.
// Structs and their elements are valid for the duration of the call.
//
// result_context
// Additional context passed to the result callback.
//
// Return value:
// 0 on success, otherwise failure.
//
// String encoding:
// Windows - UTF-16 (pal::char_t is 2 byte wchar_t)
// Non-Windows - UTF-8 (pal::char_t is 1 byte char)
//
typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_fn)(
const char_t* dotnet_root,
void* reserved,
hostfxr_get_dotnet_environment_info_result_fn result,
void* result_context);

struct hostfxr_framework_result
{
size_t size;
const char_t* name;
const char_t* requested_version;
const char_t* resolved_version;
const char_t* resolved_path;
};

struct hostfxr_resolve_frameworks_result
{
size_t size;

size_t resolved_count;
const struct hostfxr_framework_result* resolved_frameworks;

size_t unresolved_count;
const struct hostfxr_framework_result* unresolved_frameworks;
};

typedef void (HOSTFXR_CALLTYPE* hostfxr_resolve_frameworks_result_fn)(
const struct hostfxr_resolve_frameworks_result* result,
void* result_context);

//
// Resolves frameworks for a runtime config
//
// Parameters:
// runtime_config_path
// Path to the .runtimeconfig.json file
// parameters
// Optional. Additional parameters for initialization.
// If null or dotnet_root is null, the root corresponding to the running hostfx is used.
// callback
// Optional. Result callback invoked with result of the resolution.
// Structs and their elements are valid for the duration of the call.
// result_context
// Optional. Additional context passed to the result callback.
//
// Return value:
// 0 on success, otherwise failure.
//
// String encoding:
// Windows - UTF-16 (pal::char_t is 2-byte wchar_t)
// Non-Windows - UTF-8 (pal::char_t is 1-byte char)
//
typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_resolve_frameworks_for_runtime_config_fn)(
const char_t* runtime_config_path,
/*opt*/ const struct hostfxr_initialize_parameters* parameters,
/*opt*/ hostfxr_resolve_frameworks_result_fn callback,
/*opt*/ void* result_context);

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // HAVE_HOSTFXR_H
Binary file not shown.
Binary file not shown.
Loading
Loading