diff --git a/configure b/configure index 650bfd982c..5a730dc5fc 100755 --- a/configure +++ b/configure @@ -6076,6 +6076,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_dt_relr=yes +else + libc_cv_dt_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-dt-relr = $libc_cv_dt_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 605efd549d..a045f6608e 100644 --- a/configure.ac +++ b/configure.ac @@ -1336,6 +1336,11 @@ 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_dt_relr=yes], [libc_cv_dt_relr=no]) +LIBC_CONFIG_VAR([have-dt-relr], [$libc_cv_dt_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 073807ba85..3dcc550def 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -542,6 +542,25 @@ tests-special += \ # tests-special endif endif +ifeq ($(have-dt-relr),yes) +tests += \ + tst-relr \ +# tests +ifeq ($(have-fpie),yes) +tests += \ + tst-relr-pie \ +# tests +tests-pie += \ + tst-relr-pie \ +# tests-pie +tests-special += \ + $(objpfx)check-tst-relr-pie.out \ +# tests-special +endif +CFLAGS-tst-relr-pie.c += $(pie-ccflag) +LDFLAGS-tst-relr += -Wl,-z,pack-relative-relocs +LDFLAGS-tst-relr-pie += -Wl,-z,pack-relative-relocs +endif endif tests-special += $(objpfx)tst-relro-ldso.out $(objpfx)tst-relro-libc.out @@ -2789,3 +2808,9 @@ $(objpfx)check-abi-version-libc.out: $(common-objpfx)libc.so | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ | grep GLIBC_ABI_DT_RELR > $@; \ $(evaluate-test) + +$(objpfx)check-tst-relr-pie.out: $(objpfx)tst-relr-pie + LC_ALL=C $(OBJDUMP) -p $< \ + | sed -ne '/required from libc.so/,$$ p' \ + | grep GLIBC_ABI_DT_RELR > $@; \ + $(evaluate-test) diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h index 25dd7ca4f2..019088f248 100644 --- a/elf/dynamic-link.h +++ b/elf/dynamic-link.h @@ -146,12 +146,46 @@ 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] == NULL) \ + 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 int 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)); \ + if (((map) != &GL(dl_rtld_map) || DO_RTLD_BOOTSTRAP)) \ + ELF_DYNAMIC_DO_RELR (map); \ ELF_DYNAMIC_DO_REL ((map), (scope), edr_lazy, skip_ifunc); \ ELF_DYNAMIC_DO_RELA ((map), (scope), edr_lazy, skip_ifunc); \ } while (0) 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/tst-relr-pie.c b/elf/tst-relr-pie.c new file mode 100644 index 0000000000..7df0cdbfd6 --- /dev/null +++ b/elf/tst-relr-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..c634ce0d21 --- /dev/null +++ b/elf/tst-relr.c @@ -0,0 +1,65 @@ +/* Basic tests for DT_RELR. + 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 + . */ + +#include +#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 defined __PIE__ || defined __pie__ || defined PIE || defined pie + TEST_VERIFY (has_relr); +#else + TEST_VERIFY (!has_relr); +#endif + } + + for (int i = 0; i < array_length (arr); i++) + TEST_VERIFY ((arr[i] == 0 && val[i] == 0) + || (arr[i] == &o && val[i] == 1) + || (arr[i] == &x && val[i] == 2)); + + return 0; +} + +#include