gpt4 book ai didi

assembly - 理解堆栈对齐

转载 作者:行者123 更新时间:2023-12-02 19:38:55 26 4
gpt4 key购买 nike

我在看 Intel manual关于堆栈帧。注意到

The end of the input argument area shall be aligned on a 16 (32, if __m256 is passed on stack) byte boundary.



我不太明白这是什么意思。是否意味着 rsp应该指向始终在 16 对齐的地址吗?

我试着用它做实验并编写了非常简单的程序:
section .text
global _start

_start:
push byte 0xFF

;SYS_exit syscall

我用 gdb 运行它并注意到在执行 push 之前说明 rsp = 0x7fffffffdcf0 .它确实在 16 上对齐。 x/1xg $rsp返回 0x0000000000000001 .

现在,推送 rsp的内容后变成了 0x7fffffffdce8 .是否违反对齐要求?

我也注意到了 x/1xg $rsp返回 0xffffffffffffffff .这意味着我们设置了 1到接下来的 8 个字节,而不仅仅是 push 指令中指定的一个。为什么?我预计 x/1xg $rsp 的输出推后要 0x00000000000000FF (我们只推了一个字节)。

最佳答案

rsp_start - 这是操作系统入口点,它实际上违反了 ABI,因为堆栈应该在 call 之前对齐,因此 call本身会加上8B的返回地址,你可以期待rsp进入时未对齐 -8。

在应用程序进入时,请确保在调用任何其他符合 ABI 的代码之前手动对齐堆栈(或者如果您打算使用 C 运行时库,那么您的应用程序代码的入口点应该是 main ,并让 crtlib 拥有它自己的初始化代码运行在 _start )。

Now, after pushing the content of rsp became 0x7fffffffdce8. Is it a violation of the alignment requirements?



是的,如果您当时愿意 call一些更复杂的函数,例如 printf使用非平凡参数(因此它将使用 SSE 指令进行实现),它很可能会出现段错误。

关于 push byte 0xFF :

这不是 64b 模式下的合法指令(甚至在 16 位和 32 位模式下也不合法)(在 byte 操作数目标大小的意义上不合法, byte 立即数作为源值是合法的,但是 operand size can be only 16, 32 or 64 bits ),所以 NASM将猜测目标大小(任何合法的,在 64b 模式下自然选择 qword),并将猜测的目标大小与 imm8 一起使用。从源头。

顺便说一句,使用 -w+all在这种情况下使 NASM 发出(有点奇怪,但至少你可以调查)警告的选项:
warning: signed byte value exceeds bounds

例如合法 push word 0xFF只会将两个字节插入堆栈,字值 0x00FF .

如何对齐堆栈:如果您已经知道初始对齐,只需在调用一些需要 ABI 的子程序之前根据需要进行调整(在常见的 64b 代码中,通常很简单,要么不推送任何内容,要么多做一次冗余推送,例如 push rbp )。

如果您不确定对齐方式,请使用一些备用寄存器来存储原始 rsp (经常使用 rbp,所以它也起到栈帧指针的作用),然后是 and rsp,-16清除底部位。

请记住,在创建自己的符合 ABI 的子例程时,该堆栈在 call 之前对齐。 ,所以它在输入时是-8B。再次简单 push rbp通常足以同时解决多个问题,保留 rbp值(因此 mov rbp, rsp 可以“免费”)并对齐子程序其余部分的堆栈。

编辑:关于编码、源大小和即时大小...

不幸的是,我不是 100% 确定这在 NASM 中应该如何定义,但我认为实际上是 push定义是如此复杂,以至于它有点破坏了 NASM 语法(将当前语法耗尽到无法指定是操作数大小还是源立即数大小的程度,因此默默地假设大小说明符主要是操作数大小和在某些情况下会立即影响)。

通过使用 push byte 0xFF NASM 将采用 byte部分也作为“操作数大小”,而不仅仅是直接大小。和 byte不是推送的合法操作数大小,因此 NASM 将改为选择 qword默认为 64b 模式。那么它也会考虑 byte作为直接大小,并对 0xFF 进行符号扩展至 qword . IE。这在我看来是一种未定义的行为。 NASM 创建者可能不希望您指定即时大小,因为 NASM 会针对大小进行优化,因此当您这样做时 push word -1 ,它将组装为“推字操作数 imm8”。您可以以另一种方式覆盖它,以确保您通过 push strict word -1 获得 imm16 .

查看由各种组合(在 64b 模式下)产生的机器代码(其中一些严格地说至少值得警告,甚至错误,例如“严格的 qword”只产生 imm32,而不产生 imm64(因为 imm64 操作码不存在)当然)……甚至没有提到 dword 变体实际上是 qword 操作数大小,您不能在 64b 模式下使用 32b 操作数大小):
 6 00000000 6AFF                            push    -1
7 00000002 6AFF push strict byte 0xFF
8 ****************** warning: signed byte value exceeds bounds
9 00000004 6AFF push byte 0xFF
10 ****************** warning: signed byte value exceeds bounds
11 00000006 6AFF push strict byte -1
12 00000008 6AFF push byte -1
13 0000000A 6668FF00 push strict word 0xFF
14 0000000E 6668FF00 push word 0xFF
15 00000012 6668FFFF push strict word -1
16 00000016 666AFF push word -1
17 00000019 68FF000000 push strict dword 0xFF
18 0000001E 68FF000000 push dword 0xFF
19 00000023 68FFFFFFFF push strict dword -1
20 00000028 6AFF push dword -1
21 0000002A 68FF000000 push strict qword 0xFF
22 0000002F 68FF000000 push qword 0xFF
23 00000034 68FFFFFFFF push strict qword -1
24 00000039 6AFF push qword -1

无论如何,我想不会有太多人对此感到困扰,因为在 64b 模式下,您通常希望 qword push ( rsp -= 8 ) 以尽可能短的方式立即编码,所以您只需写 push -1并让 NASM 处理 imm8优化本身,期待 rsp当然要改变-8。在其他情况下,他们可能希望你知道合法的操作数大小,而不是使用 byte根本。

如果你认为这是 Not Acceptable ,我会在 NASM 论坛/bugzilla/某处提出这个问题,它应该如何工作。就我个人而言,当前的行为对我来说“足够好”(两者都有道理,而且我不时快速查看列表文件以验证机器码字节中没有令人讨厌的惊喜并且它着陆了正如预期的那样)。也就是说,我主要是代码大小介绍,所以我知道产生的每个字节及其用途。如果 NASM 突然产生 imm16而不是预期的 imm8 ,我会在二进制大小上看到它并进行调查。

关于assembly - 理解堆栈对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48683962/

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