-
Notifications
You must be signed in to change notification settings - Fork 20
Description
gprconfig uses the following to determine whether a file is a valid Windows executable
gprbuild/gpr/src/gpr-knowledge.adb
Lines 397 to 423 in 48f5161
| function Is_Windows_Executable (Filename : String) return Boolean is | |
| type Byte is mod 256; | |
| for Byte'Size use 8; | |
| for Byte'Alignment use 1; | |
| type Bytes is array (Positive range <>) of Byte; | |
| Windows_Pattern : constant Bytes := (77, 90, 144, 0); | |
| Fd : constant File_Descriptor := Open_Read (Filename, Binary); | |
| B : Bytes (1 .. 4); | |
| N_Read : Integer; | |
| begin | |
| N_Read := Read (Fd, B'Address, 4); | |
| Close (Fd); | |
| if N_Read < 4 then | |
| return False; | |
| else | |
| if B = Windows_Pattern then | |
| return True; | |
| else | |
| return False; | |
| end if; | |
| end if; | |
| end Is_Windows_Executable; |
specifically looking at the first 4 bytes of the file match using Windows_Pattern : constant Bytes := (77, 90, 144, 0);
This is typically works, but it's not actually correct - only MZ / 0x4d5a is required, the 3rd & 4th bytes are actually just e_cblp, or "Bytes on last page of file". It seems typically that's set to 0x90, but not exclusively. I'm not sure anything actually uses it.
However, I've just run into an issue where gprconfig refuses to recognise a compiler that was linked with lld where it's set to 0x78 (apparently the lld linker does actually set it?) (best not ask exactly why I'm doing this)
The correct method of determining a valid binary is to lookup the COFF File header at position 0x3c and then find the machine type - https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image
I implemented this routine in Perl a while back, which may prove of interest.
my ($path) = @_;
$path->open( "<:raw" );
my @bytes = unpack( "C*", $path->read_n_bytes( 4 ) );
my $arch;
if( $bytes[ 0 ] == 0x4d && $bytes[ 1 ] == 0x5a )
{
# COFF binary (Windows)
$path->seek_abs( 0x3c );
my $header_pos = unpack( "L", $path->read_n_bytes( 4 ) );
$path->seek_abs( $header_pos + 4 );
my $machine = unpack( "S", $path->read_n_bytes( 2 ) );
if( $machine == 0x14c ) # IMAGE_FILE_MACHINE_I386
{
$arch = 32;
}
elsif( $machine == 0x8664 ) # IMAGE_FILE_MACHINE_AMD64
{
$arch = 64;
}
else
{
print "Unknown machine type of COFF binary " . $path->get_absolute_name() . ": $machine\n";
}
}