【发布时间】:2018-09-12 03:47:51
【问题描述】:
据我所知,x86-64 要求堆栈在调用之前是 16 字节对齐的,而 gcc with -m32 doesn't require this for main.
我有以下测试代码:
.data
intfmt: .string "int: %d\n"
testint: .int 20
.text
.globl main
main:
mov %esp, %ebp
push testint
push $intfmt
call printf
mov %ebp, %esp
ret
使用as --32 test.S -o test.o && gcc -m32 test.o -o test 构建。我知道系统调用 write 存在,但据我所知,它不能像 printf 那样打印整数和浮动。
进入main后,栈上有一个4字节的返回地址。然后天真地解释这段代码,两个 push 调用每个都将 4 个字节放在堆栈上,所以 call 需要另一个 4 字节的值 push 才能对齐。
这里是gas和gcc生成的二进制文件的objdump:
0000053d <main>:
53d: 89 e5 mov %esp,%ebp
53f: ff 35 1d 20 00 00 pushl 0x201d
545: 68 14 20 00 00 push $0x2014
54a: e8 fc ff ff ff call 54b <main+0xe>
54f: 89 ec mov %ebp,%esp
551: c3 ret
552: 66 90 xchg %ax,%ax
554: 66 90 xchg %ax,%ax
556: 66 90 xchg %ax,%ax
558: 66 90 xchg %ax,%ax
55a: 66 90 xchg %ax,%ax
55c: 66 90 xchg %ax,%ax
55e: 66 90 xchg %ax,%ax
我对生成的推送指令感到非常困惑。
- 如果压入两个 4 字节值,如何实现对齐?
- 为什么推送的是 0x2014 而不是 0x14?什么是 0x201d?
-
call 54b甚至可以实现什么?hd的输出匹配objdump。为什么这在 gdb 中有所不同?这是动态链接器吗?
B+>│0x5655553d <main> mov %esp,%ebp │
│0x5655553f <main+2> pushl 0x5655701d │
│0x56555545 <main+8> push $0x56557014 │
│0x5655554a <main+13> call 0xf7e222d0 <printf> │
│0x5655554f <main+18> mov %ebp,%esp │
│0x56555551 <main+20> ret
感谢有关实际执行二进制文件时发生的情况的资源,因为我不知道实际发生了什么,而且我阅读的教程也没有涵盖它。我正在阅读How programs get run: ELF binaries。
【问题讨论】:
-
mov %esp, %ebp不保存/恢复调用者的%ebp是不好的,很容易在主返回后导致段错误。 -
用
gcc -O1 -fverbose-asm -S编译你的C 代码以获得汇编代码。阅读相关的x86 ABI 规范 -
@BasileStarynkevitch 我正在为学习目的编写程序集。我想尽可能多地保留我的原始程序集,我担心
-O1会摆脱它们。 -
然后删除
-O1。请注意,汇编程序是由gcc从一些foo.c源代码生成 到gcc -fverbose-asm -S foo.c之后的foo.s(您可以添加-O1)。我确实提到了 C 代码(不是汇编程序)以了解gcc正在生成什么样的汇编程序代码 -
@BasileStarynkevitch 我仍然不确定我是否理解。我使用
as创建目标文件和gcc创建可执行文件,根本没有C 源代码?