Search code examples
cassemblyx86-64nasmcalling-convention

Calling a C function from Assembly -- switching calling convention for functions with more than 6 args?


I have an assembly application for Linux x64 where I pass arguments to the functions via registers, thus I'm using a certain calling convention, in this case fastcall. Now I want to call a C function from the assembly application which, say, expects 10 arguments.

Do I have to switch to cdecl for that and pass the arguments via stack regardless of the fact everywhere else in my application I'm passing them via registers? Is it allowed to mix calling conventions in one application?


Solution

  • I assume that by fastcall, you mean the amd64 calling convention used by the SysV ABI (i.e. what Linux uses) where the first few arguments are passed in rdi, rsi, and rdx.

    The ABI is slightly complicated, the following is a simplification. You might want to read the specification for details.

    Generally speaking, the first few (leftmost) integer or pointer arguments are placed into the registers rdi, rsi, rdx, rcx, r8, and r9. Floating point arguments are passed in xmm0 to xmm7. If the register space is exhausted, additional arguments are passed through the stack from right to left. For example, to call a function with 10 integer arguments:

    foo(a, b, c, d, e, f, g, h, i, k);
    

    you would need code like this:

    mov $a,%edi
    mov $b,%esi
    mov $c,%edx
    mov $d,%ecx
    mov $e,%r8d
    mov $f,%r9d
    push $k
    push $i
    push $h
    push $g
    call foo
    add $32,%rsp
    

    For your concrete example, of getnameinfo:

    int getnameinfo(
        const struct sockaddr *sa,
        socklen_t salen,
        char *host,
        size_t hostlen,
        char *serv,
        size_t servlen,
        int flags);
    

    You would pass sa in rdi, salen in rsi, host in rdx, hostlen in rcx, serv in r8, servlen in r9 and flags on the stack.