]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
elf: Support DT_RELR relative relocation format
authorFangrui Song <maskray@google.com>
Mon, 25 Apr 2022 23:50:00 +0000 (16:50 -0700)
committerFangrui Song <maskray@google.com>
Mon, 25 Apr 2022 23:50:00 +0000 (16:50 -0700)
Adapted from
https://sourceware.org/pipermail/libc-alpha/2022-April/138085.html
([PATCH v11 0/7] Support DT_RELR relative relocation format),
which is expected to be included in glibc 2.36.

glibc 2.35 has a fair amount of rtld changes to avoid nested functions
(https://sourceware.org/PR27220). This patch is carefully crafted to
make the minimal changes.

Notebly, this commit

* works around b/208156916 by not bumping DT_NUM. DT_RELR and DT_RELRSZ
  take the l_info slots at DT_VERSYM+1 and DT_VERSYM+2.
* avoids changes to include/link.h
* removes the time travel compatibility check (error if DT_RELR is used
  without GLIBC_ABI_DT_RELR version need). This needs link.h change and
  the detected case cannot happen if we correctly use
  -Wl,-z,pack-relative-relocs.

19 files changed:
configure
configure.ac
elf/Makefile
elf/Versions
elf/dynamic-link.h
elf/elf.h
elf/get-dynamic-info.h
elf/tst-relr-mod2.c [new file with mode: 0644]
elf/tst-relr-mod3a.c [new file with mode: 0644]
elf/tst-relr-mod3b.c [new file with mode: 0644]
elf/tst-relr-mod4a.c [new file with mode: 0644]
elf/tst-relr-mod4b.c [new file with mode: 0644]
elf/tst-relr-mod4b.map [new file with mode: 0644]
elf/tst-relr-pie.c [new file with mode: 0644]
elf/tst-relr.c [new file with mode: 0644]
elf/tst-relr2.c [new file with mode: 0644]
elf/tst-relr3.c [new file with mode: 0644]
elf/tst-relr4.c [new file with mode: 0644]
scripts/versions.awk

index 95887b707888ed4b744096808c7e4cb5ce1996d3..447318e678a29576275db98781d4754b54fe3844 100755 (executable)
--- a/configure
+++ b/configure
@@ -730,7 +730,6 @@ infodir
 docdir
 oldincludedir
 includedir
-runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -844,7 +843,6 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
-runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1097,15 +1095,6 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
-  -runstatedir | --runstatedir | --runstatedi | --runstated \
-  | --runstate | --runstat | --runsta | --runst | --runs \
-  | --run | --ru | --r)
-    ac_prev=runstatedir ;;
-  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
-  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
-  | --run=* | --ru=* | --r=*)
-    runstatedir=$ac_optarg ;;
-
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1243,7 +1232,7 @@ fi
 for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
                datadir sysconfdir sharedstatedir localstatedir includedir \
                oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-               libdir localedir mandir runstatedir
+               libdir localedir mandir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1396,7 +1385,6 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
-  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -5879,22 +5867,25 @@ fi
 $as_echo_n "checking for linker that supports -z execstack... " >&6; }
 libc_linker_feature=no
 if test x"$gnu_ld" = x"yes"; then
-  cat > conftest.c <<EOF
+  libc_linker_check=`$LD -v --help 2>/dev/null | grep "\-z execstack"`
+  if test -n "$libc_linker_check"; then
+    cat > conftest.c <<EOF
 int _start (void) { return 42; }
 EOF
-  if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp
-                   -Wl,-z,execstack -nostdlib -nostartfiles
-                   -fPIC -shared -o conftest.so conftest.c
-                   1>&5'
+    if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp
+                               -Wl,-z,execstack -nostdlib -nostartfiles
+                               -fPIC -shared -o conftest.so conftest.c
+                               1>&5'
   { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
   (eval $ac_try) 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; }
-  then
-    libc_linker_feature=yes
+    then
+      libc_linker_feature=yes
+    fi
+    rm -f conftest*
   fi
-  rm -f conftest*
 fi
 if test $libc_linker_feature = yes; then
   libc_cv_z_execstack=yes
@@ -5908,12 +5899,46 @@ $as_echo "$libc_linker_feature" >&6; }
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker that supports -z start-stop-gc" >&5
 $as_echo_n "checking for linker that supports -z start-stop-gc... " >&6; }
 libc_linker_feature=no
+if test x"$gnu_ld" = x"yes"; then
+  libc_linker_check=`$LD -v --help 2>/dev/null | grep "\-z start-stop-gc"`
+  if test -n "$libc_linker_check"; then
+    cat > conftest.c <<EOF
+int _start (void) { return 42; }
+EOF
+    if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp
+                               -Wl,-z,start-stop-gc -nostdlib -nostartfiles
+                               -fPIC -shared -o conftest.so conftest.c
+                               1>&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+      libc_linker_feature=yes
+    fi
+    rm -f conftest*
+  fi
+fi
+if test $libc_linker_feature = yes; then
+  libc_cv_z_start_stop_gc=yes
+else
+  libc_cv_z_start_stop_gc=no
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_linker_feature" >&5
+$as_echo "$libc_linker_feature" >&6; }
+config_vars="$config_vars
+have-z-start-stop-gc = $libc_cv_z_start_stop_gc"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker that supports -z pack-relative-relocs" >&5
+$as_echo_n "checking for linker that supports -z pack-relative-relocs... " >&6; }
+libc_linker_feature=no
 if test x"$gnu_ld" = x"yes"; then
   cat > conftest.c <<EOF
 int _start (void) { return 42; }
 EOF
   if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp
-                   -Wl,-z,start-stop-gc -nostdlib -nostartfiles
+                   -Wl,-z,pack-relative-relocs -nostdlib -nostartfiles
                    -fPIC -shared -o conftest.so conftest.c
                    1>&5'
   { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
@@ -5927,14 +5952,14 @@ EOF
   rm -f conftest*
 fi
 if test $libc_linker_feature = yes; then
-  libc_cv_z_start_stop_gc=yes
+  libc_cv_dt_relr=yes
 else
-  libc_cv_z_start_stop_gc=no
+  libc_cv_dt_relr=no
 fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_linker_feature" >&5
 $as_echo "$libc_linker_feature" >&6; }
 config_vars="$config_vars
-have-z-start-stop-gc = $libc_cv_z_start_stop_gc"
+have-dt-relr = $libc_cv_dt_relr"
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker that supports --no-dynamic-linker" >&5
 $as_echo_n "checking for linker that supports --no-dynamic-linker... " >&6; }
index b3505fce4019cb85e01a6104265a891ea807a94e..b1a1530dba8940cd63b72d517562b170e84af4b1 100644 (file)
@@ -1354,6 +1354,11 @@ LIBC_LINKER_FEATURE([-z start-stop-gc], [-Wl,-z,start-stop-gc],
                    [libc_cv_z_start_stop_gc=yes], [libc_cv_z_start_stop_gc=no])
 LIBC_CONFIG_VAR([have-z-start-stop-gc], [$libc_cv_z_start_stop_gc])
 
+LIBC_LINKER_FEATURE([-z pack-relative-relocs],
+                   [-Wl,-z,pack-relative-relocs],
+                   [libc_cv_dt_relr=yes], [libc_cv_dt_relr=no])
+LIBC_CONFIG_VAR([have-dt-relr], [$libc_cv_dt_relr])
+
 LIBC_LINKER_FEATURE([--no-dynamic-linker],
                    [-Wl,--no-dynamic-linker],
                    [libc_cv_no_dynamic_linker=yes],
index 1b9acb5242f5bcf73d9afec3abe7f04bb6deb7af..7b44740999f4faa771029c43e6e1538f1d17bbe8 100644 (file)
@@ -209,6 +209,44 @@ tests-execstack-yes = tst-execstack tst-execstack-needed tst-execstack-prog
 endif
 endif
 endif
+ifeq ($(have-dt-relr),yes)
+tests += \
+  tst-relr \
+  tst-relr2 \
+  tst-relr3 \
+  tst-relr4 \
+# tests
+modules-names-dt-relr = \
+  tst-relr-mod2 \
+  tst-relr-mod3a \
+  tst-relr-mod3b \
+  tst-relr-mod4a \
+  tst-relr-mod4b \
+# modules-names-dt-relr
+modules-names += $(modules-names-dt-relr)
+# These shared libraries have special build rules.
+modules-names-nobuild += $(modules-names-dt-relr)
+ifeq ($(have-fpie),yes)
+tests += \
+  tst-relr-pie \
+# tests
+tests-pie += \
+  tst-relr-pie \
+# tests-pie
+tests-special += \
+  $(objpfx)check-tst-relr-pie.out \
+# tests-special
+endif
+CFLAGS-tst-relr-pie.c += $(pie-ccflag)
+LDFLAGS-tst-relr += -Wl,-z,pack-relative-relocs
+LDFLAGS-tst-relr-pie += -Wl,-z,pack-relative-relocs
+LDFLAGS-tst-relr2 += -Wl,--allow-shlib-undefined
+CFLAGS-tst-relr-mod2.c += $(no-stack-protector)
+CFLAGS-tst-relr-mod3a.c += $(no-stack-protector)
+CFLAGS-tst-relr-mod3b.c += $(no-stack-protector)
+CFLAGS-tst-relr-mod4a.c += $(no-stack-protector)
+CFLAGS-tst-relr-mod4b.c += $(no-stack-protector)
+endif
 ifeq ($(run-built-tests),yes)
 tests-special += $(objpfx)tst-leaks1-mem.out \
                 $(objpfx)tst-leaks1-static-mem.out $(objpfx)noload-mem.out \
@@ -1482,3 +1520,56 @@ $(objpfx)tst-dlopen-offset-comb.so: $(objpfx)tst-dlopen-offset-mod1.so $(objpfx)
        dd if=$(objpfx)tst-dlopen-offset-mod3.so of=$(objpfx)tst-dlopen-offset-comb.so bs=1024 seek=192
 
 $(objpfx)tst-big-note: $(objpfx)tst-big-note-lib.so
+
+$(objpfx)check-tst-relr-pie.out: $(objpfx)tst-relr-pie
+       LC_ALL=C $(OBJDUMP) -p $< \
+               | sed -ne '/required from libc.so/,$$ p' \
+               | grep GLIBC_ABI_DT_RELR > $@; \
+       $(evaluate-test)
+
+# The test checks if a DT_RELR shared library without DT_NEEDED works as
+# intended, so it uses an explicit link rule.
+$(objpfx)tst-relr2: $(objpfx)tst-relr-mod2.so
+$(objpfx)tst-relr-mod2.so: $(objpfx)tst-relr-mod2.os
+       $(LINK.o) -nostdlib -nostartfiles -Wl,-z,pack-relative-relocs \
+       $(LDFLAGS-soname-fname) \
+       -shared -o $@.new $(filter-out $(map-file),$^)
+       $(call after-link,$@.new)
+       mv -f $@.new $@
+
+# The test checks if a DT_RELR shared library without DT_VERNEED works as
+# intended, so it uses an explicit link rule.
+$(objpfx)tst-relr3: $(objpfx)tst-relr-mod3a.so
+$(objpfx)tst-relr-mod3b.so: $(objpfx)tst-relr-mod3b.os
+       $(LINK.o) -nostdlib -nostartfiles -Wl,-z,pack-relative-relocs \
+       $(LDFLAGS-soname-fname) \
+       -shared -o $@.new $(filter-out $(map-file),$^)
+       $(call after-link,$@.new)
+       mv -f $@.new $@
+
+$(objpfx)tst-relr-mod3a.so: $(objpfx)tst-relr-mod3a.os \
+  $(objpfx)tst-relr-mod3b.so
+       $(LINK.o) -nostdlib -nostartfiles -Wl,-z,pack-relative-relocs \
+       $(LDFLAGS-soname-fname) \
+       -shared -o $@.new $(filter-out $(map-file),$^)
+       $(call after-link,$@.new)
+       mv -f $@.new $@
+
+# The test checks if a DT_RELR shared library without libc.so on DT_NEEDED
+# works as intended, so it uses an explicit link rule.
+$(objpfx)tst-relr4: $(objpfx)tst-relr-mod4a.so
+$(objpfx)tst-relr-mod4b.so: $(objpfx)tst-relr-mod4b.os
+       $(LINK.o) -nostdlib -nostartfiles -Wl,-z,pack-relative-relocs \
+       $(LDFLAGS-soname-fname) \
+       -Wl,--version-script=tst-relr-mod4b.map \
+       -shared -o $@.new $(filter-out $(map-file),$^)
+       $(call after-link,$@.new)
+       mv -f $@.new $@
+
+$(objpfx)tst-relr-mod4a.so: $(objpfx)tst-relr-mod4a.os \
+  $(objpfx)tst-relr-mod4b.so
+       $(LINK.o) -nostdlib -nostartfiles -Wl,-z,pack-relative-relocs \
+       $(LDFLAGS-soname-fname) \
+       -shared -o $@.new $(filter-out $(map-file),$^)
+       $(call after-link,$@.new)
+       mv -f $@.new $@
index 05eba2ab58a6da39099eaf437320422278161db2..7fa8f67284b5bb6769ac83659b6f5bc4b6d85d4d 100644 (file)
@@ -20,6 +20,11 @@ libc {
     __register_frame_info_table_bases; _Unwind_Find_FDE;
   }
 %endif
+  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;
+  }
   GLIBC_PRIVATE {
     # functions used in other libraries
     _dl_addr;
index f576d787e3c9bf9bf47684cacaf8577c2cbc47ed..e9d7facad9bda1073c5bb40b951bd3b37a7ef342 100644 (file)
@@ -128,7 +128,9 @@ elf_machine_lazy_rel (struct link_map *map,
             __typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative; int lazy; }  \
       ranges[2] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };                        \
                                                                              \
-    if ((map)->l_info[DT_##RELOC])                                           \
+    /* With DT_RELR, DT_RELA/DT_REL can have zero value.  */                 \
+    if ((map)->l_info[DT_##RELOC] != NULL                                    \
+       && (map)->l_info[DT_##RELOC]->d_un.d_ptr != 0)                        \
       {                                                                              \
        ranges[0].start = D_PTR ((map), l_info[DT_##RELOC]);                  \
        ranges[0].size = (map)->l_info[DT_##RELOC##SZ]->d_un.d_val;           \
@@ -142,6 +144,8 @@ elf_machine_lazy_rel (struct link_map *map,
        ElfW(Addr) start = D_PTR ((map), l_info[DT_JMPREL]);                  \
        ElfW(Addr) size = (map)->l_info[DT_PLTRELSZ]->d_un.d_val;             \
                                                                              \
+       if (ranges[0].start == 0)                                             \
+         ranges[0].start = start;                                            \
        if (ranges[0].start + ranges[0].size == (start + size))               \
          ranges[0].size -= size;                                             \
        if (ELF_DURING_STARTUP                                                \
@@ -253,12 +257,48 @@ elf_machine_lazy_rel (struct link_map *map,
 #  define ELF_DYNAMIC_DO_RELA(map, lazy, skip_ifunc, boot_map) /* Nothing to do.  */
 # endif
 
+/* Google-local: b/208156916.  To not bump DT_NUM, use DT_VERSYM+1 for DT_RELR
+   and DT_VERSYM+2 for DT_RELRSZ.  */
+# define ELF_DYNAMIC_DO_RELR(map)                                            \
+  do {                                                                       \
+    ElfW(Addr) l_addr = (map)->l_addr, *where = 0;                           \
+    const ElfW(Relr) *r, *end;                                               \
+    if (!(map)->l_info[VERSYMIDX (DT_VERSYM + 1)])                           \
+      break;                                                                 \
+    r = (const ElfW(Relr) *)D_PTR((map), l_info[VERSYMIDX (DT_VERSYM + 1)]);  \
+    end = (const ElfW(Relr) *)((const char *)r +                             \
+                               (map)->l_info[VERSYMIDX (DT_VERSYM + 2)]->d_un.d_val); \
+    for (; r < end; r++)                                                     \
+      {                                                                              \
+       ElfW(Relr) entry = *r;                                                \
+       if ((entry & 1) == 0)                                                 \
+         {                                                                   \
+           where = (ElfW(Addr) *)(l_addr + entry);                           \
+           *where++ += l_addr;                                               \
+         }                                                                   \
+       else                                                                  \
+         {                                                                   \
+           for (long i = 0; (entry >>= 1) != 0; i++)                         \
+             if ((entry & 1) != 0)                                           \
+               where[i] += l_addr;                                           \
+           where += CHAR_BIT * sizeof(ElfW(Relr)) - 1;                       \
+         }                                                                   \
+      }                                                                              \
+  } while (0);
+
 /* This can't just be an inline function because GCC is too dumb
    to inline functions containing inlines themselves.  */
+# ifdef RTLD_BOOTSTRAP
+#  define DO_RTLD_BOOTSTRAP 1
+# else
+#  define DO_RTLD_BOOTSTRAP 0
+# endif
 # define ELF_DYNAMIC_RELOCATE(map, lazy, consider_profile, skip_ifunc, boot_map) \
   do {                                                                       \
     int edr_lazy = elf_machine_runtime_setup ((map), (lazy),                 \
                                              (consider_profile));            \
+    if (((map) != &GL(dl_rtld_map) || DO_RTLD_BOOTSTRAP))                    \
+      ELF_DYNAMIC_DO_RELR (map);                                             \
     ELF_DYNAMIC_DO_REL ((map), edr_lazy, skip_ifunc, boot_map);                        \
     ELF_DYNAMIC_DO_RELA ((map), edr_lazy, skip_ifunc, boot_map);                       \
   } while (0)
index 954f3266f711ab83996670ea504a17dcf668e061..35ff17cae297ed2242237aa283012d08fc233fe0 100644 (file)
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -444,8 +444,9 @@ typedef struct
 #define SHT_FINI_ARRAY   15            /* Array of destructors */
 #define SHT_PREINIT_ARRAY 16           /* Array of pre-constructors */
 #define SHT_GROUP        17            /* Section group */
-#define SHT_SYMTAB_SHNDX  18           /* Extended section indeces */
-#define        SHT_NUM           19            /* Number of defined types.  */
+#define SHT_SYMTAB_SHNDX  18           /* Extended section indices */
+#define SHT_RELR         19            /* RELR relative relocations */
+#define        SHT_NUM           20            /* Number of defined types.  */
 #define SHT_LOOS         0x60000000    /* Start OS-specific.  */
 #define SHT_GNU_ATTRIBUTES 0x6ffffff5  /* Object attributes.  */
 #define SHT_GNU_HASH     0x6ffffff6    /* GNU-style hash table.  */
@@ -663,6 +664,11 @@ typedef struct
   Elf64_Sxword r_addend;               /* Addend */
 } Elf64_Rela;
 
+/* RELR relocation table entry */
+
+typedef Elf32_Word     Elf32_Relr;
+typedef Elf64_Xword    Elf64_Relr;
+
 /* How to extract and insert information held in the r_info field.  */
 
 #define ELF32_R_SYM(val)               ((val) >> 8)
@@ -861,6 +867,11 @@ typedef struct
 #define DT_ENCODING    32              /* Start of encoded range */
 #define DT_PREINIT_ARRAY 32            /* Array with addresses of preinit fct*/
 #define DT_PREINIT_ARRAYSZ 33          /* size in bytes of DT_PREINIT_ARRAY */
+#define DT_SYMTAB_SHNDX        34              /* Address of SYMTAB_SHNDX section */
+#define DT_RELRSZ      35              /* Total size of RELR relative relocations */
+#define DT_RELR                36              /* Address of RELR relative relocations */
+#define DT_RELRENT     37              /* Size of one RELR relative relocaction */
+/* Google-local: b/208156916.  Don't bump DT_NUM.  */
 #define        DT_NUM          34              /* Number used */
 #define DT_LOOS                0x6000000d      /* Start of OS-specific */
 #define DT_HIOS                0x6ffff000      /* End of OS-specific */
index f9c5b84d6a5d90e359c4a915047413390fd17f3a..02ba5495b7cec1b5daa6efa8004f2da0139d6e53 100644 (file)
@@ -49,7 +49,12 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
 
   while (dyn->d_tag != DT_NULL)
     {
-      if ((d_tag_utype) dyn->d_tag < DT_NUM)
+      /* Google-local: b/208156916.  See ELF_DYNAMIC_DO_RELR.  */
+      if (dyn->d_tag == DT_RELR)
+       info[VERSYMIDX (DT_VERSYM + 1)] = dyn;
+      else if (dyn->d_tag == DT_RELRSZ)
+       info[VERSYMIDX (DT_VERSYM + 2)] = dyn;
+      else if ((d_tag_utype) dyn->d_tag < DT_NUM)
        info[dyn->d_tag] = dyn;
       else if (dyn->d_tag >= DT_LOPROC &&
               dyn->d_tag < DT_LOPROC + DT_THISPROCNUM)
@@ -104,16 +109,27 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
       ADJUST_DYN_INFO (DT_PLTGOT);
       ADJUST_DYN_INFO (DT_STRTAB);
       ADJUST_DYN_INFO (DT_SYMTAB);
+      ADJUST_DYN_INFO (VERSYMIDX (DT_VERSYM + 1)); /* DT_RELR */
+      ADJUST_DYN_INFO (DT_JMPREL);
+      ADJUST_DYN_INFO (VERSYMIDX (DT_VERSYM));
+      ADJUST_DYN_INFO (DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM
+                      + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM);
+# undef ADJUST_DYN_INFO
+
+      /* DT_RELA/DT_REL are mandatory.  But they may have zero value if
+        there is DT_RELR.  Don't relocate them if they are zero.  */
+# define ADJUST_DYN_INFO(tag) \
+      do                                                                     \
+       if (info[tag] != NULL && info[tag]->d_un.d_ptr != 0)                  \
+         info[tag]->d_un.d_ptr += l_addr;                                    \
+      while (0)
+
 # if ! ELF_MACHINE_NO_RELA
       ADJUST_DYN_INFO (DT_RELA);
 # endif
 # if ! ELF_MACHINE_NO_REL
       ADJUST_DYN_INFO (DT_REL);
 # endif
-      ADJUST_DYN_INFO (DT_JMPREL);
-      ADJUST_DYN_INFO (VERSYMIDX (DT_VERSYM));
-      ADJUST_DYN_INFO (DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM
-                      + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM);
 # undef ADJUST_DYN_INFO
       assert (cnt <= DL_RO_DYN_TEMP_CNT);
     }
diff --git a/elf/tst-relr-mod2.c b/elf/tst-relr-mod2.c
new file mode 100644 (file)
index 0000000..dc0a633
--- /dev/null
@@ -0,0 +1,46 @@
+/* Test for DT_RELR in a shared library without DT_NEEDED.
+   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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+
+static int o, x;
+
+#define ELEMS O O O O O O O O X X X X X X X O O X O O X X X E X E E O X O E
+#define E 0,
+
+#define O &o,
+#define X &x,
+void *arr[] = { ELEMS };
+#undef O
+#undef X
+
+#define O 1,
+#define X 2,
+static char val[] = { ELEMS };
+
+int
+foo (void)
+{
+  int err = 0;
+  for (int i = 0; i < array_length (arr); i++)
+    if (!((arr[i] == 0 && val[i] == 0)
+         || (arr[i] == &o && val[i] == 1)
+         || (arr[i] == &x && val[i] == 2)))
+      err++;
+  return err;
+}
diff --git a/elf/tst-relr-mod3a.c b/elf/tst-relr-mod3a.c
new file mode 100644 (file)
index 0000000..d1621c9
--- /dev/null
@@ -0,0 +1,49 @@
+/* Test for DT_RELR in a shared library without DT_VERNEED.
+   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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+
+static int o, x;
+
+#define ELEMS O O O O O O O O X X X X X X X O O X O O X X X E X E E O X O E
+#define E 0,
+
+#define O &o,
+#define X &x,
+void *arr[] = { ELEMS };
+#undef O
+#undef X
+
+#define O 1,
+#define X 2,
+static char val[] = { ELEMS };
+
+extern void bar (void);
+
+int
+foo (void)
+{
+  int err = 0;
+  for (int i = 0; i < array_length (arr); i++)
+    if (!((arr[i] == 0 && val[i] == 0)
+         || (arr[i] == &o && val[i] == 1)
+         || (arr[i] == &x && val[i] == 2)))
+      err++;
+  bar ();
+  return err;
+}
diff --git a/elf/tst-relr-mod3b.c b/elf/tst-relr-mod3b.c
new file mode 100644 (file)
index 0000000..544cb4b
--- /dev/null
@@ -0,0 +1,22 @@
+/* Test for DT_RELR in a shared library without DT_VERNEED.
+   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
+   <https://www.gnu.org/licenses/>.  */
+
+void
+bar (void)
+{
+}
diff --git a/elf/tst-relr-mod4a.c b/elf/tst-relr-mod4a.c
new file mode 100644 (file)
index 0000000..e1bfebd
--- /dev/null
@@ -0,0 +1,19 @@
+/* Test for DT_RELR in a shared library without libc.so on DT_NEEDED.
+   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
+   <https://www.gnu.org/licenses/>.  */
+
+#include "tst-relr-mod3a.c"
diff --git a/elf/tst-relr-mod4b.c b/elf/tst-relr-mod4b.c
new file mode 100644 (file)
index 0000000..617dff7
--- /dev/null
@@ -0,0 +1,19 @@
+/* Test for DT_RELR in a shared library without libc.so on DT_NEEDED.
+   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
+   <https://www.gnu.org/licenses/>.  */
+
+#include "tst-relr-mod3b.c"
diff --git a/elf/tst-relr-mod4b.map b/elf/tst-relr-mod4b.map
new file mode 100644 (file)
index 0000000..7f02247
--- /dev/null
@@ -0,0 +1,3 @@
+DT_RELR {
+  global: bar;
+};
diff --git a/elf/tst-relr-pie.c b/elf/tst-relr-pie.c
new file mode 100644 (file)
index 0000000..7df0cdb
--- /dev/null
@@ -0,0 +1 @@
+#include "tst-relr.c"
diff --git a/elf/tst-relr.c b/elf/tst-relr.c
new file mode 100644 (file)
index 0000000..c634ce0
--- /dev/null
@@ -0,0 +1,65 @@
+/* Basic tests for DT_RELR.
+   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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <link.h>
+#include <stdbool.h>
+#include <array_length.h>
+#include <support/check.h>
+
+static int o, x;
+
+#define ELEMS O O O O O O O O X X X X X X X O O X O O X X X E X E E O X O E
+#define E 0,
+
+#define O &o,
+#define X &x,
+void *arr[] = { ELEMS };
+#undef O
+#undef X
+
+#define O 1,
+#define X 2,
+static char val[] = { ELEMS };
+
+static int
+do_test (void)
+{
+  ElfW(Dyn) *d = _DYNAMIC;
+  if (d)
+    {
+      bool has_relr = false;
+      for (; d->d_tag != DT_NULL; d++)
+       if (d->d_tag == DT_RELR)
+         has_relr = true;
+
+#if defined __PIE__ || defined __pie__ || defined PIE || defined pie
+      TEST_VERIFY (has_relr);
+#else
+      TEST_VERIFY (!has_relr);
+#endif
+    }
+
+  for (int i = 0; i < array_length (arr); i++)
+    TEST_VERIFY ((arr[i] == 0 && val[i] == 0)
+                || (arr[i] == &o && val[i] == 1)
+                || (arr[i] == &x && val[i] == 2));
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-relr2.c b/elf/tst-relr2.c
new file mode 100644 (file)
index 0000000..10d77f1
--- /dev/null
@@ -0,0 +1,27 @@
+/* Test for DT_RELR in a shared library without DT_NEEDED.
+   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
+   <https://www.gnu.org/licenses/>.  */
+
+extern int foo (void);
+
+static int
+do_test (void)
+{
+  return foo ();
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-relr3.c b/elf/tst-relr3.c
new file mode 100644 (file)
index 0000000..69106cf
--- /dev/null
@@ -0,0 +1,27 @@
+/* Test for DT_RELR in a shared library without libc.so on DT_NEEDED.
+   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
+   <https://www.gnu.org/licenses/>.  */
+
+extern int foo (void);
+
+static int
+do_test (void)
+{
+  return foo ();
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-relr4.c b/elf/tst-relr4.c
new file mode 100644 (file)
index 0000000..19a7501
--- /dev/null
@@ -0,0 +1 @@
+#include "tst-relr3.c"
index a3df316c703ea98badb15ec0b912f71f611c1f2f..f4a63f3edc47273e116dda136861db09b5ccd3b8 100644 (file)
@@ -132,8 +132,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) {