Intel C++ Compiler强奸AMD CPU的证据
2009-11-23 15:47
399 查看
Update 2010/01/04
http://www.osnews.com/story/22683/Intel_Forced_to_Remove_quot_Cripple_AMD_quot_Function_from_Compiler_
但我测试Intel C++的最新版本11.1.054时发现intel并没有修正该问题
------------------------------------------------------------------
反汇编Intel C++ 安装目录中的lib中的libirc.lib会得到如下输出:
00000000 <___intel_cpu_indicator_init>:
0: 50 push %eax
1: 52 push %edx
2: 51 push %ecx
3: 53 push %ebx
4: 57 push %edi
5: 56 push %esi
6: 55 push %ebp
7: 8b ec mov %esp,%ebp
9: 83 ec 50 sub $0x50,%esp
c: 9c pushf
d: 58 pop %eax
e: 8b c8 mov %eax,%ecx
10: 35 00 00 20 00 xor $0x200000,%eax
15: 50 push %eax
16: 9d popf
17: 9c pushf
18: 58 pop %eax
19: 3b c1 cmp %ecx,%eax
1b: 74 27 je 44 <___intel_cpu_indicator_init+0x44>
1d: 51 push %ecx
1e: 9d popf
1f: 33 c0 xor %eax,%eax
21: 0f a2 cpuid
23: 89 45 f8 mov %eax,-0x8(%ebp)
26: 89 5d fc mov %ebx,-0x4(%ebp)
29: 89 4d ec mov %ecx,-0x14(%ebp)
2c: 89 55 f4 mov %edx,-0xc(%ebp)
2f: b8 01 00 00 00 mov $0x1,%eax
34: 0f a2 cpuid
36: 89 45 f0 mov %eax,-0x10(%ebp)
39: 89 5d dc mov %ebx,-0x24(%ebp)
3c: 89 4d e4 mov %ecx,-0x1c(%ebp)
3f: 89 55 e8 mov %edx,-0x18(%ebp)
42: eb 1a jmp 5e <___intel_cpu_indicator_init+0x5e>
44: 33 c0 xor %eax,%eax
46: 89 45 f8 mov %eax,-0x8(%ebp)
49: 89 45 fc mov %eax,-0x4(%ebp)
4c: 89 45 ec mov %eax,-0x14(%ebp)
4f: 89 45 f4 mov %eax,-0xc(%ebp)
52: 89 45 f0 mov %eax,-0x10(%ebp)
55: 89 45 dc mov %eax,-0x24(%ebp)
58: 89 45 e4 mov %eax,-0x1c(%ebp)
5b: 89 45 e8 mov %eax,-0x18(%ebp)
5e: 81 7d fc 47 65 6e 75 cmpl $0x756e6547,-0x4(%ebp)
65: 75 19 jne 80 <___intel_cpu_indicator_init+0x80>
67: 81 7d f4 69 6e 65 49 cmpl $0x49656e69,-0xc(%ebp)
6e: 75 10 jne 80 <___intel_cpu_indicator_init+0x80>
70: 81 7d ec 6e 74 65 6c cmpl $0x6c65746e,-0x14(%ebp)
77: 75 07 jne 80 <___intel_cpu_indicator_init+0x80>
79: b8 01 00 00 00 mov $0x1,%eax
7e: eb 02 jmp 82 <___intel_cpu_indicator_init+0x82>
80: 33 c0 xor %eax,%eax
82: 83 7d f8 00 cmpl $0x0,-0x8(%ebp)
86: 0f 84 24 01 00 00 je 1b0 <___intel_cpu_indicator_init+0x1b0>
8c: 85 c0 test %eax,%eax
8e: 0f 84 1c 01 00 00 je 1b0 <___intel_cpu_indicator_init+0x1b0>
94: 0f b6 45 f1 movzbl -0xf(%ebp),%eax
98: 83 e0 0f and $0xf,%eax
9b: 83 f8 05 cmp $0x5,%eax
9e: 0f 84 f7 00 00 00 je 19b <___intel_cpu_indicator_init+0x19b>
a4: 83 f8 06 cmp $0x6,%eax
a7: 0f 85 c9 00 00 00 jne 176 <___intel_cpu_indicator_init+0x176>
ad: 8b 45 e8 mov -0x18(%ebp),%eax
b0: 8b c8 mov %eax,%ecx
b2: 81 e1 00 00 80 00 and $0x800000,%ecx
b8: c1 e9 16 shr $0x16,%ecx
bb: bb 04 00 00 00 mov $0x4,%ebx
c0: d3 e3 shl %cl,%ebx
c2: a9 00 00 00 01 test $0x1000000,%eax
c7: 74 7c je 145 <___intel_cpu_indicator_init+0x145>
c9: 8b c8 mov %eax,%ecx
cb: 81 e1 00 00 00 02 and $0x2000000,%ecx
d1: c1 e9 18 shr $0x18,%ecx
d4: bb 20 00 00 00 mov $0x20,%ebx
d9: d3 e3 shl %cl,%ebx
db: 8b 4d e4 mov -0x1c(%ebp),%ecx
de: a9 00 00 00 04 test $0x4000000,%eax
e3: 8b c1 mov %ecx,%eax
e5: 8b d1 mov %ecx,%edx
e7: 74 05 je ee <___intel_cpu_indicator_init+0xee>
e9: bb 00 04 00 00 mov $0x400,%ebx
ee: f6 c1 01 test $0x1,%cl
f1: 74 05 je f8 <___intel_cpu_indicator_init+0xf8>
f3: bb 00 08 00 00 mov $0x800,%ebx
f8: f7 c1 00 02 00 00 test $0x200,%ecx
fe: 74 05 je 105 <___intel_cpu_indicator_init+0x105>
100: bb 00 10 00 00 mov $0x1000,%ebx
105: f7 c1 00 00 40 00 test $0x400000,%ecx
10b: 74 05 je 112 <___intel_cpu_indicator_init+0x112>
10d: bb 00 40 00 00 mov $0x4000,%ebx
112: f7 c1 00 00 08 00 test $0x80000,%ecx
118: 74 05 je 11f <___intel_cpu_indicator_init+0x11f>
11a: bb 00 20 00 00 mov $0x2000,%ebx
11f: 25 00 00 90 00 and $0x900000,%eax
124: 3d 00 00 90 00 cmp $0x900000,%eax
129: 75 05 jne 130 <___intel_cpu_indicator_init+0x130>
12b: bb 00 80 00 00 mov $0x8000,%ebx
130: 81 e2 02 00 00 02 and $0x2000002,%edx
136: 81 fa 02 00 00 02 cmp $0x2000002,%edx
13c: 75 0a jne 148 <___intel_cpu_indicator_init+0x148>
13e: bb 00 00 01 00 mov $0x10000,%ebx
143: eb 03 jmp 148 <___intel_cpu_indicator_init+0x148>
145: 8b 4d e4 mov -0x1c(%ebp),%ecx
148: f7 c1 00 00 00 08 test $0x8000000,%ecx
14e: 74 65 je 1b5 <___intel_cpu_indicator_init+0x1b5>
150: b9 00 00 00 00 mov $0x0,%ecx
155: 0f 01 d0 xgetbv
158: 89 45 e0 mov %eax,-0x20(%ebp)
15b: f7 45 e4 00 00 00 10 testl $0x10000000,-0x1c(%ebp)
162: 74 51 je 1b5 <___intel_cpu_indicator_init+0x1b5>
164: 8b 45 e0 mov -0x20(%ebp),%eax
167: 83 e0 06 and $0x6,%eax
16a: 83 f8 06 cmp $0x6,%eax
16d: 75 46 jne 1b5 <___intel_cpu_indicator_init+0x1b5>
16f: bb 00 00 02 00 mov $0x20000,%ebx
174: eb 3f jmp 1b5 <___intel_cpu_indicator_init+0x1b5>
176: 83 f8 0f cmp $0xf,%eax
179: 75 35 jne 1b0 <___intel_cpu_indicator_init+0x1b0>
17b: bb 00 02 00 00 mov $0x200,%ebx
180: f7 45 e8 00 00 00 04 testl $0x4000000,-0x18(%ebp)
187: 75 05 jne 18e <___intel_cpu_indicator_init+0x18e>
189: bb 01 00 00 00 mov $0x1,%ebx
18e: f6 45 e4 01 testb $0x1,-0x1c(%ebp)
192: 74 21 je 1b5 <___intel_cpu_indicator_init+0x1b5>
194: bb 00 08 00 00 mov $0x800,%ebx
199: eb 1a jmp 1b5 <___intel_cpu_indicator_init+0x1b5>
19b: 8b 4d e8 mov -0x18(%ebp),%ecx
19e: 81 e1 00 00 80 00 and $0x800000,%ecx
1a4: c1 e9 16 shr $0x16,%ecx
1a7: bb 02 00 00 00 mov $0x2,%ebx
1ac: d3 e3 shl %cl,%ebx
1ae: eb 05 jmp 1b5 <___intel_cpu_indicator_init+0x1b5>
1b0: bb 01 00 00 00 mov $0x1,%ebx
1b5: 89 1d 00 00 00 00 mov %ebx,0x0
1bb: 83 c4 50 add $0x50,%esp
1be: 5d pop %ebp
1bf: 5e pop %esi
1c0: 5f pop %edi
1c1: 5b pop %ebx
1c2: 59 pop %ecx
1c3: 5a pop %edx
1c4: 58 pop %eax
1c5: c3 ret
1c6: 8d 76 00 lea 0x0(%esi),%esi
1c9: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi
---------------------------------------------------------------------------------
可以看出上面代码中先用将EAX寄存器清零,然后用cpuid指令来获得CPU的制造商信息
1f: 33 c0 xor %eax,%eax
21: 0f a2 cpuid
23: 89 45 f8 mov %eax,-0x8(%ebp)
26: 89 5d fc mov %ebx,-0x4(%ebp)
29: 89 4d ec mov %ecx,-0x14(%ebp)
2c: 89 55 f4 mov %edx,-0xc(%ebp)
然后将EAX寄存器赋值为1,用cpuid指令来获得该CPU的指令集以及版本信息:
2f: b8 01 00 00 00 mov $0x1,%eax
34: 0f a2 cpuid
36: 89 45 f0 mov %eax,-0x10(%ebp)
39: 89 5d dc mov %ebx,-0x24(%ebp)
3c: 89 4d e4 mov %ecx,-0x1c(%ebp)
3f: 89 55 e8 mov %edx,-0x18(%ebp)
接着Intel就开始为强奸AMD CPU做准备了,因为紧接着就是一个强制跳转指令:
42: eb 1a jmp 5e <___intel_cpu_indicator_init+0x5e>
将PC指针跳转到0x5e的位置:
5e: 81 7d fc 47 65 6e 75 cmpl $0x756e6547,-0x4(%ebp)
65: 75 19 jne 80 <___intel_cpu_indicator_init+0x80>
67: 81 7d f4 69 6e 65 49 cmpl $0x49656e69,-0xc(%ebp)
6e: 75 10 jne 80 <___intel_cpu_indicator_init+0x80>
70: 81 7d ec 6e 74 65 6c cmpl $0x6c65746e,-0x14(%ebp)
77: 75 07 jne 80 <___intel_cpu_indicator_init+0x80>
79: b8 01 00 00 00 mov $0x1,%eax
7e: eb 02 jmp 82 <___intel_cpu_indicator_init+0x82>
80: 33 c0 xor %eax,%eax
82: 83 7d f8 00 cmpl $0x0,-0x8(%ebp)
86: 0f 84 24 01 00 00 je 1b0 <___intel_cpu_indicator_init+0x1b0>
上面代码中的如下代码:
5e: 81 7d fc 47 65 6e 75 cmpl $0x756e6547,-0x4(%ebp)
...
67: 81 7d f4 69 6e 65 49 cmpl $0x49656e69,-0xc(%ebp)
...
70: 81 7d ec 6e 74 65 6c cmpl $0x6c65746e,-0x14(%ebp)
是Intel用来检查CPU的生产厂商是否是“GenuineIntel”(0x756e6547,0x49656e69,0x6c65746e,这三个4字节的数是GenuineIntel这12个字节的16进制表示)
如果不是就会强制跳转到0x80处,将EAX清零,对于Intel的自己的CPU则先将EAX置1后跳转到0x82
Intel C++ Compiler为什么要对EAX做如此不同的初始化呢?
也许是觉得上面的通过检查CPU制造商的强奸过程还不够爽!于是Intel C++ Compiler梅开二度,下面接着对AMD CPU实施了第二次强奸:
80: 33 c0 xor %eax,%eax
82: 83 7d f8 00 cmpl $0x0,-0x8(%ebp)
86: 0f 84 24 01 00 00 je 1b0 <___intel_cpu_indicator_init+0x1b0>
8c: 85 c0 test %eax,%eax
8e: 0f 84 1c 01 00 00 je 1b0 <___intel_cpu_indicator_init+0x1b0>
这次足够Intel C++ Compiler的强奸行为爽到底了,因为这次的(test %eax %eax)直接跳转到函数的返回处的清理stack的指令:
1b0: bb 01 00 00 00 mov $0x1,%ebx
1b5: 89 1d 00 00 00 00 mov %ebx,0x0
1bb: 83 c4 50 add $0x50,%esp
1be: 5d pop %ebp
1bf: 5e pop %esi
1c0: 5f pop %edi
1c1: 5b pop %ebx
1c2: 59 pop %ecx
1c3: 5a pop %edx
1c4: 58 pop %eax
1c5: c3 ret
解决方法是用相同长度的test指令来避开这一检查。
参见如下perl脚本: icc_hack:
#!/usr/bin/perl -w
#
# (C) Copyright M. D Mackey 2004.
# Modified by next@nextplayer.net,2009/11/23
# This program may be freely modified and redistributed.
#
# A short program to patch all library files from the Intel Fortran Compiler.
# Tested on compiler versions 7.1.040, 8.1.028 and 9.0.024, and should work on
# other builds.
#
# The patch removes the check for the string "GenuineIntel" in the
# CPUID flags of the processor, thus making SSE/SSE2 code work on AMD chips that
# support it.
#
# Creates a backup file before changing anything, so should be easy to undo if
# things go wrong.
#
# If you want to check for a successful patch, do
#
# objdump -d libirc.a.orig > old
# objdump -d libirc.a > new
#
# and examine the differences between 'old' and 'new'. Three lines
# should have changed in __intel_cpu_indicator_init:
#
# 81 fa 47 65 6e 75 cmp $0x756e6547,%edx
# ...
# 81 fa 69 6e 65 49 cmp $0x49656e69,%edx
# ...
# 81 fa 6e 74 65 6c cmp $0x6c65746e,%edx
#
# (these check for "GenuineIntel"). These lines should all be changed to
# f7 c2 00 00 00 00 testl $0x00000000,%edx
#
# Note that in some compiler versions the register might be %eax, %ebx or %ecx instead.
#
use strict;
# Get list of files
opendir(HERE,".");
my(@files)=grep {//.dll$/ or //.lib$/ } readdir(HERE);
closedir(HERE);
die "File 'libirc.lib' not found! This script must be run in the 'lib' directory of the Intel Fortran Compiler install!" if (! grep {/libirc.lib/} @files);
print STDERR "Patching compiler libraries.../n";
# Iterate over all files and patch. This is hacky but it works
my($file);
foreach $file (@files) {
open(FILE,$file) or die("Couldn't open file '$file': $!");
binmode(FILE);
my($contents)=join('',<FILE>); # Shlurp up file into one long binary string
close(FILE);
my($unp)=unpack('H*',$contents); # Unpack into a sequence of hex bytes
my($count);
# Try patching "cmp $0x756e6547,%eax" to "test $0x00000000,%eax" etc
$count+=($unp=~s/3d47656e75/a900000000/g);
$count+=($unp=~s/3d696e6549/a900000000/g);
$count+=($unp=~s/3d6e74656c/a900000000/g);
# Now try patching "cmp $0x756e6547,%ebx" to "testl $0x00000000,%ebx" etc
$count+=($unp=~s/81fb47656e75/f7c300000000/g);
$count+=($unp=~s/81fb696e6549/f7c300000000/g);
$count+=($unp=~s/81fb6e74656c/f7c300000000/g);
# Now try patching "cmp $0x756e6547,%ecx" to "testl $0x00000000,%ecx" etc
$count+=($unp=~s/81f947656e75/f7c100000000/g);
$count+=($unp=~s/81f9696e6549/f7c100000000/g);
$count+=($unp=~s/81f96e74656c/f7c100000000/g);
# Now try patching "cmp $0x756e6547,%edx" to "testl $0x00000000,%edx" etc
$count+=($unp=~s/81fa47656e75/f7c200000000/g);
$count+=($unp=~s/81fa696e6549/f7c200000000/g);
$count+=($unp=~s/81fa6e74656c/f7c200000000/g);
# Now try patching "cmp $0x756e6547,%ebp" to "testl $0x00000000,%ebp" etc
$count+=($unp=~s/81fd47656e75/f7c500000000/g);
$count+=($unp=~s/81fd696e6549/f7c500000000/g);
$count+=($unp=~s/81fd6e74656c/f7c500000000/g);
# Now try patching "cmp $0x756e6547,%esi" to "testl $0x00000000,%esi" etc
$count+=($unp=~s/81fe47656e75/f7c600000000/g);
$count+=($unp=~s/81fe696e6549/f7c600000000/g);
$count+=($unp=~s/81fe6e74656c/f7c600000000/g);
# Now try patching "cmp $0x756e6547,%edi" to "testl $0x00000000,%edi" etc
$count+=($unp=~s/81ff47656e75/f7c700000000/g);
$count+=($unp=~s/81ff696e6549/f7c700000000/g);
$count+=($unp=~s/81ff6e74656c/f7c700000000/g);
# Now try patching "cmpl $0x756e6547,-0x4(%ebp)" to "testl $0x00000000,-0x4(%ebp)" etc
# Added by next@nextplayer.net,2009/11/23
$count+=($unp=~s/817dfc47656e75/f745fc00000000/g);
$count+=($unp=~s/817df4696e6549/f745f400000000/g);
$count+=($unp=~s/817dec6e74656c/f745ec00000000/g);
next if (!$count); # Don't patch if no substitutions made
if ($count % 3) { # Number of substitutions must be a multiple of 3!
warn "WARNING: $count lines were to be patched in '$file': should be a multiple of 3! Skipping this file./n";
next;
}
print STDERR "Patching $file in $count places...";
open(OUTPUT,">$file.$$.tmp") or die("Can't create temporary output file '$file.$$.tmp':$!");
binmode(OUTPUT);
print OUTPUT pack('H*',$unp);
close(OUTPUT);
`chmod --reference="$file" "$file.$$.tmp"`; # chmod to original file's mode
if (! -f "$file.orig") {
rename($file,"$file.orig") or die("Couldn't rename '$file' to '$file.orig': $!/n");
}
rename("$file.$$.tmp",$file) or die("Couldn't rename '$file.$$.tmp' to '$file': $!/n");
print STDERR "Patch operation successful, original file at '$file.orig'/n";
}
exit(0);
sub help {
print STDERR <<EOUSAGE;
Some versions of the Intel Fortran Compiler produce code which checks whether
the CPU is made by Intel or not, and disables MMX/SSE/SSE2 code if it isn't.
This program patches the compiler libraries to remove this check, so that e.g.
programs compiled with -axW will use SSE2 code on Athlon 64 chips.
To run the patch, simply execute this script in the
<INSTALLDIR>/lib directory: the appropriate '.a' and '.so' files will be patched.
A backup copy of each file will be made first, so if it all goes wrong you won't
lose anthing.
This script was tested against ifc versions 7.1.040, 8.1.028 and 9.0.024 and should
work on other version 7 to 9 releases.
EOUSAGE
exit(1);
}
脚本执行后会输出类似如下信息:
$ ./icc_hack
Patching compiler libraries...
Patching libguide.lib in 6 places...Patch operation successful, original file at 'libguide.lib.orig'
Patching libguide40.dll in 6 places...Patch operation successful, original file at 'libguide40.dll.orig'
Patching libguide40_stats.dll in 6 places...Patch operation successful, original file at 'libguide40_stats.dll.orig'
Patching libguide_stats.lib in 6 places...Patch operation successful, original file at 'libguide_stats.lib.orig'
Patching libiomp5md.dll in 6 places...Patch operation successful, original file at 'libiomp5md.dll.orig'
Patching libiomp5mt.lib in 6 places...Patch operation successful, original file at 'libiomp5mt.lib.orig'
Patching libiompprof5md.dll in 6 places...Patch operation successful, original file at 'libiompprof5md.dll.orig'
Patching libiompprof5mt.lib in 6 places...Patch operation successful, original file at 'libiompprof5mt.lib.orig'
Patching libirc.lib in 3 places...Patch operation successful, original file at 'libirc.lib.orig'
Patching libircmt.lib in 3 places...Patch operation successful, original file at 'libircmt.lib.orig'
Patching libmmd.dll in 3 places...Patch operation successful, original file at 'libmmd.dll.orig'
Patching libmmdd.dll in 3 places...Patch operation successful, original file at 'libmmdd.dll.orig'
Patching pdbx.dll in 3 places...Patch operation successful, original file at 'pdbx.dll.orig'
Patching svml_dispmd.dll in 3 places...Patch operation successful, original file at 'svml_dispmd.dll.orig'
http://www.osnews.com/story/22683/Intel_Forced_to_Remove_quot_Cripple_AMD_quot_Function_from_Compiler_
但我测试Intel C++的最新版本11.1.054时发现intel并没有修正该问题
------------------------------------------------------------------
反汇编Intel C++ 安装目录中的lib中的libirc.lib会得到如下输出:
00000000 <___intel_cpu_indicator_init>:
0: 50 push %eax
1: 52 push %edx
2: 51 push %ecx
3: 53 push %ebx
4: 57 push %edi
5: 56 push %esi
6: 55 push %ebp
7: 8b ec mov %esp,%ebp
9: 83 ec 50 sub $0x50,%esp
c: 9c pushf
d: 58 pop %eax
e: 8b c8 mov %eax,%ecx
10: 35 00 00 20 00 xor $0x200000,%eax
15: 50 push %eax
16: 9d popf
17: 9c pushf
18: 58 pop %eax
19: 3b c1 cmp %ecx,%eax
1b: 74 27 je 44 <___intel_cpu_indicator_init+0x44>
1d: 51 push %ecx
1e: 9d popf
1f: 33 c0 xor %eax,%eax
21: 0f a2 cpuid
23: 89 45 f8 mov %eax,-0x8(%ebp)
26: 89 5d fc mov %ebx,-0x4(%ebp)
29: 89 4d ec mov %ecx,-0x14(%ebp)
2c: 89 55 f4 mov %edx,-0xc(%ebp)
2f: b8 01 00 00 00 mov $0x1,%eax
34: 0f a2 cpuid
36: 89 45 f0 mov %eax,-0x10(%ebp)
39: 89 5d dc mov %ebx,-0x24(%ebp)
3c: 89 4d e4 mov %ecx,-0x1c(%ebp)
3f: 89 55 e8 mov %edx,-0x18(%ebp)
42: eb 1a jmp 5e <___intel_cpu_indicator_init+0x5e>
44: 33 c0 xor %eax,%eax
46: 89 45 f8 mov %eax,-0x8(%ebp)
49: 89 45 fc mov %eax,-0x4(%ebp)
4c: 89 45 ec mov %eax,-0x14(%ebp)
4f: 89 45 f4 mov %eax,-0xc(%ebp)
52: 89 45 f0 mov %eax,-0x10(%ebp)
55: 89 45 dc mov %eax,-0x24(%ebp)
58: 89 45 e4 mov %eax,-0x1c(%ebp)
5b: 89 45 e8 mov %eax,-0x18(%ebp)
5e: 81 7d fc 47 65 6e 75 cmpl $0x756e6547,-0x4(%ebp)
65: 75 19 jne 80 <___intel_cpu_indicator_init+0x80>
67: 81 7d f4 69 6e 65 49 cmpl $0x49656e69,-0xc(%ebp)
6e: 75 10 jne 80 <___intel_cpu_indicator_init+0x80>
70: 81 7d ec 6e 74 65 6c cmpl $0x6c65746e,-0x14(%ebp)
77: 75 07 jne 80 <___intel_cpu_indicator_init+0x80>
79: b8 01 00 00 00 mov $0x1,%eax
7e: eb 02 jmp 82 <___intel_cpu_indicator_init+0x82>
80: 33 c0 xor %eax,%eax
82: 83 7d f8 00 cmpl $0x0,-0x8(%ebp)
86: 0f 84 24 01 00 00 je 1b0 <___intel_cpu_indicator_init+0x1b0>
8c: 85 c0 test %eax,%eax
8e: 0f 84 1c 01 00 00 je 1b0 <___intel_cpu_indicator_init+0x1b0>
94: 0f b6 45 f1 movzbl -0xf(%ebp),%eax
98: 83 e0 0f and $0xf,%eax
9b: 83 f8 05 cmp $0x5,%eax
9e: 0f 84 f7 00 00 00 je 19b <___intel_cpu_indicator_init+0x19b>
a4: 83 f8 06 cmp $0x6,%eax
a7: 0f 85 c9 00 00 00 jne 176 <___intel_cpu_indicator_init+0x176>
ad: 8b 45 e8 mov -0x18(%ebp),%eax
b0: 8b c8 mov %eax,%ecx
b2: 81 e1 00 00 80 00 and $0x800000,%ecx
b8: c1 e9 16 shr $0x16,%ecx
bb: bb 04 00 00 00 mov $0x4,%ebx
c0: d3 e3 shl %cl,%ebx
c2: a9 00 00 00 01 test $0x1000000,%eax
c7: 74 7c je 145 <___intel_cpu_indicator_init+0x145>
c9: 8b c8 mov %eax,%ecx
cb: 81 e1 00 00 00 02 and $0x2000000,%ecx
d1: c1 e9 18 shr $0x18,%ecx
d4: bb 20 00 00 00 mov $0x20,%ebx
d9: d3 e3 shl %cl,%ebx
db: 8b 4d e4 mov -0x1c(%ebp),%ecx
de: a9 00 00 00 04 test $0x4000000,%eax
e3: 8b c1 mov %ecx,%eax
e5: 8b d1 mov %ecx,%edx
e7: 74 05 je ee <___intel_cpu_indicator_init+0xee>
e9: bb 00 04 00 00 mov $0x400,%ebx
ee: f6 c1 01 test $0x1,%cl
f1: 74 05 je f8 <___intel_cpu_indicator_init+0xf8>
f3: bb 00 08 00 00 mov $0x800,%ebx
f8: f7 c1 00 02 00 00 test $0x200,%ecx
fe: 74 05 je 105 <___intel_cpu_indicator_init+0x105>
100: bb 00 10 00 00 mov $0x1000,%ebx
105: f7 c1 00 00 40 00 test $0x400000,%ecx
10b: 74 05 je 112 <___intel_cpu_indicator_init+0x112>
10d: bb 00 40 00 00 mov $0x4000,%ebx
112: f7 c1 00 00 08 00 test $0x80000,%ecx
118: 74 05 je 11f <___intel_cpu_indicator_init+0x11f>
11a: bb 00 20 00 00 mov $0x2000,%ebx
11f: 25 00 00 90 00 and $0x900000,%eax
124: 3d 00 00 90 00 cmp $0x900000,%eax
129: 75 05 jne 130 <___intel_cpu_indicator_init+0x130>
12b: bb 00 80 00 00 mov $0x8000,%ebx
130: 81 e2 02 00 00 02 and $0x2000002,%edx
136: 81 fa 02 00 00 02 cmp $0x2000002,%edx
13c: 75 0a jne 148 <___intel_cpu_indicator_init+0x148>
13e: bb 00 00 01 00 mov $0x10000,%ebx
143: eb 03 jmp 148 <___intel_cpu_indicator_init+0x148>
145: 8b 4d e4 mov -0x1c(%ebp),%ecx
148: f7 c1 00 00 00 08 test $0x8000000,%ecx
14e: 74 65 je 1b5 <___intel_cpu_indicator_init+0x1b5>
150: b9 00 00 00 00 mov $0x0,%ecx
155: 0f 01 d0 xgetbv
158: 89 45 e0 mov %eax,-0x20(%ebp)
15b: f7 45 e4 00 00 00 10 testl $0x10000000,-0x1c(%ebp)
162: 74 51 je 1b5 <___intel_cpu_indicator_init+0x1b5>
164: 8b 45 e0 mov -0x20(%ebp),%eax
167: 83 e0 06 and $0x6,%eax
16a: 83 f8 06 cmp $0x6,%eax
16d: 75 46 jne 1b5 <___intel_cpu_indicator_init+0x1b5>
16f: bb 00 00 02 00 mov $0x20000,%ebx
174: eb 3f jmp 1b5 <___intel_cpu_indicator_init+0x1b5>
176: 83 f8 0f cmp $0xf,%eax
179: 75 35 jne 1b0 <___intel_cpu_indicator_init+0x1b0>
17b: bb 00 02 00 00 mov $0x200,%ebx
180: f7 45 e8 00 00 00 04 testl $0x4000000,-0x18(%ebp)
187: 75 05 jne 18e <___intel_cpu_indicator_init+0x18e>
189: bb 01 00 00 00 mov $0x1,%ebx
18e: f6 45 e4 01 testb $0x1,-0x1c(%ebp)
192: 74 21 je 1b5 <___intel_cpu_indicator_init+0x1b5>
194: bb 00 08 00 00 mov $0x800,%ebx
199: eb 1a jmp 1b5 <___intel_cpu_indicator_init+0x1b5>
19b: 8b 4d e8 mov -0x18(%ebp),%ecx
19e: 81 e1 00 00 80 00 and $0x800000,%ecx
1a4: c1 e9 16 shr $0x16,%ecx
1a7: bb 02 00 00 00 mov $0x2,%ebx
1ac: d3 e3 shl %cl,%ebx
1ae: eb 05 jmp 1b5 <___intel_cpu_indicator_init+0x1b5>
1b0: bb 01 00 00 00 mov $0x1,%ebx
1b5: 89 1d 00 00 00 00 mov %ebx,0x0
1bb: 83 c4 50 add $0x50,%esp
1be: 5d pop %ebp
1bf: 5e pop %esi
1c0: 5f pop %edi
1c1: 5b pop %ebx
1c2: 59 pop %ecx
1c3: 5a pop %edx
1c4: 58 pop %eax
1c5: c3 ret
1c6: 8d 76 00 lea 0x0(%esi),%esi
1c9: 8d bc 27 00 00 00 00 lea 0x0(%edi,%eiz,1),%edi
---------------------------------------------------------------------------------
可以看出上面代码中先用将EAX寄存器清零,然后用cpuid指令来获得CPU的制造商信息
1f: 33 c0 xor %eax,%eax
21: 0f a2 cpuid
23: 89 45 f8 mov %eax,-0x8(%ebp)
26: 89 5d fc mov %ebx,-0x4(%ebp)
29: 89 4d ec mov %ecx,-0x14(%ebp)
2c: 89 55 f4 mov %edx,-0xc(%ebp)
然后将EAX寄存器赋值为1,用cpuid指令来获得该CPU的指令集以及版本信息:
2f: b8 01 00 00 00 mov $0x1,%eax
34: 0f a2 cpuid
36: 89 45 f0 mov %eax,-0x10(%ebp)
39: 89 5d dc mov %ebx,-0x24(%ebp)
3c: 89 4d e4 mov %ecx,-0x1c(%ebp)
3f: 89 55 e8 mov %edx,-0x18(%ebp)
接着Intel就开始为强奸AMD CPU做准备了,因为紧接着就是一个强制跳转指令:
42: eb 1a jmp 5e <___intel_cpu_indicator_init+0x5e>
将PC指针跳转到0x5e的位置:
5e: 81 7d fc 47 65 6e 75 cmpl $0x756e6547,-0x4(%ebp)
65: 75 19 jne 80 <___intel_cpu_indicator_init+0x80>
67: 81 7d f4 69 6e 65 49 cmpl $0x49656e69,-0xc(%ebp)
6e: 75 10 jne 80 <___intel_cpu_indicator_init+0x80>
70: 81 7d ec 6e 74 65 6c cmpl $0x6c65746e,-0x14(%ebp)
77: 75 07 jne 80 <___intel_cpu_indicator_init+0x80>
79: b8 01 00 00 00 mov $0x1,%eax
7e: eb 02 jmp 82 <___intel_cpu_indicator_init+0x82>
80: 33 c0 xor %eax,%eax
82: 83 7d f8 00 cmpl $0x0,-0x8(%ebp)
86: 0f 84 24 01 00 00 je 1b0 <___intel_cpu_indicator_init+0x1b0>
上面代码中的如下代码:
5e: 81 7d fc 47 65 6e 75 cmpl $0x756e6547,-0x4(%ebp)
...
67: 81 7d f4 69 6e 65 49 cmpl $0x49656e69,-0xc(%ebp)
...
70: 81 7d ec 6e 74 65 6c cmpl $0x6c65746e,-0x14(%ebp)
是Intel用来检查CPU的生产厂商是否是“GenuineIntel”(0x756e6547,0x49656e69,0x6c65746e,这三个4字节的数是GenuineIntel这12个字节的16进制表示)
如果不是就会强制跳转到0x80处,将EAX清零,对于Intel的自己的CPU则先将EAX置1后跳转到0x82
Intel C++ Compiler为什么要对EAX做如此不同的初始化呢?
也许是觉得上面的通过检查CPU制造商的强奸过程还不够爽!于是Intel C++ Compiler梅开二度,下面接着对AMD CPU实施了第二次强奸:
80: 33 c0 xor %eax,%eax
82: 83 7d f8 00 cmpl $0x0,-0x8(%ebp)
86: 0f 84 24 01 00 00 je 1b0 <___intel_cpu_indicator_init+0x1b0>
8c: 85 c0 test %eax,%eax
8e: 0f 84 1c 01 00 00 je 1b0 <___intel_cpu_indicator_init+0x1b0>
这次足够Intel C++ Compiler的强奸行为爽到底了,因为这次的(test %eax %eax)直接跳转到函数的返回处的清理stack的指令:
1b0: bb 01 00 00 00 mov $0x1,%ebx
1b5: 89 1d 00 00 00 00 mov %ebx,0x0
1bb: 83 c4 50 add $0x50,%esp
1be: 5d pop %ebp
1bf: 5e pop %esi
1c0: 5f pop %edi
1c1: 5b pop %ebx
1c2: 59 pop %ecx
1c3: 5a pop %edx
1c4: 58 pop %eax
1c5: c3 ret
解决方法是用相同长度的test指令来避开这一检查。
参见如下perl脚本: icc_hack:
#!/usr/bin/perl -w
#
# (C) Copyright M. D Mackey 2004.
# Modified by next@nextplayer.net,2009/11/23
# This program may be freely modified and redistributed.
#
# A short program to patch all library files from the Intel Fortran Compiler.
# Tested on compiler versions 7.1.040, 8.1.028 and 9.0.024, and should work on
# other builds.
#
# The patch removes the check for the string "GenuineIntel" in the
# CPUID flags of the processor, thus making SSE/SSE2 code work on AMD chips that
# support it.
#
# Creates a backup file before changing anything, so should be easy to undo if
# things go wrong.
#
# If you want to check for a successful patch, do
#
# objdump -d libirc.a.orig > old
# objdump -d libirc.a > new
#
# and examine the differences between 'old' and 'new'. Three lines
# should have changed in __intel_cpu_indicator_init:
#
# 81 fa 47 65 6e 75 cmp $0x756e6547,%edx
# ...
# 81 fa 69 6e 65 49 cmp $0x49656e69,%edx
# ...
# 81 fa 6e 74 65 6c cmp $0x6c65746e,%edx
#
# (these check for "GenuineIntel"). These lines should all be changed to
# f7 c2 00 00 00 00 testl $0x00000000,%edx
#
# Note that in some compiler versions the register might be %eax, %ebx or %ecx instead.
#
use strict;
# Get list of files
opendir(HERE,".");
my(@files)=grep {//.dll$/ or //.lib$/ } readdir(HERE);
closedir(HERE);
die "File 'libirc.lib' not found! This script must be run in the 'lib' directory of the Intel Fortran Compiler install!" if (! grep {/libirc.lib/} @files);
print STDERR "Patching compiler libraries.../n";
# Iterate over all files and patch. This is hacky but it works
my($file);
foreach $file (@files) {
open(FILE,$file) or die("Couldn't open file '$file': $!");
binmode(FILE);
my($contents)=join('',<FILE>); # Shlurp up file into one long binary string
close(FILE);
my($unp)=unpack('H*',$contents); # Unpack into a sequence of hex bytes
my($count);
# Try patching "cmp $0x756e6547,%eax" to "test $0x00000000,%eax" etc
$count+=($unp=~s/3d47656e75/a900000000/g);
$count+=($unp=~s/3d696e6549/a900000000/g);
$count+=($unp=~s/3d6e74656c/a900000000/g);
# Now try patching "cmp $0x756e6547,%ebx" to "testl $0x00000000,%ebx" etc
$count+=($unp=~s/81fb47656e75/f7c300000000/g);
$count+=($unp=~s/81fb696e6549/f7c300000000/g);
$count+=($unp=~s/81fb6e74656c/f7c300000000/g);
# Now try patching "cmp $0x756e6547,%ecx" to "testl $0x00000000,%ecx" etc
$count+=($unp=~s/81f947656e75/f7c100000000/g);
$count+=($unp=~s/81f9696e6549/f7c100000000/g);
$count+=($unp=~s/81f96e74656c/f7c100000000/g);
# Now try patching "cmp $0x756e6547,%edx" to "testl $0x00000000,%edx" etc
$count+=($unp=~s/81fa47656e75/f7c200000000/g);
$count+=($unp=~s/81fa696e6549/f7c200000000/g);
$count+=($unp=~s/81fa6e74656c/f7c200000000/g);
# Now try patching "cmp $0x756e6547,%ebp" to "testl $0x00000000,%ebp" etc
$count+=($unp=~s/81fd47656e75/f7c500000000/g);
$count+=($unp=~s/81fd696e6549/f7c500000000/g);
$count+=($unp=~s/81fd6e74656c/f7c500000000/g);
# Now try patching "cmp $0x756e6547,%esi" to "testl $0x00000000,%esi" etc
$count+=($unp=~s/81fe47656e75/f7c600000000/g);
$count+=($unp=~s/81fe696e6549/f7c600000000/g);
$count+=($unp=~s/81fe6e74656c/f7c600000000/g);
# Now try patching "cmp $0x756e6547,%edi" to "testl $0x00000000,%edi" etc
$count+=($unp=~s/81ff47656e75/f7c700000000/g);
$count+=($unp=~s/81ff696e6549/f7c700000000/g);
$count+=($unp=~s/81ff6e74656c/f7c700000000/g);
# Now try patching "cmpl $0x756e6547,-0x4(%ebp)" to "testl $0x00000000,-0x4(%ebp)" etc
# Added by next@nextplayer.net,2009/11/23
$count+=($unp=~s/817dfc47656e75/f745fc00000000/g);
$count+=($unp=~s/817df4696e6549/f745f400000000/g);
$count+=($unp=~s/817dec6e74656c/f745ec00000000/g);
next if (!$count); # Don't patch if no substitutions made
if ($count % 3) { # Number of substitutions must be a multiple of 3!
warn "WARNING: $count lines were to be patched in '$file': should be a multiple of 3! Skipping this file./n";
next;
}
print STDERR "Patching $file in $count places...";
open(OUTPUT,">$file.$$.tmp") or die("Can't create temporary output file '$file.$$.tmp':$!");
binmode(OUTPUT);
print OUTPUT pack('H*',$unp);
close(OUTPUT);
`chmod --reference="$file" "$file.$$.tmp"`; # chmod to original file's mode
if (! -f "$file.orig") {
rename($file,"$file.orig") or die("Couldn't rename '$file' to '$file.orig': $!/n");
}
rename("$file.$$.tmp",$file) or die("Couldn't rename '$file.$$.tmp' to '$file': $!/n");
print STDERR "Patch operation successful, original file at '$file.orig'/n";
}
exit(0);
sub help {
print STDERR <<EOUSAGE;
Some versions of the Intel Fortran Compiler produce code which checks whether
the CPU is made by Intel or not, and disables MMX/SSE/SSE2 code if it isn't.
This program patches the compiler libraries to remove this check, so that e.g.
programs compiled with -axW will use SSE2 code on Athlon 64 chips.
To run the patch, simply execute this script in the
<INSTALLDIR>/lib directory: the appropriate '.a' and '.so' files will be patched.
A backup copy of each file will be made first, so if it all goes wrong you won't
lose anthing.
This script was tested against ifc versions 7.1.040, 8.1.028 and 9.0.024 and should
work on other version 7 to 9 releases.
EOUSAGE
exit(1);
}
脚本执行后会输出类似如下信息:
$ ./icc_hack
Patching compiler libraries...
Patching libguide.lib in 6 places...Patch operation successful, original file at 'libguide.lib.orig'
Patching libguide40.dll in 6 places...Patch operation successful, original file at 'libguide40.dll.orig'
Patching libguide40_stats.dll in 6 places...Patch operation successful, original file at 'libguide40_stats.dll.orig'
Patching libguide_stats.lib in 6 places...Patch operation successful, original file at 'libguide_stats.lib.orig'
Patching libiomp5md.dll in 6 places...Patch operation successful, original file at 'libiomp5md.dll.orig'
Patching libiomp5mt.lib in 6 places...Patch operation successful, original file at 'libiomp5mt.lib.orig'
Patching libiompprof5md.dll in 6 places...Patch operation successful, original file at 'libiompprof5md.dll.orig'
Patching libiompprof5mt.lib in 6 places...Patch operation successful, original file at 'libiompprof5mt.lib.orig'
Patching libirc.lib in 3 places...Patch operation successful, original file at 'libirc.lib.orig'
Patching libircmt.lib in 3 places...Patch operation successful, original file at 'libircmt.lib.orig'
Patching libmmd.dll in 3 places...Patch operation successful, original file at 'libmmd.dll.orig'
Patching libmmdd.dll in 3 places...Patch operation successful, original file at 'libmmdd.dll.orig'
Patching pdbx.dll in 3 places...Patch operation successful, original file at 'pdbx.dll.orig'
Patching svml_dispmd.dll in 3 places...Patch operation successful, original file at 'svml_dispmd.dll.orig'
相关文章推荐
- Intel, AMD及VIA CPU的微架构(11)
- 27款Intel和AMD的CPU实用性能定量比较
- Intel, AMD及VIA CPU的微架构(6)
- 安卓模拟器里面的cpu/abi里面有AMD、intel x86、mlps应该选择哪个
- [转]Use Intel C++ Compiler in Eclipse (CDT) under Win32
- amd cpu安装osx 10.9.2(同样适应intel构架)
- intel和amd的cpu核心详细介绍
- intel和AMD CPU性能对比(2016年CPU天梯图)组装电脑必读!
- 换CPU的品牌(比如INTEL换成AMD,当然连带主板)肯定要重装系统
- Intel 和 AMD CPU的基准测试表
- Intel C++ Compiler v9.1.030
- Intel、AMD和ARM混战 CPU上演三国演义
- AMD intel CPU 型号对比
- Intel系列开发工具- C++ Compiler,Fortran Compiler,IPP,MKL,VTune,Thread Checker
- 安装 Intel C++ Compiler 12 以后,VC6 不能编译的问题。
- Intel, AMD及VIA CPU的微架构(13)
- 购买CPU选择AMD还是Intel?
- 卸载 Intel C++ Compiler 后 Visual C++ 6.0 不能工作了
- INTEL C/C++ COMPILER
- 用Intel C++ Compiler 9.1编译STLPort5.1