为什么汇编代码因我使用的反汇编程序而异
Why does assembly code differ depending on the disassembler I use?
我正在自学调试汇编语言;我是组装新手。我有一个非常简单的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 页,因此存在明显的差异。
您遇到的差异不在于反汇编程序,而在于用于表示机器指令的语法。
汇编是一种非常低级的语言,其中机器指令和助记符之间存在一对一的映射。前者是位序列,可能是可变长度的---就像 x86 架构的情况一样。这种表示由CPU直接解释,以执行与指令语义相关的工作。汇编语言是此类序列的">人类可读"表示。
基本上,您可以找到任何方法来表示相同的机器指令。这是程序集语法。
众所周知,对于x86架构,存在两种不同的语法:AT&T和Intel。您从GBD获得的输出是根据AT&T语法生成的,而您从 Godbolt.org 获得的输出是Intel的。
英特尔和AT&T的语法在外观上非常不同,可能这就是为什么你一直认为结果不一样的原因。实际上,这只是表示相同指令的不同方式。
同一架构组件的这两种">方言"在诞生时就有不同的目标。AT&T语法是在AT&T实验室开发的,用于支持为许多不同的CPU生成程序(参见:Jeff Duntermann,汇编语言逐步)。当时,AT&T在计算机历史上扮演着重要角色。AT&T(贝尔实验室)一直是Unix的源头---它的范式目前(尽管部分)由Linux---C编程语言以及我们今天继续使用的许多其他基本工具所承诺。
另一方面,英特尔语法已经开发出来,嗯...由英特尔为他们自己的 CPU。许多采用英特尔语法的人说,在英特尔 CPU 上操作时,它要整洁得多。很可能是这种情况,因为语法是专门为 CPU 支持的内容精心设计的。
虽然目前不再使用AT&T语法(至少据我所知)为x86以外的CPU编写程序,但语法的一些"罪魁祸首"是由它更"通用"产生的。
那么,要学习哪一个呢?我的选择将取决于你工作的环境。整个Unix生态系统(包括Linux和Mac OS)有一个直接使用该语法的工具链(如gas
)。在Linux内核(和其他低级软件)中,你肯定会找到AT&T语法的内联汇编代码,以便与硬件进行交互。另一方面,Windows系统具有使用英特尔语法的工具链(例如nasm
)。虽然编译时标志可以要求这些工具切换到其他语法(例如objdump
的-M
标志),但习惯是采用"本机"语法。
关于问题中给出的具体例子,它们是"不兼容的",因为它们指的是反汇编代码的不同部分,因此两者之间的差异程度更高。 实际上,关于此 GDB 输出:
sub $0x30, %rsp
mov 0x10c1(%rip), %rdi
lea 0xfb6(%rip), %rsi
相应的英特尔反汇编将是:
sub rsp, 0x30
mov rdi, QWORD PTR [rip+0x10c1]
lea rsi, [rip+0xfb6]
另一方面,关于 Godbolt.org 输出:
sub rsp, 16
mov DWORD PTR [rbp-4], 1
mov DWORD PTR [rbp-8], 2
相应的AT&T拆解将是:
sub $0x10,%rsp
movl $0x1,-0x4(%rbp)
movl $0x2,-0x8(%rbp)
如您所见,最大的区别(可能会引起很多头痛)与以下事实有关:AT&T语法首先放置源,然后放置目的地,而英特尔语法则相反。
汇编序列不是具有不同语法的等价物,它们只是不同,可能是由于使用了不同的编译器。
第一对:
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)
这两个序列都不完整,但我认为这并不重要,因为显示的序列已经显示了差异。
因为源代码和程序集之间没有 1 对 1 的关系。 编译器可能会为以下语句生成相同的程序集:
x = x + 1
和
x++;
两者都将被编译成类似的东西
add dword ptr [rdi], 1
那么,当我们拆解它时,它应该拆解成哪一个?x = x+1
还是x++
?这几乎适用于程序的每个语句 - 如果有多种方式来表达源语言中发生的情况,并且效果相同,编译器可以选择将它们两者转换为相同的输出。 之后,您无法知道使用了哪一个。
- Capstone cs_disasm仅反汇编一小部分代码
- 如何使用 objdump 反汇编 OpenJDK(bin/java)
- 为什么汇编代码因我使用的反汇编程序而异
- 在 GDB - C++ 中反汇编重载成员函数
- 尝试从 g++ 理解简单的反汇编代码
- 反汇编中的硬编码地址
- assert()函数未在反汇编中显示
- x64 API挂钩没有反汇编程序库
- 反汇编的 exe 文件包含比源代码多得多的代码
- MFC 反编译为任何非汇编代码
- 分析崩溃的反汇编 C++ 代码
- 如何通过反汇编从C++函数获取"lea"指令?
- 如何在gdb中读取内存地址以进行i7处理器代码的反汇编
- arm汇编代码-理解cpp源代码的反汇编
- 研究一个简单的代码反汇编输出和内存映射
- Visual Studio Express 2010 C++反汇编调试
- 我的函数及其参数在反汇编视觉 c++ 中的名称
- 一些反汇编c++代码我很困惑
- 反汇编函数参数(无符号__int8)大海捞针[19] << 8)
- 关于反汇编输出的问题