-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Add NativeLibrary class #16409
Add NativeLibrary class #16409
Changes from 5 commits
7ece113
1870ec8
09d1d06
93debde
f95e29b
4c56c0c
67900c5
096cab3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
namespace System.Runtime.InteropServices | ||
{ | ||
// Contains UNIX-specific logic for NativeLibrary class. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here, "UNIX" also includes OSX. |
||
public unsafe sealed partial class NativeLibrary | ||
{ | ||
// The allowed mask values for the DllImportSearchPath. | ||
// Non-Windows sytems only allow AssemblyDirectory and LegacyBehavior. | ||
private const uint AllowedDllImportSearchPathsMask = (uint)(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.LegacyBehavior); | ||
|
||
// [DllImport] (NDirectMethodDesc::FindEntryPoint) doesn't allow lookup by ordinal on non-Windows. | ||
private const bool AllowLocatingFunctionsByOrdinal = false; | ||
|
||
// In UNIX, the CLR's HINSTANCE is actually a pointer to _MODSTRUCT. | ||
// We can extract the underlying OS handle from that structure. | ||
private IntPtr OperatingSystemHandle => ((_MODSTRUCT*)_hInstance)->dl_handle; | ||
|
||
private static bool IsValidModuleHandle(IntPtr hModule) | ||
{ | ||
// No validation other than checking for null. | ||
|
||
return (hModule != IntPtr.Zero); | ||
} | ||
|
||
// This is a *partial* copy of include/pal/module.h so that we can extract the dl_handle. | ||
[StructLayout(LayoutKind.Sequential)] | ||
private struct _MODSTRUCT | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not really a fan of this pattern, but this seemed safer than trying to have NativeLibrary.cpp reach deep into the PAL-specific header files. Thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could expose it as a PAL function, e.g. It actually seems that moving the IsValidModuleHandle to the native part of the implementation and exposing it as a QCALL would be better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There is a lot of chattiness between the managed and native parts - there are 5 new FCalls/QCalls already. I think it would be better to have bulk of this implementation in C++, and not have this chattiness. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree.. I would either all(or mostly) implements in native code or all implements in managed code. but not in middle. It will help interop team maintain these codes in long term.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we were developing this I chatted with quite a few people who have a vested interest in this type's implementation, and the recommendations eventually came down to: (a) reuse as much code as practical; and (b) if new code must be created keep as much of it managed as possible, even at the expense of performance. This would make the code easier to understand and would benefit maintenance in the long run. In practice the only real implementation logic that exists in C# is a managed equivalent to the C++ If you feel strongly about moving the managed |
||
{ | ||
internal IntPtr self; | ||
internal IntPtr dl_handle; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
namespace System.Runtime.InteropServices | ||
{ | ||
// Contains Windows-specific logic for NativeLibrary class. | ||
public sealed partial class NativeLibrary | ||
{ | ||
// The allowed mask values for the DllImportSearchPath. | ||
// The high three bytes are passed as-is to the OS (which will check them for validity), | ||
// and the low byte is allowed to contain AssemblyDirectory or LegacyBehavior. | ||
private const uint AllowedDllImportSearchPathsMask = ~0xFFU | (uint)(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.LegacyBehavior); | ||
|
||
// [DllImport] (NDirectMethodDesc::FindEntryPoint) allows lookup by ordinal on Windows. | ||
private const bool AllowLocatingFunctionsByOrdinal = true; | ||
|
||
// from libloaderapi.h | ||
private const uint GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 0x00000004; | ||
private const uint GET_MODULE_HANDLE_EX_FLAG_PIN = 0x00000001; | ||
|
||
// On Windows, the CLR's HINSTANCE is the underlying OS handle. | ||
private IntPtr OperatingSystemHandle => _hInstance; | ||
|
||
private static bool IsValidModuleHandle(IntPtr hModule) | ||
{ | ||
// This method has two purposes: (a) it ensures that the provided module handle is indeed valid, | ||
// and (b) it pins the module so that it can never be unloaded from the current process. The CLR | ||
// expects modules to be pinned (see the call to BaseHolder<...>::Extract in NDirect::LoadLibraryModule), | ||
// and we should enforce this invariant regardless of which code path ended up loading the module. | ||
|
||
if (hModule == IntPtr.Zero) | ||
{ | ||
return false; | ||
} | ||
|
||
if (!GetModuleHandleEx( | ||
dwFlags: GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, | ||
lpModuleName: hModule, // module base address, not module name | ||
phModule: out IntPtr baseAddress)) | ||
{ | ||
return false; | ||
} | ||
|
||
if (hModule != baseAddress) | ||
{ | ||
return false; | ||
} | ||
|
||
// all checks succeeded | ||
return true; | ||
} | ||
|
||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683200(v=vs.85).aspx | ||
[DllImport(Interop.Libraries.Kernel32, EntryPoint = "GetModuleHandleExW", CharSet = CharSet.Unicode, SetLastError = true)] | ||
[return: MarshalAs(UnmanagedType.Bool)] | ||
internal static extern bool GetModuleHandleEx( | ||
[In] uint dwFlags, | ||
[In] IntPtr lpModuleName, | ||
[Out] out IntPtr phModule); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is based somewhat on the full framework public API (https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.numparambytes(v=vs.110).aspx).