1

If a syscall needs some 32-bit arguments like uid_t or int (for file descriptors) or unsigned int or even some 16-bit type, how can I pass them using 64-bit registers?

Do I need to zero-extend or sign-extend them to 64-bit before I use the syscall instruction?

What if I use __X32_SYSCALL_BIT in RAX so the original 64-bit pointer-type argument becomes 32-bit, I still need to use the same 64-bit register to pass the argument, do I need to zero-extend address arguments in such cases?

1 Answer 1

2

You‘re supposed to zero-extend them, but for the common case of 32-bit values on x86-64, there’s no need to think about it: storing a value in a 32-bit register results in a zero-extended store to the corresponding 64-bit register (i.e., movl $4, %edx stores 4 in rdx). 8- and 16-bit values should be explicitly zero-extended (movzbl or movzwl from an 8- or 16-bit register to a 32-bit register, implicitly zero-extending to 64 bits).

In practice, for non-pointers, the system call implementation will only read the lower n bits anyway, so you shouldn’t see any practical difference, at least for 32-bit values. (E.g. calling SYS_read with rubbish in the top 32 bits of RDI doesn’t produce an error, and only takes the lower 32 bits into account.) I haven’t checked what happens if you set __X32_SYSCALL_BIT without clearing the top 32 bits of pointers.

2
  • What if I don't zero-extend them? Will I get -ENIVAL (32bit two's complement represention in %eax and zero-extended to 64bit) returned in %rax? Are such returned 32-bit value in %rax are guaranteed to be zero-extended? Feb 7, 2019 at 13:29
  • 1
    @炸鱼薯条德里克 my tests suggest you won’t get an error. Return values are supposed to be interpreted according to their type’s size; if a system call returns in int, you can’t read all of RAX and consider that it’s the correct value. In practice they are zero-extended. Feb 7, 2019 at 14:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .