0

我一直在阅读以下系列文章:http ://www.altdevblogaday.com/2011/11/09/a-low-level-curriculum-for-c-and-c

显示的反汇编代码和我在运行相同代码时设法生成的反汇编代码差异很大,我缺乏解释差异的理解。

有没有人可以逐行遍历它,也许可以解释它在每一步都在做什么?我从我所做的搜索中得到感觉,前几行与帧指针有关,在我的反汇编代码中似乎还有一些额外的行,以确保在将新值放入寄存器之前寄存器是空的(不存在来自文章中的代码)

我正在使用 XCode 4 中的 g++ 编译器在 OSX(原作者使用 Windows)上运行它。我真的不知道这些差异是否是由于操作系统、架构(可能是 32 位与 64 位?)或编译器本身。它甚至可能是我猜的代码——我的代码包含在主函数声明中,而原始代码没有提及这一点。

我的代码:

int main(int argc, const char * argv[])
{

    int x = 1;
    int y = 2;
    int z = 0;

    z = x + y;

}

我的反汇编代码:

0x100000f40:  pushq  %rbp
0x100000f41:  movq   %rsp, %rbp
0x100000f44:  movl   $0, %eax
0x100000f49:  movl   %edi, -4(%rbp)
0x100000f4c:  movq   %rsi, -16(%rbp)
0x100000f50:  movl   $1, -20(%rbp)
0x100000f57:  movl   $2, -24(%rbp)
0x100000f5e:  movl   $0, -28(%rbp)
0x100000f65:  movl   -20(%rbp), %edi
0x100000f68:  addl   -24(%rbp), %edi
0x100000f6b:  movl   %edi, -28(%rbp)
0x100000f6e:  popq   %rbp
0x100000f6f:  ret    

原文章的反汇编代码:

mov    dword ptr [ebp-8],1
mov    dword ptr [ebp-14h],2
mov    dword ptr [ebp-20h],0
mov    eax, dword ptr [ebp-8]
add    eax, dword ptr [ebp-14h]
mov    dword ptr [ebp-20h],eax

完整的逐行细分将非常有启发性,但对于理解这一点的任何帮助将不胜感激。

4

3 回答 3

2

原始文章中的所有代码都在您的代码中,只是周围有一些额外的东西。这:

0x100000f50:  movl   $1, -20(%rbp)
0x100000f57:  movl   $2, -24(%rbp)
0x100000f5e:  movl   $0, -28(%rbp)
0x100000f65:  movl   -20(%rbp), %edi
0x100000f68:  addl   -24(%rbp), %edi
0x100000f6b:  movl   %edi, -28(%rbp)

直接对应于文章中谈到的 6 条指令。

于 2013-02-22T22:29:53.243 回答
0

首先,列为“来自原始文章”的汇编程序使用“英特尔”语法,您帖子中的“反汇编输出”是“AT&T 语法”。这解释了指令的参数顺序是“从后到前”[让我们不要争论正确或错误,好吗?],并且寄存器名称以 a 为前缀%,常量以 . 为前缀$。引用寄存器的内存位置/偏移量的方式也有所不同 -dword ptr [reg+offs]在英特尔汇编程序中,它转换l为指令的后缀,而offs(%reg).

32 位与 64 位重命名了一些寄存器 -与文章代码中%rbp的相同ebp

实际偏移量(例如-20)不同,部分原因是寄存器在 64 位中更大,但也因为您有argcargv作为函数参数的一部分,它作为函数开头的一部分存储 - 我有一种原始的感觉文章实际上是在反汇编一个不同于main.

于 2013-02-22T22:36:36.303 回答
0

你的反汇编代码和文章的代码有两个主要区别。

一是文章使用的是 Intel 汇编器语法,而您的反汇编代码使用的是传统的 Unix/AT&T 汇编器语法。两者之间的一些差异记录在Wikipedia上。

另一个区别是文章省略了函数prologue,它建立了堆栈框架,以及函数epilogue,它破坏了堆栈框架并返回给调用者。他正在反汇编的程序必须包含执行这些操作的指令,但他的反汇编程序没有显示它们。(实际上,如果启用了优化器,堆栈帧可以并且可能会被省略,但显然没有启用。)

还有一些细微差别:您的代码对局部变量使用了稍微不同的布局,并且您的代码在不同的寄存器中计算总和。

在 Mac 上,g++ 不支持发出 Intel 助记符,但 clang 支持:

:; clang -S -mllvm --x86-asm-syntax=intel t.c
:; cat t.s
    .section    __TEXT,__text,regular,pure_instructions
    .globl  _main
    .align  4, 0x90
_main:                                  ## @main
    .cfi_startproc
## BB#0:
    push    RBP
Ltmp2:
    .cfi_def_cfa_offset 16
Ltmp3:
    .cfi_offset rbp, -16
    mov RBP, RSP
Ltmp4:
    .cfi_def_cfa_register rbp
    mov EAX, 0
    mov DWORD PTR [RBP - 4], EDI
    mov QWORD PTR [RBP - 16], RSI
    mov DWORD PTR [RBP - 20], 1
    mov DWORD PTR [RBP - 24], 2
    mov DWORD PTR [RBP - 28], 0
    mov EDI, DWORD PTR [RBP - 20]
    add EDI, DWORD PTR [RBP - 24]
    mov DWORD PTR [RBP - 28], EDI
    pop RBP
    ret
    .cfi_endproc


.subsections_via_symbols

如果添加-g标志,编译器将添加调试信息,包括源文件名和行号。它太大了,无法完整地放在这里,但这是相关的部分:

    .loc    1 4 14 prologue_end     ## t.c:4:14
Ltmp5:
    mov DWORD PTR [RBP - 20], 1
    .loc    1 5 14                  ## t.c:5:14
    mov DWORD PTR [RBP - 24], 2
    .loc    1 6 14                  ## t.c:6:14
    mov DWORD PTR [RBP - 28], 0
    .loc    1 8 5                   ## t.c:8:5
    mov EDI, DWORD PTR [RBP - 20]
    add EDI, DWORD PTR [RBP - 24]
    mov DWORD PTR [RBP - 28], EDI
于 2013-02-22T22:41:17.337 回答