This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[PATCH roland/ehdr_start] Use __ehdr_start, when available, for rtld to get its own headers.
- From: Roland McGrath <roland at hack dot frob dot com>
- To: "GNU C. Library" <libc-alpha at sourceware dot org>
- Date: Tue, 11 Mar 2014 15:29:33 -0700 (PDT)
- Subject: [PATCH roland/ehdr_start] Use __ehdr_start, when available, for rtld to get its own headers.
- Authentication-results: sourceware.org; auth=none
My motivation for this change is my NaCl port, where the old code's
assumption about ELF file layout does not hold. But in fact this is
not only cleaner but results in marginally better code. (To compensate
I've added a couple of asserts. ;-)
Tested on x86_64-linux-gnu:
* with binutils-2.22 that the configure check fails and nothing changes
* with binutils trunk (2.23 or 2.24 would do as well) that the configure
check passes and the new code works (no 'make check' regressions)
I'll commit this in a few days absent objections.
Thanks,
Roland
* configure.ac (HAVE_EHDR_START): New check.
* configure: Regenerated.
* config.h.in (HAVE_EHDR_START): New #undef.
* elf/rtld.c (dl_main) [HAVE_EHDR_START]: Use __ehdr_start rather than
assuming the lowest-addressed segment maps the start of the file.
--- a/config.h.in
+++ b/config.h.in
@@ -195,6 +195,9 @@
/* Define if linux/fanotify.h is available. */
#undef HAVE_LINUX_FANOTIFY_H
+/* Define if the linker defines __ehdr_start. */
+#undef HAVE_EHDR_START
+
/*
*/
--- a/configure
+++ b/configure
@@ -7268,6 +7268,44 @@ if test $libc_cv_predef_stack_protector = yes; then
fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the linker provides __ehdr_start" >&5
+$as_echo_n "checking whether the linker provides __ehdr_start... " >&6; }
+if ${libc_cv_ehdr_start+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+old_CFLAGS="$CFLAGS"
+old_LDFLAGS="$LDFLAGS"
+old_LIBS="$LIBS"
+CFLAGS="$CFLAGS -fPIC"
+LDFLAGS="$LDFLAGS -nostdlib -nostartfiles -shared"
+LIBS=
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+extern const char __ehdr_start __attribute__ ((visibility ("hidden")));
+const char *ehdr (void) { return &__ehdr_start; }
+
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ libc_cv_ehdr_start=yes
+else
+ libc_cv_ehdr_start=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+CFLAGS="$old_CFLAGS"
+LDFLAGS="$old_LDFLAGS"
+LIBS="$old_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_ehdr_start" >&5
+$as_echo "$libc_cv_ehdr_start" >&6; }
+if test $libc_cv_ehdr_start = yes; then
+ $as_echo "#define HAVE_EHDR_START 1" >>confdefs.h
+
+fi
+
### End of automated tests.
### Now run sysdeps configure fragments.
--- a/configure.ac
+++ b/configure.ac
@@ -2069,6 +2069,27 @@ if test $libc_cv_predef_stack_protector = yes; then
fi
AC_SUBST(libc_extra_cflags)
+AC_CACHE_CHECK([whether the linker provides __ehdr_start],
+ libc_cv_ehdr_start, [
+old_CFLAGS="$CFLAGS"
+old_LDFLAGS="$LDFLAGS"
+old_LIBS="$LIBS"
+CFLAGS="$CFLAGS -fPIC"
+LDFLAGS="$LDFLAGS -nostdlib -nostartfiles -shared"
+LIBS=
+AC_LINK_IFELSE([AC_LANG_SOURCE([
+extern const char __ehdr_start __attribute__ ((visibility ("hidden")));
+const char *ehdr (void) { return &__ehdr_start; }
+])],
+ [libc_cv_ehdr_start=yes], [libc_cv_ehdr_start=no])
+CFLAGS="$old_CFLAGS"
+LDFLAGS="$old_LDFLAGS"
+LIBS="$old_LIBS"
+])
+if test $libc_cv_ehdr_start = yes; then
+ AC_DEFINE([HAVE_EHDR_START])
+fi
+
### End of automated tests.
### Now run sysdeps configure fragments.
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1370,10 +1370,25 @@ of this helper program; chances are you did not intend to run this program.\n\
GLRO(dl_use_load_bias) = main_map->l_addr == 0 ? -1 : 0;
/* Set up the program header information for the dynamic linker
- itself. It is needed in the dl_iterate_phdr() callbacks. */
- ElfW(Ehdr) *rtld_ehdr = (ElfW(Ehdr) *) GL(dl_rtld_map).l_map_start;
- ElfW(Phdr) *rtld_phdr = (ElfW(Phdr) *) (GL(dl_rtld_map).l_map_start
- + rtld_ehdr->e_phoff);
+ itself. It is needed in the dl_iterate_phdr callbacks. */
+ const ElfW(Ehdr) *rtld_ehdr;
+
+ /* Starting from binutils-2.23, the linker will define the magic symbol
+ __ehdr_start to point to our own ELF header if it is visible in a
+ segment that also includes the phdrs. If that's not available, we use
+ the old method that assumes the beginning of the file is part of the
+ lowest-addressed PT_LOAD segment. */
+#ifdef HAVE_EHDR_START
+ extern const ElfW(Ehdr) __ehdr_start __attribute__ ((visibility ("hidden")));
+ rtld_ehdr = &__ehdr_start;
+#else
+ rtld_ehdr = (void *) GL(dl_rtld_map).l_map_start;
+#endif
+ assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr);
+ assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr)));
+
+ const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff;
+
GL(dl_rtld_map).l_phdr = rtld_phdr;
GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum;