dotnet / ILMerge

ILMerge is a static linker for .NET Assemblies.
MIT License
1.23k stars 170 forks source link

Signing with /keyfile throws an error #41

Closed bladeSLICE closed 6 years ago

bladeSLICE commented 6 years ago

In my experience, signing anything with /keyfile but not specifying /delaysign results in seeing an error message:

ILMerge error: The target assembly was not able to be strongly named (did you forget to use the /delaysign option?)."),"There was an error in the log output from signing the assembly with a keyfile

Based on how I've used early versions of ILMerge this seems like a bug to me. When I drill down into the log file I can see another error:

The system cannot find the file specified. (Exception from HRESULT: 0x80070002)

I've seen this on a few different machines running Windows 7 using different combinations of dll and snk files. I've also been able to reproduce this issue with a unit test that can be found here.

jnm2 commented 6 years ago

Nice test! Can you debug the test and have it stop on the exception and see if there's an obvious reason why it's failing to find a file?

bladeSLICE commented 6 years ago

So The error is being thrown inside ClrStrongName.
Below you can find the true exception before it gets swallowed.

The system cannot find the file specified. (Exception from HRESULT: 0x80070002)

at System.Compiler.ClrStrongName.IClrStrongName.StrongNameSignatureGeneration(String pwzFilePath, String pwzKeyContainer, Byte[] pbKeyBlob, Int32 cbKeyBlob, IntPtr ppbSignatureBlob, IntPtr pcbSignatureBlob) at System.Compiler.ClrStrongName.SignatureGeneration(String filePath, String keyContainer, Byte[] keyBlob) in D:\ILMerge\System.Compiler\ClrStrongName.cs:line 18 at System.Compiler.Writer.WritePE(String location, Boolean writeDebugSymbols, Module module, Boolean delaySign, String keyFileName, String keyName) in D:\ILMerge\System.Compiler\Writer.cs:line 5386

I don't really have the knowledge to debug any further than this but if you have any advice I'd be happy to listen. Hopefully this helps.

jnm2 commented 6 years ago

Repros on my machine from the moment #3 was merged, and the problem did not exist prior to that.

pwzFilePath is rooted and clearly exists. This gives no clue why it might return E_FILENOTFOUND: https://docs.microsoft.com/dotnet/framework/unmanaged-api/hosting/iclrstrongname-strongnamesignaturegeneration-method

themike10452 commented 6 years ago

I have this issue as well, IClsrStrongName::StrongNameSignatureGeneration method is throwing E_FILENOTFOUND.

jnm2 commented 6 years ago

Maybe we need to add the deprecated DllImports prior to #3 as a fallback.

themike10452 commented 6 years ago

That would be great. I wrote a separate console application to try IClsrStrongName::StrongNameSignatureGeneration and it didn't work there either, so I don't know where the problem might be, it's definitely not related to ILMerge code.

hrumhurum commented 6 years ago

Could E_FILENOTFOUND occur due to [ComConversionLoss] attribute?

In my understanding, [ComConversionLoss] may cause CLR to look for a corresponding .TLB file which may be missing on some machines.

Somebody could try to use Roslyn definition from https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/Interop/IClrStrongName.cs to verify that assumption on affected machines.

themike10452 commented 6 years ago

I tried what hrumhurum suggested and I got the same error.

Any updates regarding this?

hrumhurum commented 6 years ago

Just a wild guess: could it be an issue with machine key storage permission?

https://stackoverflow.com/questions/4606342/signing-assembly-access-is-denied

Experienced it on a fresh Windows 10 machine recently. The weirdness of it is that tools like sn.exe and C# compiler work, but some other ways (F# compiler and presumably some parts of IClrStrongName) don't work.

themike10452 commented 6 years ago

Sadly this doesn't help. I took ownership of the mentioned folder along with Full Access permissions, I still get the same error in ILMerge.

jnm2 commented 6 years ago

😲 Oh my goodness, I sat down to look at this again and had a sinking feeling within minutes... I see exactly what's wrong.

In C#, we're used to addressing methods by name. When .NET is doing COM interop though, the name of the method is irrelevant to identifying it. Instead, interface methods are identified by slot number which is determined by the number of method declarations which lexically preceded it within the interface declaration. Because I deleted all method definitions except the one I was interested in, all along, we've been invoking GetHashFromAssemblyFile instead of StrongNameSignatureGeneration! Let that be a lesson to me for not adding more comprehensive tests at the time!

I feel so bad, folks! I apologize that it took this long! 😢 Here's the good news: I just watched @bladeSLICE's excellent test pass as a result of pasting all the method declarations into IClrStrongName.

@bladeSLICE, may I use your commit as the first commit in a PR to fix this once and for all, to credit your work?

bladeSLICE commented 6 years ago

Yeah you can use the commit. Thanks for looking into it.