From af3c78886fd8d4ca5eebdbe581a459a6f6d29d6a Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Fri, 30 Sep 2022 15:26:30 -0700 Subject: Alter the prototype of qsort_r(3) to match POSIX, which adopted the glibc-based interface. Unfortunately, the glibc maintainers, despite knowing the existence of the FreeBSD qsort_r(3) interface in 2004 and refused to add the same interface to glibc based on grounds of the lack of standardization and portability concerns, has decided it was a good idea to introduce their own qsort_r(3) interface in 2007 as a GNU extension with a slightly different and incompatible interface. With the adoption of their interface as POSIX standard, let's switch to the same prototype, there is no need to remain incompatible. C++ and C applications written for the historical FreeBSD interface get source level compatibility when building in C++ mode, or when building with a C compiler with C11 generics support, provided that the caller passes a fifth parameter of qsort_r() that exactly matches the historical FreeBSD comparator function pointer type and does not redefine the historical qsort_r(3) prototype in their source code. Symbol versioning is used to keep old binaries working. MFC: never Relnotes: yes Reviewed by: cem, imp, hps, pauamma Differential revision: https://reviews.freebsd.org/D17083 --- include/stdlib.h | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/stdlib.h b/include/stdlib.h index bf1a612190ee..754e8f5f5fd4 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -311,8 +311,8 @@ int mergesort_b(void *, size_t, size_t, int (^)(const void *, const void *)); int mkostemp(char *, int); int mkostemps(char *, int, int); int mkostempsat(int, char *, int, int); -void qsort_r(void *, size_t, size_t, void *, - int (*)(void *, const void *, const void *)); +void qsort_r(void *, size_t, size_t, + int (*)(const void *, const void *, void *), void *); int radixsort(const unsigned char **, int, const unsigned char *, unsigned); void *reallocarray(void *, size_t, size_t) __result_use_check @@ -332,6 +332,40 @@ __int64_t __uint64_t strtouq(const char *, char **, int); +/* + * In FreeBSD 14, the prototype of qsort_r() was modified to comply with + * POSIX. The standardized qsort_r()'s order of last two parameters was + * changed, and the comparator function is now taking thunk as its last + * parameter, and both are different from the ones expected by the historical + * FreeBSD qsort_r() interface. + * + * Apply a workaround where we explicitly link against the historical + * interface, qsort_r@FBSD_1.0, in case when qsort_r() is called with + * the last parameter with a function pointer that exactly matches the + * historical FreeBSD qsort_r() comparator signature, so applications + * written for the historical interface can continue to work without + * modification. + */ +#if defined(__generic) || defined(__cplusplus) +void __qsort_r_compat(void *, size_t, size_t, void *, + int (*)(void *, const void *, const void *)); +__sym_compat(qsort_r, __qsort_r_compat, FBSD_1.0); +#endif +#if defined(__generic) && !defined(__cplusplus) +#define qsort_r(base, nel, width, arg4, arg5) \ + __generic(arg5, int (*)(void *, const void *, const void *), \ + __qsort_r_compat, qsort_r)(base, nel, width, arg4, arg5) +#elif defined(__cplusplus) +__END_DECLS +extern "C++" { +static inline void qsort_r(void *base, size_t nmemb, size_t size, + void *thunk, int (*compar)(void *, const void *, const void *)) { + __qsort_r_compat(base, nmemb, size, thunk, compar); +} +} +__BEGIN_DECLS +#endif + extern char *suboptarg; /* getsubopt(3) external variable */ #endif /* __BSD_VISIBLE */ -- cgit v1.3