Home > database >  How are variadic parameters represented on the stack when they are forwarded multiple times?
How are variadic parameters represented on the stack when they are forwarded multiple times?

Time:07-14

void fun2(char *format, ...){
   va_list arg_list;
   va_start(arg_list, format);
   vprintf(format, arg_list);
   va_end(arg_list);
}

void fun1(char *format, ...){
   fun2(format);
}

int main(){
   fun1("test: %d", 100);
}

Output:

test: 100

https://onlinegdb.com/OfdDeSJg_

Does the above example have something wrong or not recommended?

I guess that when the fun2(format); call is made, only the pointer to the first parameter (format) is passed, is that correct?

When vprintf in fun2 accesses the integer 100, where is this integer? in the stack reserved for fun1, in the stack reserved for fun2, in the stack reserved for vprintf, or somewhere else?

If as I imagine only the pointer to the first parameter is passed to fun2, does this mean that when the functions and macros called by fun2 access the integer 100 they are accessing the stack reserved for fun1?

CodePudding user response:

They aren't forwarded. This code doesn't work, unless you are lucky.

If you are lucky, it's probably because the optimizer saw that format stays in the same argument position (register or the stack), so fun1 doesn't actually have any "real" code in it (it doesn't move any arguments around), so it changed fun1 to a jump instruction that directly jumps to fun2 (a.k.a. tail-call optimization). Then since fun1 didn't mess with any of the arguments and didn't create a stack frame, they're still all in the right positions for calling a void (char*, ...) function, where fun1 picks them up.

i.e. the assembly code is probably something like this:

// Pretend calling convention: format is in ecx, and varargs on the stack

fun2:
    // actual code would go here
    ret

fun1:
    // jmp instruction doesn't affect the stack, nor ecx
    // so fun2 receives exactly the same inputs as if main
    // had directly called fun2
    jmp fun2

main:
    mov ecx, "test: %d"
    push 100
    call fun1
    ret

You can't rely on it. If you want to reliably forward varargs you either change your function to use a va_list parameter (like vprintf) or you write your own assembly code.

  • Related