x86汇编代码解释

Explanation of assembly code in x86

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int main(int argc, char *argv[]){
   char a[5];
   char b[10];
   strcpy(a,"nop");
   gets(b);
   printf("Hello there %s. Value in a is %s.\n",b,a);
   exit(0);
}

汇编输出的前几行显示:

push %ebp
mov %esp,%ebp
sub [=11=]x28,%esp
mov [=11=]x80c5b08,%edx
lea -0xd(%ebp),%eax
mov (%edx),%edx
mov %edx,(%eax)
lea -0x17(%ebp),%eax
mov %eax,(%esp)
call 0x8049c60 <gets>

有几个原因让我很困惑。首先,如果char *argv[]占8个字节,int argc占4个,a占8个,b为什么要占40个字节sub [=12=]x28,%esp ] 占12 -> 8+4+8+12 = 32?

我也在努力查看 strcpy 发生的位置以及两个内存地址 [=17=]x80c5b080x8049c60 的原因。

局部变量后可能会有一些填充 因为 gets() 的参数需要有(32 位对齐的)空间 以及通过call指令保存的PC寄存器。

注意:ebp寄存器必须指向下一个可用的堆栈地址 在本地堆栈帧之后。

注意:永远不要使用 gets() 函数, 有几个原因。请改用 fgets()

strcpy() 被编译器替换为宏调用。 该宏产生了以下内容:

mov [=10=]x80c5b08,%edx
lea -0xd(%ebp),%eax
mov (%edx),%edx

I'm also struggling to see where strcpy() happens and what accounts for the two memory addresses [=18=]x80c5b08 and 0x8049c60"

0x80c5b08 是要复制到变量中的文字地址。

0x8049c60是gets()函数的链接地址。

好吧,我会尽力而为,但我的组件有点生锈。首先,让我们从您正在查看的内容开始。使用 AT&T 语法,与 Intel 语法 (operation register data) 相比,您基本上必须向后读取地址操作 (operation data register),这是某些人更喜欢阅读 Intel 的部分原因。

从概述的角度来看,汇编调用并不太难理解。如果您查看前两个命令,第一个命令将先前的 base pointer 地址压入堆栈以保存它。 (当该程序退出时,先前的基指针地址将被恢复,这就是调用例程中的执行将恢复的位置)。第二行将该程序的 base pointer 地址移动到 current stack pointer 地址(堆栈顶部)以开始执行您的程序。这两行都被称为 assembly prolog.

push %ebp
mov %esp,%ebp

stack pointer的下一行subtracts 40 bytes (28 hex)(堆栈向下增长)为局部变量ab创建space,其中"nop" 数据和 gets 的结果将被复制。我不确定它试图实现什么样的精确对齐,但是 a 的存储空间是 5 个字节,b 是 10.

sub [=11=]x28,%esp

下一行将指针地址 0x80c5b08 移动到通用 dx 寄存器(edx 用于 80386 32 位寄存器)。在汇编中,您将要操作的数据的地址放入 CPU 寄存器之一,然后再对其进行操作。这里看起来是将 "nop" 的内存地址放在 edx.

mov [=12=]x80c5b08,%edx

下一次调用lea 加载有效地址将内存地址(偏移处)base pointer - 14(0xd hex)字节复制到eax 注册。 a 的起始地址,以便可以将字符串 "nop" 复制到那里。

lea -0xd(%ebp),%eax

以下对 mov 的调用将 edx 指向的数据复制到 eax 中指定的内存位置。复制 "nop" 到 a.

mov (%edx),%edx
mov %edx,(%eax)

下一个leabase pointer - 23(0x17十六进制)b的内存地址加载到eax中,mov将地址放在堆栈上在调用 gets 填充该位置的内存之前。

lea -0x17(%ebp),%eax
mov %eax,(%esp)

call 0x8049c60 <gets>

之后,有指令在调用printf之前加载ab的内存地址以及字符串静态部分的地址。希望这会有所帮助。