about summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile7
-rw-r--r--elf/dynamic-link.h34
-rw-r--r--elf/elf.h13
-rw-r--r--elf/get-dynamic-info.h3
-rw-r--r--elf/tst-relr-no-pie.c1
-rw-r--r--elf/tst-relr.c45
6 files changed, 101 insertions, 2 deletions
diff --git a/elf/Makefile b/elf/Makefile
index c96924e9c2..d75353eed0 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -541,6 +541,13 @@ tests-special += \
   # tests-special
 endif
 endif
+ifeq ($(have-relr),yes)
+tests += tst-relr tst-relr-no-pie
+tests-pie += tst-relr
+tests-no-pie += tst-relr-no-pie
+LDFLAGS-tst-relr += -z pack-relative-relocs
+LDFLAGS-tst-relr-no-pie += -z pack-relative-relocs
+endif
 endif
 
 ifeq ($(run-built-tests),yes)
diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h
index 25dd7ca4f2..169745f9a4 100644
--- a/elf/dynamic-link.h
+++ b/elf/dynamic-link.h
@@ -146,12 +146,46 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
 #  define ELF_DYNAMIC_DO_RELA(map, scope, lazy, skip_ifunc) /* Nothing to do.  */
 # endif
 
+# 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[DT_RELR])					      \
+      break;								      \
+    r = (const ElfW(Relr) *)D_PTR((map), l_info[DT_RELR]);		      \
+    end = (const ElfW(Relr) *)((const char *)r +			      \
+                               (map)->l_info[DT_RELRSZ]->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, scope, lazy, consider_profile, skip_ifunc) \
   do {									      \
     int edr_lazy = elf_machine_runtime_setup ((map), (scope), (lazy),	      \
 					      (consider_profile));	      \
+    if ((map) != &GL(dl_rtld_map) || DO_RTLD_BOOTSTRAP)		      	      \
+      ELF_DYNAMIC_DO_RELR (map);					      \
     ELF_DYNAMIC_DO_REL ((map), (scope), edr_lazy, skip_ifunc);		      \
     ELF_DYNAMIC_DO_RELA ((map), (scope), edr_lazy, skip_ifunc);		      \
   } while (0)
diff --git a/elf/elf.h b/elf/elf.h
index 0735f6b579..0195029188 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -443,7 +443,8 @@ typedef struct
 #define SHT_PREINIT_ARRAY 16		/* Array of pre-constructors */
 #define SHT_GROUP	  17		/* Section group */
 #define SHT_SYMTAB_SHNDX  18		/* Extended section indices */
-#define	SHT_NUM		  19		/* Number of defined types.  */
+#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.  */
@@ -662,6 +663,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)
@@ -887,7 +893,10 @@ typedef struct
 #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_NUM		35		/* Number used */
+#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 */
+#define	DT_NUM		38		/* Number used */
 #define DT_LOOS		0x6000000d	/* Start of OS-specific */
 #define DT_HIOS		0x6ffff000	/* End of OS-specific */
 #define DT_LOPROC	0x70000000	/* Start of processor-specific */
diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
index 6c2ccd6db4..6c2a3a12b1 100644
--- a/elf/get-dynamic-info.h
+++ b/elf/get-dynamic-info.h
@@ -89,6 +89,7 @@ elf_get_dynamic_info (struct link_map *l, bool bootstrap,
 # if ! ELF_MACHINE_NO_REL
       ADJUST_DYN_INFO (DT_REL);
 # endif
+      ADJUST_DYN_INFO (DT_RELR);
       ADJUST_DYN_INFO (DT_JMPREL);
       ADJUST_DYN_INFO (VERSYMIDX (DT_VERSYM));
       ADJUST_DYN_INFO (ADDRIDX (DT_GNU_HASH));
@@ -113,6 +114,8 @@ elf_get_dynamic_info (struct link_map *l, bool bootstrap,
   if (info[DT_REL] != NULL)
     assert (info[DT_RELENT]->d_un.d_val == sizeof (ElfW(Rel)));
 #endif
+  if (info[DT_RELR] != NULL)
+    assert (info[DT_RELRENT]->d_un.d_val == sizeof (ElfW(Relr)));
   if (bootstrap || static_pie_bootstrap)
     {
       assert (info[DT_RUNPATH] == NULL);
diff --git a/elf/tst-relr-no-pie.c b/elf/tst-relr-no-pie.c
new file mode 100644
index 0000000000..7df0cdbfd6
--- /dev/null
+++ b/elf/tst-relr-no-pie.c
@@ -0,0 +1 @@
+#include "tst-relr.c"
diff --git a/elf/tst-relr.c b/elf/tst-relr.c
new file mode 100644
index 0000000000..0b0fe5ed17
--- /dev/null
+++ b/elf/tst-relr.c
@@ -0,0 +1,45 @@
+#include <link.h>
+#include <stdbool.h>
+#include <stdio.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 (!has_relr)
+	{
+	  fprintf (stderr, "no DT_RELR\n");
+	  return 1;
+	}
+    }
+
+  for (int i = 0; i < sizeof (arr) / sizeof (arr[0]); i++)
+    if (!((arr[i] == 0 && val[i] == 0) ||
+	  (arr[i] == &o && val[i] == 1) ||
+	  (arr[i] == &x && val[i] == 2)))
+      return 1;
+  return 0;
+}
+
+#include <support/test-driver.c>