summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Chestnykh <dm.chestnykh@gmail.com>2024-02-06 09:13:41 +0300
committerWaldemar Brodkorb <wbx@openadk.org>2024-02-07 13:47:28 +0100
commit395f920c0a29dd565a66b268ea00fc444ae64086 (patch)
tree78e0c6905d3f98b1e53c5c562dea9f99c4e69983
parent0fef8e7e0661daa17a9ca17b087f55dcb752b378 (diff)
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>
-rw-r--r--include/elf.h8
-rw-r--r--ldso/include/dl-elf.h41
-rw-r--r--ldso/ldso/dl-elf.c5
-rw-r--r--ldso/ldso/dl-startup.c5
-rw-r--r--libc/misc/internals/reloc_static_pie.c3
5 files changed, 61 insertions, 1 deletions
diff --git a/include/elf.h b/include/elf.h
index b7edbade2..c2efa9978 100644
--- a/include/elf.h
+++ b/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 */
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)
diff --git a/libc/misc/internals/reloc_static_pie.c b/libc/misc/internals/reloc_static_pie.c
index ab1923024..81af7d666 100644
--- a/libc/misc/internals/reloc_static_pie.c
+++ b/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