How to Find If a Native Dll File Is Compiled as X64 or X86

How can I test a Windows DLL file to determine if it is 32 bit or 64 bit?

Gory details

A DLL uses the PE executable format, and it's not too tricky to read that information out of the file.

See this MSDN article on the PE File Format for an overview. You need to read the MS-DOS header, then read the IMAGE_NT_HEADERS structure. This contains the IMAGE_FILE_HEADER structure which contains the info you need in the Machine member which contains one of the following values

  • IMAGE_FILE_MACHINE_I386 (0x014c)
  • IMAGE_FILE_MACHINE_IA64 (0x0200)
  • IMAGE_FILE_MACHINE_AMD64 (0x8664)

This information should be at a fixed offset in the file, but I'd still recommend traversing the file and checking the signature of the MS-DOS header and the IMAGE_NT_HEADERS to be sure you cope with any future changes.

Use ImageHelp to read the headers...

You can also use the ImageHelp API to do this - load the DLL with LoadImage and you'll get a LOADED_IMAGE structure which will contain a pointer to an IMAGE_NT_HEADERS structure. Deallocate the LOADED_IMAGE with ImageUnload.

...or adapt this rough Perl script

Here's rough Perl script which gets the job done. It checks the file has a DOS header, then reads the PE offset from the IMAGE_DOS_HEADER 60 bytes into the file.

It then seeks to the start of the PE part, reads the signature and checks it, and then extracts the value we're interested in.

#!/usr/bin/perl
#
# usage: petype <exefile>
#
$exe = $ARGV[0];

open(EXE, $exe) or die "can't open $exe: $!";
binmode(EXE);
if (read(EXE, $doshdr, 64)) {

($magic,$skip,$offset)=unpack('a2a58l', $doshdr);
die("Not an executable") if ($magic ne 'MZ');

seek(EXE,$offset,SEEK_SET);
if (read(EXE, $pehdr, 6)){
($sig,$skip,$machine)=unpack('a2a2v', $pehdr);
die("No a PE Executable") if ($sig ne 'PE');

if ($machine == 0x014c){
print "i386\n";
}
elsif ($machine == 0x0200){
print "IA64\n";
}
elsif ($machine == 0x8664){
print "AMD64\n";
}
else{
printf("Unknown machine type 0x%lx\n", $machine);
}
}
}

close(EXE);

Windows command to tell whether a .dll file is 32 bit or 64 bit?

DUMPBIN is included with Visual C++ and can provide this information with the /HEADERS switch.

Example output from a 32-bit image:

FILE HEADER VALUES
14C machine (i386)
6 number of sections
306F7A22 time date stamp Sun Oct 01 22:35:30 1995
0 file pointer to symbol table
1D1 number of symbols
E0 size of optional header
302 characteristics
Executable
32 bit word machine
Debug information stripped

How can I determine if a .NET assembly was built for x86 or x64?

Look at System.Reflection.AssemblyName.GetAssemblyName(string assemblyFile).

You can examine assembly metadata from the returned AssemblyName instance:

Using PowerShell:


[36] C:\> [reflection.assemblyname]::GetAssemblyName("${pwd}\Microsoft.GLEE.dll") | fl

Name : Microsoft.GLEE
Version : 1.0.0.0
CultureInfo :
CodeBase : file:///C:/projects/powershell/BuildAnalyzer/...
EscapedCodeBase : file:///C:/projects/powershell/BuildAnalyzer/...
ProcessorArchitecture : MSIL
Flags : PublicKey
HashAlgorithm : SHA1
VersionCompatibility : SameMachine
KeyPair :
FullName : Microsoft.GLEE, Version=1.0.0.0, Culture=neut...

Here, ProcessorArchitecture identifies the target platform.

  • Amd64: A 64-bit processor based on the x64 architecture.
  • Arm: An ARM processor.
  • IA64: A 64-bit Intel Itanium processor only.
  • MSIL: Neutral with respect to processor and bits-per-word.
  • X86: A 32-bit Intel processor, either native or in the Windows on Windows environment on a 64-bit platform (WoW64).
  • None: An unknown or unspecified combination of processor and bits-per-word.

I'm using PowerShell in this example to call the method.

How to determine if dll file was compiled as x64 or x86 bit using either Delphi or Lazarus

You should read and parse PE header.

Like this:

function Isx64(const Strm: TStream): Boolean;
const
IMAGE_FILE_MACHINE_I386 = $014c; // Intel x86
IMAGE_FILE_MACHINE_IA64 = $0200; // Intel Itanium Processor Family (IPF)
IMAGE_FILE_MACHINE_AMD64 = $8664; // x64 (AMD64 or EM64T)
// You'll unlikely encounter the things below:
IMAGE_FILE_MACHINE_R3000_BE = $160; // MIPS big-endian
IMAGE_FILE_MACHINE_R3000 = $162; // MIPS little-endian, 0x160 big-endian
IMAGE_FILE_MACHINE_R4000 = $166; // MIPS little-endian
IMAGE_FILE_MACHINE_R10000 = $168; // MIPS little-endian
IMAGE_FILE_MACHINE_ALPHA = $184; // Alpha_AXP }
IMAGE_FILE_MACHINE_POWERPC = $1F0; // IBM PowerPC Little-Endian
var
Header: TImageDosHeader;
ImageNtHeaders: TImageNtHeaders;
begin
Strm.ReadBuffer(Header, SizeOf(Header));
if (Header.e_magic <> IMAGE_DOS_SIGNATURE) or
(Header._lfanew = 0) then
raise Exception.Create('Invalid executable');
Strm.Position := Header._lfanew;

Strm.ReadBuffer(ImageNtHeaders, SizeOf(ImageNtHeaders));
if ImageNtHeaders.Signature <> IMAGE_NT_SIGNATURE then
raise Exception.Create('Invalid executable');

Result := ImageNtHeaders.FileHeader.Machine <> IMAGE_FILE_MACHINE_I386;
end;

Check if unmanaged DLL is 32-bit or 64-bit?

Refer to the specifications. Here's a basic implementation:

public static MachineType GetDllMachineType (string dllPath)
{
// See http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
// Offset to PE header is always at 0x3C.
// The PE header starts with "PE\0\0" = 0x50 0x45 0x00 0x00,
// followed by a 2-byte machine type field (see the document above for the enum).
//
using (var fs = new FileStream (dllPath, FileMode.Open, FileAccess.Read))
using (var br = new BinaryReader (fs))
{
fs.Seek (0x3c, SeekOrigin.Begin);
Int32 peOffset = br.ReadInt32();

fs.Seek (peOffset, SeekOrigin.Begin);
UInt32 peHead = br.ReadUInt32();

if (peHead != 0x00004550) // "PE\0\0", little-endian
throw new Exception ("Can't find PE header");

return (MachineType)br.ReadUInt16();
}
}

The MachineType enum is defined as:

public enum MachineType : ushort
{
IMAGE_FILE_MACHINE_UNKNOWN = 0x0,
IMAGE_FILE_MACHINE_AM33 = 0x1d3,
IMAGE_FILE_MACHINE_AMD64 = 0x8664,
IMAGE_FILE_MACHINE_ARM = 0x1c0,
IMAGE_FILE_MACHINE_EBC = 0xebc,
IMAGE_FILE_MACHINE_I386 = 0x14c,
IMAGE_FILE_MACHINE_IA64 = 0x200,
IMAGE_FILE_MACHINE_M32R = 0x9041,
IMAGE_FILE_MACHINE_MIPS16 = 0x266,
IMAGE_FILE_MACHINE_MIPSFPU = 0x366,
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466,
IMAGE_FILE_MACHINE_POWERPC = 0x1f0,
IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1,
IMAGE_FILE_MACHINE_R4000 = 0x166,
IMAGE_FILE_MACHINE_SH3 = 0x1a2,
IMAGE_FILE_MACHINE_SH3DSP = 0x1a3,
IMAGE_FILE_MACHINE_SH4 = 0x1a6,
IMAGE_FILE_MACHINE_SH5 = 0x1a8,
IMAGE_FILE_MACHINE_THUMB = 0x1c2,
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169,
IMAGE_FILE_MACHINE_ARM64 = 0xaa64
}

I only needed three of these, but I included them all for completeness. Final 64-bit check:

// Returns true if the dll is 64-bit, false if 32-bit, and null if unknown
public static bool? UnmanagedDllIs64Bit(string dllPath)
{
switch (GetDllMachineType(dllPath))
{
case MachineType.IMAGE_FILE_MACHINE_AMD64:
case MachineType.IMAGE_FILE_MACHINE_IA64:
return true;
case MachineType.IMAGE_FILE_MACHINE_I386:
return false;
default:
return null;
}
}

How to Tell if a .NET Assembly Was Compiled as x86, x64 or Any CPU

If you just want to find this out on a given dll, then you can use the CorFlags tool that is part of the Windows SDK:

CorFlags.exe assembly.dll

If you want to do it using code, take a look at the GetPEKind method of the Module class:

Assembly assembly = Assembly.LoadFrom("path to dll");
PortableExecutableKinds peKind;
ImageFileMachine imageFileMachine;
assembly.ManifestModule.GetPEKind(out peKind, out imageFileMachine)

You then need to examine the peKind to check its value. See the MSDN docs for PortableExecutableKinds for more info.

Programatically determine if native .exe is 32-bit or 64-bit

If you really only want to do this for EXEs and not DLLs, just use GetBinaryType.

Determines whether a file is an
executable (.exe) file, and if so,
which subsystem runs the executable
file.

Target 32 Bit or 64 Bit native DLL depending on environment

Here is the solution I've used on many projects:

  • name the 32-bit assembly with a "32-bit oriented name". For
    example MyAssembly.Native.x86.dll
  • name the 64-bit assembly with a "64-bit oriented name". For example MyAssembly.Native.x64.dll
  • compile the managed assembly as 'Any Cpu'
  • ship everything in the same path

Here is how I declare P/Invoke methods:

[DllImport("MyAssembly.Native.x86.dll", EntryPoint = "MyTest")]
private static extern void MyTest86(MyType myArg);

[DllImport("MyAssembly.Native.x64.dll", EntryPoint = "MyTest")]
private static extern void MyTest64(MyType myArg);

And here is the corresponding 'MyTest' function which is the one I'll always use (the others are here just for correct bitness binding). It has the same signature than the other P/Invoke ones:

public static void MyTest(MyType myArg)
{
if (IntPtr.Size == 8)
{
MyTest64(myArg);
return;
}

MyTest86(myArg);
}

The advantages are:

  • you can ship all binaries (DLLs, EXEs, ...) in the same path
  • you support 32-bit and 64-bit processes and OSes with the same file layout
  • you don't have to resort to Win32 apis for changing dll load path

The inconveniences are:

  • you'll have 3 method declarations for 1 'real' method
  • you'll loose some CPU cycles because of the bitness test
  • depending on your context, sometimes you can't change the native DLLs names, so you just can't do this

Win32 API to tell whether a given binary (EXE or DLL) is x86, x64, or ia64

For EXEs

use GetBinaryType(...)

Here is same question for manged exe.

For DLLs (and EXEs)

Use the ImageNtHeader(...) to get the PE data of the file and then check the IMAGE_FILE_HEADER.Machine field.

Here is some code I found using Google Code Search

No Cleanup and NO error checking

// map the file to our address space
// first, create a file mapping object
hMap = CreateFileMapping(
hFile,
NULL, // security attrs
PAGE_READONLY, // protection flags
0, // max size - high DWORD
0, // max size - low DWORD
NULL ); // mapping name - not used

// next, map the file to our address space
void* mapAddr = MapViewOfFileEx(
hMap, // mapping object
FILE_MAP_READ, // desired access
0, // loc to map - hi DWORD
0, // loc to map - lo DWORD
0, // #bytes to map - 0=all
NULL ); // suggested map addr

peHdr = ImageNtHeader( mapAddr );


Related Topics



Leave a reply



Submit