如何在x86程序集中使用“nanosleep”?

时间:2018-01-28 18:25:40

标签: linux assembly x86

我在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               

2 个答案:

答案 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会使leaveRSP设置为更新的RBP,然后将结构的低qword弹出到RBP,而不是来电者保存RBP。 RSP指向结构的高qword,而不是函数返回地址。

这可能之所以有效,是因为您只是在sys_exit之后使用leave,因为您不在函数中,所以无论如何都不能ret。在leave中使用_start是没有意义的,因为它不是一个函数。您必须sys_exitsys_exit_group

但是如果你在实际函数中使用了这个片段,那么它会破坏堆栈。

答案 1 :(得分:-1)

我自己想通了。这有效:

#call nanosleep    
movq $35,%rax
subq $16, %rbp
movq %rbp,%rdi  
movq $0,%rsi          
syscall             
leave