{"id":2225178,"url":"http://patchwork.ozlabs.org/api/1.1/patches/2225178/?format=json","web_url":"http://patchwork.ozlabs.org/project/glibc/patch/20260420141459.531612-3-yury.khrustalev@arm.com/","project":{"id":41,"url":"http://patchwork.ozlabs.org/api/1.1/projects/41/?format=json","name":"GNU C Library","link_name":"glibc","list_id":"libc-alpha.sourceware.org","list_email":"libc-alpha@sourceware.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20260420141459.531612-3-yury.khrustalev@arm.com>","date":"2026-04-20T14:14:59","name":"[v2,2/2] malloc: introduce ifuncs for malloc functions","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"544a68c4f8b7a5914e41ceb0ee71ba156089d165","submitter":{"id":88214,"url":"http://patchwork.ozlabs.org/api/1.1/people/88214/?format=json","name":"Yury Khrustalev","email":"yury.khrustalev@arm.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/glibc/patch/20260420141459.531612-3-yury.khrustalev@arm.com/mbox/","series":[{"id":500613,"url":"http://patchwork.ozlabs.org/api/1.1/series/500613/?format=json","web_url":"http://patchwork.ozlabs.org/project/glibc/list/?series=500613","date":"2026-04-20T14:14:57","name":"malloc: introduce ifuncs for malloc functions","version":2,"mbox":"http://patchwork.ozlabs.org/series/500613/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2225178/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2225178/checks/","tags":{},"headers":{"Return-Path":"<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>","X-Original-To":["incoming@patchwork.ozlabs.org","libc-alpha@sourceware.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","libc-alpha@sourceware.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=arm.com header.i=@arm.com header.a=rsa-sha256\n header.s=foss header.b=SInHGL2V;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)","sourceware.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key,\n unprotected) header.d=arm.com header.i=@arm.com header.a=rsa-sha256\n header.s=foss header.b=SInHGL2V","sourceware.org;\n dmarc=pass (p=none dis=none) header.from=arm.com","sourceware.org; spf=pass smtp.mailfrom=arm.com","server2.sourceware.org;\n arc=none smtp.remote-ip=217.140.110.172"],"Received":["from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fznZk1pvdz1yCv\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 21 Apr 2026 00:15:42 +1000 (AEST)","from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 6B15F4D108FF\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 20 Apr 2026 14:15:40 +0000 (GMT)","from foss.arm.com (foss.arm.com [217.140.110.172])\n by sourceware.org (Postfix) with ESMTP id 152D84A98F01\n for <libc-alpha@sourceware.org>; Mon, 20 Apr 2026 14:15:12 +0000 (GMT)","from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14])\n by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 06A6C1758;\n Mon, 20 Apr 2026 07:15:06 -0700 (PDT)","from fdebian.localdomain (G7GWP2TF97.cambridge.arm.com [10.1.33.20])\n by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id\n 77E183F7B4; Mon, 20 Apr 2026 07:15:10 -0700 (PDT)"],"DKIM-Filter":["OpenDKIM Filter v2.11.0 sourceware.org 6B15F4D108FF","OpenDKIM Filter v2.11.0 sourceware.org 152D84A98F01"],"DMARC-Filter":"OpenDMARC Filter v1.4.2 sourceware.org 152D84A98F01","ARC-Filter":"OpenARC Filter v1.0.0 sourceware.org 152D84A98F01","ARC-Seal":"i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776694512; cv=none;\n b=nuD1gg0D/GwkAIO0qFP33eVwSaVoYkKNM2irIZopq7PAhtaYfsefz1eZTOG0KRvUHR+d6pcCndrZqKzyiXBb/GV30y/BFBW6zF2AqyjZ9Q4c1+ZIUrcmnfhFWKrG0ey8gR3lTVhaUdAqWUTsNTdCBBiMQpKVsRHsYci5sI016tc=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776694512; c=relaxed/simple;\n bh=RzfaUc05Yh5z9dXTlM5Ie8NCfOQY55PSGyYrT7M3VbM=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=WmHQUngWYCjBkadr1vtIMHhVgMj4cpx/5c7prIHbfQ3bwjpSjjPWLC2gizughYEAmx7fK1nU0IIBCA1NHD0d559uS/Qu6sqn3eEdnNgUjrSGQJhfGDuzzOZIgTHqqTSdg8uxiMwsvYwMym51XCgodUlHswHn7zwGUcLE6mhwSwI=","ARC-Authentication-Results":"i=1; server2.sourceware.org","DKIM-Signature":"v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss;\n t=1776694511; bh=RzfaUc05Yh5z9dXTlM5Ie8NCfOQY55PSGyYrT7M3VbM=;\n h=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n b=SInHGL2VH9WhWxDc82gAQm2o+xZuW2FLv/cvcxWaII0udrEA8EekejmxJSFCuDl22\n 41xlLEtdokz/eLJEyFzXBUdQLkgTgFdGzz2mwejnl3MVn+rCPYE4Fp1Zy5pzilm2TG\n tG8VNNy9SnwbmP93O9G/xIOPDg6DYtL1/JduNzww=","From":"Yury Khrustalev <yury.khrustalev@arm.com>","To":"libc-alpha@sourceware.org","Cc":"DJ Delorie <dj@redhat.com>, Florian Weimer <fweimer@redhat.com>,\n \"H . J . Lu\" <hjl.tools@gmail.com>, Carlos O'Donell <carlos@redhat.com>,\n Wilco Dijkstra <wilco.dijkstra@arm.com>","Subject":"[PATCH v2 2/2] malloc: introduce ifuncs for malloc functions","Date":"Mon, 20 Apr 2026 15:14:59 +0100","Message-ID":"<20260420141459.531612-3-yury.khrustalev@arm.com>","X-Mailer":"git-send-email 2.47.3","In-Reply-To":"<20260420141459.531612-1-yury.khrustalev@arm.com>","References":"<20260420141459.531612-1-yury.khrustalev@arm.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","X-BeenThere":"libc-alpha@sourceware.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"Libc-alpha mailing list <libc-alpha.sourceware.org>","List-Unsubscribe":"<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>","List-Archive":"<https://sourceware.org/pipermail/libc-alpha/>","List-Post":"<mailto:libc-alpha@sourceware.org>","List-Help":"<mailto:libc-alpha-request@sourceware.org?subject=help>","List-Subscribe":"<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>","Errors-To":"libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org"},"content":"Introduce ifuncs and resolvers for functions pertinent to the\nmalloc interface.\n\nIn order to do this, we first rename all core implementations by\nadding the '_core' suffix. These functions are supposed to be strictly\ninternal and must not be used beyond the malloc code. For example, the\n__libc_malloc() function becomes __libc_malloc_core() and instead\n__libc_malloc() becomes an ifunc with a corresponding resolver that\nmay return '__libc_malloc_core' as the implementation.\n\nEach ifunc comes with a generic resolver that will be used unless a\ntarget overrides the resolvers in its sysdeps directory. All resolvers\nare supposed to be overridden together, and the logic of the generic\nresolvers should be replicated by the target-specific ones.\n\nThis patch contains, as an example, aarch64-specific resolvers. At this\nmoment they are identical to the generic ones but in the future they can\nbe changed to support for features, e.g. to handle memory tagging.\n\nCorresponding aliases are moved next to the ifuncs as well.\n\nOn targets that do not support ifuncs we alias externally visible\nfunctions as well as the __libc_* functions to their respective\n*_core symbols.\n---\n malloc/Makefile                    |   6 ++\n malloc/malloc.c                    | 117 +++++++++++++++++------------\n malloc/tst-check-internal-calls.sh |  53 +++++++++++++\n sysdeps/aarch64/Makefile           |   3 +\n sysdeps/aarch64/malloc-ifuncs.c    |  85 +++++++++++++++++++++\n sysdeps/generic/Makefile           |   3 +\n sysdeps/generic/malloc-ifuncs.c    |  94 +++++++++++++++++++++++\n sysdeps/generic/malloc-ifuncs.h    |  61 +++++++++++++++\n 8 files changed, 374 insertions(+), 48 deletions(-)\n create mode 100644 malloc/tst-check-internal-calls.sh\n create mode 100644 sysdeps/aarch64/malloc-ifuncs.c\n create mode 100644 sysdeps/generic/malloc-ifuncs.c\n create mode 100644 sysdeps/generic/malloc-ifuncs.h","diff":"diff --git a/malloc/Makefile b/malloc/Makefile\nindex fef5021298..d8b2018a76 100644\n--- a/malloc/Makefile\n+++ b/malloc/Makefile\n@@ -411,6 +411,12 @@ endif\n endif\n endif\n \n+tests-special += \\\n+  $(objpfx)tst-check-internal-calls.out \\\n+  # tests-special\n+$(objpfx)tst-check-internal-calls.out: tst-check-internal-calls.sh malloc.c\n+\t$(SHELL) $^ > $@; $(evaluate-test)\n+\n include ../Rules\n \n CFLAGS-mcheck-init.c += $(PIC-ccflag)\ndiff --git a/malloc/malloc.c b/malloc/malloc.c\nindex 57b58382b1..7da040b021 100644\n--- a/malloc/malloc.c\n+++ b/malloc/malloc.c\n@@ -566,8 +566,8 @@ tag_at (void *ptr)\n   differs across systems, but is in all cases less than the maximum\n   representable value of a size_t.\n */\n-void *__libc_malloc (size_t);\n-libc_hidden_proto (__libc_malloc)\n+void *__libc_malloc_core (size_t);\n+libc_hidden_proto (__libc_malloc_core)\n \n static void *__libc_calloc2 (size_t);\n static void *__libc_malloc2 (size_t);\n@@ -583,15 +583,16 @@ static void *__libc_malloc2 (size_t);\n   when possible, automatically trigger operations that give\n   back unused memory to the system, thus reducing program footprint.\n */\n-void     __libc_free(void*);\n-libc_hidden_proto (__libc_free)\n+void __libc_free_core (void *);\n+libc_hidden_proto (__libc_free_core)\n \n /*\n   calloc(size_t n_elements, size_t element_size);\n   Returns a pointer to n_elements * element_size bytes, with all locations\n   set to zero.\n */\n-void*  __libc_calloc(size_t, size_t);\n+void * __libc_calloc_core (size_t, size_t);\n+libc_hidden_proto (__libc_calloc_core)\n \n /*\n   realloc(void* p, size_t n)\n@@ -620,8 +621,8 @@ void*  __libc_calloc(size_t, size_t);\n   The old unix realloc convention of allowing the last-free'd chunk\n   to be used as an argument to realloc is not supported.\n */\n-void*  __libc_realloc(void*, size_t);\n-libc_hidden_proto (__libc_realloc)\n+void *__libc_realloc_core (void *, size_t);\n+libc_hidden_proto (__libc_realloc_core)\n \n /*\n   memalign(size_t alignment, size_t n);\n@@ -635,16 +636,16 @@ libc_hidden_proto (__libc_realloc)\n \n   Overreliance on memalign is a sure way to fragment space.\n */\n-void*  __libc_memalign(size_t, size_t);\n-libc_hidden_proto (__libc_memalign)\n+void *__libc_memalign_core (size_t, size_t);\n+libc_hidden_proto (__libc_memalign_core)\n \n /*\n   valloc(size_t n);\n   Equivalent to memalign(pagesize, n), where pagesize is the page\n   size of the system. If the pagesize is unknown, 4096 is used.\n */\n-void*  __libc_valloc(size_t);\n-\n+void *__libc_valloc_core (size_t);\n+libc_hidden_proto (__libc_valloc_core)\n \n \n /*\n@@ -677,7 +678,8 @@ struct mallinfo __libc_mallinfo(void);\n   Equivalent to valloc(minimum-page-that-holds(n)), that is,\n   round up n to nearest pagesize.\n  */\n-void*  __libc_pvalloc(size_t);\n+void *__libc_pvalloc_core (size_t);\n+libc_hidden_proto (__libc_pvalloc_core)\n \n /*\n   malloc_trim(size_t pad);\n@@ -720,7 +722,8 @@ int      __malloc_trim(size_t);\n   assert(malloc_usable_size(p) >= 256);\n \n */\n-size_t   __malloc_usable_size(void*);\n+size_t __malloc_usable_size_core (void *);\n+libc_hidden_proto (__malloc_usable_size_core)\n \n /*\n   malloc_stats();\n@@ -2951,7 +2954,7 @@ tcache_key_initialize (void)\n \n   /* We need tcache_key to be non-zero (otherwise tcache_double_free_verify's\n      clearing of e->key would go unnoticed and it would loop getting called\n-     through __libc_free), and we want tcache_key not to be a\n+     through __libc_free_core), and we want tcache_key not to be a\n      commonly-occurring value in memory, so ensure a minimum amount of one and\n      zero bits.  */\n   int minimum_bits = __WORDSIZE / 4;\n@@ -2983,7 +2986,7 @@ tcache_put_n (mchunkptr chunk, size_t tc_idx, tcache_entry **ep, bool mangled)\n {\n   tcache_entry *e = (tcache_entry *) chunk2mem (chunk);\n \n-  /* Mark this chunk as \"in the tcache\" so the test in __libc_free will\n+  /* Mark this chunk as \"in the tcache\" so the test in __libc_free_core will\n      detect a double free.  */\n   e->key = tcache_key;\n \n@@ -3142,7 +3145,7 @@ tcache_double_free_verify (tcache_entry *e)\n      or user data that happens to match the key.  Since we are not sure,\n      clear the key and retry freeing it.  */\n   e->key = 0;\n-  __libc_free (e);\n+  __libc_free_core (e);\n }\n \n static void\n@@ -3258,7 +3261,7 @@ __libc_malloc2 (size_t bytes)\n }\n \n void *\n-__libc_malloc (size_t bytes)\n+__libc_malloc_core (size_t bytes)\n {\n #if USE_TCACHE\n   size_t nb = checked_request2size (bytes);\n@@ -3284,17 +3287,17 @@ __libc_malloc (size_t bytes)\n \n   return __libc_malloc2 (bytes);\n }\n-libc_hidden_def (__libc_malloc)\n+libc_hidden_def (__libc_malloc_core)\n \n static void __attribute_noinline__\n tcache_free_init (void *mem)\n {\n   tcache_init (NULL);\n-  __libc_free (mem);\n+  __libc_free_core (mem);\n }\n \n void\n-__libc_free (void *mem)\n+__libc_free_core (void *mem)\n {\n   mchunkptr p;                          /* chunk corresponding to mem */\n \n@@ -3352,10 +3355,10 @@ __libc_free (void *mem)\n \n   _int_free_chunk (arena_for_chunk (p), p, size, 0);\n }\n-libc_hidden_def (__libc_free)\n+libc_hidden_def (__libc_free_core)\n \n void *\n-__libc_realloc (void *oldmem, size_t bytes)\n+__libc_realloc_core (void *oldmem, size_t bytes)\n {\n   mstate ar_ptr;\n   INTERNAL_SIZE_T nb;         /* padded request size */\n@@ -3364,12 +3367,12 @@ __libc_realloc (void *oldmem, size_t bytes)\n \n   /* realloc of null is supposed to be same as malloc */\n   if (oldmem == NULL)\n-    return __libc_malloc (bytes);\n+    return __libc_malloc_core (bytes);\n \n #if REALLOC_ZERO_BYTES_FREES\n   if (bytes == 0)\n     {\n-      __libc_free (oldmem); return NULL;\n+      __libc_free_core (oldmem); return NULL;\n     }\n #endif\n \n@@ -3434,7 +3437,7 @@ __libc_realloc (void *oldmem, size_t bytes)\n \treturn oldmem;\n \n       /* Must alloc, copy, free. */\n-      newmem = __libc_malloc (bytes);\n+      newmem = __libc_malloc_core (bytes);\n       if (newmem == NULL)\n         return NULL;              /* propagate failure */\n \n@@ -3466,7 +3469,7 @@ __libc_realloc (void *oldmem, size_t bytes)\n     {\n       /* Try harder to allocate memory in other arenas.  */\n       LIBC_PROBE (memory_realloc_retry, 2, bytes, oldmem);\n-      newp = __libc_malloc (bytes);\n+      newp = __libc_malloc_core (bytes);\n       if (newp != NULL)\n         {\n \t  size_t sz = memsize (oldp);\n@@ -3478,10 +3481,10 @@ __libc_realloc (void *oldmem, size_t bytes)\n \n   return newp;\n }\n-libc_hidden_def (__libc_realloc)\n+libc_hidden_def (__libc_realloc_core)\n \n void *\n-__libc_memalign (size_t alignment, size_t bytes)\n+__libc_memalign_core (size_t alignment, size_t bytes)\n {\n   /* Round the alignment up to a power of 2.  Reject alignments that overflow\n      when rounded up.  Zero alignment is handled by _mid_memalign.  */\n@@ -3498,7 +3501,7 @@ __libc_memalign (size_t alignment, size_t bytes)\n \n   return _mid_memalign (alignment, bytes);\n }\n-libc_hidden_def (__libc_memalign)\n+libc_hidden_def (__libc_memalign_core)\n \n /* For ISO C17.  */\n void *\n@@ -3551,7 +3554,7 @@ _mid_memalign (size_t alignment, size_t bytes)\n \n   /* If we need less alignment than we give anyway, just relay to malloc.  */\n   if (alignment <= MALLOC_ALIGNMENT)\n-    return __libc_malloc (bytes);\n+    return __libc_malloc_core (bytes);\n \n #if USE_TCACHE\n   void *victim = tcache_get_align (checked_request2size (bytes), alignment);\n@@ -3586,13 +3589,14 @@ _mid_memalign (size_t alignment, size_t bytes)\n }\n \n void *\n-__libc_valloc (size_t bytes)\n+__libc_valloc_core (size_t bytes)\n {\n   return _mid_memalign (GLRO (dl_pagesize), bytes);\n }\n+libc_hidden_def (__libc_valloc_core)\n \n void *\n-__libc_pvalloc (size_t bytes)\n+__libc_pvalloc_core (size_t bytes)\n {\n   size_t pagesize = GLRO (dl_pagesize);\n   size_t rounded_bytes;\n@@ -3607,6 +3611,7 @@ __libc_pvalloc (size_t bytes)\n \n   return _mid_memalign (pagesize, rounded_bytes & -pagesize);\n }\n+libc_hidden_def (__libc_pvalloc_core)\n \n static void * __attribute_noinline__\n __libc_calloc2 (size_t sz)\n@@ -3703,7 +3708,7 @@ __libc_calloc2 (size_t sz)\n }\n \n void *\n-__libc_calloc (size_t n, size_t elem_size)\n+__libc_calloc_core (size_t n, size_t elem_size)\n {\n   size_t bytes;\n \n@@ -3747,6 +3752,7 @@ __libc_calloc (size_t n, size_t elem_size)\n #endif\n   return __libc_calloc2 (bytes);\n }\n+libc_hidden_def (__libc_calloc_core)\n #endif /* IS_IN (libc) */\n \n /*\n@@ -4754,12 +4760,13 @@ musable (void *mem)\n \n #if IS_IN (libc)\n size_t\n-__malloc_usable_size (void *m)\n+__malloc_usable_size_core (void *m)\n {\n   if (m == NULL)\n     return 0;\n   return musable (m);\n }\n+libc_hidden_def (__malloc_usable_size_core)\n #endif\n \n /*\n@@ -5458,26 +5465,40 @@ __malloc_info (int options, FILE *fp)\n }\n #if IS_IN (libc)\n weak_alias (__malloc_info, malloc_info)\n-\n-weak_alias (__libc_calloc, calloc)\n-strong_alias (__libc_free, free)\n-strong_alias (__libc_malloc, malloc)\n-weak_alias (__libc_memalign, memalign)\n-strong_alias (__libc_realloc, realloc)\n-weak_alias (__libc_valloc, valloc)\n-weak_alias (__libc_pvalloc, pvalloc)\n weak_alias (__libc_mallinfo, mallinfo)\n weak_alias (__libc_mallinfo2, mallinfo2)\n weak_alias (__libc_mallopt, mallopt)\n-\n weak_alias (__malloc_stats, malloc_stats)\n-weak_alias (__malloc_usable_size, malloc_usable_size)\n weak_alias (__malloc_trim, malloc_trim)\n-#endif\n \n-#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)\n-compat_symbol (libc, __libc_free, cfree, GLIBC_2_0);\n-#endif\n+/* On targets that do not support ifuncs we alias externally visible\n+   functions as well as the __libc_* functions to their respective\n+   *_core symbols.\n+\n+   For ifunc prototypes and resolvers see sysdeps/generic/malloc-ifuncs.h.\n+ */\n+# if !HAVE_IFUNC\n+strong_alias (__libc_malloc_core, malloc)\n+strong_alias (__libc_malloc_core, __libc_malloc)\n+weak_alias (__libc_calloc_core, calloc)\n+strong_alias (__libc_calloc_core, __libc_calloc)\n+weak_alias (__libc_memalign_core, memalign)\n+strong_alias (__libc_memalign_core, __libc_memalign)\n+weak_alias (__libc_valloc_core, valloc)\n+strong_alias (__libc_valloc_core, __libc_valloc)\n+weak_alias (__libc_pvalloc_core, pvalloc)\n+strong_alias (__libc_pvalloc_core, __libc_pvalloc)\n+strong_alias (__libc_realloc_core, realloc)\n+strong_alias (__libc_realloc_core, __libc_realloc)\n+#  if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)\n+compat_symbol (libc, __libc_free_core, cfree, GLIBC_2_0);\n+#  endif\n+strong_alias (__libc_free_core, free)\n+strong_alias (__libc_free_core, __libc_free)\n+weak_alias (__malloc_usable_size_core, malloc_usable_size)\n+# endif /* !HAVE_IFUNC */\n+\n+#endif /* IS_IN (libc) */\n \n /* ------------------------------------------------------------\n    History:\ndiff --git a/malloc/tst-check-internal-calls.sh b/malloc/tst-check-internal-calls.sh\nnew file mode 100644\nindex 0000000000..3558c15491\n--- /dev/null\n+++ b/malloc/tst-check-internal-calls.sh\n@@ -0,0 +1,53 @@\n+#!/bin/sh\n+# This script checks that malloc.c does not use any external API\n+# functions.\n+# It is important that whenever an internal function needs to used e.g.\n+# __libc_foo() it actually uses __libc_foo_core() symbol. If it uses a\n+# non-_core symbol, the returned result may not be suitable for the\n+# subsequent use of it internally.\n+# The non-_core malloc functions return and accept user-pointers which\n+# are different from the internal-pointers that are used by the _core\n+# functions.\n+\n+# Copyright (C) 2026 Free Software Foundation, Inc.\n+# This file is part of the GNU C Library.\n+\n+# The GNU C Library is free software; you can redistribute it and/or\n+# modify it under the terms of the GNU Lesser General Public\n+# License as published by the Free Software Foundation; either\n+# version 2.1 of the License, or (at your option) any later version.\n+\n+# The GNU C Library is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+# Lesser General Public License for more details.\n+\n+# You should have received a copy of the GNU Lesser General Public\n+# License along with the GNU C Library; if not, see\n+# <https://www.gnu.org/licenses/>.\n+\n+# This script accepts list of source files that need to be grep-ed for\n+# certain symbols and the result should be no matches except for the\n+# alias declarations\n+\n+status=0\n+\n+for src; do\n+  echo \"checking $src...\"\n+  for fun in \\\n+    __libc_malloc \\\n+    __libc_calloc \\\n+    __libc_memalign \\\n+    __libc_valloc \\\n+    __libc_pvalloc \\\n+    __libc_realloc \\\n+    __libc_free; do\n+    grep -nw \"$fun\" $src | egrep -vw \"^[0-9]+:(strong_alias|weak_alias)\" && {\n+      good=$fun\"_core\"\n+      echo \"error: code in $src should not use '$fun' (use '$good' instead)\"\n+      status=1\n+    }\n+  done\n+done\n+\n+exit $status\ndiff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile\nindex 1f7b01447b..517f44373a 100644\n--- a/sysdeps/aarch64/Makefile\n+++ b/sysdeps/aarch64/Makefile\n@@ -102,6 +102,9 @@ $(objpfx)tst-sme-clone3: $(objpfx)clone3.o $(objpfx)__arm_za_disable.o\n endif\n \n ifeq ($(subdir),malloc)\n+sysdep_routines += \\\n+  malloc-ifuncs \\\n+  # sysdep_routines\n sysdep_malloc_debug_routines = \\\n   __mtag_tag_region \\\n   __mtag_tag_zero_region \\\ndiff --git a/sysdeps/aarch64/malloc-ifuncs.c b/sysdeps/aarch64/malloc-ifuncs.c\nnew file mode 100644\nindex 0000000000..9986bab851\n--- /dev/null\n+++ b/sysdeps/aarch64/malloc-ifuncs.c\n@@ -0,0 +1,85 @@\n+/* Code for ifunc resolvers for malloc: aarch64 version.\n+   Copyright (C) 2026 Free Software Foundation, Inc.\n+   This file is part of the GNU C Library.\n+\n+   The GNU C Library is free software; you can redistribute it and/or\n+   modify it under the terms of the GNU Lesser General Public\n+   License as published by the Free Software Foundation; either\n+   version 2.1 of the License, or (at your option) any later version.\n+\n+   The GNU C Library is distributed in the hope that it will be useful,\n+   but WITHOUT ANY WARRANTY; without even the implied warranty of\n+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+   Lesser General Public License for more details.\n+\n+   You should have received a copy of the GNU Lesser General Public\n+   License along with the GNU C Library; if not, see\n+   <https://www.gnu.org/licenses/>.  */\n+\n+#if IS_IN (libc)\n+\n+#include <malloc/malloc-internal.h>\n+#include <malloc-ifuncs.h>\n+\n+/* AArch64-specific resolvers for malloc ifuncs.  */\n+\n+IFUNC_PROTO (__libc_malloc);\n+IFUNC_RESOLVER (__libc_malloc, uint64_t arg0, uint64_t arg1[])\n+{\n+  return __libc_malloc_core;\n+}\n+strong_alias (__libc_malloc, malloc)\n+\n+IFUNC_PROTO (__libc_calloc);\n+IFUNC_RESOLVER (__libc_calloc, uint64_t arg0, uint64_t arg1[])\n+{\n+  return __libc_calloc_core;\n+}\n+weak_alias (__libc_calloc, calloc)\n+\n+IFUNC_PROTO (__libc_memalign);\n+IFUNC_RESOLVER (__libc_memalign, uint64_t arg0, uint64_t arg1[])\n+{\n+  return __libc_memalign_core;\n+}\n+weak_alias (__libc_memalign, memalign)\n+\n+IFUNC_PROTO (__libc_valloc);\n+IFUNC_RESOLVER (__libc_valloc, uint64_t arg0, uint64_t arg1[])\n+{\n+  return __libc_valloc_core;\n+}\n+weak_alias (__libc_valloc, valloc)\n+\n+IFUNC_PROTO (__libc_pvalloc);\n+IFUNC_RESOLVER (__libc_pvalloc, uint64_t arg0, uint64_t arg1[])\n+{\n+  return __libc_pvalloc_core;\n+}\n+weak_alias (__libc_pvalloc, pvalloc)\n+\n+IFUNC_PROTO (__libc_realloc);\n+IFUNC_RESOLVER (__libc_realloc, uint64_t arg0, uint64_t arg1[])\n+{\n+  return __libc_realloc_core;\n+}\n+strong_alias (__libc_realloc, realloc)\n+\n+IFUNC_PROTO (__libc_free);\n+IFUNC_RESOLVER (__libc_free, uint64_t arg0, uint64_t arg1[])\n+{\n+  return __libc_free_core;\n+}\n+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)\n+compat_symbol (libc, __libc_free, cfree, GLIBC_2_0);\n+# endif\n+strong_alias (__libc_free, free)\n+\n+IFUNC_PROTO (__malloc_usable_size);\n+IFUNC_RESOLVER (__malloc_usable_size, uint64_t arg0, uint64_t arg1[])\n+{\n+  return __malloc_usable_size_core;\n+}\n+weak_alias (__malloc_usable_size, malloc_usable_size)\n+\n+#endif /* IS_IN (libc) */\ndiff --git a/sysdeps/generic/Makefile b/sysdeps/generic/Makefile\nindex 0b586efe63..6816cf907d 100644\n--- a/sysdeps/generic/Makefile\n+++ b/sysdeps/generic/Makefile\n@@ -43,6 +43,9 @@ endif\n endif\n \n ifeq ($(subdir),malloc)\n+sysdep_routines += \\\n+  malloc-ifuncs \\\n+  # sysdep_routines\n sysdep_malloc_debug_routines += \\\n   hugepages \\\n   # sysdep_malloc_debug_routines\ndiff --git a/sysdeps/generic/malloc-ifuncs.c b/sysdeps/generic/malloc-ifuncs.c\nnew file mode 100644\nindex 0000000000..0c03c1466e\n--- /dev/null\n+++ b/sysdeps/generic/malloc-ifuncs.c\n@@ -0,0 +1,94 @@\n+/* Code for ifunc resolvers for malloc: generic version.\n+   Copyright (C) 2026 Free Software Foundation, Inc.\n+   This file is part of the GNU C Library.\n+\n+   The GNU C Library is free software; you can redistribute it and/or\n+   modify it under the terms of the GNU Lesser General Public\n+   License as published by the Free Software Foundation; either\n+   version 2.1 of the License, or (at your option) any later version.\n+\n+   The GNU C Library is distributed in the hope that it will be useful,\n+   but WITHOUT ANY WARRANTY; without even the implied warranty of\n+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+   Lesser General Public License for more details.\n+\n+   You should have received a copy of the GNU Lesser General Public\n+   License along with the GNU C Library; if not, see\n+   <https://www.gnu.org/licenses/>.  */\n+\n+#if IS_IN (libc)\n+\n+#include <malloc/malloc-internal.h>\n+#include <malloc-ifuncs.h>\n+\n+# if HAVE_IFUNC\n+\n+/* These resolvers are used by default unless overridden by a target.\n+   The target-specific resolvers must respect this logic of the default\n+   resolvers, replicating this logic where appropriate.\n+\n+   Any aliases for malloc API functions must be defined here as well\n+   and re-defined along with the target-specific resolvers.  */\n+\n+IFUNC_PROTO (__libc_malloc);\n+IFUNC_RESOLVER (__libc_malloc, void)\n+{\n+  return __libc_malloc_core;\n+}\n+strong_alias (__libc_malloc, malloc)\n+\n+IFUNC_PROTO (__libc_calloc);\n+IFUNC_RESOLVER (__libc_calloc, void)\n+{\n+  return __libc_calloc_core;\n+}\n+weak_alias (__libc_calloc, calloc)\n+\n+IFUNC_PROTO (__libc_memalign);\n+IFUNC_RESOLVER (__libc_memalign, void)\n+{\n+  return __libc_memalign_core;\n+}\n+weak_alias (__libc_memalign, memalign)\n+\n+IFUNC_PROTO (__libc_valloc);\n+IFUNC_RESOLVER (__libc_valloc, void)\n+{\n+  return __libc_valloc_core;\n+}\n+weak_alias (__libc_valloc, valloc)\n+\n+IFUNC_PROTO (__libc_pvalloc);\n+IFUNC_RESOLVER (__libc_pvalloc, void)\n+{\n+  return __libc_pvalloc_core;\n+}\n+weak_alias (__libc_pvalloc, pvalloc)\n+\n+IFUNC_PROTO (__libc_realloc);\n+IFUNC_RESOLVER (__libc_realloc, void)\n+{\n+  return __libc_realloc_core;\n+}\n+strong_alias (__libc_realloc, realloc)\n+\n+IFUNC_PROTO (__libc_free);\n+IFUNC_RESOLVER (__libc_free, void)\n+{\n+  return __libc_free_core;\n+}\n+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)\n+compat_symbol (libc, __libc_free, cfree, GLIBC_2_0);\n+# endif\n+strong_alias (__libc_free, free)\n+\n+IFUNC_PROTO (__malloc_usable_size);\n+IFUNC_RESOLVER (__malloc_usable_size, void)\n+{\n+  return __malloc_usable_size_core;\n+}\n+weak_alias (__malloc_usable_size, malloc_usable_size)\n+\n+# endif /* HAVE_IFUNC */\n+\n+#endif /* IS_IN (libc) */\ndiff --git a/sysdeps/generic/malloc-ifuncs.h b/sysdeps/generic/malloc-ifuncs.h\nnew file mode 100644\nindex 0000000000..1d3ddb0202\n--- /dev/null\n+++ b/sysdeps/generic/malloc-ifuncs.h\n@@ -0,0 +1,61 @@\n+/* Definitions for ifunc resolvers for malloc: generic version.\n+   Copyright (C) 2026 Free Software Foundation, Inc.\n+   This file is part of the GNU C Library.\n+\n+   The GNU C Library is free software; you can redistribute it and/or\n+   modify it under the terms of the GNU Lesser General Public\n+   License as published by the Free Software Foundation; either\n+   version 2.1 of the License, or (at your option) any later version.\n+\n+   The GNU C Library is distributed in the hope that it will be useful,\n+   but WITHOUT ANY WARRANTY; without even the implied warranty of\n+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+   Lesser General Public License for more details.\n+\n+   You should have received a copy of the GNU Lesser General Public\n+   License along with the GNU C Library; if not, see\n+   <https://www.gnu.org/licenses/>.  */\n+\n+#ifndef _GENERIC_MALLOC_IFUNCS_H\n+#define _GENERIC_MALLOC_IFUNCS_H\n+\n+#if HAVE_IFUNC\n+\n+#include <stddef.h>\n+#include <sys/cdefs.h>\n+#include <shlib-compat.h>\n+\n+/* Core implementations of malloc functions.  An ifunc resolver must\n+   use this implementations as a fallback option.  Other implementations\n+   may internally call these core function.  */\n+void *__libc_malloc_core (size_t);\n+libc_hidden_proto (__libc_malloc_core)\n+void *__libc_calloc_core (size_t, size_t);\n+libc_hidden_proto (__libc_calloc_core)\n+void *__libc_memalign_core (size_t, size_t);\n+libc_hidden_proto (__libc_memalign_core)\n+void *__libc_valloc_core (size_t);\n+libc_hidden_proto (__libc_valloc_core)\n+void *__libc_pvalloc_core (size_t);\n+libc_hidden_proto (__libc_pvalloc_core)\n+void *__libc_realloc_core (void *, size_t);\n+libc_hidden_proto (__libc_realloc_core)\n+void __libc_free_core (void *);\n+libc_hidden_proto (__libc_free_core)\n+size_t __malloc_usable_size_core (void *);\n+libc_hidden_proto (__malloc_usable_size_core)\n+\n+/* Macros for defining ifunc resolvers for malloc functions.  */\n+#define IFUNC_RESOLVER_NAME(fn) fn ## _resolver\n+#define STR(x) #x\n+#define XSTR(x) STR(x)\n+#define IFUNC_PROTO(fn) \\\n+  __typeof (fn ## _core) fn \\\n+  __attribute__ ((ifunc (XSTR(IFUNC_RESOLVER_NAME(fn)))))\n+#define IFUNC_RESOLVER(fn, ...) \\\n+  static __attribute_used__ \\\n+  __typeof (fn ## _core) *IFUNC_RESOLVER_NAME(fn) (__VA_ARGS__)\n+\n+#endif /* HAVE_IFUNC */\n+\n+#endif /* _GENERIC_MALLOC_IFUNCS_H */\n","prefixes":["v2","2/2"]}