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 15 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: 1 addition & 4 deletions src/coreclr/hosts/corerun/corerun.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -555,16 +555,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
15 changes: 12 additions & 3 deletions src/coreclr/hosts/corerun/corerun.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ namespace pal
#ifdef TARGET_WINDOWS
#include <Windows.h>

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

Expand Down Expand Up @@ -307,17 +308,25 @@ class platform_specific_actions final
#include <config.h>
#include <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 Expand Up @@ -573,7 +582,7 @@ namespace pal
pal::ensure_trailing_delimiter(coreclr_path);
coreclr_path.append(pal::coreclr_lib);
coreclr_path.append(pal::nativelib_ext);

hMod = (pal::mod_t)dlopen(coreclr_path.c_str(), RTLD_NOW | RTLD_LOCAL);
if (hMod == nullptr)
{
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 @@ INT_PTR QCALLTYPE NativeLibraryNative::GetSymbol(INT_PTR handle, LPCWSTR symbolN

return address;
}

1 change: 0 additions & 1 deletion src/coreclr/vm/nativelibrarynative.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ class NativeLibraryNative
BOOL throwOnError);
static void QCALLTYPE FreeLib(INT_PTR handle);
static INT_PTR QCALLTYPE GetSymbol(INT_PTR handle, LPCWSTR symbolName, BOOL throwOnError);

};

#endif // __NATIVELIBRARYNATIVE_H__
15 changes: 15 additions & 0 deletions src/libraries/Common/src/Interop/Unix/System.Native/Interop.Dl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

internal static partial class Interop
{
internal static partial class Sys
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDefaultSearchOrderPseudoHandle", SetLastError = true)]
internal static extern IntPtr GetDefaultSearchOrderPseudoHandle();
}
}
1 change: 1 addition & 0 deletions src/libraries/Native/Unix/System.Native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ endif ()
include_directories("${CLR_SRC_NATIVE_DIR}/common")

set(NATIVE_SOURCES
pal_dl.c
pal_errno.c
pal_interfaceaddresses.c
pal_io.c
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/Native/Unix/System.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "pal_autoreleasepool.h"
#include "pal_console.h"
#include "pal_datetime.h"
#include "pal_dl.h"
#include "pal_errno.h"
#include "pal_interfaceaddresses.h"
#include "pal_io.h"
Expand Down Expand Up @@ -257,6 +258,7 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_SetPosixSignalHandler)
DllImportEntry(SystemNative_GetPlatformSignalNumber)
DllImportEntry(SystemNative_GetGroups)
DllImportEntry(SystemNative_GetDefaultSearchOrderPseudoHandle)
};

EXTERN_C const void* SystemResolveDllImport(const char* name);
Expand Down
23 changes: 23 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_dl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "pal_dl.h"
#include "dlfcn.h"
#include <stdlib.h>

#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)
{
g_defaultSearchOrderPseudoHandle = dlopen(NULL, RTLD_LAZY);
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
}
return g_defaultSearchOrderPseudoHandle;
}
#endif
8 changes: 8 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_dl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma once

#include "pal_compiler.h"

PALEXPORT void* SystemNative_GetDefaultSearchOrderPseudoHandle(void);
Original file line number Diff line number Diff line change
Expand Up @@ -1792,6 +1792,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 @@ -1934,6 +1937,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.Dl.cs">
<Link>Common\Interop\Unix\System.Native\Interop.Dl.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 Expand Up @@ -2326,4 +2332,4 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IUnaryNegationOperators.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IUnaryPlusOperators.cs" />
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -256,5 +256,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 GetEntryPointModuleHandle()
{
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)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
Copy link
Member

Choose a reason for hiding this comment

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

This still does not look right for non-Windows. This assumes that the Unix native PInvoke errors codes are the same as Win32 error codes which they certainly are not.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do we have a generalized mechanism today for converting a Unix errno value to an exception or a pattern I should follow for converting the error code to an exception on non-Windows platforms?

Copy link
Member

Choose a reason for hiding this comment

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

Do we have a generalized mechanism today for converting a Unix errno value to an exception

We do not. (The closest one is GetExceptionForIoErrno, but it is very IO scenario focused and it may not produce the right result here.)

I think we typically use throw new Win32Exception() in situation like this one that are expected to never throw in practice. Despite its name, Win32Exception on non-Windows treats the error code as the platform specific error code (not as Windows error code).

Copy link
Member Author

Choose a reason for hiding this comment

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

Currently Win32Exception lives in Microsoft.Win32.Primitives (it seems to be the only type left in that assembly). Do we want to move it down into CoreLib as part of this change, or should I do that as part of a separate PR?

I definitely think it's the right exception for this scenario. It looks to have support for pulling out the exception message using the correct platform-native API for Windows and Unix platforms, which is quite nice.

Copy link
Member

Choose a reason for hiding this comment

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

Do we want to move it down into CoreLib as part of this change, or should I do that as part of a separate PR?

I think it would be nice to do it in a separate PR for clarity.

}
return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,7 @@ public MarshalDirectiveException(string? message, System.Exception? inner) { }
public static partial class NativeLibrary
{
public static void Free(System.IntPtr handle) { }
public static System.IntPtr GetEntryPointModuleHandle() { 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
25 changes: 23 additions & 2 deletions src/mono/mono/metadata/native-library-qcall.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,40 @@ enum {
func_flag_end_of_array = 0x01,
func_flag_has_signature = 0x02,
func_flag_unreferenced = 0x04, // Suppress unused fcall check
func_flag_qcall = 0x08, // QCall - mscorlib.dll to mscorwks.dll transition implemented as PInvoke
func_flag_qcall = 0x08, // QCall - System.Private.CoreLib.dll to native runtime transition implemented as PInvoke
};

#ifndef DISABLE_QCALLS
#define FCFuncStart(name) static const MonoQCallFunc name[] = {
#define FCFuncEnd() { func_flag_end_of_array, NULL, NULL } };
#define QCFuncElement(name,impl) { func_flag_qcall, (void*)(impl), name },
#define FCClassElement(name, namespace, funcs)
#include "mono/metadata/qcall-def.h"
#undef FCClassElement
#undef QCFuncElement
#undef FCFuncEnd
#undef FCFuncStart
#endif

static const MonoQCallDef c_qcalls[] =
{
#ifndef DISABLE_QCALLS
#define FCFuncStart(name)
#define FCFuncEnd()
#define QCFuncElement(name,impl)
#define FCClassElement(name,namespace,funcs) {name, namespace, funcs},
#include "mono/metadata/qcall-def.h"
#undef FCClassElement
#undef QCFuncElement
#undef FCFuncEnd
#undef FCFuncStart
#endif
{ NULL, NULL, NULL }
};

const int c_nECClasses = sizeof (c_qcalls) / sizeof (c_qcalls[0]);
// Number of actual entries in c_qcalls.
// Subtract one to ignore the all-NULL entry at the end.
const int c_nECClasses = (sizeof (c_qcalls) / sizeof (c_qcalls[0])) - 1;

static gboolean is_end_of_array (MonoQCallFunc *func) { return !!((int)func->flags & func_flag_end_of_array); }
static gboolean has_signature (MonoQCallFunc *func) { return !!((int)func->flags & func_flag_has_signature); }
Expand Down
28 changes: 16 additions & 12 deletions src/mono/mono/metadata/native-library.c
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,21 @@ 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)
{
Expand All @@ -770,18 +785,7 @@ netcore_lookup_native_library (MonoAssemblyLoadContext *alc, MonoImage *image, c

// 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
13 changes: 9 additions & 4 deletions src/mono/mono/metadata/qcall-def.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
* where symbol_name is an array of MonoQCallFunc.
*
* FCClassElements have to be sorted by name then namespace,
* but that the functions in each one can be in any order, but
* have to end with a func_flag_end_of_array (0x01) entry.
* but the functions in each one can be in any order,
* specified by the QCFuncElement macro as follows:
* FCFuncStart(class_symbol_name)
* QCFuncElement(method_name, symbol_name)
* FCFuncEnd()
* where class_symbol_name is the symbol for the corresponding
* class specified in FCClassElement, method_name is the name of the
* method on the managed side, and symbol_name is the name
* of the implementing method on the native side.
**/

FCClassElement("", "", NULL)
41 changes: 41 additions & 0 deletions src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs
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;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -179,6 +180,27 @@ public static int Main()
success &= EXPECT(TryGetLibraryExport(handle, "NonNativeSum"), TestResult.ReturnFailure);

NativeLibrary.Free(handle);

// -----------------------------------------------
// GetEntryPointModuleHandle Tests
// -----------------------------------------------

// Mobile test runs aren't hosted by corerun, so we don't have a well-known export to test here
if (IsHostedByCoreRun())
{
// Export from CoreRun host
success &= EXPECT(GetSymbolFromEntryPointModuleHandle("GetCurrentClrDetails"));
success &= EXPECT(GetSymbolFromEntryPointModuleHandle("NonExistentCoreRunExport"), TestResult.ReturnFailure);
}

handle = NativeLibrary.Load(libName);

// On non-Windows, the returned handle can load any global symbol,
// so try loading a symbol from the native test library.
success &= EXPECT(GetSymbolFromEntryPointModuleHandle(TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum"), OperatingSystem.IsWindows() ? TestResult.ReturnFailure : TestResult.Success);
success &= EXPECT(GetSymbolFromEntryPointModuleHandle("NonNativeSum"), TestResult.ReturnFailure);

NativeLibrary.Free(handle);
}
catch (Exception e)
{
Expand All @@ -192,6 +214,11 @@ public static int Main()
return (success) ? 100 : -100;
}

static bool IsHostedByCoreRun()
{
return Process.GetCurrentProcess().MainModule.ModuleName is "corerun" or "corerun.exe";
}

static bool EXPECT(TestResult actualValue, TestResult expectedValue = TestResult.Success)
{
if (actualValue == expectedValue)
Expand Down Expand Up @@ -365,6 +392,20 @@ static TestResult TryGetLibraryExport(IntPtr handle, string name)
});
}

static TestResult GetSymbolFromEntryPointModuleHandle(string symbolToLoadFromHandle)
{
CurrentTest = nameof(GetSymbolFromEntryPointModuleHandle);
return Run(() => {
IntPtr moduleHandle = NativeLibrary.GetEntryPointModuleHandle();
bool success = NativeLibrary.TryGetExport(moduleHandle, symbolToLoadFromHandle, out IntPtr address);
if (!success)
return TestResult.ReturnFailure;
if (address == IntPtr.Zero)
return TestResult.ReturnNull;
return TestResult.Success;
});
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
}

[DllImport(NativeLibraryToLoad.Name)]
static extern int RunExportedFunction(IntPtr address, int arg1, int arg2);
}