[PATCH v7] elf: Handle static PIE with non-zero load address [BZ #31799]
Noah Goldstein
goldstein.w.n@gmail.com
Tue Oct 29 05:33:40 GMT 2024
On Mon, Oct 28, 2024 at 3:11 PM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> 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>
> ---
> configure | 74 ++++++++++++++++++++++++++++++++++++
> configure.ac | 36 ++++++++++++++++++
> elf/Makefile | 20 ++++++++++
> elf/dl-reloc-static-pie.c | 30 +++++++++++----
> elf/tst-pie-address-static.c | 19 +++++++++
> elf/tst-pie-address.c | 28 ++++++++++++++
> 6 files changed, 200 insertions(+), 7 deletions(-)
> create mode 100644 elf/tst-pie-address-static.c
> create mode 100644 elf/tst-pie-address.c
>
> diff --git a/configure b/configure
> index 9c0c0dce03..9bcf62dca5 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 d5a00461ff..895bd5267d 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 9cfe738919..4eaca7af67 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 10c23d0bf0..bfee89dfc3 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 0000000000..be2831e9d6
> --- /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 0000000000..aa1ca0a9fd
> --- /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>
> --
> 2.47.0
>
LGTM.
Think Carlos was also going to take a look at this (from monday review
minutes), so please wait for Carlos to take a look too before pushing.
More information about the Libc-alpha
mailing list