您的位置:首页 > 编程语言 > C语言/C++

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'
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: