LibC: Make ctype functions inlinable and have external linkage

Before this commit, you could either use the inlinable function by
using the macro or have the externally-linked version by first
undefining the macro and then using the function.

This commit makes all the ctype functions both inlinable and have
external linkage at the same time. To pull this trick, we provide
inlinable definitions in ctype.h and use a dedicated C feature that
forces the symbol to be emitted in the TU of ctype.c.

This could also have been achieved by using some #ifdef to hide the
inline definition in ctype.h so a C++ compiler would still emit a normal
symbol for the functions in ctype.cpp.
This commit is contained in:
Lucas Chollet committed 2026-09-24 11:53:15 +02:00
1 parent 8a1ff05985
commit 33fc5740a2
6 files changed
+100 -199

No files matched your search

+6 -1
View File
@@ -5,12 +5,17 @@ set(SOURCES
add_library(DynamicLoader_CompileOptions INTERFACE)
target_compile_definitions(DynamicLoader_CompileOptions INTERFACE NO_TLS _DYNAMIC_LOADER)
target_compile_options(DynamicLoader_CompileOptions INTERFACE -fno-rtti -fpie -ffunction-sections -fdata-sections)
target_compile_options(DynamicLoader_CompileOptions INTERFACE $<$<COMPILE_LANGUAGE:CXX>:-fno-rtti> -fpie -ffunction-sections -fdata-sections)
target_link_options(DynamicLoader_CompileOptions INTERFACE -nolibc -nostdlib++ -nostartfiles -static-libgcc -fpie -Wl,--gc-sections)
target_link_options(DynamicLoader_CompileOptions INTERFACE -fno-sanitize=undefined) # Sanitizer runtime is linked in manually
add_executable(Loader.so ${SOURCES})
# Mixing C and C++ results in linker errors with GCC > 16 and CMake < 4.0.
# The issue is fixed upstream in https://gitlab.kitware.com/cmake/cmake/-/merge_requests/12027.
# FIXME: Remove this workaround once we require a CMake version >= 4.1.
list(REMOVE_ITEM CMAKE_C_IMPLICIT_LINK_LIBRARIES "gcc_s_asneeded")
target_link_libraries(Loader.so PRIVATE DynamicLoader_CompileOptions DynamicLoader_LibELF DynamicLoader_LibCoreArgsParser)
if (ENABLE_UNDEFINED_SANITIZER)
target_link_libraries(Loader.so PRIVATE DynamicLoader_LibUBSanitizer)
+1 -1
View File
@@ -3,7 +3,7 @@ set(SOURCES
arch/${SERENITY_ARCH}/setjmp.S
arpa/inet.cpp
assert.cpp
ctype.cpp
ctype.c
cxxabi.cpp
dirent.cpp
dlfcn.cpp
+63
View File
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <ctype.h>
char const _ctype_[1 + 256] = {
0,
_C, _C, _C, _C, _C, _C, _C, _C,
_C, _C | _S, _C | _S, _C | _S, _C | _S, _C | _S, _C, _C,
_C, _C, _C, _C, _C, _C, _C, _C,
_C, _C, _C, _C, _C, _C, _C, _C,
(char)(_S | _B), _P, _P, _P, _P, _P, _P, _P,
_P, _P, _P, _P, _P, _P, _P, _P,
_N, _N, _N, _N, _N, _N, _N, _N,
_N, _N, _P, _P, _P, _P, _P, _P,
_P, _U | _X, _U | _X, _U | _X, _U | _X, _U | _X, _U | _X, _U,
_U, _U, _U, _U, _U, _U, _U, _U,
_U, _U, _U, _U, _U, _U, _U, _U,
_U, _U, _U, _P, _P, _P, _P, _P,
_P, _L | _X, _L | _X, _L | _X, _L | _X, _L | _X, _L | _X, _L,
_L, _L, _L, _L, _L, _L, _L, _L,
_L, _L, _L, _L, _L, _L, _L, _L,
_L, _L, _L, _P, _P, _P, _P, _C
};
// "The following shall be declared as functions and may also be defined as macros."
// This means that these functions need to be declared with external linkage.
// For performance reasons, we want these functions to be inlinable so we provide
// the definition in the header file. This however causes compatibility issues
// between C and C++.
// In C++, the compiler generates a symbol for inline functions at will. In the end,
// multiple TUs might include one (in this case the linker takes care of only keeping
// one) or if the function is always inlined, no symbols are emitted at all, which
// conflicts with POSIX requirements.
// In C, the linker isn't required to de-duplicate symbols emitted from inline
// functions, so the compiler doesn't emit symbols for inline functions and it falls
// back on the user to provide one if needed.
// To solve this issue we use a dedicated C feature that anchors the function
// definition in this exact TU.
// This link provides useful information on the different meanings of inline:
// https://stackoverflow.com/questions/216510/what-does-extern-inline-do
__attribute__((gnu_inline)) inline int isalnum(int c);
__attribute__((gnu_inline)) inline int isalpha(int c);
__attribute__((gnu_inline)) inline int isascii(int c);
__attribute__((gnu_inline)) inline int iscntrl(int c);
__attribute__((gnu_inline)) inline int isdigit(int c);
__attribute__((gnu_inline)) inline int isxdigit(int c);
__attribute__((gnu_inline)) inline int isspace(int c);
__attribute__((gnu_inline)) inline int ispunct(int c);
__attribute__((gnu_inline)) inline int isprint(int c);
__attribute__((gnu_inline)) inline int isgraph(int c);
__attribute__((gnu_inline)) inline int islower(int c);
__attribute__((gnu_inline)) inline int isupper(int c);
__attribute__((gnu_inline)) inline int isblank(int c);
__attribute__((gnu_inline)) inline int toascii(int c);
__attribute__((gnu_inline)) inline int tolower(int c);
__attribute__((gnu_inline)) inline int toupper(int c);
-126
View File
@@ -1,126 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <ctype.h>
extern "C" {
char const _ctype_[1 + 256] = {
0,
_C, _C, _C, _C, _C, _C, _C, _C,
_C, _C | _S, _C | _S, _C | _S, _C | _S, _C | _S, _C, _C,
_C, _C, _C, _C, _C, _C, _C, _C,
_C, _C, _C, _C, _C, _C, _C, _C,
(char)(_S | _B), _P, _P, _P, _P, _P, _P, _P,
_P, _P, _P, _P, _P, _P, _P, _P,
_N, _N, _N, _N, _N, _N, _N, _N,
_N, _N, _P, _P, _P, _P, _P, _P,
_P, _U | _X, _U | _X, _U | _X, _U | _X, _U | _X, _U | _X, _U,
_U, _U, _U, _U, _U, _U, _U, _U,
_U, _U, _U, _U, _U, _U, _U, _U,
_U, _U, _U, _P, _P, _P, _P, _P,
_P, _L | _X, _L | _X, _L | _X, _L | _X, _L | _X, _L | _X, _L,
_L, _L, _L, _L, _L, _L, _L, _L,
_L, _L, _L, _L, _L, _L, _L, _L,
_L, _L, _L, _P, _P, _P, _P, _C
};
#undef isalnum
int isalnum(int c)
{
return __inline_isalnum(c);
}
#undef isalpha
int isalpha(int c)
{
return __inline_isalpha(c);
}
#undef iscntrl
int iscntrl(int c)
{
return __inline_iscntrl(c);
}
#undef isdigit
int isdigit(int c)
{
return __inline_isdigit(c);
}
#undef isxdigit
int isxdigit(int c)
{
return __inline_isxdigit(c);
}
#undef isspace
int isspace(int c)
{
return __inline_isspace(c);
}
#undef ispunct
int ispunct(int c)
{
return __inline_ispunct(c);
}
#undef isprint
int isprint(int c)
{
return __inline_isprint(c);
}
#undef isgraph
int isgraph(int c)
{
return __inline_isgraph(c);
}
#undef isupper
int isupper(int c)
{
return __inline_isupper(c);
}
#undef islower
int islower(int c)
{
return __inline_islower(c);
}
#undef isascii
int isascii(int c)
{
return __inline_isascii(c);
}
#undef isblank
int isblank(int c)
{
return __inline_isblank(c);
}
#undef toascii
int toascii(int c)
{
return __inline_toascii(c);
}
#undef tolower
int tolower(int c)
{
return __inline_tolower(c);
}
#undef toupper
int toupper(int c)
{
return __inline_toupper(c);
}
}
+16 -57
View File
@@ -31,129 +31,88 @@ __BEGIN_DECLS
*/
extern char const _ctype_[1 + 256] __attribute__((visibility("default")));
static inline int __inline_isalnum(int c)
extern inline __attribute__((gnu_inline)) int isalnum(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_U | _L | _N);
}
static inline int __inline_isalpha(int c)
extern inline __attribute__((gnu_inline)) int isalpha(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_U | _L);
}
static inline int __inline_isascii(int c)
extern inline __attribute__((gnu_inline)) int isascii(int c)
{
return (unsigned)c <= 127;
}
static inline int __inline_iscntrl(int c)
extern inline __attribute__((gnu_inline)) int iscntrl(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_C);
}
static inline int __inline_isdigit(int c)
extern inline __attribute__((gnu_inline)) int isdigit(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_N);
}
static inline int __inline_isxdigit(int c)
extern inline __attribute__((gnu_inline)) int isxdigit(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_N | _X);
}
static inline int __inline_isspace(int c)
extern inline __attribute__((gnu_inline)) int isspace(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_S);
}
static inline int __inline_ispunct(int c)
extern inline __attribute__((gnu_inline)) int ispunct(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_P);
}
static inline int __inline_isprint(int c)
extern inline __attribute__((gnu_inline)) int isprint(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_P | _U | _L | _N | _B);
}
static inline int __inline_isgraph(int c)
extern inline __attribute__((gnu_inline)) int isgraph(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_P | _U | _L | _N);
}
static inline int __inline_islower(int c)
extern inline __attribute__((gnu_inline)) int islower(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_L);
}
static inline int __inline_isupper(int c)
extern inline __attribute__((gnu_inline)) int isupper(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_U);
}
static inline int __inline_isblank(int c)
extern inline __attribute__((gnu_inline)) int isblank(int c)
{
return _ctype_[(unsigned char)(c) + 1] & (_B) || (c == '\t');
}
static inline int __inline_toascii(int c)
extern inline __attribute__((gnu_inline)) int toascii(int c)
{
return c & 127;
}
static inline int __inline_tolower(int c)
extern inline __attribute__((gnu_inline)) int tolower(int c)
{
if (c >= 'A' && c <= 'Z')
return c | 0x20;
return c;
}
static inline int __inline_toupper(int c)
extern inline __attribute__((gnu_inline)) int toupper(int c)
{
if (c >= 'a' && c <= 'z')
return c & ~0x20;
return c;
}
#ifdef __cplusplus
extern "C" {
#endif
int isalnum(int c);
int isalpha(int c);
int isascii(int c);
int iscntrl(int c);
int isdigit(int c);
int isxdigit(int c);
int isspace(int c);
int ispunct(int c);
int isprint(int c);
int isgraph(int c);
int islower(int c);
int isupper(int c);
int isblank(int c);
int toascii(int c);
int tolower(int c);
int toupper(int c);
#ifdef __cplusplus
}
#endif
#define isalnum __inline_isalnum
#define isalpha __inline_isalpha
#define isascii __inline_isascii
#define iscntrl __inline_iscntrl
#define isdigit __inline_isdigit
#define isxdigit __inline_isxdigit
#define isspace __inline_isspace
#define ispunct __inline_ispunct
#define isprint __inline_isprint
#define isgraph __inline_isgraph
#define islower __inline_islower
#define isupper __inline_isupper
#define isblank __inline_isblank
#define toascii __inline_toascii
#define tolower __inline_tolower
#define toupper __inline_toupper
__END_DECLS
+14 -14
View File
@@ -33,62 +33,62 @@ extern "C" {
int iswalnum(wint_t wc)
{
return __inline_isalnum(wc);
return isalnum(wc);
}
int iswalpha(wint_t wc)
{
return __inline_isalpha(wc);
return isalpha(wc);
}
int iswcntrl(wint_t wc)
{
return __inline_iscntrl(wc);
return iscntrl(wc);
}
int iswdigit(wint_t wc)
{
return __inline_isdigit(wc);
return isdigit(wc);
}
int iswxdigit(wint_t wc)
{
return __inline_isxdigit(wc);
return isxdigit(wc);
}
int iswspace(wint_t wc)
{
return __inline_isspace(wc);
return isspace(wc);
}
int iswpunct(wint_t wc)
{
return __inline_ispunct(wc);
return ispunct(wc);
}
int iswprint(wint_t wc)
{
return __inline_isprint(wc);
return isprint(wc);
}
int iswgraph(wint_t wc)
{
return __inline_isgraph(wc);
return isgraph(wc);
}
int iswlower(wint_t wc)
{
return __inline_islower(wc);
return islower(wc);
}
int iswupper(wint_t wc)
{
return __inline_isupper(wc);
return isupper(wc);
}
int iswblank(wint_t wc)
{
return __inline_isblank(wc);
return isblank(wc);
}
int iswctype(wint_t wc, wctype_t charclass)
@@ -178,12 +178,12 @@ wctype_t wctype(char const* property)
wint_t towlower(wint_t wc)
{
return __inline_tolower(wc);
return tolower(wc);
}
wint_t towupper(wint_t wc)
{
return __inline_toupper(wc);
return toupper(wc);
}
wint_t towctrans(wint_t wc, wctrans_t desc)