[3/7] Add GLIBC_ABI_DT_RELR for DT_RELR support

Message ID 20220203180948.2744-4-hjl.tools@gmail.com
State Superseded
Headers
Series Support DT_RELR relative relocation format |

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent

Commit Message

H.J. Lu Feb. 3, 2022, 6:09 p.m. UTC
  The EI_ABIVERSION field of the ELF header in executables and shared
libraries can be bumped to indicate the minimum ABI requirement on the
dynamic linker.  However, EI_ABIVERSION in executables isn't checked by
the Linux kernel ELF loader nor the existing dynamic linker.  Executables
will crash mysteriously if the dynamic linker doesn't support the ABI
features required by the EI_ABIVERSION field.  The dynamic linker should
be changed to check EI_ABIVERSION in executables.

Add a glibc version, GLIBC_ABI_DT_RELR, to indicate DT_RELR support so
that the existing dynamic linkers will issue an error on executables
with GLIBC_ABI_DT_RELR depdendency.

Support __placeholder_only_for_empty_version_map as the placeholder symbol
used only for empty version map to generate GLIBC_ABI_DT_RELR without any
symbols.
---
 elf/Makefile             | 18 ++++++++++++++++--
 elf/Versions             |  7 +++++++
 elf/libc-abi-version.exp |  1 +
 scripts/abilist.awk      |  2 ++
 scripts/versions.awk     |  7 ++++++-
 5 files changed, 32 insertions(+), 3 deletions(-)
 create mode 100644 elf/libc-abi-version.exp
  

Comments

Joseph Myers Feb. 4, 2022, 7:53 p.m. UTC | #1
On Thu, 3 Feb 2022, H.J. Lu via Libc-alpha wrote:

> +%if HAVE_DT_RELR
> +  GLIBC_ABI_DT_RELR {
> +    # This symbol is used only for empty version map and will be removed
> +    # by scripts/versions.awk.
> +    __placeholder_only_for_empty_version_map;
> +  }

My understanding is that HAVE_DT_RELR describes a property of the linker 
used to build glibc, not a property of what features glibc itself 
supports.

The symbol versions provided by glibc, and whether glibc supports binaries 
using RELR relocations, should be independent of whether the linker 
version used to build glibc supports such relocations.  A given glibc 
version should either always support RELR relocations (for a given glibc 
ABI) or never support them (for that ABI), independent of linker features, 
and likewise the symbol versions provided by glibc should be independent 
of linker features.
  
H.J. Lu Feb. 4, 2022, 8:04 p.m. UTC | #2
On Fri, Feb 4, 2022 at 11:53 AM Joseph Myers <joseph@codesourcery.com> wrote:
>
> On Thu, 3 Feb 2022, H.J. Lu via Libc-alpha wrote:
>
> > +%if HAVE_DT_RELR
> > +  GLIBC_ABI_DT_RELR {
> > +    # This symbol is used only for empty version map and will be removed
> > +    # by scripts/versions.awk.
> > +    __placeholder_only_for_empty_version_map;
> > +  }
>
> My understanding is that HAVE_DT_RELR describes a property of the linker
> used to build glibc, not a property of what features glibc itself
> supports.
>
> The symbol versions provided by glibc, and whether glibc supports binaries
> using RELR relocations, should be independent of whether the linker
> version used to build glibc supports such relocations.  A given glibc
> version should either always support RELR relocations (for a given glibc
> ABI) or never support them (for that ABI), independent of linker features,
> and likewise the symbol versions provided by glibc should be independent
> of linker features.
>

In my cover letter, I said

Binutils 2.38 supports DT_RELR on x86 with the -z report-relative-reloc
option.  When DT_RELR is enabled, ld adds a GLIBC_ABI_DT_RELR symbol
version dependency on libc.so to outputs.  The DT_RELR support is enabled
in ld.so only if the linker supports -z report-relative-reloc option.

and the first patch has

 # define ELF_DYNAMIC_RELOCATE(map, scope, lazy, consider_profile, skip_ifunc) \
   do {       \
     int edr_lazy = elf_machine_runtime_setup ((map), (scope), (lazy),       \
        (consider_profile));       \
     ELF_DYNAMIC_DO_REL ((map), (scope), edr_lazy, skip_ifunc);       \
     ELF_DYNAMIC_DO_RELA ((map), (scope), edr_lazy, skip_ifunc);
      \
+    if (HAVE_DT_RELR && ((map) != &GL(dl_rtld_map) || DO_RTLD_BOOTSTRAP))     \
+      ELF_DYNAMIC_DO_RELR (map);       \
   } while (0)

If HAVE_DT_RELR is 0, DT_RELR is disabled.
  
Joseph Myers Feb. 4, 2022, 8:10 p.m. UTC | #3
On Fri, 4 Feb 2022, H.J. Lu via Libc-alpha wrote:

> In my cover letter, I said
> 
> Binutils 2.38 supports DT_RELR on x86 with the -z report-relative-reloc
> option.  When DT_RELR is enabled, ld adds a GLIBC_ABI_DT_RELR symbol
> version dependency on libc.so to outputs.  The DT_RELR support is enabled
> in ld.so only if the linker supports -z report-relative-reloc option.
> 
> and the first patch has
> 
>  # define ELF_DYNAMIC_RELOCATE(map, scope, lazy, consider_profile, skip_ifunc) \
>    do {       \
>      int edr_lazy = elf_machine_runtime_setup ((map), (scope), (lazy),       \
>         (consider_profile));       \
>      ELF_DYNAMIC_DO_REL ((map), (scope), edr_lazy, skip_ifunc);       \
>      ELF_DYNAMIC_DO_RELA ((map), (scope), edr_lazy, skip_ifunc);
>       \
> +    if (HAVE_DT_RELR && ((map) != &GL(dl_rtld_map) || DO_RTLD_BOOTSTRAP))     \
> +      ELF_DYNAMIC_DO_RELR (map);       \
>    } while (0)
> 
> If HAVE_DT_RELR is 0, DT_RELR is disabled.

It's important that glibc 2.36 supports the same executables and shared 
libraries on a given platform, independent of the binutils version used to 
build glibc.  If any build of glibc 2.36 for some platform supports RELR 
relocations, all builds of it for that platform must support such 
relocations.

That means that configure tests for linker support can *only* affect 
whether glibc is built to *use* such relocations itself - not whether it 
supports loading executables and shared libraries that use them.

(You could also increase the minimum linker version for building glibc for 
a given platform, but the RELR support is too recent for it to be a good 
idea to make the minimum version new enough to include RELR support.)
  
H.J. Lu Feb. 4, 2022, 8:40 p.m. UTC | #4
On Fri, Feb 4, 2022 at 12:11 PM Joseph Myers <joseph@codesourcery.com> wrote:
>
> On Fri, 4 Feb 2022, H.J. Lu via Libc-alpha wrote:
>
> > In my cover letter, I said
> >
> > Binutils 2.38 supports DT_RELR on x86 with the -z report-relative-reloc
> > option.  When DT_RELR is enabled, ld adds a GLIBC_ABI_DT_RELR symbol
> > version dependency on libc.so to outputs.  The DT_RELR support is enabled
> > in ld.so only if the linker supports -z report-relative-reloc option.
> >
> > and the first patch has
> >
> >  # define ELF_DYNAMIC_RELOCATE(map, scope, lazy, consider_profile, skip_ifunc) \
> >    do {       \
> >      int edr_lazy = elf_machine_runtime_setup ((map), (scope), (lazy),       \
> >         (consider_profile));       \
> >      ELF_DYNAMIC_DO_REL ((map), (scope), edr_lazy, skip_ifunc);       \
> >      ELF_DYNAMIC_DO_RELA ((map), (scope), edr_lazy, skip_ifunc);
> >       \
> > +    if (HAVE_DT_RELR && ((map) != &GL(dl_rtld_map) || DO_RTLD_BOOTSTRAP))     \
> > +      ELF_DYNAMIC_DO_RELR (map);       \
> >    } while (0)
> >
> > If HAVE_DT_RELR is 0, DT_RELR is disabled.
>
> It's important that glibc 2.36 supports the same executables and shared
> libraries on a given platform, independent of the binutils version used to
> build glibc.  If any build of glibc 2.36 for some platform supports RELR
> relocations, all builds of it for that platform must support such
> relocations.
>
> That means that configure tests for linker support can *only* affect
> whether glibc is built to *use* such relocations itself - not whether it
> supports loading executables and shared libraries that use them.
>
> (You could also increase the minimum linker version for building glibc for
> a given platform, but the RELR support is too recent for it to be a good
> idea to make the minimum version new enough to include RELR support.)

Good point.  How about "enable DT_RELR only if SUPPORT_DT_RELR is
defined?  Currently, only x86 defines SUPPORT_DT_RELR.
  
Joseph Myers Feb. 4, 2022, 9:01 p.m. UTC | #5
On Fri, 4 Feb 2022, H.J. Lu via Libc-alpha wrote:

> Good point.  How about "enable DT_RELR only if SUPPORT_DT_RELR is
> defined?  Currently, only x86 defines SUPPORT_DT_RELR.

My preference would be:

1. Support user executables and shared libraries with RELR relocations 
across all platforms, unconditionally.

2. Build glibc to use RELR relocations in its own executables and shared 
libraries based on an architecture-independent configure test for whether 
linker support is present.

And avoid any architecture-specific conditional relating to RELR support 
in glibc completely.

*If* it turns out to be hard to have a fully reliable 
architecture-independent configure test for linker support, and the 
architecture-independent test reports linker support to be present (in an 
actual binutils release, not just the development mainline) for an 
architecture where that support is in fact buggy and incomplete, then we 
might consider adding an architecture-specific test on that architecture 
to disable building glibc to use RELR relocations with the buggy linker 
version.  That is, architecture-specific tests would only be to disable 
building glibc to use RELR relocations, not to enable it, and only when 
the bugs in linker support on that architecture can't readily be detected 
by an architecture-independent test.  The default for all existing and 
future architectures would be to follow the results of the 
architecture-independent configure test.
  
H.J. Lu Feb. 4, 2022, 9:08 p.m. UTC | #6
On Fri, Feb 4, 2022 at 1:01 PM Joseph Myers <joseph@codesourcery.com> wrote:
>
> On Fri, 4 Feb 2022, H.J. Lu via Libc-alpha wrote:
>
> > Good point.  How about "enable DT_RELR only if SUPPORT_DT_RELR is
> > defined?  Currently, only x86 defines SUPPORT_DT_RELR.
>
> My preference would be:
>
> 1. Support user executables and shared libraries with RELR relocations
> across all platforms, unconditionally.

RELR is kind of like static PIE.  Not all architectures support it.
RELR should be enabled only if there is a linker which supports
-z pack-relative-relocs.  That is the main reason why RELR is
enabled in glibc 2.35.

> 2. Build glibc to use RELR relocations in its own executables and shared
> libraries based on an architecture-independent configure test for whether
> linker support is present.

This is implemented in

commit 486531c29b0fa48287b29ea20e805bee1ec19a67
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Sat Jan 22 05:44:45 2022 -0800

    Add --disable-default-dt-relr

which is similar to --disable-default-pie.

> And avoid any architecture-specific conditional relating to RELR support
> in glibc completely.
>
> *If* it turns out to be hard to have a fully reliable
> architecture-independent configure test for linker support, and the
> architecture-independent test reports linker support to be present (in an
> actual binutils release, not just the development mainline) for an
> architecture where that support is in fact buggy and incomplete, then we
> might consider adding an architecture-specific test on that architecture
> to disable building glibc to use RELR relocations with the buggy linker
> version.  That is, architecture-specific tests would only be to disable
> building glibc to use RELR relocations, not to enable it, and only when
> the bugs in linker support on that architecture can't readily be detected
> by an architecture-independent test.  The default for all existing and
> future architectures would be to follow the results of the
> architecture-independent configure test.
  
Joseph Myers Feb. 4, 2022, 11:58 p.m. UTC | #7
On Fri, 4 Feb 2022, H.J. Lu via Libc-alpha wrote:

> On Fri, Feb 4, 2022 at 1:01 PM Joseph Myers <joseph@codesourcery.com> wrote:
> >
> > On Fri, 4 Feb 2022, H.J. Lu via Libc-alpha wrote:
> >
> > > Good point.  How about "enable DT_RELR only if SUPPORT_DT_RELR is
> > > defined?  Currently, only x86 defines SUPPORT_DT_RELR.
> >
> > My preference would be:
> >
> > 1. Support user executables and shared libraries with RELR relocations
> > across all platforms, unconditionally.
> 
> RELR is kind of like static PIE.  Not all architectures support it.

My understanding is that analogy only applies to the static linker, not to 
glibc itself - that the static linker needs architecture-specific code, 
but glibc doesn't (as evidenced by the lack of any architecture-specific 
non-configure changes in this patch series).

> RELR should be enabled only if there is a linker which supports
> -z pack-relative-relocs.

(a) That should only apply to "enabled" in the sense of "glibc itself uses 
RELR relocations", not "glibc supports loading executables and shared 
libraries with such relocations".

(b) The configure test for that should be architecture-independent, with 
no architecture-specific configure changes needed at all.
  
H.J. Lu Feb. 5, 2022, 5:24 p.m. UTC | #8
On Fri, Feb 4, 2022 at 3:58 PM Joseph Myers <joseph@codesourcery.com> wrote:
>
> On Fri, 4 Feb 2022, H.J. Lu via Libc-alpha wrote:
>
> > On Fri, Feb 4, 2022 at 1:01 PM Joseph Myers <joseph@codesourcery.com> wrote:
> > >
> > > On Fri, 4 Feb 2022, H.J. Lu via Libc-alpha wrote:
> > >
> > > > Good point.  How about "enable DT_RELR only if SUPPORT_DT_RELR is
> > > > defined?  Currently, only x86 defines SUPPORT_DT_RELR.
> > >
> > > My preference would be:
> > >
> > > 1. Support user executables and shared libraries with RELR relocations
> > > across all platforms, unconditionally.
> >
> > RELR is kind of like static PIE.  Not all architectures support it.
>
> My understanding is that analogy only applies to the static linker, not to
> glibc itself - that the static linker needs architecture-specific code,
> but glibc doesn't (as evidenced by the lack of any architecture-specific
> non-configure changes in this patch series).
>
> > RELR should be enabled only if there is a linker which supports
> > -z pack-relative-relocs.
>
> (a) That should only apply to "enabled" in the sense of "glibc itself uses
> RELR relocations", not "glibc supports loading executables and shared
> libraries with such relocations".
>
> (b) The configure test for that should be architecture-independent, with
> no architecture-specific configure changes needed at all.
>

Fixed in the v2 patch.
  

Patch

diff --git a/elf/Makefile b/elf/Makefile
index d1094f40ce..c697e7b7ee 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -48,6 +48,10 @@  routines = \
   rtld_static_init \
   # routines
 
+ifeq ($(have-dt-relr),yes)
+check-abi-version-libc = $(objpfx)check-abi-version-libc.out
+endif
+
 # The core dynamic linking functions are in libc for the static and
 # profiled libraries.
 dl-routines = \
@@ -1106,8 +1110,8 @@  $(eval $(call include_dsosort_tests,dso-sort-tests-1.def))
 $(eval $(call include_dsosort_tests,dso-sort-tests-2.def))
 endif
 
-check-abi: $(objpfx)check-abi-ld.out
-tests-special += $(objpfx)check-abi-ld.out
+check-abi: $(objpfx)check-abi-ld.out $(check-abi-version-libc)
+tests-special += $(objpfx)check-abi-ld.out $(check-abi-version-libc)
 update-abi: update-abi-ld
 update-all-abi: update-all-abi-ld
 
@@ -2747,3 +2751,13 @@  $(objpfx)check-tst-relr-pie.out: $(objpfx)tst-relr-pie
 		| sed -ne '/required from libc.so/,$$ p' \
 		| grep GLIBC_ABI_DT_RELR > $@; \
 	$(evaluate-test)
+
+$(objpfx)check-abi-version-libc.out: libc-abi-version.exp \
+  $(objpfx)libc.symlist-abi-version
+	cmp $^ > $@; \
+	$(evaluate-test)
+
+$(objpfx)libc.symlist-abi-version: $(common-objpfx)libc.so
+	LC_ALL=C $(NM) -D $< | grep " GLIBC_ABI_" \
+		| sed "s/^0\+/00000000/" > $@T
+	mv -f $@T $@
diff --git a/elf/Versions b/elf/Versions
index 8bed855d8c..a84927c95f 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -23,6 +23,13 @@  libc {
   GLIBC_2.35 {
     _dl_find_object;
   }
+%if HAVE_DT_RELR
+  GLIBC_ABI_DT_RELR {
+    # This symbol is used only for empty version map and will be removed
+    # by scripts/versions.awk.
+    __placeholder_only_for_empty_version_map;
+  }
+%endif
   GLIBC_PRIVATE {
     # functions used in other libraries
     __libc_early_init;
diff --git a/elf/libc-abi-version.exp b/elf/libc-abi-version.exp
new file mode 100644
index 0000000000..455088dc6b
--- /dev/null
+++ b/elf/libc-abi-version.exp
@@ -0,0 +1 @@ 
+00000000 A GLIBC_ABI_DT_RELR
diff --git a/scripts/abilist.awk b/scripts/abilist.awk
index 24a34ccbed..6cc7af6ac8 100644
--- a/scripts/abilist.awk
+++ b/scripts/abilist.awk
@@ -55,6 +55,8 @@  $2 == "g" || $2 == "w" && (NF == 7 || NF == 8) {
   # caused STV_HIDDEN symbols to appear in .dynsym, though that is useless.
   if (NF > 7 && $7 == ".hidden") next;
 
+  if (version ~ /^GLIBC_ABI_/ && !include_abi_version) next;
+
   if (version == "GLIBC_PRIVATE" && !include_private) next;
 
   desc = "";
diff --git a/scripts/versions.awk b/scripts/versions.awk
index 357ad1355e..d70b07bd1a 100644
--- a/scripts/versions.awk
+++ b/scripts/versions.awk
@@ -185,8 +185,13 @@  END {
 	closeversion(oldver, veryoldver);
 	veryoldver = oldver;
       }
-      printf("%s {\n  global:\n", $2) > outfile;
       oldver = $2;
+      # Skip the placeholder symbol used only for empty version map.
+      if ($3 == "__placeholder_only_for_empty_version_map;") {
+	printf("%s {\n", $2) > outfile;
+	continue;
+      }
+      printf("%s {\n  global:\n", $2) > outfile;
     }
     printf("   ") > outfile;
     for (n = 3; n <= NF; ++n) {