Closed Forgind closed 2 years ago
Tagging subscribers to this area: @dotnet/area-system-reflection-metadata See info in area-owners.md if you want to be subscribed.
Author: | Forgind |
---|---|
Assignees: | - |
Labels: | `area-System.Reflection.Metadata` |
Milestone: | - |
This example shows how to create a System.Reflection.Metadata reader: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.metadata.metadatareader?view=net-6.0#examples
Once you have the reader, PEHeaders.CoffHeader.Machine
has the PE file architecture. For example, you can add Console.WriteLine(peReader.PEHeaders.CoffHeader.Machine)
to print it.
What are you trying to do with this architecture? Assemblies in modern .NET are typically built as AnyCPU even when they are architecture or OS specific, and then packaged for specific RID to capture the fact that they are architecture or OS specific.
Assemblies are typically built for concrete architecture only for compatibility with .NET Framework.
This issue has been marked needs-author-action
and may be missing some important information.
PE isn't the layer I'm after. That's a windows concept; the code I'm replacing used AssemblyName.GetAssemblyName(\<dll>).ProcessorArchitecture, and that's a .NET concept. There are two ways we're currently using it:
To figure out if something is MSIL or None (versus other).
You can do that by checking whether CorHeader
is non-null.
If someone has an assembly specifically built for, say, x86, we need to know that our current process's architecture doesn't match then start a new process with the correct architecture to load and execute their code
AssemblyName.ProcessorArchitecture in .NET Framework was inferred from the PE file architecture. It is not first class .NET concept and that's why it was deprecated.
AssemblyName.ProcessorArchitecture in .NET Framework was inferred from the PE file architecture. It is not first class .NET concept and that's why it was deprecated.
Can you link to where that was done? As a test case, I tried to figure out ProcessorArchitecture from CoffHeader, and it mostly worked, but there were cases where it didn't, and I don't know what the difference was for .dlls.
For more context, we let the user pass in a string representing the architecture they care about. We parse it into a System.Reflection.ProcessorArchitecture, then try to compare it to AssemblyName.ProcessorArchitecture. If there's a way to map CoffHeader.Machine to AssemblyName.ProcessorArchitecture, that'd be great, but Machine has a lot more in its enum, and I don't know what maps to what.
If there's a way to map CoffHeader.Machine to AssemblyName.ProcessorArchitecture,
There is not.
ProcessorArchitecture was implemented by stealing a few bits from AssemblyNameFlags. It severely limits how many different processor architectures can be represented by ProcessorArchitecture. It was one of the contributing reasons for obsoleting it. We are not adding new architectures supported by .NET to ProcessorArchitecture since we would quickly hit a dead end.
Can you link to where that was done?
The code still exists here for backward compatibility: https://github.com/dotnet/runtime/blob/6d0bcc4cc7cf98e661c91d4f2abace2c5bd282a5/src/coreclr/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreCLR.cs#L113
I'm still missing two pieces.
I wanted to go from file to ProcessorArchitecture, and I can get from file to Machine (which seems to be equivalent to the ImageFileMachine in the method you linked?) and PortableExecutableKinds to ProcessorArchitecture. How do I get a PortableExecutableKind?
Second, in the issue I'd originally commented on, you said I should use System.Reflection.Metadata. All I've seen so far is System.Reflection. Am I missing something?
I wanted to go from file to ProcessorArchitecture
ProcessorArchitecture does not have value for Arm64. What are you going to do for Arm64? I do not think that the plan with using ProcessorArchitecture is going to work. You should switch over to a non-obsolete enum. The PE file enum Machine
seems to be closest to what you are trying to achieve.
How do I get a PortableExecutableKind?
The logic to compute PortableExecutableKind is here: https://github.com/dotnet/runtime/blob/17154bd7b8f21d6d8d6fca71b89d7dcb705ec32b/src/coreclr/inc/pedecoder.inl#L1096-L1156 . Note that it is very Windows and .NET Framework specific. It does not make sense on non-Windows platforms and in .NET Core/5+.
Is there an .NET way to check the architecture of the .NET Host?
Example:
What happens in my program is that I do not know how to check the bitness the host in running in when it's AnyCPU and as such could crash when it selects the x64 plugin instead of the x86 one when in x86 host, or selects the x86 one when in x64 host which is not good at all. I could have it target specific x86 or x64 but then it will remove LargeAddressAware (for x86) which is also not good at all in the program (as it might need it set). I could set it manually if it was not for setting it up for the compiler to sign the code with an digital signature (modifying it afterwards invalidates the signature which is also not good as then people won't trust the assembly anymore when it is shipped with an invalid signature). Yes I sign my assemblies so that they know "It was not tampered with after it was compiled from CI on tag builds and deployed."
Is there an .NET way to check the architecture of the .NET Host?
RuntimeInformation.ProcessArchitecture
returns the architecture of .NET host for the current process.
Alright, I guess I could use that in my program to split plugins based on RID and require C++ plugins to output into an RID specific folder and then load plugins in this order:
And hopefully avoid said problems in my program then.
RuntimeInformation.ProcessArchitecture returns an Architecture. It sounded like your recommended way to get the architecture of a file is to get Machine from the PEHeader. Is there a built-in comparison method?
I actually found an even better way of checking the architecture:
it's System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier
which then becomes the runtime identifier of the current running process of which can only be the ones supported by the .NET SDK and the runtime.
That makes code like this utterly a waste of time (yes I did it before the above was pointed out to me) https://gist.github.com/AraHaan/0fdcfc658a909034bd5409f580088896
It sounded like your recommended way to get the architecture of a file is to get Machine from the PEHeader. Is there a built-in comparison method?
There is no built-in comparison method. Nothing that we have needs a comparison method like this.
As I have said in https://github.com/dotnet/runtime/issues/74040#issuecomment-1217483633 , the target architecture in modern .NET is identified by the RID that the assembly was packaged for. .NET SDK does not try to guess what the assembly target architecture may be by analyzing content of the binary.
In addition to CoffHeader.Machine, CorHeader.Flags might be relavant to you if you care about .NET Framework compat. In my tests, assemblies compiled for .NET Framework seems to have machine type set to I386 even if they are platform-agnostic; but those that are really x86-specific have Requires32Bit
corflag set.
Using SRM was what @jkotas recommended in https://github.com/dotnet/runtime/issues/59061#issuecomment-922392861. I had trouble figuring out how to do that, exactly, and he told me to file a new issue. Can someone point me to an example of how to do that?