rom-properties/cmake/macros/IfuncTest.c
David Korth 28857f550e Use gcc's built-in functions to detect CPU flags in IFUNC resolvers.
Due to random shenanigans and symbol ordering, libromdata started
crashing because the linker was attempting to resolve the IFUNC
functions before the rpcpu functions were available. Use gcc's
built-in CPU support functions instead.

(It *was* working for me yesterday, then I rebooted and it stopped
working until I made this change.)

This requires gcc-4.8+ or clang-6.0+.

An alternative workaround is using lazy binding (-Wl,-z,lazy), but this
could just delay the crash until later.

IfuncTest.c: Test for the CPU support functions and disable IFUNC
if they aren't available.
2022-06-23 17:59:50 -04:00

74 lines
1.8 KiB
C

/**
* IFUNC test code
*/
#include <stdlib.h>
#include <time.h>
// Minimum glibc version requirements.
#if defined(__GLIBC__)
# if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 11)
# error glibc-2.11 is required for IFUNC on most platforms
# elif defined(__arm__) || defined(__thumb__) || \
defined(__arm) || defined(_ARM) || \
defined(_M_ARM) || defined(__aarch64__)
# if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 18)
# error glibc-2.18 is required for IFUNC on ARM
# endif
# endif
#endif
static int ifunc_method_impl1(int a, long b, char c)
{
return (int)(a * b + c);
}
static int ifunc_method_impl2(int a, long b, char c)
{
return (int)(a * b + c) * 2;
}
static int ifunc_method_impl3(int a, long b, char c)
{
return (int)(a * b + c) * 3;
}
static int ifunc_method_impl4(int a, long b, char c)
{
return (int)(a * b + c) * 4;
}
static __typeof__(&ifunc_method_impl1) ifunc_method_resolve(void)
{
// NOTE: Since libromdata is a shared library now, IFUNC resolvers
// cannot call PLT functions. Otherwise, it will crash.
// We'll use gcc's built-in CPU ID functions instead.
// Requires gcc-4.8 or later, or clang-6.0 or later.
time_t s = time(NULL);
__builtin_cpu_init();
if (__builtin_cpu_supports("sse2")) s ^= 3;
switch (s & 3) {
default:
case 0: return &ifunc_method_impl1;
case 1: return &ifunc_method_impl2;
case 2: return &ifunc_method_impl3;
case 3: return &ifunc_method_impl4;
}
return NULL;
}
int ifunc_method(int a, long b, char c)
__attribute__((ifunc("ifunc_method_resolve")));
int main(void)
{
// (5 * 7) + 3 == 38
int a = 5;
long b = 7;
char c = 3;
int ret = ifunc_method(a, b, c);
if (ret == 38 || ret == 38*2 || ret == 38*3 || ret == 38*4)
return EXIT_SUCCESS;
return EXIT_FAILURE;
}