理解一些汇编语句的目的

Leg*_*end 11 c x86 assembly

我试图理解一些汇编代码,并设法完成大部分汇编代码,除了几行.我能够理解大部分内部发生的事情,但我无法完全理解代码开头和结尾发生了什么(以及为什么会发生).有人可以对此有所了解吗?

int main() {
    int a, b;
    a = 12;
    b = 20;
    b = a + 123;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

拆卸版本:

 8048394:8d 4c 24 04          lea    0x4(%esp),%ecx              ; ??
 8048398:83 e4 f0             and    $0xfffffff0,%esp            ; ??
 804839b:ff 71 fc             pushl  -0x4(%ecx)                  ; ??
 804839e:55                   push   %ebp                        ; Store the Base pointer
 804839f:89 e5                mov    %esp,%ebp                   ; Initialize the Base pointer with the stack pointer
 80483a1:51                   push   %ecx                        ; ??
 80483a2:83 ec 4c             sub    $0x4c,%esp                  ; ??
 80483a5:c7 45 f8 0c 00 00 00 movl   $0xc,-0x8(%ebp)             ; Move 12 into -0x8(%ebp)
 80483ac:c7 45 f4 14 00 00 00 movl   $0x14,-0xc(%ebp)            ; Move 20 into -0xc(%ebp)
 80483b3:8b 45 f8             mov    -0x8(%ebp),%eax             ; Move 12@-0x8(%ebp) into eax
 80483b6:83 c0 7b             add    $0x7b,%eax                  ; Add 123 to 12@eax
 80483b9:89 45 f4             mov    %eax,-0xc(%ebp)             ; Store the result into b@-0xc(%ebp)
 80483bc:b8 00 00 00 00       mov    $0x0,%eax                   ; Move 0 into eax
 80483c1:83 c4 10             add    $0x10,%esp                  ; ??
 80483c4:59                   pop    %ecx                        ; ??
 80483c5:5d                   pop    %ebp                        ; ??
 80483c6:8d 61 fc             lea    -0x4(%ecx),%esp             ; ??
Run Code Online (Sandbox Code Playgroud)

nat*_*ose 30

堆栈向下增长.A push从堆栈指针(esp)中减去并pop添加到esp.你必须牢记这一点,以了解很多这一点.

8048394:8d 4c 24 04          lea    0x4(%esp),%ecx              ; ??
Run Code Online (Sandbox Code Playgroud)

lea =加载有效地址

这将4字节的东西的地址保存到堆栈中.由于这是32位(4字节字)x86代码,这意味着堆栈中的第二项.由于这是函数的代码(在这种情况下为main),因此堆栈顶部的4个字节是返回地址.

8048398:83 e4 f0             and    $0xfffffff0,%esp            ; ??
Run Code Online (Sandbox Code Playgroud)

此代码确保堆栈对齐到16个字节.在此操作之后,esp将小于或等于此操作之前的值,因此堆栈可能会增长,从而保护可能已存在于堆栈中的任何内容.这有时是main为了防止使用未对齐的堆栈调用函数,这可能会导致事情变得非常慢(我认为16字节是x86上的缓存行宽,尽管4字节对齐在这里非常重要) .如果main有一个未对齐的堆栈,那么程序的其余部分也将如此.

 804839b:ff 71 fc             pushl  -0x4(%ecx)                  ; ??
Run Code Online (Sandbox Code Playgroud)

由于ecx之前被加载为指向前一个堆栈顶部返回地址另一侧的东西的指针,所以因为它有一个-4索引,这指的是返回当前函数被推回的返回地址到堆栈的顶部,以便main可以正常返回.(推送是神奇的,似乎能够在同一指令中加载和存储到RAM中的不同位置).

 804839e:55                   push   %ebp                        ; Store the Base pointer
 804839f:89 e5                mov    %esp,%ebp                   ; Initialize the Base pointer with the stack pointer
 80483a1:51                   push   %ecx                        ; ??
 80483a2:83 ec 4c             sub    $0x4c,%esp                  ; ??
Run Code Online (Sandbox Code Playgroud)

这主要是标准的功能序言(以前的东西是主要的特殊功能).这是一个堆栈框架(ebp和esp之间的区域),其中局部变量可以存在.推送ebp,以便可以在结尾(在当前函数的末尾)恢复旧的堆栈帧.

80483a5:c7 45 f8 0c 00 00 00 movl   $0xc,-0x8(%ebp)             ; Move 12 into -0x8(%ebp)
80483ac:c7 45 f4 14 00 00 00 movl   $0x14,-0xc(%ebp)            ; Move 20 into -0xc(%ebp)
80483b3:8b 45 f8             mov    -0x8(%ebp),%eax             ; Move 12@-0x8(%ebp) into eax
80483b6:83 c0 7b             add    $0x7b,%eax                  ; Add 123 to 12@eax
80483b9:89 45 f4             mov    %eax,-0xc(%ebp)             ; Store the result into b@-0xc(%ebp)

80483bc:b8 00 00 00 00       mov    $0x0,%eax                   ; Move 0 into eax
Run Code Online (Sandbox Code Playgroud)

eax是存储整数函数返回值的地方.这是设置为从main返回0.

80483c1:83 c4 10             add    $0x10,%esp                  ; ??
80483c4:59                   pop    %ecx                        ; ??
80483c5:5d                   pop    %ebp                        ; ??
80483c6:8d 61 fc             lea    -0x4(%ecx),%esp             ; ??
Run Code Online (Sandbox Code Playgroud)

这是功能结局.由于开头的奇怪堆栈对齐代码,因此更难理解.尽管如此,我在弄清楚为什么堆栈的调整次数比次序更低时我会遇到一些麻烦.

很明显,这个特定的代码没有使用优化编译.如果那里可能不会那么多,因为编译器可以看到,即使它没有在你main的程序的最终结果中列出的数学是相同的.对于实际执行某些操作(具有副作用或结果)的程序,有时更容易阅读轻微优化的代码(-cc或-0s参数到gcc).

对于没有的函数,编译器生成的读取汇编通常要容易得多main.如果你想阅读以理解代码,那么你自己写一个函数,它接受一些参数来产生一个结果或者对全局变量起作用,你将能够更好地理解它.

可能对你有帮助的另一件事就是让gcc为你生成汇编文件,而不是反汇编它们.该-S标志告诉它生成它(但不生成其他文件),并.s在末尾命名汇编文件.这比阅读反汇编版本更容易阅读.

  • 谢谢你的时间。很详细的解释。正是我要找的。 (2认同)

Jen*_*ger 6

不知道为什么编译器会完成所有这些工作,但这就是我能解读的内容:

 8048394:8d 4c 24 04          lea    0x4(%esp),%ecx              ; ecx := esp+4
 8048398:83 e4 f0             and    $0xfffffff0,%esp            ; align the stack to 16 bytes
 804839b:ff 71 fc             pushl  -0x4(%ecx)                  ; push [ecx-4] ([esp])
 80483a1:51                   push   %ecx                        ; push ecx
 80483a2:83 ec 4c             sub    $0x4c,%esp                  ; allocate 19 dwords on stack
 80483c1:83 c4 10             add    $0x10,%esp                  ; deallocate 4 dwords from stack
 80483c4:59                   pop    %ecx                        ; restore ecx
 80483c5:5d                   pop    %ebp                        ; and ebp
 80483c6:8d 61 fc             lea    -0x4(%ecx),%esp             ; esp := [ecx-4]
Run Code Online (Sandbox Code Playgroud)