这是我通过反汇编看到的function(1,2,3);
声明:
movl $0x3,0x8(%esp)
movl $0x2,0x4(%esp)
movl $0x1,(%esp)
call 0x4012d0 <_Z8functioniii>
似乎 ret地址根本没有被推入堆栈,那么ret
如何运作?
答案 0 :(得分:6)
在x86处理器上(与汇编语言示例一样),call
指令将堆栈上的返回地址推送并将控制转移到函数。
并非所有处理器体系结构都将返回地址放在堆栈上 - 通常有一组一个或多个寄存器用于保存返回地址。在ARM处理器上,BL
指令将返回地址放在特定寄存器(LR
或“链接寄存器”)中,并将控制转移到该函数。 ia64处理器执行类似的操作,但有几个可能的寄存器(b0
- b7
)可以接收返回地址,一个将在指令中指定(b0
是默认)。
答案 1 :(得分:5)
理想情况下,call
语句应该处理这个问题。程序计数器的下一个位置将被推入堆栈。当被调用的函数(子例程)完成它并且遇到return语句时,控件现在转到被推入堆栈的地址,它将被弹出。
答案 2 :(得分:3)
这取决于ABI和体系结构,但如果返回地址确实在堆栈上结束,那么call
指令就会产生副作用。
答案 3 :(得分:1)
调用将RIP寄存器的当前值(返回地址)推送到堆栈+执行调用
ret 从堆栈顶部(RSP寄存器指向那里)弹出返回地址(调用推送)并将其写入RIP寄存器。
GNU / Linux盒子上的示例:函数f调用函数g,让我们看一下g的框架。
低地址
...&lt; - RSP(堆栈指针显示堆栈顶部)寄存器点在此地址
g当地的vars
f的基址指针(旧RBP值)&lt; - RBP(基址指针)寄存器指向此地址
f的转发地址(旧的RIP值)(这是调用(来自f)推送的内容,以及 ret (来自g)将弹出的内容)<登记/>
那个叫做g并且不适合寄存器的args(我认为在Windows上这是不同的)
......
高级地址
g将释放本地变量(movq%rsp,%rbp)
g将弹出旧的RBP&#34;并将其存储在RBP寄存器(pop%rbp)中
g将 ret ,这将使用RSP指向的值存储的值修改RIP
希望有所帮助