Linux 帮助理解GDB中非常基本的main()反汇编

Linux 帮助理解GDB中非常基本的main()反汇编,linux,assembly,gdb,x86,disassembly,Linux,Assembly,Gdb,X86,Disassembly,嗨 我编写了这个非常基本的主要函数,以试验反汇编,并查看和希望了解在较低级别上发生了什么: int main() { return 6; } 使用gdb来disas main会产生以下结果: 0x08048374 <main+0>: lea 0x4(%esp),%ecx 0x08048378 <main+4>: and $0xfffffff0,%esp 0x0804837b <main+7>: pushl -0x4(%ec

我编写了这个非常基本的主要函数,以试验反汇编,并查看和希望了解在较低级别上发生了什么:

int main() {
  return 6;
}
使用gdb来disas main会产生以下结果:

0x08048374 <main+0>:    lea    0x4(%esp),%ecx
0x08048378 <main+4>:    and    $0xfffffff0,%esp
0x0804837b <main+7>:    pushl  -0x4(%ecx)
0x0804837e <main+10>:   push   %ebp
0x0804837f <main+11>:   mov    %esp,%ebp
0x08048381 <main+13>:   push   %ecx
0x08048382 <main+14>:   mov    $0x6,%eax
0x08048387 <main+19>:   pop    %ecx
0x08048388 <main+20>:   pop    %ebp
0x08048389 <main+21>:   lea    -0x4(%ecx),%esp
0x0804838c <main+24>:   ret  
0x08048374:lea0x4(%esp),%ecx
0x08048378:和$0xfffffff0,%esp
0x0804837b:PUSH-0x4(%ecx)
0x0804837e:推送%ebp
0x0804837f:mov%esp,%ebp
0x08048381:推送%ecx
0x08048382:mov$0x6,%eax
0x08048387:弹出%ecx
0x08048388:弹出%ebp
0x08048389:lea-0x4(%ecx),%esp
0x0804838c:ret
以下是关于我认为正在发生的事情以及我需要逐行帮助的最佳猜测:

lea0x4(%esp),%ecx

将esp+4的地址加载到ecx中为什么我们要在esp中添加4?

我在某处读到这是命令行参数的地址。但是当我做了
x/d$ecx
时,我得到了argc的值实际的命令行参数值存储在哪里?

和$0xFFFFF0,%esp

对齐堆栈

pushl-0x4(%ecx)

将esp最初所在的地址推送到堆栈上这样做的目的是什么?

push%ebp

将基指针推到堆栈上

mov%esp%ebp

将当前堆栈指针移到基指针中

push%ecx

将原始esp+4的地址推送到堆栈上为什么?

mov$0x6,%eax

我想在这里返回6,所以我猜返回值存储在eax中?

pop%ecx

将ecx恢复为堆栈上的值为什么我们返回时希望ecx为esp+4?

pop%ebp

将ebp恢复为堆栈上的值

lea-0x4(%ecx),%esp

将esp恢复到其原始值

ret

我是一个n00b当谈到组装,所以任何帮助将是伟大的!另外,如果你看到任何关于我所想发生的事情的虚假陈述,请纠正我


非常感谢!:]

你的口译做得很好。调用函数时,返回地址会自动推送到堆栈,这就是为什么第一个参数argc被推回到4(%esp)的原因。argv将从8(%esp)开始,每个参数都有一个指针,后跟一个空指针。此函数将旧值%esp推送到堆栈中,以便返回时可以包含原始的未对齐值。返回时%ecx的值无关紧要,这就是为什么它被用作%esp引用的临时存储。除此之外,您的一切都是正确的。

关于您的第一个问题(命令行参数存储在哪里),函数的参数就在
ebp
之前。我必须说,您的“真正”main从
开始,在那里它推动
ebp
,并将
esp
移动到
ebp
。我认为gcc把所有的
lea
s都搞乱了,只是为了替换
esp
函数调用前后的常规操作(上瘾和减法)。通常例程如下所示(以我所做的简单函数为例):

0x080483b4:推送%ebp
0x080483b5:mov%esp,%ebp
0x080483b7:sub$0x10,%esp#局部变量空间
0x080483ba:mov 0xc(%ebp),%eax#获取arg2
0x080483bd:mov 0x8(%ebp)、%edx#和arg1
0x080483c0:lea(%edx,%eax,1),%eax#只需添加它们
0x080483c3:mov%eax,-0x4(%ebp)#存储在本地变量中
0x080483c6:mov-0x4(%ebp),%eax#并返回总和
0x080483c9:离开
0x080483ca:ret
也许您已经启用了一些优化,这可能会使代码更加复杂。
最后是,返回值存储在
eax
中。无论如何,您的解释是非常正确的。

我认为您最初的问题中唯一突出的一点是,为什么代码中存在以下语句:

0x08048381 <main+13>:   push   %ecx
0x08048382 <main+14>:   mov    $0x6,%eax
0x08048387 <main+19>:   pop    %ecx
0x08048381:推送%ecx
0x08048382:mov$0x6,%eax
0x08048387:弹出%ecx

%ECX在“代码> >代码>和<代码> <代码>中的推和流行似乎没有什么意义-它们在<强> > <强>示例中没有真正做任何事情,但请考虑您的代码调用<强>函数调用< /强> < /p>的情况。 系统无法保证对其他函数的调用(将设置它们自己的堆栈激活帧)不会重置寄存器值。事实上,他们可能会。因此,代码在堆栈上设置了一个保存的寄存器部分,在可能将控制权移交给当前代码块“肉”中的函数调用之前,代码使用的任何寄存器(通过常规堆栈设置已保存的%esp和%ebp除外)都存储在堆栈中

当这些潜在调用返回时,系统会弹出堆栈中的值以恢复调用前寄存器值。若您是直接编写汇编程序而不是编译,那个么您将自己负责存储和检索这些寄存器值

但是,在示例代码中,没有函数调用—只有一条指令位于设置返回值的
,但编译器无法知道,并像往常一样保留其寄存器


如果您在
之后添加了将其他值推送到堆栈上的C语句,那么看看这里会发生什么是很有趣的。如果我正确地认为这是堆栈的已保存寄存器部分,那么您可能希望编译器在
之前插入自动
pop
语句,以清除这些值。

堆栈帧 begi的代码
0x08048381 <main+13>:   push   %ecx
0x08048382 <main+14>:   mov    $0x6,%eax
0x08048387 <main+19>:   pop    %ecx
push  %ebp
mov   %esp, %ebp