【发布时间】:2011-02-02 07:57:28
【问题描述】:
这是我通过反汇编看到的声明function(1,2,3);:
movl $0x3,0x8(%esp)
movl $0x2,0x4(%esp)
movl $0x1,(%esp)
call 0x4012d0 <_Z8functioniii>
好像ret地址根本没有入栈,那么ret是怎么工作的呢?
【问题讨论】:
标签: assembly x86 stack calling-convention
这是我通过反汇编看到的声明function(1,2,3);:
movl $0x3,0x8(%esp)
movl $0x2,0x4(%esp)
movl $0x1,(%esp)
call 0x4012d0 <_Z8functioniii>
好像ret地址根本没有入栈,那么ret是怎么工作的呢?
【问题讨论】:
标签: assembly x86 stack calling-convention
在 x86 处理器上(就您的汇编语言示例而言),call 指令将返回地址压入堆栈并将控制权转移给函数。
所以在进入函数时,堆栈指针指向返回地址,准备好让ret 将其弹出到程序计数器(EIP / RIP)中。
并非所有处理器架构都将返回地址放在堆栈上——通常会有一组一个或多个寄存器设计用于保存返回地址。在 ARM 处理器上,BL 指令将返回地址放入特定寄存器(LR,或“链接寄存器”)并将控制权转移给函数。
ia64 处理器做了类似的事情,除了有几个可能的寄存器 (b0-b7) 可以接收返回地址,并且将在指令中指定一个(b0 是默认值)。
【讨论】:
理想情况下,call 语句应该解决这个问题。程序计数器的下一个位置将被压入堆栈。当被调用的函数(子例程)完成它的工作并遇到返回语句时,控件现在转到被压入堆栈的地址,它将被弹出。
【讨论】:
call是怎么处理的呢?我只想看看ret地址在哪里指定
这取决于 ABI 和架构,但如果返回地址确实最终在堆栈中,那是将其放在那里的 call 指令的副作用。
【讨论】:
call 将 RIP 寄存器的当前值(返回地址)推入堆栈 + 是否调用
ret 弹出返回地址(即 调用 push)从栈顶(RSP 寄存器指向那里)并将其写入 RIP 寄存器。
GNU/Linux 机器上的示例:函数 f 调用函数 g 并查看 g 的框架。
低地址
...
g 的本地变量
f 的基指针(旧 RBP 值)
f 的 ret 地址(旧 RIP 值)(这是 call(来自 f)推送的内容,以及 ret(来自 g)将弹出的内容)
f 调用 g 并且不适合寄存器的参数(我认为在 Windows 上这是不同的)
...
高地址
g 将释放本地变量 (movq %rsp, %rbp)
g 将弹出“旧 RBP”并将其存储在 RBP 寄存器中 (pop %rbp)
g 将ret,它将使用存储在 RSP 指向的位置的值修改 RIP
希望对你有帮助
【讨论】: