i386 汇编问题:为什么我需要干预堆栈指针?

发布于 2024-09-04 01:17:26 字数 982 浏览 7 评论 0 原文

我认为在暑假期间学习 x86 汇编会有趣。因此,我从一个非常简单的 hello world 程序开始,借用了 gcc -S 可以给我的免费示例。我最终得到的结果是:

HELLO:
    .ascii "Hello, world!\12\0"
    .text

.globl _main
_main:
    pushl   %ebp        # 1. puts the base stack address on the stack
    movl    %esp, %ebp  # 2. puts the base stack address in the stack address register
    subl    $20, %esp   # 3. ???
    pushl   $HELLO      # 4. push HELLO's address on the stack
    call    _puts       # 5. call puts
    xorl    %eax, %eax  # 6. zero %eax, probably not necessary since we didn't do anything with it
    leave               # 7. clean up
    ret                 # 8. return
                        # PROFIT!

它可以编译甚至可以工作!我想我理解其中的大部分

不过,神奇的事情发生在第 3 步。如果我删除这一行,我的程序就会在调用 putsxor 之间因未对齐的堆栈错误而死掉。如果我将 $20 更改为另一个值,它也会崩溃。所以我得出的结论是这个值非常重要。

问题是,我不知道它的作用以及为什么需要它。

谁能给我解释一下吗? (我使用的是 Mac 操作系统,这有什么关系吗?)

I decided it would be fun to learn x86 assembly during the summer break. So I started with a very simple hello world program, borrowing on free examples gcc -S could give me. I ended up with this:

HELLO:
    .ascii "Hello, world!\12\0"
    .text

.globl _main
_main:
    pushl   %ebp        # 1. puts the base stack address on the stack
    movl    %esp, %ebp  # 2. puts the base stack address in the stack address register
    subl    $20, %esp   # 3. ???
    pushl   $HELLO      # 4. push HELLO's address on the stack
    call    _puts       # 5. call puts
    xorl    %eax, %eax  # 6. zero %eax, probably not necessary since we didn't do anything with it
    leave               # 7. clean up
    ret                 # 8. return
                        # PROFIT!

It compiles and even works! And I think I understand most of it.

Though, magic happens at step 3. Would I remove this line, my program would die between the call to puts and the xor from a misaligned stack error. And would I change $20 to another value, it'd crash too. So I came to the conclusion that this value is very important.

Problem is, I don't know what it does and why it's needed.

Can anyone explain me? (I'm on Mac OS, would it ever matter.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

自此以后,行同陌路 2024-09-11 01:17:26

在 x86 OSX 上,堆栈需要为函数调用进行 16 字节对齐,请参阅 ABI 文档 此处。因此,解释是

push stack pointer (#1)         -4
strange increment (#3)         -20
push argument (#4)              -4
call pushes return address (#5) -4
total                          -32

要进行检查,请将第 3 行从 $20 更改为 $4,这也有效。

此外,Ignacio Vazquez-Abrams 指出,#6 不是可选的。寄存器包含先前计算的剩余部分,因此必须明确将其清零。

我最近也学习了(仍在学习)汇编。为了避免您感到震惊,64 位调用约定有很大不同(参数在寄存器上传递)。已找到 对于 64 位非常有帮助集会。

On x86 OSX, the stack needs to be 16 byte aligned for function calls, see ABI doc here. So, the explanation is

push stack pointer (#1)         -4
strange increment (#3)         -20
push argument (#4)              -4
call pushes return address (#5) -4
total                          -32

To check, change line #3 from $20 to $4, which also works.

Also, Ignacio Vazquez-Abrams points out, #6 is not optional. Registers contain remnants of previous calculations so it has to explicitly be zeroed.

I recently learned (still learning) assembly, too. To save you the shock, 64bit calling conventions are MUCH different (parameters passed on the register). Found this very helpful for 64bit assembly.

第几種人 2024-09-11 01:17:26

注释的一般形式应该是“为局部变量分配空间”。我不确定为什么任意更改它会使其崩溃。如果你减少它,我只能看到它崩溃。 6 的正确注释是“准备从此函数返回 0”。

The general form of the comment should be "Allocates space for local variables". Why changing it arbitrarily would crash it I'm not sure. I can only see it crashing if you reduce it. And the proper comment for 6 is "Prepare to return a 0 from this function".

流心雨 2024-09-11 01:17:26

请注意,如果使用 -fomit-frame-pointer 进行编译,则部分 %ebp 指针样板文件将会消失。基指针对于调试很有帮助,但在 x86 上实际上并不是必需的。

另外,我强烈建议使用 Intel 语法,所有 GCC/binutils 都支持该语法。我曾经认为 AT&T 和 Intel 语法之间的差异只是品味问题,但有一天我遇到了 这个示例,其中 AT&T 助记符与 Intel 助记符完全不同。由于所有官方 x86 文档都使用 Intel 语法,因此这似乎是更好的方法。

玩得开心!

Note that if you compile with -fomit-frame-pointer some of that %ebp pointer boilerplate will disappear. The base pointer is helpful for debugging but isn't actually necessary on x86.

Also I highly recommend using Intel syntax, which is supported by all the GCC/binutils stuff. I used to think that the difference between AT&T and Intel syntax was just a matter of taste, but then one day I came across this example where the AT&T mnemonic is just totally different from the Intel one. And since all the official x86 documentation uses Intel syntax, it seems like a better way to go.

Have fun!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文