From 395f920c0a29dd565a66b268ea00fc444ae64086 Mon Sep 17 00:00:00 2001 From: Dmitry Chestnykh Date: Tue, 6 Feb 2024 09:13:41 +0300 Subject: 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 --- ldso/include/dl-elf.h | 41 +++++++++++++++++++++++++++++++++++++++++ ldso/ldso/dl-elf.c | 5 +++++ ldso/ldso/dl-startup.c | 5 +++++ 3 files changed, 51 insertions(+) (limited to 'ldso') diff --git a/ldso/include/dl-elf.h b/ldso/include/dl-elf.h index 2b99958d9..7d514c0f5 100644 --- a/ldso/include/dl-elf.h +++ b/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 */ diff --git a/ldso/ldso/dl-elf.c b/ldso/ldso/dl-elf.c index 8210a012e..27907d355 100644 --- a/ldso/ldso/dl-elf.c +++ b/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! */ diff --git a/ldso/ldso/dl-startup.c b/ldso/ldso/dl-startup.c index 989711fcc..d80ee75ea 100644 --- a/ldso/ldso/dl-startup.c +++ b/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) -- cgit v1.2.3