Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement NativeLibrary.GetEntryPointModuleHandle #57610

Merged
merged 42 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e4e799c
Bring in some more QCall infrastructure that matches the CoreCLR desi…
jkoritzinsky Aug 10, 2021
5f16ec8
Expose method from CoreRun for use in testing for desktop scenarios.
jkoritzinsky Aug 10, 2021
e0542ee
Refactor out the implementation of the lookup for "__Internal" so it …
jkoritzinsky Aug 10, 2021
57643bc
First pass implementing API on corelcr and mono.
jkoritzinsky Aug 10, 2021
4843165
Fix build and test failures.
jkoritzinsky Aug 17, 2021
376cd45
Fix typos and do basic cleanup.
jkoritzinsky Aug 18, 2021
d19ed4a
Enhance testing.
jkoritzinsky Aug 18, 2021
d50f07f
Update comment for mono QCalls so the comment explains how to define …
jkoritzinsky Aug 18, 2021
d338682
Fix corerun build on non-Windows.
jkoritzinsky Aug 18, 2021
3710af5
Move API implementation to libraries.
jkoritzinsky Aug 18, 2021
93fc783
Make netcore_lookup_self_native_handle static so it doesn't require a…
jkoritzinsky Aug 18, 2021
58554e0
Compile pal_dl.c in the System.Native shim
jkoritzinsky Aug 18, 2021
952903e
Add `void` to make the C compiler happy.
jkoritzinsky Aug 18, 2021
9087136
Make the cached local static
jkoritzinsky Aug 18, 2021
071b119
Add to entrypoints.c
jkoritzinsky Aug 18, 2021
af32231
Remove unused variable.
jkoritzinsky Aug 19, 2021
d71bc7b
Set the right flags to enable loading symbols from corerun with the n…
jkoritzinsky Aug 19, 2021
4db27cb
Add tests that actually pass for the global symbol lookup.
jkoritzinsky Aug 19, 2021
5c61dfb
Merge branch 'nativelibrary_entrymodule' of github.com:jkoritzinsky/r…
jkoritzinsky Aug 19, 2021
9c41825
Rename API based on offline conversation.
jkoritzinsky Aug 23, 2021
5f36838
Add some scenario names for the API testing to help make debugging is…
jkoritzinsky Aug 23, 2021
e9b70ae
Use Win32Exception for the native error code.
jkoritzinsky Aug 24, 2021
4d58716
Merge branch 'nativelibrary_entrymodule' of github.com:jkoritzinsky/r…
jkoritzinsky Sep 1, 2021
31fe53b
Merge branch 'main' into nativelibrary_entrymodule
jkoritzinsky Sep 2, 2021
19bd0a9
Fix Windows test build.
jkoritzinsky Sep 2, 2021
b31fd00
Merge branch 'main' of github.com:dotnet/runtime into nativelibrary_e…
jkoritzinsky Jan 20, 2022
d191560
Merge branch 'main' of github.com:dotnet/runtime into nativelibrary_e…
jkoritzinsky Feb 11, 2022
745c9bc
Rewrite NativeLibrary tests to use the new xunit generator model so w…
jkoritzinsky Feb 11, 2022
efc285f
Fix typo
jkoritzinsky Feb 12, 2022
3f020e8
Clean up some code and move the new P/Invoke over to be generated.
jkoritzinsky Mar 15, 2022
c9a9dba
Merge branch 'main' of github.com:jkoritzinsky/runtime into nativelib…
jkoritzinsky Mar 15, 2022
7ffac68
Rename attribute
jkoritzinsky Mar 15, 2022
a175e5c
Fix duplicate source file include
jkoritzinsky Mar 15, 2022
81f9daa
Fix allocation in NativeLibrary.Free to handle the raw lib handle cas…
jkoritzinsky Mar 16, 2022
a6117ba
Fix Apple test builds
jkoritzinsky Mar 16, 2022
2d4959a
Merge branch 'nativelibrary_entrymodule' of github.com:jkoritzinsky/r…
jkoritzinsky Mar 16, 2022
f17ec5a
Fix typo in ConditionalFact condition
jkoritzinsky Mar 16, 2022
b8aa4fe
Don't P/Invoke into the same NativeLibrary that we're testing our dou…
jkoritzinsky Mar 16, 2022
4e7a69c
Fix GetCurrentClrDetails exporting on win x86
jkoritzinsky Mar 17, 2022
c58228b
Use delegate interop to avoid hitting issues with function pointers a…
jkoritzinsky Mar 17, 2022
6bc7f4c
Clean up CMakeLists.txt for NativeLibrary tests
jkoritzinsky Mar 18, 2022
4266aab
Add a test for freeing the result of GetMainProgramHandle
jkoritzinsky Mar 18, 2022
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
5 changes: 5 additions & 0 deletions src/coreclr/hosts/corerun/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ else(CLR_CMAKE_HOST_WIN32)
include(configure.cmake)
endif(CLR_CMAKE_HOST_WIN32)

#Required to expose symbols for global symbol discovery.
set(CLR_CMAKE_KEEP_NATIVE_SYMBOLS TRUE)

add_executable_clr(corerun
corerun.cpp
dotenv.cpp
Expand All @@ -25,6 +28,8 @@ if(CLR_CMAKE_HOST_WIN32)
)
else(CLR_CMAKE_HOST_WIN32)
target_link_libraries(corerun ${CMAKE_DL_LIBS})
# Required to expose symbols for global symbol discovery
target_link_libraries(corerun -rdynamic)

# Android implements pthread natively
if(NOT CLR_CMAKE_TARGET_ANDROID)
Expand Down
5 changes: 1 addition & 4 deletions src/coreclr/hosts/corerun/corerun.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,16 +564,13 @@ int MAIN(const int argc, const char_t* argv[])
return exit_code;
}

#ifdef TARGET_WINDOWS
// Used by CoreShim to determine running CoreCLR details.
extern "C" __declspec(dllexport) HRESULT __cdecl GetCurrentClrDetails(void** clrInstance, unsigned int* appDomainId)
extern "C" DLL_EXPORT HRESULT CDECL GetCurrentClrDetails(void** clrInstance, unsigned int* appDomainId)
{
assert(clrInstance != nullptr && appDomainId != nullptr);
*clrInstance = CurrentClrInstance;
*appDomainId = CurrentAppDomainId;
return S_OK;
}
#endif // TARGET_WINDOWS

//
// Self testing for corerun.
Expand Down
14 changes: 12 additions & 2 deletions src/coreclr/hosts/corerun/corerun.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ namespace pal
}

#ifdef TARGET_WINDOWS
#define CDECL __cdecl
#include <Windows.h>

#define DLL_EXPORT __declspec(dllexport)
#define MAIN __cdecl wmain
#define W(str) L ## str

Expand Down Expand Up @@ -316,17 +318,25 @@ class platform_specific_actions final
#include <config.h>
#include <minipal/getexepath.h>

#if __GNUC__ >= 4
#define DLL_EXPORT __attribute__ ((visibility ("default")))
#else
#define DLL_EXPORT
#endif
#define CDECL
#define MAIN main
#define W(str) str
#define FAILED(result) (result < 0)

#define S_OK 0
#define FAILED(result) (result < S_OK)
#if !HAVE_DIRENT_D_TYPE
#define DT_UNKNOWN 0
#define DT_DIR 4
#define DT_REG 8
#define DT_LNK 10
#endif

typedef int HRESULT;

namespace pal
{
using char_t = char;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,6 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.SchedGetCpu.cs">
<Link>Interop\Unix\System.Native\Interop.SchedGetCpu.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.DynamicLoad.cs">
<Link>Interop\Unix\System.Native\Interop.DynamicLoad.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Threading.cs">
<Link>Interop\Unix\System.Native\Interop.Threading.cs</Link>
</Compile>
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/vm/nativelibrarynative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,3 @@ extern "C" INT_PTR QCALLTYPE NativeLibrary_GetSymbol(INT_PTR handle, LPCWSTR sym

return address;
}

Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@ internal unsafe partial class Sys

[LibraryImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_FreeLibrary")]
internal static partial void FreeLibrary(IntPtr handle);

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDefaultSearchOrderPseudoHandle", SetLastError = true)]
internal static partial IntPtr GetDefaultSearchOrderPseudoHandle();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1831,6 +1831,9 @@
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.USEROBJECTFLAGS.cs">
<Link>Common\Interop\Windows\User32\Interop.USEROBJECTFLAGS.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GetModuleHandle.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.GetModuleHandle.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\IO\FileSystem.Attributes.Windows.cs">
<Link>Common\System\IO\FileSystem.Attributes.Windows.cs</Link>
</Compile>
Expand Down Expand Up @@ -1973,6 +1976,9 @@
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.CopyFile.cs">
<Link>Common\Interop\Unix\System.Native\Interop.CopyFile.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.DynamicLoad.cs">
<Link>Common\Interop\Unix\System.Native\Interop.DynamicLoad.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.ErrNo.cs">
<Link>Common\Interop\Unix\System.Native\Interop.ErrNo.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
Expand Down Expand Up @@ -231,5 +232,26 @@ internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly asse

return resolver(libraryName, assembly, hasDllImportSearchPathFlags ? (DllImportSearchPath?)dllImportSearchPathFlags : null);
}

/// <summary>
/// Get a handle that can be used with <see cref="GetExport" /> or <see cref="TryGetExport" /> to resolve exports from the entry point module.
/// </summary>
/// <returns> The handle that can be used to resolve exports from the entry point module.</returns>
public static IntPtr GetMainProgramHandle()
{
IntPtr result = IntPtr.Zero;
#if TARGET_WINDOWS
result = Interop.Kernel32.GetModuleHandle(null);
#else
result = Interop.Sys.GetDefaultSearchOrderPseudoHandle();
#endif
// I don't know when a failure case can occur here, but checking for it and throwing an exception
// if we encounter it.
if (result == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastPInvokeError());
}
return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,7 @@ public MarshalDirectiveException(string? message, System.Exception? inner) { }
public static partial class NativeLibrary
{
public static void Free(System.IntPtr handle) { }
public static System.IntPtr GetMainProgramHandle() { throw null; }
public static System.IntPtr GetExport(System.IntPtr handle, string name) { throw null; }
public static System.IntPtr Load(string libraryPath) { throw null; }
public static System.IntPtr Load(string libraryName, System.Reflection.Assembly assembly, System.Runtime.InteropServices.DllImportSearchPath? searchPath) { throw null; }
Expand Down
35 changes: 19 additions & 16 deletions src/mono/mono/metadata/native-library.c
Original file line number Diff line number Diff line change
Expand Up @@ -757,32 +757,35 @@ netcore_check_alc_cache (MonoAssemblyLoadContext *alc, const char *scope)
return result;
}

static MonoDl*
netcore_lookup_self_native_handle()
{
char *error_msg = NULL;
if (!internal_module)
internal_module = mono_dl_open_self (&error_msg);

if (!internal_module) {
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '__Internal': '%s'.", error_msg);
g_free (error_msg);
}
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found via __Internal.");
return internal_module;
}

static MonoDl *
netcore_lookup_native_library (MonoAssemblyLoadContext *alc, MonoImage *image, const char *scope, guint32 flags)
{
MonoDl *module = NULL;
MonoDl *cached;
MonoAssembly *assembly = mono_image_get_assembly (image);
char *error_msg = NULL;

MONO_REQ_GC_UNSAFE_MODE;

mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "DllImport attempting to load: '%s'.", scope);

// We allow a special name to dlopen from the running process namespace, which is not present in CoreCLR
if (strcmp (scope, "__Internal") == 0) {
if (!internal_module)
internal_module = mono_dl_open_self (&error_msg);
module = internal_module;

if (!module) {
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '__Internal': '%s'.", error_msg);
g_free (error_msg);
}

mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found via __Internal: '%s'.", scope);

return module;
return netcore_lookup_self_native_handle();
}

/*
Expand Down Expand Up @@ -1219,9 +1222,9 @@ ves_icall_System_Runtime_InteropServices_NativeLibrary_FreeLib (gpointer lib, Mo
g_hash_table_add (native_library_module_blocklist, module);
mono_dl_close (module);
} else {
MonoDl raw_module = { { 0 } };
raw_module.handle = lib;
mono_dl_close (&raw_module);
MonoDl* raw_module = g_new0(MonoDl, 1);
raw_module->handle = lib;
mono_dl_close (raw_module);
}

leave:
Expand Down
1 change: 1 addition & 0 deletions src/native/libs/System.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_LoadLibrary)
DllImportEntry(SystemNative_GetProcAddress)
DllImportEntry(SystemNative_FreeLibrary)
DllImportEntry(SystemNative_GetDefaultSearchOrderPseudoHandle)
DllImportEntry(SystemNative_SchedGetCpu)
DllImportEntry(SystemNative_Exit)
DllImportEntry(SystemNative_Abort)
Expand Down
17 changes: 17 additions & 0 deletions src/native/libs/System.Native/pal_dynamicload.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,20 @@ void SystemNative_FreeLibrary(void* handle)
{
dlclose(handle);
}

#ifdef TARGET_ANDROID
void* SystemNative_GetDefaultSearchOrderPseudoHandle(void)
{
return (void*)RTLD_DEFAULT;
}
#else
static void* g_defaultSearchOrderPseudoHandle = NULL;
void* SystemNative_GetDefaultSearchOrderPseudoHandle(void)
{
if (g_defaultSearchOrderPseudoHandle == NULL)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has potential race condition depending on how the C++ decides to compile it. The C/C++ compiler is free to reorder the if check and reading of g_defaultSearchOrderPseudoHandle to return. It means that the method can return NULL instead of the actual value.

This is real problem. We had many bugs like that over the years - they are always very hard to trace down.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be fixed by making g_defaultSearchOrderPseudoHandle volatile, correct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. Also, you may want to cache the value in a local so that it does not need to be fetched from memory twice.

{
g_defaultSearchOrderPseudoHandle = dlopen(NULL, RTLD_LAZY);
}
return g_defaultSearchOrderPseudoHandle;
}
#endif
2 changes: 2 additions & 0 deletions src/native/libs/System.Native/pal_dynamicload.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ PALEXPORT void* SystemNative_LoadLibrary(const char* filename);
PALEXPORT void* SystemNative_GetProcAddress(void* handle, const char* symbol);

PALEXPORT void SystemNative_FreeLibrary(void* handle);

PALEXPORT void* SystemNative_GetDefaultSearchOrderPseudoHandle(void);
1 change: 1 addition & 0 deletions src/tests/Common/CoreCLRTestLibrary/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public static bool Verbose
}

public static bool IsX86 => (RuntimeInformation.ProcessArchitecture == Architecture.X86);
public static bool IsNotX86 => !IsX86;
public static bool IsX64 => (RuntimeInformation.ProcessArchitecture == Architecture.X64);
public static bool IsArm => (RuntimeInformation.ProcessArchitecture == Architecture.Arm);
public static bool IsArm64 => (RuntimeInformation.ProcessArchitecture == Architecture.Arm64);
Expand Down
96 changes: 96 additions & 0 deletions src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// 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 System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Xunit;
using static TestHelpers;

class GetLibraryExportTests : IDisposable
{
private readonly IntPtr handle;

public GetLibraryExportTests()
{
handle = NativeLibrary.Load(NativeLibraryToLoad.GetFullPath());
}

[ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsX86))]
public void GetValidExport_ManualMangling()
{
EXPECT(GetLibraryExport(handle, "_NativeSum@8"));
EXPECT(TryGetLibraryExport(handle, "_NativeSum@8"));
}

[ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNotX86))]
public void GetValidExport()
{
EXPECT(GetLibraryExport(handle, "NativeSum"));
EXPECT(TryGetLibraryExport(handle, "NativeSum"));
}

[Fact]
public void NullHandle()
{
EXPECT(GetLibraryExport(IntPtr.Zero, "NativeSum"), TestResult.ArgumentNull);
EXPECT(TryGetLibraryExport(IntPtr.Zero, "NativeSum"), TestResult.ArgumentNull);
}

[Fact]
public void NullExport()
{
EXPECT(GetLibraryExport(handle, null), TestResult.ArgumentNull);
EXPECT(TryGetLibraryExport(handle, null), TestResult.ArgumentNull);
}

[Fact]
public void ExportDoesNotExist()
{
EXPECT(GetLibraryExport(handle, "NonNativeSum"), TestResult.EntryPointNotFound);
EXPECT(TryGetLibraryExport(handle, "NonNativeSum"), TestResult.ReturnFailure);
}


public void Dispose() => NativeLibrary.Free(handle);

static TestResult GetLibraryExport(IntPtr handle, string name)
{
return Run(() => {
IntPtr address = NativeLibrary.GetExport(handle, name);
if (address == IntPtr.Zero)
return TestResult.ReturnNull;
if (RunExportedFunction(address, 1, 1) != 2)
return TestResult.IncorrectEvaluation;
return TestResult.Success;
});
}

static TestResult TryGetLibraryExport(IntPtr handle, string name)
{
return Run(() => {
IntPtr address = IntPtr.Zero;
bool success = NativeLibrary.TryGetExport(handle, name, out address);
if (!success)
return TestResult.ReturnFailure;
if (address == IntPtr.Zero)
return TestResult.ReturnNull;
if (RunExportedFunction(address, 1, 1) != 2)
return TestResult.IncorrectEvaluation;
return TestResult.Success;
});
}

private static unsafe int RunExportedFunction(IntPtr address, int arg1, int arg2)
{
// We use a delegate here instead of a function pointer to avoid hitting issues
// where Mono AOT doesn't generate the managed->native wrapper and then fails
// when in AOT-only mode.
NativeFunctionWrapper wrapper = Marshal.GetDelegateForFunctionPointer<NativeFunctionWrapper>(address);
return wrapper(arg1, arg2);
}

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate int NativeFunctionWrapper(int arg1, int arg2);
}
Loading