From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 7814) id 66CED385780F; Sat, 8 Jan 2022 08:18:52 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 66CED385780F Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Fangrui Song To: glibc-cvs@sourceware.org Subject: [glibc/maskray/relr] elf: Support DT_RELR relative relocation format [BZ #27924] X-Act-Checkin: glibc X-Git-Author: Fangrui Song X-Git-Refname: refs/heads/maskray/relr X-Git-Oldrev: 6b0978c14acc2a6b5f5dbd8e8ef75fddc6656483 X-Git-Newrev: b2cd3aeb9c0a2fbd03b37065e7630a48571bb6b8 Message-Id: <20220108081852.66CED385780F@sourceware.org> Date: Sat, 8 Jan 2022 08:18:52 +0000 (GMT) X-BeenThere: glibc-cvs@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Glibc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 08 Jan 2022 08:18:52 -0000 https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=b2cd3aeb9c0a2fbd03b37065e7630a48571bb6b8 commit b2cd3aeb9c0a2fbd03b37065e7630a48571bb6b8 Author: Fangrui Song Date: Sat Jan 8 00:17:48 2022 -0800 elf: Support DT_RELR relative relocation format [BZ #27924] 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. To protect mysterious crash when a DT_RELR using object runs with older glibc, GNU ld's -z pack-relative-relocs adds a version dependency on libc.so.6's GLIBC_ABI_DT_RELR version node. This change defines GLIBC_ABI_DT_RELR in libc.so.6. --- 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 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 Changes from v2 * Add GLIBC_ABI_DT_RELR version node similar to HJ Lu's GLIBC_ABI_VERSION_GEN2 https://sourceware.org/pipermail/libc-alpha/2021-November/133386.html Diff: --- configure | 37 +++++++++++++++++++++++++++++++++++++ configure.ac | 4 ++++ elf/Makefile | 19 ++++++++++++++++--- elf/Versions | 3 +++ elf/dynamic-link.h | 34 ++++++++++++++++++++++++++++++++++ elf/elf.h | 13 +++++++++++-- elf/get-dynamic-info.h | 3 +++ elf/libc-abi-dt-relr.c | 21 +++++++++++++++++++++ elf/tst-relr-no-pie.c | 1 + elf/tst-relr.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ scripts/abilist.awk | 2 ++ 11 files changed, 177 insertions(+), 5 deletions(-) diff --git a/configure b/configure index 3f956cf777..fc6d8d8244 100755 --- a/configure +++ b/configure @@ -6102,6 +6102,43 @@ $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 -z pack-relative-relocs" >&5 +$as_echo_n "checking for linker that supports -z pack-relative-relocs... " >&6; } +libc_linker_feature=no +if test x"$gnu_ld" = x"yes"; then + cat > conftest.c <&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } + then + if ${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp -Wl,-z,pack-relative-relocs -nostdlib \ + -nostartfiles -fPIC -shared -o conftest.so conftest.c 2>&1 \ + | grep "warning: -z pack-relative-relocs ignored" > /dev/null 2>&1; then + true + else + libc_linker_feature=yes + fi + 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 277d3527d2..9cebe17325 100644 --- a/configure.ac +++ b/configure.ac @@ -1361,6 +1361,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([-z pack-relative-relocs], [-Wl,-z,pack-relative-relocs], + [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 b86d116be9..33c795d62c 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -26,7 +26,7 @@ routines = $(all-dl-routines) dl-support dl-iteratephdr \ dl-addr dl-addr-obj enbl-secure dl-profstub \ dl-origin dl-libc dl-sym dl-sysdep dl-error \ dl-reloc-static-pie libc_early_init rtld_static_init \ - libc-dl_find_object + libc-dl_find_object libc-abi-dt-relr # The core dynamic linking functions are in libc for the static and # profiled libraries. @@ -360,6 +360,13 @@ tests-special += $(objpfx)tst-audit14-cmp.out $(objpfx)tst-audit15-cmp.out \ $(objpfx)tst-audit16-cmp.out $(objpfx)tst-audit14a-cmp.out 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 tests += $(tests-execstack-$(have-z-execstack)) ifeq ($(run-built-tests),yes) @@ -824,8 +831,10 @@ $(eval $(call include_dsosort_tests,dso-sort-tests-1.def)) $(eval $(call include_dsosort_tests,dso-sort-tests-2.def)) endif -check-abi: $(objpfx)check-abi-ld.out -tests-special += $(objpfx)check-abi-ld.out +check-abi: $(objpfx)check-abi-ld.out \ + $(objpfx)check-libc-abi-dt-relr.out +tests-special += $(objpfx)check-abi-ld.out \ + $(objpfx)check-libc-abi-dt-relr.out update-abi: update-abi-ld update-all-abi: update-all-abi-ld @@ -2350,3 +2359,7 @@ CFLAGS-tst-dl_find_object-mod6.c += -funwind-tables CFLAGS-tst-dl_find_object-mod7.c += -funwind-tables CFLAGS-tst-dl_find_object-mod8.c += -funwind-tables CFLAGS-tst-dl_find_object-mod9.c += -funwind-tables + +$(objpfx)check-libc-abi-dt-relr.out: $(common-objpfx)libc.so + LC_ALL=C $(NM) -D $< | grep '__libc_abi_dt_relr_placeholder@@GLIBC_ABI_DT_RELR' > $@; \ + $(evaluate-test) diff --git a/elf/Versions b/elf/Versions index a4f1e76708..79325817d3 100644 --- a/elf/Versions +++ b/elf/Versions @@ -23,6 +23,9 @@ libc { GLIBC_2.35 { _dl_find_object; } + GLIBC_ABI_DT_RELR { + __libc_abi_dt_relr_placeholder; + } GLIBC_PRIVATE { # functions used in other libraries __libc_early_init; diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h index 25dd7ca4f2..4dd62b34d7 100644 --- a/elf/dynamic-link.h +++ b/elf/dynamic-link.h @@ -146,14 +146,48 @@ 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)); \ ELF_DYNAMIC_DO_REL ((map), (scope), edr_lazy, skip_ifunc); \ ELF_DYNAMIC_DO_RELA ((map), (scope), edr_lazy, skip_ifunc); \ + if ((map) != &GL(dl_rtld_map) || DO_RTLD_BOOTSTRAP) \ + ELF_DYNAMIC_DO_RELR (map); \ } while (0) #endif 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/libc-abi-dt-relr.c b/elf/libc-abi-dt-relr.c new file mode 100644 index 0000000000..41c8e9f8eb --- /dev/null +++ b/elf/libc-abi-dt-relr.c @@ -0,0 +1,21 @@ +/* Placeholder definition for GLIBC_ABI_DT_RELR node. + Copyright (C) 2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifdef SHARED +const char __libc_abi_dt_relr_placeholder = 0; +#endif 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 +#include +#include + +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 diff --git a/scripts/abilist.awk b/scripts/abilist.awk index 24a34ccbed..e6a94c6571 100644 --- a/scripts/abilist.awk +++ b/scripts/abilist.awk @@ -55,6 +55,8 @@ $2 == "g" || $2 == "w" && (NF == 7 || NF == 8) { # caused STV_HIDDEN symbols to appear in .dynsym, though that is useless. if (NF > 7 && $7 == ".hidden") next; + if (version == "GLIBC_ABI_DT_RELR") next; + if (version == "GLIBC_PRIVATE" && !include_private) next; desc = "";