aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2024-10-29 06:01:14 +0800
committerH.J. Lu <hjl.tools@gmail.com>2024-11-22 06:22:13 +0800
commite7b553272196e6175b8a15f807cb59217ba2843a (patch)
tree61d06f2d421422ea5d8e14d93a1fa8395b541d3d
parent713d6d7e781ced6d194d6c147f2db4ce9961bb0c (diff)
downloadglibc-e7b553272196e6175b8a15f807cb59217ba2843a.zip
glibc-e7b553272196e6175b8a15f807cb59217ba2843a.tar.gz
glibc-e7b553272196e6175b8a15f807cb59217ba2843a.tar.bz2
elf: Handle static PIE with non-zero load address [BZ #31799]HEADmaster
For a static PIE with non-zero load address, its PT_DYNAMIC segment entries contain the relocated values for the load address in static PIE. Since static PIE usually doesn't have PT_PHDR segment, use p_vaddr of the PT_LOAD segment with offset == 0 as the load address in static PIE and adjust the entries of PT_DYNAMIC segment in static PIE by properly setting the l_addr field for static PIE. This fixes BZ #31799. Signed-off-by: H.J. Lu <hjl.tools@gmail.com> Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
-rwxr-xr-xconfigure74
-rw-r--r--configure.ac36
-rw-r--r--elf/Makefile20
-rw-r--r--elf/dl-reloc-static-pie.c30
-rw-r--r--elf/tst-pie-address-static.c19
-rw-r--r--elf/tst-pie-address.c28
6 files changed, 200 insertions, 7 deletions
diff --git a/configure b/configure
index 9c0c0dc..9bcf62d 100755
--- a/configure
+++ b/configure
@@ -8107,6 +8107,80 @@ printf "%s\n" "$libc_cv_cc_pie_default" >&6; }
config_vars="$config_vars
cc-pie-default = $libc_cv_cc_pie_default"
+# Get Position Dependent Executable (PDE) load address to be used to
+# load static Position Independent Executable (PIE) at a known working
+# non-zero load address. This is only used by glibc tests to verify
+# that PIE and static PIE with non-zero load address work correctly.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking PDE load address" >&5
+printf %s "checking PDE load address... " >&6; }
+if test ${libc_cv_pde_load_address+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) cat > conftest.S <<EOF
+.globl _start
+_start:
+.globl __start
+__start:
+EOF
+if test $libc_cv_cc_pie_default = yes; then
+ pde_ld_flags="-no-pie"
+fi
+if ${CC-cc} $pde_ld_flags $CFLAGS $CPPFLAGS $LDFLAGS \
+ -nostartfiles -nostdlib $no_ssp \
+ -o conftest conftest.S 1>&5 2>&5; then
+ # Get the load address of the first PT_LOAD segment.
+ libc_cv_pde_load_address=$(LC_ALL=C $READELF -Wl conftest \
+ | $AWK '/LOAD/ { print $3; exit 0; }')
+else
+ as_fn_error $? "${CC-cc} can not create PDE" "$LINENO" 5
+fi
+rm -f conftest* ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_pde_load_address" >&5
+printf "%s\n" "$libc_cv_pde_load_address" >&6; }
+config_vars="$config_vars
+pde-load-address = $libc_cv_pde_load_address"
+
+# Get the linker command-line option to load executable at a non-zero
+# load address. This is only used by glibc tests to verify that PIE and
+# static PIE with non-zero load address work correctly.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for linker that supports -Ttext-segment=$libc_cv_pde_load_address" >&5
+printf %s "checking for linker that supports -Ttext-segment=$libc_cv_pde_load_address... " >&6; }
+libc_linker_feature=no
+cat > conftest.c <<EOF
+int _start (void) { return 42; }
+EOF
+if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp
+ -Wl,-Ttext-segment=$libc_cv_pde_load_address -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=$?
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }
+then
+ if ${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp -Wl,-Ttext-segment=$libc_cv_pde_load_address -nostdlib \
+ -nostartfiles -fPIC -shared -o conftest.so conftest.c 2>&1 \
+ | grep "warning: -Ttext-segment=$libc_cv_pde_load_address ignored" > /dev/null 2>&1; then
+ true
+ else
+ libc_linker_feature=yes
+ fi
+fi
+rm -f conftest*
+if test $libc_linker_feature = yes; then
+ libc_cv_load_address_ldflag=-Wl,-Ttext-segment
+else
+ libc_cv_load_address_ldflag=
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_linker_feature" >&5
+printf "%s\n" "$libc_linker_feature" >&6; }
+config_vars="$config_vars
+load-address-ldflag = $libc_cv_load_address_ldflag"
+
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we can build programs as PIE" >&5
printf %s "checking if we can build programs as PIE... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
diff --git a/configure.ac b/configure.ac
index d5a0046..895bd52 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1763,6 +1763,42 @@ fi
rm -f conftest.*])
LIBC_CONFIG_VAR([cc-pie-default], [$libc_cv_cc_pie_default])
+# Get Position Dependent Executable (PDE) load address to be used to
+# load static Position Independent Executable (PIE) at a known working
+# non-zero load address. This is only used by glibc tests to verify
+# that PIE and static PIE with non-zero load address work correctly.
+AC_CACHE_CHECK([PDE load address],
+ libc_cv_pde_load_address, [dnl
+cat > conftest.S <<EOF
+.globl _start
+_start:
+.globl __start
+__start:
+EOF
+if test $libc_cv_cc_pie_default = yes; then
+ pde_ld_flags="-no-pie"
+fi
+if ${CC-cc} $pde_ld_flags $CFLAGS $CPPFLAGS $LDFLAGS \
+ -nostartfiles -nostdlib $no_ssp \
+ -o conftest conftest.S 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD; then
+ # Get the load address of the first PT_LOAD segment.
+ libc_cv_pde_load_address=$(LC_ALL=C $READELF -Wl conftest \
+ | $AWK '/LOAD/ { print $3; exit 0; }')
+else
+ AC_MSG_ERROR([${CC-cc} can not create PDE])
+fi
+rm -f conftest*])
+LIBC_CONFIG_VAR([pde-load-address], [$libc_cv_pde_load_address])
+
+# Get the linker command-line option to load executable at a non-zero
+# load address. This is only used by glibc tests to verify that PIE and
+# static PIE with non-zero load address work correctly.
+LIBC_LINKER_FEATURE([-Ttext-segment=$libc_cv_pde_load_address],
+ [-Wl,-Ttext-segment=$libc_cv_pde_load_address],
+ [libc_cv_load_address_ldflag=-Wl,-Ttext-segment],
+ [libc_cv_load_address_ldflag=])
+LIBC_CONFIG_VAR([load-address-ldflag], [$libc_cv_load_address_ldflag])
+
AC_MSG_CHECKING(if we can build programs as PIE)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#ifdef PIE_UNSUPPORTED
# error PIE is not supported
diff --git a/elf/Makefile b/elf/Makefile
index 3a1cb72..ba6b022 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -1091,6 +1091,25 @@ tests-pie += \
tst-pie1 \
tst-pie2 \
# tests-pie
+ifneq (,$(load-address-ldflag))
+tests += \
+ tst-pie-address \
+ # tests
+tests-pie += \
+ tst-pie-address \
+ # tests-pie
+LDFLAGS-tst-pie-address += $(load-address-ldflag)=$(pde-load-address)
+ifeq (yes,$(enable-static-pie))
+tests += \
+ tst-pie-address-static \
+ # tests
+tests-static += \
+ tst-pie-address-static \
+ # tests-static
+LDFLAGS-tst-pie-address-static += \
+ $(load-address-ldflag)=$(pde-load-address)
+endif
+endif
ifeq (yes,$(have-protected-data))
tests += vismain
tests-pie += vismain
@@ -1937,6 +1956,7 @@ $(objpfx)tst-array5-static-cmp.out: tst-array5-static.exp \
CFLAGS-tst-pie1.c += $(pie-ccflag)
CFLAGS-tst-pie2.c += $(pie-ccflag)
+CFLAGS-tst-pie-address.c += $(pie-ccflag)
$(objpfx)tst-piemod1.so: $(libsupport)
$(objpfx)tst-pie1: $(objpfx)tst-piemod1.so
diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
index 10c23d0..bfee89d 100644
--- a/elf/dl-reloc-static-pie.c
+++ b/elf/dl-reloc-static-pie.c
@@ -37,21 +37,37 @@ _dl_relocate_static_pie (void)
{
struct link_map *main_map = _dl_get_dl_main_map ();
- /* Figure out the run-time load address of static PIE. */
- main_map->l_addr = elf_machine_load_address ();
-
- /* Read our own dynamic section and fill in the info array. */
- main_map->l_ld = ((void *) main_map->l_addr + elf_machine_dynamic ());
-
+ /* NB: elf_machine_load_address () returns the run-time load address
+ of static PIE. The l_addr field contains the difference between the
+ link-time load address in the ELF file and the run-time load address
+ in memory. We must subtract the link-time load address of static PIE,
+ which can be non-zero, when computing the l_addr field. Since static
+ PIE usually doesn't have PT_PHDR segment, use p_vaddr of the PT_LOAD
+ segment with offset == 0 as the load address of static PIE. */
+ ElfW(Addr) file_p_vaddr = 0;
const ElfW(Phdr) *ph, *phdr = GL(dl_phdr);
size_t phnum = GL(dl_phnum);
for (ph = phdr; ph < &phdr[phnum]; ++ph)
- if (ph->p_type == PT_DYNAMIC)
+ switch (ph->p_type)
{
+ case PT_LOAD:
+ if (ph->p_offset == 0)
+ file_p_vaddr = ph->p_vaddr;
+ break;
+ case PT_DYNAMIC:
main_map->l_ld_readonly = (ph->p_flags & PF_W) == 0;
break;
+ default:
+ break;
}
+ /* Figure out the run-time load address of static PIE. */
+ ElfW(Addr) l_addr = elf_machine_load_address ();
+ main_map->l_addr = l_addr - file_p_vaddr;
+
+ /* Read our own dynamic section and fill in the info array. */
+ main_map->l_ld = ((void *) l_addr + elf_machine_dynamic ());
+
elf_get_dynamic_info (main_map, false, true);
# ifdef ELF_MACHINE_BEFORE_RTLD_RELOC
diff --git a/elf/tst-pie-address-static.c b/elf/tst-pie-address-static.c
new file mode 100644
index 0000000..be2831e
--- /dev/null
+++ b/elf/tst-pie-address-static.c
@@ -0,0 +1,19 @@
+/* Test static PIE with non-zero load address.
+ Copyright (C) 2024 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
+ <https://www.gnu.org/licenses/>. */
+
+#include "tst-pie-address.c"
diff --git a/elf/tst-pie-address.c b/elf/tst-pie-address.c
new file mode 100644
index 0000000..aa1ca0a
--- /dev/null
+++ b/elf/tst-pie-address.c
@@ -0,0 +1,28 @@
+/* Test PIE with non-zero load address.
+ Copyright (C) 2024 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+
+static int
+do_test (void)
+{
+ printf ("Hello\n");
+ return 0;
+}
+
+#include <support/test-driver.c>