Pointer to va_list in amd64 ABI

I have a problem with variable functions under Linux amd64 (x86_64).

My example builds and works fine on linux i386 (ia32), but when building for linux amd64, GCC produces the following errors:

stdarg.c: In function 'vtest': stdarg.c:21:5: attention : passing argument 2 of 'vptest' from incompatible pointer type [enabled by default] stdarg.c:5:1: note: expected 'struct __va_list_tag (*)[1]' but argument is of type 'struct __va_list_tag **' 

Here is an example:

 #include <stdio.h> #include <stdarg.h> static int vptest(int count, va_list *a) { printf("%8s: a = %p\n", __func__, a); printf("%8s: %d: %d\n", __func__, count, va_arg(*a, int)); return 0; } static int vtest(int count, va_list ap) { printf("%8s: &ap = %p\n", __func__, &ap); /* passing a pointer to ap allows ap to be used again in the calling function */ for(; count > 1; count --) { vptest(count, &ap); } if (count) { printf("%8s: %d: %d\n", __func__, count, va_arg(ap, int)); } return 0; } static int test(int count, ...) { va_list ap; va_start(ap, count); printf("%8s: &ap = %p\n", __func__, &ap); /* after passing ap to subfunction, this function must not use ap again without calling va_start */ vtest(count, ap); va_end(ap); return 0; } int main(void) { test(4, 1, 2, 3, 4); return 0; } 

According to project C11 ( ISO / IEC 9899: 2011 )

The ap object can be passed as an argument to another function; if this function calls the va_arg macro with the ap parameter, the ap value in the calling function is undefined and must be passed to the va_end macro before any additional reference to ap.

But the latter adds

It is allowed to create a pointer to va_list and pass this pointer to another function, in which case the original function can additionally use the original list after returning another function.

It is not clear to me if here the AMD 64 ABI is incorrectly related to the standard.

Changing the vtest() function to use a pointer on the first call resolves the problem, but not so that something that does not work in internal functions actually works in an external function.

 @@ -12,16 +12,16 @@ } static int -vtest(int count, va_list ap) +vtest(int count, va_list *a) { - printf("%8s: &ap = %p\n", __func__, &ap); + printf("%8s: a = %p\n", __func__, a); /* passing a pointer to ap allows ap to be used again in the calling function */ for(; count > 1; count --) { - vptest(count, &ap); + vptest(count, a); } if (count) { - printf("%8s: %d: %d\n", __func__, count, va_arg(ap, int)); + printf("%8s: %d: %d\n", __func__, count, va_arg(*a, int)); } return 0; @@ -37,7 +37,7 @@ printf("%8s: &ap = %p\n", __func__, &ap); /* after passing ap to subfunction, this function must not use ap again without calling va_start */ - vtest(count, ap); + vtest(count, &ap); va_end(ap); 

If someone can find something, if AMD64 ABI will meet the standard. Extra points for people who provide me with other ABI (same) problems when using stdarg.

Hi

+4
source share
1 answer

The behavior is consistent, because although the vtest argument vtest written as va_list ap , ap not of type va_list , but any type of pointer va_list splits into. This is appropriate because va_list permitted by the standard as an array type. The solution to this problem is to use va_copy to copy ap to the local va_list :

 va_list ap2; va_copy(ap2, ap); // ... vptest(count, &ap2); // ... va_end(ap2); 

Since the definition and type of ap2 are under your control, &ap2 is the correct type to go to vptest .

+3
source

Source: https://habr.com/ru/post/1402507/


All Articles