作为一名小白,在WSL中做一个操作系统小实验的时候,需要查看编译后代码的汇编实现,于是使用objdump
工具直接反汇编。发现跟以前学过的汇编有些不一样(学过8086汇编),上网查询Intel x86汇编的格式(因为我的机器是Intel的CPU),发现得到的汇编格式于Intel标准的汇编格式有些不一样,如下(部分):
1 | 0000000000001203 <do_sum>: |
主要是对于第一个操作数和第二个操作数顺序的疑惑,以一个mov
命令为例:mov A, B
。
这里的A、B只是表示一个操作数的符号,用于代替一个寄存器or一个地址
在Intel标准中,mov
的第一个操作数A是目的操作数,第二个操作数B是源操作数,即这条汇编的含义是,把B的值赋值给A。
如果按照这种标准,我上面反汇编得到的第二条mov指令(mov $0x5f5e100,%eax
)就是把eax
寄存器的值赋值给$0x5f5e100
,但是这个16进制的值是我定义的一个循环次数n(100,000,000,也就是一亿),于是感觉有问题。
经过大佬的指点,在Windows中使用IDA软件对这个程序进行反汇编,得到如下结果:
1 | .text:0000000000001203 public do_sum |
可以很明显的观察到,与objdump的反汇编结果的样子有较大区别,对比第二条mov指令,发现两个操作数的顺序反了过来,对头!(这熟悉的味道,这才是Intel的汇编嘛)这种的汇编按照Intel的解释也很合理(将循环次数赋值给eax,然后不断减一判断循环)。
于是发现问题所在,可以确定objdump所得到的汇编肯定不是Intel格式的,经过进一步的搜索,发现objdump默认的汇编格式是AT&T。并且知道了objdump可以通过-M
参数来修改反汇编的格式(具体请man objdump
),于是使用objudmp -d test -M intel | less
(其中test是我需要反汇编的文件名)命令得到如下汇编结果(部分):
1 | 0000000000001203 <do_sum>: |
基本与IDA的结果一致(差异可忽略)。
总结一下本次经历学到的一些知识:
x86架构汇编指令一般有两种格式:Intel汇编和AT&T汇编,DOS、Windows使用Intel汇编,而Unix、Linux、MacOS使用AT&T汇编。
下面简单列出几个Intel和AT&T汇编格式的区别:
- 第一当然是两个操作数的顺序啦:Intel的第一个操作数是目标操作数,第二个操作数是源操作数;AT&T的第一个操作数是源操作数,第二个操作数是目标操作数。
- 寄存器的表示:Intel的寄存器直接写寄存器的名字就行(eax);AT&T的寄存器需要在前面加一个百分号%修饰(%eax)。
- 立即数表示:Intel的立即数前不用加任何标志(1);AT&T的立即数前需要加
$
符号修饰($1)。 括号的使用:Intel中寻址时用的括号是中括号
[]
;AT&T中使用的是小括号()
。……