forked from OSchip/llvm-project
321 lines
9.1 KiB
Perl
Executable File
321 lines
9.1 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
#
|
|
#//===----------------------------------------------------------------------===//
|
|
#//
|
|
#// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
#// See https://llvm.org/LICENSE.txt for license information.
|
|
#// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
#//
|
|
#//===----------------------------------------------------------------------===//
|
|
#
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use FindBin;
|
|
use lib "$FindBin::Bin/lib";
|
|
|
|
use tools;
|
|
|
|
our $VERSION = "0.004";
|
|
my $target_os;
|
|
my $target_arch;
|
|
my $target_mic_arch;
|
|
|
|
my $hex = qr{[0-9a-f]}i; # hex digit.
|
|
|
|
# mic-specific details.
|
|
|
|
sub bad_mic_fmt($) {
|
|
# Before we allowed both elf64-x86-64-freebsd and elf-l1om-freebsd.
|
|
# Now the first one is obsolete, only elf64-l1om-freebsd is allowed.
|
|
my ( $fmt ) = @_;
|
|
if ( 0 ) {
|
|
} elsif ( "$target_mic_arch" eq "knf" ) {
|
|
return $fmt !~ m{\Aelf64-l1om?\z};
|
|
} elsif ( "$target_mic_arch" eq "knc" ) {
|
|
return $fmt !~ m{\Aelf64-k1om?\z};
|
|
} else {
|
|
return 1;
|
|
};
|
|
}; # sub bad_mic_fmt
|
|
|
|
# Undesired instructions for mic: all x87 and some other.
|
|
# AC: Since compiler 2010-06-30 x87 instructions are supported, removed the check of x87.
|
|
my $mic_bad_re;
|
|
sub bad_mic_instr($$) {
|
|
my ( $instr, $args ) = @_;
|
|
if ( "$target_mic_arch" eq "knc" ) {
|
|
# workaround of bad code generation on KNF Linux* OS:
|
|
return ( defined( $instr ) and $instr =~ $mic_bad_re );
|
|
} else {
|
|
return ( defined( $instr ) and $instr =~ $mic_bad_re or defined( $args ) and $args =~ m{xmm}i );
|
|
}
|
|
}; # sub bad_mic_instr
|
|
|
|
# lin_32-specific details.
|
|
|
|
sub bad_ia32_fmt($) {
|
|
my ( $fmt ) = @_;
|
|
return $fmt !~ m{\Aelf32-i386\z};
|
|
}; # sub bad_ia32_fmt
|
|
|
|
my @sse2 =
|
|
qw{
|
|
movapd movupd movhpd movlpd movmskpd movsd
|
|
addpd addsd subpd subsd mulpd mulsd divpd divsd sqrtpd sqrtsd maxpd maxsd minpd minsd
|
|
andpd andnpd orpd xorpd
|
|
cmppd cmpsd comisd ucomisd
|
|
shufpd unpckhpd unpcklpd
|
|
cvtpd2pi cvttpd2pi cvtpi2pd cvtpd2dq cvttpd2dq cvtdq2pd cvtps2pd cvtpd2ps cvtss2sd cvtsd2ss
|
|
cvtsd2si cvttsd2si cvtsi2sd cvtdq2ps cvtps2dq cvttps2dq movdqa movdqu movq2dq movdq2q
|
|
pmuludq paddq psubq pshuflw pshufhw pshufd pslldq psrldq punpckhqdq punpcklqdq clflush
|
|
lfence mfence maskmovdqu movntpd movntdq movnti
|
|
};
|
|
my @sse3 =
|
|
qw{
|
|
fisttp lddqu addsubps addsubpd haddps hsubps haddpd hsubpd movshdup movsldup movddup monitor
|
|
mwait
|
|
};
|
|
my @ssse3 =
|
|
qw{
|
|
phaddw phaddsw phaddd phsubw phsubsw phsubd pabsb pabsw pabsd pmaddubsw pmulhrsw pshufb
|
|
psignb psignw psignd palignr
|
|
};
|
|
my @sse4 =
|
|
(
|
|
# SSE4.1
|
|
qw{
|
|
pmulld pmuldq dppd dpps movntdqa blendpd blendps blendvpd blendvps pblendvb pblendw pminuw
|
|
pminud pminsb pminsd pmaxuw pmaxud pmaxsb pmaxsd roundps roundpd roundss roundsd extractps
|
|
insertps pinsrb pinsrd pinsrq pextrb pextrw pextrd pextrq pmovsxbw pmovzxbw pmovsxbd
|
|
pmovzxbd pmovsxwd pmovzxwd pmovsxbq pmovzxbq pmovsxwq pmovzxwq pmovsxdq pmovzxdq mpsadbw
|
|
phminposuw ptest pcmpeqq packusdw
|
|
},
|
|
# SSE4.2
|
|
qw{
|
|
pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq crc32 popcnt
|
|
}
|
|
);
|
|
|
|
# Undesired instructions for IA-32 architecture: Pentium 4 (SSE2) and newer.
|
|
# TODO: It would be much more reliable to list *allowed* instructions rather than list undesired
|
|
# instructions. In such a case the list will be stable and not require update when SSE5 is released.
|
|
my @ia32_bad_list = ( @sse2, @sse3, @ssse3, @sse4 );
|
|
|
|
my $ia32_bad_re = qr{@{[ "^(?:" . join( "|", @ia32_bad_list ) . ")" ]}}i;
|
|
|
|
sub bad_ia32_instr($$) {
|
|
my ( $instr, $args ) = @_;
|
|
return ( defined( $instr ) and $instr =~ $ia32_bad_re );
|
|
}; # sub bad_ia32_instr
|
|
|
|
sub check_file($;$$) {
|
|
|
|
my ( $file, $show_instructions, $max_instructions ) = @_;
|
|
my @bulk;
|
|
|
|
if ( not defined( $max_instructions ) ) {
|
|
$max_instructions = 100;
|
|
}; # if
|
|
|
|
execute( [ "x86_64-k1om-linux-objdump", "-d", $file ], -stdout => \@bulk );
|
|
|
|
my $n = 0;
|
|
my $errors = 0;
|
|
my $current_func = ""; # Name of current function.
|
|
my $reported_func = ""; # name of last reported function.
|
|
foreach my $line ( @bulk ) {
|
|
++ $n;
|
|
if ( 0 ) {
|
|
} elsif ( $line =~ m{^\s*$} ) {
|
|
# Empty line.
|
|
# Ignore.
|
|
} elsif ( $line =~ m{^In archive (.*?):\s*$} ) {
|
|
# In archive libomp.a:
|
|
} elsif ( $line =~ m{^(?:.*?):\s*file format (.*?)\s*$} ) {
|
|
# libomp.so: file format elf64-x86-64-freebsd
|
|
# kmp_ftn_cdecl.o: file format elf64-x86-64
|
|
my $fmt = $1;
|
|
if ( bad_fmt( $fmt ) ) {
|
|
runtime_error( "Invalid file format: $fmt." );
|
|
}; # if
|
|
} elsif ( $line =~ m{^Disassembly of section (.*?):\s*$} ) {
|
|
# Disassembly of section .plt:
|
|
} elsif ( $line =~ m{^$hex+ <([^>]+)>:\s*$} ) {
|
|
# 0000000000017e98 <__kmp_str_format@plt-0x10>:
|
|
$current_func = $1;
|
|
} elsif ( $line =~ m{^\s*\.{3}\s*$} ) {
|
|
} elsif ( $line =~ m{^\s*($hex+):\s+($hex$hex(?: $hex$hex)*)\s+(?:lock\s+|rex[.a-z]*\s+)?([^ ]+)(?:\s+([^#]+?))?\s*(?:#|$)} ) {
|
|
# 17e98: ff 35 fa 7d 26 00 pushq 0x267dfa(%rip) # 27fc98 <_GLOBAL_OFFSET_TABLE>
|
|
my ( $addr, $dump, $instr, $args ) = ( $1, $2, $3, $4 );
|
|
# Check this is not a bad instruction and xmm registers are not used.
|
|
if ( bad_instr( $instr, $args ) ) {
|
|
if ( $errors == 0 ) {
|
|
warning( "Invalid instructions found in `$file':" );
|
|
}; # if
|
|
if ( $current_func ne $reported_func ) {
|
|
warning( " $current_func" );
|
|
$reported_func = $current_func;
|
|
}; # if
|
|
++ $errors;
|
|
if ( $show_instructions ) {
|
|
warning( " $line" );
|
|
}; # if
|
|
if ( $errors >= $max_instructions ) {
|
|
info( "$errors invalid instructions found; scanning stopped." );
|
|
last;
|
|
}; # if
|
|
}; # if
|
|
} else {
|
|
runtime_error( "Error parsing objdump output line $n:\n>>>> $line\n" );
|
|
}; # if
|
|
}; # foreach $line
|
|
|
|
return $errors;
|
|
|
|
}; # sub check_file
|
|
|
|
# --------------------------------------------------------------------------------------------------
|
|
|
|
# Parse command line.
|
|
my $max_instructions;
|
|
my $show_instructions;
|
|
get_options(
|
|
"os=s" => \$target_os,
|
|
"arch=s" => \$target_arch,
|
|
"mic-arch=s" => \$target_mic_arch,
|
|
"max-instructions=i" => \$max_instructions,
|
|
"show-instructions!" => \$show_instructions,
|
|
);
|
|
my $target_platform = $target_os . "_" . $target_arch;
|
|
if ( "$target_os" eq "lin" and "$target_mic_arch" eq "knf" ) {
|
|
$mic_bad_re = qr{^(?:pause|[slm]fence|scatter|gather|cmpxchg16b|clevict[12])}i;
|
|
} else {
|
|
$mic_bad_re = qr{^(?:pause|[slm]fence|scatter|gather|cmov|cmpxchg16b|clevict[12])}i;
|
|
};
|
|
if ( 0 ) {
|
|
} elsif ( $target_platform eq "lin_mic" ) {
|
|
*bad_instr = \*bad_mic_instr;
|
|
*bad_fmt = \*bad_mic_fmt;
|
|
} elsif ( $target_platform eq "lin_32" ) {
|
|
*bad_instr = \*bad_ia32_instr;
|
|
*bad_fmt = \*bad_ia32_fmt;
|
|
} else {
|
|
runtime_error( "Only works on lin_32 and lin_mic platforms." );
|
|
}; # if
|
|
|
|
# Do the work.
|
|
my $rc = 0;
|
|
if ( not @ARGV ) {
|
|
info( "No arguments specified -- nothing to do." );
|
|
} else {
|
|
foreach my $arg ( @ARGV ) {
|
|
my $errs = check_file( $arg, $show_instructions, $max_instructions );
|
|
if ( $errs > 0 ) {
|
|
$rc = 3;
|
|
}; # if
|
|
}; # foreach $arg
|
|
}; # if
|
|
|
|
exit( $rc );
|
|
|
|
__END__
|
|
|
|
=pod
|
|
|
|
=head1 NAME
|
|
|
|
B<check-instruction-set.pl> -- Make sure binary file does not contain undesired instructions.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
B<check-instructions.pl> I<option>... I<file>...
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over
|
|
|
|
=item B<--architecture=>I<arch>
|
|
|
|
Specify target architecture.
|
|
|
|
=item B<--max-instructions=>I<number>
|
|
|
|
Stop scanning if I<number> invalid instructions found. 100 by default.
|
|
|
|
=item B<--os=>I<os>
|
|
|
|
Specify target OS.
|
|
|
|
=item B<-->[B<no->]B<show-instructions>
|
|
|
|
Show invalid instructions found in the file. Bu default, instructions are not shown.
|
|
|
|
=item Standard Options
|
|
|
|
=over
|
|
|
|
=item B<--doc>
|
|
|
|
=item B<--manual>
|
|
|
|
Print full help message and exit.
|
|
|
|
=item B<--help>
|
|
|
|
Print short help message and exit.
|
|
|
|
=item B<--usage>
|
|
|
|
Print very short usage message and exit.
|
|
|
|
=item B<--verbose>
|
|
|
|
Do print informational messages.
|
|
|
|
=item B<--version>
|
|
|
|
Print program version and exit.
|
|
|
|
=item B<--quiet>
|
|
|
|
Work quiet, do not print informational messages.
|
|
|
|
=back
|
|
|
|
=back
|
|
|
|
=head1 ARGUMENTS
|
|
|
|
=over
|
|
|
|
=item I<file>
|
|
|
|
File (object file or library, either static or dynamic) to check.
|
|
|
|
=back
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
The script runs F<objdump> utility to get disassembler listing and checks the file does not contain
|
|
unwanted instructions.
|
|
|
|
Currently the script works only for:
|
|
|
|
=over
|
|
|
|
=item C<lin_mic>
|
|
|
|
Intel(R) Many Integrated Core Architecture target OS. Undesired unstructions are: all x87 instructions and some others.
|
|
|
|
=item C<lin_32>
|
|
|
|
Undesired instructions are instructions not valid for Pentium 3 processor (SSE2 and newer).
|
|
|
|
=back
|
|
|
|
=cut
|
|
|