diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
index d50384c47b..9d6e16f66e 100644
--- a/sysdeps/aarch64/Makefile
+++ b/sysdeps/aarch64/Makefile
@@ -9,7 +9,7 @@ LDFLAGS-rtld += -Wl,-z,force-bti,--fatal-warnings
endif
ifeq ($(subdir),elf)
-sysdep-dl-routines += dl-bti
+sysdep-dl-routines += dl-bti dl-symaddr
tests += tst-audit26 \
tst-audit27
diff --git a/sysdeps/aarch64/dl-lookupcfg.h b/sysdeps/aarch64/dl-lookupcfg.h
index 64d46a050e..aa3e50d46f 100644
--- a/sysdeps/aarch64/dl-lookupcfg.h
+++ b/sysdeps/aarch64/dl-lookupcfg.h
@@ -22,6 +22,14 @@
struct link_map;
+#ifdef __CHERI_PURE_CAPABILITY__
+/* Symbol pointer with correct capability permission and bounds. */
+void *_dl_symbol_address (struct link_map *map, const ElfW(Sym) *ref);
+rtld_hidden_proto (_dl_symbol_address)
+
+# define DL_SYMBOL_ADDRESS(map, ref) _dl_symbol_address(map, ref)
+#endif
+
extern void _dl_unmap (struct link_map *map);
#define DL_UNMAP(map) _dl_unmap (map)
diff --git a/sysdeps/aarch64/dl-symaddr.c b/sysdeps/aarch64/dl-symaddr.c
new file mode 100644
index 0000000000..a85969a8f5
--- /dev/null
+++ b/sysdeps/aarch64/dl-symaddr.c
@@ -0,0 +1,17 @@
+/* Empty file: AArch64 does not have special DL_SYMBOL_ADDRESS.
+ Copyright (C) 2022 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
+ . */
diff --git a/sysdeps/aarch64/morello/Versions b/sysdeps/aarch64/morello/Versions
new file mode 100644
index 0000000000..d6c306bcbe
--- /dev/null
+++ b/sysdeps/aarch64/morello/Versions
@@ -0,0 +1,6 @@
+ld {
+ GLIBC_PRIVATE {
+ # in ld.so, but used by libc.so too.
+ _dl_symbol_address;
+ }
+}
diff --git a/sysdeps/aarch64/morello/dl-symaddr.c b/sysdeps/aarch64/morello/dl-symaddr.c
new file mode 100644
index 0000000000..b49b416170
--- /dev/null
+++ b/sysdeps/aarch64/morello/dl-symaddr.c
@@ -0,0 +1,49 @@
+/* Get the symbol address. Morello version.
+ Copyright (C) 2022 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
+ . */
+
+#include
+#include
+
+void *
+_dl_symbol_address (struct link_map *map, const ElfW(Sym) *ref)
+{
+ elfptr_t value = SYMBOL_ADDRESS (map, ref, false);
+ if (map == NULL)
+ return (void *) value;
+ if (ELFW(ST_TYPE) (ref->st_info) == STT_OBJECT)
+ {
+ unsigned long perm_mask = CAP_PERM_MASK_R;
+ for (int i = 0; i < map->l_rw_count; i++)
+ if (map->l_rw_range[i].start <= value
+ && map->l_rw_range[i].end > value)
+ {
+ value = dl_rw_ptr (map, value - map->l_addr);
+ perm_mask = CAP_PERM_MASK_RW;
+ break;
+ }
+ value = __builtin_cheri_bounds_set_exact (value, ref->st_size);
+ value = __builtin_cheri_perms_and (value, perm_mask);
+ }
+ else if (ELFW(ST_TYPE) (ref->st_info) == STT_FUNC)
+ {
+ /* Seal function pointers. Note: ifunc is handled by the caller. */
+ value = __builtin_cheri_seal_entry (value);
+ }
+ return (void *) value;
+}
+rtld_hidden_def (_dl_symbol_address)
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 3cb35dbb93..0cc9d7e89c 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -140,8 +140,10 @@ typedef void (*dl_init_t) (int, char **, char **);
to the actual code of the function but rather an architecture
specific descriptor. */
#ifndef ELF_FUNCTION_PTR_IS_SPECIAL
-# define DL_SYMBOL_ADDRESS(map, ref) \
+# ifndef __CHERI_PURE_CAPABILITY__
+# define DL_SYMBOL_ADDRESS(map, ref) \
(void *) SYMBOL_ADDRESS (map, ref, false)
+# endif
# define DL_LOOKUP_ADDRESS(addr) ((ElfW(Addr)) (addr))
# define DL_CALL_DT_INIT(map, start, argc, argv, env) \
((dl_init_t) (start)) (argc, argv, env)