1

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               
Jongware
  • 22,200
  • 8
  • 54
  • 100
IchMagBier
  • 29
  • 5
  • 4
    `int $0x80` is for making 32 bit system calls. In 64 bit assembly, you should use 64 bit system calls which use different numbers and a different calling convention. It is very much possible that your code doesn't work because the stack is not located within the first 4 GiB of address space. If you use actual 64 bit system calls, it should work. – fuz Jan 28 '18 at 18:31
  • It doesnt work with "syscall" for me. See my edited code. – IchMagBier Jan 28 '18 at 18:43
  • For future readers: this *was* a duplicate of [What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?](https://stackoverflow.com/questions/46087730/what-happens-if-you-use-the-32-bit-int-0x80-linux-abi-in-64-bit-code), but then the OP's edit to use `syscall` in response to comments changed it into a different question because it introduced a new bug: using `%rbp` instead of `%rsp` (as well as deleting the `sys_exit`, so this is now an incomplete code fragment...). Anyway, this new question is the one that I answered; reopening. – Peter Cordes Jan 29 '18 at 11:51

2 Answers2

5

After pushing 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.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
0

I figured it out on myself. This works:

#call nanosleep    
movq $35,%rax
subq $16, %rbp
movq %rbp,%rdi  
movq $0,%rsi          
syscall             
leave  
IchMagBier
  • 29
  • 5
  • 2
    You're making this overcomplicated: after the `push`es, `mov %rsp, %rdi`. RSP (the current stack pointer) is what's pointing to your newly-pushed struct, not the frame pointer. `lea -16(%rbp), %rdi` would also work, but **modifying `%rbp` before `leave` is a *bad* idea**. That will set `RSP` to the updated `RBP` and then pop the struct into `RBP`, instead of the caller's saved `RBP`. This probably only works because you don't actually `ret` anywhere, and just use `sys_exit` after `leave`. – Peter Cordes Jan 29 '18 at 01:11