栈中的返回地址是如何指定的?
这是我通过反汇编语句 function(1,2,3);
看到的结果:
movl $0x3,0x8(%esp)
movl $0x2,0x4(%esp)
movl $0x1,(%esp)
call 0x4012d0 <_Z8functioniii>
看起来 ret 地址 根本没有压入堆栈,那么怎么办? ret
工作吗?
This is what I see by disassemble for the statement function(1,2,3);
:
movl $0x3,0x8(%esp)
movl $0x2,0x4(%esp)
movl $0x1,(%esp)
call 0x4012d0 <_Z8functioniii>
It seems the ret address is not pushed into stack at all,then how does ret
work?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在 x86 处理器上(对于您的汇编语言示例),
call
指令将返回地址压入堆栈并将控制权转移给函数。因此,在进入函数时,堆栈指针指向返回地址,准备好 ret 将其弹出到程序计数器 (EIP / RIP) 中。
并非所有处理器体系结构都将返回地址放在堆栈上 - 通常有一组一个或多个寄存器设计用于保存返回地址。在 ARM 处理器上,
BL
指令将返回地址放置在特定寄存器(LR
或“链接寄存器”)中,并将控制权转移给函数。ia64 处理器执行类似的操作,只不过有多个可能的寄存器 (
b0
-b7
) 可以接收返回地址,并且将在指令中指定一个寄存器(使用 < code>b0 是默认值)。On an x86 processor (as for your assembly language example), the
call
instruction pushes the return address on the stack and transfers control to the function.So on entry to a function, the stack pointer is pointing at a return address, ready for
ret
to pop it into the program counter (EIP / RIP).Not all processor architectures put the return address on the stack- often there's a set of one or more registers designed to hold return addresses. On ARM processors, the
BL
instruction places the return address in a specific register (LR
, or the 'link register') and transfers control to the function.The ia64 processor does something similar, except that there are several possible registers (
b0
-b7
) that can receive the return address and one will be specified in the instruction (withb0
being the default).理想情况下,
call
语句应该处理这个问题。程序计数器的下一个位置将被推入堆栈。当被调用的函数(子例程)完成其工作并且遇到 return 语句时,控件现在转到被推入堆栈的地址,并且它将被弹出。Ideally, the
call
statement should take care of that. The program counter's next location will be pushed into the stack. When the function (sub routine) that was called completes it work and when it encounters a return statement, the control now goes to the address that was pushed into the stack and it will get popped.它取决于 ABI 和体系结构,但如果返回地址最终出现在堆栈上,则这是将其放在那里的
call
指令的副作用。It depends on the ABI and the architecture, but if the return address does end up on the stack it's a side-effect of the
call
instruction that puts it there.调用将RIP寄存器的当前值(返回地址)压入堆栈+执行调用
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
希望它有帮助
call pushes the current value of the RIP register (return address) to the stack + does the call
ret pops the return address(that call pushed) from the top of the stack (RSP register points there) and writes it in the RIP register.
Example on a GNU/Linux box: function f calls function g and lets look at the frame of g.
LOW ADDRESS
... <- RSP (stack pointer shows top of stack) register points at this address
g's local vars
f's base pointer (old RBP value) <- RBP (base pointer) register points at this address
f's ret address (old RIP value) (this is what the call (from f) pushed, and what the ret (from g) will pop)
args that f called g with and didn't fit in the registers (I think on Windows this is different)
...
HIGH ADDRESS
g will free the local vars (movq %rsp, %rbp)
g will pop the "old RBP" and store it in RBP register (pop %rbp)
g will ret, which will modify RIP with the value that is stored where RSP points at
Hope it helps