1
votes

I want to write programs in SysV ABI x86_64 assembly and so far I have passed the arguments in registers quite randomly.

But I just saw on this forum, that there is a standard for this. We must pass RDI, RSI, RDX and RCX (int that exact order).

Now I am asking myself two questions.

First, are not ESI and EDI supposed to be used only during operations on strings? What happens if I want to pass an integer as an argument and not a string?

Secondly, what if I need to pass a 32-bit argument and not a 64-bit argument? For example, if I want to create an identifier for the system call write, I would write this:

;; void write(int fd, const void *buf, size_t count);
;; Inputs   :  ESI = offset string, EDX = number of characters to write, EBX = file descriptor
;; Outputs  :  <none>
;; Clobbers :  <none>
write:
    mov ecx, esi

    mov eax, 4
    int 0x80

    ret

But with the standard, how can I move the values from 64-bit registers to 32-bit registers? Because I can't do that:

mov ecx, rdi ; impossible
1
related: What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? - a normal write wrapper would be mov eax, 1 / syscall, because the x86-64 Linux system-calling convention is close to the x86-64 System V function-calling convention, matching for the first 3 args.Peter Cordes

1 Answers

3
votes

In general rdi and rsi can be treated as general purpose registers, i.e. you can use them for arbitrary arithmetic and memory operations. They have certain special meanings, as they are also used as index registers for string operations. However, the architecture does not care whether you store a string pointer or another arbitrary 64-bit number in there.


Regarding passing 32-bit values, you can simply access the lower 32-bit portion of your source register:

mov ecx, edi

This only moves the least-significant 32 bits to ecx. Note that it does not really make a difference if you pass the entire 64 bits instead - if the callee only accesses the 32-bit sub register ecx, the result is the same:

mov rcx, rdi
; ...
; use ecx

A small note regarding the example code in the question: You seem to be using a 32-bit style system call in a 64-bit environment. This may break if the pointer to buf does not fit into 32 bits. A 64-bit version of the write system call would look like this:

write:
    ; syscall number
    mov rax, 1

    ; all other arguments are already in the right registers
    syscall

    ret

Further information: