Hi all,
When migrating C programs from C99/C11/C17 to C23, function pointer types
need to be updated:
A function type with parameter list () meant "unspecified arguments".
This does not exist any more in C23, as a parameter list () now means
"no arguments".
One has to use a function type with parameter list (...), which means a
variable number of arguments.
Quite simple and easy, I thought.
But not on macOS. On macOS, functions with a parameter list (...) have
a different calling convention [1]. Code that worked in C17 with ()
does not work in C23 with (...) any more. How to reproduce:
==================================== foo.c ====================================
#include <stdio.h>
typedef int (*old_generic_function_t) ();
#if __STDC_VERSION__ >= 202300 /* Avoid error "ISO C requires a named parameter
before '...'" */
typedef int (*new_generic_function_t) (...);
#endif
int
f (int x)
{
return x + 42;
}
old_generic_function_t fp1 = (old_generic_function_t) f;
#if __STDC_VERSION__ >= 202300
new_generic_function_t fp2 = (new_generic_function_t) f;
#endif
int (*fp3) (int) = f;
int
main ()
{
#if !(defined __cplusplus || __STDC_VERSION__ >= 202300) /* Avoid error "too
many arguments to function" */
printf ("Calling f through old_generic_function_t: %d\n", fp1 (100));
#endif
#if __STDC_VERSION__ >= 202300
printf ("Calling f through new_generic_function_t: %d\n", fp2 (1000));
#endif
printf ("Calling f through precise function type: %d\n", fp3 (10000));
}
===============================================================================
On all other platforms:
$ gcc -std=c17 foo.c
$ ./a.out
Calling f through old_generic_function_t: 142
Calling f through precise function type: 10042
$ gcc -std=c23 foo.c
$ ./a.out
Calling f through new_generic_function_t: 1042
Calling f through precise function type: 10042
With Apple cc (clang) on macOS/arm64:
$ cc -std=c17 foo.c
foo.c:27:65: warning: passing arguments to a function without a prototype is
deprecated in all versions of C and is not supported in C23
[-Wdeprecated-non-prototype]
27 | printf ("Calling f through old_generic_function_t: %d\n", fp1 (100));
| ^
1 warning generated.
$ ./a.out
Calling f through old_generic_function_t: 142
Calling f through precise function type: 10042
$ cc -std=c23 foo.c
$ ./a.out
Calling f through new_generic_function_t: 43 <== UNEXPECTED! should be
1042.
Calling f through precise function type: 10042
With /opt/homebrew/bin/gcc-16 on macOS/arm64:
$ /opt/homebrew/bin/gcc-16 -std=c17 foo.c
$ ./a.out
Calling f through old_generic_function_t: 142
Calling f through precise function type: 10042
$ /opt/homebrew/bin/gcc-16 -std=c23 foo.c
$ ./a.out
Calling f through new_generic_function_t: 14747170 <== UNEXPECTED! should be
1042.
Calling f through precise function type: 10042
What has changed is the argument passing convention:
An integer argument gets passed to a () function in $x0,
but to a (...) function on the stack.
Does anyone see a workaround?
It does affect GNU libffcall: the 'trampoline' API on macOS/C23 is broken.
It may also affect libffi; I don't know.
I suspect that it also affects many other packages that use "generic"
function types in one form or the other.
Bruno
[1]
https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms