[PATCH v8 6/6] elf: Raise SIGABRT for assert within ld.so

Adhemerval Zanella adhemerval.zanella@linaro.org
Wed Mar 18 19:34:17 GMT 2026


The assert static path already properly raises SIGABRT for assert during
process startup, while ld.so only prints the error message and issues
_exit.  This prevents a coredump from being created if the environment
is configured to do so.

Similar to rtld malloc hooks, the assert symbol __assert_fail and
assert_perror_fail need to be relocated to the libc symbol after the
final relocation (so dlfcn.h functions call libc __assert_fail instead
of dl-minimal.c one). The hook framework is renamed to be more generic,
instead of tying to rtld malloc.

The Hurd requires a proper __raise_direct implementation, issuing raise
or kill pull a lot of symbols that prevents ld.so building.

Checked on x86_64-linux-gnu and i686-linux-gnu.
---
 elf/dl-minimal.c                 | 82 ++++++++++++++++++++++++--------
 elf/rtld.c                       |  4 +-
 include/assert.h                 |  2 +
 include/rtld-assert.h            | 55 +++++++++++++++++++++
 include/rtld-malloc.h            | 14 ------
 include/stdio.h                  |  2 +
 sysdeps/generic/ldsodefs.h       | 16 +++++++
 sysdeps/mach/hurd/Makefile       |  6 +++
 sysdeps/mach/hurd/raise_direct.c | 29 +++++++++++
 9 files changed, 175 insertions(+), 35 deletions(-)
 create mode 100644 include/rtld-assert.h
 create mode 100644 sysdeps/mach/hurd/raise_direct.c

diff --git a/elf/dl-minimal.c b/elf/dl-minimal.c
index 4583c48db3..c00f4427f8 100644
--- a/elf/dl-minimal.c
+++ b/elf/dl-minimal.c
@@ -27,23 +27,35 @@
 #include <unistd.h>
 #include <errno.h>
 
-/* The rtld startup code calls __rtld_malloc_init_stubs after the
+#include <abort-instr.h>
+#ifndef ABORT_INSTRUCTION
+/* No such instruction is available.  */
+# define ABORT_INSTRUCTION
+#endif
+
+/* The rtld startup code calls __rtld_init_stubs after the
   first self-relocation to adjust the pointers to the minimal
   implementation below.  Before the final relocation,
-  __rtld_malloc_init_real is called to replace the pointers with the
+  __rtld_init_real is called to replace the pointers with the
   real implementation.  */
 __typeof (calloc) *__rtld_calloc attribute_relro;
 __typeof (free) *__rtld_free attribute_relro;
 __typeof (malloc) *__rtld_malloc attribute_relro;
 __typeof (realloc) *__rtld_realloc attribute_relro;
+__typeof (__assert_fail) *__rtld_assert_fail attribute_relro;
+__typeof (__assert_perror_fail) *__rtld_assert_perror_fail attribute_relro;
+__typeof (__libc_fatal) *__rtld_libc_fatal attribute_relro;
 
 void
-__rtld_malloc_init_stubs (void)
+__rtld_init_stubs (void)
 {
   __rtld_calloc = &__minimal_calloc;
   __rtld_free = &__minimal_free;
   __rtld_malloc = &__minimal_malloc;
   __rtld_realloc = &__minimal_realloc;
+  __rtld_assert_fail = &__assert_fail;
+  __rtld_assert_perror_fail = &__assert_perror_fail;
+  __rtld_libc_fatal = &__libc_fatal;
 }
 
 bool
@@ -56,8 +68,8 @@ __rtld_malloc_is_complete (void)
 
 /* Lookup NAME at VERSION in the scope of MATCH.  */
 static void *
-lookup_malloc_symbol (struct link_map *main_map, const char *name,
-		      struct r_found_version *version)
+lookup_symbol (struct link_map *main_map, const char *name,
+	       struct r_found_version *version)
 {
 
   const ElfW(Sym) *ref = NULL;
@@ -71,8 +83,19 @@ lookup_malloc_symbol (struct link_map *main_map, const char *name,
   return _dl_sym_post (result, ref, value, 0, main_map);
 }
 
+static struct r_found_version
+create_r_round_version (const char *name)
+{
+  return (struct r_found_version) {
+    .name = name,
+    .hidden = 0,
+    .hash = _dl_elf_hash (name),
+    .filename = NULL
+  };
+}
+
 void
-__rtld_malloc_init_real (struct link_map *main_map)
+__rtld_init_real (struct link_map *main_map)
 {
   /* We cannot use relocations and initializers for this because the
      changes made by __rtld_malloc_init_stubs break REL-style
@@ -82,16 +105,22 @@ __rtld_malloc_init_real (struct link_map *main_map)
      rtld relocation (which enables RELRO, after which the pointer
      variables cannot be written to).  */
 
-  struct r_found_version version;
-  version.name = symbol_version_string (libc, GLIBC_2_0);
-  version.hidden = 0;
-  version.hash = _dl_elf_hash (version.name);
-  version.filename = NULL;
+  struct r_found_version version =
+    create_r_round_version (symbol_version_string (libc, GLIBC_2_0));
 
-  void *new_calloc = lookup_malloc_symbol (main_map, "calloc", &version);
-  void *new_free = lookup_malloc_symbol (main_map, "free", &version);
-  void *new_malloc = lookup_malloc_symbol (main_map, "malloc", &version);
-  void *new_realloc = lookup_malloc_symbol (main_map, "realloc", &version);
+  void *new_calloc = lookup_symbol (main_map, "calloc", &version);
+  void *new_free = lookup_symbol (main_map, "free", &version);
+  void *new_malloc = lookup_symbol (main_map, "malloc", &version);
+  void *new_realloc = lookup_symbol (main_map, "realloc", &version);
+  void *new_assert_fail = lookup_symbol (main_map, "__assert_fail", &version);
+  void *new_assert_perror_fail = lookup_symbol (main_map, "__assert_perror_fail",
+						&version);
+
+  struct r_found_version version_private =
+    create_r_round_version (symbol_version_string (libc, GLIBC_PRIVATE));
+
+  void *new_libc_fatal = lookup_symbol (main_map, "__libc_fatal",
+					&version_private);
 
   /* Update the pointers in one go, so that any internal allocations
      performed by lookup_malloc_symbol see a consistent
@@ -100,6 +129,9 @@ __rtld_malloc_init_real (struct link_map *main_map)
   __rtld_free = new_free;
   __rtld_malloc = new_malloc;
   __rtld_realloc = new_realloc;
+  __rtld_assert_fail = new_assert_fail;
+  __rtld_assert_perror_fail = new_assert_perror_fail;
+  __rtld_libc_fatal = new_libc_fatal;
 }
 
 
@@ -114,6 +146,18 @@ __sigjmp_save (sigjmp_buf env, int savemask __attribute__ ((unused)))
   return 0;
 }
 
+_Noreturn static void
+rtld_abort (void)
+{
+  __raise_direct (SIGABRT);
+  /* Assume there is no SIGABRT handler installed during process startup, so
+     there is no need to add a fallback to change the signal disposition, as
+     the libc implementation does.  */
+  ABORT_INSTRUCTION;
+
+  _exit (127);
+}
+
 /* Define our own version of the internal function used by strerror.  We
    only provide the messages for some common errors.  This avoids pulling
    in the whole error list.  */
@@ -172,11 +216,11 @@ void weak_function
 __assert_fail (const char *assertion,
 	       const char *file, unsigned int line, const char *function)
 {
-  _dl_fatal_printf ("\
+  _dl_error_printf ("\
 Inconsistency detected by ld.so: %s: %u: %s%sAssertion `%s' failed!\n",
 		    file, line, function ?: "", function ? ": " : "",
 		    assertion);
-
+  rtld_abort ();
 }
 # ifndef NO_RTLD_HIDDEN
 rtld_hidden_weak (__assert_fail)
@@ -188,11 +232,11 @@ __assert_perror_fail (int errnum,
 		      const char *function)
 {
   char errbuf[400];
-  _dl_fatal_printf ("\
+  _dl_error_printf ("\
 Inconsistency detected by ld.so: %s: %u: %s%sUnexpected error: %s.\n",
 		    file, line, function ?: "", function ? ": " : "",
 		    __strerror_r (errnum, errbuf, sizeof errbuf));
-
+  rtld_abort ();
 }
 # ifndef NO_RTLD_HIDDEN
 rtld_hidden_weak (__assert_perror_fail)
diff --git a/elf/rtld.c b/elf/rtld.c
index 783db6eb5c..b78757bd11 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -451,7 +451,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
 {
   ElfW(Addr) start_addr;
 
-  __rtld_malloc_init_stubs ();
+  __rtld_init_stubs ();
 
   /* Do not use an initializer for these members because it would
      interfere with __rtld_static_init.  */
@@ -2342,7 +2342,7 @@ dl_main (const ElfW(Phdr) *phdr,
       rtld_timer_accum (&relocate_time, start);
 
       __rtld_mutex_init ();
-      __rtld_malloc_init_real (main_map);
+      __rtld_init_real (main_map);
 
       /* Update copy-relocated _r_debug if necessary.  */
       _dl_debug_post_relocate (main_map);
diff --git a/include/assert.h b/include/assert.h
index c812808f9b..4c9ea5cebb 100644
--- a/include/assert.h
+++ b/include/assert.h
@@ -1,6 +1,8 @@
 #include <assert/assert.h>
 
 #ifndef _ISOMAC
+# include <rtld-assert.h>
+
 /* This prints an "Assertion failed" message and aborts.
    In installed assert.h this is only conditionally declared,
    so it has to be repeated here.  */
diff --git a/include/rtld-assert.h b/include/rtld-assert.h
new file mode 100644
index 0000000000..c171c5ade6
--- /dev/null
+++ b/include/rtld-assert.h
@@ -0,0 +1,55 @@
+/* Redirection of assert inside the dynamic linker.
+   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 _RTLD_ASSERT_H
+#define _RTLD_ASSERT_H
+
+#if IS_IN (rtld)
+
+extern void (*__rtld_assert_fail)(const char *, const char *, unsigned int,
+				  const char *) attribute_hidden;
+extern void (*__rtld_assert_perror_fail)(int, const char *, unsigned int,
+					 const char *) attribute_hidden;
+extern void (*__rtld_libc_fatal)(const char *) attribute_hidden;
+
+__extern_inline _Noreturn void
+__assert_fail (const char *assertion, const char *file, unsigned int line,
+	       const char *function)
+{
+  __rtld_assert_fail (assertion, file, line, function);
+  __builtin_unreachable ();
+}
+
+__extern_inline _Noreturn void
+__assert_perror_fail (int errnum, const char *file, unsigned int line,
+		      const char *function)
+{
+  __rtld_assert_perror_fail (errnum, file, line, function);
+  __builtin_unreachable ();
+}
+
+__extern_inline _Noreturn void
+__libc_fatal (const char *message)
+{
+  __rtld_libc_fatal (message);
+  __builtin_unreachable ();
+}
+
+#endif
+
+#endif
diff --git a/include/rtld-malloc.h b/include/rtld-malloc.h
index 8c7cea4060..8f5f40e261 100644
--- a/include/rtld-malloc.h
+++ b/include/rtld-malloc.h
@@ -62,20 +62,6 @@ realloc (void *ptr, size_t size)
   return __rtld_realloc (ptr, size);
 }
 
-/* Called after the first self-relocation to activate the minimal malloc
-   implementation.  */
-void __rtld_malloc_init_stubs (void) attribute_hidden;
-
-/* Return false if the active malloc is the ld.so minimal malloc, true
-   if it is the full implementation from libc.so.  */
-_Bool __rtld_malloc_is_complete (void) attribute_hidden;
-
-/* Called shortly before the final self-relocation (when RELRO
-   variables are still writable) to activate the real malloc
-   implementation.  MAIN_MAP is the link map of the executable.  */
-struct link_map;
-void __rtld_malloc_init_real (struct link_map *main_map) attribute_hidden;
-
 #else /* !IS_IN (rtld) */
 
 /* This allows static/non-rtld builds to get a pointer to the
diff --git a/include/stdio.h b/include/stdio.h
index 88166993dd..c6b2a26ec1 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -169,6 +169,8 @@ extern int __gen_tempname (char *__tmpl, int __suffixlen, int __flags,
 #  define __GT_DIR	1	/* create a directory */
 #  define __GT_NOCREATE	2	/* just find a name not currently in use */
 
+/* Enable rtld __libc_fatal minimal implementation.  */
+#  include <rtld-assert.h>
 /* Print out MESSAGE (which should end with a newline) on the error output
    and abort.  */
 extern void __libc_fatal (const char *__message)
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 15c4659853..6ba9adf36b 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1436,6 +1436,22 @@ _dl_audit_objclose (struct link_map *l)
 }
 #endif /* !SHARED */
 
+#if IS_IN (rtld)
+/* Called after the first self-relocation to activate the minimal malloc
+   implementation.  */
+void __rtld_init_stubs (void) attribute_hidden;
+
+/* Return false if the active malloc is the ld.so minimal malloc, true
+   if it is the full implementation from libc.so.  */
+_Bool __rtld_malloc_is_complete (void) attribute_hidden;
+
+/* Called shortly before the final self-relocation (when RELRO
+   variables are still writable) to activate the real malloc
+   implementation.  MAIN_MAP is the link map of the executable.  */
+struct link_map;
+void __rtld_init_real (struct link_map *main_map) attribute_hidden;
+#endif
+
 #if __PTHREAD_NPTL && defined SHARED
 /* Recursive locking implementation for use within the dynamic loader.
    Used to define the __rtld_lock_lock_recursive and
diff --git a/sysdeps/mach/hurd/Makefile b/sysdeps/mach/hurd/Makefile
index 9c9eb86945..0c2d853a1d 100644
--- a/sysdeps/mach/hurd/Makefile
+++ b/sysdeps/mach/hurd/Makefile
@@ -334,3 +334,9 @@ endif
 ifeq ($(subdir),io)
 tests-unsupported += test-lfs
 endif
+
+ifeq ($(subdir),signal)
+sysdep_routines += \
+  raise_direct \
+  # sysdep_routines
+endif
diff --git a/sysdeps/mach/hurd/raise_direct.c b/sysdeps/mach/hurd/raise_direct.c
new file mode 100644
index 0000000000..fd693a6759
--- /dev/null
+++ b/sysdeps/mach/hurd/raise_direct.c
@@ -0,0 +1,29 @@
+/* Internal function to send a signal to itself.  Hurd 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/>.  */
+
+#include <hurd.h>
+#include <signal.h>
+#include <unistd.h>
+
+int
+__raise_direct (int sig)
+{
+  /* TODO: proper implement it to work correctly on dynamic linker
+     startup.  */
+  return 0;
+}
-- 
2.43.0



More information about the Libc-alpha mailing list