-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
P/Invoke function returning SafeSocketHandle throws "System.MissingMethodException: .ctor" #45633
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsDescriptionOn .NET 5, P/Invoke function returning public static class Program
{
private const int AF_INET = 2;
private const int SOCK_STREAM = 1;
public static void Main()
{
// System.MissingMethodException: '.ctor'
// (This returns -1 because we have not initialized WinSock2; that does not matter, though.)
var sock = socket(AF_INET, SOCK_STREAM, 0);
sock.Dispose();
}
[DllImport("Ws2_32.dll")]
public static extern SafeSocketHandle socket(int af, int type, int protocol);
} Configuration
Regression?Yes. Worked on 3.1.404. Other informationThe SafeHandle doc says "You should also provide a parameterless constructor". A parameterless ctor is defined in the I do not think this is an intended behavior. I do now know the root cause, though. Stack traces:
|
I just wanted to bring this to your attention. Something seems wrong with the released assemblies. This issue itself is not a blocking issue to me because this issue is easy to work around. It is even acceptable to me if this is marked as an unsupported use case (by design). |
Possible linker issue? I'd think the linker would special-case |
This comment has been minimized.
This comment has been minimized.
@marek-safar, so even though the ctor exists: runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.cs Line 41 in dc0e11d
the linker is trimming it out in the assembly build because no P/Invokes are using it in the assembly build? This should be fixed in servicing; I'm not sure how that works. Can we service the linker and then service release/5.0 to use the updated linker? |
As a follow-up, we should diff all (incl. non-public) members of the runtime + BCL's pre-trimmed DLLs and post-trimmed DLLs. Could help identify other places where we've inadvertently made a break. |
Yeah, I assume this code would cover it if the SafeHandle was actually used in a p/invoke: It happens to cover safe handles and things like blittable classes. If the SafeHandle is actually used in a p/invoke, linker will keep it. But it's not used in a p/invoke during the library build. Linker should probably treat constructors of SafeHandles the same as if they were public. |
Yep, this is an actually different issue. We are trimming the constructor as part of libraries build because it's not referenced anywhere and it's private. I don't think there is much to be fixed in the linker. The linker on final apps would work fine if the constructor was actually present in the runtime/SDK assemblies. We should fix libraries build not to trim such constructors. There are multiple ways how to do it, one of them is to change the constructor to be public. |
We should not need to contort the public API surface; whether we want to expose that ctor to be used by code directly is a separate conversation.
I agree with @MichalStrehovsky on this. This isn't specific to this particular SafeHandle. |
I meant to make it public in the implementation library, there are many other options but private does not feel like the right fit for a method which is called externally.
We could add another quirk option for the libraries build only into linker but that sounds like a riskier option for servicing to me.
Correct but wouldn't be better to handle the analysis of correct pinvoke support of SafeHandles via Roslyn Analyzers or even source code generator? |
I'm not following. It's perfectly correct to have a public SafeHandle with a private parameterless ctor and that isn't used by any P/Invokes in that library. And such a SafeHandle, like the one in question here, would validate correctly with an analyzer. |
The current convention for safe handle constructors works poorly for source generated interop as well. The constructor cannot be invoked directly from source, and thus it requires workarounds using private reflection or similar solution. cc @AaronRobinsonMSFT I agree with @marek-safar that the best forward looking option is to mark the safe handle constructors as public. A separate question is whether this is the best option for servicing. It may be - marking a few methods public in the implementation is fairly low-risk. |
Note that this problem is not limited to SafeHandle - interop will also call private parameterless constructors of blittable classes if they're in an We've been using ILLinkTrim/making members public as a solution to private reflection problems. |
If we want to define new guidelines moving forward for new stuff, and retroactively make sure all our SafeHandles conform, that's fine. But it doesn't change the fact that a) the linker is a public tool for use in the ecosystem and should work with standard .NET patterns, and b) private ctors are supported as a standard pattern with SafeHandle. |
Many .NET patterns are fundamentally incompatible with linking. We can always add more special cases to the linker to make some of these patterns work. It makes the complexity to go through the roof. We went this route with .NET Native implementation of the linker. It was not a successful sustainable path. I am just saying that there is a balance to strike. Just because of something is standard .NET pattern does not imply that linker has to support it. |
Is it going to work fine when the SafeHandle is used as argument of a marshaled delegate; or when the interop signature is emitted via Reflection.Emit? |
Nope: dotnet/linker#1660
I was going back and forth whether Ref.Emit should be considered linking safe or not. Maybe this is the case where it isn't. |
That's the pattern linker supports today and works fine but we run linker in a special mode nobody else runs for our own build. We run linker on single library in kind of pre-trim mode to remove any unused methods. We have handled previous cases where unused methods were removed via XML descriptors, new linker options but and we could handle it with yet another special option but I think we have a broader problem here. 1, Are we sure we have the right default constructors on all SafeHandle types? |
This seems equally unsustainable, if "many" common patterns simply fail to work properly. Do most of them produce warnings? This one obviously doesn't. |
They do not produce warnings today and it is something we need to work on. We have a long way to go on linker compatibility. The problematic relationship of interop and linker friendliness is called out as one of the motivations for https://github.com/dotnet/runtime/blob/master/docs/design/features/source-generator-pinvokes.md. I expect that we will eventually recommend interop source generators for linker friendliness of all interop, except the most trivial one. |
For what its worth the Interop team is going to be writing a proposal to handle this for source generators - see #43060 under "New .NET APIs". |
I just want to reiterate Marek's comment: "That's the pattern linker supports today and works fine but we run linker in a special mode nobody else runs for our own build" - if we were trimming an entire app and the SafeHandle is actually used in a p/invoke, linker would keep the ctor - not because linker knows about SafeHandle, but because it just keeps ctors of all parameter types used in |
This alone makes it easy to create a handle instance you can't actually use, as the private ctor was used by the runtime with access to the protected handle field as well. We'd probably also want to add a public SetHandle-like API, while thinking through implications on source compatibility with such a new method on the base type. |
cc @eerhardt Folks that hit this in 5.0 have the workaround of adding a linker.xml file, right? It's just tough for them to realize what the fix/workaround is. |
Yes. This is part of what the interop team will be proposing to help support the |
It was in the quote ".. step for library authors to reduce the size of their NuGet packages."
I think this should be handled on @danmosemsft side for servicing. Maybe @eerhardt can quickly step in but the cut for 5.0.2 is soon.
I agree that XML descriptor for libraries build is the easiest fix with no risks. |
For SafeSocketHandle, no; it's removed as part of our build of the .dll. |
This was one of the COM related issues that also occurred: dotnet/coreclr#28061 |
For the 5.0 fix, I looked through all SafeHandle types in the shared framework with the following code: using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
foreach (var file in Directory.EnumerateFiles(@"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.1\", "*.dll"))
{
Assembly assembly;
try
{
assembly = Assembly.Load(Path.GetFileNameWithoutExtension(file));
}
catch
{
continue;
}
foreach (Type t in assembly.GetTypes())
{
if (t.IsAssignableTo(typeof(SafeHandle)) && t.IsPublic && !t.IsAbstract)
{
if (t.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null) == null)
{
Console.WriteLine(t.FullName);
}
}
}
} That spit out 2 types that would have this issue:
So to fix this in 5.0 servicing, my plan is to introduce 2 "Library_Build" XML descriptors for the 2 SafeHandles above. That will ensure their private constructors are preserved. Going forward, I agree that we need a better long term strategy here - manually inspecting/verifying that every SafeHandle we create has the right constructors after building isn't a sustainable solution. Looking through the rest of the SafeHandle types that aren't public, one jumps out that may be in trouble: runtime/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ISymWrapperCore.cs Lines 368 to 374 in 53c1619
This SafeHandle is used as an |
Some more information on the above research:
The first 2 are Windows-specific, so there is no need to change them, for the same reason as The 2 MemoryMapped SafeHandles can be used cross-platform. We have never shipped a non-Windows version of The only remaining SafeHandle is |
…ption The ILLinker is trimming the private default constructors of SafeHandle classes if they are not used by the library. Adding an XML descriptor file to preserve the private constructors of these SafeHandles so they can be used in P/Invokes in external code. Fix dotnet#45633
…ption (#45911) * P/Invoke function returning SafeSocketHandle throws MissingMethodException The ILLinker is trimming the private default constructors of SafeHandle classes if they are not used by the library. Adding an XML descriptor file to preserve the private constructors of these SafeHandles so they can be used in P/Invokes in external code. Fix #45633 Suppress P/Invoke test on Browser
…ption (dotnet#45911) * P/Invoke function returning SafeSocketHandle throws MissingMethodException The ILLinker is trimming the private default constructors of SafeHandle classes if they are not used by the library. Adding an XML descriptor file to preserve the private constructors of these SafeHandles so they can be used in P/Invokes in external code. Fix dotnet#45633 Suppress P/Invoke test on Browser
Reopening for backport to 5.0. |
…singMethodException (#45911) (#45942) * P/Invoke function returning SafeSocketHandle throws MissingMethodException (#45911) The ILLinker is trimming the private default constructors of SafeHandle classes if they are not used by the library. Adding an XML descriptor file to preserve the private constructors of these SafeHandles so they can be used in P/Invokes in external code. Fix #45633 Suppress P/Invoke test on Browser cherry-pick of af1b1ad * Move ILLinkTrim files to the .csproj folder, where the 5.0 build infrastructure expects them.
@eerhardt When can we expect releasing .NET 5 update with the fix ? |
This fix is in the release branch, so the next servicing release of .NET 5 will have the fix. According to https://docs.microsoft.com/en-us/dotnet/core/releases-and-support, |
5.0.2 should go out next week, you can test the fix by installing https://github.com/dotnet/installer#installers-and-binaries 5.0.2 preview version |
Description
On .NET 5, P/Invoke function returning
SafeSocketHandle
throws "System.MissingMethodException: .ctor". See MissingSafeSocketHandleCtor.zip for a full repro project.Configuration
Regression?
Yes. Worked on 3.1.404.
Other information
The SafeHandle doc says "You should also provide a parameterless constructor".
A parameterless ctor is defined in the
SafeSocketHandle
source file but missing from the released assembly at "C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0\System.Net.Sockets.dll".I do not think this is an intended behavior. I do now know the root cause, though.
Stack traces:
The text was updated successfully, but these errors were encountered: