assembly - 正确调用 printf 的堆栈对齐?
问题描述
我已经看到了堆栈指针/在调用之前esp
递减并4
在调用之后printf
重新调整的示例:12
printf
section .text
global main
extern printf
main:
sub esp, 4
push msg
push format_str
call printf
add esp, 12
ret
section .data:
msg db "print me!", 0
format_str db "%s", 0
而且我已经看到了堆栈指针/在调用之前esp
递减并8
在调用之后printf
重新调整的示例:16
printf
section .text
global main
extern printf
main:
sub esp, 8
push msg
push format_str
call printf
add esp, 16
ret
section .data:
msg db "print me!", 0
format_str db "%s", 0
在从 libc 调用任何函数之前,我读到的内容esp
应该递减8
,然后重新调整/递增。16
这些示例中的差异让我感到困惑,哪个堆栈对齐示例是正确的,为什么?可以解释这个递增/递减过程以减少混乱吗?
解决方案
我已经看到堆栈指针/esp 在调用 printf 之前减少 4 并在调用 printf 之后重新调整 12 的示例:
根据另一个问题的评论,堆栈应在可以使用 SSE 指令的系统(例如库、操作系统)上以 16 个字节对齐。
main
假设调用函数 ( )时堆栈指针正确对齐,call
指令会从 中减去 4 个字节,esp
因此指令必须从 中减去 12、28、40 ... 字节以保持堆栈指针正确对齐。sub
push
esp
子特别,8
显然,在这种情况下,编译器不会被告知要注意 16 字节的堆栈对齐。
显然,在这种情况下,编译器分配的堆栈比必要的多。
我刚刚告诉编译器为堆栈生成一个 8 字节和 16 字节对齐;所有其他编译器选项(当然还有源代码)都是相同的。
不同之处在于,在 8 字节对齐的情况下,编译器生成sub esp, 4
,在 16 字节对齐的情况下sub esp, 20
。
显然,这是编译器优化中的一个问题:
如果sub esp,20
将堆栈对齐到 16 个字节,sub esp, 4
也将对齐到 16 个字节。
并且使用“对齐到 8 字节”选项表明绝对可以使用 asub esp, 4
而不是 a sub esp, 20
。
这表明一些编译器为某些未知目的保留了比必要更多的堆栈。
推荐阅读
- ruby-on-rails - 使 WebSocket 与 Ruby on Rails 与 Puma 和 Nginx 一起工作
- node.js - Multipart/form-data 使用 axios API Rest 发送空数据
- c - 我如何在这里获得最后一个 printf 语句来打印用户输入的 x 和 y 的输出?
- sql - case 语句不返回所有值
- regex - 如何使用 regex_substr 从 xml 文件中解析多个文件名
- gcc - 无法使用 ml/ml64 组装
- sql-server - 如果子查询没有返回结果,为什么“= ALL(子查询)”评估为真?
- latex - 引文中的扩展链接
- ajax - WP:尝试使用 AJAX 调用更新 post_meta 中的关联数组
- c++ - Makefile 获取多个子目录中的源文件