如何解决额外的推动会对装配程序产生什么影响?
给定:
typedef struct __attribute__((packed)) _Node{
int data;
struct _Node *left;
struct _Node *right;
} Node;
以及以下在树中搜索值的汇编代码。 (与 How this assembly code will be translated into c? 代码相同)
.section .text
.global _start
_start:
mov $8,%esi
mov $A,%rdi
call func
movq $60,%rax
movq $0,%rdi
syscall
func:
pushq %rbp
movq %rsp,%rbp
cmp (%rdi),%esi
jne continue
mov $1,%eax
jmp finish
continue: # go left
cmpq $0,4(%rdi)
je next
pushq %rdi # 3
mov 4(%rdi),%rdi
call func
pop %rdi # 4
cmp $1,%eax
je finish
next: # go right
cmpq $0,12(%rdi)
je fail
pushq %rdi # 1
mov 12(%rdi),%rdi
call func
pop %rdi # 2
cmp $1,%eax
je finish
fail:
mov $0,%rax
finish:
leave
ret
我想知道此更改会产生什么影响,以及它是否会导致程序无法按预期运行:
在继续后立即添加 push %rdi
。
据我所知,这会导致问题,因为我们正在向堆栈推送一些额外的值,因此此迭代的调用者可能会弹出错误的 %rdi 值,例如在这种情况下的调用者:
pushq %rdi # 1
mov 12(%rdi),%rdi
call func
pop %rdi # 2
可能会弹出 12+%rdi 而不是弹出 %rdi,但是我运行了很多测试,所有测试似乎都在 RAX 中返回了正确的值,这是为什么?
注意:这一行也会导致堆栈溢出吗?我想答案可能是肯定的。
解决方法
注意尾声中的 leave
:它在 ret
之前恢复 RSP,撤消任何额外的推送。如果它不存在,额外的推送会使您的函数崩溃(例如,将 RDI 的副本弹出到 RIP 中),无法成功返回,但 RSP 指向错误的位置。
可能会弹出 12+%rdi 而不是弹出 %rdi
不,这不可能。推送发生在修改 RDI 之前,因此您正在推送原始值,然后 pop 将其读回。 call
对 RSP 没有净影响,即 RSP 是呼叫保留的。 (如果你打破了你的堆栈,ret
会弹出错误的东西而不是返回地址,所以你会崩溃而不是返回。所以除非你故意将你的返回地址复制到其他地方,否则你不能返回已修改 RSP)。
此外,RDI+12 处的内存内容与 RDI+12 不同。如果您的字面意思是 pop 12(%rdi)
(即弹出内存),显然不是; pop %rdi
始终写入寄存器,无论弹出什么数据。
就像我刚刚在您发布此内容时评论过 your last question 一样,leave
在结语中掩盖了任何不平衡推送的问题(无论是添加额外推送,或删除弹出),只要实际需要将正确值获取到正确寄存器的推/弹出操作 仍然存在。 (在 first call
周围保存 RDI)。您的函数在第二个 call
之后不再需要节点指针,因此它毫无意义,最好只使用 leave
/ mov 12(%rdi),%rdi
/ jmp func
。但是在第二个 call
之前的任何额外推送都无关紧要。
唯一会出问题的是额外的 pop
会在最后一个 call
之前从堆栈中删除您的返回地址,因此它会被覆盖。 (最后一次调用之后的不平衡 pop
会使您的返回地址低于 RSP,但这在 x86-64 System V ABI 的用户空间中是安全的,因为它保证了红色区域低于 RSP,这样就不会被信号处理程序和诸如此类的东西异步破坏。因此,leave
仍会将 RSP 指向它应该在的位置,让 ret
弹出返回地址。
请记住,leave
等价于 mov %rbp,%rsp
/ pop %rbp
,因此它会为自身和以后的堆栈操作重置 RSP。
在递归 call
之前的任何额外推送意味着每个堆栈深度使用 8 个额外字节的堆栈空间,但 Linux 用户空间堆栈默认为 8MiB,因此需要非常深的树才能接近实际溢出
在调试器(例如 GDB)中单步执行您的代码以查看其运行情况。
使用 display /x *(long (*)[5])$rsp
在每一步后从堆栈中转储前 5 个 qwords(通过将其转换为指向数组的指针并取消引用)。然后 stepi
浏览您的代码,看看它是如何变化的,尤其是在 leave
处。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。