* * *

Author Topic: [SOLVED] How to tell if dll is 32-bit or 64-bit  (Read 2388 times)

Zoran

  • Hero Member
  • *****
  • Posts: 1148
    • http://wiki.lazarus.freepascal.org/User:Zoran
[SOLVED] How to tell if dll is 32-bit or 64-bit
« on: May 11, 2017, 08:33:28 am »
In a program I search for a dll in some standard paths in file system.

When the program finds the dll with the name I want, is there a way to detect whether this dll is 64-bit or 32-bit before trying to load it dynamically (so I can skip it if it doesn't match the program bitness)?
« Last Edit: May 12, 2017, 09:01:03 am by Zoran »

rvk

  • Hero Member
  • *****
  • Posts: 2646
Re: How to tell if dll is 32-bit or 64-bit
« Reply #1 on: May 11, 2017, 09:24:16 am »
Besides the fact there shouldn't be mismatching DLL in your path...

You can find the answer here:
How to determine if dll file was compiled as x64 or x86 bit using either Delphi or Lazarus

GetMem

  • Hero Member
  • *****
  • Posts: 2246
Re: How to tell if dll is 32-bit or 64-bit
« Reply #2 on: May 11, 2017, 10:28:21 am »
Hi Zoran,

I wrote this small function a few years ago. Works both wit exe and dll files:
Code: Pascal  [Select]
  1. uses JwaWindows;
  2.  
  3. function GetPEType(const APath: WideString): Byte;
  4. const
  5.   PE_UNKNOWN = 0;
  6.   PE_16BIT   = 1;
  7.   PE_32BIT   = 2;
  8.   PE_64BIT   = 3;
  9. var
  10.   hFile, hFileMap: THandle;
  11.   PMapView: Pointer;
  12.   PIDH: PImageDosHeader;
  13.   PINTH: PImageNtHeaders;
  14.   Base: LongWord;
  15. begin
  16.   Result := PE_UNKNOWN;
  17.  
  18.   hFile := CreateFileW(PWideChar(APath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  19.   if hFile = INVALID_HANDLE_VALUE then
  20.   begin
  21.     CloseHandle(hFile);
  22.     Exit;
  23.   end;
  24.  
  25.   hFileMap  := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
  26.   if hFileMap = 0 then
  27.   begin
  28.     CloseHandle(hFile);
  29.     CloseHandle(hFileMap);
  30.     Exit;
  31.   end;
  32.  
  33.   PMapView := MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
  34.   if PMapView = nil then
  35.   begin
  36.     CloseHandle(hFile);
  37.     CloseHandle(hFileMap);
  38.     Exit;
  39.   end;
  40.  
  41.   PIDH := PImageDosHeader(PMapView);
  42.   if PIDH^.e_magic <> IMAGE_DOS_SIGNATURE then
  43.   begin
  44.     CloseHandle(hFile);
  45.     CloseHandle(hFileMap);
  46.     UnmapViewOfFile(PMapView);
  47.     Exit;
  48.   end;
  49.  
  50.   Base := LongWord(PIDH);
  51.   PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));
  52.   if PINTH^.Signature <> IMAGE_NT_SIGNATURE then
  53.   begin
  54.     CloseHandle(hFile);
  55.     CloseHandle(hFileMap);
  56.     UnmapViewOfFile(PMapView);
  57.     Exit;
  58.   end;
  59.  
  60.   if PINTH^.Signature = $4550 then
  61.   begin
  62.     case PINTH^.OptionalHeader.Magic of
  63.       $10b: Result := PE_32BIT;
  64.       $20b: Result := PE_64BIT
  65.     end;
  66.   end
  67.   else
  68.     Result := PE_16BIT;
  69.  
  70.   CloseHandle(hFile);
  71.   CloseHandle(hFileMap);
  72.   UnmapViewOfFile(PMapView);
  73. end;
  74.  
  75.  
  76. procedure TForm1.Button1Click(Sender: TObject);
  77. var
  78.   PEType: Byte;
  79. begin
  80.   PEType := GetPEType(WideString('C:\mydll.dll'));
  81.   case PEType of
  82.     0: ShowMessage('unknown PE');
  83.     1: ShowMessage('16 bit PE');
  84.     2: ShowMessage('32 bit PE');
  85.     3: ShowMessage('64 bit PE');
  86.   end;
  87. end;

Zoran

  • Hero Member
  • *****
  • Posts: 1148
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: How to tell if dll is 32-bit or 64-bit
« Reply #3 on: May 11, 2017, 01:21:40 pm »
Thank you both.

GetMem, I tested your function and it works well. I'll use it, thanks again.

Zoran

  • Hero Member
  • *****
  • Posts: 1148
    • http://wiki.lazarus.freepascal.org/User:Zoran

GetMem

  • Hero Member
  • *****
  • Posts: 2246

Thaddy

  • Hero Member
  • *****
  • Posts: 4413
Re: [SOLVED] How to tell if dll is 32-bit or 64-bit
« Reply #6 on: May 12, 2017, 12:15:47 pm »
Same problem exists on Linux btw. Not always, but on 32/64 bit mixed.
"Logically, no number of positive outcomes at the level of experimental testing can confirm a scientific theory, but a single counterexample is logically decisive."

Zoran

  • Hero Member
  • *****
  • Posts: 1148
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: [SOLVED] How to tell if dll is 32-bit or 64-bit
« Reply #7 on: May 15, 2017, 08:10:48 am »
Now I noticed that compiler issues hints on these two lines:
Code: Pascal  [Select]
  1.   Base := LongWord(PIDH);
  2.   PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));
  3.  

And I don't know is there a reason why you cut Pointer to LongWord (in a 64-bit application this might lose significant bits)?

Wouldn't it be better:
Code: Pascal  [Select]
  1. var
  2.   ...
  3.   Base: Pointer;
  4. ...
  5.  Base := PIDH;
  6.  PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));
  7.  

In my test, both versions (with Base as LongWord and as Pointer) seem to work always... but...

What do you think?

GetMem

  • Hero Member
  • *****
  • Posts: 2246
Re: [SOLVED] How to tell if dll is 32-bit or 64-bit
« Reply #8 on: May 15, 2017, 10:31:32 am »
Hi Zoran,

Quote
And I don't know is there a reason why you cut Pointer to LongWord
Initially I wrote that function for delphi, where an explicit typecast needed, otherwise the function won't compile. The error message( both with D2007 and XE4): "E2015 Operator not applicable to this operand type".

Quote
Now I noticed that compiler issues hints on these two lines:
Because we casting a pointer to a fixed size variable. The compiler knows that the size of pointers can have different values on different platforms, while the size of the variable stays the same(or not, it depends on a lot of things), hence the warning, nothing to worry about in my opinion.

Quote
in a 64-bit application this might lose significant bits
You have a valid point here, Longword is enough for a 32 bit system, but NativeUInt(PtrUint) or QWord is more appropriate for 64 bit. Your pointer alternative is also good if you don't mind dropping the delphi compatibility support.

PS: I also find this: https://0x2a.wtf/projects/pesp . Supports both Windows and Linux. It's pure pascal. You get a lot more info then bitness: Import/Export tables, Entry Point, etc...This is also an answer to @Thaddy's reply.
« Last Edit: May 15, 2017, 10:39:37 am by GetMem »

Zoran

  • Hero Member
  • *****
  • Posts: 1148
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: [SOLVED] How to tell if dll is 32-bit or 64-bit
« Reply #9 on: May 17, 2017, 10:00:15 am »
Okay, thanks.
I updated the wiki to use Pointer.

Now, another thing, this function can never return PE_16BIT, take a look at these lines (see added comments):
Code: Pascal  [Select]
  1. ...
  2.  if PINTH^.Signature <> IMAGE_NT_SIGNATURE then // this is asked again later in code, to return 16-bit
  3.  begin
  4.    CloseHandle(hFile);
  5.    CloseHandle(hFileMap);
  6.    UnmapViewOfFile(PMapView);
  7.    Exit;
  8.  end;
  9.  
  10.  if PINTH^.Signature = $4550 then // $4550 is same as IMAGE_NT_SIGNATURE, which was already tested above.
  11.  begin
  12.    case PINTH^.OptionalHeader.Magic of
  13.      $10b: Result := PE_32BIT;
  14.      $20b: Result := PE_64BIT
  15.    end;
  16.  end
  17.  else
  18.    Result := PE_16BIT; // so, this line will never execute!!!!!
  19.  
  20.  CloseHandle(hFile);
  21.  CloseHandle(hFileMap);
  22.  UnmapViewOfFile(PMapView);  
  23. end; // the function ends here.
  24.  

Personally, I don't need 16-bit testing, but the wiki should be corrected. Either by removing the first test which prevents returning 16-bit, or by removing 16 bit result. What do you think?

GetMem

  • Hero Member
  • *****
  • Posts: 2246
Re: [SOLVED] How to tell if dll is 32-bit or 64-bit
« Reply #10 on: May 17, 2017, 10:14:19 am »
Quote
Now, another thing, this function can never return PE_16BIT, take a look at these lines (see added comments):
You're correct! It's a logical error, thank you for noticing it. Feel free to modify the code anyway you like, personally I don't think 16 bit support is needed nowadays.
« Last Edit: May 17, 2017, 10:47:05 am by GetMem »

Zoran

  • Hero Member
  • *****
  • Posts: 1148
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: [SOLVED] How to tell if dll is 32-bit or 64-bit
« Reply #11 on: May 17, 2017, 01:02:19 pm »
Quote
Now, another thing, this function can never return PE_16BIT, take a look at these lines (see added comments):
You're correct! It's a logical error, thank you for noticing it. Feel free to modify the code anyway you like, personally I don't think 16 bit support is needed nowadays.

Thanks. I updated the wiki.

ASerge

  • Sr. Member
  • ****
  • Posts: 401
Re: [SOLVED] How to tell if dll is 32-bit or 64-bit
« Reply #12 on: May 19, 2017, 11:16:02 pm »
Multiple CloseHandle calls can be excluded:
Code: Pascal  [Select]
  1. uses Windows;
  2.  
  3. type
  4.   TPEBitness = (pebUnknown, peb16, peb32, peb64);
  5.  
  6. function GetPEBitness(const APath: WideString): TPEBitness;
  7. const
  8.   IMAGE_NT_OPTIONAL_HDR32_MAGIC = $10b;
  9.   IMAGE_NT_OPTIONAL_HDR64_MAGIC = $20b;
  10. var
  11.   HFile, HFileMap: THandle;
  12.   PMapView: Pointer;
  13.   PIDH: PImageDosHeader;
  14.   PINTH: PImageNtHeaders;
  15. begin
  16.   Result := pebUnknown;
  17.   HFile := CreateFileW(PWideChar(APath), GENERIC_READ, FILE_SHARE_READ, nil,
  18.     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  19.   if HFile = INVALID_HANDLE_VALUE then
  20.     Exit;
  21.   HFileMap  := CreateFileMapping(HFile, nil, PAGE_READONLY, 0, 0, nil);
  22.   CloseHandle(HFile);
  23.   if HFileMap = 0 then
  24.     Exit;
  25.   PMapView := MapViewOfFile(HFileMap, FILE_MAP_READ, 0, 0, 0);
  26.   CloseHandle(HFileMap);
  27.   if PMapView = nil then
  28.     Exit;
  29.   PIDH := PImageDosHeader(PMapView);
  30.   if PIDH^.e_magic = IMAGE_DOS_SIGNATURE then
  31.   begin
  32.     PINTH := PImageNtHeaders(PAnsiChar(PMapView) + PIDH^._lfanew);
  33.     if PINTH^.Signature <> IMAGE_NT_SIGNATURE then
  34.       Result := peb16
  35.     else
  36.       case PINTH^.OptionalHeader.Magic of
  37.         IMAGE_NT_OPTIONAL_HDR32_MAGIC:
  38.           Result := peb32;
  39.         IMAGE_NT_OPTIONAL_HDR64_MAGIC:
  40.           Result := peb64;
  41.       end;
  42.   end;
  43.   UnmapViewOfFile(PMapView);
  44. end;

Zoran

  • Hero Member
  • *****
  • Posts: 1148
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: [SOLVED] How to tell if dll is 32-bit or 64-bit
« Reply #13 on: May 20, 2017, 12:03:44 am »

Thank you, ASerge. Are you sure that it works well for 16-bit. Are you sure that it will not in some case return peb16 when it is not a valid 16-bit dll?

I'm asking because in the first version (see this post), there was a protective block, and I'm affraid that it was maybe inserted when the function returned 16-bit when it was not a valid 16-bit dll (I don't know, but it just looks like that...).

ASerge

  • Sr. Member
  • ****
  • Posts: 401
Re: [SOLVED] How to tell if dll is 32-bit or 64-bit
« Reply #14 on: May 20, 2017, 01:04:37 am »
Are you sure that it works well for 16-bit
if PIDH^.e_magic = IMAGE_DOS_SIGNATURE then // Executable
  if PINTH^.Signature <> IMAGE_NT_SIGNATURE then // But for NT

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus