Skip to content

Commit

Permalink
Add test for external assembly probe mechanism (#113007)
Browse files Browse the repository at this point in the history
- Only try to convert to a loaded layout by file mapping for images that are files
- Add test that uses corerun with external assembly probe
  • Loading branch information
elinor-fung authored Mar 3, 2025
1 parent 9c8ec9c commit 1d44636
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 4 deletions.
74 changes: 72 additions & 2 deletions src/coreclr/hosts/corerun/corerun.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ namespace envvar

// Variable used to preload a mock hostpolicy for testing.
const char_t* mockHostPolicy = W("MOCK_HOSTPOLICY");

// Variable used to indicate how app assemblies should be provided to the runtime
// - PROPERTY: corerun will pass the paths vias the TRUSTED_PLATFORM_ASSEMBLIES property
// - EXTERNAL: corerun will pass an external assembly probe to the runtime for app assemblies
// - Not set: same as PROPERTY
const char_t* appAssemblies = W("APP_ASSEMBLIES");
}

static void wait_for_debugger()
Expand Down Expand Up @@ -242,6 +248,37 @@ size_t HOST_CONTRACT_CALLTYPE get_runtime_property(
return -1;
}

// Paths for external assembly probe
static char* s_core_libs_path = nullptr;
static char* s_core_root_path = nullptr;

static bool HOST_CONTRACT_CALLTYPE external_assembly_probe(
const char* path,
void** data_start,
int64_t* size)
{
// Get just the file name
const char* name = path;
const char* pos = strrchr(name, '/');
if (pos != NULL)
name = pos + 1;

// Try to map the file from our known app assembly paths
for (const char* dir : { s_core_libs_path, s_core_root_path })
{
if (dir == nullptr)
continue;

std::string full_path = dir;
assert(full_path.back() == pal::dir_delim);
full_path.append(name);
if (pal::try_map_file_readonly(full_path.c_str(), data_start, size))
return true;
}

return false;
}

static int run(const configuration& config)
{
platform_specific_actions actions;
Expand Down Expand Up @@ -295,7 +332,37 @@ static int run(const configuration& config)
native_search_dirs << core_root << pal::env_path_delim;
}

string_t tpa_list = build_tpa(core_root, core_libs);
string_t tpa_list;
string_t app_assemblies_env = pal::getenv(envvar::appAssemblies);
bool use_external_assembly_probe = false;
if (app_assemblies_env.empty() || app_assemblies_env == W("PROPERTY"))
{
// Use the TRUSTED_PLATFORM_ASSEMBLIES property to pass the app assemblies to the runtime.
tpa_list = build_tpa(core_root, core_libs);
}
else if (app_assemblies_env == W("EXTERNAL"))
{
// Use the external assembly probe to load assemblies from the app assembly paths.
use_external_assembly_probe = true;
if (!core_libs.empty())
{
pal::string_utf8_t core_libs_utf8 = pal::convert_to_utf8(core_libs.c_str());
s_core_libs_path = (char*)::malloc(core_libs_utf8.length() + 1);
::strcpy(s_core_libs_path, core_libs_utf8.c_str());
}

if (!core_root.empty())
{
pal::string_utf8_t core_root_utf8 = pal::convert_to_utf8(core_root.c_str());
s_core_root_path = (char*)::malloc(core_root_utf8.length() + 1);
::strcpy(s_core_root_path, core_root_utf8.c_str());
}
}
else
{
pal::fprintf(stderr, W("Unknown value for APP_ASSEMBLIES environment variable: %s\n"), app_assemblies_env.c_str());
return -1;
}

{
// Load hostpolicy if requested.
Expand Down Expand Up @@ -376,7 +443,8 @@ static int run(const configuration& config)
(void*)&config,
&get_runtime_property,
nullptr,
nullptr };
nullptr,
use_external_assembly_probe ? &external_assembly_probe : nullptr };
propertyKeys.push_back(HOST_PROPERTY_RUNTIME_CONTRACT);
std::stringstream ss;
ss << "0x" << std::hex << (size_t)(&host_contract);
Expand Down Expand Up @@ -457,6 +525,8 @@ static int run(const configuration& config)
if (exit_code != -1)
exit_code = latched_exit_code;

::free((void*)s_core_libs_path);
::free((void*)s_core_root_path);
return exit_code;
}

Expand Down
58 changes: 57 additions & 1 deletion src/coreclr/hosts/corerun/corerun.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,34 @@ namespace pal
return INVALID_FILE_ATTRIBUTES != ::GetFileAttributesW(file_path.c_str());
}

inline bool try_map_file_readonly(const char* path, void** mapped, int64_t* size)
{
HANDLE file = ::CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file == INVALID_HANDLE_VALUE)
return false;

HANDLE file_mapping = ::CreateFileMappingA(file, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (file_mapping == nullptr)
{
::CloseHandle(file);
return false;
}

void* mapped_local = ::MapViewOfFile(file_mapping, FILE_MAP_READ, 0, 0, 0);
if (mapped_local == nullptr)
{
::CloseHandle(file);
::CloseHandle(file_mapping);
return false;
}

*size = ::GetFileSize(file, nullptr);
*mapped = mapped_local;
::CloseHandle(file_mapping);
::CloseHandle(file);
return true;
}

// Forward declaration
void ensure_trailing_delimiter(pal::string_t& dir);

Expand Down Expand Up @@ -300,7 +328,9 @@ class platform_specific_actions final
#else // !TARGET_WINDOWS
#include <dirent.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

Expand All @@ -309,7 +339,6 @@ class platform_specific_actions final
#include <sys/sysctl.h>
#include <sys/types.h>
#else // !__APPLE__
#include <fcntl.h>
#include <ctype.h>
#endif // !__APPLE__

Expand Down Expand Up @@ -434,6 +463,33 @@ namespace pal
return true;
}

inline bool try_map_file_readonly(const char* path, void** mapped, int64_t* size)
{
int fd = open(path, O_RDONLY);
if (fd == -1)
return false;

struct stat buf;
if (fstat(fd, &buf) == -1)
{
close(fd);
return false;
}

int64_t size_local = buf.st_size;
void* mapped_local = mmap(NULL, size_local, PROT_READ, MAP_PRIVATE, fd, 0);
if (mapped == MAP_FAILED)
{
close(fd);
return false;
}

*mapped = mapped_local;
*size = size_local;
close(fd);
return true;
}

// Forward declaration
template<size_t LEN>
bool string_ends_with(const string_t& str, const char_t(&suffix)[LEN]);
Expand Down
4 changes: 3 additions & 1 deletion src/coreclr/vm/peimagelayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,9 @@ ConvertedImageLayout::ConvertedImageLayout(FlatImageLayout* source, bool disable
LOG((LF_LOADER, LL_INFO100, "PEImage: Opening manually mapped stream\n"));

#ifdef TARGET_WINDOWS
if (!disableMapping)
// LoadImageByMappingParts assumes the source has a file mapping handle created/managed by the runtime.
// This is not the case for non-file images (for example, external data). Only try to load by mapping for files.
if (!disableMapping && m_pOwner->IsFile())
{
loadedImage = source->LoadImageByMappingParts(this->m_imageParts);
if (loadedImage == NULL)
Expand Down
17 changes: 17 additions & 0 deletions src/tests/Loader/ExternalAssemblyProbe/ExternalAssemblyProbe.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Xunit;

public unsafe class ExternalAssemblyProbe
{
[Fact]
public static void ExternalAppAssemblies()
{
// In order to get to this point, the runtime must have been able to find the app assemblies
// Check that the TPA is indeed empty - that is, the runtime is not relying on that property.
string tpa = AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") as string;
Assert.True(string.IsNullOrEmpty(tpa), "TRUSTED_PLATFORM_ASSEMBLIES should be empty");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Needed for CLRTestEnvironmentVariable -->
<RequiresProcessIsolation>true</RequiresProcessIsolation>
<NativeAotIncompatible>true</NativeAotIncompatible>
<!-- External assembly probe via host-runtime contract is not implemented.
The test uses probing to start at all, so it needs to be disabled in the project, not via an attribute -->
<CLRTestTargetUnsupported Condition="'$(RuntimeFlavor)' != 'coreclr'">true</CLRTestTargetUnsupported>
</PropertyGroup>
<ItemGroup>
<Compile Include="ExternalAssemblyProbe.cs" />

<CLRTestEnvironmentVariable Include="APP_ASSEMBLIES" Value="EXTERNAL" />
</ItemGroup>
</Project>

0 comments on commit 1d44636

Please sign in to comment.