Reputation: 39
I have some problems with Linux' nanosleep syscall. This code should wait 2 seconds before it exits, but it doesn't:
.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
Upvotes: 2
Views: 2360
Reputation: 365237
After push
ing stuff on the stack, use mov %rsp, %rdi
. RSP (the current stack pointer) is what's pointing to your newly-pushed struct, not RBP (the frame pointer). lea (%rsp), %rdi
is a less-efficient way to write that, but would also work.
You're passing RBP
as the pointer, but it still points to the saved RBP value from making a "stack frame". Note that is _start
, not a function, so you're really just terminating the linked list of saved-RBP values. The System V ABI recommends doing this by explicitly setting RBP to zero, but Linux zeros registers (other than RSP) on process startup so this works.
Anyway, at _start
, (rsp)
is argc
, and then you push a 0
(the saved RBP) and set RBP to point there. So the struct you're passing to sys_nanosleep
is {0, argc}
. Or argc
nanoseconds. (Test with strace
to see if I got this right; I didn't try it.)
This is what you should do:
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.
I also optimized by not using 64-bit operand-size when you don't need it (because writing a 32-bit register zeros the upper 32 bits). I like letting register sizes imply operand size instead of using movq
, like in Intel syntax. I also used the idiomatic way to zero a register, and improving the comments.
Your proposed answer is broken: subq $16, %rbp
before leave
is bad idea.
If you want to address your newly-pushed struct relative to your RBP stack frame, you could lea -16(%rbp), %rdi
.
But modifying %rbp
will make leave
set RSP
to the updated RBP
and then pop the low qword of the struct into RBP
, instead of the caller's saved RBP
. RSP is left pointing to the high qword of your struct, rather than the function return address.
This probably only works because you just use sys_exit
after leave
, because you're not in a function so you couldn't ret
anyway. It makes no sense to use leave
in _start
, because it's not a function. You have to just sys_exit
or sys_exit_group
.
But if you used this fragment inside an actual function, it would break the stack.
Upvotes: 7
Reputation: 39
I figured it out on myself. This works:
#call nanosleep
movq $35,%rax
subq $16, %rbp
movq %rbp,%rdi
movq $0,%rsi
syscall
leave
Upvotes: 0