[PATCH v2 2/2] malloc: introduce ifuncs for malloc functions
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Wed May 13 13:32:48 GMT 2026
On 20/04/26 11:14, Yury Khrustalev wrote:
> Introduce ifuncs and resolvers for functions pertinent to the
> malloc interface.
>
> In order to do this, we first rename all core implementations by
> adding the '_core' suffix. These functions are supposed to be strictly
> internal and must not be used beyond the malloc code. For example, the
> __libc_malloc() function becomes __libc_malloc_core() and instead
> __libc_malloc() becomes an ifunc with a corresponding resolver that
> may return '__libc_malloc_core' as the implementation.
>
> Each ifunc comes with a generic resolver that will be used unless a
> target overrides the resolvers in its sysdeps directory. All resolvers
> are supposed to be overridden together, and the logic of the generic
> resolvers should be replicated by the target-specific ones.
>
> This patch contains, as an example, aarch64-specific resolvers. At this
> moment they are identical to the generic ones but in the future they can
> be changed to support for features, e.g. to handle memory tagging.
>
> Corresponding aliases are moved next to the ifuncs as well.
>
> On targets that do not support ifuncs we alias externally visible
> functions as well as the __libc_* functions to their respective
> *_core symbols.
I think the potential issues I raised on the weekly calls are from current
IFUNC limitation (like [1] and [2]), so this patch should not add any
extra corner cases.
[1] https://sourceware.org/bugzilla/show_bug.cgi?id=20673
[2] https://sourceware.org/bugzilla/show_bug.cgi?id=23240> ---
> malloc/Makefile | 6 ++
> malloc/malloc.c | 117 +++++++++++++++++------------
> malloc/tst-check-internal-calls.sh | 53 +++++++++++++
> sysdeps/aarch64/Makefile | 3 +
> sysdeps/aarch64/malloc-ifuncs.c | 85 +++++++++++++++++++++
> sysdeps/generic/Makefile | 3 +
> sysdeps/generic/malloc-ifuncs.c | 94 +++++++++++++++++++++++
> sysdeps/generic/malloc-ifuncs.h | 61 +++++++++++++++
> 8 files changed, 374 insertions(+), 48 deletions(-)
> create mode 100644 malloc/tst-check-internal-calls.sh
> create mode 100644 sysdeps/aarch64/malloc-ifuncs.c
> create mode 100644 sysdeps/generic/malloc-ifuncs.c
> create mode 100644 sysdeps/generic/malloc-ifuncs.h
>
> diff --git a/malloc/Makefile b/malloc/Makefile
> index fef5021298..d8b2018a76 100644
> --- a/malloc/Makefile
> +++ b/malloc/Makefile
> @@ -411,6 +411,12 @@ endif
> endif
> endif
>
> +tests-special += \
> + $(objpfx)tst-check-internal-calls.out \
> + # tests-special
> +$(objpfx)tst-check-internal-calls.out: tst-check-internal-calls.sh malloc.c
> + $(SHELL) $^ > $@; $(evaluate-test)
> +
> include ../Rules
>
> CFLAGS-mcheck-init.c += $(PIC-ccflag)
> diff --git a/malloc/malloc.c b/malloc/malloc.c
> index 57b58382b1..7da040b021 100644
> --- a/malloc/malloc.c
> +++ b/malloc/malloc.c
> @@ -566,8 +566,8 @@ tag_at (void *ptr)
> differs across systems, but is in all cases less than the maximum
> representable value of a size_t.
> */
> -void *__libc_malloc (size_t);
> -libc_hidden_proto (__libc_malloc)
> +void *__libc_malloc_core (size_t);
> +libc_hidden_proto (__libc_malloc_core)
>
> static void *__libc_calloc2 (size_t);
> static void *__libc_malloc2 (size_t);
> @@ -583,15 +583,16 @@ static void *__libc_malloc2 (size_t);
> when possible, automatically trigger operations that give
> back unused memory to the system, thus reducing program footprint.
> */
> -void __libc_free(void*);
> -libc_hidden_proto (__libc_free)
> +void __libc_free_core (void *);
> +libc_hidden_proto (__libc_free_core)
>
> /*
> calloc(size_t n_elements, size_t element_size);
> Returns a pointer to n_elements * element_size bytes, with all locations
> set to zero.
> */
> -void* __libc_calloc(size_t, size_t);
> +void * __libc_calloc_core (size_t, size_t);
> +libc_hidden_proto (__libc_calloc_core)
>
> /*
> realloc(void* p, size_t n)
> @@ -620,8 +621,8 @@ void* __libc_calloc(size_t, size_t);
> The old unix realloc convention of allowing the last-free'd chunk
> to be used as an argument to realloc is not supported.
> */
> -void* __libc_realloc(void*, size_t);
> -libc_hidden_proto (__libc_realloc)
> +void *__libc_realloc_core (void *, size_t);
> +libc_hidden_proto (__libc_realloc_core)
>
> /*
> memalign(size_t alignment, size_t n);
> @@ -635,16 +636,16 @@ libc_hidden_proto (__libc_realloc)
>
> Overreliance on memalign is a sure way to fragment space.
> */
> -void* __libc_memalign(size_t, size_t);
> -libc_hidden_proto (__libc_memalign)
> +void *__libc_memalign_core (size_t, size_t);
> +libc_hidden_proto (__libc_memalign_core)
>
> /*
> valloc(size_t n);
> Equivalent to memalign(pagesize, n), where pagesize is the page
> size of the system. If the pagesize is unknown, 4096 is used.
> */
> -void* __libc_valloc(size_t);
> -
> +void *__libc_valloc_core (size_t);
> +libc_hidden_proto (__libc_valloc_core)
>
>
> /*
> @@ -677,7 +678,8 @@ struct mallinfo __libc_mallinfo(void);
> Equivalent to valloc(minimum-page-that-holds(n)), that is,
> round up n to nearest pagesize.
> */
> -void* __libc_pvalloc(size_t);
> +void *__libc_pvalloc_core (size_t);
> +libc_hidden_proto (__libc_pvalloc_core)
>
> /*
> malloc_trim(size_t pad);
> @@ -720,7 +722,8 @@ int __malloc_trim(size_t);
> assert(malloc_usable_size(p) >= 256);
>
> */
> -size_t __malloc_usable_size(void*);
> +size_t __malloc_usable_size_core (void *);
> +libc_hidden_proto (__malloc_usable_size_core)
>
> /*
> malloc_stats();
> @@ -2951,7 +2954,7 @@ tcache_key_initialize (void)
>
> /* We need tcache_key to be non-zero (otherwise tcache_double_free_verify's
> clearing of e->key would go unnoticed and it would loop getting called
> - through __libc_free), and we want tcache_key not to be a
> + through __libc_free_core), and we want tcache_key not to be a
> commonly-occurring value in memory, so ensure a minimum amount of one and
> zero bits. */
> int minimum_bits = __WORDSIZE / 4;
> @@ -2983,7 +2986,7 @@ tcache_put_n (mchunkptr chunk, size_t tc_idx, tcache_entry **ep, bool mangled)
> {
> tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
>
> - /* Mark this chunk as "in the tcache" so the test in __libc_free will
> + /* Mark this chunk as "in the tcache" so the test in __libc_free_core will
> detect a double free. */
> e->key = tcache_key;
>
> @@ -3142,7 +3145,7 @@ tcache_double_free_verify (tcache_entry *e)
> or user data that happens to match the key. Since we are not sure,
> clear the key and retry freeing it. */
> e->key = 0;
> - __libc_free (e);
> + __libc_free_core (e);
> }
>
> static void
> @@ -3258,7 +3261,7 @@ __libc_malloc2 (size_t bytes)
> }
>
> void *
> -__libc_malloc (size_t bytes)
> +__libc_malloc_core (size_t bytes)
> {
> #if USE_TCACHE
> size_t nb = checked_request2size (bytes);
> @@ -3284,17 +3287,17 @@ __libc_malloc (size_t bytes)
>
> return __libc_malloc2 (bytes);
> }
> -libc_hidden_def (__libc_malloc)
> +libc_hidden_def (__libc_malloc_core)
>
> static void __attribute_noinline__
> tcache_free_init (void *mem)
> {
> tcache_init (NULL);
> - __libc_free (mem);
> + __libc_free_core (mem);
> }
>
> void
> -__libc_free (void *mem)
> +__libc_free_core (void *mem)
> {
> mchunkptr p; /* chunk corresponding to mem */
>
> @@ -3352,10 +3355,10 @@ __libc_free (void *mem)
>
> _int_free_chunk (arena_for_chunk (p), p, size, 0);
> }
> -libc_hidden_def (__libc_free)
> +libc_hidden_def (__libc_free_core)
>
> void *
> -__libc_realloc (void *oldmem, size_t bytes)
> +__libc_realloc_core (void *oldmem, size_t bytes)
> {
> mstate ar_ptr;
> INTERNAL_SIZE_T nb; /* padded request size */
> @@ -3364,12 +3367,12 @@ __libc_realloc (void *oldmem, size_t bytes)
>
> /* realloc of null is supposed to be same as malloc */
> if (oldmem == NULL)
> - return __libc_malloc (bytes);
> + return __libc_malloc_core (bytes);
>
> #if REALLOC_ZERO_BYTES_FREES
> if (bytes == 0)
> {
> - __libc_free (oldmem); return NULL;
> + __libc_free_core (oldmem); return NULL;
> }
> #endif
>
> @@ -3434,7 +3437,7 @@ __libc_realloc (void *oldmem, size_t bytes)
> return oldmem;
>
> /* Must alloc, copy, free. */
> - newmem = __libc_malloc (bytes);
> + newmem = __libc_malloc_core (bytes);
> if (newmem == NULL)
> return NULL; /* propagate failure */
>
> @@ -3466,7 +3469,7 @@ __libc_realloc (void *oldmem, size_t bytes)
> {
> /* Try harder to allocate memory in other arenas. */
> LIBC_PROBE (memory_realloc_retry, 2, bytes, oldmem);
> - newp = __libc_malloc (bytes);
> + newp = __libc_malloc_core (bytes);
> if (newp != NULL)
> {
> size_t sz = memsize (oldp);
> @@ -3478,10 +3481,10 @@ __libc_realloc (void *oldmem, size_t bytes)
>
> return newp;
> }
> -libc_hidden_def (__libc_realloc)
> +libc_hidden_def (__libc_realloc_core)
>
> void *
> -__libc_memalign (size_t alignment, size_t bytes)
> +__libc_memalign_core (size_t alignment, size_t bytes)
> {
> /* Round the alignment up to a power of 2. Reject alignments that overflow
> when rounded up. Zero alignment is handled by _mid_memalign. */
> @@ -3498,7 +3501,7 @@ __libc_memalign (size_t alignment, size_t bytes)
>
> return _mid_memalign (alignment, bytes);
> }
> -libc_hidden_def (__libc_memalign)
> +libc_hidden_def (__libc_memalign_core)
>
> /* For ISO C17. */
> void *
> @@ -3551,7 +3554,7 @@ _mid_memalign (size_t alignment, size_t bytes)
>
> /* If we need less alignment than we give anyway, just relay to malloc. */
> if (alignment <= MALLOC_ALIGNMENT)
> - return __libc_malloc (bytes);
> + return __libc_malloc_core (bytes);
>
> #if USE_TCACHE
> void *victim = tcache_get_align (checked_request2size (bytes), alignment);
> @@ -3586,13 +3589,14 @@ _mid_memalign (size_t alignment, size_t bytes)
> }
>
> void *
> -__libc_valloc (size_t bytes)
> +__libc_valloc_core (size_t bytes)
> {
> return _mid_memalign (GLRO (dl_pagesize), bytes);
> }
> +libc_hidden_def (__libc_valloc_core)
>
> void *
> -__libc_pvalloc (size_t bytes)
> +__libc_pvalloc_core (size_t bytes)
> {
> size_t pagesize = GLRO (dl_pagesize);
> size_t rounded_bytes;
> @@ -3607,6 +3611,7 @@ __libc_pvalloc (size_t bytes)
>
> return _mid_memalign (pagesize, rounded_bytes & -pagesize);
> }
> +libc_hidden_def (__libc_pvalloc_core)
>
> static void * __attribute_noinline__
> __libc_calloc2 (size_t sz)
> @@ -3703,7 +3708,7 @@ __libc_calloc2 (size_t sz)
> }
>
> void *
> -__libc_calloc (size_t n, size_t elem_size)
> +__libc_calloc_core (size_t n, size_t elem_size)
> {
> size_t bytes;
>
> @@ -3747,6 +3752,7 @@ __libc_calloc (size_t n, size_t elem_size)
> #endif
> return __libc_calloc2 (bytes);
> }
> +libc_hidden_def (__libc_calloc_core)
> #endif /* IS_IN (libc) */
>
> /*
> @@ -4754,12 +4760,13 @@ musable (void *mem)
>
> #if IS_IN (libc)
> size_t
> -__malloc_usable_size (void *m)
> +__malloc_usable_size_core (void *m)
> {
> if (m == NULL)
> return 0;
> return musable (m);
> }
> +libc_hidden_def (__malloc_usable_size_core)
> #endif
>
> /*
> @@ -5458,26 +5465,40 @@ __malloc_info (int options, FILE *fp)
> }
> #if IS_IN (libc)
> weak_alias (__malloc_info, malloc_info)
> -
> -weak_alias (__libc_calloc, calloc)
> -strong_alias (__libc_free, free)
> -strong_alias (__libc_malloc, malloc)
> -weak_alias (__libc_memalign, memalign)
> -strong_alias (__libc_realloc, realloc)
> -weak_alias (__libc_valloc, valloc)
> -weak_alias (__libc_pvalloc, pvalloc)
> weak_alias (__libc_mallinfo, mallinfo)
> weak_alias (__libc_mallinfo2, mallinfo2)
> weak_alias (__libc_mallopt, mallopt)
> -
> weak_alias (__malloc_stats, malloc_stats)
> -weak_alias (__malloc_usable_size, malloc_usable_size)
> weak_alias (__malloc_trim, malloc_trim)
> -#endif
>
> -#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)
> -compat_symbol (libc, __libc_free, cfree, GLIBC_2_0);
> -#endif
> +/* On targets that do not support ifuncs we alias externally visible
> + functions as well as the __libc_* functions to their respective
> + *_core symbols.
> +
> + For ifunc prototypes and resolvers see sysdeps/generic/malloc-ifuncs.h.
> + */
> +# if !HAVE_IFUNC
> +strong_alias (__libc_malloc_core, malloc)
> +strong_alias (__libc_malloc_core, __libc_malloc)
> +weak_alias (__libc_calloc_core, calloc)
> +strong_alias (__libc_calloc_core, __libc_calloc)
> +weak_alias (__libc_memalign_core, memalign)
> +strong_alias (__libc_memalign_core, __libc_memalign)
> +weak_alias (__libc_valloc_core, valloc)
> +strong_alias (__libc_valloc_core, __libc_valloc)
> +weak_alias (__libc_pvalloc_core, pvalloc)
> +strong_alias (__libc_pvalloc_core, __libc_pvalloc)
> +strong_alias (__libc_realloc_core, realloc)
> +strong_alias (__libc_realloc_core, __libc_realloc)
> +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)
> +compat_symbol (libc, __libc_free_core, cfree, GLIBC_2_0);
> +# endif
> +strong_alias (__libc_free_core, free)
> +strong_alias (__libc_free_core, __libc_free)
> +weak_alias (__malloc_usable_size_core, malloc_usable_size)
> +# endif /* !HAVE_IFUNC */
> +
> +#endif /* IS_IN (libc) */
>
> /* ------------------------------------------------------------
> History:
> diff --git a/malloc/tst-check-internal-calls.sh b/malloc/tst-check-internal-calls.sh
> new file mode 100644
> index 0000000000..3558c15491
> --- /dev/null
> +++ b/malloc/tst-check-internal-calls.sh
> @@ -0,0 +1,53 @@
> +#!/bin/sh
> +# This script checks that malloc.c does not use any external API
> +# functions.
> +# It is important that whenever an internal function needs to used e.g.
> +# __libc_foo() it actually uses __libc_foo_core() symbol. If it uses a
> +# non-_core symbol, the returned result may not be suitable for the
> +# subsequent use of it internally.
> +# The non-_core malloc functions return and accept user-pointers which
> +# are different from the internal-pointers that are used by the _core
> +# functions.
> +
> +# Copyright (C) 2026 Free Software Foundation, Inc.
> +# This file is part of the GNU C Library.
> +
> +# The GNU C Library is free software; you can redistribute it and/or
> +# modify it under the terms of the GNU Lesser General Public
> +# License as published by the Free Software Foundation; either
> +# version 2.1 of the License, or (at your option) any later version.
> +
> +# The GNU C Library is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> +# Lesser General Public License for more details.
> +
> +# You should have received a copy of the GNU Lesser General Public
> +# License along with the GNU C Library; if not, see
> +# <https://www.gnu.org/licenses/>.
> +
> +# This script accepts list of source files that need to be grep-ed for
> +# certain symbols and the result should be no matches except for the
> +# alias declarations
> +
> +status=0
> +
> +for src; do
> + echo "checking $src..."
> + for fun in \
> + __libc_malloc \
> + __libc_calloc \
> + __libc_memalign \
> + __libc_valloc \
> + __libc_pvalloc \
> + __libc_realloc \
> + __libc_free; do
> + grep -nw "$fun" $src | egrep -vw "^[0-9]+:(strong_alias|weak_alias)" && {
> + good=$fun"_core"
> + echo "error: code in $src should not use '$fun' (use '$good' instead)"
> + status=1
> + }
> + done
> +done
> +
> +exit $status
Should we extend the tests for ree_sized/free_aligned_sized? They currently calls
free, but a MTE allocator might use a different strategy.
> diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
> index 1f7b01447b..517f44373a 100644
> --- a/sysdeps/aarch64/Makefile
> +++ b/sysdeps/aarch64/Makefile
> @@ -102,6 +102,9 @@ $(objpfx)tst-sme-clone3: $(objpfx)clone3.o $(objpfx)__arm_za_disable.o
> endif
>
> ifeq ($(subdir),malloc)
> +sysdep_routines += \
> + malloc-ifuncs \
> + # sysdep_routines
> sysdep_malloc_debug_routines = \
> __mtag_tag_region \
> __mtag_tag_zero_region \
> diff --git a/sysdeps/aarch64/malloc-ifuncs.c b/sysdeps/aarch64/malloc-ifuncs.c
> new file mode 100644
> index 0000000000..9986bab851
> --- /dev/null
> +++ b/sysdeps/aarch64/malloc-ifuncs.c
> @@ -0,0 +1,85 @@
> +/* Code for ifunc resolvers for malloc: aarch64 version.
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#if IS_IN (libc)
> +
> +#include <malloc/malloc-internal.h>
> +#include <malloc-ifuncs.h>
> +
> +/* AArch64-specific resolvers for malloc ifuncs. */
> +
> +IFUNC_PROTO (__libc_malloc);
> +IFUNC_RESOLVER (__libc_malloc, uint64_t arg0, uint64_t arg1[])
Should the last argument be 'const'?
> +{
> + return __libc_malloc_core;
> +}
> +strong_alias (__libc_malloc, malloc)
> +
> +IFUNC_PROTO (__libc_calloc);
> +IFUNC_RESOLVER (__libc_calloc, uint64_t arg0, uint64_t arg1[])
> +{
> + return __libc_calloc_core;
> +}
> +weak_alias (__libc_calloc, calloc)
> +
> +IFUNC_PROTO (__libc_memalign);
> +IFUNC_RESOLVER (__libc_memalign, uint64_t arg0, uint64_t arg1[])
> +{
> + return __libc_memalign_core;
> +}
> +weak_alias (__libc_memalign, memalign)
> +
> +IFUNC_PROTO (__libc_valloc);
> +IFUNC_RESOLVER (__libc_valloc, uint64_t arg0, uint64_t arg1[])
> +{
> + return __libc_valloc_core;
> +}
> +weak_alias (__libc_valloc, valloc)
> +
> +IFUNC_PROTO (__libc_pvalloc);
> +IFUNC_RESOLVER (__libc_pvalloc, uint64_t arg0, uint64_t arg1[])
> +{
> + return __libc_pvalloc_core;
> +}
> +weak_alias (__libc_pvalloc, pvalloc)
> +
> +IFUNC_PROTO (__libc_realloc);
> +IFUNC_RESOLVER (__libc_realloc, uint64_t arg0, uint64_t arg1[])
> +{
> + return __libc_realloc_core;
> +}
> +strong_alias (__libc_realloc, realloc)
> +
> +IFUNC_PROTO (__libc_free);
> +IFUNC_RESOLVER (__libc_free, uint64_t arg0, uint64_t arg1[])
> +{
> + return __libc_free_core;
> +}
> +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)
> +compat_symbol (libc, __libc_free, cfree, GLIBC_2_0);
> +# endif
> +strong_alias (__libc_free, free)
> +
> +IFUNC_PROTO (__malloc_usable_size);
> +IFUNC_RESOLVER (__malloc_usable_size, uint64_t arg0, uint64_t arg1[])
> +{
> + return __malloc_usable_size_core;
> +}
> +weak_alias (__malloc_usable_size, malloc_usable_size)
> +
> +#endif /* IS_IN (libc) */
> diff --git a/sysdeps/generic/Makefile b/sysdeps/generic/Makefile
> index 0b586efe63..6816cf907d 100644
> --- a/sysdeps/generic/Makefile
> +++ b/sysdeps/generic/Makefile
> @@ -43,6 +43,9 @@ endif
> endif
>
> ifeq ($(subdir),malloc)
> +sysdep_routines += \
> + malloc-ifuncs \
> + # sysdep_routines
> sysdep_malloc_debug_routines += \
> hugepages \
> # sysdep_malloc_debug_routines
> diff --git a/sysdeps/generic/malloc-ifuncs.c b/sysdeps/generic/malloc-ifuncs.c
> new file mode 100644
> index 0000000000..0c03c1466e
> --- /dev/null
> +++ b/sysdeps/generic/malloc-ifuncs.c
> @@ -0,0 +1,94 @@
> +/* Code for ifunc resolvers for malloc: generic version.
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#if IS_IN (libc)
> +
> +#include <malloc/malloc-internal.h>
> +#include <malloc-ifuncs.h>
> +
> +# if HAVE_IFUNC
> +
> +/* These resolvers are used by default unless overridden by a target.
> + The target-specific resolvers must respect this logic of the default
> + resolvers, replicating this logic where appropriate.
> +
> + Any aliases for malloc API functions must be defined here as well
> + and re-defined along with the target-specific resolvers. */
> +
> +IFUNC_PROTO (__libc_malloc);
> +IFUNC_RESOLVER (__libc_malloc, void)
> +{
> + return __libc_malloc_core;
> +}
> +strong_alias (__libc_malloc, malloc)
> +
> +IFUNC_PROTO (__libc_calloc);
> +IFUNC_RESOLVER (__libc_calloc, void)
> +{
> + return __libc_calloc_core;
> +}
> +weak_alias (__libc_calloc, calloc)
> +
> +IFUNC_PROTO (__libc_memalign);
> +IFUNC_RESOLVER (__libc_memalign, void)
> +{
> + return __libc_memalign_core;
> +}
> +weak_alias (__libc_memalign, memalign)
> +
> +IFUNC_PROTO (__libc_valloc);
> +IFUNC_RESOLVER (__libc_valloc, void)
> +{
> + return __libc_valloc_core;
> +}
> +weak_alias (__libc_valloc, valloc)
> +
> +IFUNC_PROTO (__libc_pvalloc);
> +IFUNC_RESOLVER (__libc_pvalloc, void)
> +{
> + return __libc_pvalloc_core;
> +}
> +weak_alias (__libc_pvalloc, pvalloc)
> +
> +IFUNC_PROTO (__libc_realloc);
> +IFUNC_RESOLVER (__libc_realloc, void)
> +{
> + return __libc_realloc_core;
> +}
> +strong_alias (__libc_realloc, realloc)
> +
> +IFUNC_PROTO (__libc_free);
> +IFUNC_RESOLVER (__libc_free, void)
> +{
> + return __libc_free_core;
> +}
> +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)
> +compat_symbol (libc, __libc_free, cfree, GLIBC_2_0);
> +# endif
> +strong_alias (__libc_free, free)
> +
> +IFUNC_PROTO (__malloc_usable_size);
> +IFUNC_RESOLVER (__malloc_usable_size, void)
> +{
> + return __malloc_usable_size_core;
> +}
> +weak_alias (__malloc_usable_size, malloc_usable_size)
> +
> +# endif /* HAVE_IFUNC */
> +
> +#endif /* IS_IN (libc) */
> diff --git a/sysdeps/generic/malloc-ifuncs.h b/sysdeps/generic/malloc-ifuncs.h
> new file mode 100644
> index 0000000000..1d3ddb0202
> --- /dev/null
> +++ b/sysdeps/generic/malloc-ifuncs.h
> @@ -0,0 +1,61 @@
> +/* Definitions for ifunc resolvers for malloc: generic version.
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#ifndef _GENERIC_MALLOC_IFUNCS_H
> +#define _GENERIC_MALLOC_IFUNCS_H
> +
> +#if HAVE_IFUNC
> +
> +#include <stddef.h>
> +#include <sys/cdefs.h>
> +#include <shlib-compat.h>
> +
> +/* Core implementations of malloc functions. An ifunc resolver must
> + use this implementations as a fallback option. Other implementations
> + may internally call these core function. */
> +void *__libc_malloc_core (size_t);
> +libc_hidden_proto (__libc_malloc_core)
> +void *__libc_calloc_core (size_t, size_t);
> +libc_hidden_proto (__libc_calloc_core)
> +void *__libc_memalign_core (size_t, size_t);
> +libc_hidden_proto (__libc_memalign_core)
> +void *__libc_valloc_core (size_t);
> +libc_hidden_proto (__libc_valloc_core)
> +void *__libc_pvalloc_core (size_t);
> +libc_hidden_proto (__libc_pvalloc_core)
> +void *__libc_realloc_core (void *, size_t);
> +libc_hidden_proto (__libc_realloc_core)
> +void __libc_free_core (void *);
> +libc_hidden_proto (__libc_free_core)
> +size_t __malloc_usable_size_core (void *);
> +libc_hidden_proto (__malloc_usable_size_core)
> +
> +/* Macros for defining ifunc resolvers for malloc functions. */
> +#define IFUNC_RESOLVER_NAME(fn) fn ## _resolver
> +#define STR(x) #x
> +#define XSTR(x) STR(x)
> +#define IFUNC_PROTO(fn) \
> + __typeof (fn ## _core) fn \
> + __attribute__ ((ifunc (XSTR(IFUNC_RESOLVER_NAME(fn)))))
> +#define IFUNC_RESOLVER(fn, ...) \
> + static __attribute_used__ \
> + __typeof (fn ## _core) *IFUNC_RESOLVER_NAME(fn) (__VA_ARGS__)
This duplicatd the already provided ifunc support from libc-symbols.h (libc_ifunc,
__ifunc_args, __ifunc_hidden), which handles inhibit_stack_protector (which is
missing here) and the fallback for !HAVE_GCC_IFUNC. I think we should coalesce
the ifunc macros on only one place to avoid definitions drifiting and missing
handling (like the stack protector here).
> +
> +#endif /* HAVE_IFUNC */
> +
> +#endif /* _GENERIC_MALLOC_IFUNCS_H */
More information about the Libc-alpha
mailing list