[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