Search code examples
cassemblyx86-64variadic-functionscalling-convention

Is it possible to count the function (variable) arguments in a __cdecl with Inline ASM?


Is it possible for the callee to iterate (and count) through the function call parameters by offsetting the stack base pointer (rbp) using the inline ASM (x86) without knowing the type or quantity of the arguments?

void foo(char *arg, ...);

I am using Intel compiler but its documentation states that it supports GCC style inline assembly. So GCC based example would be sufficient.

#include <stdio.h>
#include <inttypes.h>

int    main(int argc, char **argv){

    uint64_t n;

    __asm__ __volatile__(
      "movq %%rbp, %0\n\t"
      : "=r"(n)
    );

    printf("rbp = 0x%" PRIx64 "\n", n);
    return 0;
}

the code in this post


Solution

  • The only possible way for this to work is with a unique sentinel value (e.g. a NULL pointer) that marks the last argument.

    This normally only works when all the args are pointers, e.g. as used by the POSIX execl(3) functions with signatures like

    int execl(const char *pathname, const char *arg, ...
                       /* (char  *) NULL */);
    

    (Then you don't need inline asm; you can just use C VA_ARG macros.)


    Also, rbp is useless; you don't know whether or not the function was compiled with optimization enabled so it might not be a frame pointer at all. If you want the frame address in GNU C, use __builtin_frame_address(0). (Look at the compiler-generated asm to see what it does. IIRC, it forces -fno-omit-frame-pointer for that function and just gives you the value of rbp)

    And even getting a stack-frame address doesn't help you get register args (first 6 integer/pointer and first 8 FP args in x86-64 System V. Or first 4 total args in Windows x64.)

    Declare your function as variadic and Use VA_ARG to read all the variadic args as uint64_t so you can check them for zero, or whatever bit-pattern you selected as your sentinel.

    Obviously this requires the cooperation of the caller to pass a sentinel.


    BTW, no calling convention called __cdecl exists for x86-64. Microsoft calls theirs x64 __fastcall or __vectorcall.