在 32 位机器上编译的汇编代码是否与使用 "gcc -m32" 编译的代码不同?
Does assembler code compiled on a 32bit machine differ from code compiled with "gcc -m32"?
我现在正在学习汇编语言。我正在学习的书中的代码示例都是在 x86 32 位机器(我认为)上用 gcc 编译的。为了将我的输出与书中的输出相匹配,我用 "gcc -m32".
编译了我的简单 helloworld.c
#include <stdio.h>
int main(){
int i;
for(i=0;i<10;i++){
printf("Hello World!\n");
}
return 0;
}
我从 运行 "objdump -D a32.out | grep -A20 main.:" 得到的输出如下所示:
0000051d <main>:
51d: 8d 4c 24 04 lea 0x4(%esp),%ecx
521: 83 e4 f0 and [=12=]xfffffff0,%esp
524: ff 71 fc pushl -0x4(%ecx)
527: 55 push %ebp
528: 89 e5 mov %esp,%ebp
52a: 53 push %ebx
52b: 51 push %ecx
52c: 83 ec 10 sub [=12=]x10,%esp
52f: e8 ec fe ff ff call 420 <__x86.get_pc_thunk.bx>
534: 81 c3 cc 1a 00 00 add [=12=]x1acc,%ebx
53a: c7 45 f4 00 00 00 00 movl [=12=]x0,-0xc(%ebp)
541: eb 16 jmp 559 <main+0x3c>
543: 83 ec 0c sub [=12=]xc,%esp
546: 8d 83 f0 e5 ff ff lea -0x1a10(%ebx),%eax
54c: 50 push %eax
54d: e8 5e fe ff ff call 3b0 <puts@plt>
552: 83 c4 10 add [=12=]x10,%esp
555: 83 45 f4 01 addl [=12=]x1,-0xc(%ebp)
559: 83 7d f4 09 cmpl [=12=]x9,-0xc(%ebp)
55d: 7e e4 jle 543 <main+0x26>
但是在书中是这样的:
它错过了我得到的输出的前三行,除了接下来的两行外,它看起来一点也不像。这是为什么 ?我如何更改我的 compiler/disassembly 设置以使其看起来相似?
您的编译器默认配置为生成与位置无关的二进制文件,这导致与本书存在重大差异。 (这是最近的 GCC 开发)。
如果禁用此功能,您将更接近本书:
gcc -fno-pie -no-pie -m32 hello.c
正如其他人所说,除非您使用完全相同的配置构建完全相同的编译器,否则无法保证您将获得与书中内容相同的代码。
相同版本的 gcc 配置相同,运行 gcc -m32
,无论是 运行 在 x86-64 主机还是 i386 主机上都应该是相同的。或者从 ARM 主机交叉编译!
不同之处在于 gcc 版本和默认设置。 (@Employed Russian 指出,主要的是 -fpie
在您的 gcc 中默认打开。相关:32-bit absolute addresses no longer allowed in x86-64 Linux?)对于 32 位代码,PIE 默认打开也有点糟糕,不仅仅是 64,因为在 32 位模式下额外的开销要大得多(没有 RIP 相对寻址。)
除了像 -fno-pie
这样的配置之外,不同版本的 gcc 会产生不同的代码。
无论如何,如果你真的想匹配书中的内容,试试他们使用的相同版本的 gcc。 (希望他们提到这一点?如果没有,请查看它的出版年份以获得粗略的提示)。 您可以在 http://gcc.godbolt.org/.
上尝试每个 gcc 版本,最远可追溯到 4.4
这里是 Godbolt 上的 a link to your sample code,在两个单独的输出窗格中使用编译器 asm 输出和反汇编二进制文件,两者都禁用了优化。
请注意,Matt Godbolt 在没有 default-pie
配置的情况下配置 gcc,但您可以使用 -fpie
获取 asm,就像在桌面上一样。
请注意,对于未优化的代码,与启用优化相比,它更依赖于 gcc 的内部结构。也就是说,-O0
可以让您一窥 gcc "thinks about" 程序的内幕,因为它甚至不尝试优化。 (加上它 spills/reloads 在每个语句之间支持 changing 变量与调试器;这就是为什么它这么慢的部分原因:它故意将一个球和链条绑在你的代码上。所有这种额外的存储/重新加载使其更难阅读;我通常查看 -O2
或 -O3
输出。另请参阅 )
我现在正在学习汇编语言。我正在学习的书中的代码示例都是在 x86 32 位机器(我认为)上用 gcc 编译的。为了将我的输出与书中的输出相匹配,我用 "gcc -m32".
编译了我的简单 helloworld.c#include <stdio.h>
int main(){
int i;
for(i=0;i<10;i++){
printf("Hello World!\n");
}
return 0;
}
我从 运行 "objdump -D a32.out | grep -A20 main.:" 得到的输出如下所示:
0000051d <main>:
51d: 8d 4c 24 04 lea 0x4(%esp),%ecx
521: 83 e4 f0 and [=12=]xfffffff0,%esp
524: ff 71 fc pushl -0x4(%ecx)
527: 55 push %ebp
528: 89 e5 mov %esp,%ebp
52a: 53 push %ebx
52b: 51 push %ecx
52c: 83 ec 10 sub [=12=]x10,%esp
52f: e8 ec fe ff ff call 420 <__x86.get_pc_thunk.bx>
534: 81 c3 cc 1a 00 00 add [=12=]x1acc,%ebx
53a: c7 45 f4 00 00 00 00 movl [=12=]x0,-0xc(%ebp)
541: eb 16 jmp 559 <main+0x3c>
543: 83 ec 0c sub [=12=]xc,%esp
546: 8d 83 f0 e5 ff ff lea -0x1a10(%ebx),%eax
54c: 50 push %eax
54d: e8 5e fe ff ff call 3b0 <puts@plt>
552: 83 c4 10 add [=12=]x10,%esp
555: 83 45 f4 01 addl [=12=]x1,-0xc(%ebp)
559: 83 7d f4 09 cmpl [=12=]x9,-0xc(%ebp)
55d: 7e e4 jle 543 <main+0x26>
但是在书中是这样的:
它错过了我得到的输出的前三行,除了接下来的两行外,它看起来一点也不像。这是为什么 ?我如何更改我的 compiler/disassembly 设置以使其看起来相似?
您的编译器默认配置为生成与位置无关的二进制文件,这导致与本书存在重大差异。 (这是最近的 GCC 开发)。
如果禁用此功能,您将更接近本书:
gcc -fno-pie -no-pie -m32 hello.c
正如其他人所说,除非您使用完全相同的配置构建完全相同的编译器,否则无法保证您将获得与书中内容相同的代码。
相同版本的 gcc 配置相同,运行 gcc -m32
,无论是 运行 在 x86-64 主机还是 i386 主机上都应该是相同的。或者从 ARM 主机交叉编译!
不同之处在于 gcc 版本和默认设置。 (@Employed Russian 指出,主要的是 -fpie
在您的 gcc 中默认打开。相关:32-bit absolute addresses no longer allowed in x86-64 Linux?)对于 32 位代码,PIE 默认打开也有点糟糕,不仅仅是 64,因为在 32 位模式下额外的开销要大得多(没有 RIP 相对寻址。)
除了像 -fno-pie
这样的配置之外,不同版本的 gcc 会产生不同的代码。
无论如何,如果你真的想匹配书中的内容,试试他们使用的相同版本的 gcc。 (希望他们提到这一点?如果没有,请查看它的出版年份以获得粗略的提示)。 您可以在 http://gcc.godbolt.org/.
上尝试每个 gcc 版本,最远可追溯到 4.4这里是 Godbolt 上的 a link to your sample code,在两个单独的输出窗格中使用编译器 asm 输出和反汇编二进制文件,两者都禁用了优化。
请注意,Matt Godbolt 在没有 default-pie
配置的情况下配置 gcc,但您可以使用 -fpie
获取 asm,就像在桌面上一样。
请注意,对于未优化的代码,与启用优化相比,它更依赖于 gcc 的内部结构。也就是说,-O0
可以让您一窥 gcc "thinks about" 程序的内幕,因为它甚至不尝试优化。 (加上它 spills/reloads 在每个语句之间支持 changing 变量与调试器;这就是为什么它这么慢的部分原因:它故意将一个球和链条绑在你的代码上。所有这种额外的存储/重新加载使其更难阅读;我通常查看 -O2
或 -O3
输出。另请参阅