我在Linux'nanosleep系统调用方面遇到了一些问题。此代码应在退出前等待2秒,但不会:
.text
.globl _start
_start:
pushq %rbp
movq %rsp,%rbp
pushq $0 #0 nanoseconds
pushq $2 #2 seconds
leaq (%rbp),%rdi #the time structure on the stack
movq $35,%rax #nanosleep syscall
movq $0,%rsi #disable useless parameter
syscall
leave
答案 0 :(得分:2)
在堆栈上push
之后,使用mov %rsp, %rdi
。 RSP(当前堆栈指针)指向新推送的结构,而不是RBP(帧指针)。 lea (%rsp), %rdi
是一种效率较低的写法,但也可以。
您正在将RBP
作为指针传递,但它仍然指向保存的RBP值来制作"堆栈帧"。请注意,它是_start
,而不是函数,因此您实际上只是终止已保存RBP值的链接列表。 System V ABI建议通过显式将RBP设置为零来执行此操作,但是在进程启动时Linux 0会注册(RSP除外),因此可以正常工作。
无论如何,在_start
,(rsp)
为argc
,然后您推送0
(已保存的RBP)并将RBP设置为指向那里。因此,您传递给sys_nanosleep
的结构是{0, argc}
。或argc
纳秒。 (使用strace
进行测试,看看我是否做到了这一点;我没有尝试过。)
这是你应该做的:
pushq $0 #0 nanoseconds
pushq $2 #2 seconds
### RSP instead of RBP in the next instruction:
mov %rsp, %rdi #the time structure we just pushed
mov $35, %eax #SYS_nanosleep
xor %esi, %esi #rem=NULL, we don't care if we wake early
syscall
# RSP is 16 bytes lower than it was before this fragment, in case that matters for later code.
当你不需要64位操作数时,我也进行了优化(因为写32位寄存器会使高32位为零)。我喜欢让寄存器大小暗示操作数大小而不是使用movq
,就像在Intel语法中一样。我还使用惯用的方法将寄存器归零,并改进了注释。
您的建议答案已被删除:subq $16, %rbp
leave
错误想法之前。
如果要解决相对于RBP堆栈帧的新推送的结构,可以lea -16(%rbp), %rdi
。
但修改%rbp
会使leave
将RSP
设置为更新的RBP
,然后将结构的低qword弹出到RBP
,而不是来电者保存RBP
。 RSP指向结构的高qword,而不是函数返回地址。
这可能之所以有效,是因为您只是在sys_exit
之后使用leave
,因为您不在函数中,所以无论如何都不能ret
。在leave
中使用_start
是没有意义的,因为它不是一个函数。您必须sys_exit
或sys_exit_group
。
但是如果你在实际函数中使用了这个片段,那么它会破坏堆栈。
答案 1 :(得分:-1)
我自己想通了。这有效:
#call nanosleep
movq $35,%rax
subq $16, %rbp
movq %rbp,%rdi
movq $0,%rsi
syscall
leave