diff --git a/NEWS b/NEWS
index d65eaeadf7..723dac1ccc 100644
--- a/NEWS
+++ b/NEWS
@@ -49,6 +49,12 @@ Major new features:
   mappings to avoid further change during process execution such as protection
   permissions, unmapping, moving to another location, or shrinking the size.
 
+* The loader will memory seal all libraries that contains the new gnu
+  attribute GNU_PROPERTY_MEMORY_SEAL.  The memory sealing uses the new Linux
+  mseal syscall, and it will be applied to all shared libraries dependencies,
+  the binary, any preload and audit modules, and aby library loaded with
+  RTLD_NODELETE.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The big-endian ARC port (arceb-linux-gnu) has been removed.
diff --git a/elf/dl-load.c b/elf/dl-load.c
index e986d7faab..b52c29ccb7 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1415,6 +1415,10 @@ cannot enable executable stack as shared object requires");
     /* Assign the next available module ID.  */
     _dl_assign_tls_modid (l);
 
+  if (l->l_seal == lt_seal_toseal
+      && (mode & __RTLD_DLOPEN) && !(mode & RTLD_NODELETE))
+    l->l_seal = lt_seal_dont_dlopen;
+
 #ifdef DL_AFTER_LOAD
   DL_AFTER_LOAD (l);
 #endif
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index 30977cf800..51c5286f6e 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -18,6 +18,7 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <dl-load.h>
+#include <dl-mseal.h>
 
 /* Map a segment and align it properly.  */
 
@@ -115,11 +116,15 @@ _dl_map_segments (struct link_map *l, int fd,
 	  if (__glibc_unlikely (loadcmds[nloadcmds - 1].mapstart <
 				c->mapend))
 	    return N_("ELF load command address/offset not page-aligned");
+
+	  caddr_t hole_start = (caddr_t) (l->l_addr + c->mapend);
+	  size_t hole_size = loadcmds[nloadcmds - 1].mapstart - c->mapend;
+
           if (__glibc_unlikely
-              (__mprotect ((caddr_t) (l->l_addr + c->mapend),
-                           loadcmds[nloadcmds - 1].mapstart - c->mapend,
-                           PROT_NONE) < 0))
+              (__mprotect (hole_start, hole_size, PROT_NONE) < 0))
             return DL_MAP_SEGMENTS_ERROR_MPROTECT;
+	  if (l->l_seal)
+	    _dl_mseal (hole_start, hole_size);
         }
 
       l->l_contiguous = 1;
@@ -188,6 +193,11 @@ _dl_map_segments (struct link_map *l, int fd,
                               -1, 0);
               if (__glibc_unlikely (mapat == MAP_FAILED))
                 return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL;
+	      /* We need to seal this here because it will not be part of
+		 the PT_LOAD segments, nor it is taken in RELRO
+		 calculation.  */
+	      if (l->l_seal)
+		_dl_mseal (mapat, zeroend - zeropage);
             }
         }
 
diff --git a/elf/dl-minimal-malloc.c b/elf/dl-minimal-malloc.c
index 69fc19c1b7..a701ff89d5 100644
--- a/elf/dl-minimal-malloc.c
+++ b/elf/dl-minimal-malloc.c
@@ -27,6 +27,7 @@
 #include <ldsodefs.h>
 #include <malloc/malloc-internal.h>
 #include <setvmaname.h>
+#include <dl-mseal.h>
 
 static void *alloc_ptr, *alloc_end, *alloc_last_block;
 
@@ -62,6 +63,8 @@ __minimal_malloc (size_t n)
       if (page == MAP_FAILED)
 	return NULL;
       __set_vma_name (page, nup, " glibc: loader malloc");
+      if (GL(dl_rtld_map).l_seal == lt_seal_toseal)
+	_dl_mseal (page, nup);
       if (page != alloc_end)
 	alloc_ptr = page;
       alloc_end = page + nup;
diff --git a/elf/dl-open.c b/elf/dl-open.c
index f283a87144..36ee904eba 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -807,6 +807,10 @@ dl_open_worker (void *a)
   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
     _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
 		      new->l_name, new->l_ns, new->l_direct_opencount);
+
+  /* The seal flag is set only for NEW, however its dependencies could not be
+     unloaded and thus can also be sealed.  */
+  _dl_mseal_map (new, true, false);
 }
 
 void *
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 76d14830dd..f8127cb166 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -28,6 +28,7 @@
 #include <_itoa.h>
 #include <libc-pointer-arith.h>
 #include "dynamic-link.h"
+#include <dl-mseal.h>
 
 /* Statistics function.  */
 #ifdef SHARED
@@ -345,6 +346,7 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
     return;
   _dl_relocate_object_no_relro (l, scope, reloc_mode, consider_profiling);
   _dl_protect_relro (l);
+  _dl_mseal_map (l, false, false);
 }
 
 void
@@ -369,6 +371,54 @@ cannot apply additional memory protection after relocation");
     }
 }
 
+static void
+_dl_mseal_map_1 (struct link_map *l, bool force)
+{
+  /* The 'force' check allow to seal audit with sealing enabled after
+     they are loader during process startup.  */
+  if (l->l_seal == lt_seal_dont
+      || (force
+	  ? l->l_seal != lt_seal_dont_dlopen
+	  : l->l_seal == lt_seal_dont_dlopen))
+    return;
+
+  int r = -1;
+  if (l->l_contiguous)
+    r = _dl_mseal ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
+  else
+    {
+      /* We can use the PT_LOAD segments because even if relro splits the
+	 original RW VMA, mseal works with multiple VMAs with different
+	 flags.  */
+      const ElfW(Phdr) *ph;
+      for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
+	switch (ph->p_type)
+	  {
+	  case PT_LOAD:
+	    {
+	      ElfW(Addr) mapstart = l->l_addr
+		  + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1));
+	      ElfW(Addr) allocend = l->l_addr + ph->p_vaddr + ph->p_memsz;
+	      r = _dl_mseal ((void *) mapstart, allocend - mapstart);
+	    }
+	    break;
+	}
+    }
+
+  if (r == 0)
+    l->l_seal = lt_seal_sealed;
+}
+
+void
+_dl_mseal_map (struct link_map *l, bool dep, bool force)
+{
+  if (l->l_searchlist.r_list == NULL || !dep)
+    _dl_mseal_map_1 (l, force);
+  else
+    for (unsigned int i = 0; i < l->l_searchlist.r_nlist; ++i)
+      _dl_mseal_map_1 (l->l_searchlist.r_list[i], force);
+}
+
 void
 __attribute_noinline__
 _dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt)
diff --git a/elf/dl-support.c b/elf/dl-support.c
index f0b6be07e9..e43b455de4 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -46,6 +46,7 @@
 #include <array_length.h>
 #include <dl-symbol-redir-ifunc.h>
 #include <dl-prop.h>
+#include <dl-mseal.h>
 
 extern char *__progname;
 char **_dl_argv = &__progname;	/* This is checked for some error messages.  */
@@ -100,6 +101,7 @@ static struct link_map _dl_main_map =
     .l_used = 1,
     .l_tls_offset = NO_TLS_OFFSET,
     .l_serial = 1,
+    .l_seal = lt_seal_dont,
   };
 
 /* Namespace information.  */
@@ -352,6 +354,7 @@ _dl_non_dynamic_init (void)
 
   /* Setup relro on the binary itself.  */
   _dl_protect_relro (&_dl_main_map);
+  _dl_mseal_map (&_dl_main_map, false, false);
 }
 
 #ifdef DL_SYSINFO_IMPLEMENTATION
diff --git a/elf/elf.h b/elf/elf.h
index 33aea7f743..b9fe2064af 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -1357,6 +1357,8 @@ typedef struct
 #define GNU_PROPERTY_STACK_SIZE			1
 /* No copy relocation on protected data symbol.  */
 #define GNU_PROPERTY_NO_COPY_ON_PROTECTED	2
+/* No memory sealing.  */
+#define GNU_PROPERTY_MEMORY_SEAL		3
 
 /* A 4-byte unsigned integer property: A bit is set if it is set in all
    relocatable inputs.  */
diff --git a/elf/rtld.c b/elf/rtld.c
index 21d282fc87..71902de400 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -53,6 +53,7 @@
 #include <dl-find_object.h>
 #include <dl-audit-check.h>
 #include <dl-call_tls_init_tp.h>
+#include <dl-mseal.h>
 
 #include <assert.h>
 
@@ -478,6 +479,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
   GL(dl_rtld_map).l_real = &GL(dl_rtld_map);
   GL(dl_rtld_map).l_map_start = (ElfW(Addr)) &__ehdr_start;
   GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;
+  GL(dl_rtld_map).l_seal = lt_seal_dont;
   /* Copy the TLS related data if necessary.  */
 #ifndef DONT_USE_BOOTSTRAP_MAP
 # if NO_TLS_OFFSET != 0
@@ -1042,6 +1044,11 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
 
   /* Mark the DSO as being used for auditing.  */
   dlmargs.map->l_auditing = 1;
+
+  /* Audit modules can not be loaded with RTLD_NODELETE, so apply the sealing
+     again on all dependencies an and ignore any possible missing seal due
+     dlopen without RTLD_NODELETE.  */
+  _dl_mseal_map (dlmargs.map, true, true);
 }
 
 /* Load all audit modules.  */
@@ -1124,6 +1131,7 @@ rtld_setup_main_map (struct link_map *main_map)
   /* And it was opened directly.  */
   ++main_map->l_direct_opencount;
   main_map->l_contiguous = 1;
+  main_map->l_seal = lt_seal_dont;
 
   /* A PT_LOAD segment at an unexpected address will clear the
      l_contiguous flag.  The ELF specification says that PT_LOAD
@@ -2344,8 +2352,8 @@ dl_main (const ElfW(Phdr) *phdr,
        at this point.  */
     __rtld_malloc_init_real (main_map);
 
-    if (GL(dl_rtld_map).l_relro_size != 0)
-      _dl_protect_relro (&GL(dl_rtld_map));
+    _dl_protect_relro (&GL(dl_rtld_map));
+    _dl_mseal_map (&GL(dl_rtld_map), false, false);
 
     rtld_timer_accum (&relocate_time, start);
   }
diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
index 888e1e4897..f115e6eb78 100644
--- a/elf/setup-vdso.h
+++ b/elf/setup-vdso.h
@@ -66,6 +66,8 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
 
       /* The vDSO is always used.  */
       l->l_used = 1;
+      /* The PT_LOAD may not cover all the vdso mapping.  */
+      l->l_seal = lt_seal_dont;
 
       /* Initialize l_local_scope to contain just this map.  This allows
 	 the use of dl_lookup_symbol_x to resolve symbols within the vdso.
diff --git a/include/link.h b/include/link.h
index 5ed445d5a6..e8ee740099 100644
--- a/include/link.h
+++ b/include/link.h
@@ -214,6 +214,14 @@ struct link_map
 					       lt_library map.  */
     unsigned int l_tls_in_slotinfo:1; /* TLS slotinfo updated in dlopen.  */
 
+    enum			/* Memory sealing status.  */
+      {
+	lt_seal_dont = 0,	/* Do not seal the object.  */
+	lt_seal_dont_dlopen,    /* Do not seal from a dlopen.  */
+	lt_seal_toseal,		/* The library is marked to be sealed.  */
+	lt_seal_sealed		/* The library is sealed.  */
+      } l_seal:2;
+
     /* NODELETE status of the map.  Only valid for maps of type
        lt_loaded.  Lazy binding sets l_nodelete_active directly,
        potentially from signal handlers.  Initial loading of an
diff --git a/sysdeps/aarch64/dl-prop.h b/sysdeps/aarch64/dl-prop.h
index df05c0211d..c66d9a49f0 100644
--- a/sysdeps/aarch64/dl-prop.h
+++ b/sysdeps/aarch64/dl-prop.h
@@ -19,6 +19,8 @@
 #ifndef _DL_PROP_H
 #define _DL_PROP_H
 
+#include <dl-prop-mseal.h>
+
 extern void _dl_bti_protect (struct link_map *, int) attribute_hidden;
 
 extern void _dl_bti_check (struct link_map *, const char *)
@@ -45,6 +47,9 @@ static inline int
 _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type,
 			  uint32_t datasz, void *data)
 {
+  if (_dl_process_gnu_property_seal (l, fd, type, datasz, data))
+    return 0;
+
   if (!GLRO(dl_aarch64_cpu_features).bti)
     /* Skip note processing.  */
     return 0;
diff --git a/sysdeps/generic/dl-mseal.h b/sysdeps/generic/dl-mseal.h
new file mode 100644
index 0000000000..dccf78ae38
--- /dev/null
+++ b/sysdeps/generic/dl-mseal.h
@@ -0,0 +1,23 @@
+/* Memory sealing.  Generic version.
+   Copyright (C) 2024 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/>.  */
+
+static inline int
+_dl_mseal (void *addr, size_t len)
+{
+  return 0;
+}
diff --git a/sysdeps/generic/dl-prop-mseal.h b/sysdeps/generic/dl-prop-mseal.h
new file mode 100644
index 0000000000..b1f93a17fb
--- /dev/null
+++ b/sysdeps/generic/dl-prop-mseal.h
@@ -0,0 +1,36 @@
+/* Support for GNU properties.  Generic version.
+   Copyright (C) 2024 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 _DL_PROP_MSEAL_H
+#define _LD_PROP_MSEAL_H
+
+#include <dl-tunables.h>
+
+static __always_inline bool
+_dl_process_gnu_property_seal (struct link_map *l, int fd, uint32_t type,
+			       uint32_t datasz, void *data)
+{
+  if (type == GNU_PROPERTY_MEMORY_SEAL && datasz == 0)
+    {
+      l->l_seal = lt_seal_toseal;
+      return true;
+    }
+  return false;
+}
+
+#endif
diff --git a/sysdeps/generic/dl-prop.h b/sysdeps/generic/dl-prop.h
index 1d92920a96..5fac690c81 100644
--- a/sysdeps/generic/dl-prop.h
+++ b/sysdeps/generic/dl-prop.h
@@ -19,6 +19,8 @@
 #ifndef _DL_PROP_H
 #define _DL_PROP_H
 
+#include <dl-prop-mseal.h>
+
 /* The following functions are used by the dynamic loader and the
    dlopen machinery to process PT_NOTE and PT_GNU_PROPERTY entries in
    the binary or shared object.  The notes can be used to change the
@@ -47,6 +49,9 @@ static inline int __attribute__ ((always_inline))
 _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type,
 			  uint32_t datasz, void *data)
 {
+  if (_dl_process_gnu_property_seal (l, fd, type, datasz, data))
+    return 0;
+
   /* Continue until GNU_PROPERTY_1_NEEDED is found.  */
   if (type == GNU_PROPERTY_1_NEEDED)
     {
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 91447a5e77..dbfa5d7a6a 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1024,6 +1024,15 @@ void _dl_relocate_object_no_relro (struct link_map *map,
 /* Protect PT_GNU_RELRO area.  */
 extern void _dl_protect_relro (struct link_map *map) attribute_hidden;
 
+/* Issue memory sealing for the link map MAP.  If MAP is contiguous the
+   whole region is sealed, otherwise iterate over the program headerrs and
+   seal each PT_LOAD segment.i
+   The DEP specify whether to seal the dependencies as well, while FORCE
+   ignores any possible missing seal due dlopen without RTLD_NODELETE.
+   The memory sealing should be done *after* RELRO setup.  */
+extern void _dl_mseal_map (struct link_map *map, bool dep, bool force)
+     attribute_hidden;
+
 /* Call _dl_signal_error with a message about an unhandled reloc type.
    TYPE is the result of ELFW(R_TYPE) (r_info), i.e. an R_<CPU>_* value.
    PLT is nonzero if this was a PLT reloc; it just affects the message.  */
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index a270b0af4c..8c1389cb0f 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -644,6 +644,10 @@ sysdep-rtld-routines += \
   dl-sbrk \
   # sysdep-rtld-routines
 
+dl-routines += \
+  dl-mseal \
+  # dl-routines
+
 others += \
   pldd \
   # others
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.c b/sysdeps/unix/sysv/linux/dl-mseal.c
new file mode 100644
index 0000000000..c99fd991cb
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.c
@@ -0,0 +1,41 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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 <atomic.h>
+#include <dl-mseal.h>
+#include <dl-tunables.h>
+#include <ldsodefs.h>
+
+int
+_dl_mseal (void *addr, size_t len)
+{
+  int r;
+#if __ASSUME_MSEAL
+  r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+#else
+  r = -ENOSYS;
+  static int mseal_supported = true;
+  if (atomic_load_relaxed (&mseal_supported))
+    {
+      r = INTERNAL_SYSCALL_CALL (mseal, addr, len, 0);
+      if (r == -ENOSYS)
+	atomic_store_relaxed (&mseal_supported, false);
+    }
+#endif
+  return r;
+}
diff --git a/sysdeps/unix/sysv/linux/dl-mseal.h b/sysdeps/unix/sysv/linux/dl-mseal.h
new file mode 100644
index 0000000000..25e3f724dc
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mseal.h
@@ -0,0 +1,27 @@
+/* Memory sealing.  Linux version.
+   Copyright (C) 2024 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/>.  */
+
+/* Seal the ADDR or size LEN to protect against modifications, such as
+   changes on the permission flags (through mprotect), remap (through
+   mmap and/or remap), shrink, destruction changes (madvise with
+   MADV_DONTNEED), or change its size.  The input has the same constraints
+   as the mseal syscall.
+
+   Return 0 in case of success or a negative value otherwise (a negative
+   errno).  */
+int _dl_mseal (void *addr, size_t len) attribute_hidden;
diff --git a/sysdeps/x86/dl-prop.h b/sysdeps/x86/dl-prop.h
index 08387dfaff..26a687d611 100644
--- a/sysdeps/x86/dl-prop.h
+++ b/sysdeps/x86/dl-prop.h
@@ -19,6 +19,7 @@
 #ifndef _DL_PROP_H
 #define _DL_PROP_H
 
+#include <dl-prop-mseal.h>
 #include <libintl.h>
 
 extern void _dl_cet_check (struct link_map *, const char *)
@@ -241,6 +242,9 @@ _dl_process_gnu_property (struct link_map *l, int fd, uint32_t type,
 			  uint32_t datasz, void *data)
 {
   /* This is called on each GNU property.  */
+  if (_dl_process_gnu_property_seal (l, fd, type, datasz, data))
+    return 0;
+
   unsigned int needed_1 = 0;
   unsigned int feature_1_and = 0;
   unsigned int isa_1_needed = 0;
