]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
support: add support_address_diff function
authorYury Khrustalev <yury.khrustalev@arm.com>
Wed, 25 Mar 2026 10:04:19 +0000 (10:04 +0000)
committerYury Khrustalev <yury.khrustalev@arm.com>
Wed, 22 Apr 2026 13:55:12 +0000 (14:55 +0100)
Some malloc tests compare pointers meaning to compare addresses.
On AArch64, the 64-bit value of the pointer may contain metadata
along with the values of the address.

In order to correctly compare addresses, we add new function for
AArch64 target that will use the AArch64 64 SUBP (subtract pointer)
instruction when it is available. This instruction uses the 56-bit
addresses ignoring top-byte metadata.

Best implementation is selected using ifunc resolver.

On other targets and also on AArch64 when MTE is not available this
function defaults to PTR_DIFF defined in libc-pointer-arith.h.

Three malloc tests are modified accordingly:
 - tst-memalign-2.c
 - tst-memalign-3.c
 - tst-realloc.c

Reviewed-by: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
malloc/tst-memalign-2.c
malloc/tst-memalign-3.c
malloc/tst-realloc.c
support/Makefile
support/address-diff.h [new file with mode: 0644]
sysdeps/aarch64/Makefile
sysdeps/aarch64/support-address-diff.c [new file with mode: 0644]
sysdeps/generic/Makefile
sysdeps/generic/support-address-diff.c [new file with mode: 0644]

index b3e393c249c361a70e272d82fa5529857ed27049..2dd88082363128066750d914892ad19d69ebac2f 100644 (file)
@@ -23,6 +23,7 @@
 #include <unistd.h>
 #include <array_length.h>
 #include <libc-pointer-arith.h>
+#include <support/address-diff.h>
 #include <support/check.h>
 #include "tst-malloc-aux.h"
 
@@ -82,9 +83,9 @@ do_test (void)
       /* This should return the same chunk as was just free'd.  */
       tcache_allocs[i].ptr2 = memalign (tcache_allocs[i].alignment, sz2);
       CHECK (tcache_allocs[i].ptr2, tcache_allocs[i].alignment);
+      TEST_VERIFY (support_address_diff (
+       tcache_allocs[i].ptr1, tcache_allocs[i].ptr2) == 0);
       free (tcache_allocs[i].ptr2);
-
-      TEST_VERIFY (tcache_allocs[i].ptr1 == tcache_allocs[i].ptr2);
     }
 
   /* Test for non-head tcache hits.  This exercises the memalign
@@ -110,7 +111,7 @@ do_test (void)
 
   count = 0;
   for (i = 0; i < 10; ++ i)
-    if (ptr[i] == p)
+    if (support_address_diff (ptr[i], p) == 0)
       ++ count;
   free (p);
   TEST_VERIFY (count > 0);
@@ -146,7 +147,8 @@ do_test (void)
     {
       int ok = 0;
       for (j = 0; j < LN; ++ j)
-       if (large_allocs[i].ptr1 == large_allocs[j].ptr2)
+       if (support_address_diff (large_allocs[i].ptr1, large_allocs[j].ptr2)
+           == 0)
          ok = 1;
       if (ok == 1)
        count ++;
index 435e1c94d541919ce58454f02921a1e51dab65eb..f1b045b09555802336c4ac1ea230f9d367ce8a43 100644 (file)
@@ -24,6 +24,7 @@
 #include <unistd.h>
 #include <array_length.h>
 #include <libc-pointer-arith.h>
+#include <support/address-diff.h>
 #include <support/check.h>
 #include <support/xthread.h>
 #include "tst-malloc-aux.h"
@@ -85,9 +86,9 @@ mem_test (void *closure)
       /* This should return the same chunk as was just free'd.  */
       tcache_allocs[i].ptr2 = memalign (tcache_allocs[i].alignment, sz2);
       CHECK (tcache_allocs[i].ptr2, tcache_allocs[i].alignment);
+      TEST_VERIFY (support_address_diff (
+       tcache_allocs[i].ptr1, tcache_allocs[i].ptr2) == 0);
       free (tcache_allocs[i].ptr2);
-
-      TEST_VERIFY (tcache_allocs[i].ptr1 == tcache_allocs[i].ptr2);
     }
 
   /* Test for non-head tcache hits.  */
@@ -112,7 +113,7 @@ mem_test (void *closure)
 
   count = 0;
   for (i = 0; i < 10; ++ i)
-    if (ptr[i] == p)
+    if (support_address_diff (ptr[i], p) == 0)
       ++ count;
   free (p);
   TEST_VERIFY (count > 0);
@@ -146,7 +147,8 @@ mem_test (void *closure)
     {
       int ok = 0;
       for (j = 0; j < LN; ++ j)
-       if (large_allocs[i].ptr1 == large_allocs[j].ptr2)
+       if (support_address_diff (large_allocs[i].ptr1, large_allocs[j].ptr2)
+           == 0)
          ok = 1;
       if (ok == 1)
        count ++;
index e02b28d98299ee2264d3e7b1d1c4e4697ebbf066..0e616d38279f4af0a447d68c29c17867a21cf358 100644 (file)
@@ -22,6 +22,7 @@
 #include <string.h>
 #include <libc-diag.h>
 #include <support/check.h>
+#include <support/address-diff.h>
 
 #include "tst-malloc-aux.h"
 
@@ -155,9 +156,9 @@ do_test (void)
       size_t newsz = malloc_usable_size (p);
       printf ("size: %zu, usable size: %zu, extra: %zu\n",
              sz, newsz, newsz - sz);
-      uintptr_t oldp = (uintptr_t) p;
+      void *oldp = p;
       void *new_p = realloc (p, newsz);
-      if ((uintptr_t) new_p != oldp)
+      if (support_address_diff (new_p, oldp) != 0)
        FAIL_EXIT1 ("Expanding (%zu bytes) to usable size (%zu) moved block",
                    sz, newsz);
       free (new_p);
index 1dae2802cf7276636e08ec30b4509effcd920eee..3f19a98bdc4cfe4ce33c98ee81033952d3236c0b 100644 (file)
@@ -254,7 +254,7 @@ libsupport-routines = \
   xwrite \
   # libsupport-routines
 
-libsupport-static-only-routines := $(libsupport-routines)
+libsupport-static-only-routines = $(libsupport-routines) $(libsupport-sysdep_routines)
 # Only build one variant of the library.
 libsupport-inhibit-o := .os
 ifeq ($(build-shared),yes)
diff --git a/support/address-diff.h b/support/address-diff.h
new file mode 100644 (file)
index 0000000..ef58b9d
--- /dev/null
@@ -0,0 +1,27 @@
+/* Support functions for pointer arithmetic.
+   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 _POINTER_ARITH_H
+#define _POINTER_ARITH_H 1
+
+#include <stddef.h>
+
+/* Returns difference in bytes between addresses of two pointers.  */
+ptrdiff_t support_address_diff (const void *lhs, const void *rhs);
+
+#endif /* _POINTER_ARITH_H */
index d6c5cc96ca351ebcef354ccb1959d9c89f15e2de..d3146ddceda05d3de982e0eb8aaafa31aeccb9e9 100644 (file)
@@ -90,3 +90,9 @@ endif
 ifeq ($(subdir),malloc)
 sysdep_malloc_debug_routines = __mtag_tag_zero_region __mtag_tag_region
 endif # malloc directory
+
+ifeq ($(subdir),support)
+libsupport-sysdep_routines += \
+  support-address-diff \
+  # libsupport-sysdep_routines
+endif
diff --git a/sysdeps/aarch64/support-address-diff.c b/sysdeps/aarch64/support-address-diff.c
new file mode 100644 (file)
index 0000000..fd8d3ed
--- /dev/null
@@ -0,0 +1,50 @@
+/* Support functions for pointer arithmetic for AArch64.
+   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/>.  */
+
+#include "address-diff.h"
+
+#include <sys/ifunc.h>
+#include <sys/auxv.h>
+#include <libc-pointer-arith.h>
+
+static ptrdiff_t
+address_diff_mte (const void *lhs, const void *rhs)
+{
+  register const void *x0 asm ("x0") = lhs;
+  register const void *x1 asm ("x1") = rhs;
+  asm (".inst 0x9ac10000 /* subp x0, x0, x1 */" : "+r" (x0) : "r" (x1));
+  return (ptrdiff_t)x0;
+}
+
+static ptrdiff_t
+address_diff_generic (const void *lhs, const void *rhs)
+{
+  return PTR_DIFF (lhs, rhs);
+}
+
+static void * __attribute__ ((unused))
+address_diff_resolver (unsigned long a0, const unsigned long *a1)
+{
+  unsigned long hwcap2 = __ifunc_hwcap (_IFUNC_ARG_AT_HWCAP2, a0, a1);
+  if (hwcap2 & HWCAP2_MTE)
+    return (void *)address_diff_mte;
+  return (void *)address_diff_generic;
+}
+
+ptrdiff_t support_address_diff (const void *lhs, const void *rhs)
+__attribute__ ((ifunc ("address_diff_resolver")));
index 53c4efa63e5c6d0e528d5cb2cfe3a951b0fdb8ad..8af1fad579eb1f03bc272b18dcec252e7d993959 100644 (file)
@@ -38,3 +38,9 @@ endif
 ifeq ($(subdir),misc)
 sysdep_routines += hugepages
 endif
+
+ifeq ($(subdir),support)
+libsupport-sysdep_routines += \
+  support-address-diff \
+  # libsupport-sysdep_routines
+endif
diff --git a/sysdeps/generic/support-address-diff.c b/sysdeps/generic/support-address-diff.c
new file mode 100644 (file)
index 0000000..51b5640
--- /dev/null
@@ -0,0 +1,26 @@
+/* Support functions for pointer arithmetic.
+   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/>.  */
+
+#include "address-diff.h"
+
+#include <libc-pointer-arith.h>
+
+ptrdiff_t support_address_diff (const void *lhs, const void *rhs)
+{
+  return PTR_DIFF (lhs, rhs);
+}