我正在教自己调试汇编语言;我是集会新手。我有一个非常简单的C ++程序,我使用不同的反汇编程序将它拆解了3次:GDB,otool和godbolt.org。 GDB和godbolt.org产生了大约相同数量的代码(文字处理器中的1页),尽管许多行不同。 otool -tv命令生成了大约14页的代码,因此在GDB和godbolt.org输出方面存在许多差异。汇编代码太长,无法发布。我期待汇编代码输出彼此相同。为什么它们不同,哪种反汇编最好?
这是我的C ++程序:
#include <iostream>
int main () {
int a = 1;
int b = 2;
int c = 3;
a += b;
a = a + c;
std::cout << "Value of A is " << a << std::endl;
return 0;
}
汇编差异的一个例子:
GDB:
0x0000000100000f44 <+4>: sub $0x30,%rsp
0x0000000100000f48 <+8>: mov 0x10c1(%rip),%rdi # 0x100002010
0x0000000100000f4f <+15>: lea 0xfb6(%rip),%rsi
Godbolt.org:
sub rsp, 16
mov DWORD PTR [rbp-4], 1
mov DWORD PTR [rbp-8], 2
Otool -tv比其他代码提供了13页代码,因此存在明显的差异。
答案 0 :(得分:2)
您遇到的差异不在反汇编程序中,而是在用于表示机器指令的语法中。
汇编是一种非常低级的语言,其中机器指令和助记符之间存在一对一的映射。前者是位序列,可能是可变长度的 - 就像x86架构的情况一样。该表示由CPU直接解释以执行与指令的语义相关联的工作。汇编语言是一个&#34; 人类可读&#34;这种序列的表示。
基本上,您可以找到任何方式来表示相同的机器指令。这是汇编语法。
众所周知,对于x86架构,存在两种不同的语法: AT&amp; T 和 Intel 。您从GBD获得的输出是根据AT&amp; T语法生成的,而您从Godbolt.org获得的输出是 Intel 。
英特尔和AT&amp; T语法在外观上彼此非常不同,这可能就是为什么你一直认为结果不一样的原因。实际上,它只是表示完全相同的指令的另一种方式。
这两个&#34; 方言&#34;对于同一建筑的装配而言,出生时考虑了不同的目标。 AT&amp; T语法是在AT&amp; T实验室开发的,用于支持为许多不同的CPU生成程序(参见书籍Jeff Duntermann, Assembly Language Step-by-Step)。当时,AT&amp; T在计算机史上扮演着重要角色。 AT&amp; T(贝尔实验室)一直是Unix的源头 - 它的范例目前(虽然部分地)致力于Linux - C编程语言,以及我们今天继续使用的许多其他基础工具。
另一方面,英特尔的语法已由英特尔开发,用于他们自己的CPU。许多英特尔语法的采用者都表示,在英特尔CPU上进行prorgamming时它更加整洁。情况可能就是这样,因为语法已经精心设计为CPU支持的内容。
虽然现在不再使用AT&amp; T语法(至少,据我所知)为x86以外的CPU编写程序,但有些&#34;罪魁祸首&#34;语法的生成源于更多&#34; general&#34;。
那么,哪一个要学习?我的选择将取决于您所处理的环境。整个Unix生态系统(包括Linux和Mac Os)都有一个直接使用该语法的工具链(例如gas
)。在Linux内核(以及其他低级软件)中,您肯定会发现AT&amp; T语法的内联汇编代码与硬件交互。另一方面,Windows系统具有说英特尔语法的工具链(例如nasm
)。虽然编译时标志可以要求这些工具切换到其他语法(例如-M
的{{1}}标志),但习惯是采用&#34; native&#34;语法。
关于问题中给出的具体示例,它们是“不兼容的”,因为它们指的是反汇编代码的不同部分,因此两者之间存在更高程度的差异。 。 实际上,关于这个GDB输出:
objdump
相应的英特尔反汇编将是:
sub $0x30, %rsp
mov 0x10c1(%rip), %rdi
lea 0xfb6(%rip), %rsi
另一方面,关于Godbolt.org输出:
sub rsp, 0x30
mov rdi, QWORD PTR [rip+0x10c1]
lea rsi, [rip+0xfb6]
相应的AT&amp; T反汇编将是:
sub rsp, 16
mov DWORD PTR [rbp-4], 1
mov DWORD PTR [rbp-8], 2
正如您所看到的,最大的区别可能会引起很多麻烦,这与AT&amp; T语法首先放置源然后放置目标这一事实有关,而英特尔语法则相反。< / p>
答案 1 :(得分:1)
汇编序列不是具有不同语法的等价物,它们只是不同,可能是由于使用了不同的编译器。
第一对:
sub $0x30,%rsp ;rsp -= 0x30
sub rsp,16 ;rsp -= 0x10
下一对:
mov 0x10c1(%rip),%rdi ;rdi = [rip+0x10c1] (loads a value)
mov DWORD PTR [rbp-4],1 ;[rbp+4] = 1 (stores an immediate value)
下一对:
lea 0xfb6(%rip),%rsi ;rsi = rip+0xfb6 (loads an offset)
mov DWORD PTR [rbp-8],2 ;[rbp+8] = 2 (stores an immediate value)
这两个序列都不完整,但我认为这并不重要,因为显示的序列已经显示出差异。
答案 2 :(得分:-5)
因为源代码和程序集之间没有1对1的关系。编译器可能会为以下语句生成相同的程序集:
x = x + 1
和
x++;
这两个都会被编译成类似
的东西add dword ptr [rdi], 1
那么,当我们拆开它时,哪一个应该被拆解成? x = x+1
或x++
?这几乎适用于程序的每个语句 - 如果有多种方式表达源语言中发生的事情,并且效果相同,编译器可能会选择将它们转换为相同的输出。之后,你无法知道使用了哪一个。