Skip to content
Draft
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
6 changes: 6 additions & 0 deletions src/native/corehost/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ if(NOT CLR_CMAKE_TARGET_BROWSER)
include_directories(${CLR_SRC_NATIVE_DIR}/external/)
endif()

if(NOT CLR_CMAKE_TARGET_WIN32)
add_subdirectory(${CLR_SRC_NATIVE_DIR}/containers containers)
target_include_directories(dn-containers PRIVATE ${CLR_SRC_NATIVE_DIR})
target_include_directories(dn-containers-no-lto PRIVATE ${CLR_SRC_NATIVE_DIR})
endif()

add_subdirectory(hostcommon)
add_subdirectory(hostmisc)
add_subdirectory(nethost)
Expand Down
31 changes: 13 additions & 18 deletions src/native/corehost/apphost/bundle_marker.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,21 @@
#ifndef __BUNDLE_MARKER_H__
#define __BUNDLE_MARKER_H__

#include <cstdint>
#include "bundle_marker_c.h"

#pragma pack(push, 1)
union bundle_marker_t
// C++ wrapper for backward compatibility with existing C++ code (e.g., corehost.cpp).
// The actual implementation is in bundle_marker.c.
struct bundle_marker_t
{
static int64_t header_offset()
{
public:
uint8_t placeholder[40];
struct
{
int64_t bundle_header_offset;
uint8_t signature[32];
} locator;

static int64_t header_offset();
static bool is_bundle()
{
return header_offset() != 0;
}
};
#pragma pack(pop)
return bundle_marker_header_offset();
}

static bool is_bundle()
{
return bundle_marker_is_bundle();
}
};

#endif // __BUNDLE_MARKER_H__
68 changes: 39 additions & 29 deletions src/native/corehost/apphost/standalone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,68 @@

include_directories(..)

set(SOURCES
../bundle_marker.cpp
./hostfxr_resolver.cpp
../../corehost.cpp
)

set(HEADERS
../bundle_marker.h
../../hostfxr_resolver.h
)

if(CLR_CMAKE_TARGET_WIN32)
# Windows: use existing C++ sources with C bundle_marker implementation
set(SOURCES
../../hostmisc/bundle_marker.c
./hostfxr_resolver.cpp
../../corehost.cpp
)

set(HEADERS
../bundle_marker.h
../../hostmisc/bundle_marker_c.h
../../hostfxr_resolver.h
)

add_compile_definitions(UNICODE)
list(APPEND SOURCES
../apphost.windows.cpp)

list(APPEND HEADERS
../apphost.windows.h)
endif()

if(CLR_CMAKE_TARGET_WIN32)
list(APPEND SOURCES ${HEADERS})
endif()

add_compile_definitions(FEATURE_APPHOST)

add_executable(apphost ${SOURCES} ${RESOURCES})
add_compile_definitions(FEATURE_APPHOST)

target_link_libraries(apphost PRIVATE hostmisc fxr_resolver)
add_executable(apphost ${SOURCES} ${RESOURCES})

add_sanitizer_runtime_support(apphost)

if(NOT CLR_CMAKE_TARGET_WIN32)
disable_pax_mprotect(apphost)
endif()

install_with_stripped_symbols(apphost TARGETS corehost)
target_link_libraries(apphost PRIVATE hostmisc fxr_resolver)
target_link_libraries(apphost PRIVATE shell32)

if(CLR_CMAKE_TARGET_WIN32)
# Disable manifest generation into the file .exe on Windows
target_link_options(apphost PRIVATE "/MANIFEST:NO")

# Enable CET-compatibility
if (CLR_CMAKE_HOST_ARCH_AMD64)
target_link_options(apphost PRIVATE "/CETCOMPAT")
endif()
endif()
else()
# Non-Windows (Linux, macOS, etc.): use C sources only - no C++ in the apphost binary
set(SOURCES
./apphost_main.c
./apphost_hostfxr_resolver.c
)

if (CLR_CMAKE_TARGET_WIN32)
target_link_libraries(apphost PRIVATE shell32)
add_compile_definitions(FEATURE_APPHOST)

add_executable(apphost ${SOURCES} ${RESOURCES})

target_include_directories(apphost PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_SOURCE_DIR}/../..)

target_link_libraries(apphost PRIVATE hostmisc_c dn-containers m)

disable_pax_mprotect(apphost)
endif()

add_sanitizer_runtime_support(apphost)

install_with_stripped_symbols(apphost TARGETS corehost)

if (CLR_CMAKE_HOST_APPLE)
adhoc_sign_with_entitlements(apphost "${CLR_ENG_NATIVE_DIR}/entitlements.plist")
endif()
170 changes: 170 additions & 0 deletions src/native/corehost/apphost/standalone/apphost_hostfxr_resolver.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "apphost_hostfxr_resolver.h"
#include "pal_c.h"
#include "trace_c.h"
#include "utils_c.h"
#include "fxr_resolver_c.h"

#include <assert.h>
#include <string.h>

// SHA-256 of "dotnet-search" in UTF-8
#define EMBED_DOTNET_SEARCH_HI_PART_UTF8 "19ff3e9c3602ae8e841925bb461a0adb"
#define EMBED_DOTNET_SEARCH_LO_PART_UTF8 "064a1f1903667a5e0d87e8f608f425ac"

// <search_location> \0 <app_relative_dotnet_placeholder>
#define EMBED_DOTNET_SEARCH_FULL_UTF8 ("\0\0" EMBED_DOTNET_SEARCH_HI_PART_UTF8 EMBED_DOTNET_SEARCH_LO_PART_UTF8)

// Get the .NET search options that should be used
// Returns false if options are invalid
static bool try_get_dotnet_search_options(fxr_search_location* out_search_location, char* out_app_relative_dotnet, size_t out_app_relative_dotnet_len)
{
enum { EMBED_SIZE = 512 };

// Contains the EMBED_DOTNET_SEARCH_FULL_UTF8 value at compile time or app-relative .NET path written by the SDK.
static char embed[EMBED_SIZE] = EMBED_DOTNET_SEARCH_FULL_UTF8;

*out_search_location = (fxr_search_location)embed[0];
assert(embed[1] == 0); // NUL separates the search location and embedded .NET root value
if ((*out_search_location & search_location_app_relative) == 0)
return true;

// Get the embedded app-relative .NET path
const char* binding = &embed[2];
size_t binding_len = strlen(binding);

// Check if the path exceeds the max allowed size
enum { EMBED_APP_RELATIVE_DOTNET_MAX_SIZE = EMBED_SIZE - 3 }; // -2 for search location + null, -1 for null terminator
if (binding_len > EMBED_APP_RELATIVE_DOTNET_MAX_SIZE)
{
trace_error("The app-relative .NET path is longer than the max allowed length (%d)", EMBED_APP_RELATIVE_DOTNET_MAX_SIZE);
return false;
}

// Check if the value is empty or the same as the placeholder
static const char hi_part[] = EMBED_DOTNET_SEARCH_HI_PART_UTF8;
static const char lo_part[] = EMBED_DOTNET_SEARCH_LO_PART_UTF8;
size_t hi_len = sizeof(hi_part) - 1;
size_t lo_len = sizeof(lo_part) - 1;
if (binding_len == 0
|| (binding_len >= (hi_len + lo_len)
&& memcmp(binding, hi_part, hi_len) == 0
&& memcmp(binding + hi_len, lo_part, lo_len) == 0))
{
trace_error("The app-relative .NET path is not embedded.");
return false;
}

if (binding_len >= out_app_relative_dotnet_len)
{
trace_error("The app-relative .NET path could not be retrieved from the executable image.");
return false;
}

memcpy(out_app_relative_dotnet, binding, binding_len + 1);
trace_info("Embedded app-relative .NET path: '%s'", out_app_relative_dotnet);
return true;
}

hostfxr_main_bundle_startupinfo_fn hostfxr_resolver_resolve_main_bundle_startupinfo(const hostfxr_resolver_t* resolver)
{
assert(resolver->hostfxr_dll != NULL);
return (hostfxr_main_bundle_startupinfo_fn)pal_get_symbol(resolver->hostfxr_dll, "hostfxr_main_bundle_startupinfo");
}

hostfxr_set_error_writer_fn hostfxr_resolver_resolve_set_error_writer(const hostfxr_resolver_t* resolver)
{
assert(resolver->hostfxr_dll != NULL);
return (hostfxr_set_error_writer_fn)pal_get_symbol(resolver->hostfxr_dll, "hostfxr_set_error_writer");
}

hostfxr_main_startupinfo_fn hostfxr_resolver_resolve_main_startupinfo(const hostfxr_resolver_t* resolver)
{
assert(resolver->hostfxr_dll != NULL);
return (hostfxr_main_startupinfo_fn)pal_get_symbol(resolver->hostfxr_dll, "hostfxr_main_startupinfo");
}

hostfxr_main_fn hostfxr_resolver_resolve_main_v1(const hostfxr_resolver_t* resolver)
{
assert(resolver->hostfxr_dll != NULL);
return (hostfxr_main_fn)pal_get_symbol(resolver->hostfxr_dll, "hostfxr_main");
}

void hostfxr_resolver_init(hostfxr_resolver_t* resolver, const char* app_root)
{
resolver->hostfxr_dll = NULL;
resolver->dotnet_root = NULL;
resolver->fxr_path = NULL;
resolver->status_code = Success;

fxr_search_location search_loc = search_location_default;
char app_relative_dotnet[512];
app_relative_dotnet[0] = '\0';

if (!try_get_dotnet_search_options(&search_loc, app_relative_dotnet, sizeof(app_relative_dotnet)))
{
resolver->status_code = AppHostExeNotBoundFailure;
return;
}

trace_info(".NET root search location options: %d", search_loc);

char* app_relative_dotnet_path = NULL;
if (app_relative_dotnet[0] != '\0')
{
size_t root_len = strlen(app_root);
size_t rel_len = strlen(app_relative_dotnet);
size_t total = root_len + 1 + rel_len + 1;
app_relative_dotnet_path = (char*)malloc(total);
if (app_relative_dotnet_path != NULL)
{
snprintf(app_relative_dotnet_path, total, "%s", app_root);
utils_append_path(app_relative_dotnet_path, total, app_relative_dotnet);
}
}

char* dotnet_root = NULL;
char* fxr_path = NULL;
if (!fxr_resolver_try_get_path(app_root, search_loc, app_relative_dotnet_path,
&dotnet_root, &fxr_path))
{
resolver->status_code = CoreHostLibMissingFailure;
}
else if (!pal_is_path_fully_qualified(fxr_path))
{
trace_error("Path to %s must be fully qualified: [%s]", LIBFXR_NAME, fxr_path);
free(dotnet_root);
free(fxr_path);
resolver->status_code = CoreHostLibMissingFailure;
}
else if (pal_load_library(fxr_path, &resolver->hostfxr_dll))
{
resolver->dotnet_root = dotnet_root;
resolver->fxr_path = fxr_path;
resolver->status_code = Success;
}
else
{
trace_error("The library %s was found, but loading it from %s failed", LIBFXR_NAME, fxr_path);
free(dotnet_root);
free(fxr_path);
resolver->status_code = CoreHostLibLoadFailure;
}

free(app_relative_dotnet_path);
}

void hostfxr_resolver_cleanup(hostfxr_resolver_t* resolver)
{
if (resolver->hostfxr_dll != NULL)
{
pal_unload_library(resolver->hostfxr_dll);
resolver->hostfxr_dll = NULL;
}
free(resolver->dotnet_root);
resolver->dotnet_root = NULL;
free(resolver->fxr_path);
resolver->fxr_path = NULL;
}
33 changes: 33 additions & 0 deletions src/native/corehost/apphost/standalone/apphost_hostfxr_resolver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#ifndef APPHOST_HOSTFXR_RESOLVER_H
#define APPHOST_HOSTFXR_RESOLVER_H

#include <stdbool.h>
#include <stdint.h>
#include "hostfxr.h"
#include "error_codes.h"

// Opaque hostfxr resolver handle
typedef struct hostfxr_resolver
{
void* hostfxr_dll;
char* dotnet_root; // dynamically allocated, NULL if not set
char* fxr_path; // dynamically allocated, NULL if not set
int status_code; // StatusCode enum value
} hostfxr_resolver_t;

// Initialize the resolver: find and load hostfxr.
void hostfxr_resolver_init(hostfxr_resolver_t* resolver, const char* app_root);

// Clean up the resolver: unload hostfxr if loaded.
void hostfxr_resolver_cleanup(hostfxr_resolver_t* resolver);

// Resolve function pointers from the loaded hostfxr.
hostfxr_main_bundle_startupinfo_fn hostfxr_resolver_resolve_main_bundle_startupinfo(const hostfxr_resolver_t* resolver);
hostfxr_set_error_writer_fn hostfxr_resolver_resolve_set_error_writer(const hostfxr_resolver_t* resolver);
hostfxr_main_startupinfo_fn hostfxr_resolver_resolve_main_startupinfo(const hostfxr_resolver_t* resolver);
hostfxr_main_fn hostfxr_resolver_resolve_main_v1(const hostfxr_resolver_t* resolver);

#endif // APPHOST_HOSTFXR_RESOLVER_H
Loading
Loading