Browse Source

ld.so: Add support of DT_RELR relocation format.

Nowadays modern libcs like Glibc and musl currently
support processing of RELATIVE relocations compressed
with DT_RELR format. However I have noticed that uClibc-ng
doesn't support this feature and if the source will be linked with
`-Wl,-z,pack-relative-relos` (bfd) or `-Wl,--pack-dyn-relocs=relr`
(lld) then ld.so cannot properly load the produced DSO.
This patch is intended to fix this issue and adds applying
of DT_RELR relative relocation.

Signed-off-by: Dmitry Chestnykh <dm.chestnykh@gmail.com>
Dmitry Chestnykh 2 months ago
parent
commit
395f920c0a
5 changed files with 61 additions and 1 deletions
  1. 7 1
      include/elf.h
  2. 41 0
      ldso/include/dl-elf.h
  3. 5 0
      ldso/ldso/dl-elf.c
  4. 5 0
      ldso/ldso/dl-startup.c
  5. 3 0
      libc/misc/internals/reloc_static_pie.c

+ 7 - 1
include/elf.h

@@ -60,6 +60,9 @@ typedef uint16_t Elf64_Section;
 typedef Elf32_Half Elf32_Versym;
 typedef Elf64_Half Elf64_Versym;
 
+/* Type for relative relocations in DT_RELR format */
+typedef Elf32_Word  Elf32_Relr;
+typedef Elf64_Xword Elf64_Relr;
 
 /* The ELF file header.  This appears at the start of every ELF file.  */
 
@@ -818,7 +821,10 @@ 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_NUM		34		/* Number used */
+#define DT_RELRSZ          35   	/* Size in bytes, of DT_RELR table */
+#define DT_RELR            36   	/* Address of Relr relocs */
+#define DT_RELRENT         37   	/* Size in bytes of one DT_RELR entry */
+#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 */

+ 41 - 0
ldso/include/dl-elf.h

@@ -250,5 +250,46 @@ unsigned int __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info
 		    (((X) & PF_W) ? PROT_WRITE : 0) | \
 		    (((X) & PF_X) ? PROT_EXEC : 0))
 
+/* FDPIC ABI don't use relative relocations */
+#if !defined(__FDPIC__)
+/* Apply relocations in DT_RELR format */
+#define DL_DO_RELOCATE_RELR(load_addr, relr_start, relr_end) \
+		do { \
+			const ElfW(Relr) *relr = 0; \
+			ElfW(Addr) *reloc_addr = 0; \
+			for (relr = relr_start; relr < relr_end; relr++) { \
+				ElfW(Relr) relr_entry = *relr; \
+				if (!(relr_entry & 1)) \
+				{ \
+					reloc_addr = (ElfW(Addr) *)DL_RELOC_ADDR(load_addr, relr_entry); \
+					*reloc_addr = (ElfW(Addr))DL_RELOC_ADDR(load_addr, reloc_addr); \
+					reloc_addr++; \
+				} \
+				else \
+				{ \
+					for (long int i = 0; (relr_entry >>= 1) != 0; ++i) { \
+						if ((relr_entry & 1) != 0) \
+							reloc_addr[i] = (ElfW(Addr))DL_RELOC_ADDR(load_addr, reloc_addr[i]); \
+					} \
+					reloc_addr += CHAR_BIT * sizeof(ElfW(Relr)) - 1; \
+				} \
+			} \
+		} while (0);
+
+/* The macro to prepare data for the above DL_DO_RELOCATE_RELR */
+#define DL_RELOCATE_RELR(dyn) \
+		do { \
+			if (dyn->dynamic_info[DT_RELRENT]) \
+				_dl_assert(dyn->dynamic_info[DT_RELRENT] == sizeof(ElfW(Relr))); \
+			if (dyn->dynamic_info[DT_RELR] && \
+				dyn->dynamic_info[DT_RELRSZ]) { \
+				ElfW(Relr) *relr_start = (ElfW(Relr) *)((ElfW(Addr))dyn->loadaddr + (ElfW(Addr))dyn->dynamic_info[DT_RELR]); \
+				ElfW(Relr) *relr_end = (ElfW(Relr) *)((const char *)relr_start + dyn->dynamic_info[DT_RELRSZ]); \
+				_dl_if_debug_dprint("Relocating DT_RELR in %s: start:%p, end:%p\n", \
+						    dyn->libname, (void *)relr_start, (void *)relr_end); \
+				DL_DO_RELOCATE_RELR(dyn->loadaddr, relr_start, relr_end); \
+			} \
+		} while (0);
+#endif /* __FDPIC__ */
 
 #endif /* _DL_ELF_H */

+ 5 - 0
ldso/ldso/dl-elf.c

@@ -1027,6 +1027,11 @@ int _dl_fixup(struct dyn_elf *rpnt, struct r_scope_elem *scope, int now_flag)
 		return goof;
 	}
 
+#if !defined(__FDPIC__)
+	/* Process DT_RELR relative relocations */
+	DL_RELOCATE_RELR(tpnt);
+#endif
+
 	reloc_size = tpnt->dynamic_info[DT_RELOC_TABLE_SIZE];
 /* On some machines, notably SPARC & PPC, DT_REL* includes DT_JMPREL in its
    range.  Note that according to the ELF spec, this is completely legal! */

+ 5 - 0
ldso/ldso/dl-startup.c

@@ -264,6 +264,11 @@ DL_START(unsigned long args)
 	   that once we are done, we have considerably more flexibility. */
 	SEND_EARLY_STDERR_DEBUG("About to do library loader relocations\n");
 
+#if !defined(__FDPIC__)
+	/* Process DT_RELR relative relocations */
+	DL_RELOCATE_RELR(tpnt);
+#endif
+
 	{
 		int indx;
 #if defined(ELF_MACHINE_PLTREL_OVERLAP)

+ 3 - 0
libc/misc/internals/reloc_static_pie.c

@@ -53,6 +53,9 @@ reloc_static_pie(ElfW(Addr) load_addr)
 	PERFORM_BOOTSTRAP_GOT(tpnt);
 #endif
 
+#if !defined(__FDPIC__)
+    DL_RELOCATE_RELR(tpnt);
+#endif
 
 #if defined(ELF_MACHINE_PLTREL_OVERLAP)
 # define INDX_MAX 1