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