gpt4 book ai didi

c - 理解一些汇编语句的目的

转载 作者:太空狗 更新时间:2023-10-29 16:42:20 25 4
gpt4 key购买 nike

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

int main() {
int a, b;
a = 12;
b = 20;
b = a + 123;
return 0;
}

反汇编版本:

 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 ; ??

最佳答案

堆栈向下增长。 push 从堆栈指针 (esp) 减去,pop 添加到 esp。你必须牢记这一点才能理解其中的很多内容。

8048394:8d 4c 24 04          lea    0x4(%esp),%ecx              ; ??

lea = 加载有效地址

这会将位于堆栈中 4 个字节的东西的地址保存到堆栈中。由于这是 32 位(4 字节字)x86 代码,这意味着堆栈中的第二项。由于这是函数的代码(在本例中为 main),堆栈顶部的 4 个字节是返回地址。

8048398:83 e4 f0             and    $0xfffffff0,%esp            ; ??

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

 804839b:ff 71 fc             pushl  -0x4(%ecx)                  ; ??

因为 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 ; ??

这主要是标准的函数序言(之前的内容是 main 的特殊内容)。这正在创建一个堆栈框架(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

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 ; ??

这是函数结尾。因为一开始的奇怪的堆栈对齐代码,所以比较难理解。不过,我很难弄清楚为什么这次堆栈的调整量比序言中的少。

很明显,此特定代码未在优化的情况下编译。如果它在那里可能不会太多,因为编译器可以看到即使它没有执行 main 中列出的数学运算,程序的最终结果也是相同的。对于实际做某事(有副作用或结果)的程序,有时更容易阅读轻微优化的代码(gcc 的 -O1 或 -0s 参数)。

对于不是 main 的函数,读取由编译器生成的程序集通常要容易得多。如果您想通过阅读来理解代码,那么您可以自己编写一个函数,该函数采用一些参数来生成结果或对全局变量起作用,您将能够更好地理解它。

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

关于c - 理解一些汇编语句的目的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4228261/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com