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




Reply via email to