From: Fangrui Song <maskray@google.com>
To: libc-alpha@sourceware.org
Cc: binutils@sourceware.org, Fangrui Song <maskray@google.com>
Subject: [PATCH v2] elf: Support DT_RELR relative relocation format [BZ #27924]
Date: Sat, 16 Oct 2021 17:50:20 -0700 [thread overview]
Message-ID: <20211017005020.2645717-1-maskray@google.com> (raw)
PIE and shared objects usually have many relative relocations. In
2017/2018, SHT_RELR/DT_RELR was proposed on
https://groups.google.com/g/generic-abi/c/bX460iggiKg/m/GxjM0L-PBAAJ
("Proposal for a new section type SHT_RELR") and is a pre-standard. RELR
usually takes 3% or smaller space than R_*_RELATIVE relocations. The
virtual memory size of a mostly statically linked PIE is typically 5~10%
smaller.
---
Notes I will not include in the submitted commit:
Available on https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/maskray/relr
"pre-standard": even Solaris folks are happy with the refined generic-abi
proposal. Cary Coutant will apply the change
https://sourceware.org/pipermail/libc-alpha/2021-October/131781.html
This patch is simpler than Chrome OS's glibc patch and makes ELF_DYNAMIC_DO_RELR
available to all ports. I don't think the current glibc implementation
supports ia64 in an ELFCLASS32 container. That said, the style I used is
works with an ELFCLASS32 container for 64-bit machine if ElfW(Addr) is
64-bit.
* Chrome OS folks have carried a local patch since 2018 (latest version:
https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/refs/heads/main/sys-libs/glibc/files/local/glibc-2.32).
I.e. this feature has been battle tested.
* Android bionic supports 2018 and switched to DT_RELR==36 in 2020.
* The Linux kernel has supported CONFIG_RELR since 2019-08
(https://git.kernel.org/linus/5cf896fb6be3effd9aea455b22213e27be8bdb1d).
* A musl patch (by me) exists but is not applied:
https://www.openwall.com/lists/musl/2019/03/06/3
* rtld-elf from FreeBSD 14 will support DT_RELR.
I believe upstream glibc should support DT_RELR to benefit all Linux
distributions. I filed some feature requests to get their attention:
* Gentoo: https://bugs.gentoo.org/818376
* Arch Linux: https://bugs.archlinux.org/task/72433
* Debian https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=996598
* Fedora https://bugzilla.redhat.com/show_bug.cgi?id=2014699
As of linker support (to the best of my knowledge):
* LLD support DT_RELR.
* https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/refs/heads/main/sys-devel/binutils/files/
has a gold patch.
* GNU ld feature request https://sourceware.org/bugzilla/show_bug.cgi?id=27923
I wish that GNU ld and gold maintainers can implement the feature as well :)
Tested on aarch64 and x86_64.
Changes from v1 (https://sourceware.org/pipermail/libc-alpha/2021-October/131768.html)
* Fix style, simplify code
* Improve test
---
configure | 31 +++++++++++++++++++++++++++++++
configure.ac | 4 ++++
elf/Makefile | 4 ++++
elf/dynamic-link.h | 28 ++++++++++++++++++++++++++++
elf/elf.h | 13 +++++++++++--
elf/get-dynamic-info.h | 3 +++
elf/tst-relr.c | 27 +++++++++++++++++++++++++++
7 files changed, 108 insertions(+), 2 deletions(-)
create mode 100644 elf/tst-relr.c
diff --git a/configure b/configure
index 3227e434d3..fdab6a97ef 100755
--- a/configure
+++ b/configure
@@ -6067,6 +6067,37 @@ $as_echo "$libc_linker_feature" >&6; }
config_vars="$config_vars
have-depaudit = $libc_cv_depaudit"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker that supports --pack-dyn-relocs=relr" >&5
+$as_echo_n "checking for linker that supports --pack-dyn-relocs=relr... " >&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,--pack-dyn-relocs=relr -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
+if test $libc_linker_feature = yes; then
+ libc_cv_relr=yes
+else
+ libc_cv_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-relr = $libc_cv_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; }
libc_linker_feature=no
diff --git a/configure.ac b/configure.ac
index 00f49f09f7..96110f9d7d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1354,6 +1354,10 @@ LIBC_LINKER_FEATURE([--depaudit], [-Wl,--depaudit,x],
[libc_cv_depaudit=yes], [libc_cv_depaudit=no])
LIBC_CONFIG_VAR([have-depaudit], [$libc_cv_depaudit])
+LIBC_LINKER_FEATURE([--pack-dyn-relocs=relr], [-Wl,--pack-dyn-relocs=relr],
+ [libc_cv_relr=yes], [libc_cv_relr=no])
+LIBC_CONFIG_VAR([have-relr], [$libc_cv_relr])
+
LIBC_LINKER_FEATURE([--no-dynamic-linker],
[-Wl,--no-dynamic-linker],
[libc_cv_no_dynamic_linker=yes],
diff --git a/elf/Makefile b/elf/Makefile
index bf45d8ee24..2c4cdfac68 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -245,6 +245,10 @@ tests-special += $(objpfx)tst-audit14-cmp.out $(objpfx)tst-audit15-cmp.out \
$(objpfx)tst-audit16-cmp.out
endif
endif
+ifeq ($(have-relr),yes)
+tests += tst-relr
+LDFLAGS-tst-relr += -Wl,--pack-dyn-relocs=relr
+endif
endif
tests += $(tests-execstack-$(have-z-execstack))
ifeq ($(run-built-tests),yes)
diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h
index ac4cc70dea..fafd42f918 100644
--- a/elf/dynamic-link.h
+++ b/elf/dynamic-link.h
@@ -160,6 +160,33 @@ 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. */
# define ELF_DYNAMIC_RELOCATE(map, scope, lazy, consider_profile, skip_ifunc) \
@@ -168,6 +195,7 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
(consider_profile)); \
ELF_DYNAMIC_DO_REL ((map), (scope), edr_lazy, skip_ifunc); \
ELF_DYNAMIC_DO_RELA ((map), (scope), edr_lazy, skip_ifunc); \
+ ELF_DYNAMIC_DO_RELR ((map)); \
} while (0)
#endif
diff --git a/elf/elf.h b/elf/elf.h
index 50f87baceb..6dc1ee0ac0 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 1ac0663d1f..ce0151c2b4 100644
--- a/elf/get-dynamic-info.h
+++ b/elf/get-dynamic-info.h
@@ -88,6 +88,7 @@ elf_get_dynamic_info (struct link_map *l)
# 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));
@@ -112,6 +113,8 @@ elf_get_dynamic_info (struct link_map *l)
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)));
#ifdef STATIC_PIE_BOOTSTRAP
assert (info[DT_RUNPATH] == NULL);
assert (info[DT_RPATH] == NULL);
diff --git a/elf/tst-relr.c b/elf/tst-relr.c
new file mode 100644
index 0000000000..79736a4a8a
--- /dev/null
+++ b/elf/tst-relr.c
@@ -0,0 +1,27 @@
+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)
+{
+ 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>
--
2.33.0.1079.g6e70778dc9-goog
next reply other threads:[~2021-10-17 0:50 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-10-17 0:50 Fangrui Song [this message]
2021-10-18 14:42 ` H.J. Lu
2021-10-18 16:16 ` Fāng-ruì Sòng
2021-10-18 17:28 ` H.J. Lu
2021-10-18 18:15 ` Fāng-ruì Sòng
2021-10-18 18:27 ` H.J. Lu
2021-10-18 19:19 ` Fāng-ruì Sòng
2021-10-18 19:44 ` H.J. Lu
2021-10-18 20:11 ` Fāng-ruì Sòng
2021-10-18 21:10 ` Joseph Myers
2021-10-18 22:27 ` H.J. Lu
2021-10-18 22:30 ` Joseph Myers
2021-10-18 22:42 ` H.J. Lu
2021-10-18 23:00 ` Joseph Myers
2021-10-18 23:36 ` H.J. Lu
2021-10-18 23:44 ` Joseph Myers
2021-10-19 1:05 ` H.J. Lu
2021-10-18 19:21 ` Joseph Myers
2021-10-18 19:45 ` H.J. Lu
2021-10-29 18:21 ` Carlos O'Donell
2021-10-29 18:36 ` H.J. Lu
2021-10-29 19:15 ` Fangrui Song
2021-10-29 18:38 ` Fāng-ruì Sòng
2021-10-29 19:35 ` Carlos O'Donell
2021-10-29 19:53 ` Adhemerval Zanella
2021-11-01 4:50 ` Fāng-ruì Sòng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20211017005020.2645717-1-maskray@google.com \
--to=maskray@google.com \
--cc=binutils@sourceware.org \
--cc=libc-alpha@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).