elf: Implement --preload option for the dynamic linker

This commit is contained in:
David Newall 2019-02-04 13:35:11 +01:00 committed by Florian Weimer
parent 5e10136b3c
commit 8692ebdb12
5 changed files with 89 additions and 7 deletions

View File

@ -1,3 +1,16 @@
2019-02-04 David Newall <glibc@davidnewall.com>
elf: Implement --preload option for the dynamic linker.
* elf/rtld.c (preloadarg): New variable.
(handle_preload_list): Pass through “where” argument to
do_preload.
(dl_main): Handle "--preload" and add second call to
handle_preload_list.
* elf/Makefile (tests-special): Add tst-rtld-preload.out.
(tst-rtld-preload-OBJS): Set variable.
(tst-rtld-preload.out): New target.
* elf/tst-rtld-preload.sh: New file.
2019-02-04 Matthew Malcomson <matthew.malcomson@arm.com> 2019-02-04 Matthew Malcomson <matthew.malcomson@arm.com>
* Makefile (testrun.sh): Exit in case of incorrect argument. * Makefile (testrun.sh): Exit in case of incorrect argument.

3
NEWS
View File

@ -9,7 +9,8 @@ Version 2.30
Major new features: Major new features:
[Add new features here] * The dynamic linker accepts the --preload argument to preload shared
objects, in addition to the LD_PRELOAD environment variable.
Deprecated and removed features, and other changes affecting compatibility: Deprecated and removed features, and other changes affecting compatibility:

View File

@ -354,7 +354,8 @@ endif
ifeq (yes,$(build-shared)) ifeq (yes,$(build-shared))
ifeq ($(run-built-tests),yes) ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)tst-pathopt.out $(objpfx)tst-rtld-load-self.out tests-special += $(objpfx)tst-pathopt.out $(objpfx)tst-rtld-load-self.out \
$(objpfx)tst-rtld-preload.out
endif endif
tests-special += $(objpfx)check-textrel.out $(objpfx)check-execstack.out \ tests-special += $(objpfx)check-textrel.out $(objpfx)check-execstack.out \
$(objpfx)check-localplt.out $(objpfx)check-initfini.out $(objpfx)check-localplt.out $(objpfx)check-initfini.out
@ -883,6 +884,15 @@ $(objpfx)tst-rtld-load-self.out: tst-rtld-load-self.sh $(objpfx)ld.so
$(SHELL) $^ '$(test-wrapper)' '$(test-wrapper-env)' > $@; \ $(SHELL) $^ '$(test-wrapper)' '$(test-wrapper-env)' > $@; \
$(evaluate-test) $(evaluate-test)
tst-rtld-preload-OBJS = $(subst $(empty) ,:,$(strip $(preloadtest-preloads:=.so)))
$(objpfx)tst-rtld-preload.out: tst-rtld-preload.sh $(objpfx)ld.so \
$(objpfx)preloadtest \
$(preloadtest-preloads:%=$(objpfx)%.so)
$(SHELL) $< $(objpfx)ld.so $(objpfx)preloadtest \
'$(test-wrapper)' '$(test-wrapper-env)' '$(run_program_env)' \
'$(rpath-link)' '$(tst-rtld-preload-OBJS)' > $@; \
$(evaluate-test)
$(objpfx)initfirst: $(libdl) $(objpfx)initfirst: $(libdl)
$(objpfx)initfirst.out: $(objpfx)firstobj.so $(objpfx)initfirst.out: $(objpfx)firstobj.so

View File

@ -826,15 +826,18 @@ static const char *library_path attribute_relro;
static const char *preloadlist attribute_relro; static const char *preloadlist attribute_relro;
/* Nonzero if information about versions has to be printed. */ /* Nonzero if information about versions has to be printed. */
static int version_info attribute_relro; static int version_info attribute_relro;
/* The preload list passed as a command argument. */
static const char *preloadarg attribute_relro;
/* The LD_PRELOAD environment variable gives list of libraries /* The LD_PRELOAD environment variable gives list of libraries
separated by white space or colons that are loaded before the separated by white space or colons that are loaded before the
executable's dependencies and prepended to the global scope list. executable's dependencies and prepended to the global scope list.
(If the binary is running setuid all elements containing a '/' are (If the binary is running setuid all elements containing a '/' are
ignored since it is insecure.) Return the number of preloads ignored since it is insecure.) Return the number of preloads
performed. */ performed. Ditto for --preload command argument. */
unsigned int unsigned int
handle_ld_preload (const char *preloadlist, struct link_map *main_map) handle_preload_list (const char *preloadlist, struct link_map *main_map,
const char *where)
{ {
unsigned int npreloads = 0; unsigned int npreloads = 0;
const char *p = preloadlist; const char *p = preloadlist;
@ -858,7 +861,7 @@ handle_ld_preload (const char *preloadlist, struct link_map *main_map)
++p; ++p;
if (dso_name_valid_for_suid (fname)) if (dso_name_valid_for_suid (fname))
npreloads += do_preload (fname, main_map, "LD_PRELOAD"); npreloads += do_preload (fname, main_map, where);
} }
return npreloads; return npreloads;
} }
@ -974,6 +977,13 @@ dl_main (const ElfW(Phdr) *phdr,
{ {
process_dl_audit (_dl_argv[2]); process_dl_audit (_dl_argv[2]);
_dl_skip_args += 2;
_dl_argc -= 2;
_dl_argv += 2;
}
else if (! strcmp (_dl_argv[1], "--preload") && _dl_argc > 2)
{
preloadarg = _dl_argv[2];
_dl_skip_args += 2; _dl_skip_args += 2;
_dl_argc -= 2; _dl_argc -= 2;
_dl_argv += 2; _dl_argv += 2;
@ -1006,7 +1016,8 @@ of this helper program; chances are you did not intend to run this program.\n\
variable LD_LIBRARY_PATH\n\ variable LD_LIBRARY_PATH\n\
--inhibit-rpath LIST ignore RUNPATH and RPATH information in object names\n\ --inhibit-rpath LIST ignore RUNPATH and RPATH information in object names\n\
in LIST\n\ in LIST\n\
--audit LIST use objects named in LIST as auditors\n"); --audit LIST use objects named in LIST as auditors\n\
--preload LIST preload objects named in LIST\n");
++_dl_skip_args; ++_dl_skip_args;
--_dl_argc; --_dl_argc;
@ -1620,7 +1631,16 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
if (__glibc_unlikely (preloadlist != NULL)) if (__glibc_unlikely (preloadlist != NULL))
{ {
HP_TIMING_NOW (start); HP_TIMING_NOW (start);
npreloads += handle_ld_preload (preloadlist, main_map); npreloads += handle_preload_list (preloadlist, main_map, "LD_PRELOAD");
HP_TIMING_NOW (stop);
HP_TIMING_DIFF (diff, start, stop);
HP_TIMING_ACCUM_NT (load_time, diff);
}
if (__glibc_unlikely (preloadarg != NULL))
{
HP_TIMING_NOW (start);
npreloads += handle_preload_list (preloadarg, main_map, "--preload");
HP_TIMING_NOW (stop); HP_TIMING_NOW (stop);
HP_TIMING_DIFF (diff, start, stop); HP_TIMING_DIFF (diff, start, stop);
HP_TIMING_ACCUM_NT (load_time, diff); HP_TIMING_ACCUM_NT (load_time, diff);

38
elf/tst-rtld-preload.sh Executable file
View File

@ -0,0 +1,38 @@
#!/bin/sh
# Test --preload argument ld.so.
# Copyright (C) 2019 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
# <http://www.gnu.org/licenses/>.
set -e
rtld=$1
test_program=$2
test_wrapper=$3
test_wrapper_env=$4
run_program_env=$5
library_path=$6
preload=$7
echo "# [${test_wrapper}] [$rtld] [--library-path] [$library_path]" \
"[--preload] [$preload] [$test_program]"
${test_wrapper_env} \
${run_program_env} \
${test_wrapper} $rtld --library-path "$library_path" \
--preload "$preload" $test_program 2>&1 && rc=0 || rc=$?
echo "# exit status $rc"
exit $rc