| #!/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 fuction. |
| 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 |
| |