diff options
Diffstat (limited to 'elf')
111 files changed, 7998 insertions, 1771 deletions
diff --git a/elf/Makefile b/elf/Makefile index 028be25..06e376d 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -1,4 +1,4 @@ -# Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. +# Copyright (C) 1995-2004, 2005, 2006 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 @@ -20,8 +20,8 @@ subdir := elf -headers = elf.h bits/elfclass.h link.h -routines = $(dl-routines) dl-open dl-close dl-support dl-iteratephdr \ +headers = elf.h bits/elfclass.h link.h bits/link.h +routines = $(dl-routines) dl-support dl-iteratephdr \ dl-addr enbl-secure dl-profstub \ dl-origin dl-libc dl-sym dl-tsd @@ -30,7 +30,7 @@ routines = $(dl-routines) dl-open dl-close dl-support dl-iteratephdr \ dl-routines = $(addprefix dl-,load cache lookup object reloc deps \ runtime error init fini debug misc \ version profile conflict tls origin \ - execstack caller) + execstack caller open close trampoline) all-dl-routines = $(dl-routines) $(sysdep-dl-routines) # But they are absent from the shared libc, because that code is in ld.so. elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin @@ -72,7 +72,7 @@ distribute := rtld-Rules \ tst-tlsmod1.c tst-tlsmod2.c tst-tlsmod3.c tst-tlsmod4.c \ tst-tlsmod5.c tst-tlsmod6.c tst-tlsmod7.c tst-tlsmod8.c \ tst-tlsmod9.c tst-tlsmod10.c tst-tlsmod11.c \ - tst-tlsmod12.c tst-tls10.h tst-alignmod.c \ + tst-tlsmod12.c tst-tls10.h tst-alignmod.c tst-alignmod2.c \ circlemod1.c circlemod1a.c circlemod2.c circlemod2a.c \ circlemod3.c circlemod3a.c nodlopenmod2.c \ tls-macros.h \ @@ -83,7 +83,16 @@ distribute := rtld-Rules \ tst-array2dep.c tst-piemod1.c \ tst-execstack-mod.c tst-dlmodcount.c \ check-textrel.c dl-sysdep.h test-dlopenrpathmod.c \ - tst-deep1mod1.c tst-deep1mod2.c tst-deep1mod3.c + tst-deep1mod1.c tst-deep1mod2.c tst-deep1mod3.c \ + unload3mod1.c unload3mod2.c unload3mod3.c unload3mod4.c \ + unload4mod1.c unload4mod2.c unload4mod3.c unload4mod4.c \ + unload6mod1.c unload6mod2.c unload6mod3.c \ + unload7mod1.c unload7mod2.c \ + tst-auditmod1.c tst-audit.sh \ + order2mod1.c order2mod2.c order2mod3.c order2mod4.c \ + tst-stackguard1.c tst-stackguard1-static.c \ + tst-array5.c tst-array5-static.c tst-array5dep.c \ + tst-array5.exp tst-leaks1.c CFLAGS-dl-runtime.c = -fexceptions -fasynchronous-unwind-tables CFLAGS-dl-lookup.c = -fexceptions -fasynchronous-unwind-tables @@ -131,16 +140,19 @@ vpath %.c ../locale/programs endif endif -tests = tst-tls1 tst-tls2 tst-tls9 +tests = tst-tls1 tst-tls2 tst-tls9 tst-leaks1 ifeq (yes,$(have-initfini-array)) -tests += tst-array1 tst-array2 tst-array3 tst-array4 +tests += tst-array1 tst-array2 tst-array3 tst-array4 tst-array5 endif ifeq (yes,$(build-static)) -tests-static = tst-tls1-static tst-tls2-static +tests-static = tst-tls1-static tst-tls2-static tst-stackguard1-static ifeq (yesyesyes,$(build-static)$(build-shared)$(elf)) tests-static += tst-tls9-static tst-tls9-static-ENV = \ - LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn + LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn +endif +ifeq (yes,$(have-initfini-array)) +tests-static += tst-array1-static tst-array5-static endif tests += $(tests-static) endif @@ -152,9 +164,14 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \ neededtest3 neededtest4 unload2 lateglobal initfirst global \ restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \ circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8 \ - tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-align \ - $(tests-execstack-$(have-z-execstack)) tst-dlmodcount \ - tst-dlopenrpath tst-deep1 tst-dlmopen1 tst-dlmopen2 tst-dlmopen3 + tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-tls15 \ + tst-tls-dlinfo \ + tst-align tst-align2 $(tests-execstack-$(have-z-execstack)) \ + tst-dlmodcount tst-dlopenrpath tst-deep1 \ + tst-dlmopen1 tst-dlmopen2 tst-dlmopen3 \ + unload3 unload4 unload5 unload6 unload7 tst-global1 order2 \ + tst-audit1 tst-audit2 \ + tst-stackguard1 tst-addr1 tst-thrlock # reldep9 test-srcs = tst-pathopt tests-vis-yes = vismain @@ -165,6 +182,7 @@ endif ifeq (yesyes,$(have-fpie)$(build-shared)) tests: $(objpfx)tst-pie1.out endif +tests: $(objpfx)tst-leaks1-mem modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ testobj1_1 failobj constload2 constload3 unloadmod \ dep1 dep2 dep3 dep4 $(modules-vis-$(have-protected)) \ @@ -182,15 +200,22 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ tst-tlsmod5 tst-tlsmod6 tst-tlsmod7 tst-tlsmod8 \ tst-tlsmod9 tst-tlsmod10 tst-tlsmod11 tst-tlsmod12 \ tst-tlsmod13 tst-tlsmod13a tst-tlsmod14a tst-tlsmod14b \ + tst-tlsmod15a tst-tlsmod15b \ circlemod1 circlemod1a circlemod2 circlemod2a \ circlemod3 circlemod3a \ reldep8mod1 reldep8mod2 reldep8mod3 \ reldep9mod1 reldep9mod2 reldep9mod3 \ - tst-alignmod $(modules-execstack-$(have-z-execstack)) \ + tst-alignmod tst-alignmod2 \ + $(modules-execstack-$(have-z-execstack)) \ tst-dlopenrpathmod tst-deep1mod1 tst-deep1mod2 tst-deep1mod3 \ - tst-dlmopen1mod + tst-dlmopen1mod tst-auditmod1 \ + unload3mod1 unload3mod2 unload3mod3 unload3mod4 \ + unload4mod1 unload4mod2 unload4mod3 unload4mod4 \ + unload6mod1 unload6mod2 unload6mod3 \ + unload7mod1 unload7mod2 \ + order2mod1 order2mod2 order2mod3 order2mod4 ifeq (yes,$(have-initfini-array)) -modules-names += tst-array2dep +modules-names += tst-array2dep tst-array5dep endif ifeq (yesyes,$(have-fpie)$(build-shared)) modules-names += tst-piemod1 @@ -200,10 +225,14 @@ modules-nodelete-yes = nodelmod1 nodelmod2 nodelmod3 nodelmod4 \ nodel2mod1 nodel2mod2 nodel2mod3 modules-nodlopen-yes = nodlopenmod nodlopenmod2 modules-execstack-yes = tst-execstack-mod -extra-objs += $(addsuffix .os,$(strip $(modules-names))) +extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) # We need this variable to be sure the test modules get the right CPPFLAGS. test-extras += $(modules-names) +# filtmod1.so has a special rule +modules-names-nobuild := filtmod1 + + include ../Rules check-abi: check-abi-ld @@ -420,6 +449,16 @@ $(objpfx)reldep8mod3.so: $(objpfx)reldep8mod1.so $(objpfx)reldep8mod2.so $(objpfx)nodel2mod3.so: $(objpfx)nodel2mod1.so $(objpfx)nodel2mod2.so $(objpfx)reldep9mod2.so: $(objpfx)reldep9mod1.so $(objpfx)reldep9mod3.so: $(objpfx)reldep9mod1.so $(objpfx)reldep9mod2.so +$(objpfx)unload3mod1.so: $(objpfx)unload3mod3.so +$(objpfx)unload3mod2.so: $(objpfx)unload3mod3.so +$(objpfx)unload3mod3.so: $(objpfx)unload3mod4.so +$(objpfx)unload4mod1.so: $(objpfx)unload4mod2.so $(objpfx)unload4mod3.so +$(objpfx)unload4mod2.so: $(objpfx)unload4mod4.so $(objpfx)unload4mod3.so +$(objpfx)unload6mod1.so: $(libdl) +$(objpfx)unload6mod2.so: $(libdl) +$(objpfx)unload6mod3.so: $(libdl) +$(objpfx)unload7mod1.so: $(libdl) +$(objpfx)unload7mod2.so: $(objpfx)unload7mod1.so LDFLAGS-tst-tlsmod5.so = -nostdlib LDFLAGS-tst-tlsmod6.so = -nostdlib @@ -454,15 +493,14 @@ tst-tlsmod10.so-no-z-defs = yes tst-tlsmod12.so-no-z-defs = yes tst-tlsmod14a.so-no-z-defs = yes tst-tlsmod14b.so-no-z-defs = yes +tst-tlsmod15a.so-no-z-defs = yes circlemod2.so-no-z-defs = yes circlemod3.so-no-z-defs = yes circlemod3a.so-no-z-defs = yes reldep8mod2.so-no-z-defs = yes reldep9mod1.so-no-z-defs = yes - -# filtmod1.so has a special rule -$(filter-out $(objpfx)filtmod1.so, $(test-modules)): $(objpfx)%.so: $(objpfx)%.os - $(build-module) +unload3mod4.so-no-z-defs = yes +unload4mod1.so-no-z-defs = yes ifeq ($(build-shared),yes) # Build all the modules even when not actually running test programs. @@ -617,7 +655,7 @@ $(objpfx)dblunload: $(libdl) $(objpfx)dblunload.out: $(objpfx)dblloadmod1.so $(objpfx)dblloadmod2.so $(objpfx)reldep5: $(libdl) -$(objpfx)reldep5.out: $(objpfx)reldepmod5.so $(objpfx)reldepmod5.so +$(objpfx)reldep5.out: $(objpfx)reldepmod5.so $(objpfx)reldepmod6.so $(objpfx)reldep6: $(libdl) $(objpfx)reldep6.out: $(objpfx)reldep6mod3.so $(objpfx)reldep6mod4.so @@ -664,13 +702,43 @@ $(objpfx)tst-tls12: $(objpfx)tst-tlsmod12.so $(objpfx)tst-tls13: $(libdl) $(objpfx)tst-tls13.out: $(objpfx)tst-tlsmod13a.so -$(objpfx)tst-tls14: $(objpfx)tst-tlsmod14a.so $(libdl) -$(objpfx)tst-tls14.out:$(objpfx)tst-tlsmod14b.so +$(objpfx)tst-tls14: $(objpfx)tst-tlsmod14a.so $(libdl) +$(objpfx)tst-tls14.out: $(objpfx)tst-tlsmod14b.so + +$(objpfx)tst-tls15: $(libdl) +$(objpfx)tst-tls15.out: $(objpfx)tst-tlsmod15a.so $(objpfx)tst-tlsmod15b.so + +$(objpfx)tst-tls-dlinfo: $(libdl) +$(objpfx)tst-tls-dlinfo.out: $(objpfx)tst-tlsmod2.so + + CFLAGS-tst-align.c = $(stack-align-test-flags) +CFLAGS-tst-align2.c = $(stack-align-test-flags) CFLAGS-tst-alignmod.c = $(stack-align-test-flags) +CFLAGS-tst-alignmod2.c = $(stack-align-test-flags) $(objpfx)tst-align: $(libdl) $(objpfx)tst-align.out: $(objpfx)tst-alignmod.so +$(objpfx)tst-align2: $(objpfx)tst-alignmod2.so + +$(objpfx)unload3: $(libdl) +$(objpfx)unload3.out: $(objpfx)unload3mod1.so $(objpfx)unload3mod2.so \ + $(objpfx)unload3mod3.so $(objpfx)unload3mod4.so + +$(objpfx)unload4: $(libdl) +$(objpfx)unload4.out: $(objpfx)unload4mod1.so $(objpfx)unload4mod3.so + +$(objpfx)unload5: $(libdl) +$(objpfx)unload5.out: $(objpfx)unload3mod1.so $(objpfx)unload3mod2.so \ + $(objpfx)unload3mod3.so $(objpfx)unload3mod4.so + +$(objpfx)unload6: $(libdl) +$(objpfx)unload6.out: $(objpfx)unload6mod1.so $(objpfx)unload6mod2.so \ + $(objpfx)unload6mod3.so + +$(objpfx)unload7: $(libdl) +$(objpfx)unload7.out: $(objpfx)unload7mod1.so $(objpfx)unload7mod2.so +unload7-ENV = MALLOC_PERTURB_=85 ifdef libdl $(objpfx)tst-tls9-static: $(common-objpfx)dlfcn/libdl.a @@ -695,6 +763,10 @@ $(objpfx)tst-array1.out: $(objpfx)tst-array1 $(objpfx)tst-array1 > $@ cmp $@ tst-array1.exp > /dev/null +$(objpfx)tst-array1-static.out: $(objpfx)tst-array1-static + $(objpfx)tst-array1-static > $@ + cmp $@ tst-array1.exp > /dev/null + $(objpfx)tst-array2: $(objpfx)tst-array2dep.so $(objpfx)tst-array2.out: $(objpfx)tst-array2 $(elf-objpfx)$(rtld-installed-name) \ @@ -715,6 +787,17 @@ $(objpfx)tst-array4.out: $(objpfx)tst-array4 $(objpfx)tst-array2dep.so $< > $@ cmp $@ tst-array4.exp > /dev/null +$(objpfx)tst-array5: $(objpfx)tst-array5dep.so +$(objpfx)tst-array5.out: $(objpfx)tst-array5 + $(elf-objpfx)$(rtld-installed-name) \ + --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \ + $(objpfx)tst-array5 > $@ + cmp $@ tst-array5.exp > /dev/null + +$(objpfx)tst-array5-static.out: $(objpfx)tst-array5-static + $(objpfx)tst-array5-static > $@ + cmp $@ tst-array5-static.exp > /dev/null + ifeq (yesyes,$(have-fpie)$(build-shared)) CFLAGS-tst-pie1.c += -fpie @@ -732,12 +815,18 @@ $(objpfx)tst-pie1: $(objpfx)tst-pie1.o $(objpfx)tst-piemod1.so -L$(subst :, -L,$(rpath-link)) -Wl,-rpath-link=$(rpath-link) \ -o $@ $(objpfx)tst-pie1.o $(objpfx)tst-piemod1.so \ $(common-objpfx)libc_nonshared.a + +generated += tst-pie1 tst-pie1.out tst-pie1.o endif check-textrel-CFLAGS = -O -Wall -D_XOPEN_SOURCE=600 -D_BSD_SOURCE $(objpfx)check-textrel: check-textrel.c $(native-compile) +check-localplt-CFLAGS = -O -Wall -D_GNU_SOURCE -std=gnu99 +$(objpfx)check-localplt: check-localplt.c + $(native-compile) + ifeq (yes,$(build-shared)) tests: $(objpfx)check-textrel.out @@ -750,6 +839,28 @@ generated += check-textrel check-textrel.out $(objpfx)tst-dlmodcount: $(libdl) $(objpfx)tst-dlmodcount.out: $(test-modules) +check-data := $(firstword $(wildcard \ + $(foreach M,$(config-machine) $(base-machine),\ + ../scripts/data/localplt-$M-$(config-os).data))) +ifneq (,$(check-data)) +tests: $(objpfx)check-localplt.out + +ifeq ($(have-thread-library),yes) +thread-dso := $(filter-out %_nonshared.a, $(shared-thread-library)) +endif + +$(objpfx)check-localplt.out: $(objpfx)check-localplt $(common-objpfx)libc.so \ + $(common-objpfx)math/libm.so $(thread-dso) \ + $(common-objpfx)rt/librt.so \ + $(common-objpfx)dlfcn/libdl.so \ + $(check-data) + $(objpfx)check-localplt $(common-objpfx)libc.so \ + $(common-objpfx)math/libm.so $(thread-dso) \ + $(common-objpfx)rt/librt.so \ + $(common-objpfx)dlfcn/libdl.so | \ + LC_ALL=C sort | \ + diff -u $(check-data) - > $@ +endif endif $(objpfx)tst-dlopenrpathmod.so: $(libdl) @@ -773,3 +884,37 @@ $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so $(objpfx)tst-dlmopen3: $(libdl) $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so + +$(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so +tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + +$(objpfx)tst-audit2.out: $(objpfx)tst-auditmod1.so +tst-audit2-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + +$(objpfx)tst-global1: $(libdl) +$(objpfx)tst-global1.out: $(objpfx)testobj6.so $(objpfx)testobj2.so + +$(objpfx)order2: $(libdl) +$(objpfx)order2.out: $(objpfx)order2 $(objpfx)order2mod1.so \ + $(objpfx)order2mod2.so + $(elf-objpfx)$(rtld-installed-name) \ + --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \ + $(objpfx)order2 > $@ + (echo "12345" | cmp $@ -) > /dev/null +$(objpfx)order2mod1.so: $(objpfx)order2mod4.so +$(objpfx)order2mod4.so: $(objpfx)order2mod3.so +$(objpfx)order2mod2.so: $(objpfx)order2mod3.so +order2mod2.so-no-z-defs = yes + +tst-stackguard1-ARGS = --command "$(built-program-cmd) --child" +tst-stackguard1-static-ARGS = --command "$(objpfx)tst-stackguard1-static --child" + +$(objpfx)tst-leaks1: $(libdl) +$(objpfx)tst-leaks1-mem: $(objpfx)tst-leaks1.out + $(common-objpfx)malloc/mtrace $(objpfx)tst-leaks1.mtrace > $@ + +tst-leaks1-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1.mtrace + +$(objpfx)tst-addr1: $(libdl) + +$(objpfx)tst-thrlock: $(libdl) $(shared-thread-library) diff --git a/elf/Versions b/elf/Versions index e24b2de..967ebdb 100644 --- a/elf/Versions +++ b/elf/Versions @@ -19,7 +19,7 @@ libc { %endif GLIBC_PRIVATE { # functions used in other libraries - _dl_open; _dl_close; _dl_addr; + _dl_addr; _dl_sym; _dl_vsym; _dl_open_hook; __libc_dlopen_mode; __libc_dlsym; __libc_dlclose; @@ -43,6 +43,10 @@ ld { # runtime interface to TLS __tls_get_addr; } + GLIBC_2.4 { + # stack canary + __stack_chk_guard; + } GLIBC_PRIVATE { # Those are in the dynamic linker, but used by libc.so. __libc_enable_secure; @@ -53,8 +57,11 @@ ld { _dl_allocate_tls; _dl_deallocate_tls; _dl_get_tls_static_info; _dl_allocate_tls_init; _dl_tls_setup; _dl_rtld_di_serinfo; + _dl_tls_get_addr_soft; _dl_make_stack_executable; # Only here for gdb while a better method is developed. _dl_debug_state; + # Pointer protection. + __pointer_chk_guard; } } diff --git a/elf/cache.c b/elf/cache.c index 22ad55c..6730fb3 100644 --- a/elf/cache.c +++ b/elf/cache.c @@ -1,22 +1,19 @@ -/* Copyright (C) 1999, 2000, 2001, 2002, 2003 - Free Software Foundation, Inc. +/* Copyright (C) 1999-2003,2005,2006 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Andreas Jaeger <aj@suse.de>, 1999. - 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. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. - The GNU C Library is distributed in the hope that it will be useful, + This program 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. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <errno.h> #include <error.h> @@ -32,8 +29,8 @@ #include <sys/stat.h> #include <sys/types.h> -#include "ldconfig.h" -#include "dl-cache.h" +#include <ldconfig.h> +#include <dl-cache.h> struct cache_entry { @@ -99,7 +96,7 @@ print_entry (const char *lib, int flag, unsigned int osversion, break; } if (hwcap != 0) - printf (", hwcap: 0x%" PRIx64, hwcap); + printf (", hwcap: %#.16" PRIx64, hwcap); if (osversion != 0) { static const char *const abi_tag_os[] = @@ -108,7 +105,9 @@ print_entry (const char *lib, int flag, unsigned int osversion, [1] = "Hurd", [2] = "Solaris", [3] = "FreeBSD", - [4] = N_("Unknown OS") + [4] = "kNetBSD", + [5] = "Syllable", + [6] = N_("Unknown OS") }; #define MAXTAG (sizeof abi_tag_os / sizeof abi_tag_os[0] - 1) unsigned int os = osversion >> 24; @@ -421,7 +420,7 @@ save_cache (const char *cache_name) if (opt_format != 2) { if (write (fd, file_entries, file_entries_size) - != (ssize_t)file_entries_size) + != (ssize_t) file_entries_size) error (EXIT_FAILURE, errno, _("Writing of cache data failed")); } if (opt_format != 0) @@ -430,15 +429,16 @@ save_cache (const char *cache_name) if (opt_format != 2) { char zero[pad]; - if (write (fd, zero, pad) != (ssize_t)pad) + memset (zero, '\0', pad); + if (write (fd, zero, pad) != (ssize_t) pad) error (EXIT_FAILURE, errno, _("Writing of cache data failed")); } if (write (fd, file_entries_new, file_entries_new_size) - != (ssize_t)file_entries_new_size) + != (ssize_t) file_entries_new_size) error (EXIT_FAILURE, errno, _("Writing of cache data failed")); } - if (write (fd, strings, total_strlen) != (ssize_t)total_strlen) + if (write (fd, strings, total_strlen) != (ssize_t) total_strlen) error (EXIT_FAILURE, errno, _("Writing of cache data failed.")); close (fd); @@ -455,6 +455,7 @@ save_cache (const char *cache_name) cache_name); /* Free all allocated memory. */ + free (file_entries_new); free (file_entries); free (strings); diff --git a/elf/check-localplt.c b/elf/check-localplt.c new file mode 100644 index 0000000..b4358a8 --- /dev/null +++ b/elf/check-localplt.c @@ -0,0 +1,299 @@ +/* Show local PLT use in DSOs. + Copyright (C) 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contribute by Ulrich Drepper <drepper@redhat.com>. 2006. + + 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <byteswap.h> +#include <elf.h> +#include <endian.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +#ifdef BITS + +# define AB(name) _AB (name, BITS) +# define _AB(name, bits) __AB (name, bits) +# define __AB(name, bits) name##bits +# define E(name) _E (name, BITS) +# define _E(name, bits) __E (name, bits) +# define __E(name, bits) Elf##bits##_##name +# define EE(name) _EE (name, BITS) +# define _EE(name, bits) __EE (name, bits) +# define __EE(name, bits) ELF##bits##_##name +# define SWAP(val) \ + ({ __typeof (val) __res; \ + if (((ehdr.e_ident[EI_DATA] == ELFDATA2MSB \ + && BYTE_ORDER == LITTLE_ENDIAN) \ + || (ehdr.e_ident[EI_DATA] == ELFDATA2LSB \ + && BYTE_ORDER == BIG_ENDIAN)) \ + && sizeof (val) != 1) \ + { \ + if (sizeof (val) == 2) \ + __res = bswap_16 (val); \ + else if (sizeof (val) == 4) \ + __res = bswap_32 (val); \ + else \ + __res = bswap_64 (val); \ + } \ + else \ + __res = (val); \ + __res; }) + + +static int +AB(handle_file) (const char *fname, int fd) +{ + E(Ehdr) ehdr; + + if (pread (fd, &ehdr, sizeof (ehdr), 0) != sizeof (ehdr)) + { + read_error: + printf ("%s: read error: %m\n", fname); + return 1; + } + + const size_t phnum = SWAP (ehdr.e_phnum); + const size_t phentsize = SWAP (ehdr.e_phentsize); + + /* Read the program header. */ + E(Phdr) *phdr = alloca (phentsize * phnum); + if (pread (fd, phdr, phentsize * phnum, SWAP (ehdr.e_phoff)) + != phentsize * phnum) + goto read_error; + + /* Search for the PT_DYNAMIC entry. */ + size_t cnt; + E(Phdr) *dynphdr = NULL; + for (cnt = 0; cnt < phnum; ++cnt) + if (SWAP (phdr[cnt].p_type) == PT_DYNAMIC) + { + dynphdr = &phdr[cnt]; + break; + } + + if (dynphdr == NULL) + { + printf ("%s: no DYNAMIC segment found\n", fname); + return 1; + } + + /* Read the dynamic segment. */ + size_t pmemsz = SWAP(dynphdr->p_memsz); + E(Dyn) *dyn = alloca (pmemsz); + if (pread64 (fd, dyn, pmemsz, SWAP(dynphdr->p_offset)) != pmemsz) + goto read_error; + + /* Search for an DT_PLTREL, DT_JMPREL, DT_PLTRELSZ, DT_STRTAB, + DT_STRSZ, and DT_SYMTAB entries. */ + size_t pltrel_idx = SIZE_MAX; + size_t jmprel_idx = SIZE_MAX; + size_t pltrelsz_idx = SIZE_MAX; + size_t strtab_idx = SIZE_MAX; + size_t strsz_idx = SIZE_MAX; + size_t symtab_idx = SIZE_MAX; + for (cnt = 0; (cnt + 1) * sizeof (E(Dyn)) - 1 < pmemsz; ++cnt) + { + unsigned int tag = SWAP (dyn[cnt].d_tag); + + if (tag == DT_NULL) + /* We reached the end. */ + break; + + if (tag == DT_PLTREL) + pltrel_idx = cnt; + else if (tag == DT_JMPREL) + jmprel_idx = cnt; + else if (tag == DT_PLTRELSZ) + pltrelsz_idx = cnt; + else if (tag == DT_STRTAB) + strtab_idx = cnt; + else if (tag == DT_STRSZ) + strsz_idx = cnt; + else if (tag == DT_SYMTAB) + symtab_idx = cnt; + } + + if (pltrel_idx == SIZE_MAX || jmprel_idx == SIZE_MAX + || pltrelsz_idx == SIZE_MAX || strtab_idx == SIZE_MAX + || strsz_idx == SIZE_MAX || symtab_idx == SIZE_MAX) + { + puts ("not all PLT information found"); + return 1; + } + + E(Xword) relsz = SWAP (dyn[pltrelsz_idx].d_un.d_val); + + void *relmem = NULL; + char *strtab = NULL; + E(Xword) symtab_offset = 0; + + /* Find the offset of DT_JMPREL and load the data. */ + for (cnt = 0; cnt < phnum; ++cnt) + if (SWAP (phdr[cnt].p_type) == PT_LOAD) + { + E(Addr) vaddr = SWAP (phdr[cnt].p_vaddr); + E(Xword) memsz = SWAP (phdr[cnt].p_memsz); + + if (vaddr <= SWAP (dyn[jmprel_idx].d_un.d_val) + && vaddr + memsz >= SWAP (dyn[jmprel_idx].d_un.d_val) + relsz) + { + relmem = alloca (SWAP (dyn[pltrelsz_idx].d_un.d_val)); + if (pread64 (fd, relmem, relsz, + SWAP (phdr[cnt].p_offset) + + SWAP (dyn[jmprel_idx].d_un.d_val) - vaddr) + != relsz) + { + puts ("cannot read JMPREL"); + return 1; + } + } + + if (vaddr <= SWAP (dyn[symtab_idx].d_un.d_val) + && vaddr + memsz > SWAP (dyn[symtab_idx].d_un.d_val)) + symtab_offset = (SWAP (phdr[cnt].p_offset) + + SWAP (dyn[symtab_idx].d_un.d_val) - vaddr); + + if (vaddr <= SWAP (dyn[strtab_idx].d_un.d_val) + && vaddr + memsz >= (SWAP (dyn[strtab_idx].d_un.d_val) + + SWAP(dyn[strsz_idx].d_un.d_val))) + { + strtab = alloca (SWAP(dyn[strsz_idx].d_un.d_val)); + if (pread64 (fd, strtab, SWAP(dyn[strsz_idx].d_un.d_val), + SWAP (phdr[cnt].p_offset) + + SWAP (dyn[strtab_idx].d_un.d_val) - vaddr) + != SWAP(dyn[strsz_idx].d_un.d_val)) + { + puts ("cannot read STRTAB"); + return 1; + } + } + } + + if (relmem == NULL || strtab == NULL || symtab_offset == 0) + { + puts ("couldn't load PLT data"); + return 1; + } + + if (SWAP (dyn[pltrel_idx].d_un.d_val) == DT_RELA) + for (E(Rela) *rela = relmem; (char *) rela - (char *) relmem < relsz; + ++rela) + { + E(Sym) sym; + + if (pread64 (fd, &sym, sizeof (sym), + symtab_offset + + EE(R_SYM) (SWAP (rela->r_info)) * sizeof (sym)) + != sizeof (sym)) + { + puts ("cannot read symbol"); + return 1; + } + + if (sym.st_value != 0) + /* This symbol is locally defined. */ + printf ("%s: %s\n", basename (fname), strtab + SWAP (sym.st_name)); + } + else + for (E(Rel) *rel = relmem; (char *) rel - (char *) relmem < relsz; ++rel) + { + E(Sym) sym; + + if (pread64 (fd, &sym, sizeof (sym), + symtab_offset + + EE(R_SYM) (SWAP (rel->r_info)) * sizeof (sym)) + != sizeof (sym)) + { + puts ("cannot read symbol"); + return 1; + } + + if (sym.st_value != 0) + /* This symbol is locally defined. */ + printf ("%s: %s\n", basename (fname), strtab + SWAP (sym.st_name)); + } + + return 0; +} + +# undef BITS +#else + +# define BITS 32 +# include "check-localplt.c" + +# define BITS 64 +# include "check-localplt.c" + + +static int +handle_file (const char *fname) +{ + int fd = open (fname, O_RDONLY); + if (fd == -1) + { + printf ("cannot open %s: %m\n", fname); + return 1; + } + + /* Read was is supposed to be the ELF header. Read the initial + bytes to determine whether this is a 32 or 64 bit file. */ + char ident[EI_NIDENT]; + if (read (fd, ident, EI_NIDENT) != EI_NIDENT) + { + printf ("%s: read error: %m\n", fname); + close (fd); + return 1; + } + + if (memcmp (&ident[EI_MAG0], ELFMAG, SELFMAG) != 0) + { + printf ("%s: not an ELF file\n", fname); + close (fd); + return 1; + } + + int result; + if (ident[EI_CLASS] == ELFCLASS64) + result = handle_file64 (fname, fd); + else + result = handle_file32 (fname, fd); + + close (fd); + + return result; +} + + +int +main (int argc, char *argv[]) +{ + int cnt; + int result = 0; + + for (cnt = 1; cnt < argc; ++cnt) + result |= handle_file (argv[cnt]); + + return result; +} +#endif diff --git a/elf/check-textrel.c b/elf/check-textrel.c index ec97e4b..1a9a5ec 100644 --- a/elf/check-textrel.c +++ b/elf/check-textrel.c @@ -1,5 +1,5 @@ /* Check for text relocations in DSOs. - Copyright (C) 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. Contribute by Ulrich Drepper <drepper@redhat.com>. 2002. @@ -79,20 +79,36 @@ AB(handle_file) (const char *fname, int fd) /* Search for the PT_DYNAMIC entry. */ size_t cnt; + E(Phdr) *dynphdr = NULL; for (cnt = 0; cnt < phnum; ++cnt) if (SWAP (phdr[cnt].p_type) == PT_DYNAMIC) - break; + dynphdr = &phdr[cnt]; + else if (SWAP (phdr[cnt].p_type) == PT_LOAD + && (SWAP (phdr[cnt].p_flags) & (PF_X | PF_W)) == (PF_X | PF_W)) + { + printf ("%s: segment %zu is executable and writable\n", + fname, cnt); +#if !defined __sparc__ \ + && !defined __alpha__ \ + && (!defined __powerpc__ || defined __powerpc64__ || defined HAVE_PPC_SECURE_PLT) + /* sparc, sparc64, alpha and powerpc32 (the last one only when using + -mbss-plt) are expected to have PF_X | PF_W segment containing .plt + section, it is part of their ABI. It is bad security wise, nevertheless + this test shouldn't fail because of this. */ + return 1; +#endif + } - if (cnt == phnum) + if (dynphdr == NULL) { printf ("%s: no DYNAMIC segment found\n", fname); return 1; } /* Read the dynamic segment. */ - size_t pmemsz = SWAP(phdr[cnt].p_memsz); + size_t pmemsz = SWAP(dynphdr->p_memsz); E(Dyn) *dyn = alloca (pmemsz); - if (pread (fd, dyn, pmemsz, SWAP(phdr[cnt].p_offset)) != pmemsz) + if (pread (fd, dyn, pmemsz, SWAP(dynphdr->p_offset)) != pmemsz) goto read_error; /* Search for an DT_TEXTREL entry of DT_FLAGS with the DF_TEXTREL diff --git a/elf/chroot_canon.c b/elf/chroot_canon.c index d29a032..3ef2fdf 100644 --- a/elf/chroot_canon.c +++ b/elf/chroot_canon.c @@ -1,22 +1,20 @@ /* Return the canonical absolute name of a given file inside chroot. - Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2004 - Free Software Foundation, Inc. + Copyright (C) 1996,1997,1998,1999,2000,2001,2004,2005 + 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. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. - The GNU C Library is distributed in the hope that it will be useful, + This program 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. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <stdlib.h> #include <string.h> @@ -28,7 +26,7 @@ #include <stddef.h> #include <stdint.h> -#include "ldconfig.h" +#include <ldconfig.h> #ifndef PATH_MAX #define PATH_MAX 1024 diff --git a/elf/circleload1.c b/elf/circleload1.c index 7ac101a..990ff84 100644 --- a/elf/circleload1.c +++ b/elf/circleload1.c @@ -5,6 +5,8 @@ #include <stdlib.h> #include <string.h> +#define MAPS ((struct link_map *) _r_debug.r_map) + static int check_loaded_objects (const char **loaded) { @@ -24,10 +26,10 @@ check_loaded_objects (const char **loaded) printf(" Name\n"); printf(" --------------------------------------------------------\n"); - for (lm = _r_debug.r_map; lm; lm = lm->l_next) + for (lm = MAPS; lm; lm = lm->l_next) { if (lm->l_name && lm->l_name[0]) - printf(" %s, count = %d\n", lm->l_name, (int) lm->l_opencount); + printf(" %s, count = %d\n", lm->l_name, (int) lm->l_direct_opencount); if (lm->l_type == lt_loaded && lm->l_name) { int match = 0; @@ -72,9 +74,9 @@ load_dso (const char **loading, int undef, int flag) printf ("\nThis is what is in memory now:\n"); errors += check_loaded_objects (loaded); - printf ("Loading shared object %s: %s\n", loading [0], + printf ("Loading shared object %s: %s\n", loading[0], flag == RTLD_LAZY ? "RTLD_LAZY" : "RTLD_NOW"); - obj = dlopen (loading [0], flag); + obj = dlopen (loading[0], flag); if (obj == NULL) { if (flag == RTLD_LAZY) @@ -120,15 +122,15 @@ load_dso (const char **loading, int undef, int flag) } } - loaded[0] = loading [0]; - loaded[1] = loading [1]; - loaded[2] = loading [2]; + loaded[0] = loading[0]; + loaded[1] = loading[1]; + loaded[2] = loading[2]; } errors += check_loaded_objects (loaded); if (obj) { - printf ("UnLoading shared object %s\n", loading [0]); + printf ("UnLoading shared object %s\n", loading[0]); dlclose (obj); loaded[0] = NULL; loaded[1] = NULL; @@ -145,15 +147,15 @@ main (void) int errors = 0; const char *loading[3]; - loading [0] = "circlemod1a.so"; - loading [1] = "circlemod2a.so"; - loading [2] = "circlemod3a.so"; + loading[0] = "circlemod1a.so"; + loading[1] = "circlemod2a.so"; + loading[2] = "circlemod3a.so"; errors += load_dso (loading, 0, RTLD_LAZY); errors += load_dso (loading, 0, RTLD_NOW); - loading [0] = "circlemod1.so"; - loading [1] = "circlemod2.so"; - loading [2] = "circlemod3.so"; + loading[0] = "circlemod1.so"; + loading[1] = "circlemod2.so"; + loading[2] = "circlemod3.so"; errors += load_dso (loading, 1, RTLD_LAZY); errors += load_dso (loading, 1, RTLD_NOW); diff --git a/elf/dl-addr.c b/elf/dl-addr.c index 685cab9..7dbf716 100644 --- a/elf/dl-addr.c +++ b/elf/dl-addr.c @@ -1,5 +1,5 @@ /* Locate the shared object symbol nearest a given address. - Copyright (C) 1996-2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1996-2004, 2005, 2006, 2007 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 @@ -22,61 +22,62 @@ #include <ldsodefs.h> -int -internal_function -_dl_addr (const void *address, Dl_info *info, - struct link_map **mapp, const ElfW(Sym) **symbolp) +static void +__attribute ((always_inline)) +determine_info (const ElfW(Addr) addr, struct link_map *match, Dl_info *info, + struct link_map **mapp, const ElfW(Sym) **symbolp) { - const ElfW(Addr) addr = DL_LOOKUP_ADDRESS (address); - struct link_map *match; - const ElfW(Sym) *symtab, *matchsym, *symtabend; - const char *strtab; - ElfW(Word) strtabsize; + /* Now we know what object the address lies in. */ + info->dli_fname = match->l_name; + info->dli_fbase = (void *) match->l_map_start; - /* Protect against concurrent loads and unloads. */ - __rtld_lock_lock_recursive (GL(dl_load_lock)); + /* If this is the main program the information is incomplete. */ + if (__builtin_expect (match->l_name[0], 'a') == '\0' + && match->l_type == lt_executable) + info->dli_fname = _dl_argv[0]; - /* Find the highest-addressed object that ADDRESS is not below. */ - match = NULL; - for (Lmid_t ns = 0; ns < DL_NNS; ++ns) - for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l; l = l->l_next) - if (addr >= l->l_map_start && addr < l->l_map_end) + const ElfW(Sym) *symtab + = (const ElfW(Sym) *) D_PTR (match, l_info[DT_SYMTAB]); + const char *strtab = (const char *) D_PTR (match, l_info[DT_STRTAB]); + + ElfW(Word) strtabsize = match->l_info[DT_STRSZ]->d_un.d_val; + + const ElfW(Sym) *matchsym = NULL; + if (match->l_info[DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM + + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] != NULL) + { + /* We look at all symbol table entries referenced by the hash + table. */ + for (Elf_Symndx bucket = 0; bucket < match->l_nbuckets; ++bucket) { - /* We know ADDRESS lies within L if in any shared object. - Make sure it isn't past the end of L's segments. */ - size_t n = l->l_phnum; - if (n > 0) + Elf32_Word symndx = match->l_gnu_buckets[bucket]; + if (symndx != 0) { + const Elf32_Word *hasharr = &match->l_gnu_chain_zero[symndx]; + do - --n; - while (l->l_phdr[n].p_type != PT_LOAD); - if (addr >= (l->l_addr + - l->l_phdr[n].p_vaddr + l->l_phdr[n].p_memsz)) - /* Off the end of the highest-addressed shared object. */ - continue; + { + /* The hash table never references local symbols so + we can omit that test here. */ + if ((symtab[symndx].st_shndx != SHN_UNDEF + || symtab[symndx].st_value != 0) +#ifdef USE_TLS + && ELFW(ST_TYPE) (symtab[symndx].st_info) != STT_TLS +#endif + && DL_ADDR_SYM_MATCH (match, &symtab[symndx], + matchsym, addr) + && symtab[symndx].st_name < strtabsize) + matchsym = (ElfW(Sym) *) &symtab[symndx]; + + ++symndx; + } + while ((*hasharr++ & 1u) == 0); } - - match = l; - break; } - - int result = 0; - if (match != NULL) + } + else { - /* Now we know what object the address lies in. */ - info->dli_fname = match->l_name; - info->dli_fbase = (void *) match->l_map_start; - - /* If this is the main program the information is incomplete. */ - if (__builtin_expect (match->l_name[0], 'a') == '\0' - && match->l_type == lt_executable) - info->dli_fname = _dl_argv[0]; - - symtab = (const void *) D_PTR (match, l_info[DT_SYMTAB]); - strtab = (const void *) D_PTR (match, l_info[DT_STRTAB]); - - strtabsize = match->l_info[DT_STRSZ]->d_un.d_val; - + const ElfW(Sym) *symtabend; if (match->l_info[DT_HASH] != NULL) symtabend = (symtab + ((Elf_Symndx *) D_PTR (match, l_info[DT_HASH]))[1]); @@ -87,49 +88,83 @@ _dl_addr (const void *address, Dl_info *info, the string table which generally follows the symbol table. */ symtabend = (const ElfW(Sym) *) strtab; - /* We assume that the string table follows the symbol table, - because there is no way in ELF to know the size of the - dynamic symbol table!! */ - for (matchsym = NULL; (void *) symtab < (void *) symtabend; ++symtab) - if (addr >= match->l_addr + symtab->st_value -#if defined USE_TLS + for (; (void *) symtab < (void *) symtabend; ++symtab) + if ((ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL + || ELFW(ST_BIND) (symtab->st_info) == STB_WEAK) +#ifdef USE_TLS && ELFW(ST_TYPE) (symtab->st_info) != STT_TLS #endif - && ((symtab->st_size == 0 - && addr == match->l_addr + symtab->st_value) - || addr < match->l_addr + symtab->st_value + symtab->st_size) - && symtab->st_name < strtabsize - && (matchsym == NULL || matchsym->st_value < symtab->st_value) - && (ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL - || ELFW(ST_BIND) (symtab->st_info) == STB_WEAK)) + && (symtab->st_shndx != SHN_UNDEF + || symtab->st_value != 0) + && DL_ADDR_SYM_MATCH (match, symtab, matchsym, addr) + && symtab->st_name < strtabsize) matchsym = (ElfW(Sym) *) symtab; + } - if (mapp) - *mapp = match; - if (symbolp) - *symbolp = matchsym; + if (mapp) + *mapp = match; + if (symbolp) + *symbolp = matchsym; - if (matchsym) - { - /* We found a symbol close by. Fill in its name and exact - address. */ - lookup_t matchl = LOOKUP_VALUE (match); + if (matchsym) + { + /* We found a symbol close by. Fill in its name and exact + address. */ + lookup_t matchl = LOOKUP_VALUE (match); - info->dli_sname = strtab + matchsym->st_name; - info->dli_saddr = DL_SYMBOL_ADDRESS (matchl, matchsym); - } - else + info->dli_sname = strtab + matchsym->st_name; + info->dli_saddr = DL_SYMBOL_ADDRESS (matchl, matchsym); + } + else + { + /* No symbol matches. We return only the containing object. */ + info->dli_sname = NULL; + info->dli_saddr = NULL; + } +} + + +int +internal_function +_dl_addr (const void *address, Dl_info *info, + struct link_map **mapp, const ElfW(Sym) **symbolp) +{ + const ElfW(Addr) addr = DL_LOOKUP_ADDRESS (address); + int result = 0; + + /* Protect against concurrent loads and unloads. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + /* Find the highest-addressed object that ADDRESS is not below. */ + for (Lmid_t ns = 0; ns < DL_NNS; ++ns) + for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l; l = l->l_next) + if (addr >= l->l_map_start && addr < l->l_map_end + && (l->l_contiguous || _dl_addr_inside_object (l, addr))) { - /* No symbol matches. We return only the containing object. */ - info->dli_sname = NULL; - info->dli_saddr = NULL; + determine_info (addr, l, info, mapp, symbolp); + result = 1; + goto out; } - result = 1; - } - + out: __rtld_lock_unlock_recursive (GL(dl_load_lock)); return result; } libc_hidden_def (_dl_addr) + +/* Return non-zero if ADDR lies within one of L's segments. */ +int +internal_function +_dl_addr_inside_object (struct link_map *l, const ElfW(Addr) addr) +{ + int n = l->l_phnum; + const ElfW(Addr) reladdr = addr - l->l_addr; + + while (--n >= 0) + if (l->l_phdr[n].p_type == PT_LOAD + && reladdr - l->l_phdr[n].p_vaddr >= 0 + && reladdr - l->l_phdr[n].p_vaddr < l->l_phdr[n].p_memsz) + return 1; + return 0; +} diff --git a/elf/dl-brk.c b/elf/dl-brk.c new file mode 100644 index 0000000..c37cdfe --- /dev/null +++ b/elf/dl-brk.c @@ -0,0 +1,5 @@ +/* We can use the normal code but we also know the __curbrk is not exported + from ld.so. */ +extern void *__curbrk attribute_hidden; + +#include <brk.c> diff --git a/elf/dl-cache.c b/elf/dl-cache.c new file mode 100644 index 0000000..fc7d991 --- /dev/null +++ b/elf/dl-cache.c @@ -0,0 +1,311 @@ +/* Support for reading /etc/ld.so.cache files written by Linux ldconfig. + Copyright (C) 1996-2002, 2003, 2004, 2006 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <assert.h> +#include <unistd.h> +#include <ldsodefs.h> +#include <sys/mman.h> +#include <dl-cache.h> +#include <dl-procinfo.h> + +#include <stdio-common/_itoa.h> + +#ifndef _DL_PLATFORMS_COUNT +# define _DL_PLATFORMS_COUNT 0 +#endif + +/* This is the starting address and the size of the mmap()ed file. */ +static struct cache_file *cache; +static struct cache_file_new *cache_new; +static size_t cachesize; + +/* 1 if cache_data + PTR points into the cache. */ +#define _dl_cache_verify_ptr(ptr) (ptr < cache_data_size) + +#define SEARCH_CACHE(cache) \ +/* We use binary search since the table is sorted in the cache file. \ + The first matching entry in the table is returned. \ + It is important to use the same algorithm as used while generating \ + the cache file. */ \ +do \ + { \ + left = 0; \ + right = cache->nlibs - 1; \ + \ + while (left <= right) \ + { \ + __typeof__ (cache->libs[0].key) key; \ + \ + middle = (left + right) / 2; \ + \ + key = cache->libs[middle].key; \ + \ + /* Make sure string table indices are not bogus before using \ + them. */ \ + if (! _dl_cache_verify_ptr (key)) \ + { \ + cmpres = 1; \ + break; \ + } \ + \ + /* Actually compare the entry with the key. */ \ + cmpres = _dl_cache_libcmp (name, cache_data + key); \ + if (__builtin_expect (cmpres == 0, 0)) \ + { \ + /* Found it. LEFT now marks the last entry for which we \ + know the name is correct. */ \ + left = middle; \ + \ + /* There might be entries with this name before the one we \ + found. So we have to find the beginning. */ \ + while (middle > 0) \ + { \ + __typeof__ (cache->libs[0].key) key; \ + \ + key = cache->libs[middle - 1].key; \ + /* Make sure string table indices are not bogus before \ + using them. */ \ + if (! _dl_cache_verify_ptr (key) \ + /* Actually compare the entry. */ \ + || _dl_cache_libcmp (name, cache_data + key) != 0) \ + break; \ + --middle; \ + } \ + \ + do \ + { \ + int flags; \ + __typeof__ (cache->libs[0]) *lib = &cache->libs[middle]; \ + \ + /* Only perform the name test if necessary. */ \ + if (middle > left \ + /* We haven't seen this string so far. Test whether the \ + index is ok and whether the name matches. Otherwise \ + we are done. */ \ + && (! _dl_cache_verify_ptr (lib->key) \ + || (_dl_cache_libcmp (name, cache_data + lib->key) \ + != 0))) \ + break; \ + \ + flags = lib->flags; \ + if (_dl_cache_check_flags (flags) \ + && _dl_cache_verify_ptr (lib->value)) \ + { \ + if (best == NULL || flags == GLRO(dl_correct_cache_id)) \ + { \ + HWCAP_CHECK; \ + best = cache_data + lib->value; \ + \ + if (flags == GLRO(dl_correct_cache_id)) \ + /* We've found an exact match for the shared \ + object and no general `ELF' release. Stop \ + searching. */ \ + break; \ + } \ + } \ + } \ + while (++middle <= right); \ + break; \ + } \ + \ + if (cmpres < 0) \ + left = middle + 1; \ + else \ + right = middle - 1; \ + } \ + } \ +while (0) + + +int +internal_function +_dl_cache_libcmp (const char *p1, const char *p2) +{ + while (*p1 != '\0') + { + if (*p1 >= '0' && *p1 <= '9') + { + if (*p2 >= '0' && *p2 <= '9') + { + /* Must compare this numerically. */ + int val1; + int val2; + + val1 = *p1++ - '0'; + val2 = *p2++ - '0'; + while (*p1 >= '0' && *p1 <= '9') + val1 = val1 * 10 + *p1++ - '0'; + while (*p2 >= '0' && *p2 <= '9') + val2 = val2 * 10 + *p2++ - '0'; + if (val1 != val2) + return val1 - val2; + } + else + return 1; + } + else if (*p2 >= '0' && *p2 <= '9') + return -1; + else if (*p1 != *p2) + return *p1 - *p2; + else + { + ++p1; + ++p2; + } + } + return *p1 - *p2; +} + + +/* Look up NAME in ld.so.cache and return the file name stored there, + or null if none is found. */ + +const char * +internal_function +_dl_load_cache_lookup (const char *name) +{ + int left, right, middle; + int cmpres; + const char *cache_data; + uint32_t cache_data_size; + const char *best; + + /* Print a message if the loading of libs is traced. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) + _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE); + + if (cache == NULL) + { + /* Read the contents of the file. */ + void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize, + PROT_READ); + + /* We can handle three different cache file formats here: + - the old libc5/glibc2.0/2.1 format + - the old format with the new format in it + - only the new format + The following checks if the cache contains any of these formats. */ + if (file != MAP_FAILED && cachesize > sizeof *cache + && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0) + { + size_t offset; + /* Looks ok. */ + cache = file; + + /* Check for new version. */ + offset = ALIGN_CACHE (sizeof (struct cache_file) + + cache->nlibs * sizeof (struct file_entry)); + + cache_new = (struct cache_file_new *) ((void *) cache + offset); + if (cachesize < (offset + sizeof (struct cache_file_new)) + || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW, + sizeof CACHEMAGIC_VERSION_NEW - 1) != 0) + cache_new = (void *) -1; + } + else if (file != MAP_FAILED && cachesize > sizeof *cache_new + && memcmp (file, CACHEMAGIC_VERSION_NEW, + sizeof CACHEMAGIC_VERSION_NEW - 1) == 0) + { + cache_new = file; + cache = file; + } + else + { + if (file != MAP_FAILED) + __munmap (file, cachesize); + cache = (void *) -1; + } + + assert (cache != NULL); + } + + if (cache == (void *) -1) + /* Previously looked for the cache file and didn't find it. */ + return NULL; + + best = NULL; + + if (cache_new != (void *) -1) + { + uint64_t platform; + + /* This is where the strings start. */ + cache_data = (const char *) cache_new; + + /* Now we can compute how large the string table is. */ + cache_data_size = (const char *) cache + cachesize - cache_data; + + platform = _dl_string_platform (GLRO(dl_platform)); + if (platform != (uint64_t) -1) + platform = 1ULL << platform; + + /* Only accept hwcap if it's for the right platform. */ +#ifdef USE_TLS +# define _DL_HWCAP_TLS_MASK (1LL << 63) +#else +# define _DL_HWCAP_TLS_MASK 0 +#endif +#define HWCAP_CHECK \ + if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion)) \ + continue; \ + if (_DL_PLATFORMS_COUNT \ + && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0 \ + && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform) \ + continue; \ + if (lib->hwcap \ + & ~(GLRO(dl_hwcap) | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK)) \ + continue + SEARCH_CACHE (cache_new); + } + else + { + /* This is where the strings start. */ + cache_data = (const char *) &cache->libs[cache->nlibs]; + + /* Now we can compute how large the string table is. */ + cache_data_size = (const char *) cache + cachesize - cache_data; + +#undef HWCAP_CHECK +#define HWCAP_CHECK do {} while (0) + SEARCH_CACHE (cache); + } + + /* Print our result if wanted. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0) + && best != NULL) + _dl_debug_printf (" trying file=%s\n", best); + + return best; +} + +#ifndef MAP_COPY +/* If the system does not support MAP_COPY we cannot leave the file open + all the time since this would create problems when the file is replaced. + Therefore we provide this function to close the file and open it again + once needed. */ +void +_dl_unload_cache (void) +{ + if (cache != NULL && cache != (struct cache_file *) -1) + { + __munmap (cache, cachesize); + cache = NULL; + } +} +#endif diff --git a/elf/dl-close.c b/elf/dl-close.c index c823b17..5cd8b9b 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -1,5 +1,5 @@ /* Close a shared object opened by `_dl_open'. - Copyright (C) 1996-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1996-2005, 2006, 2007 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 @@ -19,20 +19,29 @@ #include <assert.h> #include <dlfcn.h> +#include <errno.h> #include <libintl.h> +#include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <bits/libc-lock.h> #include <ldsodefs.h> #include <sys/types.h> #include <sys/mman.h> +#include <sysdep-cancel.h> +#include <tls.h> /* Type of the constructor functions. */ typedef void (*fini_t) (void); +/* Special l_idx value used to indicate which objects remain loaded. */ +#define IDX_STILL_USED -1 + + #ifdef USE_TLS /* Returns true we an non-empty was found. */ static bool @@ -99,187 +108,156 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, void -internal_function -_dl_close (void *_map) +_dl_close_worker (struct link_map *map) { - struct reldep_list - { - struct link_map **rellist; - unsigned int nrellist; - unsigned int nhandled; - struct reldep_list *next; - bool handled[0]; - } *reldeps = NULL; - struct link_map **list; - struct link_map *map = _map; - unsigned int i; - unsigned int *new_opencount; -#ifdef USE_TLS - bool any_tls = false; -#endif - - /* First see whether we can remove the object at all. */ - if (__builtin_expect (map->l_flags_1 & DF_1_NODELETE, 0) - && map->l_init_called) - /* Nope. Do nothing. */ - return; - - if (__builtin_expect (map->l_opencount, 1) == 0) - GLRO(dl_signal_error) (0, map->l_name, NULL, N_("shared object not open")); - - /* Acquire the lock. */ - __rtld_lock_lock_recursive (GL(dl_load_lock)); - /* One less direct use. */ - assert (map->l_direct_opencount > 0); --map->l_direct_opencount; - /* Decrement the reference count. */ - if (map->l_opencount > 1 || map->l_type != lt_loaded) + /* If _dl_close is called recursively (some destructor call dlclose), + just record that the parent _dl_close will need to do garbage collection + again and return. */ + static enum { not_pending, pending, rerun } dl_close_state; + + if (map->l_direct_opencount > 0 || map->l_type != lt_loaded + || dl_close_state != not_pending) { + if (map->l_direct_opencount == 0 && map->l_type == lt_loaded) + dl_close_state = rerun; + /* There are still references to this object. Do nothing more. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) - GLRO(dl_debug_printf) ("\nclosing file=%s; opencount == %u\n", - map->l_name, map->l_opencount); + _dl_debug_printf ("\nclosing file=%s; direct_opencount=%u\n", + map->l_name, map->l_direct_opencount); - /* Decrement the object's reference counter, not the dependencies'. */ - --map->l_opencount; - - /* If the direct use counter reaches zero we have to decrement - all the dependencies' usage counter. */ - if (map->l_direct_opencount == 0) - for (i = 1; i < map->l_searchlist.r_nlist; ++i) - --map->l_searchlist.r_list[i]->l_opencount; - - __rtld_lock_unlock_recursive (GL(dl_load_lock)); return; } - list = map->l_initfini; - - /* Compute the new l_opencount values. */ - i = map->l_searchlist.r_nlist; - if (__builtin_expect (i == 0, 0)) - /* This can happen if we handle relocation dependencies for an - object which wasn't loaded directly. */ - for (i = 1; list[i] != NULL; ++i) - ; + Lmid_t nsid = map->l_ns; + struct link_namespaces *ns = &GL(dl_ns)[nsid]; - unsigned int nopencount = i; - new_opencount = (unsigned int *) alloca (i * sizeof (unsigned int)); + retry: + dl_close_state = pending; - for (i = 0; list[i] != NULL; ++i) +#ifdef USE_TLS + bool any_tls = false; +#endif + const unsigned int nloaded = ns->_ns_nloaded; + char used[nloaded]; + char done[nloaded]; + struct link_map *maps[nloaded]; + + /* Run over the list and assign indexes to the link maps and enter + them into the MAPS array. */ + int idx = 0; + for (struct link_map *l = ns->_ns_loaded; l != NULL; l = l->l_next) { - list[i]->l_idx = i; - new_opencount[i] = list[i]->l_opencount; + l->l_idx = idx; + maps[idx] = l; + ++idx; } - --new_opencount[0]; - for (i = 1; list[i] != NULL; ++i) - if ((list[i]->l_flags_1 & DF_1_NODELETE) == 0 - /* Decrement counter. */ - && (assert (new_opencount[i] > 0), --new_opencount[i] == 0)) - { - void mark_removed (struct link_map *remmap) - { - /* Test whether this object was also loaded directly. */ - if (remmap->l_searchlist.r_list != NULL - && remmap->l_direct_opencount > 0) - { - /* In this case we have to decrement all the dependencies of - this object. They are all in MAP's dependency list. */ - unsigned int j; - struct link_map **dep_list = remmap->l_searchlist.r_list; - - for (j = 1; j < remmap->l_searchlist.r_nlist; ++j) - if (! (dep_list[j]->l_flags_1 & DF_1_NODELETE) - || ! dep_list[j]->l_init_called) + assert (idx == nloaded); + + /* Prepare the bitmaps. */ + memset (used, '\0', sizeof (used)); + memset (done, '\0', sizeof (done)); + + /* Keep track of the lowest index link map we have covered already. */ + int done_index = -1; + while (++done_index < nloaded) + { + struct link_map *l = maps[done_index]; + + if (done[done_index]) + /* Already handled. */ + continue; + + /* Check whether this object is still used. */ + if (l->l_type == lt_loaded + && l->l_direct_opencount == 0 + && (l->l_flags_1 & DF_1_NODELETE) == 0 + && !used[done_index]) + continue; + + /* We need this object and we handle it now. */ + done[done_index] = 1; + used[done_index] = 1; + /* Signal the object is still needed. */ + l->l_idx = IDX_STILL_USED; + + /* Mark all dependencies as used. */ + if (l->l_initfini != NULL) + { + struct link_map **lp = &l->l_initfini[1]; + while (*lp != NULL) + { + if ((*lp)->l_idx != IDX_STILL_USED) { - assert (dep_list[j]->l_idx < map->l_searchlist.r_nlist); - assert (new_opencount[dep_list[j]->l_idx] > 0); - if (--new_opencount[dep_list[j]->l_idx] == 0) + assert ((*lp)->l_idx >= 0 && (*lp)->l_idx < nloaded); + + if (!used[(*lp)->l_idx]) { - assert (dep_list[j]->l_type == lt_loaded); - mark_removed (dep_list[j]); + used[(*lp)->l_idx] = 1; + if ((*lp)->l_idx - 1 < done_index) + done_index = (*lp)->l_idx - 1; } } - } - if (remmap->l_reldeps != NULL) + ++lp; + } + } + /* And the same for relocation dependencies. */ + if (l->l_reldeps != NULL) + for (unsigned int j = 0; j < l->l_reldepsact; ++j) + { + struct link_map *jmap = l->l_reldeps[j]; + + if (jmap->l_idx != IDX_STILL_USED) { - unsigned int j; - for (j = 0; j < remmap->l_reldepsact; ++j) + assert (jmap->l_idx >= 0 && jmap->l_idx < nloaded); + + if (!used[jmap->l_idx]) { - struct link_map *depmap = remmap->l_reldeps[j]; - - /* Find out whether this object is in our list. */ - if (depmap->l_idx < nopencount - && list[depmap->l_idx] == depmap) - { - /* Yes, it is. If is has a search list, make a - recursive call to handle this. */ - if (depmap->l_searchlist.r_list != NULL) - { - assert (new_opencount[depmap->l_idx] > 0); - if (--new_opencount[depmap->l_idx] == 0) - { - /* This one is now gone, too. */ - assert (depmap->l_type == lt_loaded); - mark_removed (depmap); - } - } - else - { - /* Otherwise we have to handle the dependency - deallocation here. */ - unsigned int k; - for (k = 0; depmap->l_initfini[k] != NULL; ++k) - { - struct link_map *rl = depmap->l_initfini[k]; - - if (rl->l_idx < nopencount - && list[rl->l_idx] == rl) - { - assert (new_opencount[rl->l_idx] > 0); - if (--new_opencount[rl->l_idx] == 0) - { - /* Another module to remove. */ - assert (rl->l_type == lt_loaded); - mark_removed (rl); - } - } - else - { - assert (rl->l_opencount > 0); - if (--rl->l_opencount == 0) - mark_removed (rl); - } - } - } - } + used[jmap->l_idx] = 1; + if (jmap->l_idx - 1 < done_index) + done_index = jmap->l_idx - 1; } } } + } - mark_removed (list[i]); - } - assert (new_opencount[0] == 0); + /* Sort the entries. */ + _dl_sort_fini (ns->_ns_loaded, maps, nloaded, used, nsid); /* Call all termination functions at once. */ - for (i = 0; list[i] != NULL; ++i) +#ifdef SHARED + bool do_audit = GLRO(dl_naudit) > 0 && !ns->_ns_loaded->l_auditing; +#endif + bool unload_any = false; + bool scope_mem_left = false; + unsigned int unload_global = 0; + unsigned int first_loaded = ~0; + for (unsigned int i = 0; i < nloaded; ++i) { - struct link_map *imap = list[i]; - if (new_opencount[i] == 0 && imap->l_type == lt_loaded - && (imap->l_flags_1 & DF_1_NODELETE) == 0) + struct link_map *imap = maps[i]; + + /* All elements must be in the same namespace. */ + assert (imap->l_ns == nsid); + + if (!used[i]) { - /* When debugging print a message first. */ - if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) - GLRO(dl_debug_printf) ("\ncalling fini: %s [%lu]\n\n", - imap->l_name, imap->l_ns); + assert (imap->l_type == lt_loaded + && (imap->l_flags_1 & DF_1_NODELETE) == 0); /* Call its termination function. Do not do it for half-cooked objects. */ if (imap->l_init_called) { + /* When debugging print a message first. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, + 0)) + _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", + imap->l_name, nsid); + if (imap->l_info[DT_FINI_ARRAY] != NULL) { ElfW(Addr) *array = @@ -299,75 +277,235 @@ _dl_close (void *_map) + imap->l_info[DT_FINI]->d_un.d_ptr))) (); } - /* This object must not be used anymore. We must remove the - reference from the scope. */ - unsigned int j; - struct link_map **searchlist = map->l_searchlist.r_list; - unsigned int nsearchlist = map->l_searchlist.r_nlist; +#ifdef SHARED + /* Auditing checkpoint: we have a new object. */ + if (__builtin_expect (do_audit, 0)) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objclose != NULL) + /* Return value is ignored. */ + (void) afct->objclose (&imap->l_audit[cnt].cookie); -#ifndef NDEBUG - bool found = false; + afct = afct->next; + } + } #endif - for (j = 0; j < nsearchlist; ++j) - if (imap == searchlist[j]) - { - /* This is the object to remove. Copy all the - following ones. */ - while (++j < nsearchlist) - searchlist[j - 1] = searchlist[j]; - searchlist[j - 1] = NULL; + /* This object must not be used anymore. */ + imap->l_removed = 1; - --map->l_searchlist.r_nlist; + /* We indeed have an object to remove. */ + unload_any = true; -#ifndef NDEBUG - found = true; -#endif - break; - } - assert (found); + if (imap->l_global) + ++unload_global; + + /* Remember where the first dynamically loaded object is. */ + if (i < first_loaded) + first_loaded = i; } - else if (new_opencount[i] != 0 && imap->l_type == lt_loaded - && imap->l_searchlist.r_list == NULL - && imap->l_initfini != NULL) + /* Else used[i]. */ + else if (imap->l_type == lt_loaded) { - /* The object is still used. But the object we are - unloading right now is responsible for loading it. If - the current object does not have it's own scope yet we - have to create one. This has to be done before running - the finalizers. - - To do this count the number of dependencies. */ - unsigned int cnt; - for (cnt = 1; imap->l_initfini[cnt] != NULL; ++cnt) - if (imap->l_initfini[cnt]->l_idx >= i - && imap->l_initfini[cnt]->l_idx < nopencount) - ++new_opencount[imap->l_initfini[cnt]->l_idx]; - else - ++imap->l_initfini[cnt]->l_opencount; + struct r_scope_elem *new_list = NULL; - /* We simply reuse the l_initfini list. */ - imap->l_searchlist.r_list = &imap->l_initfini[cnt + 1]; - imap->l_searchlist.r_nlist = cnt; + if (imap->l_searchlist.r_list == NULL && imap->l_initfini != NULL) + { + /* The object is still used. But one of the objects we are + unloading right now is responsible for loading it. If + the current object does not have it's own scope yet we + have to create one. This has to be done before running + the finalizers. + + To do this count the number of dependencies. */ + unsigned int cnt; + for (cnt = 1; imap->l_initfini[cnt] != NULL; ++cnt) + ; + + /* We simply reuse the l_initfini list. */ + imap->l_searchlist.r_list = &imap->l_initfini[cnt + 1]; + imap->l_searchlist.r_nlist = cnt; + + new_list = &imap->l_searchlist; + } - for (cnt = 0; imap->l_scope[cnt] != NULL; ++cnt) - if (imap->l_scope[cnt] == &map->l_searchlist) + /* Count the number of scopes which remain after the unload. + When we add the local search list count it. Always add + one for the terminating NULL pointer. */ + size_t remain = (new_list != NULL) + 1; + bool removed_any = false; + for (size_t cnt = 0; imap->l_scope[cnt] != NULL; ++cnt) + /* This relies on l_scope[] entries being always set either + to its own l_symbolic_searchlist address, or some map's + l_searchlist address. */ + if (imap->l_scope[cnt] != &imap->l_symbolic_searchlist) { - imap->l_scope[cnt] = &imap->l_searchlist; - break; + struct link_map *tmap = (struct link_map *) + ((char *) imap->l_scope[cnt] + - offsetof (struct link_map, l_searchlist)); + assert (tmap->l_ns == nsid); + if (tmap->l_idx == IDX_STILL_USED) + ++remain; + else + removed_any = true; } + else + ++remain; + + if (removed_any) + { + /* Always allocate a new array for the scope. This is + necessary since we must be able to determine the last + user of the current array. If possible use the link map's + memory. */ + size_t new_size; + struct r_scope_elem **newp; + +#define SCOPE_ELEMS(imap) \ + (sizeof (imap->l_scope_mem) / sizeof (imap->l_scope_mem[0])) + + if (imap->l_scope != imap->l_scope_mem + && remain < SCOPE_ELEMS (imap)) + { + new_size = SCOPE_ELEMS (imap); + newp = imap->l_scope_mem; + } + else + { + new_size = imap->l_scope_max; + newp = (struct r_scope_elem **) + malloc (new_size * sizeof (struct r_scope_elem *)); + if (newp == NULL) + _dl_signal_error (ENOMEM, "dlclose", NULL, + N_("cannot create scope list")); + } + + /* Copy over the remaining scope elements. */ + remain = 0; + for (size_t cnt = 0; imap->l_scope[cnt] != NULL; ++cnt) + { + if (imap->l_scope[cnt] != &imap->l_symbolic_searchlist) + { + struct link_map *tmap = (struct link_map *) + ((char *) imap->l_scope[cnt] + - offsetof (struct link_map, l_searchlist)); + if (tmap->l_idx != IDX_STILL_USED) + { + /* Remove the scope. Or replace with own map's + scope. */ + if (new_list != NULL) + { + newp[remain++] = new_list; + new_list = NULL; + } + continue; + } + } + + newp[remain++] = imap->l_scope[cnt]; + } + newp[remain] = NULL; + + struct r_scope_elem **old = imap->l_scope; + + imap->l_scope = newp; + + /* No user anymore, we can free it now. */ + if (old != imap->l_scope_mem) + { + if (_dl_scope_free (old)) + /* If _dl_scope_free used THREAD_GSCOPE_WAIT (), + no need to repeat it. */ + scope_mem_left = false; + } + else + scope_mem_left = true; + + imap->l_scope_max = new_size; + } + + /* The loader is gone, so mark the object as not having one. + Note: l_idx != IDX_STILL_USED -> object will be removed. */ + if (imap->l_loader != NULL + && imap->l_loader->l_idx != IDX_STILL_USED) + imap->l_loader = NULL; + + /* Remember where the first dynamically loaded object is. */ + if (i < first_loaded) + first_loaded = i; } + } + + /* If there are no objects to unload, do nothing further. */ + if (!unload_any) + goto out; - /* Store the new l_opencount value. */ - imap->l_opencount = new_opencount[i]; +#ifdef SHARED + /* Auditing checkpoint: we will start deleting objects. */ + if (__builtin_expect (do_audit, 0)) + { + struct link_map *head = ns->_ns_loaded; + struct audit_ifaces *afct = GLRO(dl_audit); + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_DELETE); - /* Just a sanity check. */ - assert (imap->l_type == lt_loaded || imap->l_opencount > 0); + afct = afct->next; + } + } } +#endif /* Notify the debugger we are about to remove some loaded objects. */ - _r_debug.r_state = RT_DELETE; - GLRO(dl_debug_state) (); + struct r_debug *r = _dl_debug_initialize (0, nsid); + r->r_state = RT_DELETE; + _dl_debug_state (); + + if (unload_global) + { + /* Some objects are in the global scope list. Remove them. */ + struct r_scope_elem *ns_msl = ns->_ns_main_searchlist; + unsigned int i; + unsigned int j = 0; + unsigned int cnt = ns_msl->r_nlist; + + while (cnt > 0 && ns_msl->r_list[cnt - 1]->l_removed) + --cnt; + + if (cnt + unload_global == ns_msl->r_nlist) + /* Speed up removing most recently added objects. */ + j = cnt; + else + for (i = 0; i < cnt; i++) + if (ns_msl->r_list[i]->l_removed == 0) + { + if (i != j) + ns_msl->r_list[j] = ns_msl->r_list[i]; + j++; + } + ns_msl->r_nlist = j; + } + + if (!RTLD_SINGLE_THREAD_P + && (unload_global + || scope_mem_left + || (GL(dl_scope_free_list) != NULL + && GL(dl_scope_free_list)->count))) + { + struct dl_scope_free_list *fsl; + + THREAD_GSCOPE_WAIT (); + /* Now we can free any queued old scopes. */ + if ((fsl = GL(dl_scope_free_list)) != NULL) + while (fsl->count > 0) + free (fsl->list[--fsl->count]); + } #ifdef USE_TLS size_t tls_free_start; @@ -377,34 +515,15 @@ _dl_close (void *_map) /* Check each element of the search list to see if all references to it are gone. */ - for (i = 0; list[i] != NULL; ++i) + for (unsigned int i = first_loaded; i < nloaded; ++i) { - struct link_map *imap = list[i]; - if (imap->l_opencount == 0 && imap->l_type == lt_loaded) + struct link_map *imap = maps[i]; + if (!used[i]) { - struct libname_list *lnp; + assert (imap->l_type == lt_loaded); /* That was the last reference, and this was a dlopen-loaded object. We can unmap it. */ - if (__builtin_expect (imap->l_global, 0)) - { - /* This object is in the global scope list. Remove it. */ - unsigned int cnt - = GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_nlist; - - do - --cnt; - while (GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_list[cnt] - != imap); - - /* The object was already correctly registered. */ - while (++cnt - < GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_nlist) - GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_list[cnt - 1] - = GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_list[cnt]; - - --GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_nlist; - } #ifdef USE_TLS /* Remove the object from the dtv slotinfo array if it uses TLS. */ @@ -412,9 +531,10 @@ _dl_close (void *_map) { any_tls = true; - if (! remove_slotinfo (imap->l_tls_modid, - GL(dl_tls_dtv_slotinfo_list), 0, - imap->l_init_called)) + if (GL(dl_tls_dtv_slotinfo_list) != NULL + && ! remove_slotinfo (imap->l_tls_modid, + GL(dl_tls_dtv_slotinfo_list), 0, + imap->l_init_called)) /* All dynamically loaded modules with TLS are unloaded. */ GL(dl_tls_max_dtv_idx) = GL(dl_tls_static_nelem); @@ -499,12 +619,12 @@ _dl_close (void *_map) else { #ifdef SHARED - assert (imap->l_ns != LM_ID_BASE); + assert (nsid != LM_ID_BASE); #endif - GL(dl_ns)[imap->l_ns]._ns_loaded = imap->l_next; + ns->_ns_loaded = imap->l_next; } - --GL(dl_ns)[imap->l_ns]._ns_nloaded; + --ns->_ns_nloaded; if (imap->l_next != NULL) imap->l_next->l_prev = imap->l_prev; @@ -512,39 +632,18 @@ _dl_close (void *_map) if (imap->l_origin != (char *) -1) free ((char *) imap->l_origin); - /* If the object has relocation dependencies save this - information for latter. */ - if (__builtin_expect (imap->l_reldeps != NULL, 0)) - { - struct reldep_list *newrel; - - newrel = (struct reldep_list *) alloca (sizeof (*reldeps) - + (imap->l_reldepsact - * sizeof (bool))); - newrel->rellist = imap->l_reldeps; - newrel->nrellist = imap->l_reldepsact; - newrel->next = reldeps; - - newrel->nhandled = imap->l_reldepsact; - unsigned int j; - for (j = 0; j < imap->l_reldepsact; ++j) - { - /* Find out whether this object is in our list. */ - if (imap->l_reldeps[j]->l_idx < nopencount - && list[imap->l_reldeps[j]->l_idx] == imap->l_reldeps[j]) - /* Yes, it is. */ - newrel->handled[j] = true; - else - newrel->handled[j] = false; - } + free (imap->l_reldeps); - reldeps = newrel; - } + /* Print debugging message. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) + _dl_debug_printf ("\nfile=%s [%lu]; destroying link map\n", + imap->l_name, imap->l_ns); /* This name always is allocated. */ free (imap->l_name); /* Remove the list with all the names of the shared object. */ - lnp = imap->l_libname; + + struct libname_list *lnp = imap->l_libname; do { struct libname_list *this = lnp; @@ -555,8 +654,7 @@ _dl_close (void *_map) while (lnp != NULL); /* Remove the searchlists. */ - if (imap != map) - free (imap->l_initfini); + free (imap->l_initfini); /* Remove the scope array if we allocated it. */ if (imap->l_scope != imap->l_scope_mem) @@ -579,40 +677,69 @@ _dl_close (void *_map) if (any_tls) { if (__builtin_expect (++GL(dl_tls_generation) == 0, 0)) - __libc_fatal (_("TLS generation counter wrapped! Please report as described in <http://www.gnu.org/software/libc/bugs.html>.")); + _dl_fatal_printf ("TLS generation counter wrapped! Please report as described in <http://www.gnu.org/software/libc/bugs.html>.\n"); if (tls_free_end == GL(dl_tls_static_used)) GL(dl_tls_static_used) = tls_free_start; } #endif +#ifdef SHARED + /* Auditing checkpoint: we have deleted all objects. */ + if (__builtin_expect (do_audit, 0)) + { + struct link_map *head = ns->_ns_loaded; + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT); + + afct = afct->next; + } + } + } +#endif + /* Notify the debugger those objects are finalized and gone. */ - _r_debug.r_state = RT_CONSISTENT; - GLRO(dl_debug_state) (); + r->r_state = RT_CONSISTENT; + _dl_debug_state (); - /* Now we can perhaps also remove the modules for which we had - dependencies because of symbol lookup. */ - while (__builtin_expect (reldeps != NULL, 0)) - { - while (reldeps->nrellist-- > 0) - /* Some of the relocation dependencies might be on the - dependency list of the object we are closing right now. - They were already handled. Do not close them again. */ - if (reldeps->nrellist < reldeps->nhandled - && ! reldeps->handled[reldeps->nrellist]) - _dl_close (reldeps->rellist[reldeps->nrellist]); + /* Recheck if we need to retry, release the lock. */ + out: + if (dl_close_state == rerun) + goto retry; - free (reldeps->rellist); + dl_close_state = not_pending; +} + + +void +_dl_close (void *_map) +{ + struct link_map *map = _map; - reldeps = reldeps->next; + /* First see whether we can remove the object at all. */ + if (__builtin_expect (map->l_flags_1 & DF_1_NODELETE, 0)) + { + assert (map->l_init_called); + /* Nope. Do nothing. */ + return; } - free (list); + if (__builtin_expect (map->l_direct_opencount, 1) == 0) + GLRO(dl_signal_error) (0, map->l_name, NULL, N_("shared object not open")); + + /* Acquire the lock. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + _dl_close_worker (map); - /* Release the lock. */ __rtld_lock_unlock_recursive (GL(dl_load_lock)); } -libc_hidden_def (_dl_close) #ifdef USE_TLS @@ -647,22 +774,22 @@ free_slotinfo (struct dtv_slotinfo_list **elemp) libc_freeres_fn (free_mem) { - for (Lmid_t ns = 0; ns < DL_NNS; ++ns) - if (__builtin_expect (GL(dl_ns)[ns]._ns_global_scope_alloc, 0) != 0 - && (GL(dl_ns)[ns]._ns_main_searchlist->r_nlist + for (Lmid_t nsid = 0; nsid < DL_NNS; ++nsid) + if (__builtin_expect (GL(dl_ns)[nsid]._ns_global_scope_alloc, 0) != 0 + && (GL(dl_ns)[nsid]._ns_main_searchlist->r_nlist // XXX Check whether we need NS-specific initial_searchlist == GLRO(dl_initial_searchlist).r_nlist)) { /* All object dynamically loaded by the program are unloaded. Free the memory allocated for the global scope variable. */ - struct link_map **old = GL(dl_ns)[ns]._ns_main_searchlist->r_list; + struct link_map **old = GL(dl_ns)[nsid]._ns_main_searchlist->r_list; /* Put the old map in. */ - GL(dl_ns)[ns]._ns_main_searchlist->r_list + GL(dl_ns)[nsid]._ns_main_searchlist->r_list // XXX Check whether we need NS-specific initial_searchlist = GLRO(dl_initial_searchlist).r_list; /* Signal that the original map is used. */ - GL(dl_ns)[ns]._ns_global_scope_alloc = 0; + GL(dl_ns)[nsid]._ns_global_scope_alloc = 0; /* Now free the old map. */ free (old); @@ -680,10 +807,14 @@ libc_freeres_fn (free_mem) free_slotinfo (&GL(dl_tls_dtv_slotinfo_list)); else # endif - /* The first element of the list does not have to be deallocated. + /* The first element of the list does not have to be deallocated. It was allocated in the dynamic linker (i.e., with a different malloc), and in the static library it's in .bss space. */ free_slotinfo (&GL(dl_tls_dtv_slotinfo_list)->next); } #endif + + void *scope_free_list = GL(dl_scope_free_list); + GL(dl_scope_free_list) = NULL; + free (scope_free_list); } diff --git a/elf/dl-conflict.c b/elf/dl-conflict.c index 4ced40f..9b49e77 100644 --- a/elf/dl-conflict.c +++ b/elf/dl-conflict.c @@ -1,5 +1,5 @@ /* Resolve conflicts against already prelinked libraries. - Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Jakub Jelinek <jakub@redhat.com>, 2001. @@ -34,8 +34,8 @@ _dl_resolve_conflicts (struct link_map *l, ElfW(Rela) *conflict, { #if ! ELF_MACHINE_NO_RELA if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_RELOC, 0)) - _dl_printf ("\nconflict processing: %s\n", - l->l_name[0] ? l->l_name : rtld_progname); + _dl_debug_printf ("\nconflict processing: %s\n", + l->l_name[0] ? l->l_name : rtld_progname); { /* Do the conflict relocation of the object and library GOT and other diff --git a/elf/dl-debug.c b/elf/dl-debug.c index bd6ee69..2538364 100644 --- a/elf/dl-debug.c +++ b/elf/dl-debug.c @@ -1,5 +1,6 @@ /* Communicate dynamic linker state to the debugger at runtime. - Copyright (C) 1996, 1998, 2000, 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 1996, 1998,2000,2002,2004,2005,2006 + 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 @@ -19,6 +20,18 @@ #include <ldsodefs.h> + +/* These are the members in the public `struct link_map' type. + Sanity check that the internal type and the public type match. */ +#define VERIFY_MEMBER(name) \ + (offsetof (struct link_map_public, name) == offsetof (struct link_map, name)) +extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr) + && VERIFY_MEMBER (l_name) + && VERIFY_MEMBER (l_ld) + && VERIFY_MEMBER (l_next) + && VERIFY_MEMBER (l_prev)) + ? 1 : -1]; + /* This structure communicates dl state to the debugger. The debugger normally finds it via the DT_DEBUG entry in the dynamic section, but in a statically-linked program there is no dynamic section for the debugger @@ -32,20 +45,25 @@ struct r_debug _r_debug; struct r_debug * internal_function -_dl_debug_initialize (ElfW(Addr) ldbase) +_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) { - if (_r_debug.r_brk == 0) + struct r_debug *r; + + if (ns == LM_ID_BASE) + r = &_r_debug; + else + r = &GL(dl_ns)[ns]._ns_debug; + + if (r->r_map == NULL || ldbase != 0) { /* Tell the debugger where to find the map of loaded objects. */ - _r_debug.r_version = 1 /* R_DEBUG_VERSION XXX */; - _r_debug.r_ldbase = ldbase; - // XXX This is problematic. It means we cannot tell the debugger - // XXX about namespaces other than the main one. - _r_debug.r_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; - _r_debug.r_brk = (ElfW(Addr)) &_dl_debug_state; + r->r_version = 1 /* R_DEBUG_VERSION XXX */; + r->r_ldbase = ldbase ?: _r_debug.r_ldbase; + r->r_map = (void *) GL(dl_ns)[ns]._ns_loaded; + r->r_brk = (ElfW(Addr)) &_dl_debug_state; } - return &_r_debug; + return r; } diff --git a/elf/dl-deps.c b/elf/dl-deps.c index a1c16d7..c35cc97 100644 --- a/elf/dl-deps.c +++ b/elf/dl-deps.c @@ -1,5 +1,5 @@ /* Load the dependencies of a mapped object. - Copyright (C) 1996-2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1996-2003, 2004, 2005, 2006 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 @@ -92,7 +92,7 @@ struct list { int done; /* Nonzero if this map was processed. */ struct link_map *map; /* The data. */ - struct list *next; /* Elements for normal list. */ + struct list *next; /* Elements for normal list. */ }; @@ -101,9 +101,9 @@ struct list ({ \ const char *__str = (str); \ const char *__result = __str; \ - size_t __cnt = DL_DST_COUNT(__str, 0); \ + size_t __dst_cnt = DL_DST_COUNT (__str, 0); \ \ - if (__cnt != 0) \ + if (__dst_cnt != 0) \ { \ char *__newp; \ \ @@ -113,9 +113,9 @@ struct list DST not allowed in SUID/SGID programs")); \ \ __newp = (char *) alloca (DL_DST_REQUIRED (l, __str, strlen (__str), \ - __cnt)); \ + __dst_cnt)); \ \ - __result = _dl_dst_substitute (l, __str, __newp, 0); \ + __result = _dl_dst_substitute (l, __str, __newp, 0); \ \ if (*__result == '\0') \ { \ @@ -235,16 +235,23 @@ _dl_map_object_deps (struct link_map *map, { /* Map in the needed object. */ struct link_map *dep; - int err; /* Recognize DSTs. */ name = expand_dst (l, strtab + d->d_un.d_val, 0); /* Store the tag in the argument structure. */ args.name = name; - err = _dl_catch_error (&objname, &errstring, openaux, &args); + bool malloced; + int err = _dl_catch_error (&objname, &errstring, &malloced, + openaux, &args); if (__builtin_expect (errstring != NULL, 0)) { + char *new_errstring = strdupa (errstring); + objname = strdupa (objname); + if (malloced) + free ((char *) errstring); + errstring = new_errstring; + if (err) errno_reason = err; else @@ -288,8 +295,6 @@ _dl_map_object_deps (struct link_map *map, if (d->d_tag == DT_AUXILIARY) { - int err; - /* Say that we are about to load an auxiliary library. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) @@ -301,13 +306,14 @@ _dl_map_object_deps (struct link_map *map, /* We must be prepared that the addressed shared object is not available. */ - err = _dl_catch_error (&objname, &errstring, openaux, - &args); + bool malloced; + (void) _dl_catch_error (&objname, &errstring, &malloced, + openaux, &args); if (__builtin_expect (errstring != NULL, 0)) { /* We are not interested in the error message. */ assert (errstring != NULL); - if (errstring != INTUSE(_dl_out_of_memory)) + if (malloced) free ((char *) errstring); /* Simply ignore this error and continue the work. */ @@ -316,8 +322,6 @@ _dl_map_object_deps (struct link_map *map, } else { - int err; - /* Say that we are about to load an auxiliary library. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) @@ -328,10 +332,17 @@ _dl_map_object_deps (struct link_map *map, ? l->l_name : rtld_progname); /* For filter objects the dependency must be available. */ - err = _dl_catch_error (&objname, &errstring, openaux, - &args); + bool malloced; + int err = _dl_catch_error (&objname, &errstring, &malloced, + openaux, &args); if (__builtin_expect (errstring != NULL, 0)) { + char *new_errstring = strdupa (errstring); + objname = strdupa (objname); + if (malloced) + free ((char *) errstring); + errstring = new_errstring; + if (err) errno_reason = err; else @@ -566,8 +577,6 @@ Filters not supported with LD_TRACE_PRELINKING")); { /* A direct or transitive dependency is also on the list of relocation dependencies. Remove the latter. */ - --map->l_reldeps[i]->l_opencount; - for (j = i + 1; j < map->l_reldepsact; ++j) map->l_reldeps[j - 1] = map->l_reldeps[j]; diff --git a/elf/dl-dst.h b/elf/dl-dst.h index 42bd418..175b7cd 100644 --- a/elf/dl-dst.h +++ b/elf/dl-dst.h @@ -1,5 +1,6 @@ /* Handling of dynamic sring tokens. - Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1999,2001,2002,2003,2004,2006,2007 + 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 @@ -50,6 +51,7 @@ \ First get the origin string if it is not available yet. \ This can only happen for the map of the executable. */ \ + DL_DST_REQ_STATIC (l) \ if ((l)->l_origin == NULL) \ { \ assert ((l)->l_name[0] == '\0'); \ @@ -66,6 +68,18 @@ \ __len; }) +#ifdef SHARED +# define DL_DST_REQ_STATIC(l) /* nothing */ +#else +# define DL_DST_REQ_STATIC(l) \ + if ((l) == NULL) \ + { \ + const char *origin = _dl_get_origin (); \ + origin_len = (origin && origin != (char *) -1 ? strlen (origin) : 0); \ + } \ + else +#endif + #ifndef IS_IN_rtld # define _dl_get_origin GLRO(dl_get_origin) # define _dl_dst_substitute GLRO(dl_dst_substitute) diff --git a/elf/dl-environ.c b/elf/dl-environ.c new file mode 100644 index 0000000..089e89e --- /dev/null +++ b/elf/dl-environ.c @@ -0,0 +1,86 @@ +/* Environment handling for dynamic loader. + Copyright (C) 1995-1998, 2000, 2001, 2002 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <ldsodefs.h> + +/* Walk through the environment of the process and return all entries + starting with `LD_'. */ +char * +internal_function +_dl_next_ld_env_entry (char ***position) +{ + char **current = *position; + char *result = NULL; + + while (*current != NULL) + { + if (__builtin_expect ((*current)[0] == 'L', 0) + && (*current)[1] == 'D' && (*current)[2] == '_') + { + result = &(*current)[3]; + + /* Save current position for next visit. */ + *position = ++current; + + break; + } + + ++current; + } + + return result; +} + + +/* In ld.so __environ is not exported. */ +extern char **__environ attribute_hidden; + +int +unsetenv (const char *name) +{ + char **ep; + + ep = __environ; + while (*ep != NULL) + { + size_t cnt = 0; + + while ((*ep)[cnt] == name[cnt] && name[cnt] != '\0') + ++cnt; + + if (name[cnt] == '\0' && (*ep)[cnt] == '=') + { + /* Found it. Remove this pointer by moving later ones to + the front. */ + char **dp = ep; + + do + dp[0] = dp[1]; + while (*dp++); + /* Continue the loop in case NAME appears again. */ + } + else + ++ep; + } + + return 0; +} diff --git a/elf/dl-error.c b/elf/dl-error.c index 0ef76c8..79ebaaf 100644 --- a/elf/dl-error.c +++ b/elf/dl-error.c @@ -1,5 +1,5 @@ /* Error handling for runtime dynamic linker. - Copyright (C) 1995-2002,2004 Free Software Foundation, Inc. + Copyright (C) 1995-2002,2004,2005 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 @@ -19,6 +19,7 @@ #include <libintl.h> #include <setjmp.h> +#include <stdbool.h> #include <stdlib.h> #include <string.h> #include <unistd.h> @@ -30,6 +31,8 @@ struct catch { const char *objname; /* Object/File name. */ const char *errstring; /* Error detail filled in here. */ + bool malloced; /* Nonzero if the string is malloced + by the libc malloc. */ jmp_buf env; /* longjmp here on error. */ }; @@ -44,8 +47,7 @@ struct catch /* This message we return as a last resort. We define the string in a variable since we have to avoid freeing it and so have to enable a pointer comparison. See below and in dlfcn/dlerror.c. */ -const char _dl_out_of_memory[] = "out of memory"; -INTVARDEF(_dl_out_of_memory) +static const char _dl_out_of_memory[] = "out of memory"; /* This points to a function which is called when an continuable error is @@ -87,17 +89,31 @@ _dl_signal_error (int errcode, const char *objname, const char *occation, lcatch->errstring = (char *) malloc (len_objname + len_errstring); if (lcatch->errstring != NULL) - /* Make a copy of the object file name and the error string. */ - lcatch->objname = memcpy (__mempcpy ((char *) lcatch->errstring, - errstring, len_errstring), - objname, len_objname); + { + /* Make a copy of the object file name and the error string. */ + lcatch->objname = memcpy (__mempcpy ((char *) lcatch->errstring, + errstring, len_errstring), + objname, len_objname); + + /* If the main executable is relocated it means the libc's malloc + is used. */ +#ifdef SHARED + lcatch->malloced = (GL(dl_ns)[LM_ID_BASE]._ns_loaded != NULL + && (GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_relocated + != 0)); +#else + lcatch->malloced = true; +#endif + } else { /* This is better than nothing. */ lcatch->objname = ""; - lcatch->errstring = INTUSE(_dl_out_of_memory); + lcatch->errstring = _dl_out_of_memory; + lcatch->malloced = false; } - longjmp (lcatch->env, errcode ?: -1); + /* We do not restore the signal mask because none was saved. */ + __longjmp (lcatch->env[0].__jmpbuf, errcode ?: -1); } else { @@ -140,7 +156,7 @@ _dl_signal_cerror (int errcode, const char *objname, const char *occation, int internal_function _dl_catch_error (const char **objname, const char **errstring, - void (*operate) (void *), void *args) + bool *mallocedp, void (*operate) (void *), void *args) { int errcode; struct catch *volatile old; @@ -154,7 +170,8 @@ _dl_catch_error (const char **objname, const char **errstring, struct catch **const catchp = &CATCH_HOOK; old = *catchp; - errcode = setjmp (c.env); + /* Do not save the signal mask. */ + errcode = __sigsetjmp (c.env, 0); if (__builtin_expect (errcode, 0) == 0) { *catchp = &c; @@ -162,6 +179,7 @@ _dl_catch_error (const char **objname, const char **errstring, *catchp = old; *objname = NULL; *errstring = NULL; + *mallocedp = false; return 0; } @@ -169,6 +187,7 @@ _dl_catch_error (const char **objname, const char **errstring, *catchp = old; *objname = c.objname; *errstring = c.errstring; + *mallocedp = c.malloced; return errcode == -1 ? 0 : errcode; } diff --git a/elf/dl-execstack.c b/elf/dl-execstack.c new file mode 100644 index 0000000..6dce21e --- /dev/null +++ b/elf/dl-execstack.c @@ -0,0 +1,32 @@ +/* Stack executability handling for GNU dynamic linker. Stub version. + Copyright (C) 2003, 2004 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <ldsodefs.h> +#include <errno.h> + +/* There is no portable way to know the bounds of the initial thread's stack + so as to mprotect it. */ + +int +internal_function +_dl_make_stack_executable (void **stack_endp) +{ + return ENOSYS; +} +rtld_hidden_def (_dl_make_stack_executable) diff --git a/elf/dl-fini.c b/elf/dl-fini.c index f43f4a0..3cd7e7b 100644 --- a/elf/dl-fini.c +++ b/elf/dl-fini.c @@ -1,5 +1,5 @@ /* Call the termination functions of loaded shared objects. - Copyright (C) 1995,96,1998-2002,2004 Free Software Foundation, Inc. + Copyright (C) 1995,96,1998-2002,2004, 2005 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 @@ -29,6 +29,94 @@ typedef void (*fini_t) (void); void internal_function +_dl_sort_fini (struct link_map *l, struct link_map **maps, size_t nmaps, + char *used, Lmid_t ns) +{ + if (ns == LM_ID_BASE) + /* The main executable always comes first. */ + l = l->l_next; + + for (; l != NULL; l = l->l_next) + /* Do not handle ld.so in secondary namespaces and object which + are not removed. */ + if (l == l->l_real && l->l_idx != -1) + { + /* Find the place in the 'maps' array. */ + unsigned int j; + for (j = ns == LM_ID_BASE ? 1 : 0; maps[j] != l; ++j) + assert (j < nmaps); + + /* Find all object for which the current one is a dependency + and move the found object (if necessary) in front. */ + for (unsigned int k = j + 1; k < nmaps; ++k) + { + struct link_map **runp = maps[k]->l_initfini; + if (runp != NULL) + { + while (*runp != NULL) + if (*runp == l) + { + struct link_map *here = maps[k]; + + /* Move it now. */ + memmove (&maps[j] + 1, + &maps[j], (k - j) * sizeof (struct link_map *)); + maps[j] = here; + + if (used != NULL) + { + char here_used = used[k]; + + memmove (&used[j] + 1, + &used[j], (k - j) * sizeof (char)); + used[j] = here_used; + } + + ++j; + + break; + } + else + ++runp; + } + + if (__builtin_expect (maps[k]->l_reldeps != NULL, 0)) + { + unsigned int m = maps[k]->l_reldepsact; + struct link_map **relmaps = maps[k]->l_reldeps; + + while (m-- > 0) + { + if (relmaps[m] == l) + { + struct link_map *here = maps[k]; + + /* Move it now. */ + memmove (&maps[j] + 1, + &maps[j], + (k - j) * sizeof (struct link_map *)); + maps[j] = here; + + if (used != NULL) + { + char here_used = used[k]; + + memmove (&used[j] + 1, + &used[j], (k - j) * sizeof (char)); + used[j] = here_used; + } + + break; + } + } + } + } + } +} + + +void +internal_function _dl_fini (void) { /* Lots of fun ahead. We have to call the destructors for all still @@ -48,16 +136,29 @@ _dl_fini (void) /* We run the destructors of the main namespaces last. As for the other namespaces, we pick run the destructors in them in reverse order of the namespace ID. */ - for (Lmid_t cnt = DL_NNS - 1; cnt >= 0; --cnt) +#ifdef SHARED + int do_audit = 0; + again: +#endif + for (Lmid_t ns = DL_NNS - 1; ns >= 0; --ns) { /* Protect against concurrent loads and unloads. */ __rtld_lock_lock_recursive (GL(dl_load_lock)); - unsigned int nloaded = GL(dl_ns)[cnt]._ns_nloaded; + unsigned int nmaps = 0; + unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded; + /* No need to do anything for empty namespaces or those used for + auditing DSOs. */ + if (nloaded == 0 +#ifdef SHARED + || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit +#endif + ) + goto out; /* XXX Could it be (in static binaries) that there is no object loaded? */ - assert (cnt != LM_ID_BASE || nloaded > 0); + assert (ns != LM_ID_BASE || nloaded > 0); /* Now we can allocate an array to hold all the pointers and copy the pointers in. */ @@ -76,86 +177,28 @@ _dl_fini (void) unsigned int i; struct link_map *l; - for (l = GL(dl_ns)[cnt]._ns_loaded, i = 0; l != NULL; l = l->l_next) + assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL); + for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next) /* Do not handle ld.so in secondary namespaces. */ if (l == l->l_real) { assert (i < nloaded); - maps[i++] = l; + maps[i] = l; + l->l_idx = i; + ++i; - /* Bump l_opencount of all objects so that they are not - dlclose()ed from underneath us. */ - ++l->l_opencount; + /* Bump l_direct_opencount of all objects so that they are + not dlclose()ed from underneath us. */ + ++l->l_direct_opencount; } - assert (cnt != LM_ID_BASE || i == nloaded); - assert (cnt == LM_ID_BASE || i == nloaded || i == nloaded - 1); - unsigned int nmaps = i; + assert (ns != LM_ID_BASE || i == nloaded); + assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1); + nmaps = i; if (nmaps != 0) - { - /* Now we have to do the sorting. */ - l = GL(dl_ns)[cnt]._ns_loaded; - if (cnt == LM_ID_BASE) - /* The main executable always comes first. */ - l = l->l_next; - for (; l != NULL; l = l->l_next) - /* Do not handle ld.so in secondary namespaces. */ - if (l == l->l_real) - { - /* Find the place in the 'maps' array. */ - unsigned int j; - for (j = cnt == LM_ID_BASE ? 1 : 0; maps[j] != l; ++j) - assert (j < nmaps); - - /* Find all object for which the current one is a dependency - and move the found object (if necessary) in front. */ - for (unsigned int k = j + 1; k < nmaps; ++k) - { - struct link_map **runp = maps[k]->l_initfini; - if (runp != NULL) - { - while (*runp != NULL) - if (*runp == l) - { - struct link_map *here = maps[k]; - - /* Move it now. */ - memmove (&maps[j] + 1, - &maps[j], - (k - j) * sizeof (struct link_map *)); - maps[j++] = here; - - break; - } - else - ++runp; - } - - if (__builtin_expect (maps[k]->l_reldeps != NULL, 0)) - { - unsigned int m = maps[k]->l_reldepsact; - struct link_map **relmaps = maps[k]->l_reldeps; - - while (m-- > 0) - { - if (relmaps[m] == l) - { - struct link_map *here = maps[k]; - - /* Move it now. */ - memmove (&maps[j] + 1, - &maps[j], - (k - j) * sizeof (struct link_map *)); - maps[j] = here; - - break; - } - } - } - } - } - } + /* Now we have to do the sorting. */ + _dl_sort_fini (GL(dl_ns)[ns]._ns_loaded, maps, nmaps, NULL, ns); /* We do not rely on the linked list of loaded object anymore from this point on. We have our own list here (maps). The various @@ -163,6 +206,7 @@ _dl_fini (void) high and will be decremented in this loop. So we release the lock so that some code which might be called from a destructor can directly or indirectly access the lock. */ + out: __rtld_lock_unlock_recursive (GL(dl_load_lock)); /* 'maps' now contains the objects in the right order. Now call the @@ -176,49 +220,68 @@ _dl_fini (void) /* Make sure nothing happens if we are called twice. */ l->l_init_called = 0; - /* Don't call the destructors for objects we are not - supposed to. */ - if (l->l_name[0] == '\0' && l->l_type == lt_executable) - continue; - /* Is there a destructor function? */ - if (l->l_info[DT_FINI_ARRAY] == NULL - && l->l_info[DT_FINI] == NULL) - continue; - - /* When debugging print a message first. */ - if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, - 0)) - _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", - l->l_name[0] ? l->l_name : rtld_progname, - cnt); - - /* First see whether an array is given. */ - if (l->l_info[DT_FINI_ARRAY] != NULL) + if (l->l_info[DT_FINI_ARRAY] != NULL + || l->l_info[DT_FINI] != NULL) { - ElfW(Addr) *array = - (ElfW(Addr) *) (l->l_addr - + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); - unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val - / sizeof (ElfW(Addr))); - while (i-- > 0) - ((fini_t) array[i]) (); + /* When debugging print a message first. */ + if (__builtin_expect (GLRO(dl_debug_mask) + & DL_DEBUG_IMPCALLS, 0)) + _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", + l->l_name[0] ? l->l_name : rtld_progname, + ns); + + /* First see whether an array is given. */ + if (l->l_info[DT_FINI_ARRAY] != NULL) + { + ElfW(Addr) *array = + (ElfW(Addr) *) (l->l_addr + + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); + unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val + / sizeof (ElfW(Addr))); + while (i-- > 0) + ((fini_t) array[i]) (); + } + + /* Next try the old-style destructor. */ + if (l->l_info[DT_FINI] != NULL) + ((fini_t) DL_DT_FINI_ADDRESS (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr)) (); } - /* Next try the old-style destructor. */ - if (l->l_info[DT_FINI] != NULL) - ((fini_t) DL_DT_FINI_ADDRESS (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr)) (); +#ifdef SHARED + /* Auditing checkpoint: another object closed. */ + if (!do_audit && __builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objclose != NULL) + /* Return value is ignored. */ + (void) afct->objclose (&l->l_audit[cnt].cookie); + + afct = afct->next; + } + } +#endif } /* Correct the previous increment. */ - --l->l_opencount; + --l->l_direct_opencount; } } +#ifdef SHARED + if (! do_audit && GLRO(dl_naudit) > 0) + { + do_audit = 1; + goto again; + } + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS, 0)) _dl_debug_printf ("\nruntime linker statistics:\n" " final number of relocations: %lu\n" "final number of relocations from cache: %lu\n", GL(dl_num_relocations), GL(dl_num_cache_relocations)); +#endif } diff --git a/elf/dl-fptr.c b/elf/dl-fptr.c new file mode 100644 index 0000000..78beecf --- /dev/null +++ b/elf/dl-fptr.c @@ -0,0 +1,323 @@ +/* Manage function descriptors. Generic version. + Copyright (C) 1999,2000,2001,2002,2003,2004 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <libintl.h> +#include <unistd.h> +#include <string.h> +#include <sys/param.h> +#include <sys/mman.h> +#include <link.h> +#include <ldsodefs.h> +#include <elf/dynamic-link.h> +#include <dl-fptr.h> +#include <atomic.h> + +#ifndef ELF_MACHINE_BOOT_FPTR_TABLE_LEN +/* ELF_MACHINE_BOOT_FPTR_TABLE_LEN should be greater than the number of + dynamic symbols in ld.so. */ +# define ELF_MACHINE_BOOT_FPTR_TABLE_LEN 256 +#endif + +#ifndef ELF_MACHINE_LOAD_ADDRESS +# error "ELF_MACHINE_LOAD_ADDRESS is not defined." +#endif + +#ifndef COMPARE_AND_SWAP +# define COMPARE_AND_SWAP(ptr, old, new) \ + (atomic_compare_and_exchange_bool_acq (ptr, new, old) == 0) +#endif + +ElfW(Addr) _dl_boot_fptr_table [ELF_MACHINE_BOOT_FPTR_TABLE_LEN]; + +static struct local + { + struct fdesc_table *root; + struct fdesc *free_list; + unsigned int npages; /* # of pages to allocate */ + /* the next to members MUST be consecutive! */ + struct fdesc_table boot_table; + struct fdesc boot_fdescs[1024]; + } +local = + { + .root = &local.boot_table, + .npages = 2, + .boot_table = + { + .len = sizeof (local.boot_fdescs) / sizeof (local.boot_fdescs[0]), + .first_unused = 0 + } + }; + +/* Create a new fdesc table and return a pointer to the first fdesc + entry. The fdesc lock must have been acquired already. */ + +static struct fdesc_table * +new_fdesc_table (struct local *l, size_t *size) +{ + size_t old_npages = l->npages; + size_t new_npages = old_npages + old_npages; + struct fdesc_table *new_table; + + /* If someone has just created a new table, we return NULL to tell + the caller to use the new table. */ + if (! COMPARE_AND_SWAP (&l->npages, old_npages, new_npages)) + return (struct fdesc_table *) NULL; + + *size = old_npages * GLRO(dl_pagesize); + new_table = __mmap (NULL, *size, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + if (new_table == MAP_FAILED) + _dl_signal_error (errno, NULL, NULL, + N_("cannot map pages for fdesc table")); + + new_table->len + = (*size - sizeof (*new_table)) / sizeof (struct fdesc); + new_table->first_unused = 1; + return new_table; +} + + +static ElfW(Addr) +make_fdesc (ElfW(Addr) ip, ElfW(Addr) gp) +{ + struct fdesc *fdesc = NULL; + struct fdesc_table *root; + unsigned int old; + struct local *l; + + ELF_MACHINE_LOAD_ADDRESS (l, local); + + retry: + root = l->root; + while (1) + { + old = root->first_unused; + if (old >= root->len) + break; + else if (COMPARE_AND_SWAP (&root->first_unused, old, old + 1)) + { + fdesc = &root->fdesc[old]; + goto install; + } + } + + if (l->free_list) + { + /* Get it from free-list. */ + do + { + fdesc = l->free_list; + if (fdesc == NULL) + goto retry; + } + while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->free_list, + (ElfW(Addr)) fdesc, fdesc->ip)); + } + else + { + /* Create a new fdesc table. */ + size_t size; + struct fdesc_table *new_table = new_fdesc_table (l, &size); + + if (new_table == NULL) + goto retry; + + new_table->next = root; + if (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->root, + (ElfW(Addr)) root, + (ElfW(Addr)) new_table)) + { + /* Someone has just installed a new table. Return NULL to + tell the caller to use the new table. */ + __munmap (new_table, size); + goto retry; + } + + /* Note that the first entry was reserved while allocating the + memory for the new page. */ + fdesc = &new_table->fdesc[0]; + } + + install: + fdesc->ip = ip; + fdesc->gp = gp; + + return (ElfW(Addr)) fdesc; +} + + +static inline ElfW(Addr) * __attribute__ ((always_inline)) +make_fptr_table (struct link_map *map) +{ + const ElfW(Sym) *symtab + = (const void *) D_PTR (map, l_info[DT_SYMTAB]); + const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + ElfW(Addr) *fptr_table; + size_t size; + size_t len; + + /* XXX Apparently the only way to find out the size of the dynamic + symbol section is to assume that the string table follows right + afterwards... */ + len = ((strtab - (char *) symtab) + / map->l_info[DT_SYMENT]->d_un.d_val); + size = ((len * sizeof (fptr_table[0]) + GLRO(dl_pagesize) - 1) + & -GLRO(dl_pagesize)); + /* XXX We don't support here in the moment systems without MAP_ANON. + There probably are none for IA-64. In case this is proven wrong + we will have to open /dev/null here and use the file descriptor + instead of the hard-coded -1. */ + fptr_table = __mmap (NULL, size, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, + -1, 0); + if (fptr_table == MAP_FAILED) + _dl_signal_error (errno, NULL, NULL, + N_("cannot map pages for fptr table")); + + if (COMPARE_AND_SWAP ((ElfW(Addr) *) &map->l_mach.fptr_table, + (ElfW(Addr)) NULL, (ElfW(Addr)) fptr_table)) + map->l_mach.fptr_table_len = len; + else + __munmap (fptr_table, len * sizeof (fptr_table[0])); + + return map->l_mach.fptr_table; +} + + +ElfW(Addr) +_dl_make_fptr (struct link_map *map, const ElfW(Sym) *sym, + ElfW(Addr) ip) +{ + ElfW(Addr) *ftab = map->l_mach.fptr_table; + const ElfW(Sym) *symtab; + Elf_Symndx symidx; + struct local *l; + + if (__builtin_expect (ftab == NULL, 0)) + ftab = make_fptr_table (map); + + symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]); + symidx = sym - symtab; + + if (symidx >= map->l_mach.fptr_table_len) + _dl_signal_error (0, NULL, NULL, + N_("internal error: symidx out of range of fptr table")); + + while (ftab[symidx] == 0) + { + /* GOT has already been relocated in elf_get_dynamic_info - + don't try to relocate it again. */ + ElfW(Addr) fdesc + = make_fdesc (ip, map->l_info[DT_PLTGOT]->d_un.d_ptr); + + if (__builtin_expect (COMPARE_AND_SWAP (&ftab[symidx], (ElfW(Addr)) NULL, + fdesc), 1)) + { + /* Noone has updated the entry and the new function + descriptor has been installed. */ +#if 0 + const char *strtab + = (const void *) D_PTR (map, l_info[DT_STRTAB]); + + ELF_MACHINE_LOAD_ADDRESS (l, local); + if (l->root != &l->boot_table + || l->boot_table.first_unused > 20) + _dl_debug_printf ("created fdesc symbol `%s' at %lx\n", + strtab + sym->st_name, ftab[symidx]); +#endif + break; + } + else + { + /* We created a duplicated function descriptor. We put it on + free-list. */ + struct fdesc *f = (struct fdesc *) fdesc; + + ELF_MACHINE_LOAD_ADDRESS (l, local); + + do + f->ip = (ElfW(Addr)) l->free_list; + while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->free_list, + f->ip, fdesc)); + } + } + + return ftab[symidx]; +} + + +void +_dl_unmap (struct link_map *map) +{ + ElfW(Addr) *ftab = map->l_mach.fptr_table; + struct fdesc *head = NULL, *tail = NULL; + size_t i; + + __munmap ((void *) map->l_map_start, + map->l_map_end - map->l_map_start); + + if (ftab == NULL) + return; + + /* String together the fdesc structures that are being freed. */ + for (i = 0; i < map->l_mach.fptr_table_len; ++i) + { + if (ftab[i]) + { + *(struct fdesc **) ftab[i] = head; + head = (struct fdesc *) ftab[i]; + if (tail == NULL) + tail = head; + } + } + + /* Prepend the new list to the free_list: */ + if (tail) + do + tail->ip = (ElfW(Addr)) local.free_list; + while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &local.free_list, + tail->ip, (ElfW(Addr)) head)); + + __munmap (ftab, (map->l_mach.fptr_table_len + * sizeof (map->l_mach.fptr_table[0]))); + + map->l_mach.fptr_table = NULL; +} + + +ElfW(Addr) +_dl_lookup_address (const void *address) +{ + ElfW(Addr) addr = (ElfW(Addr)) address; + struct fdesc_table *t; + unsigned long int i; + + for (t = local.root; t != NULL; t = t->next) + { + i = (struct fdesc *) addr - &t->fdesc[0]; + if (i < t->first_unused && addr == (ElfW(Addr)) &t->fdesc[i]) + { + addr = t->fdesc[i].ip; + break; + } + } + + return addr; +} diff --git a/elf/dl-init.c b/elf/dl-init.c index e700dff..e7b6757 100644 --- a/elf/dl-init.c +++ b/elf/dl-init.c @@ -93,7 +93,6 @@ _dl_init (struct link_map *main_map, int argc, char **argv, char **env) { ElfW(Dyn) *preinit_array = main_map->l_info[DT_PREINIT_ARRAY]; ElfW(Dyn) *preinit_array_size = main_map->l_info[DT_PREINIT_ARRAYSZ]; - struct r_debug *r; unsigned int i; if (__builtin_expect (GL(dl_initfirst) != NULL, 0)) @@ -120,13 +119,6 @@ _dl_init (struct link_map *main_map, int argc, char **argv, char **env) ((init_t) addrs[cnt]) (argc, argv, env); } - /* Notify the debugger we have added some objects. We need to call - _dl_debug_initialize in a static program in case dynamic linking has - not been used before. */ - r = _dl_debug_initialize (0); - r->r_state = RT_ADD; - _dl_debug_state (); - /* Stupid users forced the ELF specification to be changed. It now says that the dynamic loader is responsible for determining the order in which the constructors have to run. The constructors @@ -141,10 +133,6 @@ _dl_init (struct link_map *main_map, int argc, char **argv, char **env) while (i-- > 0) call_init (main_map->l_initfini[i], argc, argv, env); - /* Notify the debugger all new objects are now ready to go. */ - r->r_state = RT_CONSISTENT; - _dl_debug_state (); - #ifndef HAVE_INLINED_SYSCALLS /* Finished starting up. */ INTUSE(_dl_starting_up) = 0; diff --git a/elf/dl-iteratephdr.c b/elf/dl-iteratephdr.c index 6ed90c7..b29534d 100644 --- a/elf/dl-iteratephdr.c +++ b/elf/dl-iteratephdr.c @@ -1,5 +1,5 @@ /* Get loaded objects program headers. - Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2001,2002,2003,2004,2006,2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Jakub Jelinek <jakub@redhat.com>, 2001. @@ -54,9 +54,9 @@ __dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, nloaded += GL(dl_ns)[cnt]._ns_nloaded; if (caller >= (const void *) l->l_map_start - && caller < (const void *) l->l_map_end) - /* There must be exactly one DSO for the range of the virtual - memory. Otherwise something is really broken. */ + && caller < (const void *) l->l_map_end + && (l->l_contiguous + || _dl_addr_inside_object (l, (ElfW(Addr)) caller))) ns = cnt; } @@ -68,6 +68,13 @@ __dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, info.dlpi_phnum = l->l_phnum; info.dlpi_adds = GL(dl_load_adds); info.dlpi_subs = GL(dl_load_adds) - nloaded; + info.dlpi_tls_modid = 0; + info.dlpi_tls_data = NULL; +#ifdef USE_TLS + info.dlpi_tls_modid = l->l_tls_modid; + if (info.dlpi_tls_modid != 0) + info.dlpi_tls_data = _dl_tls_get_addr_soft (l); +#endif ret = callback (&info, sizeof (struct dl_phdr_info), data); if (ret) break; diff --git a/elf/dl-libc.c b/elf/dl-libc.c index 8a3f542..1b995ed 100644 --- a/elf/dl-libc.c +++ b/elf/dl-libc.c @@ -1,5 +1,5 @@ /* Handle loading and unloading shared objects for internal libc purposes. - Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 1999,2000,2001,2002,2004,2005 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Zack Weinberg <zack@rabi.columbia.edu>, 1999. @@ -22,6 +22,11 @@ #include <stdlib.h> #include <ldsodefs.h> +extern int __libc_argc attribute_hidden; +extern char **__libc_argv attribute_hidden; + +extern char **__environ; + /* The purpose of this file is to provide wrappers around the dynamic linker error mechanism (similar to dlopen() et al in libdl) which are usable from within libc. Generally we want to throw away the @@ -37,12 +42,13 @@ dlerror_run (void (*operate) (void *), void *args) { const char *objname; const char *last_errstring = NULL; - int result; + bool malloced; - (void) GLRO(dl_catch_error) (&objname, &last_errstring, operate, args); + (void) GLRO(dl_catch_error) (&objname, &last_errstring, &malloced, + operate, args); - result = last_errstring != NULL; - if (result && last_errstring != _dl_out_of_memory) + int result = last_errstring != NULL; + if (result && malloced) free ((char *) last_errstring); return result; @@ -77,7 +83,8 @@ do_dlopen (void *ptr) { struct do_dlopen_args *args = (struct do_dlopen_args *) ptr; /* Open and relocate the shared object. */ - args->map = _dl_open (args->name, args->mode, NULL, __LM_ID_CALLER); + args->map = GLRO(dl_open) (args->name, args->mode, NULL, __LM_ID_CALLER, + __libc_argc, __libc_argv, __environ); } static void @@ -93,7 +100,7 @@ do_dlsym (void *ptr) static void do_dlclose (void *ptr) { - _dl_close ((struct link_map *) ptr); + GLRO(dl_close) ((struct link_map *) ptr); } /* This code is to support __libc_dlopen from __libc_dlopen'ed shared @@ -109,7 +116,7 @@ struct dl_open_hook #ifdef SHARED extern struct dl_open_hook *_dl_open_hook; libc_hidden_proto (_dl_open_hook); -struct dl_open_hook *_dl_open_hook __attribute__((nocommon)); +struct dl_open_hook *_dl_open_hook __attribute__ ((nocommon)); libc_hidden_data_def (_dl_open_hook); #else static void @@ -119,7 +126,7 @@ do_dlsym_private (void *ptr) struct r_found_version vers; vers.name = "GLIBC_PRIVATE"; vers.hidden = 1; - /* vers.hash = _dl_elf_hash (version); */ + /* vers.hash = _dl_elf_hash (vers.name); */ vers.hash = 0x0963cf85; vers.filename = NULL; diff --git a/elf/dl-load.c b/elf/dl-load.c index eb1a791..9625030 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1,5 +1,5 @@ /* Map in a shared object's segments from the file. - Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1995-2005, 2006, 2007 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 @@ -33,9 +33,9 @@ #include <sys/types.h> #include "dynamic-link.h" #include <abi-tag.h> -#include <dl-osinfo.h> #include <stackinfo.h> #include <caller.h> +#include <sysdep.h> #include <dl-dst.h> @@ -50,15 +50,17 @@ overwritten. Some losing VM systems like Linux's lack MAP_COPY. All we get is MAP_PRIVATE, which copies each page when it is modified; this means if the file is overwritten, we may at some point get some pages - from the new version after starting with pages from the old version. */ -#ifndef MAP_COPY -# define MAP_COPY MAP_PRIVATE -#endif + from the new version after starting with pages from the old version. -/* We want to prevent people from modifying DSOs which are currently in - use. This is what MAP_DENYWRITE is for. */ -#ifndef MAP_DENYWRITE -# define MAP_DENYWRITE 0 + To make up for the lack and avoid the overwriting problem, + what Linux does have is MAP_DENYWRITE. This prevents anyone + from modifying the file while we have it mapped. */ +#ifndef MAP_COPY +# ifdef MAP_DENYWRITE +# define MAP_COPY (MAP_PRIVATE | MAP_DENYWRITE) +# else +# define MAP_COPY MAP_PRIVATE +# endif #endif /* Some systems link their relocatable objects for another base address @@ -105,13 +107,13 @@ ELF_PREFERRED_ADDRESS_DATA; int __stack_prot attribute_hidden attribute_relro - = (PROT_READ|PROT_WRITE #if _STACK_GROWS_DOWN && defined PROT_GROWSDOWN - |PROT_GROWSDOWN + = PROT_GROWSDOWN; #elif _STACK_GROWS_UP && defined PROT_GROWSUP - |PROT_GROWSUP + = PROT_GROWSUP; +#else + = 0; #endif - ); /* Type for the buffer we put the ELF header and hopefully the program @@ -122,19 +124,19 @@ int __stack_prot attribute_hidden attribute_relro question is how large are the ELF and program header combined. The ELF header 32-bit files is 52 bytes long and in 64-bit files is 64 bytes long. Each program header entry is again 32 and 56 bytes - long respectively. I.e., even with a file which has 7 program - header entries we only have to read 512B. Add to this a bit of - margin for program notes and reading 512B and 640B for 32-bit and - 64-bit files respecitvely is enough. If this heuristic should - really fail for some file the code in `_dl_map_object_from_fd' - knows how to recover. */ + long respectively. I.e., even with a file which has 10 program + header entries we only have to read 372B/624B respectively. Add to + this a bit of margin for program notes and reading 512B and 832B + for 32-bit and 64-bit files respecitvely is enough. If this + heuristic should really fail for some file the code in + `_dl_map_object_from_fd' knows how to recover. */ struct filebuf { ssize_t len; #if __WORDSIZE == 32 # define FILEBUF_SIZE 512 #else -# define FILEBUF_SIZE 640 +# define FILEBUF_SIZE 832 #endif char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr))))); }; @@ -264,7 +266,14 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result, ++name; if ((len = is_dst (start, name, "ORIGIN", is_path, INTUSE(__libc_enable_secure))) != 0) - repl = l->l_origin; + { +#ifndef SHARED + if (l == NULL) + repl = _dl_get_origin (); + else +#endif + repl = l->l_origin; + } else if ((len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0) repl = GLRO(dl_platform); else if ((len = is_dst (start, name, "LIB", is_path, 0)) != 0) @@ -502,7 +511,7 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep, } -static void +static bool internal_function decompose_rpath (struct r_search_path_struct *sps, const char *rpath, struct link_map *l, const char *what) @@ -537,19 +546,8 @@ decompose_rpath (struct r_search_path_struct *sps, { /* This object is on the list of objects for which the RUNPATH and RPATH must not be used. */ - result = calloc (1, sizeof *result); - if (result == NULL) - { - signal_error_cache: - errstring = N_("cannot create cache for search path"); - signal_error: - _dl_signal_error (ENOMEM, NULL, NULL, errstring); - } - - sps->dirs = result; - sps->malloced = 1; - - return; + sps->dirs = (void *) -1; + return false; } while (*inhp != '\0') @@ -579,7 +577,11 @@ decompose_rpath (struct r_search_path_struct *sps, result = (struct r_search_path_elem **) malloc ((nelems + 1 + 1) * sizeof (*result)); if (result == NULL) - goto signal_error_cache; + { + errstring = N_("cannot create cache for search path"); + signal_error: + _dl_signal_error (ENOMEM, NULL, NULL, errstring); + } fillin_rpath (copy, result, ":", 0, what, where); @@ -590,6 +592,7 @@ decompose_rpath (struct r_search_path_struct *sps, sps->dirs = result; /* The caller will change this value if we haven't used a real malloc. */ sps->malloced = 1; + return true; } /* Make sure cached path information is stored in *SP @@ -614,10 +617,9 @@ cache_rpath (struct link_map *l, } /* Make sure the cache information is available. */ - decompose_rpath (sp, (const char *) (D_PTR (l, l_info[DT_STRTAB]) - + l->l_info[tag]->d_un.d_val), - l, what); - return true; + return decompose_rpath (sp, (const char *) (D_PTR (l, l_info[DT_STRTAB]) + + l->l_info[tag]->d_un.d_val), + l, what); } @@ -740,7 +742,25 @@ _dl_init_paths (const char *llp) { size_t nllp; const char *cp = llp; - char *llp_tmp = strdupa (llp); + char *llp_tmp; + +#ifdef SHARED + /* Expand DSTs. */ + size_t cnt = DL_DST_COUNT (llp, 1); + if (__builtin_expect (cnt == 0, 1)) + llp_tmp = strdupa (llp); + else + { + /* Determine the length of the substituted string. */ + size_t total = DL_DST_REQUIRED (l, llp, strlen (llp), cnt); + + /* Allocate the necessary memory. */ + llp_tmp = (char *) alloca (total + 1); + llp_tmp = _dl_dst_substitute (l, llp, llp_tmp, 1); + } +#else + llp_tmp = strdupa (llp); +#endif /* Decompose the LD_LIBRARY_PATH contents. First determine how many elements it has. */ @@ -783,7 +803,7 @@ _dl_init_paths (const char *llp) static void __attribute__ ((noreturn, noinline)) lose (int code, int fd, const char *name, char *realname, struct link_map *l, - const char *msg) + const char *msg, struct r_debug *r) { /* The file might already be closed. */ if (fd != -1) @@ -802,6 +822,13 @@ lose (int code, int fd, const char *name, char *realname, struct link_map *l, free (l); } free (realname); + + if (r != NULL) + { + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + } + _dl_signal_error (code, name, NULL, msg); } @@ -827,6 +854,8 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, /* Initialize to keep the compiler happy. */ const char *errstring = NULL; int errval = 0; + struct r_debug *r = _dl_debug_initialize (0, nsid); + bool make_consistent = false; /* Get file information. */ if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st) < 0, 0)) @@ -835,12 +864,13 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, call_lose_errno: errval = errno; call_lose: - lose (errval, fd, name, realname, l, errstring); + lose (errval, fd, name, realname, l, errstring, + make_consistent ? r : NULL); } /* Look again to see if the real name matched another already loaded. */ for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next) - if (l->l_ino == st.st_ino && l->l_dev == st.st_dev) + if (l->l_removed == 0 && l->l_ino == st.st_ino && l->l_dev == st.st_dev) { /* The object is already loaded. Just bump its reference count and return it. */ @@ -905,6 +935,39 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, } #endif + /* Signal that we are going to add new objects. */ + if (r->r_state == RT_CONSISTENT) + { +#ifdef SHARED + /* Auditing checkpoint: we are going to add new objects. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct link_map *head = GL(dl_ns)[nsid]._ns_loaded; + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_ADD); + + afct = afct->next; + } + } + } +#endif + + /* Notify the debugger we have added some objects. We need to + call _dl_debug_initialize in a static program in case dynamic + linking has not been used before. */ + r->r_state = RT_ADD; + _dl_debug_state (); + make_consistent = true; + } + else + assert (r->r_state == RT_ADD); + /* Enter the new object in the list of loaded objects. */ l = _dl_new_object (realname, name, l_type, loader, mode, nsid); if (__builtin_expect (l == NULL, 0)) @@ -1044,7 +1107,7 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, } # ifdef SHARED - if (l->l_prev == NULL) + if (l->l_prev == NULL || (mode & __RTLD_AUDIT) != 0) /* We are loading the executable itself when the dynamic linker was executed directly. The setup will happen later. */ break; @@ -1140,7 +1203,7 @@ cannot allocate TLS data structures for initial thread"); /* Remember which part of the address space this object uses. */ l->l_map_start = (ElfW(Addr)) __mmap ((void *) mappref, maplength, c->prot, - MAP_COPY|MAP_FILE|MAP_DENYWRITE, + MAP_COPY|MAP_FILE, fd, c->mapoff); if (__builtin_expect ((void *) l->l_map_start == MAP_FAILED, 0)) { @@ -1159,9 +1222,11 @@ cannot allocate TLS data structures for initial thread"); handle the portion of the segment past the end of the file mapping. */ __mprotect ((caddr_t) (l->l_addr + c->mapend), - loadcmds[nloadcmds - 1].allocend - c->mapend, + loadcmds[nloadcmds - 1].mapstart - c->mapend, PROT_NONE); + l->l_contiguous = 1; + goto postmap; } @@ -1181,6 +1246,7 @@ cannot allocate TLS data structures for initial thread"); /* Remember which part of the address space this object uses. */ l->l_map_start = c->mapstart + l->l_addr; l->l_map_end = l->l_map_start + maplength; + l->l_contiguous = !has_holes; while (c < &loadcmds[nloadcmds]) { @@ -1188,7 +1254,7 @@ cannot allocate TLS data structures for initial thread"); /* Map the segment contents from the file. */ && (__mmap ((void *) (l->l_addr + c->mapstart), c->mapend - c->mapstart, c->prot, - MAP_FIXED|MAP_COPY|MAP_FILE|MAP_DENYWRITE, + MAP_FIXED|MAP_COPY|MAP_FILE, fd, c->mapoff) == MAP_FAILED)) goto map_error; @@ -1313,26 +1379,44 @@ cannot allocate TLS data structures for initial thread"); if (__builtin_expect ((stack_flags &~ GL(dl_stack_flags)) & PF_X, 0)) { + if (__builtin_expect (__check_caller (RETURN_ADDRESS (0), allow_ldso), + 0) != 0) + { + errstring = N_("invalid caller"); + goto call_lose; + } + /* The stack is presently not executable, but this module requires that it be executable. We must change the protection of the variable which contains the flags used in the mprotect calls. */ -#ifdef HAVE_Z_RELRO - if (mode & __RTLD_DLOPEN) +#if defined HAVE_Z_RELRO && defined SHARED + if ((mode & (__RTLD_DLOPEN | __RTLD_AUDIT)) == __RTLD_DLOPEN) { - uintptr_t p = ((uintptr_t) &__stack_prot) & ~(GLRO(dl_pagesize) - 1); - size_t s = (uintptr_t) &__stack_prot - p + sizeof (int); - - __mprotect ((void *) p, s, PROT_READ|PROT_WRITE); - if (__builtin_expect (__check_caller (RETURN_ADDRESS (0), - allow_ldso|allow_libc) == 0, - 0)) - __stack_prot |= PROT_EXEC; - __mprotect ((void *) p, s, PROT_READ); + const uintptr_t p = (uintptr_t) &__stack_prot & -GLRO(dl_pagesize); + const size_t s = (uintptr_t) (&__stack_prot + 1) - p; + + struct link_map *const m = &GL(dl_rtld_map); + const uintptr_t relro_end = ((m->l_addr + m->l_relro_addr + + m->l_relro_size) + & -GLRO(dl_pagesize)); + if (__builtin_expect (p + s <= relro_end, 1)) + { + /* The variable lies in the region protected by RELRO. */ + __mprotect ((void *) p, s, PROT_READ|PROT_WRITE); + __stack_prot |= PROT_READ|PROT_WRITE|PROT_EXEC; + __mprotect ((void *) p, s, PROT_READ); + } + else + __stack_prot |= PROT_READ|PROT_WRITE|PROT_EXEC; } else #endif - __stack_prot |= PROT_EXEC; + __stack_prot |= PROT_READ|PROT_WRITE|PROT_EXEC; + +#ifdef check_consistency + check_consistency (); +#endif errval = (*GL(dl_make_stack_executable_hook)) (stack_endp); if (errval) @@ -1424,6 +1508,27 @@ cannot enable executable stack as shared object requires"); add_name_to_object (l, ((const char *) D_PTR (l, l_info[DT_STRTAB]) + l->l_info[DT_SONAME]->d_un.d_val)); +#ifdef SHARED + /* Auditing checkpoint: we have a new object. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0) + && !GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objopen != NULL) + { + l->l_audit[cnt].bindflags + = afct->objopen (l, nsid, &l->l_audit[cnt].cookie); + + l->l_audit_any_plt |= l->l_audit[cnt].bindflags != 0; + } + + afct = afct->next; + } + } +#endif + return l; } @@ -1471,7 +1576,8 @@ print_search_path (struct r_search_path_elem **list, this could mean there is something wrong in the installation and the user might want to know about this. */ static int -open_verify (const char *name, struct filebuf *fbp) +open_verify (const char *name, struct filebuf *fbp, struct link_map *loader, + int whatcode, bool *found_other_class, bool free_name) { /* This is the expected ELF header. */ #define ELF32_CLASS ELFCLASS32 @@ -1500,13 +1606,34 @@ open_verify (const char *name, struct filebuf *fbp) ElfW(Word) type; char vendor[4]; } expected_note = { 4, 16, 1, "GNU" }; - int fd; /* Initialize it to make the compiler happy. */ const char *errstring = NULL; int errval = 0; +#ifdef SHARED + /* Give the auditing libraries a chance. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0) && whatcode != 0 + && loader->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objsearch != NULL) + { + name = afct->objsearch (name, &loader->l_audit[cnt].cookie, + whatcode); + if (name == NULL) + /* Ignore the path. */ + return -1; + } + + afct = afct->next; + } + } +#endif + /* Open the file. We always open files read-only. */ - fd = __open (name, O_RDONLY); + int fd = __open (name, O_RDONLY); if (fd != -1) { ElfW(Ehdr) *ehdr; @@ -1531,7 +1658,13 @@ open_verify (const char *name, struct filebuf *fbp) errstring = (errval == 0 ? N_("file too short") : N_("cannot read file data")); call_lose: - lose (errval, fd, name, NULL, NULL, errstring); + if (free_name) + { + char *realname = (char *) name; + name = strdupa (realname); + free (realname); + } + lose (errval, fd, name, NULL, NULL, errstring, NULL); } /* See whether the ELF header is what we expect. */ @@ -1554,10 +1687,13 @@ open_verify (const char *name, struct filebuf *fbp) ) errstring = N_("invalid ELF header"); else if (ehdr->e_ident[EI_CLASS] != ELFW(CLASS)) - /* This is not a fatal error. On architectures where - 32-bit and 64-bit binaries can be run this might - happen. */ - goto close_and_out; + { + /* This is not a fatal error. On architectures where + 32-bit and 64-bit binaries can be run this might + happen. */ + *found_other_class = true; + goto close_and_out; + } else if (ehdr->e_ident[EI_DATA] != byteorder) { if (BYTE_ORDER == BIG_ENDIAN) @@ -1664,7 +1800,8 @@ open_verify (const char *name, struct filebuf *fbp) static int open_path (const char *name, size_t namelen, int preloaded, struct r_search_path_struct *sps, char **realname, - struct filebuf *fbp) + struct filebuf *fbp, struct link_map *loader, int whatcode, + bool *found_other_class) { struct r_search_path_elem **dirs = sps->dirs; char *buf; @@ -1672,6 +1809,11 @@ open_path (const char *name, size_t namelen, int preloaded, const char *current_what = NULL; int any = 0; + if (__builtin_expect (dirs == NULL, 0)) + /* We're called before _dl_init_paths when loading the main executable + given on the command line when rtld is run directly. */ + return -1; + buf = alloca (max_dirnamelen + max_capstrlen + namelen); do { @@ -1708,12 +1850,17 @@ open_path (const char *name, size_t namelen, int preloaded, if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) _dl_debug_printf (" trying file=%s\n", buf); - fd = open_verify (buf, fbp); + fd = open_verify (buf, fbp, loader, whatcode, found_other_class, + false); if (this_dir->status[cnt] == unknown) { if (fd != -1) this_dir->status[cnt] = existing; - else + /* Do not update the directory information when loading + auditing code. We must try to disturb the program as + little as possible. */ + else if (loader == NULL + || GL(dl_ns)[loader->l_ns]._ns_loaded->l_auditing == 0) { /* We failed to open machine dependent library. Let's test whether there is any directory at all. */ @@ -1731,7 +1878,7 @@ open_path (const char *name, size_t namelen, int preloaded, } /* Remember whether we found any existing directory. */ - here_any |= this_dir->status[cnt] == existing; + here_any |= this_dir->status[cnt] != nonexisting; if (fd != -1 && __builtin_expect (preloaded, 0) && INTUSE(__libc_enable_secure)) @@ -1788,7 +1935,12 @@ open_path (const char *name, size_t namelen, int preloaded, must not be freed using the general free() in libc. */ if (sps->malloced) free (sps->dirs); - sps->dirs = (void *) -1; +#ifdef HAVE_Z_RELRO + /* rtld_search_dirs is attribute_relro, therefore avoid writing + into it. */ + if (sps != &rtld_search_dirs) +#endif + sps->dirs = (void *) -1; } return -1; @@ -1816,7 +1968,8 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, /* If the requested name matches the soname of a loaded object, use that object. Elide this check for names that have not yet been opened. */ - if (__builtin_expect (l->l_faked, 0) != 0) + if (__builtin_expect (l->l_faked, 0) != 0 + || __builtin_expect (l->l_removed, 0) != 0) continue; if (!_dl_name_match_p (name, l)) { @@ -1847,6 +2000,35 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, loader->l_name[0] ? loader->l_name : rtld_progname, loader->l_ns); +#ifdef SHARED + /* Give the auditing libraries a chance to change the name before we + try anything. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0) + && (loader == NULL || loader->l_auditing == 0)) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objsearch != NULL) + { + name = afct->objsearch (name, &loader->l_audit[cnt].cookie, + LA_SER_ORIG); + if (name == NULL) + { + /* Do not try anything further. */ + fd = -1; + goto no_file; + } + } + + afct = afct->next; + } + } +#endif + + /* Will be true if we found a DSO which is of the other ELF class. */ + bool found_other_class = false; + if (strchr (name, '/') == NULL) { /* Search for NAME in several places. */ @@ -1862,36 +2044,50 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, RPATHs. */ if (loader == NULL || loader->l_info[DT_RUNPATH] == NULL) { + /* This is the executable's map (if there is one). Make sure that + we do not look at it twice. */ + struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + bool did_main_map = false; + /* First try the DT_RPATH of the dependent object that caused NAME to be loaded. Then that object's dependent, and on up. */ - for (l = loader; fd == -1 && l; l = l->l_loader) + for (l = loader; l; l = l->l_loader) if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH")) - fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs, - &realname, &fb); + { + fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs, + &realname, &fb, loader, LA_SER_RUNPATH, + &found_other_class); + if (fd != -1) + break; + + did_main_map |= l == main_map; + } /* If dynamically linked, try the DT_RPATH of the executable itself. NB: we do this for lookups in any namespace. */ - if (fd == -1) - { - l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; - if (l && l->l_type != lt_loaded && l != loader - && cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH")) - fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs, - &realname, &fb); - } + if (fd == -1 && !did_main_map + && main_map != NULL && main_map->l_type != lt_loaded + && cache_rpath (main_map, &main_map->l_rpath_dirs, DT_RPATH, + "RPATH")) + fd = open_path (name, namelen, preloaded, &main_map->l_rpath_dirs, + &realname, &fb, loader ?: main_map, LA_SER_RUNPATH, + &found_other_class); } /* Try the LD_LIBRARY_PATH environment variable. */ if (fd == -1 && env_path_list.dirs != (void *) -1) fd = open_path (name, namelen, preloaded, &env_path_list, - &realname, &fb); + &realname, &fb, + loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded, + LA_SER_LIBPATH, &found_other_class); /* Look at the RUNPATH information for this binary. */ if (fd == -1 && loader != NULL && cache_rpath (loader, &loader->l_runpath_dirs, DT_RUNPATH, "RUNPATH")) fd = open_path (name, namelen, preloaded, - &loader->l_runpath_dirs, &realname, &fb); + &loader->l_runpath_dirs, &realname, &fb, loader, + LA_SER_RUNPATH, &found_other_class); if (fd == -1 && (__builtin_expect (! preloaded, 1) @@ -1939,7 +2135,9 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, if (cached != NULL) { - fd = open_verify (cached, &fb); + fd = open_verify (cached, + &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded, + LA_SER_CONFIG, &found_other_class, false); if (__builtin_expect (fd != -1, 1)) { realname = local_strdup (cached); @@ -1959,7 +2157,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, || __builtin_expect (!(l->l_flags_1 & DF_1_NODEFLIB), 1)) && rtld_search_dirs.dirs != (void *) -1) fd = open_path (name, namelen, preloaded, &rtld_search_dirs, - &realname, &fb); + &realname, &fb, l, LA_SER_DEFAULT, &found_other_class); /* Add another newline when we are tracing the library loading. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) @@ -1975,12 +2173,17 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, fd = -1; else { - fd = open_verify (realname, &fb); + fd = open_verify (realname, &fb, + loader ?: GL(dl_ns)[nsid]._ns_loaded, 0, + &found_other_class, true); if (__builtin_expect (fd, 0) == -1) free (realname); } } +#ifdef SHARED + no_file: +#endif /* In case the LOADER information has only been provided to get to the appropriate RUNPATH/RPATH information we do not need it anymore. */ @@ -2002,8 +2205,11 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, if ((name_copy = local_strdup (name)) == NULL || (l = _dl_new_object (name_copy, name, type, loader, mode, nsid)) == NULL) - _dl_signal_error (ENOMEM, name, NULL, - N_("cannot create shared object descriptor")); + { + free (name_copy); + _dl_signal_error (ENOMEM, name, NULL, + N_("cannot create shared object descriptor")); + } /* Signal that this is a faked entry. */ l->l_faked = 1; /* Since the descriptor is initialized with zero we do not @@ -2015,6 +2221,11 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, return l; } + else if (found_other_class) + _dl_signal_error (0, name, NULL, + ELFW(CLASS) == ELFCLASS32 + ? N_("wrong ELF class: ELFCLASS64") + : N_("wrong ELF class: ELFCLASS32")); else _dl_signal_error (errno, name, NULL, N_("cannot open shared object file")); diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index fdb0769..e971929 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -1,5 +1,5 @@ /* Look up a symbol in the loaded objects. - Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1995-2005, 2006, 2007 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 @@ -23,8 +23,9 @@ #include <string.h> #include <unistd.h> #include <ldsodefs.h> -#include "dl-hash.h" +#include <dl-hash.h> #include <dl-machine.h> +#include <sysdep-cancel.h> #include <bits/libc-lock.h> #include <tls.h> @@ -72,6 +73,16 @@ struct sym_val #include "do-lookup.h" +static uint_fast32_t +dl_new_hash (const char *s) +{ + uint_fast32_t h = 5381; + for (unsigned char c = *s; c != '\0'; c = *++s) + h = h * 33 + c; + return h & 0xffffffff; +} + + /* Add extra dependency on MAP to UNDEF_MAP. */ static int internal_function @@ -91,11 +102,6 @@ add_dependency (struct link_map *undef_map, struct link_map *map) /* Make sure nobody can unload the object while we are at it. */ __rtld_lock_lock_recursive (GL(dl_load_lock)); - /* Don't create cross-reference between modules which are - dynamically loaded by the same dlopen() call. */ - if (undef_map->l_opencount == 0 && map->l_opencount == 0) - goto out; - /* Avoid references to objects which cannot be unloaded anyway. */ if (map->l_type != lt_loaded || (map->l_flags_1 & DF_1_NODELETE) != 0) @@ -107,14 +113,13 @@ add_dependency (struct link_map *undef_map, struct link_map *map) if (undef_map->l_type != lt_loaded || (undef_map->l_flags_1 & DF_1_NODELETE) != 0) { - ++map->l_opencount; map->l_flags_1 |= DF_1_NODELETE; goto out; } /* Determine whether UNDEF_MAP already has a reference to MAP. First look in the normal dependencies. */ - if (undef_map->l_searchlist.r_list != NULL) + if (undef_map->l_initfini != NULL) { list = undef_map->l_initfini; @@ -172,19 +177,6 @@ add_dependency (struct link_map *undef_map, struct link_map *map) if (__builtin_expect (act < undef_map->l_reldepsmax, 1)) undef_map->l_reldeps[undef_map->l_reldepsact++] = map; - if (map->l_searchlist.r_list != NULL) - /* And increment the counter in the referenced object. */ - ++map->l_opencount; - else - /* We have to bump the counts for all dependencies since so far - this object was only a normal or transitive dependency. - Now it might be closed with _dl_close() directly. */ - for (list = map->l_initfini; *list != NULL; ++list) - ++(*list)->l_opencount; - - /* As if it is opened through _dl_open. */ - ++map->l_direct_opencount; - /* Display information if we are debugging. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) _dl_debug_printf ("\ @@ -209,14 +201,17 @@ add_dependency (struct link_map *undef_map, struct link_map *map) static void internal_function _dl_debug_bindings (const char *undef_name, struct link_map *undef_map, - const ElfW(Sym) **ref, struct r_scope_elem *symbol_scope[], - struct sym_val *value, + const ElfW(Sym) **ref, struct sym_val *value, const struct r_found_version *version, int type_class, int protected); /* Search loaded objects' symbol tables for a definition of the symbol - UNDEF_NAME, perhaps with a requested version for the symbol. */ + UNDEF_NAME, perhaps with a requested version for the symbol. + + We must never have calls to the audit functions inside this function + or in any function which gets called. If this would happen the audit + code might create a thread which can throw off all the scope locking. */ lookup_t internal_function _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, @@ -225,7 +220,8 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, const struct r_found_version *version, int type_class, int flags, struct link_map *skip_map) { - const unsigned long int hash = _dl_elf_hash (undef_name); + const uint_fast32_t new_hash = dl_new_hash (undef_name); + unsigned long int old_hash = 0xffffffff; struct sym_val current_value = { NULL, NULL }; struct r_scope_elem **scope = symbol_scope; @@ -233,23 +229,20 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, /* No other flag than DL_LOOKUP_ADD_DEPENDENCY is allowed if we look up a versioned symbol. */ - assert (version == NULL || flags == 0 || flags == DL_LOOKUP_ADD_DEPENDENCY); + assert (version == NULL || (flags & ~(DL_LOOKUP_ADD_DEPENDENCY)) == 0); size_t i = 0; if (__builtin_expect (skip_map != NULL, 0)) - { - /* Search the relevant loaded objects for a definition. */ - while ((*scope)->r_list[i] != skip_map) - ++i; - - assert (i < (*scope)->r_nlist); - } + /* Search the relevant loaded objects for a definition. */ + while ((*scope)->r_list[i] != skip_map) + ++i; /* Search the relevant loaded objects for a definition. */ for (size_t start = i; *scope != NULL; start = 0, ++scope) { - int res = do_lookup_x (undef_name, hash, *ref, ¤t_value, *scope, - start, version, flags, skip_map, type_class); + int res = do_lookup_x (undef_name, new_hash, &old_hash, *ref, + ¤t_value, *scope, start, version, flags, + skip_map, type_class); if (res > 0) break; @@ -320,9 +313,9 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, struct sym_val protected_value = { NULL, NULL }; for (scope = symbol_scope; *scope != NULL; i = 0, ++scope) - if (do_lookup_x (undef_name, hash, *ref, &protected_value, - *scope, i, version, flags, skip_map, - ELF_RTYPE_CLASS_PLT) != 0) + if (do_lookup_x (undef_name, new_hash, &old_hash, *ref, + &protected_value, *scope, i, version, flags, + skip_map, ELF_RTYPE_CLASS_PLT) != 0) break; if (protected_value.s != NULL && protected_value.m != undef_map) @@ -345,16 +338,15 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, && add_dependency (undef_map, current_value.m) < 0) /* Something went wrong. Perhaps the object we tried to reference was just removed. Try finding another definition. */ - return _dl_lookup_symbol_x (undef_name, undef_map, ref, - symbol_scope, version, type_class, - flags, skip_map); + return _dl_lookup_symbol_x (undef_name, undef_map, ref, symbol_scope, + version, type_class, flags, skip_map); /* The object is used. */ current_value.m->l_used = 1; if (__builtin_expect (GLRO(dl_debug_mask) & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK), 0)) - _dl_debug_bindings (undef_name, undef_map, ref, symbol_scope, + _dl_debug_bindings (undef_name, undef_map, ref, ¤t_value, version, type_class, protected); *ref = current_value.s; @@ -371,6 +363,31 @@ _dl_setup_hash (struct link_map *map) Elf_Symndx *hash; Elf_Symndx nchain; + if (__builtin_expect (map->l_info[DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + + DT_THISPROCNUM + DT_VERSIONTAGNUM + + DT_EXTRANUM + DT_VALNUM] != NULL, 1)) + { + Elf32_Word *hash32 + = (void *) D_PTR (map, l_info[DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + + DT_THISPROCNUM + DT_VERSIONTAGNUM + + DT_EXTRANUM + DT_VALNUM]); + map->l_nbuckets = *hash32++; + Elf32_Word symbias = *hash32++; + Elf32_Word bitmask_nwords = *hash32++; + /* Must be a power of two. */ + assert ((bitmask_nwords & (bitmask_nwords - 1)) == 0); + map->l_gnu_bitmask_idxbits = bitmask_nwords - 1; + map->l_gnu_shift = *hash32++; + + map->l_gnu_bitmask = (ElfW(Addr) *) hash32; + hash32 += __ELF_NATIVE_CLASS / 32 * bitmask_nwords; + + map->l_gnu_buckets = hash32; + hash32 += map->l_nbuckets; + map->l_gnu_chain_zero = hash32 - symbias; + return; + } + if (!map->l_info[DT_HASH]) return; hash = (void *) D_PTR (map, l_info[DT_HASH]); @@ -386,8 +403,7 @@ _dl_setup_hash (struct link_map *map) static void internal_function _dl_debug_bindings (const char *undef_name, struct link_map *undef_map, - const ElfW(Sym) **ref, struct r_scope_elem *symbol_scope[], - struct sym_val *value, + const ElfW(Sym) **ref, struct sym_val *value, const struct r_found_version *version, int type_class, int protected) { @@ -395,11 +411,13 @@ _dl_debug_bindings (const char *undef_name, struct link_map *undef_map, if (GLRO(dl_debug_mask) & DL_DEBUG_BINDINGS) { - _dl_debug_printf ("binding file %s to %s: %s symbol `%s'", + _dl_debug_printf ("binding file %s [%lu] to %s [%lu]: %s symbol `%s'", (reference_name[0] ? reference_name : (rtld_progname ?: "<main program>")), + undef_map->l_ns, value->m->l_name[0] ? value->m->l_name : rtld_progname, + value->m->l_ns, protected ? "protected" : "normal", undef_name); if (version) _dl_debug_printf_c (" [%s]\n", version->name); @@ -416,9 +434,10 @@ _dl_debug_bindings (const char *undef_name, struct link_map *undef_map, || GLRO(dl_trace_prelink_map) == GL(dl_ns)[LM_ID_BASE]._ns_loaded) && undef_map != GL(dl_ns)[LM_ID_BASE]._ns_loaded) { - const unsigned long int hash = _dl_elf_hash (undef_name); + const uint_fast32_t new_hash = dl_new_hash (undef_name); + unsigned long int old_hash = 0xffffffff; - do_lookup_x (undef_name, hash, *ref, &val, + do_lookup_x (undef_name, new_hash, &old_hash, *ref, &val, undef_map->l_local_scope[0], 0, version, 0, NULL, type_class); diff --git a/elf/dl-minimal.c b/elf/dl-minimal.c index adb85e6..eeea790 100644 --- a/elf/dl-minimal.c +++ b/elf/dl-minimal.c @@ -1,5 +1,6 @@ /* Minimal replacements for basic facilities used in the dynamic linker. - Copyright (C) 1995-1998,2000-2002,2004 Free Software Foundation, Inc. + Copyright (C) 1995-1998,2000-2002,2004-2006,2007 + 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 @@ -74,14 +75,21 @@ __libc_memalign (size_t align, size_t n) alloc_ptr = (void *) 0 + (((alloc_ptr - (void *) 0) + align - 1) & ~(align - 1)); - if (alloc_ptr + n >= alloc_end) + if (alloc_ptr + n >= alloc_end || n >= -(uintptr_t) alloc_ptr) { /* Insufficient space left; allocate another page. */ caddr_t page; size_t nup = (n + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1); + if (__builtin_expect (nup == 0, 0)) + { + if (n) + return NULL; + nup = GLRO(dl_pagesize); + } page = __mmap (0, nup, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, _dl_zerofd, 0); - assert (page != MAP_FAILED); + if (page == MAP_FAILED) + return NULL; if (page != alloc_end) alloc_ptr = page; alloc_end = page + nup; @@ -107,7 +115,14 @@ calloc (size_t nmemb, size_t size) /* New memory from the trivial malloc above is always already cleared. (We make sure that's true in the rare occasion it might not be, by clearing memory in free, below.) */ - return malloc (nmemb * size); + size_t bytes = nmemb * size; + +#define HALF_SIZE_T (((size_t) 1) << (8 * sizeof (size_t) / 2)) + if (__builtin_expect ((nmemb | size) >= HALF_SIZE_T, 0) + && size != 0 && bytes / size != nmemb) + return NULL; + + return malloc (bytes); } /* This will rarely be called. */ @@ -128,14 +143,13 @@ free (void *ptr) void * weak_function realloc (void *ptr, size_t n) { - void *new; if (ptr == NULL) return malloc (n); assert (ptr == alloc_last_block); + size_t old_size = alloc_ptr - alloc_last_block; alloc_ptr = alloc_last_block; - new = malloc (n); - assert (new == ptr); - return new; + void *new = malloc (n); + return new != ptr ? memcpy (new, ptr, old_size) : new; } /* Avoid signal frobnication in setjmp/longjmp. Keeps things smaller. */ @@ -148,12 +162,6 @@ __sigjmp_save (sigjmp_buf env, int savemask __attribute__ ((unused))) env[0].__mask_was_saved = 0; return 0; } - -void weak_function -longjmp (jmp_buf env, int val) -{ - __longjmp (env[0].__jmpbuf, val); -} /* Define our own version of the internal function used by strerror. We only provide the messages for some common errors. This avoids pulling @@ -270,7 +278,7 @@ __strtoul_internal (const char *nptr, char **endptr, int base, int group) while (*nptr >= '0' && *nptr <= '9') { unsigned long int digval = *nptr - '0'; - if (result > LONG_MAX / 10 + if (result > ULONG_MAX / 10 || (result == ULONG_MAX / 10 && digval > ULONG_MAX % 10)) { errno = ERANGE; diff --git a/elf/dl-misc.c b/elf/dl-misc.c index 08d6495..6da1e2e 100644 --- a/elf/dl-misc.c +++ b/elf/dl-misc.c @@ -1,5 +1,5 @@ /* Miscellaneous support functions for dynamic linker - Copyright (C) 1997-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1997-2002, 2003, 2004, 2006 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 @@ -308,7 +308,7 @@ _dl_dprintf (int fd, const char *fmt, ...) /* Test whether given NAME matches any of the names of the given object. */ int internal_function -_dl_name_match_p (const char *name, struct link_map *map) +_dl_name_match_p (const char *name, const struct link_map *map) { if (strcmp (name, map->l_name) == 0) return 1; diff --git a/elf/dl-object.c b/elf/dl-object.c index b46ebdc..86f7a8e 100644 --- a/elf/dl-object.c +++ b/elf/dl-object.c @@ -39,14 +39,24 @@ _dl_new_object (char *realname, const char *libname, int type, size_t libname_len = strlen (libname) + 1; struct link_map *new; struct libname_list *newname; +#ifdef SHARED + /* We create the map for the executable before we know whether we have + auditing libraries and if yes, how many. Assume the worst. */ + unsigned int naudit = GLRO(dl_naudit) ?: ((mode & __RTLD_OPENEXEC) + ? DL_NNS : 0); + size_t audit_space = naudit * sizeof (new->l_audit[0]); +#else +# define audit_space 0 +#endif - new = (struct link_map *) calloc (sizeof (*new) + sizeof (*newname) - + libname_len, 1); + new = (struct link_map *) calloc (sizeof (*new) + audit_space + + sizeof (*newname) + libname_len, 1); if (new == NULL) return NULL; new->l_real = new; - new->l_libname = newname = (struct libname_list *) (new + 1); + new->l_libname = newname = (struct libname_list *) ((char *) (new + 1) + + audit_space); newname->name = (char *) memcpy (newname + 1, libname, libname_len); /* newname->next = NULL; We use calloc therefore not necessary. */ newname->dont_free = 1; @@ -59,6 +69,14 @@ _dl_new_object (char *realname, const char *libname, int type, #endif new->l_ns = nsid; +#ifdef SHARED + for (unsigned int cnt = 0; cnt < naudit; ++cnt) + { + new->l_audit[cnt].cookie = (uintptr_t) new; + /* new->l_audit[cnt].bindflags = 0; */ + } +#endif + /* new->l_global = 0; We use calloc therefore not necessary. */ /* Use the 'l_scope_mem' array by default for the the 'l_scope' diff --git a/elf/dl-open.c b/elf/dl-open.c index 7e890ad..32e7caa 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -1,5 +1,5 @@ /* Load a shared object at runtime, relocate it, and run its initializer. - Copyright (C) 1996-2001, 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1996-2004, 2005, 2006, 2007 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 @@ -31,16 +31,12 @@ #include <ldsodefs.h> #include <bp-sym.h> #include <caller.h> +#include <sysdep-cancel.h> +#include <tls.h> #include <dl-dst.h> -#ifndef SHARED -/* Giving this initialized value preallocates some surplus bytes in the - static TLS area, see __libc_setup_tls (libc-tls.c). */ -size_t _dl_tls_static_size = 2048; -#endif - extern ElfW(Addr) _dl_sysdep_start (void **start_argptr, void (*dl_main) (const ElfW(Phdr) *phdr, ElfW(Word) phnum, @@ -49,11 +45,6 @@ weak_extern (BP_SYM (_dl_sysdep_start)) extern int __libc_multiple_libcs; /* Defined in init-first.c. */ -extern int __libc_argc attribute_hidden; -extern char **__libc_argv attribute_hidden; - -extern char **__environ; - /* Undefine the following for debugging. */ /* #define SCOPE_DEBUG 1 */ #ifdef SCOPE_DEBUG @@ -74,6 +65,10 @@ struct dl_open_args struct link_map *map; /* Namespace ID. */ Lmid_t nsid; + /* Original parameters to the program and the current environment. */ + int argc; + char **argv; + char **env; }; @@ -103,47 +98,57 @@ add_to_global (struct link_map *new) in an realloc() call. Therefore we allocate a completely new array the first time we have to add something to the locale scope. */ - if (GL(dl_ns)[new->l_ns]._ns_global_scope_alloc == 0) + struct link_namespaces *ns = &GL(dl_ns)[new->l_ns]; + if (ns->_ns_global_scope_alloc == 0) { /* This is the first dynamic object given global scope. */ - GL(dl_ns)[new->l_ns]._ns_global_scope_alloc - = GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist + to_add + 8; + ns->_ns_global_scope_alloc + = ns->_ns_main_searchlist->r_nlist + to_add + 8; new_global = (struct link_map **) - malloc (GL(dl_ns)[new->l_ns]._ns_global_scope_alloc - * sizeof (struct link_map *)); + malloc (ns->_ns_global_scope_alloc * sizeof (struct link_map *)); if (new_global == NULL) { - GL(dl_ns)[new->l_ns]._ns_global_scope_alloc = 0; + ns->_ns_global_scope_alloc = 0; nomem: - GLRO(dl_signal_error) (ENOMEM, new->l_libname->name, NULL, - N_("cannot extend global scope")); + _dl_signal_error (ENOMEM, new->l_libname->name, NULL, + N_("cannot extend global scope")); return 1; } /* Copy over the old entries. */ - GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list - = memcpy (new_global, - GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list, - (GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist + ns->_ns_main_searchlist->r_list + = memcpy (new_global, ns->_ns_main_searchlist->r_list, + (ns->_ns_main_searchlist->r_nlist * sizeof (struct link_map *))); } - else if (GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist + to_add - > GL(dl_ns)[new->l_ns]._ns_global_scope_alloc) + else if (ns->_ns_main_searchlist->r_nlist + to_add + > ns->_ns_global_scope_alloc) { /* We have to extend the existing array of link maps in the main map. */ + struct link_map **old_global + = GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list; + size_t new_nalloc = ((ns->_ns_global_scope_alloc + to_add) * 2); + new_global = (struct link_map **) - realloc (GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list, - ((GL(dl_ns)[new->l_ns]._ns_global_scope_alloc + to_add + 8) - * sizeof (struct link_map *))); + malloc (new_nalloc * sizeof (struct link_map *)); if (new_global == NULL) goto nomem; - GL(dl_ns)[new->l_ns]._ns_global_scope_alloc += to_add + 8; - GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list = new_global; + memcpy (new_global, old_global, + ns->_ns_global_scope_alloc * sizeof (struct link_map *)); + + ns->_ns_global_scope_alloc = new_nalloc; + ns->_ns_main_searchlist->r_list = new_global; + + if (!RTLD_SINGLE_THREAD_P) + THREAD_GSCOPE_WAIT (); + + free (old_global); } /* Now add the new entries. */ + unsigned int new_nlist = ns->_ns_main_searchlist->r_nlist; for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt) { struct link_map *map = new->l_searchlist.r_list[cnt]; @@ -151,15 +156,49 @@ add_to_global (struct link_map *new) if (map->l_global == 0) { map->l_global = 1; - GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list[GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist] - = map; - ++GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist; + ns->_ns_main_searchlist->r_list[new_nlist++] = map; } } + atomic_write_barrier (); + ns->_ns_main_searchlist->r_nlist = new_nlist; return 0; } +int +_dl_scope_free (struct r_scope_elem **old) +{ + struct dl_scope_free_list *fsl; +#define DL_SCOPE_FREE_LIST_SIZE (sizeof (fsl->list) / sizeof (fsl->list[0])) + + if (RTLD_SINGLE_THREAD_P) + free (old); + else if ((fsl = GL(dl_scope_free_list)) == NULL) + { + GL(dl_scope_free_list) = fsl = malloc (sizeof (*fsl)); + if (fsl == NULL) + { + THREAD_GSCOPE_WAIT (); + free (old); + return 1; + } + else + { + fsl->list[0] = old; + fsl->count = 1; + } + } + else if (fsl->count < DL_SCOPE_FREE_LIST_SIZE) + fsl->list[fsl->count++] = old; + else + { + THREAD_GSCOPE_WAIT (); + while (fsl->count > 0) + free (fsl->list[--fsl->count]); + return 1; + } + return 0; +} static void dl_open_worker (void *a) @@ -167,17 +206,18 @@ dl_open_worker (void *a) struct dl_open_args *args = a; const char *file = args->file; int mode = args->mode; - struct link_map *new, *l; + struct link_map *new; int lazy; unsigned int i; #ifdef USE_TLS - bool any_tls; + bool any_tls = false; #endif struct link_map *call_map = NULL; /* Check whether _dl_open() has been called from a valid DSO. */ - if (__check_caller (args->caller_dl_open, allow_libc|allow_libdl) != 0) - GLRO(dl_signal_error) (0, "dlopen", NULL, N_("invalid caller")); + if (__check_caller (args->caller_dl_open, + allow_libc|allow_libdl|allow_ldso) != 0) + _dl_signal_error (0, "dlopen", NULL, N_("invalid caller")); /* Determine the caller's map if necessary. This is needed in case we have a DST, when we don't know the namespace ID we have to put @@ -193,13 +233,14 @@ dl_open_worker (void *a) By default we assume this is the main application. */ call_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + struct link_map *l; for (Lmid_t ns = 0; ns < DL_NNS; ++ns) for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) if (caller_dlopen >= (const void *) l->l_map_start - && caller_dlopen < (const void *) l->l_map_end) + && caller_dlopen < (const void *) l->l_map_end + && (l->l_contiguous + || _dl_addr_inside_object (l, (ElfW(Addr)) caller_dlopen))) { - /* There must be exactly one DSO for the range of the virtual - memory. Otherwise something is really broken. */ assert (ns == l->l_ns); call_map = l; goto found_caller; @@ -218,6 +259,8 @@ dl_open_worker (void *a) } } + assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); + /* Maybe we have to expand a DST. */ if (__builtin_expect (dst != NULL, 0)) { @@ -225,13 +268,6 @@ dl_open_worker (void *a) size_t required; char *new_file; - /* DSTs must not appear in SUID/SGID programs. */ - if (__libc_enable_secure) - /* This is an error. */ - GLRO(dl_signal_error) (0, "dlopen", NULL, - N_("DST not allowed in SUID/SGID programs")); - - /* Determine how much space we need. We have to allocate the memory locally. */ required = DL_DST_REQUIRED (call_map, file, len, _dl_dst_count (dst, 0)); @@ -244,8 +280,8 @@ dl_open_worker (void *a) /* If the substitution failed don't try to load. */ if (*new_file == '\0') - GLRO(dl_signal_error) (0, "dlopen", NULL, - N_("empty dynamic string token substitution")); + _dl_signal_error (0, "dlopen", NULL, + N_("empty dynamic string token substitution")); /* Now we have a new file name. */ file = new_file; @@ -256,8 +292,8 @@ dl_open_worker (void *a) } /* Load the named object. */ - args->map = new = GLRO(dl_map_object) (call_map, file, 0, lt_loaded, 0, - mode | __RTLD_CALLMAP, args->nsid); + args->map = new = _dl_map_object (call_map, file, 0, lt_loaded, 0, + mode | __RTLD_CALLMAP, args->nsid); /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is set and the object is not already loaded. */ @@ -279,47 +315,65 @@ dl_open_worker (void *a) { /* Let the user know about the opencount. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) - GLRO(dl_debug_printf) ("opening file=%s [%lu]; opencount=%u\n\n", - new->l_name, new->l_ns, new->l_opencount); + _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", + new->l_name, new->l_ns, new->l_direct_opencount); /* If the user requested the object to be in the global namespace but it is not so far, add it now. */ if ((mode & RTLD_GLOBAL) && new->l_global == 0) (void) add_to_global (new); - if (new->l_direct_opencount == 1) - /* This is the only direct reference. Increment all the - dependencies' reference counter. */ - for (i = 0; i < new->l_searchlist.r_nlist; ++i) - ++new->l_searchlist.r_list[i]->l_opencount; - else - /* Increment just the reference counter of the object. */ - ++new->l_opencount; + assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); return; } /* Load that object's dependencies. */ - GLRO(dl_map_object_deps) (new, NULL, 0, 0, - mode & (__RTLD_DLOPEN | RTLD_DEEPBIND)); + _dl_map_object_deps (new, NULL, 0, 0, + mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT)); /* So far, so good. Now check the versions. */ for (i = 0; i < new->l_searchlist.r_nlist; ++i) if (new->l_searchlist.r_list[i]->l_real->l_versions == NULL) - (void) GLRO(dl_check_map_versions) (new->l_searchlist.r_list[i]->l_real, - 0, 0); + (void) _dl_check_map_versions (new->l_searchlist.r_list[i]->l_real, + 0, 0); #ifdef SCOPE_DEBUG show_scope (new); #endif +#ifdef SHARED + /* Auditing checkpoint: we have added all objects. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct link_map *head = GL(dl_ns)[new->l_ns]._ns_loaded; + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT); + + afct = afct->next; + } + } + } +#endif + + /* Notify the debugger all new objects are now ready to go. */ + struct r_debug *r = _dl_debug_initialize (0, args->nsid); + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + /* Only do lazy relocation if `LD_BIND_NOW' is not set. */ lazy = (mode & RTLD_BINDING_MASK) == RTLD_LAZY && GLRO(dl_lazy); /* Relocate the objects loaded. We do this in reverse order so that copy relocs of earlier objects overwrite the data written by later objects. */ - l = new; + struct link_map *l = new; while (l->l_next) l = l->l_next; while (1) @@ -327,7 +381,7 @@ dl_open_worker (void *a) if (! l->l_real->l_relocated) { #ifdef SHARED - if (GLRO(dl_profile) != NULL) + if (__builtin_expect (GLRO(dl_profile) != NULL, 0)) { /* If this here is the shared object which we want to profile make sure the profile is started. We can find out whether @@ -336,12 +390,12 @@ dl_open_worker (void *a) start the profiling. */ struct link_map *old_profile_map = GL(dl_profile_map); - GLRO(dl_relocate_object) (l, l->l_scope, 1, 1); + _dl_relocate_object (l, l->l_scope, 1, 1); if (old_profile_map == NULL && GL(dl_profile_map) != NULL) { /* We must prepare the profiling. */ - GLRO(dl_start_profile) (); + _dl_start_profile (); /* Prevent unloading the object. */ GL(dl_profile_map)->l_flags_1 |= DF_1_NODELETE; @@ -349,7 +403,7 @@ dl_open_worker (void *a) } else #endif - GLRO(dl_relocate_object) (l, l->l_scope, lazy, 0); + _dl_relocate_object (l, l->l_scope, lazy, 0); } if (l == new) @@ -357,160 +411,115 @@ dl_open_worker (void *a) l = l->l_prev; } -#ifdef USE_TLS - /* Do static TLS initialization now if it has been delayed because - the TLS template might not be fully relocated at _dl_allocate_static_tls - time. */ - for (l = new; l; l = l->l_next) - if (l->l_need_tls_init) - { - l->l_need_tls_init = 0; - GL(dl_init_static_tls) (l); - } - - /* We normally don't bump the TLS generation counter. There must be - actually a need to do this. */ - any_tls = false; -#endif - - /* Increment the open count for all dependencies. If the file is - not loaded as a dependency here add the search list of the newly - loaded object to the scope. */ + /* If the file is not loaded now as a dependency, add the search + list of the newly loaded object to the scope. */ for (i = 0; i < new->l_searchlist.r_nlist; ++i) - if (++new->l_searchlist.r_list[i]->l_opencount > 1 - && new->l_real->l_searchlist.r_list[i]->l_type == lt_loaded) - { - struct link_map *imap = new->l_searchlist.r_list[i]; - struct r_scope_elem **runp = imap->l_scope; - size_t cnt = 0; - - while (*runp != NULL) - { - /* This can happen if imap was just loaded, but during - relocation had l_opencount bumped because of relocation - dependency. Avoid duplicates in l_scope. */ - if (__builtin_expect (*runp == &new->l_searchlist, 0)) - break; - - ++cnt; - ++runp; - } - - if (*runp != NULL) - /* Avoid duplicates. */ - continue; - - if (__builtin_expect (cnt + 1 >= imap->l_scope_max, 0)) - { - /* The 'r_scope' array is too small. Allocate a new one - dynamically. */ - struct r_scope_elem **newp; - size_t new_size = imap->l_scope_max * 2; - - if (imap->l_scope == imap->l_scope_mem) - { - newp = (struct r_scope_elem **) - malloc (new_size * sizeof (struct r_scope_elem *)); - if (newp == NULL) - GLRO(dl_signal_error) (ENOMEM, "dlopen", NULL, - N_("cannot create scope list")); - imap->l_scope = memcpy (newp, imap->l_scope, - cnt * sizeof (imap->l_scope[0])); - } - else - { - newp = (struct r_scope_elem **) - realloc (imap->l_scope, - new_size * sizeof (struct r_scope_elem *)); - if (newp == NULL) - GLRO(dl_signal_error) (ENOMEM, "dlopen", NULL, - N_("cannot create scope list")); - imap->l_scope = newp; - } - - imap->l_scope_max = new_size; - } - - imap->l_scope[cnt++] = &new->l_searchlist; - imap->l_scope[cnt] = NULL; - } + { + struct link_map *imap = new->l_searchlist.r_list[i]; + + /* If the initializer has been called already, the object has + not been loaded here and now. */ + if (imap->l_init_called && imap->l_type == lt_loaded) + { + struct r_scope_elem **runp = imap->l_scope; + size_t cnt = 0; + + while (*runp != NULL) + { + if (*runp == &new->l_searchlist) + break; + ++cnt; + ++runp; + } + + if (*runp != NULL) + /* Avoid duplicates. */ + continue; + + if (__builtin_expect (cnt + 1 >= imap->l_scope_max, 0)) + { + /* The 'r_scope' array is too small. Allocate a new one + dynamically. */ + size_t new_size; + struct r_scope_elem **newp; + +#define SCOPE_ELEMS(imap) \ + (sizeof (imap->l_scope_mem) / sizeof (imap->l_scope_mem[0])) + + if (imap->l_scope != imap->l_scope_mem + && imap->l_scope_max < SCOPE_ELEMS (imap)) + { + new_size = SCOPE_ELEMS (imap); + newp = imap->l_scope_mem; + } + else + { + new_size = imap->l_scope_max * 2; + newp = (struct r_scope_elem **) + malloc (new_size * sizeof (struct r_scope_elem *)); + if (newp == NULL) + _dl_signal_error (ENOMEM, "dlopen", NULL, + N_("cannot create scope list")); + } + + memcpy (newp, imap->l_scope, cnt * sizeof (imap->l_scope[0])); + struct r_scope_elem **old = imap->l_scope; + + imap->l_scope = newp; + + if (old != imap->l_scope_mem) + _dl_scope_free (old); + + imap->l_scope_max = new_size; + } + + /* First terminate the extended list. Otherwise a thread + might use the new last element and then use the garbage + at offset IDX+1. */ + imap->l_scope[cnt + 1] = NULL; + atomic_write_barrier (); + imap->l_scope[cnt] = &new->l_searchlist; + } #if USE_TLS - else if (new->l_searchlist.r_list[i]->l_opencount == 1 - /* Only if the module defines thread local data. */ - && __builtin_expect (new->l_searchlist.r_list[i]->l_tls_blocksize - > 0, 0)) - { - /* Now that we know the object is loaded successfully add - modules containing TLS data to the dtv info table. We - might have to increase its size. */ - struct dtv_slotinfo_list *listp; - struct dtv_slotinfo_list *prevp; - size_t idx = new->l_searchlist.r_list[i]->l_tls_modid; - - assert (new->l_searchlist.r_list[i]->l_type == lt_loaded); - - /* Find the place in the dtv slotinfo list. */ - listp = GL(dl_tls_dtv_slotinfo_list); - prevp = NULL; /* Needed to shut up gcc. */ - do - { - /* Does it fit in the array of this list element? */ - if (idx < listp->len) - break; - idx -= listp->len; - prevp = listp; - listp = listp->next; - } - while (listp != NULL); - - if (listp == NULL) - { - /* When we come here it means we have to add a new element - to the slotinfo list. And the new module must be in - the first slot. */ - assert (idx == 0); - - listp = prevp->next = (struct dtv_slotinfo_list *) - malloc (sizeof (struct dtv_slotinfo_list) - + TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo)); - if (listp == NULL) - { - /* We ran out of memory. We will simply fail this - call but don't undo anything we did so far. The - application will crash or be terminated anyway very - soon. */ - - /* We have to do this since some entries in the dtv - slotinfo array might already point to this - generation. */ - ++GL(dl_tls_generation); - - GLRO(dl_signal_error) (ENOMEM, "dlopen", NULL, N_("\ -cannot create TLS data structures")); - } - - listp->len = TLS_SLOTINFO_SURPLUS; - listp->next = NULL; - memset (listp->slotinfo, '\0', - TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo)); - } - - /* Add the information into the slotinfo data structure. */ - listp->slotinfo[idx].map = new->l_searchlist.r_list[i]; - listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1; - - /* We have to bump the generation counter. */ - any_tls = true; - } + /* Only add TLS memory if this object is loaded now and + therefore is not yet initialized. */ + else if (! imap->l_init_called + /* Only if the module defines thread local data. */ + && __builtin_expect (imap->l_tls_blocksize > 0, 0)) + { + /* Now that we know the object is loaded successfully add + modules containing TLS data to the slot info table. We + might have to increase its size. */ + _dl_add_to_slotinfo (imap); + if (imap->l_need_tls_init) + { + imap->l_need_tls_init = 0; +# ifdef SHARED + /* Update the slot information data for at least the + generation of the DSO we are allocating data for. */ + _dl_update_slotinfo (imap->l_tls_modid); +# endif + + GL(dl_init_static_tls) (imap); + assert (imap->l_need_tls_init == 0); + } + + /* We have to bump the generation counter. */ + any_tls = true; + } +#endif + } + +#if USE_TLS /* Bump the generation number if necessary. */ - if (any_tls) - if (__builtin_expect (++GL(dl_tls_generation) == 0, 0)) - __libc_fatal (_("TLS generation counter wrapped! Please report this.")); + if (any_tls && __builtin_expect (++GL(dl_tls_generation) == 0, 0)) + _dl_fatal_printf (N_("\ +TLS generation counter wrapped! Please report this.")); #endif /* Run the initializer functions of new objects. */ - GLRO(dl_init) (new, __libc_argc, __libc_argv, __environ); + _dl_init (new, args->argc, args->argv, args->env); /* Now we can make the new map available in the global scope. */ if (mode & RTLD_GLOBAL) @@ -532,24 +541,18 @@ cannot create TLS data structures")); /* Let the user know about the opencount. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) - GLRO(dl_debug_printf) ("opening file=%s [%lu]; opencount=%u\n\n", - new->l_name, new->l_ns, new->l_opencount); + _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", + new->l_name, new->l_ns, new->l_direct_opencount); } void * -internal_function -_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid) +_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid, + int argc, char *argv[], char *env[]) { - struct dl_open_args args; - const char *objname; - const char *errstring; - int errcode; - if ((mode & RTLD_BINDING_MASK) == 0) /* One of the flags must be set. */ - GLRO(dl_signal_error) (EINVAL, file, NULL, - N_("invalid mode for dlopen()")); + _dl_signal_error (EINVAL, file, NULL, N_("invalid mode for dlopen()")); /* Make sure we are alone. */ __rtld_lock_lock_recursive (GL(dl_load_lock)); @@ -566,66 +569,74 @@ _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid) /* No more namespace available. */ __rtld_lock_unlock_recursive (GL(dl_load_lock)); - GLRO(dl_signal_error) (EINVAL, file, NULL, N_("\ + _dl_signal_error (EINVAL, file, NULL, N_("\ no more namespaces available for dlmopen()")); } + + _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT; } /* Never allow loading a DSO in a namespace which is empty. Such - direct placements is only causing problems. */ + direct placements is only causing problems. Also don't allow + loading into a namespace used for auditing. */ else if (nsid != LM_ID_BASE && nsid != __LM_ID_CALLER - && GL(dl_ns)[nsid]._ns_nloaded == 0) - GLRO(dl_signal_error) (EINVAL, file, NULL, - N_("invalid target namespace in dlmopen()")); + && (GL(dl_ns)[nsid]._ns_nloaded == 0 + || GL(dl_ns)[nsid]._ns_loaded->l_auditing)) + _dl_signal_error (EINVAL, file, NULL, + N_("invalid target namespace in dlmopen()")); + struct dl_open_args args; args.file = file; args.mode = mode; args.caller_dlopen = caller_dlopen; args.caller_dl_open = RETURN_ADDRESS (0); args.map = NULL; args.nsid = nsid; - errcode = GLRO(dl_catch_error) (&objname, &errstring, dl_open_worker, &args); + args.argc = argc; + args.argv = argv; + args.env = env; + + const char *objname; + const char *errstring; + bool malloced; + int errcode = _dl_catch_error (&objname, &errstring, &malloced, + dl_open_worker, &args); #ifndef MAP_COPY /* We must munmap() the cache file. */ - GLRO(dl_unload_cache) (); + _dl_unload_cache (); #endif - /* Release the lock. */ - __rtld_lock_unlock_recursive (GL(dl_load_lock)); - + /* See if an error occurred during loading. */ if (__builtin_expect (errstring != NULL, 0)) { - /* Some error occurred during loading. */ - char *local_errstring; - size_t len_errstring; - /* Remove the object from memory. It may be in an inconsistent state if relocation failed, for example. */ if (args.map) { - unsigned int i; - - /* Increment open counters for all objects since this - sometimes has not happened yet. */ - if (args.map->l_searchlist.r_list[0]->l_opencount == 0) - for (i = 0; i < args.map->l_searchlist.r_nlist; ++i) - ++args.map->l_searchlist.r_list[i]->l_opencount; - #ifdef USE_TLS - /* Maybe some of the modules which were loaded uses TLS. + /* Maybe some of the modules which were loaded use TLS. Since it will be removed in the following _dl_close call - we have to mark the dtv array as having gaps to fill - the holes. This is a pessimistic assumption which won't - hurt if not true. */ - GL(dl_tls_dtv_gaps) = true; + we have to mark the dtv array as having gaps to fill the + holes. This is a pessimistic assumption which won't hurt + if not true. There is no need to do this when we are + loading the auditing DSOs since TLS has not yet been set + up. */ + if ((mode & __RTLD_AUDIT) == 0) + GL(dl_tls_dtv_gaps) = true; #endif - _dl_close (args.map); + _dl_close_worker (args.map); } + assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT); + + /* Release the lock. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + /* Make a local copy of the error string so that we can release the memory allocated for it. */ - len_errstring = strlen (errstring) + 1; + size_t len_errstring = strlen (errstring) + 1; + char *local_errstring; if (objname == errstring + len_errstring) { size_t total_len = len_errstring + strlen (objname) + 1; @@ -639,20 +650,24 @@ no more namespaces available for dlmopen()")); memcpy (local_errstring, errstring, len_errstring); } - if (errstring != _dl_out_of_memory) + if (malloced) free ((char *) errstring); /* Reraise the error. */ - GLRO(dl_signal_error) (errcode, objname, NULL, local_errstring); + _dl_signal_error (errcode, objname, NULL, local_errstring); } + assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT); + + /* Release the lock. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + #ifndef SHARED DL_STATIC_INIT (args.map); #endif return args.map; } -libc_hidden_def (_dl_open) #ifdef SCOPE_DEBUG @@ -682,3 +697,21 @@ show_scope (struct link_map *new) } } #endif + +#ifdef IS_IN_rtld +/* Return non-zero if ADDR lies within one of L's segments. */ +int +internal_function +_dl_addr_inside_object (struct link_map *l, const ElfW(Addr) addr) +{ + int n = l->l_phnum; + const ElfW(Addr) reladdr = addr - l->l_addr; + + while (--n >= 0) + if (l->l_phdr[n].p_type == PT_LOAD + && reladdr - l->l_phdr[n].p_vaddr >= 0 + && reladdr - l->l_phdr[n].p_vaddr < l->l_phdr[n].p_memsz) + return 1; + return 0; +} +#endif diff --git a/elf/dl-origin.c b/elf/dl-origin.c new file mode 100644 index 0000000..8761937 --- /dev/null +++ b/elf/dl-origin.c @@ -0,0 +1,51 @@ +/* Find path of executable. + Copyright (C) 1998, 1999, 2000, 2002, 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. + + 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <stdlib.h> +#include <string.h> +#include <sys/param.h> +#include <ldsodefs.h> + +#include <dl-dst.h> + + +const char * +_dl_get_origin (void) +{ + char *result = (char *) -1; + /* We use the environment variable LD_ORIGIN_PATH. If it is set make + a copy and strip out trailing slashes. */ + if (GLRO(dl_origin_path) != NULL) + { + size_t len = strlen (GLRO(dl_origin_path)); + result = (char *) malloc (len + 1); + if (result == NULL) + result = (char *) -1; + else + { + char *cp = __mempcpy (result, GLRO(dl_origin_path), len); + while (cp > result + 1 && cp[-1] == '/') + --cp; + *cp = '\0'; + } + } + + return result; +} diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c index 4004316..117410e 100644 --- a/elf/dl-reloc.c +++ b/elf/dl-reloc.c @@ -1,5 +1,5 @@ /* Relocate a shared object and resolve its references to other loaded objects. - Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1995-2004, 2005 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 @@ -48,8 +48,6 @@ void internal_function __attribute_noinline__ _dl_allocate_static_tls (struct link_map *map) { - size_t offset; - /* If the alignment requirements are too high fail. */ if (map->l_tls_align > GL(dl_tls_static_align)) { @@ -71,15 +69,15 @@ cannot allocate memory in static TLS block")); n = (freebytes - blsize) / map->l_tls_align; - offset = GL(dl_tls_static_used) + (freebytes - n * map->l_tls_align - - map->l_tls_firstbyte_offset); + size_t offset = GL(dl_tls_static_used) + (freebytes - n * map->l_tls_align + - map->l_tls_firstbyte_offset); map->l_tls_offset = GL(dl_tls_static_used) = offset; # elif TLS_DTV_AT_TP size_t used; size_t check; - offset = roundup (GL(dl_tls_static_used), map->l_tls_align); + size_t offset = roundup (GL(dl_tls_static_used), map->l_tls_align); used = offset + map->l_tls_blocksize; check = used; /* dl_tls_static_used includes the TCB at the beginning. */ @@ -93,8 +91,20 @@ cannot allocate memory in static TLS block")); # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" # endif - if (map->l_relocated) - GL(dl_init_static_tls) (map); + /* If the object is not yet relocated we cannot initialize the + static TLS region. Delay it. */ + if (map->l_real->l_relocated) + { +#ifdef SHARED + if (__builtin_expect (THREAD_DTV()[0].counter != GL(dl_tls_generation), + 0)) + /* Update the slot information data for at least the generation of + the DSO we are allocating data for. */ + (void) _dl_update_slotinfo (map->l_tls_modid); +#endif + + GL(dl_init_static_tls) (map); + } else map->l_need_tls_init = 1; } @@ -114,7 +124,10 @@ _dl_nothread_init_static_tls (struct link_map *map) # endif /* Fill in the DTV slot so that a later LD/GD access will find it. */ - THREAD_DTV ()[map->l_tls_modid].pointer = dest; + dtv_t *dtv = THREAD_DTV (); + assert (map->l_tls_modid <= dtv[-1].counter); + dtv[map->l_tls_modid].pointer.val = dest; + dtv[map->l_tls_modid].pointer.is_static = true; /* Initialize the memory. */ memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size), @@ -137,11 +150,20 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], /* Initialize it to make the compiler happy. */ const char *errstring = NULL; +#ifdef SHARED + /* If we are auditing, install the same handlers we need for profiling. */ + consider_profiling |= GLRO(dl_audit) != NULL; +#elif defined PROF + /* Never use dynamic linker profiling for gprof profiling code. */ +# define consider_profiling 0 +#endif + if (l->l_relocated) return; /* If DT_BIND_NOW is set relocate all references in this object. We do not do this if we are profiling, of course. */ + // XXX Correct for auditing? if (!consider_profiling && __builtin_expect (l->l_info[DT_BIND_NOW] != NULL, 0)) lazy = 0; @@ -225,29 +247,6 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], l->l_lookup_cache.ret = (*ref); \ l->l_lookup_cache.value = _lr; })) \ : l) -#define RESOLVE(ref, version, r_type) \ - (ELFW(ST_BIND) ((*ref)->st_info) != STB_LOCAL \ - ? ((__builtin_expect ((*ref) == l->l_lookup_cache.sym, 0) \ - && elf_machine_type_class (r_type) == l->l_lookup_cache.type_class) \ - ? (bump_num_cache_relocations (), \ - (*ref) = l->l_lookup_cache.ret, \ - l->l_lookup_cache.value) \ - : ({ lookup_t _lr; \ - int _tc = elf_machine_type_class (r_type); \ - l->l_lookup_cache.type_class = _tc; \ - l->l_lookup_cache.sym = (*ref); \ - const struct r_found_version *v = NULL; \ - int flags = DL_LOOKUP_ADD_DEPENDENCY; \ - if ((version) != NULL && (version)->hash != 0) \ - { \ - v = (version); \ - flags = 0; \ - } \ - _lr = _dl_lookup_symbol_x (strtab + (*ref)->st_name, l, (ref), \ - scope, v, _tc, flags, NULL); \ - l->l_lookup_cache.ret = (*ref); \ - l->l_lookup_cache.value = _lr; })) \ - : l->l_addr) /* This macro is used as a callback from elf_machine_rel{a,} when a static TLS reloc is about to be performed. Since (in dl-load.c) we @@ -268,6 +267,7 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling); +#ifndef PROF if (__builtin_expect (consider_profiling, 0)) { /* Allocate the array which will contain the already found @@ -276,23 +276,23 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], will be NULL. */ if (l->l_info[DT_PLTRELSZ] == NULL) { - errstring = N_("%s: profiler found no PLTREL in object %s\n"); + errstring = N_("%s: no PLTREL found in object %s\n"); fatal: _dl_fatal_printf (errstring, rtld_progname ?: "<program name unknown>", l->l_name); } - l->l_reloc_result = - (ElfW(Addr) *) calloc (sizeof (ElfW(Addr)), - l->l_info[DT_PLTRELSZ]->d_un.d_val); + l->l_reloc_result = calloc (sizeof (l->l_reloc_result[0]), + l->l_info[DT_PLTRELSZ]->d_un.d_val); if (l->l_reloc_result == NULL) { errstring = N_("\ -%s: profiler out of memory shadowing PLTREL of %s\n"); +%s: out of memory to store relocation results for %s\n"); goto fatal; } } +#endif } /* Mark the object so we know this work has been done. */ diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c index a0aecda..ee2b8b5 100644 --- a/elf/dl-runtime.c +++ b/elf/dl-runtime.c @@ -1,5 +1,5 @@ /* On-demand PLT fixup for shared objects. - Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1995-2006, 2007 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 @@ -22,8 +22,12 @@ #include <alloca.h> #include <stdlib.h> #include <unistd.h> +#include <sys/param.h> #include <ldsodefs.h> +#include <sysdep-cancel.h> #include "dynamic-link.h" +#include <tls.h> + #if (!defined ELF_MACHINE_NO_RELA && !defined ELF_MACHINE_PLT_REL) \ || ELF_MACHINE_NO_REL @@ -51,15 +55,15 @@ function. */ #ifndef ELF_MACHINE_NO_PLT -static ElfW(Addr) -__attribute ((used, noinline)) ARCH_FIXUP_ATTRIBUTE -fixup ( +DL_FIXUP_VALUE_TYPE +__attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE +_dl_fixup ( # ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS - ELF_MACHINE_RUNTIME_FIXUP_ARGS, + ELF_MACHINE_RUNTIME_FIXUP_ARGS, # endif - /* GKM FIXME: Fix trampoline to pass bounds so we can do - without the `__unbounded' qualifier. */ - struct link_map *__unbounded l, ElfW(Word) reloc_offset) + /* GKM FIXME: Fix trampoline to pass bounds so we can do + without the `__unbounded' qualifier. */ + struct link_map *__unbounded l, ElfW(Word) reloc_offset) { const ElfW(Sym) *const symtab = (const void *) D_PTR (l, l_info[DT_SYMTAB]); @@ -70,7 +74,7 @@ fixup ( const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)]; void *const rel_addr = (void *)(l->l_addr + reloc->r_offset); lookup_t result; - ElfW(Addr) value; + DL_FIXUP_VALUE_TYPE value; /* Sanity check that we're really looking at a PLT relocation. */ assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT); @@ -80,8 +84,6 @@ fixup ( if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) { const struct r_found_version *version = NULL; - // XXX Why exactly do we have the differentiation of the flags here? - int flags = DL_LOOKUP_ADD_DEPENDENCY; if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) { @@ -91,27 +93,35 @@ fixup ( version = &l->l_versions[ndx]; if (version->hash == 0) version = NULL; - else - flags = 0; } - result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, - l->l_scope, version, ELF_RTYPE_CLASS_PLT, - DL_LOOKUP_ADD_DEPENDENCY, NULL); + /* We need to keep the scope around so do some locking. This is + not necessary for objects which cannot be unloaded or when + we are not using any threads (yet). */ + int flags = DL_LOOKUP_ADD_DEPENDENCY; + if (!RTLD_SINGLE_THREAD_P) + THREAD_GSCOPE_SET_FLAG (); + + result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, + version, ELF_RTYPE_CLASS_PLT, flags, NULL); + + /* We are done with the global scope. */ + if (!RTLD_SINGLE_THREAD_P) + THREAD_GSCOPE_RESET_FLAG (); /* Currently result contains the base load address (or link map) of the object that defines sym. Now add in the symbol offset. */ - value = (sym ? LOOKUP_VALUE_ADDRESS (result) + sym->st_value : 0); + value = DL_FIXUP_MAKE_VALUE (result, + sym ? (LOOKUP_VALUE_ADDRESS (result) + + sym->st_value) : 0); } else { /* We already found the symbol. The module (and therefore its load address) is also known. */ - value = l->l_addr + sym->st_value; -#ifdef DL_LOOKUP_RETURNS_MAP + value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value); result = l; -#endif } /* And now perhaps the relocation addend. */ @@ -127,45 +137,45 @@ fixup ( #if !defined PROF && !defined ELF_MACHINE_NO_PLT && !__BOUNDED_POINTERS__ -static ElfW(Addr) -__attribute ((used, noinline)) ARCH_FIXUP_ATTRIBUTE -profile_fixup ( +DL_FIXUP_VALUE_TYPE +__attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE +_dl_profile_fixup ( #ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS - ELF_MACHINE_RUNTIME_FIXUP_ARGS, + ELF_MACHINE_RUNTIME_FIXUP_ARGS, #endif - struct link_map *l, ElfW(Word) reloc_offset, ElfW(Addr) retaddr) + struct link_map *l, ElfW(Word) reloc_offset, + ElfW(Addr) retaddr, void *regs, long int *framesizep) { void (*mcount_fct) (ElfW(Addr), ElfW(Addr)) = INTUSE(_dl_mcount); - ElfW(Addr) *resultp; - lookup_t result; - ElfW(Addr) value; /* This is the address in the array where we store the result of previous relocations. */ - resultp = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)]; + struct reloc_result *reloc_result + = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)]; + DL_FIXUP_VALUE_TYPE *resultp = &reloc_result->addr; - value = *resultp; - if (value == 0) + DL_FIXUP_VALUE_TYPE value = *resultp; + if (DL_FIXUP_VALUE_CODE_ADDR (value) == 0) { /* This is the first time we have to relocate this object. */ const ElfW(Sym) *const symtab = (const void *) D_PTR (l, l_info[DT_SYMTAB]); - const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); + const char *strtab = (const char *) D_PTR (l, l_info[DT_STRTAB]); const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset); - const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)]; + const ElfW(Sym) *refsym = &symtab[ELFW(R_SYM) (reloc->r_info)]; + const ElfW(Sym) *defsym = refsym; + lookup_t result; /* Sanity check that we're really looking at a PLT relocation. */ assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT); /* Look up the target symbol. If the symbol is marked STV_PROTECTED don't look in the global scope. */ - if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) + if (__builtin_expect (ELFW(ST_VISIBILITY) (refsym->st_other), 0) == 0) { const struct r_found_version *version = NULL; - // XXX Why exactly do we have the differentiation of the flags here? - int flags = DL_LOOKUP_ADD_DEPENDENCY; if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) { @@ -175,38 +185,208 @@ profile_fixup ( version = &l->l_versions[ndx]; if (version->hash == 0) version = NULL; - else - flags = 0; } - result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, - l->l_scope, version, - ELF_RTYPE_CLASS_PLT, - DL_LOOKUP_ADD_DEPENDENCY, NULL); + /* We need to keep the scope around so do some locking. This is + not necessary for objects which cannot be unloaded or when + we are not using any threads (yet). */ + int flags = DL_LOOKUP_ADD_DEPENDENCY; + if (!RTLD_SINGLE_THREAD_P) + THREAD_GSCOPE_SET_FLAG (); + + result = _dl_lookup_symbol_x (strtab + refsym->st_name, l, + &defsym, l->l_scope, version, + ELF_RTYPE_CLASS_PLT, flags, NULL); + + /* We are done with the global scope. */ + if (!RTLD_SINGLE_THREAD_P) + THREAD_GSCOPE_RESET_FLAG (); /* Currently result contains the base load address (or link map) of the object that defines sym. Now add in the symbol offset. */ - value = (sym ? LOOKUP_VALUE_ADDRESS (result) + sym->st_value : 0); + value = DL_FIXUP_MAKE_VALUE (result, + defsym != NULL + ? LOOKUP_VALUE_ADDRESS (result) + + defsym->st_value : 0); } else { /* We already found the symbol. The module (and therefore its load address) is also known. */ - value = l->l_addr + sym->st_value; -#ifdef DL_LOOKUP_RETURNS_MAP + value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + refsym->st_value); result = l; -#endif } /* And now perhaps the relocation addend. */ value = elf_machine_plt_value (l, reloc, value); +#ifdef SHARED + /* Auditing checkpoint: we have a new binding. Provide the + auditing libraries the possibility to change the value and + tell us whether further auditing is wanted. */ + if (defsym != NULL && GLRO(dl_naudit) > 0) + { + reloc_result->bound = result; + /* Compute index of the symbol entry in the symbol table of + the DSO with the definition. */ + reloc_result->boundndx = (defsym + - (ElfW(Sym) *) D_PTR (result, + l_info[DT_SYMTAB])); + + /* Determine whether any of the two participating DSOs is + interested in auditing. */ + if ((l->l_audit_any_plt | result->l_audit_any_plt) != 0) + { + unsigned int altvalue = 0; + struct audit_ifaces *afct = GLRO(dl_audit); + /* Synthesize a symbol record where the st_value field is + the result. */ + ElfW(Sym) sym = *defsym; + sym.st_value = DL_FIXUP_VALUE_ADDR (value); + + /* Keep track whether there is any interest in tracing + the call in the lower two bits. */ + assert (DL_NNS * 2 <= sizeof (reloc_result->flags) * 8); + assert ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) == 3); + reloc_result->enterexit = LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT; + + const char *strtab2 = (const void *) D_PTR (result, + l_info[DT_STRTAB]); + + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + /* XXX Check whether both DSOs must request action or + only one */ + if ((l->l_audit[cnt].bindflags & LA_FLG_BINDFROM) != 0 + && (result->l_audit[cnt].bindflags & LA_FLG_BINDTO) != 0) + { + unsigned int flags = altvalue; + if (afct->symbind != NULL) + { + uintptr_t new_value + = afct->symbind (&sym, reloc_result->boundndx, + &l->l_audit[cnt].cookie, + &result->l_audit[cnt].cookie, + &flags, + strtab2 + defsym->st_name); + if (new_value != (uintptr_t) sym.st_value) + { + altvalue = LA_SYMB_ALTVALUE; + sym.st_value = new_value; + } + } + + /* Remember the results for every audit library and + store a summary in the first two bits. */ + reloc_result->enterexit + &= flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT); + reloc_result->enterexit + |= ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)) + << ((cnt + 1) * 2)); + } + else + /* If the bind flags say this auditor is not interested, + set the bits manually. */ + reloc_result->enterexit + |= ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) + << ((cnt + 1) * 2)); + + afct = afct->next; + } + + reloc_result->flags = altvalue; + value = DL_FIXUP_ADDR_VALUE (sym.st_value); + } + else + /* Set all bits since this symbol binding is not interesting. */ + reloc_result->enterexit = (1u << DL_NNS) - 1; + } +#endif + /* Store the result for later runs. */ if (__builtin_expect (! GLRO(dl_bind_not), 1)) *resultp = value; } - (*mcount_fct) (retaddr, value); + /* By default we do not call the pltexit function. */ + long int framesize = -1; + +#ifdef SHARED + /* Auditing checkpoint: report the PLT entering and allow the + auditors to change the value. */ + if (DL_FIXUP_VALUE_CODE_ADDR (value) != 0 && GLRO(dl_naudit) > 0 + /* Don't do anything if no auditor wants to intercept this call. */ + && (reloc_result->enterexit & LA_SYMB_NOPLTENTER) == 0) + { + ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound, + l_info[DT_SYMTAB]) + + reloc_result->boundndx); + + /* Set up the sym parameter. */ + ElfW(Sym) sym = *defsym; + sym.st_value = DL_FIXUP_VALUE_ADDR (value); + + /* Get the symbol name. */ + const char *strtab = (const void *) D_PTR (reloc_result->bound, + l_info[DT_STRTAB]); + const char *symname = strtab + sym.st_name; + + /* Keep track of overwritten addresses. */ + unsigned int altvalue = reloc_result->flags; + + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->ARCH_LA_PLTENTER != NULL + && (reloc_result->enterexit + & (LA_SYMB_NOPLTENTER << (2 * (cnt + 1)))) == 0) + { + unsigned int flags = altvalue; + long int new_framesize = -1; + uintptr_t new_value + = afct->ARCH_LA_PLTENTER (&sym, reloc_result->boundndx, + &l->l_audit[cnt].cookie, + &reloc_result->bound->l_audit[cnt].cookie, + regs, &flags, symname, + &new_framesize); + if (new_value != (uintptr_t) sym.st_value) + { + altvalue = LA_SYMB_ALTVALUE; + sym.st_value = new_value; + } + + /* Remember the results for every audit library and + store a summary in the first two bits. */ + reloc_result->enterexit + |= ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)) + << (2 * (cnt + 1))); + + if ((reloc_result->enterexit & (LA_SYMB_NOPLTEXIT + << (2 * (cnt + 1)))) + == 0 && new_framesize != -1 && framesize != -2) + { + /* If this is the first call providing information, + use it. */ + if (framesize == -1) + framesize = new_framesize; + /* If two pltenter calls provide conflicting information, + use the larger value. */ + else if (new_framesize != framesize) + framesize = MAX (new_framesize, framesize); + } + } + + afct = afct->next; + } + + value = DL_FIXUP_ADDR_VALUE (sym.st_value); + } +#endif + + /* Store the frame size information. */ + *framesizep = framesize; + + (*mcount_fct) (retaddr, DL_FIXUP_VALUE_CODE_ADDR (value)); return value; } @@ -214,9 +394,45 @@ profile_fixup ( #endif /* PROF && ELF_MACHINE_NO_PLT */ -/* This macro is defined in dl-machine.h to define the entry point called - by the PLT. The `fixup' function above does the real work, but a little - more twiddling is needed to get the stack right and jump to the address - finally resolved. */ +#include <stdio.h> +void +ARCH_FIXUP_ATTRIBUTE +_dl_call_pltexit (struct link_map *l, ElfW(Word) reloc_offset, + const void *inregs, void *outregs) +{ +#ifdef SHARED + /* This is the address in the array where we store the result of previous + relocations. */ + // XXX Maybe the bound information must be stored on the stack since + // XXX with bind_not a new value could have been stored in the meantime. + struct reloc_result *reloc_result + = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)]; + ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound, + l_info[DT_SYMTAB]) + + reloc_result->boundndx); + + /* Set up the sym parameter. */ + ElfW(Sym) sym = *defsym; + + /* Get the symbol name. */ + const char *strtab = (const void *) D_PTR (reloc_result->bound, + l_info[DT_STRTAB]); + const char *symname = strtab + sym.st_name; + + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->ARCH_LA_PLTEXIT != NULL + && (reloc_result->enterexit + & (LA_SYMB_NOPLTEXIT >> (2 * cnt))) == 0) + { + afct->ARCH_LA_PLTEXIT (&sym, reloc_result->boundndx, + &l->l_audit[cnt].cookie, + &reloc_result->bound->l_audit[cnt].cookie, + inregs, outregs, symname); + } -ELF_MACHINE_RUNTIME_TRAMPOLINE + afct = afct->next; + } +#endif +} diff --git a/elf/dl-sbrk.c b/elf/dl-sbrk.c new file mode 100644 index 0000000..4713a92 --- /dev/null +++ b/elf/dl-sbrk.c @@ -0,0 +1,5 @@ +/* We can use the normal code but we also know the __curbrk is not exported + from ld.so. */ +extern void *__curbrk attribute_hidden; + +#include <sbrk.c> diff --git a/elf/dl-support.c b/elf/dl-support.c index b10dc90..4b7be6b 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -1,5 +1,5 @@ /* Support for dynamic linking code in static libc. - Copyright (C) 1996-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1996-2005, 2006, 2007 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 @@ -29,6 +29,7 @@ #include <bits/libc-lock.h> #include <dl-cache.h> #include <dl-librecon.h> +#include <dl-procinfo.h> #include <unsecvars.h> #include <hp-timing.h> @@ -121,7 +122,7 @@ int _dl_correct_cache_id = _DL_CACHE_DEFAULT_ID; ElfW(Phdr) *_dl_phdr; size_t _dl_phnum; -unsigned long int _dl_hwcap __attribute__ ((nocommon)); +uint64_t _dl_hwcap __attribute__ ((nocommon)); /* Prevailing state of the stack, PF_X indicating it's executable. */ ElfW(Word) _dl_stack_flags = PF_R|PF_W|PF_X; @@ -133,6 +134,11 @@ int (*_dl_make_stack_executable_hook) (void **) internal_function = _dl_make_stack_executable; +/* Function in libpthread to wait for termination of lookups. */ +void (*_dl_wait_lookup_done) (void); + +struct dl_scope_free_list *_dl_scope_free_list; + #ifdef NEED_DL_SYSINFO /* Needed for improved syscall handling on at least x86/Linux. */ uintptr_t _dl_sysinfo = DL_SYSINFO_DEFAULT; @@ -173,13 +179,13 @@ _dl_aux_init (ElfW(auxv_t) *av) GLRO(dl_clktck) = av->a_un.a_val; break; case AT_PHDR: - GL(dl_phdr) = av->a_un.a_ptr; + GL(dl_phdr) = (void *) av->a_un.a_val; break; case AT_PHNUM: GL(dl_phnum) = av->a_un.a_val; break; case AT_HWCAP: - GLRO(dl_hwcap) = av->a_un.a_val; + GLRO(dl_hwcap) = (unsigned long int) av->a_un.a_val; break; #ifdef NEED_DL_SYSINFO case AT_SYSINFO: @@ -188,7 +194,7 @@ _dl_aux_init (ElfW(auxv_t) *av) #endif #if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO case AT_SYSINFO_EHDR: - GL(dl_sysinfo_dso) = av->a_un.a_ptr; + GL(dl_sysinfo_dso) = (void *) av->a_un.a_val; break; #endif case AT_UID: diff --git a/elf/dl-sym.c b/elf/dl-sym.c index ba00ef5..fa0e3a6 100644 --- a/elf/dl-sym.c +++ b/elf/dl-sym.c @@ -1,5 +1,5 @@ /* Look up a symbol in a shared object loaded by `dlopen'. - Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 1999-2002,2004,2006,2007 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 @@ -17,6 +17,7 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#include <assert.h> #include <stddef.h> #include <setjmp.h> #include <libintl.h> @@ -24,6 +25,7 @@ #include <dlfcn.h> #include <ldsodefs.h> #include <dl-hash.h> +#include <sysdep-cancel.h> #ifdef USE_TLS # include <dl-tls.h> #endif @@ -58,6 +60,29 @@ _dl_tls_symaddr (struct link_map *map, const ElfW(Sym) *ref) #endif +struct call_dl_lookup_args +{ + /* Arguments to do_dlsym. */ + struct link_map *map; + const char *name; + struct r_found_version *vers; + int flags; + + /* Return values of do_dlsym. */ + lookup_t loadbase; + const ElfW(Sym) **refp; +}; + +static void +call_dl_lookup (void *ptr) +{ + struct call_dl_lookup_args *args = (struct call_dl_lookup_args *) ptr; + args->map = GLRO(dl_lookup_symbol_x) (args->name, args->map, args->refp, + args->map->l_scope, args->vers, 0, + args->flags, NULL); +} + + static void * internal_function do_sym (void *handle, const char *name, void *who, @@ -75,19 +100,59 @@ do_sym (void *handle, const char *name, void *who, for (Lmid_t ns = 0; ns < DL_NNS; ++ns) for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) - if (caller >= l->l_map_start && caller < l->l_map_end) + if (caller >= l->l_map_start && caller < l->l_map_end + && (l->l_contiguous || _dl_addr_inside_object (l, caller))) { - /* There must be exactly one DSO for the range of the virtual - memory. Otherwise something is really broken. */ match = l; break; } if (handle == RTLD_DEFAULT) - /* Search the global scope. */ - result = GLRO(dl_lookup_symbol_x) (name, match, &ref, match->l_scope, - vers, 0, flags|DL_LOOKUP_ADD_DEPENDENCY, - NULL); + { + /* Search the global scope. We have the simple case where + we look up in the scope of an object which was part of + the initial binary. And then the more complex part + where the object is dynamically loaded and the scope + array can change. */ + if (RTLD_SINGLE_THREAD_P) + result = GLRO(dl_lookup_symbol_x) (name, match, &ref, + match->l_scope, vers, 0, + flags | DL_LOOKUP_ADD_DEPENDENCY, + NULL); + else + { + struct call_dl_lookup_args args; + args.name = name; + args.map = match; + args.vers = vers; + args.flags = flags | DL_LOOKUP_ADD_DEPENDENCY; + args.refp = &ref; + + THREAD_GSCOPE_SET_FLAG (); + + const char *objname; + const char *errstring = NULL; + bool malloced; + int err = GLRO(dl_catch_error) (&objname, &errstring, &malloced, + call_dl_lookup, &args); + + THREAD_GSCOPE_RESET_FLAG (); + + if (__builtin_expect (errstring != NULL, 0)) + { + /* The lookup was unsuccessful. Rethrow the error. */ + char *errstring_dup = strdupa (errstring); + char *objname_dup = strdupa (objname); + if (malloced) + free ((char *) errstring); + + GLRO(dl_signal_error) (err, objname_dup, NULL, errstring_dup); + /* NOTREACHED */ + } + + result = args.map; + } + } else if (handle == RTLD_NEXT) { if (__builtin_expect (match == GL(dl_ns)[LM_ID_BASE]._ns_loaded, 0)) @@ -103,7 +168,7 @@ RTLD_NEXT used in code not dynamically loaded")); while (l->l_loader != NULL) l = l->l_loader; - result = GLRO(dl_lookup_symbol_x) (name, l, &ref, l->l_local_scope, + result = GLRO(dl_lookup_symbol_x) (name, match, &ref, l->l_local_scope, vers, 0, 0, match); } else @@ -116,14 +181,69 @@ RTLD_NEXT used in code not dynamically loaded")); if (ref != NULL) { + void *value; + #if defined USE_TLS && defined SHARED if (ELFW(ST_TYPE) (ref->st_info) == STT_TLS) /* The found symbol is a thread-local storage variable. Return the address for to the current thread. */ - return _dl_tls_symaddr (result, ref); + value = _dl_tls_symaddr (result, ref); + else +#endif + value = DL_SYMBOL_ADDRESS (result, ref); + +#ifdef SHARED + /* Auditing checkpoint: we have a new binding. Provide the + auditing libraries the possibility to change the value and + tell us whether further auditing is wanted. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + const char *strtab = (const char *) D_PTR (result, + l_info[DT_STRTAB]); + /* Compute index of the symbol entry in the symbol table of + the DSO with the definition. */ + unsigned int ndx = (ref - (ElfW(Sym) *) D_PTR (result, + l_info[DT_SYMTAB])); + + if ((match->l_audit_any_plt | result->l_audit_any_plt) != 0) + { + unsigned int altvalue = 0; + struct audit_ifaces *afct = GLRO(dl_audit); + /* Synthesize a symbol record where the st_value field is + the result. */ + ElfW(Sym) sym = *ref; + sym.st_value = (ElfW(Addr)) value; + + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->symbind != NULL + && ((match->l_audit[cnt].bindflags & LA_FLG_BINDFROM) + != 0 + || ((result->l_audit[cnt].bindflags & LA_FLG_BINDTO) + != 0))) + { + unsigned int flags = altvalue | LA_SYMB_DLSYM; + uintptr_t new_value + = afct->symbind (&sym, ndx, + &match->l_audit[cnt].cookie, + &result->l_audit[cnt].cookie, + &flags, strtab + ref->st_name); + if (new_value != (uintptr_t) sym.st_value) + { + altvalue = LA_SYMB_ALTVALUE; + sym.st_value = new_value; + } + } + + afct = afct->next; + } + + value = (void *) sym.st_value; + } + } #endif - return DL_SYMBOL_ADDRESS (result, ref); + return value; } return NULL; diff --git a/elf/dl-symaddr.c b/elf/dl-symaddr.c new file mode 100644 index 0000000..3c850ca --- /dev/null +++ b/elf/dl-symaddr.c @@ -0,0 +1,33 @@ +/* Get the symbol address. Generic version. + Copyright (C) 1999, 2000, 2001, 2003 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <ldsodefs.h> +#include <dl-fptr.h> + +void * +_dl_symbol_address (struct link_map *map, const ElfW(Sym) *ref) +{ + ElfW(Addr) value = (map ? map->l_addr : 0) + ref->st_value; + + /* Return the pointer to function descriptor. */ + if (ELFW(ST_TYPE) (ref->st_info) == STT_FUNC) + return (void *) _dl_make_fptr (map, ref, value); + else + return (void *) value; +} diff --git a/elf/dl-sysdep.c b/elf/dl-sysdep.c new file mode 100644 index 0000000..68e08f4 --- /dev/null +++ b/elf/dl-sysdep.c @@ -0,0 +1,602 @@ +/* Operating system support for run-time dynamic linker. Generic Unix version. + Copyright (C) 1995-1998, 2000-2005, 2007 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <assert.h> +#include <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <libintl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <ldsodefs.h> +#include <stdio-common/_itoa.h> +#include <fpu_control.h> + +#include <entry.h> +#include <dl-machine.h> +#include <dl-procinfo.h> +#include <dl-osinfo.h> +#include <hp-timing.h> +#include <tls.h> + +#ifdef _DL_FIRST_PLATFORM +# define _DL_FIRST_EXTRA (_DL_FIRST_PLATFORM + _DL_PLATFORMS_COUNT) +#else +# define _DL_FIRST_EXTRA _DL_HWCAP_COUNT +#endif + +extern char **_environ attribute_hidden; +extern void _end attribute_hidden; + +/* Protect SUID program against misuse of file descriptors. */ +extern void __libc_check_standard_fds (void); + +#ifdef NEED_DL_BASE_ADDR +ElfW(Addr) _dl_base_addr; +#endif +int __libc_enable_secure attribute_relro = 0; +INTVARDEF(__libc_enable_secure) +int __libc_multiple_libcs = 0; /* Defining this here avoids the inclusion + of init-first. */ +/* This variable contains the lowest stack address ever used. */ +void *__libc_stack_end attribute_relro = NULL; +rtld_hidden_data_def(__libc_stack_end) +static ElfW(auxv_t) *_dl_auxv attribute_relro; + +#ifndef DL_FIND_ARG_COMPONENTS +# define DL_FIND_ARG_COMPONENTS(cookie, argc, argv, envp, auxp) \ + do { \ + void **_tmp; \ + (argc) = *(long int *) cookie; \ + (argv) = (char **) ((long int *) cookie + 1); \ + (envp) = (argv) + (argc) + 1; \ + for (_tmp = (void **) (envp); *_tmp; ++_tmp) \ + continue; \ + (auxp) = (void *) ++_tmp; \ + } while (0) +#endif + +#ifndef DL_STACK_END +# define DL_STACK_END(cookie) ((void *) (cookie)) +#endif + +ElfW(Addr) +_dl_sysdep_start (void **start_argptr, + void (*dl_main) (const ElfW(Phdr) *phdr, ElfW(Word) phnum, + ElfW(Addr) *user_entry)) +{ + const ElfW(Phdr) *phdr = NULL; + ElfW(Word) phnum = 0; + ElfW(Addr) user_entry; + ElfW(auxv_t) *av; +#ifdef HAVE_AUX_SECURE +# define set_seen(tag) (tag) /* Evaluate for the side effects. */ +# define set_seen_secure() ((void) 0) +#else + uid_t uid = 0; + gid_t gid = 0; + unsigned int seen = 0; +# define set_seen_secure() (seen = -1) +# ifdef HAVE_AUX_XID +# define set_seen(tag) (tag) /* Evaluate for the side effects. */ +# else +# define M(type) (1 << (type)) +# define set_seen(tag) seen |= M ((tag)->a_type) +# endif +#endif +#ifdef NEED_DL_SYSINFO + uintptr_t new_sysinfo = 0; +#endif + + __libc_stack_end = DL_STACK_END (start_argptr); + DL_FIND_ARG_COMPONENTS (start_argptr, _dl_argc, INTUSE(_dl_argv), _environ, + _dl_auxv); + + user_entry = (ElfW(Addr)) ENTRY_POINT; + GLRO(dl_platform) = NULL; /* Default to nothing known about the platform. */ + + for (av = _dl_auxv; av->a_type != AT_NULL; set_seen (av++)) + switch (av->a_type) + { + case AT_PHDR: + phdr = (void *) av->a_un.a_val; + break; + case AT_PHNUM: + phnum = av->a_un.a_val; + break; + case AT_PAGESZ: + GLRO(dl_pagesize) = av->a_un.a_val; + break; + case AT_ENTRY: + user_entry = av->a_un.a_val; + break; +#ifdef NEED_DL_BASE_ADDR + case AT_BASE: + _dl_base_addr = av->a_un.a_val; + break; +#endif +#ifndef HAVE_AUX_SECURE + case AT_UID: + case AT_EUID: + uid ^= av->a_un.a_val; + break; + case AT_GID: + case AT_EGID: + gid ^= av->a_un.a_val; + break; +#endif + case AT_SECURE: +#ifndef HAVE_AUX_SECURE + seen = -1; +#endif + INTUSE(__libc_enable_secure) = av->a_un.a_val; + break; + case AT_PLATFORM: + GLRO(dl_platform) = (void *) av->a_un.a_val; + break; + case AT_HWCAP: + GLRO(dl_hwcap) = (unsigned long int) av->a_un.a_val; + break; + case AT_CLKTCK: + GLRO(dl_clktck) = av->a_un.a_val; + break; + case AT_FPUCW: + GLRO(dl_fpu_control) = av->a_un.a_val; + break; +#ifdef NEED_DL_SYSINFO + case AT_SYSINFO: + new_sysinfo = av->a_un.a_val; + break; +#endif +#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO + case AT_SYSINFO_EHDR: + GLRO(dl_sysinfo_dso) = (void *) av->a_un.a_val; + break; +#endif +#ifdef DL_PLATFORM_AUXV + DL_PLATFORM_AUXV +#endif + } + +#ifndef HAVE_AUX_SECURE + if (seen != -1) + { + /* Fill in the values we have not gotten from the kernel through the + auxiliary vector. */ +# ifndef HAVE_AUX_XID +# define SEE(UID, var, uid) \ + if ((seen & M (AT_##UID)) == 0) var ^= __get##uid () + SEE (UID, uid, uid); + SEE (EUID, uid, euid); + SEE (GID, gid, gid); + SEE (EGID, gid, egid); +# endif + + /* If one of the two pairs of IDs does not match this is a setuid + or setgid run. */ + INTUSE(__libc_enable_secure) = uid | gid; + } +#endif + +#ifndef HAVE_AUX_PAGESIZE + if (GLRO(dl_pagesize) == 0) + GLRO(dl_pagesize) = __getpagesize (); +#endif + +#if defined NEED_DL_SYSINFO + /* Only set the sysinfo value if we also have the vsyscall DSO. */ + if (GLRO(dl_sysinfo_dso) != 0 && new_sysinfo) + GLRO(dl_sysinfo) = new_sysinfo; +#endif + +#ifdef DL_SYSDEP_INIT + DL_SYSDEP_INIT; +#endif + +#ifdef DL_PLATFORM_INIT + DL_PLATFORM_INIT; +#endif + + /* Determine the length of the platform name. */ + if (GLRO(dl_platform) != NULL) + GLRO(dl_platformlen) = strlen (GLRO(dl_platform)); + + if (__sbrk (0) == &_end) + /* The dynamic linker was run as a program, and so the initial break + starts just after our bss, at &_end. The malloc in dl-minimal.c + will consume the rest of this page, so tell the kernel to move the + break up that far. When the user program examines its break, it + will see this new value and not clobber our data. */ + __sbrk (GLRO(dl_pagesize) + - ((&_end - (void *) 0) & (GLRO(dl_pagesize) - 1))); + + /* If this is a SUID program we make sure that FDs 0, 1, and 2 are + allocated. If necessary we are doing it ourself. If it is not + possible we stop the program. */ + if (__builtin_expect (INTUSE(__libc_enable_secure), 0)) + __libc_check_standard_fds (); + + (*dl_main) (phdr, phnum, &user_entry); + return user_entry; +} + +void +internal_function +_dl_sysdep_start_cleanup (void) +{ +} + +void +internal_function +_dl_show_auxv (void) +{ + char buf[64]; + ElfW(auxv_t) *av; + + /* Terminate string. */ + buf[63] = '\0'; + + /* The following code assumes that the AT_* values are encoded + starting from 0 with AT_NULL, 1 for AT_IGNORE, and all other values + close by (otherwise the array will be too large). In case we have + to support a platform where these requirements are not fulfilled + some alternative implementation has to be used. */ + for (av = _dl_auxv; av->a_type != AT_NULL; ++av) + { + static const struct + { + const char label[20]; + enum { unknown = 0, dec, hex, str, ignore } form; + } auxvars[] = + { + [AT_EXECFD - 2] = { "AT_EXECFD: ", dec }, + [AT_PHDR - 2] = { "AT_PHDR: 0x", hex }, + [AT_PHENT - 2] = { "AT_PHENT: ", dec }, + [AT_PHNUM - 2] = { "AT_PHNUM: ", dec }, + [AT_PAGESZ - 2] = { "AT_PAGESZ: ", dec }, + [AT_BASE - 2] = { "AT_BASE: 0x", hex }, + [AT_FLAGS - 2] = { "AT_FLAGS: 0x", hex }, + [AT_ENTRY - 2] = { "AT_ENTRY: 0x", hex }, + [AT_NOTELF - 2] = { "AT_NOTELF: ", hex }, + [AT_UID - 2] = { "AT_UID: ", dec }, + [AT_EUID - 2] = { "AT_EUID: ", dec }, + [AT_GID - 2] = { "AT_GID: ", dec }, + [AT_EGID - 2] = { "AT_EGID: ", dec }, + [AT_PLATFORM - 2] = { "AT_PLATFORM: ", str }, + [AT_HWCAP - 2] = { "AT_HWCAP: ", hex }, + [AT_CLKTCK - 2] = { "AT_CLKTCK: ", dec }, + [AT_FPUCW - 2] = { "AT_FPUCW: ", hex }, + [AT_DCACHEBSIZE - 2] = { "AT_DCACHEBSIZE: 0x", hex }, + [AT_ICACHEBSIZE - 2] = { "AT_ICACHEBSIZE: 0x", hex }, + [AT_UCACHEBSIZE - 2] = { "AT_UCACHEBSIZE: 0x", hex }, + [AT_IGNOREPPC - 2] = { "AT_IGNOREPPC", ignore }, + [AT_SECURE - 2] = { "AT_SECURE: ", dec }, + [AT_SYSINFO - 2] = { "AT_SYSINFO: 0x", hex }, + [AT_SYSINFO_EHDR - 2] = { "AT_SYSINFO_EHDR: 0x", hex }, + }; + unsigned int idx = (unsigned int) (av->a_type - 2); + + if ((unsigned int) av->a_type < 2u || auxvars[idx].form == ignore) + continue; + + assert (AT_NULL == 0); + assert (AT_IGNORE == 1); + + if (av->a_type == AT_HWCAP) + { + /* This is handled special. */ + if (_dl_procinfo (av->a_un.a_val) == 0) + continue; + } + + if (idx < sizeof (auxvars) / sizeof (auxvars[0]) + && auxvars[idx].form != unknown) + { + const char *val = (char *) av->a_un.a_val; + + if (__builtin_expect (auxvars[idx].form, dec) == dec) + val = _itoa ((unsigned long int) av->a_un.a_val, + buf + sizeof buf - 1, 10, 0); + else if (__builtin_expect (auxvars[idx].form, hex) == hex) + val = _itoa ((unsigned long int) av->a_un.a_val, + buf + sizeof buf - 1, 16, 0); + + _dl_printf ("%s%s\n", auxvars[idx].label, val); + + continue; + } + + /* Unknown value: print a generic line. */ + char buf2[17]; + buf[sizeof (buf2) - 1] = '\0'; + const char *val2 = _itoa ((unsigned long int) av->a_un.a_val, + buf2 + sizeof buf2 - 1, 16, 0); + const char *val = _itoa ((unsigned long int) av->a_type, + buf + sizeof buf - 1, 16, 0); + _dl_printf ("AT_??? (0x%s): 0x%s\n", val, val2); + } +} + + +/* Return an array of useful/necessary hardware capability names. */ +const struct r_strlenpair * +internal_function +_dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz, + size_t *max_capstrlen) +{ + /* Determine how many important bits are set. */ + uint64_t masked = GLRO(dl_hwcap) & GLRO(dl_hwcap_mask); + size_t cnt = platform != NULL; + size_t n, m; + size_t total; + struct r_strlenpair *temp; + struct r_strlenpair *result; + struct r_strlenpair *rp; + char *cp; + + /* Count the number of bits set in the masked value. */ + for (n = 0; (~((1ULL << n) - 1) & masked) != 0; ++n) + if ((masked & (1ULL << n)) != 0) + ++cnt; + +#if (defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO) && defined SHARED + /* The system-supplied DSO can contain a note of type 2, vendor "GNU". + This gives us a list of names to treat as fake hwcap bits. */ + + const char *dsocaps = NULL; + size_t dsocapslen = 0; + if (GLRO(dl_sysinfo_map) != NULL) + { + const ElfW(Phdr) *const phdr = GLRO(dl_sysinfo_map)->l_phdr; + const ElfW(Word) phnum = GLRO(dl_sysinfo_map)->l_phnum; + for (uint_fast16_t i = 0; i < phnum; ++i) + if (phdr[i].p_type == PT_NOTE) + { + const ElfW(Addr) start = (phdr[i].p_vaddr + + GLRO(dl_sysinfo_map)->l_addr); + const struct + { + ElfW(Word) vendorlen; + ElfW(Word) datalen; + ElfW(Word) type; + } *note = (const void *) start; + while ((ElfW(Addr)) (note + 1) - start < phdr[i].p_memsz) + { +#define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word))) + if (note->type == 2 + && note->vendorlen == sizeof "GNU" + && !memcmp ((note + 1), "GNU", sizeof "GNU") + && note->datalen > 2 * sizeof (ElfW(Word)) + 2) + { + const ElfW(Word) *p = ((const void *) (note + 1) + + ROUND (sizeof "GNU")); + cnt += *p++; + ++p; /* Skip mask word. */ + dsocaps = (const char *) p; + dsocapslen = note->datalen - sizeof *p * 2; + break; + } + note = ((const void *) (note + 1) + + ROUND (note->vendorlen) + ROUND (note->datalen)); + } + if (dsocaps != NULL) + break; + } + } +#endif + +#ifdef USE_TLS + /* For TLS enabled builds always add 'tls'. */ + ++cnt; +#else + if (cnt == 0) + { + /* If we no have platform name and no important capability we only + have the base directory to search. */ + result = (struct r_strlenpair *) malloc (sizeof (*result)); + if (result == NULL) + goto no_memory; + + result[0].str = (char *) result; /* Does not really matter. */ + result[0].len = 0; + + *sz = 1; + return result; + } +#endif + + /* Create temporary data structure to generate result table. */ + temp = (struct r_strlenpair *) alloca (cnt * sizeof (*temp)); + m = 0; +#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO + if (dsocaps != NULL) + { + const ElfW(Word) mask = ((const ElfW(Word) *) dsocaps)[-1]; + GLRO(dl_hwcap) |= (uint64_t) mask << _DL_FIRST_EXTRA; + size_t len; + for (const char *p = dsocaps; p < dsocaps + dsocapslen; p += len + 1) + { + uint_fast8_t bit = *p++; + len = strlen (p); + + /* Skip entries that are not enabled in the mask word. */ + if (__builtin_expect (mask & ((ElfW(Word)) 1 << bit), 1)) + { + temp[m].str = p; + temp[m].len = len; + ++m; + } + else + --cnt; + } + } +#endif + for (n = 0; masked != 0; ++n) + if ((masked & (1ULL << n)) != 0) + { + temp[m].str = _dl_hwcap_string (n); + temp[m].len = strlen (temp[m].str); + masked ^= 1ULL << n; + ++m; + } + if (platform != NULL) + { + temp[m].str = platform; + temp[m].len = platform_len; + ++m; + } +#ifdef USE_TLS + temp[m].str = "tls"; + temp[m].len = 3; + ++m; +#endif + assert (m == cnt); + + /* Determine the total size of all strings together. */ + if (cnt == 1) + total = temp[0].len + 1; + else + { + total = temp[0].len + temp[cnt - 1].len + 2; + if (cnt > 2) + { + total <<= 1; + for (n = 1; n + 1 < cnt; ++n) + total += temp[n].len + 1; + if (cnt > 3 + && (cnt >= sizeof (size_t) * 8 + || total + (sizeof (*result) << 3) + >= (1UL << (sizeof (size_t) * 8 - cnt + 3)))) + _dl_signal_error (ENOMEM, NULL, NULL, + N_("cannot create capability list")); + + total <<= cnt - 3; + } + } + + /* The result structure: we use a very compressed way to store the + various combinations of capability names. */ + *sz = 1 << cnt; + result = (struct r_strlenpair *) malloc (*sz * sizeof (*result) + total); + if (result == NULL) + { +#ifndef USE_TLS + no_memory: +#endif + _dl_signal_error (ENOMEM, NULL, NULL, + N_("cannot create capability list")); + } + + if (cnt == 1) + { + result[0].str = (char *) (result + *sz); + result[0].len = temp[0].len + 1; + result[1].str = (char *) (result + *sz); + result[1].len = 0; + cp = __mempcpy ((char *) (result + *sz), temp[0].str, temp[0].len); + *cp = '/'; + *sz = 2; + *max_capstrlen = result[0].len; + + return result; + } + + /* Fill in the information. This follows the following scheme + (indeces from TEMP for four strings): + entry #0: 0, 1, 2, 3 binary: 1111 + #1: 0, 1, 3 1101 + #2: 0, 2, 3 1011 + #3: 0, 3 1001 + This allows the representation of all possible combinations of + capability names in the string. First generate the strings. */ + result[1].str = result[0].str = cp = (char *) (result + *sz); +#define add(idx) \ + cp = __mempcpy (__mempcpy (cp, temp[idx].str, temp[idx].len), "/", 1); + if (cnt == 2) + { + add (1); + add (0); + } + else + { + n = 1 << (cnt - 1); + do + { + n -= 2; + + /* We always add the last string. */ + add (cnt - 1); + + /* Add the strings which have the bit set in N. */ + for (m = cnt - 2; m > 0; --m) + if ((n & (1 << m)) != 0) + add (m); + + /* Always add the first string. */ + add (0); + } + while (n != 0); + } +#undef add + + /* Now we are ready to install the string pointers and length. */ + for (n = 0; n < (1UL << cnt); ++n) + result[n].len = 0; + n = cnt; + do + { + size_t mask = 1 << --n; + + rp = result; + for (m = 1 << cnt; m > 0; ++rp) + if ((--m & mask) != 0) + rp->len += temp[n].len + 1; + } + while (n != 0); + + /* The first half of the strings all include the first string. */ + n = (1 << cnt) - 2; + rp = &result[2]; + while (n != (1UL << (cnt - 1))) + { + if ((--n & 1) != 0) + rp[0].str = rp[-2].str + rp[-2].len; + else + rp[0].str = rp[-1].str; + ++rp; + } + + /* The second half starts right after the first part of the string of + the corresponding entry in the first half. */ + do + { + rp[0].str = rp[-(1 << (cnt - 1))].str + temp[cnt - 1].len + 1; + ++rp; + } + while (--n != 0); + + /* The maximum string length. */ + *max_capstrlen = result[0].len; + + return result; +} diff --git a/elf/dl-tls.c b/elf/dl-tls.c new file mode 100644 index 0000000..a0f4f77 --- /dev/null +++ b/elf/dl-tls.c @@ -0,0 +1,843 @@ +/* Thread-local storage handling in the ELF dynamic linker. Generic version. + Copyright (C) 2002,2003,2004,2005,2006 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <assert.h> +#include <errno.h> +#include <libintl.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/param.h> + +#include <tls.h> + +/* We don't need any of this if TLS is not supported. */ +#ifdef USE_TLS + +# include <dl-tls.h> +# include <ldsodefs.h> + +/* Amount of excess space to allocate in the static TLS area + to allow dynamic loading of modules defining IE-model TLS data. */ +# define TLS_STATIC_SURPLUS 64 + DL_NNS * 100 + +/* Value used for dtv entries for which the allocation is delayed. */ +# define TLS_DTV_UNALLOCATED ((void *) -1l) + + +/* Out-of-memory handler. */ +# ifdef SHARED +static void +__attribute__ ((__noreturn__)) +oom (void) +{ + _dl_fatal_printf ("cannot allocate memory for thread-local data: ABORT\n"); +} +# endif + + +size_t +internal_function +_dl_next_tls_modid (void) +{ + size_t result; + + if (__builtin_expect (GL(dl_tls_dtv_gaps), false)) + { + size_t disp = 0; + struct dtv_slotinfo_list *runp = GL(dl_tls_dtv_slotinfo_list); + + /* Note that this branch will never be executed during program + start since there are no gaps at that time. Therefore it + does not matter that the dl_tls_dtv_slotinfo is not allocated + yet when the function is called for the first times. + + NB: the offset +1 is due to the fact that DTV[0] is used + for something else. */ + result = GL(dl_tls_static_nelem) + 1; + if (result <= GL(dl_tls_max_dtv_idx)) + do + { + while (result - disp < runp->len) + { + if (runp->slotinfo[result - disp].map == NULL) + break; + + ++result; + assert (result <= GL(dl_tls_max_dtv_idx) + 1); + } + + if (result - disp < runp->len) + break; + + disp += runp->len; + } + while ((runp = runp->next) != NULL); + + if (result > GL(dl_tls_max_dtv_idx)) + { + /* The new index must indeed be exactly one higher than the + previous high. */ + assert (result == GL(dl_tls_max_dtv_idx) + 1); + /* There is no gap anymore. */ + GL(dl_tls_dtv_gaps) = false; + + goto nogaps; + } + } + else + { + /* No gaps, allocate a new entry. */ + nogaps: + + result = ++GL(dl_tls_max_dtv_idx); + } + + return result; +} + + +# ifdef SHARED +void +internal_function +_dl_determine_tlsoffset (void) +{ + size_t max_align = TLS_TCB_ALIGN; + size_t freetop = 0; + size_t freebottom = 0; + + /* The first element of the dtv slot info list is allocated. */ + assert (GL(dl_tls_dtv_slotinfo_list) != NULL); + /* There is at this point only one element in the + dl_tls_dtv_slotinfo_list list. */ + assert (GL(dl_tls_dtv_slotinfo_list)->next == NULL); + + struct dtv_slotinfo *slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo; + + /* Determining the offset of the various parts of the static TLS + block has several dependencies. In addition we have to work + around bugs in some toolchains. + + Each TLS block from the objects available at link time has a size + and an alignment requirement. The GNU ld computes the alignment + requirements for the data at the positions *in the file*, though. + I.e, it is not simply possible to allocate a block with the size + of the TLS program header entry. The data is layed out assuming + that the first byte of the TLS block fulfills + + p_vaddr mod p_align == &TLS_BLOCK mod p_align + + This means we have to add artificial padding at the beginning of + the TLS block. These bytes are never used for the TLS data in + this module but the first byte allocated must be aligned + according to mod p_align == 0 so that the first byte of the TLS + block is aligned according to p_vaddr mod p_align. This is ugly + and the linker can help by computing the offsets in the TLS block + assuming the first byte of the TLS block is aligned according to + p_align. + + The extra space which might be allocated before the first byte of + the TLS block need not go unused. The code below tries to use + that memory for the next TLS block. This can work if the total + memory requirement for the next TLS block is smaller than the + gap. */ + +# if TLS_TCB_AT_TP + /* We simply start with zero. */ + size_t offset = 0; + + for (size_t cnt = 0; slotinfo[cnt].map != NULL; ++cnt) + { + assert (cnt < GL(dl_tls_dtv_slotinfo_list)->len); + + size_t firstbyte = (-slotinfo[cnt].map->l_tls_firstbyte_offset + & (slotinfo[cnt].map->l_tls_align - 1)); + size_t off; + max_align = MAX (max_align, slotinfo[cnt].map->l_tls_align); + + if (freebottom - freetop >= slotinfo[cnt].map->l_tls_blocksize) + { + off = roundup (freetop + slotinfo[cnt].map->l_tls_blocksize + - firstbyte, slotinfo[cnt].map->l_tls_align) + + firstbyte; + if (off <= freebottom) + { + freetop = off; + + /* XXX For some architectures we perhaps should store the + negative offset. */ + slotinfo[cnt].map->l_tls_offset = off; + continue; + } + } + + off = roundup (offset + slotinfo[cnt].map->l_tls_blocksize - firstbyte, + slotinfo[cnt].map->l_tls_align) + firstbyte; + if (off > offset + slotinfo[cnt].map->l_tls_blocksize + + (freebottom - freetop)) + { + freetop = offset; + freebottom = off - slotinfo[cnt].map->l_tls_blocksize; + } + offset = off; + + /* XXX For some architectures we perhaps should store the + negative offset. */ + slotinfo[cnt].map->l_tls_offset = off; + } + + GL(dl_tls_static_used) = offset; + GL(dl_tls_static_size) = (roundup (offset + TLS_STATIC_SURPLUS, max_align) + + TLS_TCB_SIZE); +# elif TLS_DTV_AT_TP + /* The TLS blocks start right after the TCB. */ + size_t offset = TLS_TCB_SIZE; + + for (size_t cnt = 0; slotinfo[cnt].map != NULL; ++cnt) + { + assert (cnt < GL(dl_tls_dtv_slotinfo_list)->len); + + size_t firstbyte = (-slotinfo[cnt].map->l_tls_firstbyte_offset + & (slotinfo[cnt].map->l_tls_align - 1)); + size_t off; + max_align = MAX (max_align, slotinfo[cnt].map->l_tls_align); + + if (slotinfo[cnt].map->l_tls_blocksize <= freetop - freebottom) + { + off = roundup (freebottom, slotinfo[cnt].map->l_tls_align); + if (off - freebottom < firstbyte) + off += slotinfo[cnt].map->l_tls_align; + if (off + slotinfo[cnt].map->l_tls_blocksize - firstbyte <= freetop) + { + slotinfo[cnt].map->l_tls_offset = off - firstbyte; + freebottom = (off + slotinfo[cnt].map->l_tls_blocksize + - firstbyte); + continue; + } + } + + off = roundup (offset, slotinfo[cnt].map->l_tls_align); + if (off - offset < firstbyte) + off += slotinfo[cnt].map->l_tls_align; + + slotinfo[cnt].map->l_tls_offset = off - firstbyte; + if (off - firstbyte - offset > freetop - freebottom) + { + freebottom = offset; + freetop = off - firstbyte; + } + + offset = off + slotinfo[cnt].map->l_tls_blocksize - firstbyte; + } + + GL(dl_tls_static_used) = offset; + GL(dl_tls_static_size) = roundup (offset + TLS_STATIC_SURPLUS, + TLS_TCB_ALIGN); +# else +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" +# endif + + /* The alignment requirement for the static TLS block. */ + GL(dl_tls_static_align) = max_align; +} + + +/* This is called only when the data structure setup was skipped at startup, + when there was no need for it then. Now we have dynamically loaded + something needing TLS, or libpthread needs it. */ +int +internal_function +_dl_tls_setup (void) +{ + assert (GL(dl_tls_dtv_slotinfo_list) == NULL); + assert (GL(dl_tls_max_dtv_idx) == 0); + + const size_t nelem = 2 + TLS_SLOTINFO_SURPLUS; + + GL(dl_tls_dtv_slotinfo_list) + = calloc (1, (sizeof (struct dtv_slotinfo_list) + + nelem * sizeof (struct dtv_slotinfo))); + if (GL(dl_tls_dtv_slotinfo_list) == NULL) + return -1; + + GL(dl_tls_dtv_slotinfo_list)->len = nelem; + + /* Number of elements in the static TLS block. It can't be zero + because of various assumptions. The one element is null. */ + GL(dl_tls_static_nelem) = GL(dl_tls_max_dtv_idx) = 1; + + /* This initializes more variables for us. */ + _dl_determine_tlsoffset (); + + return 0; +} +rtld_hidden_def (_dl_tls_setup) +# endif + +static void * +internal_function +allocate_dtv (void *result) +{ + dtv_t *dtv; + size_t dtv_length; + + /* We allocate a few more elements in the dtv than are needed for the + initial set of modules. This should avoid in most cases expansions + of the dtv. */ + dtv_length = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS; + dtv = calloc (dtv_length + 2, sizeof (dtv_t)); + if (dtv != NULL) + { + /* This is the initial length of the dtv. */ + dtv[0].counter = dtv_length; + + /* The rest of the dtv (including the generation counter) is + Initialize with zero to indicate nothing there. */ + + /* Add the dtv to the thread data structures. */ + INSTALL_DTV (result, dtv); + } + else + result = NULL; + + return result; +} + + +/* Get size and alignment requirements of the static TLS block. */ +void +internal_function +_dl_get_tls_static_info (size_t *sizep, size_t *alignp) +{ + *sizep = GL(dl_tls_static_size); + *alignp = GL(dl_tls_static_align); +} + + +void * +internal_function +_dl_allocate_tls_storage (void) +{ + void *result; + size_t size = GL(dl_tls_static_size); + +# if TLS_DTV_AT_TP + /* Memory layout is: + [ TLS_PRE_TCB_SIZE ] [ TLS_TCB_SIZE ] [ TLS blocks ] + ^ This should be returned. */ + size += (TLS_PRE_TCB_SIZE + GL(dl_tls_static_align) - 1) + & ~(GL(dl_tls_static_align) - 1); +# endif + + /* Allocate a correctly aligned chunk of memory. */ + result = __libc_memalign (GL(dl_tls_static_align), size); + if (__builtin_expect (result != NULL, 1)) + { + /* Allocate the DTV. */ + void *allocated = result; + +# if TLS_TCB_AT_TP + /* The TCB follows the TLS blocks. */ + result = (char *) result + size - TLS_TCB_SIZE; + + /* Clear the TCB data structure. We can't ask the caller (i.e. + libpthread) to do it, because we will initialize the DTV et al. */ + memset (result, '\0', TLS_TCB_SIZE); +# elif TLS_DTV_AT_TP + result = (char *) result + size - GL(dl_tls_static_size); + + /* Clear the TCB data structure and TLS_PRE_TCB_SIZE bytes before it. + We can't ask the caller (i.e. libpthread) to do it, because we will + initialize the DTV et al. */ + memset ((char *) result - TLS_PRE_TCB_SIZE, '\0', + TLS_PRE_TCB_SIZE + TLS_TCB_SIZE); +# endif + + result = allocate_dtv (result); + if (result == NULL) + free (allocated); + } + + return result; +} + + +void * +internal_function +_dl_allocate_tls_init (void *result) +{ + if (result == NULL) + /* The memory allocation failed. */ + return NULL; + + dtv_t *dtv = GET_DTV (result); + struct dtv_slotinfo_list *listp; + size_t total = 0; + size_t maxgen = 0; + + /* We have to prepare the dtv for all currently loaded modules using + TLS. For those which are dynamically loaded we add the values + indicating deferred allocation. */ + listp = GL(dl_tls_dtv_slotinfo_list); + while (1) + { + size_t cnt; + + for (cnt = total == 0 ? 1 : 0; cnt < listp->len; ++cnt) + { + struct link_map *map; + void *dest; + + /* Check for the total number of used slots. */ + if (total + cnt > GL(dl_tls_max_dtv_idx)) + break; + + map = listp->slotinfo[cnt].map; + if (map == NULL) + /* Unused entry. */ + continue; + + /* Keep track of the maximum generation number. This might + not be the generation counter. */ + maxgen = MAX (maxgen, listp->slotinfo[cnt].gen); + + if (map->l_tls_offset == NO_TLS_OFFSET) + { + /* For dynamically loaded modules we simply store + the value indicating deferred allocation. */ + dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED; + dtv[map->l_tls_modid].pointer.is_static = false; + continue; + } + + assert (map->l_tls_modid == cnt); + assert (map->l_tls_blocksize >= map->l_tls_initimage_size); +# if TLS_TCB_AT_TP + assert ((size_t) map->l_tls_offset >= map->l_tls_blocksize); + dest = (char *) result - map->l_tls_offset; +# elif TLS_DTV_AT_TP + dest = (char *) result + map->l_tls_offset; +# else +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" +# endif + + /* Copy the initialization image and clear the BSS part. */ + dtv[map->l_tls_modid].pointer.val = dest; + dtv[map->l_tls_modid].pointer.is_static = true; + memset (__mempcpy (dest, map->l_tls_initimage, + map->l_tls_initimage_size), '\0', + map->l_tls_blocksize - map->l_tls_initimage_size); + } + + total += cnt; + if (total >= GL(dl_tls_max_dtv_idx)) + break; + + listp = listp->next; + assert (listp != NULL); + } + + /* The DTV version is up-to-date now. */ + dtv[0].counter = maxgen; + + return result; +} +rtld_hidden_def (_dl_allocate_tls_init) + +void * +internal_function +_dl_allocate_tls (void *mem) +{ + return _dl_allocate_tls_init (mem == NULL + ? _dl_allocate_tls_storage () + : allocate_dtv (mem)); +} +rtld_hidden_def (_dl_allocate_tls) + + +void +internal_function +_dl_deallocate_tls (void *tcb, bool dealloc_tcb) +{ + dtv_t *dtv = GET_DTV (tcb); + + /* We need to free the memory allocated for non-static TLS. */ + for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt) + if (! dtv[1 + cnt].pointer.is_static + && dtv[1 + cnt].pointer.val != TLS_DTV_UNALLOCATED) + free (dtv[1 + cnt].pointer.val); + + /* The array starts with dtv[-1]. */ +#ifdef SHARED + if (dtv != GL(dl_initial_dtv)) +#endif + free (dtv - 1); + + if (dealloc_tcb) + { +# if TLS_TCB_AT_TP + /* The TCB follows the TLS blocks. Back up to free the whole block. */ + tcb -= GL(dl_tls_static_size) - TLS_TCB_SIZE; +# elif TLS_DTV_AT_TP + /* Back up the TLS_PRE_TCB_SIZE bytes. */ + tcb -= (TLS_PRE_TCB_SIZE + GL(dl_tls_static_align) - 1) + & ~(GL(dl_tls_static_align) - 1); +# endif + free (tcb); + } +} +rtld_hidden_def (_dl_deallocate_tls) + + +# ifdef SHARED +/* The __tls_get_addr function has two basic forms which differ in the + arguments. The IA-64 form takes two parameters, the module ID and + offset. The form used, among others, on IA-32 takes a reference to + a special structure which contain the same information. The second + form seems to be more often used (in the moment) so we default to + it. Users of the IA-64 form have to provide adequate definitions + of the following macros. */ +# ifndef GET_ADDR_ARGS +# define GET_ADDR_ARGS tls_index *ti +# endif +# ifndef GET_ADDR_MODULE +# define GET_ADDR_MODULE ti->ti_module +# endif +# ifndef GET_ADDR_OFFSET +# define GET_ADDR_OFFSET ti->ti_offset +# endif + + +static void * +allocate_and_init (struct link_map *map) +{ + void *newp; + + newp = __libc_memalign (map->l_tls_align, map->l_tls_blocksize); + if (newp == NULL) + oom (); + + /* Initialize the memory. */ + memset (__mempcpy (newp, map->l_tls_initimage, map->l_tls_initimage_size), + '\0', map->l_tls_blocksize - map->l_tls_initimage_size); + + return newp; +} + + +struct link_map * +_dl_update_slotinfo (unsigned long int req_modid) +{ + struct link_map *the_map = NULL; + dtv_t *dtv = THREAD_DTV (); + + /* The global dl_tls_dtv_slotinfo array contains for each module + index the generation counter current when the entry was created. + This array never shrinks so that all module indices which were + valid at some time can be used to access it. Before the first + use of a new module index in this function the array was extended + appropriately. Access also does not have to be guarded against + modifications of the array. It is assumed that pointer-size + values can be read atomically even in SMP environments. It is + possible that other threads at the same time dynamically load + code and therefore add to the slotinfo list. This is a problem + since we must not pick up any information about incomplete work. + The solution to this is to ignore all dtv slots which were + created after the one we are currently interested. We know that + dynamic loading for this module is completed and this is the last + load operation we know finished. */ + unsigned long int idx = req_modid; + struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list); + + while (idx >= listp->len) + { + idx -= listp->len; + listp = listp->next; + } + + if (dtv[0].counter < listp->slotinfo[idx].gen) + { + /* The generation counter for the slot is higher than what the + current dtv implements. We have to update the whole dtv but + only those entries with a generation counter <= the one for + the entry we need. */ + size_t new_gen = listp->slotinfo[idx].gen; + size_t total = 0; + + /* We have to look through the entire dtv slotinfo list. */ + listp = GL(dl_tls_dtv_slotinfo_list); + do + { + for (size_t cnt = total == 0 ? 1 : 0; cnt < listp->len; ++cnt) + { + size_t gen = listp->slotinfo[cnt].gen; + + if (gen > new_gen) + /* This is a slot for a generation younger than the + one we are handling now. It might be incompletely + set up so ignore it. */ + continue; + + /* If the entry is older than the current dtv layout we + know we don't have to handle it. */ + if (gen <= dtv[0].counter) + continue; + + /* If there is no map this means the entry is empty. */ + struct link_map *map = listp->slotinfo[cnt].map; + if (map == NULL) + { + /* If this modid was used at some point the memory + might still be allocated. */ + if (! dtv[total + cnt].pointer.is_static + && dtv[total + cnt].pointer.val != TLS_DTV_UNALLOCATED) + { + free (dtv[total + cnt].pointer.val); + dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED; + } + + continue; + } + + /* Check whether the current dtv array is large enough. */ + size_t modid = map->l_tls_modid; + assert (total + cnt == modid); + if (dtv[-1].counter < modid) + { + /* Reallocate the dtv. */ + dtv_t *newp; + size_t newsize = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS; + size_t oldsize = dtv[-1].counter; + + assert (map->l_tls_modid <= newsize); + + if (dtv == GL(dl_initial_dtv)) + { + /* This is the initial dtv that was allocated + during rtld startup using the dl-minimal.c + malloc instead of the real malloc. We can't + free it, we have to abandon the old storage. */ + + newp = malloc ((2 + newsize) * sizeof (dtv_t)); + if (newp == NULL) + oom (); + memcpy (newp, &dtv[-1], oldsize * sizeof (dtv_t)); + } + else + { + newp = realloc (&dtv[-1], + (2 + newsize) * sizeof (dtv_t)); + if (newp == NULL) + oom (); + } + + newp[0].counter = newsize; + + /* Clear the newly allocated part. */ + memset (newp + 2 + oldsize, '\0', + (newsize - oldsize) * sizeof (dtv_t)); + + /* Point dtv to the generation counter. */ + dtv = &newp[1]; + + /* Install this new dtv in the thread data + structures. */ + INSTALL_NEW_DTV (dtv); + } + + /* If there is currently memory allocate for this + dtv entry free it. */ + /* XXX Ideally we will at some point create a memory + pool. */ + if (! dtv[modid].pointer.is_static + && dtv[modid].pointer.val != TLS_DTV_UNALLOCATED) + /* Note that free is called for NULL is well. We + deallocate even if it is this dtv entry we are + supposed to load. The reason is that we call + memalign and not malloc. */ + free (dtv[modid].pointer.val); + + /* This module is loaded dynamically- We defer memory + allocation. */ + dtv[modid].pointer.is_static = false; + dtv[modid].pointer.val = TLS_DTV_UNALLOCATED; + + if (modid == req_modid) + the_map = map; + } + + total += listp->len; + } + while ((listp = listp->next) != NULL); + + /* This will be the new maximum generation counter. */ + dtv[0].counter = new_gen; + } + + return the_map; +} + + +/* The generic dynamic and local dynamic model cannot be used in + statically linked applications. */ +void * +__tls_get_addr (GET_ADDR_ARGS) +{ + dtv_t *dtv = THREAD_DTV (); + struct link_map *the_map = NULL; + void *p; + + if (__builtin_expect (dtv[0].counter != GL(dl_tls_generation), 0)) + the_map = _dl_update_slotinfo (GET_ADDR_MODULE); + + p = dtv[GET_ADDR_MODULE].pointer.val; + + if (__builtin_expect (p == TLS_DTV_UNALLOCATED, 0)) + { + /* The allocation was deferred. Do it now. */ + if (the_map == NULL) + { + /* Find the link map for this module. */ + size_t idx = GET_ADDR_MODULE; + struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list); + + while (idx >= listp->len) + { + idx -= listp->len; + listp = listp->next; + } + + the_map = listp->slotinfo[idx].map; + } + + p = dtv[GET_ADDR_MODULE].pointer.val = allocate_and_init (the_map); + dtv[GET_ADDR_MODULE].pointer.is_static = false; + } + + return (char *) p + GET_ADDR_OFFSET; +} +# endif + + +/* Look up the module's TLS block as for __tls_get_addr, + but never touch anything. Return null if it's not allocated yet. */ +void * +internal_function +_dl_tls_get_addr_soft (struct link_map *l) +{ + if (__builtin_expect (l->l_tls_modid == 0, 0)) + /* This module has no TLS segment. */ + return NULL; + + dtv_t *dtv = THREAD_DTV (); + if (__builtin_expect (dtv[0].counter != GL(dl_tls_generation), 0)) + { + /* This thread's DTV is not completely current, + but it might already cover this module. */ + + if (l->l_tls_modid >= dtv[-1].counter) + /* Nope. */ + return NULL; + + size_t idx = l->l_tls_modid; + struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list); + while (idx >= listp->len) + { + idx -= listp->len; + listp = listp->next; + } + + /* We've reached the slot for this module. + If its generation counter is higher than the DTV's, + this thread does not know about this module yet. */ + if (dtv[0].counter < listp->slotinfo[idx].gen) + return NULL; + } + + void *data = dtv[l->l_tls_modid].pointer.val; + if (__builtin_expect (data == TLS_DTV_UNALLOCATED, 0)) + /* The DTV is current, but this thread has not yet needed + to allocate this module's segment. */ + data = NULL; + + return data; +} + + +void +_dl_add_to_slotinfo (struct link_map *l) +{ + /* Now that we know the object is loaded successfully add + modules containing TLS data to the dtv info table. We + might have to increase its size. */ + struct dtv_slotinfo_list *listp; + struct dtv_slotinfo_list *prevp; + size_t idx = l->l_tls_modid; + + /* Find the place in the dtv slotinfo list. */ + listp = GL(dl_tls_dtv_slotinfo_list); + prevp = NULL; /* Needed to shut up gcc. */ + do + { + /* Does it fit in the array of this list element? */ + if (idx < listp->len) + break; + idx -= listp->len; + prevp = listp; + listp = listp->next; + } + while (listp != NULL); + + if (listp == NULL) + { + /* When we come here it means we have to add a new element + to the slotinfo list. And the new module must be in + the first slot. */ + assert (idx == 0); + + listp = prevp->next = (struct dtv_slotinfo_list *) + malloc (sizeof (struct dtv_slotinfo_list) + + TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo)); + if (listp == NULL) + { + /* We ran out of memory. We will simply fail this + call but don't undo anything we did so far. The + application will crash or be terminated anyway very + soon. */ + + /* We have to do this since some entries in the dtv + slotinfo array might already point to this + generation. */ + ++GL(dl_tls_generation); + + _dl_signal_error (ENOMEM, "dlopen", NULL, N_("\ +cannot create TLS data structures")); + } + + listp->len = TLS_SLOTINFO_SURPLUS; + listp->next = NULL; + memset (listp->slotinfo, '\0', + TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo)); + } + + /* Add the information into the slotinfo data structure. */ + listp->slotinfo[idx].map = l; + listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1; +} +#endif /* use TLS */ diff --git a/elf/dl-trampoline.c b/elf/dl-trampoline.c new file mode 100644 index 0000000..3ca89f3 --- /dev/null +++ b/elf/dl-trampoline.c @@ -0,0 +1 @@ +#error "Architecture specific PLT trampolines must be defined." diff --git a/elf/dl-tsd.c b/elf/dl-tsd.c index f44fa7d..f01f8d6 100644 --- a/elf/dl-tsd.c +++ b/elf/dl-tsd.c @@ -1,5 +1,5 @@ /* Thread-local data used by error handling for runtime dynamic linker. - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2005 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 @@ -49,7 +49,7 @@ void **(*_dl_error_catch_tsd) (void) __attribute__ ((const)) void ** __attribute__ ((const)) __libc_dl_error_tsd (void) { - static __thread void *data; + static __thread void *data attribute_tls_model_ie; return &data; } diff --git a/elf/dl-version.c b/elf/dl-version.c index d5fe2f4..9e88116 100644 --- a/elf/dl-version.c +++ b/elf/dl-version.c @@ -1,5 +1,5 @@ /* Handle symbol and library versioning. - Copyright (C) 1997-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1997-2002, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -77,7 +77,7 @@ find_needed (const char *name, struct link_map *map) static int internal_function -match_symbol (const char *name, ElfW(Word) hash, const char *string, +match_symbol (const char *name, Lmid_t ns, ElfW(Word) hash, const char *string, struct link_map *map, int verbose, int weak) { const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); @@ -90,9 +90,9 @@ match_symbol (const char *name, ElfW(Word) hash, const char *string, /* Display information about what we are doing while debugging. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_VERSIONS, 0)) _dl_debug_printf ("\ -checking for version `%s' in file %s required by file %s\n", +checking for version `%s' in file %s [%lu] required by file %s [%lu]\n", string, map->l_name[0] ? map->l_name : rtld_progname, - name); + map->l_ns, name, ns); if (__builtin_expect (map->l_info[VERSYMIDX (DT_VERDEF)] == NULL, 0)) { @@ -242,7 +242,7 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) /* Match the symbol. */ result |= match_symbol ((*map->l_name ? map->l_name : rtld_progname), - aux->vna_hash, + map->l_ns, aux->vna_hash, strtab + aux->vna_name, needed->l_real, verbose, aux->vna_flags & VER_FLG_WEAK); diff --git a/elf/do-lookup.h b/elf/do-lookup.h index e57d9df..ab9a510 100644 --- a/elf/do-lookup.h +++ b/elf/do-lookup.h @@ -1,5 +1,5 @@ /* Look up a symbol in the loaded objects. - Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1995-2004, 2005, 2006, 2007 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 @@ -17,150 +17,208 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ + /* Inner part of the lookup functions. We return a value > 0 if we found the symbol, the value 0 if nothing is found and < 0 if something bad happened. */ static int __attribute_noinline__ -do_lookup_x (const char *undef_name, unsigned long int hash, - const ElfW(Sym) *ref, struct sym_val *result, - struct r_scope_elem *scope, size_t i, +do_lookup_x (const char *undef_name, uint_fast32_t new_hash, + unsigned long int *old_hash, const ElfW(Sym) *ref, + struct sym_val *result, struct r_scope_elem *scope, size_t i, const struct r_found_version *const version, int flags, struct link_map *skip, int type_class) { - struct link_map **list = scope->r_list; size_t n = scope->r_nlist; - struct link_map *map; + /* Make sure we read the value before proceeding. Otherwise we + might use r_list pointing to the initial scope and r_nlist being + the value after a resize. That is the only path in dl-open.c not + protected by GSCOPE. A read barrier here might be to expensive. */ + __asm volatile ("" : "+r" (n), "+m" (scope->r_list)); + struct link_map **list = scope->r_list; do { - const ElfW(Sym) *symtab; - const char *strtab; - const ElfW(Half) *verstab; + /* These variables are used in the nested function. */ Elf_Symndx symidx; - const ElfW(Sym) *sym; int num_versions = 0; const ElfW(Sym) *versioned_sym = NULL; - map = list[i]->l_real; + const struct link_map *map = list[i]->l_real; /* Here come the extra test needed for `_dl_lookup_symbol_skip'. */ - if (skip != NULL && map == skip) + if (map == skip) continue; /* Don't search the executable when resolving a copy reloc. */ if ((type_class & ELF_RTYPE_CLASS_COPY) && map->l_type == lt_executable) continue; + /* Do not look into objects which are going to be removed. */ + if (map->l_removed) + continue; + /* Print some debugging info if wanted. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS, 0)) - _dl_debug_printf ("symbol=%s; lookup in file=%s\n", + _dl_debug_printf ("symbol=%s; lookup in file=%s [%lu]\n", undef_name, - map->l_name[0] ? map->l_name : rtld_progname); + map->l_name[0] ? map->l_name : rtld_progname, + map->l_ns); - symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]); - strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); - verstab = map->l_versyms; + /* If the hash table is empty there is nothing to do here. */ + if (map->l_nbuckets == 0) + continue; - /* Search the appropriate hash bucket in this object's symbol table - for a definition for the same symbol name. */ - for (symidx = map->l_buckets[hash % map->l_nbuckets]; - symidx != STN_UNDEF; - symidx = map->l_chain[symidx]) - { - sym = &symtab[symidx]; - - assert (ELF_RTYPE_CLASS_PLT == 1); - if ((sym->st_value == 0 /* No value. */ -#ifdef USE_TLS - && ELFW(ST_TYPE) (sym->st_info) != STT_TLS -#endif - ) - || (type_class & (sym->st_shndx == SHN_UNDEF))) - continue; - - if (ELFW(ST_TYPE) (sym->st_info) > STT_FUNC -#ifdef USE_TLS - && ELFW(ST_TYPE) (sym->st_info) != STT_TLS -#endif - ) - /* Ignore all but STT_NOTYPE, STT_OBJECT and STT_FUNC - entries (and STT_TLS if TLS is supported) since these - are no code/data definitions. */ - continue; - - if (sym != ref && strcmp (strtab + sym->st_name, undef_name)) - /* Not the symbol we are looking for. */ - continue; - - if (version != NULL) - { - if (__builtin_expect (verstab == NULL, 0)) - { - /* We need a versioned symbol but haven't found any. If - this is the object which is referenced in the verneed - entry it is a bug in the library since a symbol must - not simply disappear. - - It would also be a bug in the object since it means that - the list of required versions is incomplete and so the - tests in dl-version.c haven't found a problem.*/ - assert (version->filename == NULL - || ! _dl_name_match_p (version->filename, map)); - - /* Otherwise we accept the symbol. */ - } - else - { - /* We can match the version information or use the - default one if it is not hidden. */ - ElfW(Half) ndx = verstab[symidx] & 0x7fff; - if ((map->l_versions[ndx].hash != version->hash - || strcmp (map->l_versions[ndx].name, version->name)) - && (version->hidden || map->l_versions[ndx].hash - || (verstab[symidx] & 0x8000))) - /* It's not the version we want. */ - continue; - } - } - else - { - /* No specific version is selected. There are two ways we - can got here: + /* The tables for this map. */ + const ElfW(Sym) *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]); + const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + + + /* Nested routine to check whether the symbol matches. */ + const ElfW(Sym) * + __attribute_noinline__ + check_match (const ElfW(Sym) *sym) + { + assert (ELF_RTYPE_CLASS_PLT == 1); + if (__builtin_expect ((sym->st_value == 0 /* No value. */ + && ELFW(ST_TYPE) (sym->st_info) != STT_TLS) + || (type_class & (sym->st_shndx == SHN_UNDEF)), + 0)) + return NULL; + + if (__builtin_expect (ELFW(ST_TYPE) (sym->st_info) > STT_FUNC + && ELFW(ST_TYPE) (sym->st_info) != STT_TLS, 0)) + /* Ignore all but STT_NOTYPE, STT_OBJECT and STT_FUNC + entries (and STT_TLS if TLS is supported) since these + are no code/data definitions. */ + return NULL; + + if (sym != ref && strcmp (strtab + sym->st_name, undef_name)) + /* Not the symbol we are looking for. */ + return NULL; - - a binary which does not include versioning information - is loaded + const ElfW(Half) *verstab = map->l_versyms; + if (version != NULL) + { + if (__builtin_expect (verstab == NULL, 0)) + { + /* We need a versioned symbol but haven't found any. If + this is the object which is referenced in the verneed + entry it is a bug in the library since a symbol must + not simply disappear. - - dlsym() instead of dlvsym() is used to get a symbol which - might exist in more than one form + It would also be a bug in the object since it means that + the list of required versions is incomplete and so the + tests in dl-version.c haven't found a problem.*/ + assert (version->filename == NULL + || ! _dl_name_match_p (version->filename, map)); - If the library does not provide symbol version - information there is no problem at at: we simply use the - symbol if it is defined. + /* Otherwise we accept the symbol. */ + } + else + { + /* We can match the version information or use the + default one if it is not hidden. */ + ElfW(Half) ndx = verstab[symidx] & 0x7fff; + if ((map->l_versions[ndx].hash != version->hash + || strcmp (map->l_versions[ndx].name, version->name)) + && (version->hidden || map->l_versions[ndx].hash + || (verstab[symidx] & 0x8000))) + /* It's not the version we want. */ + return NULL; + } + } + else + { + /* No specific version is selected. There are two ways we + can got here: - These two lookups need to be handled differently if the - library defines versions. In the case of the old - unversioned application the oldest (default) version - should be used. In case of a dlsym() call the latest and - public interface should be returned. */ - if (verstab != NULL) + - a binary which does not include versioning information + is loaded + + - dlsym() instead of dlvsym() is used to get a symbol which + might exist in more than one form + + If the library does not provide symbol version information + there is no problem at at: we simply use the symbol if it + is defined. + + These two lookups need to be handled differently if the + library defines versions. In the case of the old + unversioned application the oldest (default) version + should be used. In case of a dlsym() call the latest and + public interface should be returned. */ + if (verstab != NULL) + { + if ((verstab[symidx] & 0x7fff) + >= ((flags & DL_LOOKUP_RETURN_NEWEST) ? 2 : 3)) + { + /* Don't accept hidden symbols. */ + if ((verstab[symidx] & 0x8000) == 0 + && num_versions++ == 0) + /* No version so far. */ + versioned_sym = sym; + + return NULL; + } + } + } + + /* There cannot be another entry for this symbol so stop here. */ + return sym; + } + + const ElfW(Sym) *sym; + const ElfW(Addr) *bitmask = map->l_gnu_bitmask; + if (__builtin_expect (bitmask != NULL, 1)) + { + ElfW(Addr) bitmask_word + = bitmask[(new_hash / __ELF_NATIVE_CLASS) + & map->l_gnu_bitmask_idxbits]; + + unsigned int hashbit1 = new_hash & (__ELF_NATIVE_CLASS - 1); + unsigned int hashbit2 = ((new_hash >> map->l_gnu_shift) + & (__ELF_NATIVE_CLASS - 1)); + + if (__builtin_expect ((bitmask_word >> hashbit1) + & (bitmask_word >> hashbit2) & 1, 0)) + { + Elf32_Word bucket = map->l_gnu_buckets[new_hash + % map->l_nbuckets]; + if (bucket != 0) { - if ((verstab[symidx] & 0x7fff) - >= ((flags & DL_LOOKUP_RETURN_NEWEST) ? 2 : 3)) - { - /* Don't accept hidden symbols. */ - if ((verstab[symidx] & 0x8000) == 0 - && num_versions++ == 0) - /* No version so far. */ - versioned_sym = sym; + const Elf32_Word *hasharr = &map->l_gnu_chain_zero[bucket]; - continue; - } + do + if ((*hasharr & ~1u) == (new_hash & ~1u)) + { + symidx = hasharr - map->l_gnu_chain_zero; + sym = check_match (&symtab[symidx]); + if (sym != NULL) + goto found_it; + } + while ((*hasharr++ & 1u) == 0); } } + /* No symbol found. */ + symidx = SHN_UNDEF; + } + else + { + if (*old_hash == 0xffffffff) + *old_hash = _dl_elf_hash (undef_name); - /* There cannot be another entry for this symbol so stop here. */ - goto found_it; + /* Use the old SysV-style hash table. Search the appropriate + hash bucket in this object's symbol table for a definition + for the same symbol name. */ + for (symidx = map->l_buckets[*old_hash % map->l_nbuckets]; + symidx != STN_UNDEF; + symidx = map->l_chain[symidx]) + { + sym = check_match (&symtab[symidx]); + if (sym != NULL) + goto found_it; + } } /* If we have seen exactly one versioned symbol while we are @@ -181,7 +239,7 @@ do_lookup_x (const char *undef_name, unsigned long int hash, if (! result->s) { result->s = sym; - result->m = map; + result->m = (struct link_map *) map; } break; } @@ -189,7 +247,7 @@ do_lookup_x (const char *undef_name, unsigned long int hash, case STB_GLOBAL: /* Global definition. Just what we need. */ result->s = sym; - result->m = map; + result->m = (struct link_map *) map; return 1; default: /* Local symbols are ignored. */ @@ -208,7 +266,3 @@ do_lookup_x (const char *undef_name, unsigned long int hash, /* We have not found anything until now. */ return 0; } - -#undef FCT -#undef ARG -#undef VERSIONED diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h index f9559dc..7eb9a36 100644 --- a/elf/dynamic-link.h +++ b/elf/dynamic-link.h @@ -1,5 +1,5 @@ /* Inline functions for dynamic linking. - Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1995-2005, 2006 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 @@ -20,7 +20,7 @@ #include <elf.h> #include <assert.h> -#ifdef RESOLVE +#ifdef RESOLVE_MAP /* We pass reloc_addr as a pointer to void, as opposed to a pointer to ElfW(Addr), because not all architectures can assume that the relocated address is properly aligned, whereas the compiler is @@ -31,26 +31,30 @@ optimizing away alignment tests or using word instructions for copying memory, breaking the very code written to handle the unaligned cases. */ -auto void __attribute__((always_inline)) +# if ! ELF_MACHINE_NO_REL +auto inline void __attribute__((always_inline)) elf_machine_rel (struct link_map *map, const ElfW(Rel) *reloc, const ElfW(Sym) *sym, const struct r_found_version *version, void *const reloc_addr); -auto void __attribute__((always_inline)) +auto inline void __attribute__((always_inline)) +elf_machine_rel_relative (ElfW(Addr) l_addr, const ElfW(Rel) *reloc, + void *const reloc_addr); +# endif +# if ! ELF_MACHINE_NO_RELA +auto inline void __attribute__((always_inline)) elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc, const ElfW(Sym) *sym, const struct r_found_version *version, void *const reloc_addr); -auto void __attribute__((always_inline)) -elf_machine_rel_relative (ElfW(Addr) l_addr, const ElfW(Rel) *reloc, - void *const reloc_addr); -auto void __attribute__((always_inline)) +auto inline void __attribute__((always_inline)) elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc, void *const reloc_addr); +# endif # if ELF_MACHINE_NO_RELA || defined ELF_MACHINE_PLT_REL -auto void __attribute__((always_inline)) +auto inline void __attribute__((always_inline)) elf_machine_lazy_rel (struct link_map *map, ElfW(Addr) l_addr, const ElfW(Rel) *reloc); # else -auto void __attribute__((always_inline)) +auto inline void __attribute__((always_inline)) elf_machine_lazy_rel (struct link_map *map, ElfW(Addr) l_addr, const ElfW(Rela) *reloc); # endif @@ -64,7 +68,7 @@ elf_machine_lazy_rel (struct link_map *map, /* Read the dynamic section at DYN and fill in INFO with indices DT_*. */ -#ifndef RESOLVE +#ifndef RESOLVE_MAP static #else auto @@ -139,6 +143,8 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp) # endif ADJUST_DYN_INFO (DT_JMPREL); ADJUST_DYN_INFO (VERSYMIDX (DT_VERSYM)); + ADJUST_DYN_INFO (DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM + + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM); # undef ADJUST_DYN_INFO assert (cnt <= DL_RO_DYN_TEMP_CNT); } @@ -199,7 +205,7 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp) #endif } -#ifdef RESOLVE +#ifdef RESOLVE_MAP # ifdef RTLD_BOOTSTRAP # define ELF_DURING_STARTUP (1) @@ -1,5 +1,5 @@ /* This file defines standard ELF types, structures, and macros. - Copyright (C) 1995-2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1995-2003,2004,2005,2006 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 @@ -329,7 +329,8 @@ typedef struct #define SHT_GROUP 17 /* Section group */ #define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ #define SHT_NUM 19 /* Number of defined types. */ -#define SHT_LOOS 0x60000000 /* Start OS-specific */ +#define SHT_LOOS 0x60000000 /* Start OS-specific. */ +#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ #define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ #define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ #define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ @@ -699,6 +700,9 @@ typedef struct If any adjustment is made to the ELF object after it has been built these entries will need to be adjusted. */ #define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ +#define DT_TLSDESC_PLT 0x6ffffef6 +#define DT_TLSDESC_GOT 0x6ffffef7 #define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ #define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ #define DT_CONFIG 0x6ffffefa /* Configuration information. */ @@ -709,7 +713,7 @@ typedef struct #define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ #define DT_ADDRRNGHI 0x6ffffeff #define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ -#define DT_ADDRNUM 10 +#define DT_ADDRNUM 11 /* The versioning entry types. The next are defined as part of the GNU extension. */ @@ -899,23 +903,25 @@ typedef struct typedef struct { - int a_type; /* Entry type */ + uint32_t a_type; /* Entry type */ union { - long int a_val; /* Integer value */ - void *a_ptr; /* Pointer value */ - void (*a_fcn) (void); /* Function pointer value */ + uint32_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ } a_un; } Elf32_auxv_t; typedef struct { - long int a_type; /* Entry type */ + uint64_t a_type; /* Entry type */ union { - long int a_val; /* Integer value */ - void *a_ptr; /* Pointer value */ - void (*a_fcn) (void); /* Function pointer value */ + uint64_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ } a_un; } Elf64_auxv_t; @@ -1248,14 +1254,15 @@ typedef struct #define DT_SPARC_REGISTER 0x70000001 #define DT_SPARC_NUM 2 -/* Bits present in AT_HWCAP, primarily for Sparc32. */ +/* Bits present in AT_HWCAP on SPARC. */ -#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ +#define HWCAP_SPARC_FLUSH 1 /* The CPU supports flush insn. */ #define HWCAP_SPARC_STBAR 2 #define HWCAP_SPARC_SWAP 4 #define HWCAP_SPARC_MULDIV 8 -#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ +#define HWCAP_SPARC_V9 16 /* The CPU is v9, so v8plus is ok. */ #define HWCAP_SPARC_ULTRA3 32 +#define HWCAP_SPARC_BLKINIT 64 /* Sun4v with block-init/load-twin. */ /* MIPS R3000 specific definitions. */ @@ -1491,8 +1498,21 @@ typedef struct #define R_MIPS_PJUMP 35 #define R_MIPS_RELGOT 36 #define R_MIPS_JALR 37 +#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ +#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ +#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ +#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ +#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ +#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ +#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ +#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ +#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ +#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ +#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ +#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ +#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ /* Keep this the last entry. */ -#define R_MIPS_NUM 38 +#define R_MIPS_NUM 51 /* Legal values for p_type field of Elf32_Phdr. */ @@ -1851,6 +1871,9 @@ typedef Elf32_Addr Elf32_Conflict; #define LITUSE_ALPHA_TLS_GD 4 #define LITUSE_ALPHA_TLS_LDM 5 +/* Legal values for d_tag of Elf64_Dyn. */ +#define DT_ALPHA_PLTRO (DT_LOPROC + 0) +#define DT_ALPHA_NUM 1 /* PowerPC specific declarations */ @@ -1961,10 +1984,19 @@ typedef Elf32_Addr Elf32_Conflict; #define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ #define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ +/* GNU relocs used in PIC code sequences. */ +#define R_PPC_REL16 249 /* word32 (sym-.) */ +#define R_PPC_REL16_LO 250 /* half16 (sym-.)@l */ +#define R_PPC_REL16_HI 251 /* half16 (sym-.)@h */ +#define R_PPC_REL16_HA 252 /* half16 (sym-.)@ha */ + /* This is a phony reloc to handle any old fashioned TOC16 references that may still be in object files. */ #define R_PPC_TOC16 255 +/* PowerPC specific values for the Dyn d_tag field. */ +#define DT_PPC_GOT (DT_LOPROC + 0) +#define DT_PPC_NUM 1 /* PowerPC64 relocations defined by the ABIs */ #define R_PPC64_NONE R_PPC_NONE @@ -2125,7 +2157,11 @@ typedef Elf32_Addr Elf32_Conflict; #define PF_ARM_SB 0x10000000 /* Segment contains the location addressed by the static base */ +/* Processor specific values for the Phdr p_type field. */ +#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ + /* ARM relocs. */ + #define R_ARM_NONE 0 /* No reloc */ #define R_ARM_PC24 1 /* PC relative 26 bit branch */ #define R_ARM_ABS32 2 /* Direct 32 bit */ @@ -2143,6 +2179,9 @@ typedef Elf32_Addr Elf32_Conflict; #define R_ARM_THM_SWI8 14 #define R_ARM_XPC25 15 #define R_ARM_THM_XPC22 16 +#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ +#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ +#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ #define R_ARM_COPY 20 /* Copy symbol at runtime */ #define R_ARM_GLOB_DAT 21 /* Create GOT entry */ #define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ @@ -2161,6 +2200,16 @@ typedef Elf32_Addr Elf32_Conflict; #define R_ARM_GNU_VTINHERIT 101 #define R_ARM_THM_PC11 102 /* thumb unconditional branch */ #define R_ARM_THM_PC9 103 /* thumb conditional branch */ +#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic + thread local data */ +#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic + thread local data */ +#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS + block */ +#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of + static TLS block offset */ +#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static + TLS block */ #define R_ARM_RXPC25 249 #define R_ARM_RSBREL32 250 #define R_ARM_THM_RPC22 251 @@ -2525,6 +2574,7 @@ typedef Elf32_Addr Elf32_Conflict; #define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ #define R_M32R_RELA_GNU_VTINHERIT 43 #define R_M32R_RELA_GNU_VTENTRY 44 +#define R_M32R_REL32 45 /* PC relative 32 bit. */ #define R_M32R_GOT24 48 /* 24 bit GOT entry */ #define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ diff --git a/elf/enbl-secure.c b/elf/enbl-secure.c new file mode 100644 index 0000000..fac3b9c --- /dev/null +++ b/elf/enbl-secure.c @@ -0,0 +1,37 @@ +/* Define and initialize the `__libc_enable_secure' flag. Generic version. + Copyright (C) 1996, 1997, 1998, 2000, 2003 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* This file is used in the static libc. For the shared library, + dl-sysdep.c defines and initializes __libc_enable_secure. */ + +#include <unistd.h> +#include <libc-internal.h> + +/* If nonzero __libc_enable_secure is already set. */ +int __libc_enable_secure_decided; +/* Safest assumption, if somehow the initializer isn't run. */ +int __libc_enable_secure = 1; + +void +__libc_init_secure (void) +{ + if (__libc_enable_secure_decided == 0) + __libc_enable_secure = (__geteuid () != __getuid () + || __getegid () != __getgid ()); +} diff --git a/elf/ldconfig.c b/elf/ldconfig.c index aab52b7..18bac78 100644 --- a/elf/ldconfig.c +++ b/elf/ldconfig.c @@ -1,21 +1,19 @@ -/* Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. +/* Copyright (C) 1999-2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Andreas Jaeger <aj@suse.de>, 1999. - 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. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. - The GNU C Library is distributed in the hope that it will be useful, + This program 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. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define PROCINFO_CLASS static #include <alloca.h> @@ -39,10 +37,16 @@ #include <glob.h> #include <libgen.h> -#include "ldconfig.h" -#include "dl-cache.h" +#include <ldconfig.h> +#include <dl-cache.h> -#include "dl-procinfo.h" +#include <dl-procinfo.h> + +#ifdef _DL_FIRST_PLATFORM +# define _DL_FIRST_EXTRA (_DL_FIRST_PLATFORM + _DL_PLATFORMS_COUNT) +#else +# define _DL_FIRST_EXTRA _DL_HWCAP_COUNT +#endif #ifndef LD_SO_CONF # define LD_SO_CONF SYSCONFDIR "/ld.so.conf" @@ -115,6 +119,9 @@ static const char *config_file; /* Mask to use for important hardware capabilities. */ static unsigned long int hwcap_mask = HWCAP_IMPORTANT; +/* Configuration-defined capabilities defined in kernel vDSOs. */ +static const char *hwcap_extra[64 - _DL_FIRST_EXTRA]; + /* Name and version of program. */ static void print_version (FILE *stream, struct argp_state *state); void (*argp_program_version_hook) (FILE *, struct argp_state *) @@ -165,10 +172,10 @@ is_hwcap_platform (const char *name) if (hwcap_idx != -1) return 1; -#ifdef USE_TLS - if (strcmp (name, "tls") == 0) - return 1; -#endif + for (hwcap_idx = _DL_FIRST_EXTRA; hwcap_idx < 64; ++hwcap_idx) + if (hwcap_extra[hwcap_idx - _DL_FIRST_EXTRA] != NULL + && !strcmp (name, hwcap_extra[hwcap_idx - _DL_FIRST_EXTRA])) + return 1; return 0; } @@ -203,11 +210,11 @@ path_hwcap (const char *path) h = _dl_string_platform (ptr + 1); if (h == (uint64_t) -1) { -#ifdef USE_TLS - if (strcmp (ptr + 1, "tls") == 0) - h = 63; - else -#endif + for (h = _DL_FIRST_EXTRA; h < 64; ++h) + if (hwcap_extra[h - _DL_FIRST_EXTRA] != NULL + && !strcmp (ptr + 1, hwcap_extra[h - _DL_FIRST_EXTRA])) + break; + if (h == 64) break; } } @@ -279,7 +286,7 @@ print_version (FILE *stream, struct argp_state *state) Copyright (C) %s Free Software Foundation, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ -"), "2004"); +"), "2006"); fprintf (stream, gettext ("Written by %s.\n"), "Andreas Jaeger"); } @@ -636,7 +643,7 @@ search_dir (const struct dir_entry *entry) if (opt_verbose) { if (hwcap != 0) - printf ("%s: (hwcap: 0x%" PRIx64 ")\n", entry->path, hwcap); + printf ("%s: (hwcap: %#.16" PRIx64 ")\n", entry->path, hwcap); else printf ("%s:\n", entry->path); } @@ -686,10 +693,23 @@ search_dir (const struct dir_entry *entry) #endif !is_hwcap_platform (direntry->d_name))) continue; - len = strlen (entry->path) + strlen (direntry->d_name); + len = strlen (direntry->d_name); + /* Skip temporary files created by the prelink program. Files with + names like these are never really DSOs we want to look at. */ + if (len >= sizeof (".#prelink#") - 1) + { + if (strcmp (direntry->d_name + len - sizeof (".#prelink#") + 1, + ".#prelink#") == 0) + continue; + if (len >= sizeof (".#prelink#.XXXXXX") - 1 + && memcmp (direntry->d_name + len - sizeof (".#prelink#.XXXXXX") + + 1, ".#prelink#.", sizeof (".#prelink#.") - 1) == 0) + continue; + } + len += strlen (entry->path) + 2; if (len > file_name_len) { - file_name_len = len + 1; + file_name_len = len; file_name = alloca (file_name_len); if (!opt_chroot) real_file_name = file_name; @@ -697,10 +717,10 @@ search_dir (const struct dir_entry *entry) sprintf (file_name, "%s/%s", entry->path, direntry->d_name); if (opt_chroot) { - len = strlen (dir_name) + strlen (direntry->d_name); + len = strlen (dir_name) + strlen (direntry->d_name) + 2; if (len > real_file_name_len) { - real_file_name_len = len + 1; + real_file_name_len = len; real_file_name = alloca (real_file_name_len); } sprintf (real_file_name, "%s/%s", dir_name, direntry->d_name); @@ -944,17 +964,19 @@ search_dirs (void) static void parse_conf_include (const char *config_file, unsigned int lineno, - bool do_chroot, const char *pattern); + const char *prefix, bool do_chroot, + const char *pattern); /* Parse configuration file. */ static void -parse_conf (const char *filename, bool do_chroot) +parse_conf (const char *filename, const char *prefix, bool do_chroot) { FILE *file = NULL; char *line = NULL; const char *canon; size_t len = 0; unsigned int lineno; + size_t prefix_len = prefix ? strlen (prefix) : 0; if (do_chroot && opt_chroot) { @@ -1015,7 +1037,61 @@ parse_conf (const char *filename, bool do_chroot) cp += 8; while ((dir = strsep (&cp, " \t")) != NULL) if (dir[0] != '\0') - parse_conf_include (filename, lineno, do_chroot, dir); + parse_conf_include (filename, lineno, prefix, do_chroot, dir); + } + else if (prefix != NULL) + { + size_t cp_len = strlen (cp); + char new_cp [prefix_len + cp_len + 1]; + memcpy (mempcpy (new_cp, prefix, prefix_len), cp, cp_len + 1); + add_dir (new_cp); + } + else if (!strncasecmp (cp, "hwcap", 5) && isblank (cp[5])) + { + cp += 6; + char *p, *name = NULL; + unsigned long int n = strtoul (cp, &cp, 0); + if (cp != NULL && isblank (*cp)) + while ((p = strsep (&cp, " \t")) != NULL) + if (p[0] != '\0') + { + if (name == NULL) + name = p; + else + { + name = NULL; + break; + } + } + if (name == NULL) + { + error (EXIT_FAILURE, 0, _("%s:%u: bad syntax in hwcap line"), + filename, lineno); + break; + } + if (n >= (64 - _DL_FIRST_EXTRA)) + error (EXIT_FAILURE, 0, + _("%s:%u: hwcap index %lu above maximum %u"), + filename, lineno, n, 64 - _DL_FIRST_EXTRA - 1); + if (hwcap_extra[n] == NULL) + { + for (unsigned long int h = 0; h < (64 - _DL_FIRST_EXTRA); ++h) + if (hwcap_extra[h] != NULL && !strcmp (name, hwcap_extra[h])) + error (EXIT_FAILURE, 0, + _("%s:%u: hwcap index %lu already defined as %s"), + filename, lineno, h, name); + hwcap_extra[n] = xstrdup (name); + } + else + { + if (strcmp (name, hwcap_extra[n])) + error (EXIT_FAILURE, 0, + _("%s:%u: hwcap index %lu already defined as %s"), + filename, lineno, n, hwcap_extra[n]); + if (opt_verbose) + error (0, 0, _("%s:%u: duplicate hwcap %lu %s"), + filename, lineno, n, name); + } } else add_dir (cp); @@ -1031,7 +1107,7 @@ parse_conf (const char *filename, bool do_chroot) config files to read. */ static void parse_conf_include (const char *config_file, unsigned int lineno, - bool do_chroot, const char *pattern) + const char *prefix, bool do_chroot, const char *pattern) { if (opt_chroot && pattern[0] != '/') error (EXIT_FAILURE, 0, @@ -1061,7 +1137,7 @@ parse_conf_include (const char *config_file, unsigned int lineno, { case 0: for (size_t i = 0; i < gl.gl_pathc; ++i) - parse_conf (gl.gl_pathv[i], false); + parse_conf (gl.gl_pathv[i], prefix, false); globfree64 (&gl); break; @@ -1101,6 +1177,8 @@ main (int argc, char **argv) { int remaining; + arch_startup (argc, argv); + /* Parse and process arguments. */ argp_parse (&argp, argc, argv, 0, &remaining, NULL); @@ -1118,12 +1196,16 @@ main (int argc, char **argv) add_dir (argv[i]); } +#ifdef USE_TLS + hwcap_extra[63 - _DL_FIRST_EXTRA] = "tls"; +#endif + set_hwcap (); if (opt_chroot) { /* Normalize the path a bit, we might need it for printing later. */ - char *endp = strchr (opt_chroot, '\0'); + char *endp = rawmemchr (opt_chroot, '\0'); while (endp > opt_chroot && endp[-1] == '/') --endp; *endp = '\0'; @@ -1209,12 +1291,14 @@ main (int argc, char **argv) if (!opt_only_cline) { - parse_conf (config_file, true); + parse_conf (config_file, NULL, true); /* Always add the standard search paths. */ add_system_dir (SLIBDIR); if (strcmp (SLIBDIR, LIBDIR)) add_system_dir (LIBDIR); + + add_arch_dirs (config_file); } search_dirs (); diff --git a/elf/ldd.bash.in b/elf/ldd.bash.in index 4d7c33c..d1591a5 100644 --- a/elf/ldd.bash.in +++ b/elf/ldd.bash.in @@ -1,5 +1,5 @@ #! @BASH@ -# Copyright (C) 1996-2001, 2002, 2003, 2004 Free Software Foundation, Inc. +# Copyright (C) 1996-2004, 2005, 2006 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 @@ -39,7 +39,7 @@ while test $# -gt 0; do printf $"Copyright (C) %s Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -" "2004" +" "2006" printf $"Written by %s and %s. " "Roland McGrath" "Ulrich Drepper" exit 0 @@ -144,13 +144,17 @@ for file do *) file=./$file ;; esac - if test ! -f "$file"; then + if test ! -e "$file"; then echo "ldd: ${file}:" $"No such file or directory" >&2 result=1 + elif test ! -f "$file"; then + echo "ldd: ${file}:" $"not regular file" >&2 + result=1 elif test -r "$file"; then test -x "$file" || echo 'ldd:' $"\ warning: you do not have execution permission for" "\`$file'" >&2 RTLD= + ret=1 for rtld in ${RTLDLIST}; do if test -x $rtld; then verify_out=`${rtld} --verify "$file"` @@ -160,12 +164,6 @@ warning: you do not have execution permission for" "\`$file'" >&2 esac fi done - if test -z "${RTLD}"; then - set ${RTLDLIST} - RTLD=$1 - verify_out=`${RTLD} --verify "$file"` - ret=$? - fi case $ret in 0) # If the program exits with exit code 5, it means the process has been @@ -1,6 +1,6 @@ /* Data structure for communication from the run-time dynamic linker for loaded ELF shared objects. - Copyright (C) 1995-1999, 2000, 2001, 2004 Free Software Foundation, Inc. + Copyright (C) 1995-2001, 2004, 2005, 2006 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 @@ -33,6 +33,7 @@ #define _ElfW_1(e,w,t) e##w##t #include <bits/elfclass.h> /* Defines __ELF_NATIVE_CLASS. */ +#include <bits/link.h> /* Rendezvous structure used by the run-time dynamic linker to communicate details of shared object loading to the debugger. If the executable's @@ -94,6 +95,46 @@ struct link_map #ifdef __USE_GNU +/* Version numbers for la_version handshake interface. */ +#define LAV_CURRENT 1 + +/* Activity types signaled through la_activity. */ +enum + { + LA_ACT_CONSISTENT, /* Link map consistent again. */ + LA_ACT_ADD, /* New object will be added. */ + LA_ACT_DELETE /* Objects will be removed. */ + }; + +/* Values representing origin of name for dynamic loading. */ +enum + { + LA_SER_ORIG = 0x01, /* Original name. */ + LA_SER_LIBPATH = 0x02, /* Directory from LD_LIBRARY_PATH. */ + LA_SER_RUNPATH = 0x04, /* Directory from RPATH/RUNPATH. */ + LA_SER_CONFIG = 0x08, /* Found through ldconfig. */ + LA_SER_DEFAULT = 0x40, /* Default directory. */ + LA_SER_SECURE = 0x80 /* Unused. */ + }; + +/* Values for la_objopen return value. */ +enum + { + LA_FLG_BINDTO = 0x01, /* Audit symbols bound to this object. */ + LA_FLG_BINDFROM = 0x02 /* Audit symbols bound from this object. */ + }; + +/* Values for la_symbind flags parameter. */ +enum + { + LA_SYMB_NOPLTENTER = 0x01, /* la_pltenter will not be called. */ + LA_SYMB_NOPLTEXIT = 0x02, /* la_pltexit will not be called. */ + LA_SYMB_STRUCTCALL = 0x04, /* Return value is a structure. */ + LA_SYMB_DLSYM = 0x08, /* Binding due to dlsym call. */ + LA_SYMB_ALTVALUE = 0x10 /* Value has been changed by a previous + la_symbind call. */ + }; + struct dl_phdr_info { ElfW(Addr) dlpi_addr; @@ -101,22 +142,50 @@ struct dl_phdr_info const ElfW(Phdr) *dlpi_phdr; ElfW(Half) dlpi_phnum; - /* Note: the next two members were introduced after the first + /* Note: Following members were introduced after the first version of this structure was available. Check the SIZE - argument passed to the dl_iterate_phdr() callback to determine - whether or not they are provided. */ + argument passed to the dl_iterate_phdr callback to determine + whether or not each later member is available. */ /* Incremented when a new object may have been added. */ unsigned long long int dlpi_adds; /* Incremented when an object may have been removed. */ unsigned long long int dlpi_subs; + + /* If there is a PT_TLS segment, its module ID as used in + TLS relocations, else zero. */ + size_t dlpi_tls_modid; + + /* The address of the calling thread's instance of this module's + PT_TLS segment, if it has one and it has been allocated + in the calling thread, otherwise a null pointer. */ + void *dlpi_tls_data; }; __BEGIN_DECLS -extern int dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, - size_t size, void *data), - void *data); +extern int dl_iterate_phdr (int (*__callback) (struct dl_phdr_info *, + size_t, void *), + void *__data); + + +/* Prototypes for the ld.so auditing interfaces. These are not + defined anywhere in ld.so but instead have to be provided by the + auditing DSO. */ +extern unsigned int la_version (unsigned int __version); +extern void la_activity (uintptr_t *__cookie, unsigned int __flag); +extern char *la_objsearch (const char *__name, uintptr_t *__cookie, + unsigned int __flag); +extern unsigned int la_objopen (struct link_map *__map, Lmid_t __lmid, + uintptr_t *__cookie); +extern void la_preinit (uintptr_t *__cookie); +extern uintptr_t la_symbind32 (Elf32_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, uintptr_t *__defcook, + unsigned int *__flags, const char *__symname); +extern uintptr_t la_symbind64 (Elf64_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, uintptr_t *__defcook, + unsigned int *__flags, const char *__symname); +extern unsigned int la_objclose (uintptr_t *__cookie); __END_DECLS diff --git a/elf/loadtest.c b/elf/loadtest.c index 6b8f4bb..727469b 100644 --- a/elf/loadtest.c +++ b/elf/loadtest.c @@ -70,11 +70,13 @@ static const struct #include <include/link.h> +#define MAPS ((struct link_map *) _r_debug.r_map) + #define OUT \ - for (map = _r_debug.r_map; map != NULL; map = map->l_next) \ + for (map = MAPS; map != NULL; map = map->l_next) \ if (map->l_type == lt_loaded) \ - printf ("name = \"%s\", opencount = %d\n", \ - map->l_name, (int) map->l_opencount); \ + printf ("name = \"%s\", direct_opencount = %d\n", \ + map->l_name, (int) map->l_direct_opencount); \ fflush (stdout) @@ -147,7 +149,7 @@ main (int argc, char *argv[]) { /* In this case none of the objects above should be present. */ - for (map = _r_debug.r_map; map != NULL; map = map->l_next) + for (map = MAPS; map != NULL; map = map->l_next) if (map->l_type == lt_loaded && (strstr (map->l_name, testobjs[0].name) != NULL || strstr (map->l_name, testobjs[1].name) != NULL @@ -180,11 +182,11 @@ main (int argc, char *argv[]) } /* Check whether all files are unloaded. */ - for (map = _r_debug.r_map; map != NULL; map = map->l_next) + for (map = MAPS; map != NULL; map = map->l_next) if (map->l_type == lt_loaded) { - printf ("name = \"%s\", opencount = %d\n", - map->l_name, (int) map->l_opencount); + printf ("name = \"%s\", direct_opencount = %d\n", + map->l_name, (int) map->l_direct_opencount); result = 1; } diff --git a/elf/neededtest.c b/elf/neededtest.c index e6e99bf..3cea499 100644 --- a/elf/neededtest.c +++ b/elf/neededtest.c @@ -5,6 +5,8 @@ #include <stdlib.h> #include <string.h> +#define MAPS ((struct link_map *) _r_debug.r_map) + static int check_loaded_objects (const char **loaded) { @@ -24,10 +26,10 @@ check_loaded_objects (const char **loaded) printf(" Name\n"); printf(" --------------------------------------------------------\n"); - for (lm = _r_debug.r_map; lm; lm = lm->l_next) + for (lm = MAPS; lm; lm = lm->l_next) { if (lm->l_name && lm->l_name[0]) - printf(" %s, count = %d\n", lm->l_name, (int) lm->l_opencount); + printf(" %s, count = %d\n", lm->l_name, (int) lm->l_direct_opencount); if (lm->l_type == lt_loaded && lm->l_name) { int match = 0; diff --git a/elf/neededtest2.c b/elf/neededtest2.c index cf111bc..17c75f2 100644 --- a/elf/neededtest2.c +++ b/elf/neededtest2.c @@ -5,6 +5,8 @@ #include <stdlib.h> #include <string.h> +#define MAPS ((struct link_map *) _r_debug.r_map) + static int check_loaded_objects (const char **loaded) { @@ -24,10 +26,10 @@ check_loaded_objects (const char **loaded) printf(" Name\n"); printf(" --------------------------------------------------------\n"); - for (lm = _r_debug.r_map; lm; lm = lm->l_next) + for (lm = MAPS; lm; lm = lm->l_next) { if (lm->l_name && lm->l_name[0]) - printf(" %s, count = %d\n", lm->l_name, (int) lm->l_opencount); + printf(" %s, count = %d\n", lm->l_name, (int) lm->l_direct_opencount); if (lm->l_type == lt_loaded && lm->l_name) { int match = 0; diff --git a/elf/neededtest3.c b/elf/neededtest3.c index 38b3c6c..41970cf 100644 --- a/elf/neededtest3.c +++ b/elf/neededtest3.c @@ -5,6 +5,8 @@ #include <stdlib.h> #include <string.h> +#define MAPS ((struct link_map *) _r_debug.r_map) + static int check_loaded_objects (const char **loaded) { @@ -24,10 +26,10 @@ check_loaded_objects (const char **loaded) printf(" Name\n"); printf(" --------------------------------------------------------\n"); - for (lm = _r_debug.r_map; lm; lm = lm->l_next) + for (lm = MAPS; lm; lm = lm->l_next) { if (lm->l_name && lm->l_name[0]) - printf(" %s, count = %d\n", lm->l_name, (int) lm->l_opencount); + printf(" %s, count = %d\n", lm->l_name, (int) lm->l_direct_opencount); if (lm->l_type == lt_loaded && lm->l_name) { int match = 0; diff --git a/elf/neededtest4.c b/elf/neededtest4.c index 04ab10e..bd79341 100644 --- a/elf/neededtest4.c +++ b/elf/neededtest4.c @@ -5,6 +5,8 @@ #include <stdlib.h> #include <string.h> +#define MAPS ((struct link_map *) _r_debug.r_map) + static int check_loaded_objects (const char **loaded) { @@ -24,10 +26,10 @@ check_loaded_objects (const char **loaded) printf(" Name\n"); printf(" --------------------------------------------------------\n"); - for (lm = _r_debug.r_map; lm; lm = lm->l_next) + for (lm = MAPS; lm; lm = lm->l_next) { if (lm->l_name && lm->l_name[0]) - printf(" %s, count = %d\n", lm->l_name, (int) lm->l_opencount); + printf(" %s, count = %d\n", lm->l_name, (int) lm->l_direct_opencount); if (lm->l_type == lt_loaded && lm->l_name) { int match = 0; diff --git a/elf/order2.c b/elf/order2.c new file mode 100644 index 0000000..3dbfdd1 --- /dev/null +++ b/elf/order2.c @@ -0,0 +1,46 @@ +#include <dlfcn.h> +#include <stdio.h> + + +int call_puts; + +static int +do_test (void) +{ + call_puts = 1; + + void *h1 = dlopen ("$ORIGIN/order2mod1.so", RTLD_LAZY | RTLD_GLOBAL); + if (h1 == NULL) + { + puts ("cannot load order2mod1"); + return 1; + } + void *h2 = dlopen ("$ORIGIN/order2mod2.so", RTLD_LAZY); + if (h2 == NULL) + { + puts ("cannot load order2mod2"); + return 1; + } + if (dlclose (h1) != 0) + { + puts ("dlclose order2mod1 failed"); + return 1; + } + if (dlclose (h2) != 0) + { + puts ("dlclose order2mod2 failed"); + return 1; + } + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" + +static void +__attribute__ ((destructor)) +fini (void) +{ + if (call_puts) + puts ("5"); +} diff --git a/elf/order2mod1.c b/elf/order2mod1.c new file mode 100644 index 0000000..b695db2 --- /dev/null +++ b/elf/order2mod1.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +static void +__attribute__ ((destructor)) +fini (void) +{ + putchar ('1'); +} diff --git a/elf/order2mod2.c b/elf/order2mod2.c new file mode 100644 index 0000000..026cd2a --- /dev/null +++ b/elf/order2mod2.c @@ -0,0 +1,18 @@ +#include <stdio.h> + +extern int foo (void); +extern int bar (void); + +void +__attribute__ ((constructor)) +init (void) +{ + (void) (foo () - bar ()); +} + +static void +__attribute__ ((destructor)) +fini (void) +{ + putchar ('2'); +} diff --git a/elf/order2mod3.c b/elf/order2mod3.c new file mode 100644 index 0000000..7913a79 --- /dev/null +++ b/elf/order2mod3.c @@ -0,0 +1,14 @@ +#include <stdio.h> + +int +bar (void) +{ + return 1; +} + +static void +__attribute__ ((destructor)) +fini (void) +{ + putchar ('4'); +} diff --git a/elf/order2mod4.c b/elf/order2mod4.c new file mode 100644 index 0000000..4f2026f --- /dev/null +++ b/elf/order2mod4.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern int bar (void); + +int +foo (void) +{ + return 42 + bar (); +} + +static void +__attribute__ ((destructor)) +fini (void) +{ + putchar ('3'); +} diff --git a/elf/readelflib.c b/elf/readelflib.c new file mode 100644 index 0000000..26444ad --- /dev/null +++ b/elf/readelflib.c @@ -0,0 +1,220 @@ +/* Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Andreas Jaeger <aj@suse.de>, 1999 and + Jakub Jelinek <jakub@redhat.com>, 1999. + + 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* This code is a heavily simplified version of the readelf program + that's part of the current binutils development version. For architectures + which need to handle both 32bit and 64bit ELF libraries, this file is + included twice for each arch size. */ + +/* check_ptr checks that a pointer is in the mmaped file and doesn't + point outside it. */ +#undef check_ptr +#define check_ptr(ptr) \ +do \ + { \ + if ((void *)(ptr) < file_contents \ + || (void *)(ptr) > (file_contents+file_length)) \ + { \ + error (0, 0, _("file %s is truncated\n"), file_name); \ + return 1; \ + } \ + } \ + while (0); + +/* Returns 0 if everything is ok, != 0 in case of error. */ +int +process_elf_file (const char *file_name, const char *lib, int *flag, + unsigned int *osversion, char **soname, void *file_contents, + size_t file_length) +{ + int i; + unsigned int j; + ElfW(Addr) loadaddr; + unsigned int dynamic_addr; + size_t dynamic_size; + char *program_interpreter; + + ElfW(Ehdr) *elf_header; + ElfW(Phdr) *elf_pheader, *segment; + ElfW(Dyn) *dynamic_segment, *dyn_entry; + char *dynamic_strings; + + elf_header = (ElfW(Ehdr) *) file_contents; + *osversion = 0; + + if (elf_header->e_ident [EI_CLASS] != ElfW (CLASS)) + { + if (opt_verbose) + { + if (elf_header->e_ident [EI_CLASS] == ELFCLASS32) + error (0, 0, _("%s is a 32 bit ELF file.\n"), file_name); + else if (elf_header->e_ident [EI_CLASS] == ELFCLASS64) + error (0, 0, _("%s is a 64 bit ELF file.\n"), file_name); + else + error (0, 0, _("Unknown ELFCLASS in file %s.\n"), file_name); + } + return 1; + } + + if (elf_header->e_type != ET_DYN) + { + error (0, 0, _("%s is not a shared object file (Type: %d).\n"), file_name, + elf_header->e_type); + return 1; + } + + /* Get information from elf program header. */ + elf_pheader = (ElfW(Phdr) *) (elf_header->e_phoff + file_contents); + check_ptr (elf_pheader); + + /* The library is an elf library, now search for soname and + libc5/libc6. */ + *flag = FLAG_ELF; + + loadaddr = -1; + dynamic_addr = 0; + dynamic_size = 0; + program_interpreter = NULL; + for (i = 0, segment = elf_pheader; + i < elf_header->e_phnum; i++, segment++) + { + check_ptr (segment); + + switch (segment->p_type) + { + case PT_LOAD: + if (loadaddr == (ElfW(Addr)) -1) + loadaddr = segment->p_vaddr - segment->p_offset; + break; + + case PT_DYNAMIC: + if (dynamic_addr) + error (0, 0, _("more than one dynamic segment\n")); + + dynamic_addr = segment->p_offset; + dynamic_size = segment->p_filesz; + break; + + case PT_INTERP: + program_interpreter = (char *) (file_contents + segment->p_offset); + check_ptr (program_interpreter); + + /* Check if this is enough to classify the binary. */ + for (j = 0; j < sizeof (interpreters) / sizeof (interpreters [0]); + ++j) + if (strcmp (program_interpreter, interpreters[j].soname) == 0) + { + *flag = interpreters[j].flag; + break; + } + break; + + case PT_NOTE: + if (!*osversion && segment->p_filesz == 32 && segment->p_align >= 4) + { + ElfW(Word) *abi_note = (ElfW(Word) *) (file_contents + + segment->p_offset); + if (abi_note [0] == 4 && abi_note [1] == 16 && abi_note [2] == 1 + && memcmp (abi_note + 3, "GNU", 4) == 0) + *osversion = (abi_note [4] << 24) | + ((abi_note [5] & 0xff) << 16) | + ((abi_note [6] & 0xff) << 8) | + (abi_note [7] & 0xff); + } + break; + + default: + break; + } + + } + if (loadaddr == (ElfW(Addr)) -1) + { + /* Very strange. */ + loadaddr = 0; + } + + /* Now we can read the dynamic sections. */ + if (dynamic_size == 0) + return 1; + + dynamic_segment = (ElfW(Dyn) *) (file_contents + dynamic_addr); + check_ptr (dynamic_segment); + + /* Find the string table. */ + dynamic_strings = NULL; + for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL; + ++dyn_entry) + { + check_ptr (dyn_entry); + if (dyn_entry->d_tag == DT_STRTAB) + { + dynamic_strings = (char *) (file_contents + dyn_entry->d_un.d_val - loadaddr); + check_ptr (dynamic_strings); + break; + } + } + + if (dynamic_strings == NULL) + return 1; + + /* Now read the DT_NEEDED and DT_SONAME entries. */ + for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL; + ++dyn_entry) + { + if (dyn_entry->d_tag == DT_NEEDED || dyn_entry->d_tag == DT_SONAME) + { + char *name = dynamic_strings + dyn_entry->d_un.d_val; + check_ptr (name); + + if (dyn_entry->d_tag == DT_NEEDED) + { + + if (*flag == FLAG_ELF) + { + /* Check if this is enough to classify the binary. */ + for (j = 0; + j < sizeof (known_libs) / sizeof (known_libs [0]); + ++j) + if (strcmp (name, known_libs [j].soname) == 0) + { + *flag = known_libs [j].flag; + break; + } + } + } + + else if (dyn_entry->d_tag == DT_SONAME) + *soname = xstrdup (name); + + /* Do we have everything we need? */ + if (*soname && *flag != FLAG_ELF) + return 0; + } + } + + /* We reach this point only if the file doesn't contain a DT_SONAME + or if we can't classify the library. If it doesn't have a + soname, return the name of the library. */ + if (*soname == NULL) + *soname = xstrdup (lib); + + return 0; +} diff --git a/elf/readlib.c b/elf/readlib.c index 4fbc3e5..8896bbd 100644 --- a/elf/readlib.c +++ b/elf/readlib.c @@ -1,22 +1,20 @@ -/* Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. +/* Copyright (C) 1999-2003, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Andreas Jaeger <aj@suse.de>, 1999 and Jakub Jelinek <jakub@redhat.com>, 1999. - 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. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. - The GNU C Library is distributed in the hope that it will be useful, + This program 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. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* The code in this file and in readelflib is a heavily simplified version of the readelf program that's part of the current binutils @@ -36,7 +34,7 @@ #include <sys/stat.h> #include <gnu/lib-names.h> -#include "ldconfig.h" +#include <ldconfig.h> #define Elf32_CLASS ELFCLASS32 #define Elf64_CLASS ELFCLASS64 @@ -180,4 +178,4 @@ process_file (const char *real_file_name, const char *file_name, } /* Get architecture specific version of process_elf_file. */ -#include "readelflib.c" +#include <readelflib.c> diff --git a/elf/rtld-Rules b/elf/rtld-Rules index ac96f72..01fbbdf 100644 --- a/elf/rtld-Rules +++ b/elf/rtld-Rules @@ -1,6 +1,6 @@ # Subroutine makefile for compiling libc modules linked into dynamic linker. -# Copyright (C) 2002, 2003 Free Software Foundation, Inc. +# Copyright (C) 2002, 2003, 2005, 2006 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 @@ -28,7 +28,14 @@ rtld-all: # When run from the elf/Makefile to build rtld-libc.a, $(subdir) is elf. -ifeq ($(subdir),elf) +ifneq ($(subdir),elf) +ifndef rtld-modules +error rtld-modules not set +endif +endif + +ifndef rtld-modules +# Running to build rtld-libc.a, driving runs of $(rtld-subdir-make), below. ifndef rtld-subdirs error This makefile is a subroutine of elf/Makefile not to be used directly @@ -65,10 +72,16 @@ include $(patsubst %,../o-iterator.mk,$(object-suffixes-left)) # This is how we descend into each subdirectory. See below. define rtld-subdir-make -$(MAKE) -C ../$* objdir=$(objdir) -f Makefile -f ../elf/rtld-Rules rtld-all \ +$(MAKE) $(subdir-args) objdir=$(objdir) \ + -f Makefile -f ../elf/rtld-Rules rtld-all \ rtld-modules='$(addprefix rtld-,$(rtld-$*))' endef +# See subdir-target-args in ../Makefile for the model. +subdir-args = subdir=$*$(if $($*-srcdir),\ + -C $($*-srcdir) ..=`pwd`/,\ + -C $(..)$* ..=../) + FORCE: else @@ -1,5 +1,5 @@ /* Run time dynamic linker. - Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1995-2006, 2007 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 @@ -34,9 +34,10 @@ #include <hp-timing.h> #include <bits/libc-lock.h> #include "dynamic-link.h" -#include "dl-librecon.h" +#include <dl-librecon.h> #include <unsecvars.h> #include <dl-cache.h> +#include <dl-osinfo.h> #include <dl-procinfo.h> #include <tls.h> @@ -60,6 +61,9 @@ static void print_missing_version (int errcode, const char *objname, /* Print the various times we collected. */ static void print_statistics (hp_timing_t *total_timep); +/* Add audit objects. */ +static void process_dl_audit (char *str); + /* This is a list of all the modes the dynamic loader can be in. */ enum mode { normal, list, verify, trace }; @@ -68,16 +72,39 @@ enum mode { normal, list, verify, trace }; all the entries. */ static void process_envvars (enum mode *modep); -int _dl_argc attribute_relro attribute_hidden; #ifdef DL_ARGV_NOT_RELRO +int _dl_argc attribute_hidden; char **_dl_argv = NULL; +/* Nonzero if we were run directly. */ +unsigned int _dl_skip_args attribute_hidden; #else +int _dl_argc attribute_relro attribute_hidden; char **_dl_argv attribute_relro = NULL; +unsigned int _dl_skip_args attribute_relro attribute_hidden; #endif INTDEF(_dl_argv) -/* Nonzero if we were run directly. */ -unsigned int _dl_skip_args attribute_relro attribute_hidden; +#ifndef THREAD_SET_STACK_GUARD +/* Only exported for architectures that don't store the stack guard canary + in thread local area. */ +uintptr_t __stack_chk_guard attribute_relro; +#endif + +/* Only exported for architectures that don't store the pointer guard + value in thread local area. */ +uintptr_t __pointer_chk_guard_local + attribute_relro attribute_hidden __attribute__ ((nocommon)); +#ifndef THREAD_SET_POINTER_GUARD +strong_alias (__pointer_chk_guard_local, __pointer_chk_guard) +#endif + + +/* List of auditing DSOs. */ +static struct audit_list +{ + const char *name; + struct audit_list *next; +} *audit_list; #ifndef HAVE_INLINED_SYSCALLS /* Set nonzero during loading and initialization of executable and @@ -124,27 +151,17 @@ struct rtld_global_ro _rtld_global_ro attribute_relro = ._dl_hwcap_mask = HWCAP_IMPORTANT, ._dl_lazy = 1, ._dl_fpu_control = _FPU_DEFAULT, + ._dl_pointer_guard = 1, /* Function pointers. */ - ._dl_get_origin = _dl_get_origin, - ._dl_dst_count = _dl_dst_count, - ._dl_dst_substitute = _dl_dst_substitute, - ._dl_map_object = _dl_map_object, - ._dl_map_object_deps = _dl_map_object_deps, - ._dl_relocate_object = _dl_relocate_object, - ._dl_check_map_versions = _dl_check_map_versions, - ._dl_init = _dl_init, - ._dl_debug_state = _dl_debug_state, -#ifndef MAP_COPY - ._dl_unload_cache = _dl_unload_cache, -#endif ._dl_debug_printf = _dl_debug_printf, ._dl_catch_error = _dl_catch_error, ._dl_signal_error = _dl_signal_error, - ._dl_start_profile = _dl_start_profile, ._dl_mcount = _dl_mcount_internal, ._dl_lookup_symbol_x = _dl_lookup_symbol_x, - ._dl_check_caller = _dl_check_caller + ._dl_check_caller = _dl_check_caller, + ._dl_open = _dl_open, + ._dl_close = _dl_close }; /* If we would use strong_alias here the compiler would see a non-hidden definition. This would undo the effect of the previous @@ -267,10 +284,10 @@ _dl_start_final (void *arg, struct dl_start_final_info *info) memcpy (GL(dl_rtld_map).l_info, info->l.l_info, sizeof GL(dl_rtld_map).l_info); GL(dl_rtld_map).l_mach = info->l.l_mach; + GL(dl_rtld_map).l_relocated = 1; #endif _dl_setup_hash (&GL(dl_rtld_map)); GL(dl_rtld_map).l_real = &GL(dl_rtld_map); - GL(dl_rtld_map).l_opencount = 1; GL(dl_rtld_map).l_map_start = (ElfW(Addr)) _begin; GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end; GL(dl_rtld_map).l_text_end = (ElfW(Addr)) _etext; @@ -286,7 +303,6 @@ _dl_start_final (void *arg, struct dl_start_final_info *info) GL(dl_rtld_map).l_tls_offset = info->l.l_tls_offset; GL(dl_rtld_map).l_tls_modid = 1; # else - assert (info->l.l_tls_modid == 0); # if NO_TLS_OFFSET != 0 GL(dl_rtld_map).l_tls_offset = NO_TLS_OFFSET; # endif @@ -348,8 +364,6 @@ _dl_start (void *arg) #define RTLD_BOOTSTRAP #define RESOLVE_MAP(sym, version, flags) \ ((*(sym))->st_shndx == SHN_UNDEF ? 0 : &bootstrap_map) -#define RESOLVE(sym, version, flags) \ - ((*(sym))->st_shndx == SHN_UNDEF ? 0 : bootstrap_map.l_addr) #include "dynamic-link.h" if (HP_TIMING_INLINE && HP_TIMING_AVAIL) @@ -374,6 +388,9 @@ _dl_start (void *arg) ++cnt) bootstrap_map.l_info[cnt] = 0; # endif +# if USE___THREAD + bootstrap_map.l_tls_modid = 0; +# endif #endif /* Figure out the run-time load address of the dynamic linker itself. */ @@ -472,7 +489,7 @@ _dl_start (void *arg) while (remaining-- > 0) *p++ = '\0'; } -#endif +# endif /* Install the pointer to the dtv. */ @@ -515,6 +532,7 @@ _dl_start (void *arg) ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, 0); } + bootstrap_map.l_relocated = 1; /* Please note that we don't allow profiling of this object and therefore need not test whether we have to allocate the array @@ -566,6 +584,19 @@ struct map_args struct link_map *map; }; +struct dlmopen_args +{ + const char *fname; + struct link_map *map; +}; + +struct lookup_args +{ + const char *name; + struct link_map *map; + void *result; +}; + /* Arguments to version_check_doit. */ struct version_check_args { @@ -591,6 +622,28 @@ map_doit (void *a) } static void +dlmopen_doit (void *a) +{ + struct dlmopen_args *args = (struct dlmopen_args *) a; + args->map = _dl_open (args->fname, RTLD_LAZY | __RTLD_DLOPEN | __RTLD_AUDIT, + dl_main, LM_ID_NEWLM, _dl_argc, INTUSE(_dl_argv), + __environ); +} + +static void +lookup_doit (void *a) +{ + struct lookup_args *args = (struct lookup_args *) a; + const ElfW(Sym) *ref = NULL; + args->result = NULL; + lookup_t l = _dl_lookup_symbol_x (args->name, args->map, &ref, + args->map->l_local_scope, NULL, 0, + DL_LOOKUP_RETURN_NEWEST, NULL); + if (ref != NULL) + args->result = DL_SYMBOL_ADDRESS (l, ref); +} + +static void version_check_doit (void *a) { struct version_check_args *args = (struct version_check_args *) a; @@ -648,6 +701,80 @@ match_version (const char *string, struct link_map *map) return 0; } +#ifdef USE_TLS +static bool tls_init_tp_called; + +static void * +init_tls (void) +{ + /* Number of elements in the static TLS block. */ + GL(dl_tls_static_nelem) = GL(dl_tls_max_dtv_idx); + + /* Do not do this twice. The audit interface might have required + the DTV interfaces to be set up early. */ + if (GL(dl_initial_dtv) != NULL) + return NULL; + + /* Allocate the array which contains the information about the + dtv slots. We allocate a few entries more than needed to + avoid the need for reallocation. */ + size_t nelem = GL(dl_tls_max_dtv_idx) + 1 + TLS_SLOTINFO_SURPLUS; + + /* Allocate. */ + GL(dl_tls_dtv_slotinfo_list) = (struct dtv_slotinfo_list *) + calloc (sizeof (struct dtv_slotinfo_list) + + nelem * sizeof (struct dtv_slotinfo), 1); + /* No need to check the return value. If memory allocation failed + the program would have been terminated. */ + + struct dtv_slotinfo *slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo; + GL(dl_tls_dtv_slotinfo_list)->len = nelem; + GL(dl_tls_dtv_slotinfo_list)->next = NULL; + + /* Fill in the information from the loaded modules. No namespace + but the base one can be filled at this time. */ + assert (GL(dl_ns)[LM_ID_BASE + 1]._ns_loaded == NULL); + int i = 0; + for (struct link_map *l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l != NULL; + l = l->l_next) + if (l->l_tls_blocksize != 0) + { + /* This is a module with TLS data. Store the map reference. + The generation counter is zero. */ + slotinfo[i].map = l; + /* slotinfo[i].gen = 0; */ + ++i; + } + assert (i == GL(dl_tls_max_dtv_idx)); + + /* Compute the TLS offsets for the various blocks. */ + _dl_determine_tlsoffset (); + + /* Construct the static TLS block and the dtv for the initial + thread. For some platforms this will include allocating memory + for the thread descriptor. The memory for the TLS block will + never be freed. It should be allocated accordingly. The dtv + array can be changed if dynamic loading requires it. */ + void *tcbp = _dl_allocate_tls_storage (); + if (tcbp == NULL) + _dl_fatal_printf ("\ +cannot allocate TLS data structures for initial thread"); + + /* Store for detection of the special case by __tls_get_addr + so it knows not to pass this dtv to the normal realloc. */ + GL(dl_initial_dtv) = GET_DTV (tcbp); + + /* And finally install it for the main thread. If ld.so itself uses + TLS we know the thread pointer was initialized earlier. */ + const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD); + if (__builtin_expect (lossage != NULL, 0)) + _dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage); + tls_init_tp_called = true; + + return tcbp; +} +#endif + #ifdef _LIBC_REENTRANT /* _dl_error_catch_tsd points to this for the single-threaded case. It's reset by the thread library for multithreaded programs. */ @@ -659,14 +786,49 @@ _dl_initial_error_catch_tsd (void) } #endif + +static unsigned int +do_preload (char *fname, struct link_map *main_map, const char *where) +{ + const char *objname; + const char *err_str = NULL; + struct map_args args; + bool malloced; + + args.str = fname; + args.loader = main_map; + args.is_preloaded = 1; + args.mode = 0; + + unsigned int old_nloaded = GL(dl_ns)[LM_ID_BASE]._ns_nloaded; + + (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit, &args); + if (__builtin_expect (err_str != NULL, 0)) + { + _dl_error_printf ("\ +ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", + fname, where); + /* No need to call free, this is still before + the libc's malloc is used. */ + } + else if (GL(dl_ns)[LM_ID_BASE]._ns_nloaded != old_nloaded) + /* It is no duplicate. */ + return 1; + + /* Nothing loaded. */ + return 0; +} + #if defined SHARED && defined _LIBC_REENTRANT \ && defined __rtld_lock_default_lock_recursive -static void rtld_lock_default_lock_recursive (void *lock) +static void +rtld_lock_default_lock_recursive (void *lock) { __rtld_lock_default_lock_recursive (lock); } -static void rtld_lock_default_unlock_recursive (void *lock) +static void +rtld_lock_default_unlock_recursive (void *lock) { __rtld_lock_default_unlock_recursive (lock); } @@ -687,8 +849,6 @@ dl_main (const ElfW(Phdr) *phdr, { const ElfW(Phdr) *ph; enum mode mode; - struct link_map **preloads; - unsigned int npreloads; struct link_map *main_map; size_t file_size; char *file; @@ -702,7 +862,7 @@ dl_main (const ElfW(Phdr) *phdr, hp_timing_t diff; #endif #ifdef USE_TLS - void *tcbp; + void *tcbp = NULL; #endif #ifdef _LIBC_REENTRANT @@ -790,6 +950,14 @@ dl_main (const ElfW(Phdr) *phdr, _dl_argc -= 2; INTUSE(_dl_argv) += 2; } + else if (! strcmp (INTUSE(_dl_argv)[1], "--audit") && _dl_argc > 2) + { + process_dl_audit (INTUSE(_dl_argv)[2]); + + _dl_skip_args += 2; + _dl_argc -= 2; + INTUSE(_dl_argv) += 2; + } else break; @@ -822,10 +990,6 @@ of this helper program; chances are you did not intend to run this program.\n\ --_dl_argc; ++INTUSE(_dl_argv); - /* Initialize the data structures for the search paths for shared - objects. */ - _dl_init_paths (library_path); - /* The initialization of _dl_stack_flags done below assumes the executable's PT_GNU_STACK may have been honored by the kernel, and so a PT_GNU_STACK with PF_X set means the stack started out with @@ -850,12 +1014,14 @@ of this helper program; chances are you did not intend to run this program.\n\ const char *objname; const char *err_str = NULL; struct map_args args; + bool malloced; args.str = rtld_progname; args.loader = NULL; args.is_preloaded = 0; args.mode = __RTLD_OPENEXEC; - (void) _dl_catch_error (&objname, &err_str, map_doit, &args); + (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit, + &args); if (__builtin_expect (err_str != NULL, 0)) /* We don't free the returned string, the programs stops anyway. */ @@ -887,10 +1053,10 @@ of this helper program; chances are you did not intend to run this program.\n\ { /* Create a link_map for the executable itself. This will be what dlopen on "" returns. */ - _dl_new_object ((char *) "", "", lt_executable, NULL, 0, LM_ID_BASE); - main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; - if (main_map == NULL) - _dl_fatal_printf ("cannot allocate memory for link map\n"); + main_map = _dl_new_object ((char *) "", "", lt_executable, NULL, + __RTLD_OPENEXEC, LM_ID_BASE); + assert (main_map != NULL); + assert (main_map == GL(dl_ns)[LM_ID_BASE]._ns_loaded); main_map->l_phdr = phdr; main_map->l_phnum = phnum; main_map->l_entry = *user_entry; @@ -918,8 +1084,6 @@ of this helper program; chances are you did not intend to run this program.\n\ main_map->l_text_end = 0; /* Perhaps the executable has no PT_LOAD header entries at all. */ main_map->l_map_start = ~0; - /* We opened the file, account for it. */ - ++main_map->l_opencount; /* And it was opened directly. */ ++main_map->l_direct_opencount; @@ -991,8 +1155,9 @@ of this helper program; chances are you did not intend to run this program.\n\ main_map->l_text_end = allocend; } break; -#ifdef USE_TLS + case PT_TLS: +#ifdef USE_TLS if (ph->p_memsz > 0) { /* Note that in the case the dynamic linker we duplicate work @@ -1012,8 +1177,12 @@ of this helper program; chances are you did not intend to run this program.\n\ /* This image gets the ID one. */ GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1; } - break; +#else + _dl_fatal_printf ("\ +ld.so does not support TLS, but program uses it!\n"); #endif + break; + case PT_GNU_STACK: GL(dl_stack_flags) = ph->p_flags; break; @@ -1045,6 +1214,26 @@ of this helper program; chances are you did not intend to run this program.\n\ else assert (GL(dl_rtld_map).l_libname); /* How else did we get here? */ + /* If the current libname is different from the SONAME, add the + latter as well. */ + if (GL(dl_rtld_map).l_info[DT_SONAME] != NULL + && strcmp (GL(dl_rtld_map).l_libname->name, + (const char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB]) + + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_val) != 0) + { + static struct libname_list newname; + newname.name = ((char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB]) + + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_ptr); + newname.next = NULL; + newname.dont_free = 1; + + assert (GL(dl_rtld_map).l_libname->next == NULL); + GL(dl_rtld_map).l_libname->next = &newname; + } + /* The ld.so must be relocated since otherwise loading audit modules + will fail since they reuse the very same ld.so. */ + assert (GL(dl_rtld_map).l_relocated); + if (! rtld_is_main) { /* Extract the contents of the dynamic section for easy access. */ @@ -1069,10 +1258,110 @@ of this helper program; chances are you did not intend to run this program.\n\ _exit (has_interp ? 0 : 2); } - if (! rtld_is_main) - /* Initialize the data structures for the search paths for shared - objects. */ - _dl_init_paths (library_path); + struct link_map **first_preload = &GL(dl_rtld_map).l_next; +#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO + /* Set up the data structures for the system-supplied DSO early, + so they can influence _dl_init_paths. */ + if (GLRO(dl_sysinfo_dso) != NULL) + { + /* Do an abridged version of the work _dl_map_object_from_fd would do + to map in the object. It's already mapped and prelinked (and + better be, since it's read-only and so we couldn't relocate it). + We just want our data structures to describe it as if we had just + mapped and relocated it normally. */ + struct link_map *l = _dl_new_object ((char *) "", "", lt_library, NULL, + 0, LM_ID_BASE); + if (__builtin_expect (l != NULL, 1)) + { + static ElfW(Dyn) dyn_temp[DL_RO_DYN_TEMP_CNT] attribute_relro; + + l->l_phdr = ((const void *) GLRO(dl_sysinfo_dso) + + GLRO(dl_sysinfo_dso)->e_phoff); + l->l_phnum = GLRO(dl_sysinfo_dso)->e_phnum; + for (uint_fast16_t i = 0; i < l->l_phnum; ++i) + { + const ElfW(Phdr) *const ph = &l->l_phdr[i]; + if (ph->p_type == PT_DYNAMIC) + { + l->l_ld = (void *) ph->p_vaddr; + l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn)); + } + else if (ph->p_type == PT_LOAD) + { + if (! l->l_addr) + l->l_addr = ph->p_vaddr; + if (ph->p_vaddr + ph->p_memsz >= l->l_map_end) + l->l_map_end = ph->p_vaddr + ph->p_memsz; + if ((ph->p_flags & PF_X) + && ph->p_vaddr + ph->p_memsz >= l->l_text_end) + l->l_text_end = ph->p_vaddr + ph->p_memsz; + } + else + /* There must be no TLS segment. */ + assert (ph->p_type != PT_TLS); + } + l->l_map_start = (ElfW(Addr)) GLRO(dl_sysinfo_dso); + l->l_addr = l->l_map_start - l->l_addr; + l->l_map_end += l->l_addr; + l->l_text_end += l->l_addr; + l->l_ld = (void *) ((ElfW(Addr)) l->l_ld + l->l_addr); + elf_get_dynamic_info (l, dyn_temp); + _dl_setup_hash (l); + l->l_relocated = 1; + + /* Initialize l_local_scope to contain just this map. This allows + the use of dl_lookup_symbol_x to resolve symbols within the vdso. + So we create a single entry list pointing to l_real as its only + element */ + l->l_local_scope[0]->r_nlist = 1; + l->l_local_scope[0]->r_list = &l->l_real; + + /* Now that we have the info handy, use the DSO image's soname + so this object can be looked up by name. Note that we do not + set l_name here. That field gives the file name of the DSO, + and this DSO is not associated with any file. */ + if (l->l_info[DT_SONAME] != NULL) + { + /* Work around a kernel problem. The kernel cannot handle + addresses in the vsyscall DSO pages in writev() calls. */ + const char *dsoname = ((char *) D_PTR (l, l_info[DT_STRTAB]) + + l->l_info[DT_SONAME]->d_un.d_val); + size_t len = strlen (dsoname); + char *copy = malloc (len); + if (copy == NULL) + _dl_fatal_printf ("out of memory\n"); + l->l_libname->name = memcpy (copy, dsoname, len); + } + + /* Rearrange the list so this DSO appears after rtld_map. */ + assert (l->l_next == NULL); + assert (l->l_prev == main_map); + GL(dl_rtld_map).l_next = l; + l->l_prev = &GL(dl_rtld_map); + first_preload = &l->l_next; + + /* We have a prelinked DSO preloaded by the system. */ + GLRO(dl_sysinfo_map) = l; +# ifdef NEED_DL_SYSINFO + if (GLRO(dl_sysinfo) == DL_SYSINFO_DEFAULT) + GLRO(dl_sysinfo) = GLRO(dl_sysinfo_dso)->e_entry + l->l_addr; +# endif + } + } +#endif + +#ifdef DL_SYSDEP_OSCHECK + DL_SYSDEP_OSCHECK (dl_fatal); +#endif + + /* Initialize the data structures for the search paths for shared + objects. */ + _dl_init_paths (library_path); + + /* Initialize _r_debug. */ + struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr, + LM_ID_BASE); + r->r_state = RT_CONSISTENT; /* Put the link_map for ourselves on the chain so it can be found by name. Note that at this point the global chain of link maps contains @@ -1101,6 +1390,7 @@ of this helper program; chances are you did not intend to run this program.\n\ GL(dl_rtld_map).l_phdr = rtld_phdr; GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum; + /* PT_GNU_RELRO is usually the last phdr. */ size_t cnt = rtld_ehdr->e_phnum; while (cnt-- > 0) @@ -1111,11 +1401,229 @@ of this helper program; chances are you did not intend to run this program.\n\ break; } +#ifdef USE_TLS + /* Add the dynamic linker to the TLS list if it also uses TLS. */ + if (GL(dl_rtld_map).l_tls_blocksize != 0) + /* Assign a module ID. Do this before loading any audit modules. */ + GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid (); +#endif + + /* If we have auditing DSOs to load, do it now. */ + if (__builtin_expect (audit_list != NULL, 0)) + { + /* Iterate over all entries in the list. The order is important. */ + struct audit_ifaces *last_audit = NULL; + struct audit_list *al = audit_list->next; + +#ifdef USE_TLS + /* Since we start using the auditing DSOs right away we need to + initialize the data structures now. */ + tcbp = init_tls (); +#endif + do + { +#ifdef USE_TLS + int tls_idx = GL(dl_tls_max_dtv_idx); + + /* Now it is time to determine the layout of the static TLS + block and allocate it for the initial thread. Note that we + always allocate the static block, we never defer it even if + no DF_STATIC_TLS bit is set. The reason is that we know + glibc will use the static model. */ +#endif + struct dlmopen_args dlmargs; + dlmargs.fname = al->name; + dlmargs.map = NULL; + + const char *objname; + const char *err_str = NULL; + bool malloced; + (void) _dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit, + &dlmargs); + if (__builtin_expect (err_str != NULL, 0)) + { + not_loaded: + _dl_error_printf ("\ +ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", + al->name, err_str); + if (malloced) + free ((char *) err_str); + } + else + { + struct lookup_args largs; + largs.name = "la_version"; + largs.map = dlmargs.map; + + /* Check whether the interface version matches. */ + (void) _dl_catch_error (&objname, &err_str, &malloced, + lookup_doit, &largs); + + unsigned int (*laversion) (unsigned int); + unsigned int lav; + if (err_str == NULL + && (laversion = largs.result) != NULL + && (lav = laversion (LAV_CURRENT)) > 0 + && lav <= LAV_CURRENT) + { + /* Allocate structure for the callback function pointers. + This call can never fail. */ + union + { + struct audit_ifaces ifaces; +#define naudit_ifaces 8 + void (*fptr[naudit_ifaces]) (void); + } *newp = malloc (sizeof (*newp)); + + /* Names of the auditing interfaces. All in one + long string. */ + static const char audit_iface_names[] = + "la_activity\0" + "la_objsearch\0" + "la_objopen\0" + "la_preinit\0" +#if __ELF_NATIVE_CLASS == 32 + "la_symbind32\0" +#elif __ELF_NATIVE_CLASS == 64 + "la_symbind64\0" +#else +# error "__ELF_NATIVE_CLASS must be defined" +#endif +#define STRING(s) __STRING (s) + "la_" STRING (ARCH_LA_PLTENTER) "\0" + "la_" STRING (ARCH_LA_PLTEXIT) "\0" + "la_objclose\0"; + unsigned int cnt = 0; + const char *cp = audit_iface_names; + do + { + largs.name = cp; + (void) _dl_catch_error (&objname, &err_str, &malloced, + lookup_doit, &largs); + + /* Store the pointer. */ + if (err_str == NULL && largs.result != NULL) + { + newp->fptr[cnt] = largs.result; + + /* The dynamic linker link map is statically + allocated, initialize the data now. */ + GL(dl_rtld_map).l_audit[cnt].cookie + = (intptr_t) &GL(dl_rtld_map); + } + else + newp->fptr[cnt] = NULL; + ++cnt; + + cp = (char *) rawmemchr (cp, '\0') + 1; + } + while (*cp != '\0'); + assert (cnt == naudit_ifaces); + + /* Now append the new auditing interface to the list. */ + newp->ifaces.next = NULL; + if (last_audit == NULL) + last_audit = GLRO(dl_audit) = &newp->ifaces; + else + last_audit = last_audit->next = &newp->ifaces; + ++GLRO(dl_naudit); + + /* Mark the DSO as being used for auditing. */ + dlmargs.map->l_auditing = 1; + } + else + { + /* We cannot use the DSO, it does not have the + appropriate interfaces or it expects something + more recent. */ +#ifndef NDEBUG + Lmid_t ns = dlmargs.map->l_ns; +#endif + _dl_close (dlmargs.map); + + /* Make sure the namespace has been cleared entirely. */ + assert (GL(dl_ns)[ns]._ns_loaded == NULL); + assert (GL(dl_ns)[ns]._ns_nloaded == 0); + +#ifdef USE_TLS + GL(dl_tls_max_dtv_idx) = tls_idx; +#endif + goto not_loaded; + } + } + + al = al->next; + } + while (al != audit_list->next); + + /* If we have any auditing modules, announce that we already + have two objects loaded. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct link_map *ls[2] = { main_map, &GL(dl_rtld_map) }; + + for (unsigned int outer = 0; outer < 2; ++outer) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objopen != NULL) + { + ls[outer]->l_audit[cnt].bindflags + = afct->objopen (ls[outer], LM_ID_BASE, + &ls[outer]->l_audit[cnt].cookie); + + ls[outer]->l_audit_any_plt + |= ls[outer]->l_audit[cnt].bindflags != 0; + } + + afct = afct->next; + } + } + } + } + + /* Set up debugging before the debugger is notified for the first time. */ +#ifdef ELF_MACHINE_DEBUG_SETUP + /* Some machines (e.g. MIPS) don't use DT_DEBUG in this way. */ + ELF_MACHINE_DEBUG_SETUP (main_map, r); + ELF_MACHINE_DEBUG_SETUP (&GL(dl_rtld_map), r); +#else + if (main_map->l_info[DT_DEBUG] != NULL) + /* There is a DT_DEBUG entry in the dynamic section. Fill it in + with the run-time address of the r_debug structure */ + main_map->l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r; + + /* Fill in the pointer in the dynamic linker's own dynamic section, in + case you run gdb on the dynamic linker directly. */ + if (GL(dl_rtld_map).l_info[DT_DEBUG] != NULL) + GL(dl_rtld_map).l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r; +#endif + + /* We start adding objects. */ + r->r_state = RT_ADD; + _dl_debug_state (); + + /* Auditing checkpoint: we are ready to signal that the initial map + is being constructed. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&main_map->l_audit[cnt].cookie, LA_ACT_ADD); + + afct = afct->next; + } + } + /* We have two ways to specify objects to preload: via environment variable and via the file /etc/ld.so.preload. The latter can also be used when security is enabled. */ - preloads = NULL; - npreloads = 0; + assert (*first_preload == NULL); + struct link_map **preloads = NULL; + unsigned int npreloads = 0; if (__builtin_expect (preloadlist != NULL, 0)) { @@ -1134,14 +1642,7 @@ of this helper program; chances are you did not intend to run this program.\n\ if (p[0] != '\0' && (__builtin_expect (! INTUSE(__libc_enable_secure), 1) || strchr (p, '/') == NULL)) - { - struct link_map *new_map = _dl_map_object (main_map, p, 1, - lt_library, 0, 0, - LM_ID_BASE); - if (++new_map->l_opencount == 1) - /* It is no duplicate. */ - ++npreloads; - } + npreloads += do_preload (p, main_map, "LD_PRELOAD"); HP_TIMING_NOW (stop); HP_TIMING_DIFF (diff, start, stop); @@ -1213,41 +1714,14 @@ of this helper program; chances are you did not intend to run this program.\n\ runp = file; while ((p = strsep (&runp, ": \t\n")) != NULL) if (p[0] != '\0') - { - const char *objname; - const char *err_str = NULL; - struct map_args args; - - args.str = p; - args.loader = main_map; - args.is_preloaded = 1; - args.mode = 0; - - (void) _dl_catch_error (&objname, &err_str, map_doit, - &args); - if (__builtin_expect (err_str != NULL, 0)) - { - _dl_error_printf ("\ -ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", - p, preload_file); - /* No need to call free, this is still before - the libc's malloc is used. */ - } - else if (++args.map->l_opencount == 1) - /* It is no duplicate. */ - ++npreloads; - } + npreloads += do_preload (p, main_map, preload_file); } if (problem != NULL) { char *p = strndupa (problem, file_size - (problem - file)); - struct link_map *new_map = _dl_map_object (main_map, p, 1, - lt_library, 0, 0, - LM_ID_BASE); - if (++new_map->l_opencount == 1) - /* It is no duplicate. */ - ++npreloads; + + npreloads += do_preload (p, main_map, preload_file); } HP_TIMING_NOW (stop); @@ -1259,12 +1733,11 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", } } - if (__builtin_expect (npreloads, 0) != 0) + if (__builtin_expect (*first_preload != NULL, 0)) { /* Set up PRELOADS with a vector of the preloaded libraries. */ - struct link_map *l; + struct link_map *l = *first_preload; preloads = __alloca (npreloads * sizeof preloads[0]); - l = GL(dl_rtld_map).l_next; /* End of the chain before preloads. */ i = 0; do { @@ -1274,79 +1747,6 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", assert (i == npreloads); } -#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO - struct link_map *sysinfo_map = NULL; - if (GLRO(dl_sysinfo_dso) != NULL) - { - /* Do an abridged version of the work _dl_map_object_from_fd would do - to map in the object. It's already mapped and prelinked (and - better be, since it's read-only and so we couldn't relocate it). - We just want our data structures to describe it as if we had just - mapped and relocated it normally. */ - struct link_map *l = _dl_new_object ((char *) "", "", lt_library, NULL, - 0, LM_ID_BASE); - if (__builtin_expect (l != NULL, 1)) - { - static ElfW(Dyn) dyn_temp[DL_RO_DYN_TEMP_CNT] attribute_relro; - - l->l_phdr = ((const void *) GLRO(dl_sysinfo_dso) - + GLRO(dl_sysinfo_dso)->e_phoff); - l->l_phnum = GLRO(dl_sysinfo_dso)->e_phnum; - for (uint_fast16_t i = 0; i < l->l_phnum; ++i) - { - const ElfW(Phdr) *const ph = &l->l_phdr[i]; - if (ph->p_type == PT_DYNAMIC) - { - l->l_ld = (void *) ph->p_vaddr; - l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn)); - } - else if (ph->p_type == PT_LOAD) - { - if (! l->l_addr) - l->l_addr = ph->p_vaddr; - else if (ph->p_vaddr + ph->p_memsz >= l->l_map_end) - l->l_map_end = ph->p_vaddr + ph->p_memsz; - else if ((ph->p_flags & PF_X) - && ph->p_vaddr + ph->p_memsz >= l->l_text_end) - l->l_text_end = ph->p_vaddr + ph->p_memsz; - } - } - l->l_map_start = (ElfW(Addr)) GLRO(dl_sysinfo_dso); - l->l_addr = l->l_map_start - l->l_addr; - l->l_map_end += l->l_addr; - l->l_text_end += l->l_addr; - l->l_ld = (void *) ((ElfW(Addr)) l->l_ld + l->l_addr); - elf_get_dynamic_info (l, dyn_temp); - _dl_setup_hash (l); - l->l_relocated = 1; - - /* Now that we have the info handy, use the DSO image's soname - so this object can be looked up by name. Note that we do not - set l_name here. That field gives the file name of the DSO, - and this DSO is not associated with any file. */ - if (l->l_info[DT_SONAME] != NULL) - { - /* Work around a kernel problem. The kernel cannot handle - addresses in the vsyscall DSO pages in writev() calls. */ - const char *dsoname = ((char *) D_PTR (l, l_info[DT_STRTAB]) - + l->l_info[DT_SONAME]->d_un.d_val); - size_t len = strlen (dsoname); - char *copy = malloc (len); - if (copy == NULL) - _dl_fatal_printf ("out of memory\n"); - l->l_libname->name = memcpy (copy, dsoname, len); - } - - /* We have a prelinked DSO preloaded by the system. */ - sysinfo_map = l; -# ifdef NEED_DL_SYSINFO - if (GLRO(dl_sysinfo) == DL_SYSINFO_DEFAULT) - GLRO(dl_sysinfo) = GLRO(dl_sysinfo_dso)->e_entry + l->l_addr; -# endif - } - } -#endif - /* Load all the libraries specified by DT_NEEDED entries. If LD_PRELOAD specified some libraries to load, these are inserted before the actual dependencies in the executable's searchlist for symbol resolution. */ @@ -1356,14 +1756,9 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", HP_TIMING_DIFF (diff, start, stop); HP_TIMING_ACCUM_NT (load_time, diff); - /* Mark all objects as being in the global scope and set the open - counter. */ + /* Mark all objects as being in the global scope. */ for (i = main_map->l_searchlist.r_nlist; i > 0; ) - { - --i; - main_map->l_searchlist.r_list[i]->l_global = 1; - ++main_map->l_searchlist.r_list[i]->l_opencount; - } + main_map->l_searchlist.r_list[--i]->l_global = 1; #ifndef MAP_ANON /* We are done mapping things, so close the zero-fill descriptor. */ @@ -1373,18 +1768,22 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", /* Remove _dl_rtld_map from the chain. */ GL(dl_rtld_map).l_prev->l_next = GL(dl_rtld_map).l_next; - if (GL(dl_rtld_map).l_next) + if (GL(dl_rtld_map).l_next != NULL) GL(dl_rtld_map).l_next->l_prev = GL(dl_rtld_map).l_prev; - if (__builtin_expect (GL(dl_rtld_map).l_opencount > 1, 1)) + for (i = 1; i < main_map->l_searchlist.r_nlist; ++i) + if (main_map->l_searchlist.r_list[i] == &GL(dl_rtld_map)) + break; + + bool rtld_multiple_ref = false; + if (__builtin_expect (i < main_map->l_searchlist.r_nlist, 1)) { /* Some DT_NEEDED entry referred to the interpreter object itself, so put it back in the list of visible objects. We insert it into the chain in symbol search order because gdb uses the chain's order as its symbol search order. */ - i = 1; - while (main_map->l_searchlist.r_list[i] != &GL(dl_rtld_map)) - ++i; + rtld_multiple_ref = true; + GL(dl_rtld_map).l_prev = main_map->l_searchlist.r_list[i - 1]; if (__builtin_expect (mode, normal) == normal) { @@ -1392,10 +1791,10 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", ? main_map->l_searchlist.r_list[i + 1] : NULL); #if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO - if (sysinfo_map != NULL - && GL(dl_rtld_map).l_prev->l_next == sysinfo_map - && GL(dl_rtld_map).l_next != sysinfo_map) - GL(dl_rtld_map).l_prev = sysinfo_map; + if (GLRO(dl_sysinfo_map) != NULL + && GL(dl_rtld_map).l_prev->l_next == GLRO(dl_sysinfo_map) + && GL(dl_rtld_map).l_next != GLRO(dl_sysinfo_map)) + GL(dl_rtld_map).l_prev = GLRO(dl_sysinfo_map); #endif } else @@ -1425,20 +1824,6 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", } #ifdef USE_TLS - /* Now it is time to determine the layout of the static TLS block - and allocate it for the initial thread. Note that we always - allocate the static block, we never defer it even if no - DF_STATIC_TLS bit is set. The reason is that we know glibc will - use the static model. First add the dynamic linker to the list - if it also uses TLS. */ - if (GL(dl_rtld_map).l_tls_blocksize != 0) - /* Assign a module ID. */ - GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid (); - -# ifndef TLS_INIT_TP_EXPENSIVE -# define TLS_INIT_TP_EXPENSIVE 0 -# endif - /* We do not initialize any of the TLS functionality unless any of the initial modules uses TLS. This makes dynamic loading of modules with TLS impossible, but to support it requires either eagerly doing setup @@ -1446,59 +1831,35 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", an old kernel that can't perform TLS_INIT_TP, even if no TLS is ever used. Trying to do it lazily is too hairy to try when there could be multiple threads (from a non-TLS-using libpthread). */ - if (!TLS_INIT_TP_EXPENSIVE || GL(dl_tls_max_dtv_idx) > 0) - { - struct link_map *l; - size_t nelem; - struct dtv_slotinfo *slotinfo; - - /* Number of elements in the static TLS block. */ - GL(dl_tls_static_nelem) = GL(dl_tls_max_dtv_idx); - - /* Allocate the array which contains the information about the - dtv slots. We allocate a few entries more than needed to - avoid the need for reallocation. */ - nelem = GL(dl_tls_max_dtv_idx) + 1 + TLS_SLOTINFO_SURPLUS; - - /* Allocate. */ - GL(dl_tls_dtv_slotinfo_list) = (struct dtv_slotinfo_list *) - malloc (sizeof (struct dtv_slotinfo_list) - + nelem * sizeof (struct dtv_slotinfo)); - /* No need to check the return value. If memory allocation failed - the program would have been terminated. */ - - slotinfo = memset (GL(dl_tls_dtv_slotinfo_list)->slotinfo, '\0', - nelem * sizeof (struct dtv_slotinfo)); - GL(dl_tls_dtv_slotinfo_list)->len = nelem; - GL(dl_tls_dtv_slotinfo_list)->next = NULL; - - /* Fill in the information from the loaded modules. */ - for (l = main_map, i = 0; l != NULL; l = l->l_next) - if (l->l_tls_blocksize != 0) - /* This is a module with TLS data. Store the map reference. - The generation counter is zero. */ - slotinfo[++i].map = l; - assert (i == GL(dl_tls_max_dtv_idx)); - - /* Compute the TLS offsets for the various blocks. */ - _dl_determine_tlsoffset (); - - /* Construct the static TLS block and the dtv for the initial - thread. For some platforms this will include allocating memory - for the thread descriptor. The memory for the TLS block will - never be freed. It should be allocated accordingly. The dtv - array can be changed if dynamic loading requires it. */ - tcbp = _dl_allocate_tls_storage (); - if (tcbp == NULL) - _dl_fatal_printf ("\ -cannot allocate TLS data structures for initial thread"); + bool was_tls_init_tp_called = tls_init_tp_called; + if (tcbp == NULL) + tcbp = init_tls (); +#endif - /* Store for detection of the special case by __tls_get_addr - so it knows not to pass this dtv to the normal realloc. */ - GL(dl_initial_dtv) = GET_DTV (tcbp); - } + /* Set up the stack checker's canary. */ + uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (); +#ifdef THREAD_SET_STACK_GUARD + THREAD_SET_STACK_GUARD (stack_chk_guard); +#else + __stack_chk_guard = stack_chk_guard; #endif + /* Set up the pointer guard as well, if necessary. */ + if (GLRO(dl_pointer_guard)) + { + // XXX If it is cheap, we should use a separate value. + uintptr_t pointer_chk_guard = stack_chk_guard; +#ifndef HP_TIMING_NONAVAIL + hp_timing_t now; + HP_TIMING_NOW (now); + pointer_chk_guard ^= now; +#endif +#ifdef THREAD_SET_POINTER_GUARD + THREAD_SET_POINTER_GUARD (pointer_chk_guard); +#endif + __pointer_chk_guard_local = pointer_chk_guard; + } + if (__builtin_expect (mode, normal) != normal) { /* We were run just to list the shared libraries. It is @@ -1618,7 +1979,7 @@ cannot allocate TLS data structures for initial thread"); } else { - /* If LD_WARN is set warn about undefined symbols. */ + /* If LD_WARN is set, warn about undefined symbols. */ if (GLRO(dl_lazy) >= 0 && GLRO(dl_verbose)) { /* We have to do symbol dependency testing. */ @@ -1628,7 +1989,7 @@ cannot allocate TLS data structures for initial thread"); args.lazy = GLRO(dl_lazy); l = main_map; - while (l->l_next) + while (l->l_next != NULL) l = l->l_next; do { @@ -1639,14 +2000,18 @@ cannot allocate TLS data structures for initial thread"); &args); } l = l->l_prev; - } while (l); + } + while (l != NULL); if ((GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) - && GL(dl_rtld_map).l_opencount > 1) - _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, - 0, 0); - } - + && rtld_multiple_ref) + { + /* Mark the link map as not yet relocated again. */ + GL(dl_rtld_map).l_relocated = 0; + _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, + 0, 0); + } + } #define VERNEEDTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (DT_VERNEED)) if (version_info) { @@ -1724,7 +2089,8 @@ cannot allocate TLS data structures for initial thread"); } if (main_map->l_info[ADDRIDX (DT_GNU_LIBLIST)] - && ! __builtin_expect (GLRO(dl_profile) != NULL, 0)) + && ! __builtin_expect (GLRO(dl_profile) != NULL, 0) + && ! __builtin_expect (GLRO(dl_dynamic_weak), 0)) { ElfW(Lib) *liblist, *liblistend; struct link_map **r_list, **r_listend, *l; @@ -1777,35 +2143,8 @@ cannot allocate TLS data structures for initial thread"); } - /* Initialize _r_debug. */ - struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr); - { - struct link_map *l = main_map; - -#ifdef ELF_MACHINE_DEBUG_SETUP - - /* Some machines (e.g. MIPS) don't use DT_DEBUG in this way. */ - - ELF_MACHINE_DEBUG_SETUP (l, r); - ELF_MACHINE_DEBUG_SETUP (&GL(dl_rtld_map), r); - -#else - - if (l->l_info[DT_DEBUG] != NULL) - /* There is a DT_DEBUG entry in the dynamic section. Fill it in - with the run-time address of the r_debug structure */ - l->l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r; - - /* Fill in the pointer in the dynamic linker's own dynamic section, in - case you run gdb on the dynamic linker directly. */ - if (GL(dl_rtld_map).l_info[DT_DEBUG] != NULL) - GL(dl_rtld_map).l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r; -#endif - } - /* Now set up the variable which helps the assembler startup code. */ GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist = &main_map->l_searchlist; - GL(dl_ns)[LM_ID_BASE]._ns_global_scope[0] = &main_map->l_searchlist; /* Save the information about the original global scope list since we need it in the memory handling later. */ @@ -1813,8 +2152,6 @@ cannot allocate TLS data structures for initial thread"); if (prelinked) { - struct link_map *l; - if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL) { ElfW(Rela) *conflict, *conflictend; @@ -1837,11 +2174,17 @@ cannot allocate TLS data structures for initial thread"); /* Mark all the objects so we know they have been already relocated. */ - for (l = main_map; l != NULL; l = l->l_next) + for (struct link_map *l = main_map; l != NULL; l = l->l_next) { l->l_relocated = 1; if (l->l_relro_size) _dl_protect_relro (l); + +#ifdef USE_TLS + /* Add object to slot information data if necessasy. */ + if (l->l_tls_blocksize != 0 && tls_init_tp_called) + _dl_add_to_slotinfo (l); +#endif } _dl_sysdep_start_cleanup (); @@ -1857,18 +2200,16 @@ cannot allocate TLS data structures for initial thread"); the dynamic linker out of order because it has no copy relocs (we know that because it is self-contained). */ - struct link_map *l; int consider_profiling = GLRO(dl_profile) != NULL; #ifndef HP_TIMING_NONAVAIL hp_timing_t start; hp_timing_t stop; - hp_timing_t add; #endif /* If we are profiling we also must do lazy reloaction. */ GLRO(dl_lazy) |= consider_profiling; - l = main_map; + struct link_map *l = main_map; while (l->l_next) l = l->l_next; @@ -1890,6 +2231,12 @@ cannot allocate TLS data structures for initial thread"); _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy), consider_profiling); +#ifdef USE_TLS + /* Add object to slot information data if necessasy. */ + if (l->l_tls_blocksize != 0 && tls_init_tp_called) + _dl_add_to_slotinfo (l); +#endif + l = l->l_prev; } while (l); @@ -1911,17 +2258,6 @@ cannot allocate TLS data structures for initial thread"); if (__builtin_expect (GL(dl_profile_map) != NULL, 0)) /* We must prepare the profiling. */ _dl_start_profile (); - - if (GL(dl_rtld_map).l_opencount > 1) - { - /* There was an explicit ref to the dynamic linker as a shared lib. - Re-relocate ourselves with user-controlled symbol definitions. */ - HP_TIMING_NOW (start); - _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0); - HP_TIMING_NOW (stop); - HP_TIMING_DIFF (add, start, stop); - HP_TIMING_ACCUM_NT (relocate_time, add); - } } #ifndef NONTLS_INIT_TP @@ -1929,25 +2265,75 @@ cannot allocate TLS data structures for initial thread"); #endif #ifdef USE_TLS - if (GL(dl_tls_max_dtv_idx) > 0 || USE___THREAD || !TLS_INIT_TP_EXPENSIVE) - { - /* Now that we have completed relocation, the initializer data - for the TLS blocks has its final values and we can copy them - into the main thread's TLS area, which we allocated above. */ - _dl_allocate_tls_init (tcbp); + if (!was_tls_init_tp_called && GL(dl_tls_max_dtv_idx) > 0) + ++GL(dl_tls_generation); + + /* Now that we have completed relocation, the initializer data + for the TLS blocks has its final values and we can copy them + into the main thread's TLS area, which we allocated above. */ + _dl_allocate_tls_init (tcbp); - /* And finally install it for the main thread. If ld.so itself uses - TLS we know the thread pointer was initialized earlier. */ + /* And finally install it for the main thread. If ld.so itself uses + TLS we know the thread pointer was initialized earlier. */ + if (! tls_init_tp_called) + { const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD); if (__builtin_expect (lossage != NULL, 0)) - _dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage); + _dl_fatal_printf ("cannot set up thread-local storage: %s\n", + lossage); } - else +#else + NONTLS_INIT_TP; #endif - NONTLS_INIT_TP; - /* Notify the debugger that all objects are now mapped in. */ - r->r_state = RT_ADD; + if (! prelinked && rtld_multiple_ref) + { + /* There was an explicit ref to the dynamic linker as a shared lib. + Re-relocate ourselves with user-controlled symbol definitions. + + We must do this after TLS initialization in case after this + re-relocation, we might call a user-supplied function + (e.g. calloc from _dl_relocate_object) that uses TLS data. */ + +#ifndef HP_TIMING_NONAVAIL + hp_timing_t start; + hp_timing_t stop; + hp_timing_t add; +#endif + + HP_TIMING_NOW (start); + /* Mark the link map as not yet relocated again. */ + GL(dl_rtld_map).l_relocated = 0; + _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0); + HP_TIMING_NOW (stop); + HP_TIMING_DIFF (add, start, stop); + HP_TIMING_ACCUM_NT (relocate_time, add); + } + +#ifdef SHARED + /* Auditing checkpoint: we have added all objects. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT); + + afct = afct->next; + } + } + } +#endif + + /* Notify the debugger all new objects are now ready to go. We must re-get + the address since by now the variable might be in another object. */ + r = _dl_debug_initialize (0, LM_ID_BASE); + r->r_state = RT_CONSISTENT; _dl_debug_state (); #ifndef MAP_COPY @@ -2079,6 +2465,32 @@ a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n"); } } +static void +process_dl_audit (char *str) +{ + /* The parameter is a colon separated list of DSO names. */ + char *p; + + while ((p = (strsep) (&str, ":")) != NULL) + if (p[0] != '\0' + && (__builtin_expect (! INTUSE(__libc_enable_secure), 1) + || strchr (p, '/') == NULL)) + { + /* This is using the local malloc, not the system malloc. The + memory can never be freed. */ + struct audit_list *newp = malloc (sizeof (*newp)); + newp->name = p; + + if (audit_list == NULL) + audit_list = newp->next = newp; + else + { + newp->next = audit_list->next; + audit_list = audit_list->next = newp; + } + } +} + /* Process all environments variables the dynamic linker must recognize. Since all of them start with `LD_' we are a bit smarter while finding all the entries. */ @@ -2121,7 +2533,12 @@ process_envvars (enum mode *modep) case 5: /* Debugging of the dynamic linker? */ if (memcmp (envline, "DEBUG", 5) == 0) - process_dl_debug (&envline[6]); + { + process_dl_debug (&envline[6]); + break; + } + if (memcmp (envline, "AUDIT", 5) == 0) + process_dl_audit (&envline[6]); break; case 7: @@ -2205,7 +2622,13 @@ process_envvars (enum mode *modep) #endif if (!INTUSE(__libc_enable_secure) && memcmp (envline, "USE_LOAD_BIAS", 13) == 0) - GLRO(dl_use_load_bias) = envline[14] == '1' ? -1 : 0; + { + GLRO(dl_use_load_bias) = envline[14] == '1' ? -1 : 0; + break; + } + + if (memcmp (envline, "POINTER_GUARD", 13) == 0) + GLRO(dl_pointer_guard) = envline[14] != '0'; break; case 14: diff --git a/elf/sofini.c b/elf/sofini.c index 16e77e7..5e06f0c 100644 --- a/elf/sofini.c +++ b/elf/sofini.c @@ -8,12 +8,10 @@ static void (*const __DTOR_END__[1]) (void) __attribute__ ((used, section (".dtors"))) = { 0 }; -#ifdef HAVE_DWARF2_UNWIND_INFO /* Terminate the frame unwind info section with a 4byte 0 as a sentinel; this would be the 'length' field in a real FDE. */ typedef unsigned int ui32 __attribute__ ((mode (SI))); -static ui32 __FRAME_END__[1] - __attribute__ ((used, section (".eh_frame"))) - = { 0 }; -#endif +static const ui32 __FRAME_END__[1] + __attribute__ ((used, section (".eh_frame"))) + = { 0 }; diff --git a/elf/soinit.c b/elf/soinit.c index 2015ea2..c0a881e 100644 --- a/elf/soinit.c +++ b/elf/soinit.c @@ -6,10 +6,6 @@ #include <libc-internal.h> #include <stdlib.h> -#ifdef HAVE_DWARF2_UNWIND_INFO_STATIC -# include <gccframe.h> -#endif - static void (*const __CTOR_LIST__[1]) (void) __attribute__ ((section (".ctors"))) = { (void (*) (void)) -1 }; @@ -24,21 +20,9 @@ run_hooks (void (*const list[]) (void)) (**list) (); } -#ifdef HAVE_DWARF2_UNWIND_INFO -static char __EH_FRAME_BEGIN__[] - __attribute__ ((section (".eh_frame"))) - = { }; -# ifdef HAVE_DWARF2_UNWIND_INFO_STATIC -extern void __register_frame_info (const void *, struct object *); -extern void __register_frame_info_bases (const void *, struct object *, - void *, void *); -extern void __deregister_frame_info (const void *); -extern void __deregister_frame_info_bases (const void *); -# else -extern void __register_frame (const void *); -extern void __deregister_frame (const void *); -# endif -#endif +static const char __EH_FRAME_BEGIN__[] + __attribute__ ((used, section (".eh_frame"))) + = { }; /* This function will be called from _init in init-first.c. */ void @@ -46,62 +30,17 @@ __libc_global_ctors (void) { /* Call constructor functions. */ run_hooks (__CTOR_LIST__); - -#ifdef HAVE_DWARF2_UNWIND_INFO -# ifdef HAVE_DWARF2_UNWIND_INFO_STATIC - { - static struct object ob; -# if defined CRT_GET_RFIB_TEXT || defined CRT_GET_RFIB_DATA - void *tbase, *dbase; - -# ifdef CRT_GET_RFIB_TEXT - CRT_GET_RFIB_TEXT (tbase); -# else - tbase = NULL; -# endif -# ifdef CRT_GET_RFIB_DATA - CRT_GET_RFIB_DATA (dbase); -# else - dbase = NULL; -# endif - __register_frame_info_bases (__EH_FRAME_BEGIN__, &ob, tbase, dbase); -# else - __register_frame_info (__EH_FRAME_BEGIN__, &ob); -# endif - } -# else - __register_frame (__EH_FRAME_BEGIN__); -# endif -#endif } /* This function becomes the DT_FINI termination function for the C library. */ -#ifndef HAVE_INITFINI_ARRAY -void _fini (void) __attribute__ ((section (".fini"))); /* Just for kicks. */ -void -_fini (void) -#else void __libc_fini (void) -#endif { /* Call destructor functions. */ run_hooks (__DTOR_LIST__); -#ifdef HAVE_DWARF2_UNWIND_INFO -# ifdef HAVE_DWARF2_UNWIND_INFO_STATIC -# if defined CRT_GET_RFIB_TEXT || defined CRT_GET_RFIB_DATA - __deregister_frame_info_bases (__EH_FRAME_BEGIN__); -# else - __deregister_frame_info (__EH_FRAME_BEGIN__); -# endif -# else - __deregister_frame (__EH_FRAME_BEGIN__); -# endif -#endif } -#ifdef HAVE_INITFINI_ARRAY + void (*_fini_ptr) (void) __attribute__ ((section (".fini_array"))) = &__libc_fini; -#endif diff --git a/elf/sprof.c b/elf/sprof.c index afe3955..e53a7ba 100644 --- a/elf/sprof.c +++ b/elf/sprof.c @@ -1,5 +1,5 @@ /* Read and display shared object profiling data. - Copyright (C) 1997-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1997-2004, 2005, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. @@ -357,7 +357,7 @@ Copyright (C) %s Free Software Foundation, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ "), - "2004"); + "2006"); fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); } diff --git a/elf/stackguard-macros.h b/elf/stackguard-macros.h new file mode 100644 index 0000000..97db8bc --- /dev/null +++ b/elf/stackguard-macros.h @@ -0,0 +1,33 @@ +#include <stdint.h> + +#ifdef __i386__ +# define STACK_CHK_GUARD \ + ({ uintptr_t x; asm ("movl %%gs:0x14, %0" : "=r" (x)); x; }) +#elif defined __x86_64__ +# define STACK_CHK_GUARD \ + ({ uintptr_t x; asm ("movq %%fs:0x28, %0" : "=r" (x)); x; }) +#elif defined __powerpc64__ +# define STACK_CHK_GUARD \ + ({ uintptr_t x; asm ("ld %0,-28688(13)" : "=r" (x)); x; }) +#elif defined __powerpc__ +# define STACK_CHK_GUARD \ + ({ uintptr_t x; asm ("lwz %0,-28680(2)" : "=r" (x)); x; }) +#elif defined __sparc__ && defined __arch64__ +# define STACK_CHK_GUARD \ + ({ uintptr_t x; asm ("ldx [%%g7+0x28], %0" : "=r" (x)); x; }) +#elif defined __sparc__ +# define STACK_CHK_GUARD \ + ({ uintptr_t x; asm ("ld [%%g7+0x14], %0" : "=r" (x)); x; }) +#elif defined __s390x__ +# define STACK_CHK_GUARD \ + ({ uintptr_t x; asm ("ear %0,%%a0; sllg %0,%0,32; ear %0,%%a1; lg %0,0x28(%0)" : "=a" (x)); x; }) +#elif defined __s390__ +# define STACK_CHK_GUARD \ + ({ uintptr_t x; asm ("ear %0,%%a0; l %0,0x14(%0)" : "=a" (x)); x; }) +#elif defined __ia64__ +# define STACK_CHK_GUARD \ + ({ uintptr_t x; asm ("adds %0 = -8, r13;; ld8 %0 = [%0]" : "=r" (x)); x; }) +#else +extern uintptr_t __stack_chk_guard; +# define STACK_CHK_GUARD __stack_chk_guard +#endif diff --git a/elf/testobj2.c b/elf/testobj2.c index 6514c56..7e4b610 100644 --- a/elf/testobj2.c +++ b/elf/testobj2.c @@ -1,5 +1,6 @@ #include <dlfcn.h> #include <stdlib.h> +#include <stdio.h> #include "testobj.h" @@ -23,3 +24,9 @@ preload (int a) return fp (a) + 10; return 10; } + +void +p (void) +{ + puts ("hello world"); +} diff --git a/elf/tls-macros.h b/elf/tls-macros.h index bed2e14..37cbe75 100644 --- a/elf/tls-macros.h +++ b/elf/tls-macros.h @@ -16,6 +16,7 @@ /* XXX Until we get compiler support we don't need declarations. */ #define VAR_INT_DECL(x) +#include_next <tls-macros.h> /* XXX Each architecture must have its own asm for now. */ #ifdef __i386__ @@ -440,6 +441,74 @@ register void *__gp __asm__("$29"); "o5", "o7", "cc"); \ __o0; }) +#elif defined __sparc__ && defined __arch64__ + +# define TLS_LE(x) \ + ({ int *__l; \ + asm ("sethi %%tle_hix22(" #x "), %0" : "=r" (__l)); \ + asm ("xor %1, %%tle_lox10(" #x "), %0" : "=r" (__l) : "r" (__l)); \ + asm ("add %%g7, %1, %0" : "=r" (__l) : "r" (__l)); \ + __l; }) + +# ifdef __PIC__ +# define TLS_LOAD_PIC \ + ({ long pc, got; \ + asm ("sethi %%hi(_GLOBAL_OFFSET_TABLE_-4), %1\n\t" \ + "rd %%pc, %0\n\t" \ + "add %1, %%lo(_GLOBAL_OFFSET_TABLE_+4), %1\n\t" \ + "add %1, %0, %1\n\t" \ + : "=r" (pc), "=r" (got)); \ + got; }) +# else +# define TLS_LOAD_PIC \ + ({ long got; \ + asm (".hidden _GLOBAL_OFFSET_TABLE_\n\t" \ + "sethi %%hi(_GLOBAL_OFFSET_TABLE_), %0\n\t" \ + "or %0, %%lo(_GLOBAL_OFFSET_TABLE_), %0" \ + : "=r" (got)); \ + got; }) +# endif + +# define TLS_IE(x) \ + ({ int *__l; \ + asm ("sethi %%tie_hi22(" #x "), %0" : "=r" (__l)); \ + asm ("add %1, %%tie_lo10(" #x "), %0" : "=r" (__l) : "r" (__l)); \ + asm ("ldx [%1 + %2], %0, %%tie_ldx(" #x ")" \ + : "=r" (__l) : "r" (TLS_LOAD_PIC), "r" (__l)); \ + asm ("add %%g7, %1, %0, %%tie_add(" #x ")" : "=r" (__l) : "r" (__l)); \ + __l; }) + +# define TLS_LD(x) \ + ({ int *__l; register void *__o0 asm ("%o0"); \ + long __o; \ + asm ("sethi %%tldm_hi22(" #x "), %0" : "=r" (__l)); \ + asm ("add %1, %%tldm_lo10(" #x "), %0" : "=r" (__l) : "r" (__l)); \ + asm ("add %1, %2, %0, %%tldm_add(" #x ")" \ + : "=r" (__o0) : "r" (TLS_LOAD_PIC), "r" (__l)); \ + asm ("call __tls_get_addr, %%tgd_call(" #x ")\n\t" \ + " nop" \ + : "=r" (__o0) : "0" (__o0) \ + : "g1", "g2", "g3", "g4", "g5", "g6", "o1", "o2", "o3", "o4", \ + "o5", "o7", "cc"); \ + asm ("sethi %%tldo_hix22(" #x "), %0" : "=r" (__o)); \ + asm ("xor %1, %%tldo_lox10(" #x "), %0" : "=r" (__o) : "r" (__o)); \ + asm ("add %1, %2, %0, %%tldo_add(" #x ")" : "=r" (__l) \ + : "r" (__o0), "r" (__o)); \ + __l; }) + +# define TLS_GD(x) \ + ({ int *__l; register void *__o0 asm ("%o0"); \ + asm ("sethi %%tgd_hi22(" #x "), %0" : "=r" (__l)); \ + asm ("add %1, %%tgd_lo10(" #x "), %0" : "=r" (__l) : "r" (__l)); \ + asm ("add %1, %2, %0, %%tgd_add(" #x ")" \ + : "=r" (__o0) : "r" (TLS_LOAD_PIC), "r" (__l)); \ + asm ("call __tls_get_addr, %%tgd_call(" #x ")\n\t" \ + " nop" \ + : "=r" (__o0) : "0" (__o0) \ + : "g1", "g2", "g3", "g4", "g5", "g6", "o1", "o2", "o3", "o4", \ + "o5", "o7", "cc"); \ + __o0; }) + #elif defined __s390x__ # define TLS_LE(x) \ @@ -634,6 +703,8 @@ register void *__gp __asm__("$29"); #elif defined __powerpc__ && !defined __powerpc64__ +#include "config.h" + # define __TLS_CALL_CLOBBERS \ "0", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", \ "lr", "ctr", "cr0", "cr1", "cr5", "cr6", "cr7" @@ -646,7 +717,20 @@ register void *__gp __asm__("$29"); __result; }) /* PowerPC32 Initial Exec TLS access. */ -# define TLS_IE(x) \ +# ifdef HAVE_ASM_PPC_REL16 +# define TLS_IE(x) \ + ({ int *__result; \ + asm ("bcl 20,31,1f\n1:\t" \ + "mflr %0\n\t" \ + "addis %0,%0,_GLOBAL_OFFSET_TABLE_-1b@ha\n\t" \ + "addi %0,%0,_GLOBAL_OFFSET_TABLE_-1b@l\n\t" \ + "lwz %0," #x "@got@tprel(%0)\n\t" \ + "add %0,%0," #x "@tls" \ + : "=b" (__result) : \ + : "lr"); \ + __result; }) +# else +# define TLS_IE(x) \ ({ int *__result; \ asm ("bl _GLOBAL_OFFSET_TABLE_@local-4\n\t" \ "mflr %0\n\t" \ @@ -655,9 +739,24 @@ register void *__gp __asm__("$29"); : "=b" (__result) : \ : "lr"); \ __result; }) +# endif /* PowerPC32 Local Dynamic TLS access. */ -# define TLS_LD(x) \ +# ifdef HAVE_ASM_PPC_REL16 +# define TLS_LD(x) \ + ({ int *__result; \ + asm ("bcl 20,31,1f\n1:\t" \ + "mflr 3\n\t" \ + "addis 3,3,_GLOBAL_OFFSET_TABLE_-1b@ha\n\t" \ + "addi 3,3,_GLOBAL_OFFSET_TABLE_-1b@l\n\t" \ + "addi 3,3," #x "@got@tlsld\n\t" \ + "bl __tls_get_addr@plt\n\t" \ + "addi %0,3," #x "@dtprel" \ + : "=r" (__result) : \ + : __TLS_CALL_CLOBBERS); \ + __result; }) +# else +# define TLS_LD(x) \ ({ int *__result; \ asm ("bl _GLOBAL_OFFSET_TABLE_@local-4\n\t" \ "mflr 3\n\t" \ @@ -667,9 +766,23 @@ register void *__gp __asm__("$29"); : "=r" (__result) : \ : __TLS_CALL_CLOBBERS); \ __result; }) +# endif /* PowerPC32 General Dynamic TLS access. */ -# define TLS_GD(x) \ +# ifdef HAVE_ASM_PPC_REL16 +# define TLS_GD(x) \ + ({ register int *__result __asm__ ("r3"); \ + asm ("bcl 20,31,1f\n1:\t" \ + "mflr 3\n\t" \ + "addis 3,3,_GLOBAL_OFFSET_TABLE_-1b@ha\n\t" \ + "addi 3,3,_GLOBAL_OFFSET_TABLE_-1b@l\n\t" \ + "addi 3,3," #x "@got@tlsgd\n\t" \ + "bl __tls_get_addr@plt" \ + : : \ + : __TLS_CALL_CLOBBERS); \ + __result; }) +# else +# define TLS_GD(x) \ ({ register int *__result __asm__ ("r3"); \ asm ("bl _GLOBAL_OFFSET_TABLE_@local-4\n\t" \ "mflr 3\n\t" \ @@ -678,6 +791,7 @@ register void *__gp __asm__("$29"); : : \ : __TLS_CALL_CLOBBERS); \ __result; }) +# endif #elif defined __powerpc__ && defined __powerpc64__ @@ -731,6 +845,7 @@ register void *__gp __asm__("$29"); __result; \ }) -#else +#elif !defined TLS_LE || !defined TLS_IE \ + || !defined TLS_LD || !defined TLS_GD # error "No support for this architecture so far." #endif diff --git a/elf/tst-addr1.c b/elf/tst-addr1.c new file mode 100644 index 0000000..637906e --- /dev/null +++ b/elf/tst-addr1.c @@ -0,0 +1,26 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> + +static int +do_test (void) +{ + Dl_info i; + if (dladdr (&printf, &i) == 0) + { + puts ("not found"); + return 1; + } + printf ("found symbol %s in %s\n", i.dli_sname, i.dli_fname); + return i.dli_sname == NULL + || (strcmp (i.dli_sname, "printf") != 0 + /* On architectures which create PIC code by default + &printf may resolve to an address in libc.so + rather than in the binary. printf and _IO_printf + are aliased and which one comes first in the + hash table is up to the linker. */ + && strcmp (i.dli_sname, "_IO_printf") != 0); +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/elf/tst-align2.c b/elf/tst-align2.c new file mode 100644 index 0000000..4fc0330 --- /dev/null +++ b/elf/tst-align2.c @@ -0,0 +1,157 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2005. + + 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <tst-stack-align.h> +#include <unistd.h> + +static int res, fds[2], result; +static bool test_destructors; + +extern void in_dso (int *, bool *, int *); + +static void __attribute__ ((constructor)) con (void) +{ + res = TEST_STACK_ALIGN () ? -1 : 1; +} + +static void __attribute__ ((destructor)) des (void) +{ + if (!test_destructors) + return; + + char c = TEST_STACK_ALIGN () ? 'B' : 'A'; + write (fds[1], &c, 1); +} + +static int +do_test (void) +{ + if (!res) + { + puts ("binary's constructor has not been run"); + result = 1; + } + else if (res != 1) + { + puts ("binary's constructor has been run without sufficient alignment"); + result = 1; + } + + if (TEST_STACK_ALIGN ()) + { + puts ("insufficient stack alignment in do_test"); + result = 1; + } + + in_dso (&result, &test_destructors, &fds[1]); + + if (pipe (fds) < 0) + { + printf ("couldn't create pipe: %m\n"); + return 1; + } + + pid_t pid = fork (); + if (pid < 0) + { + printf ("fork failed: %m\n"); + return 1; + } + + if (!pid) + { + close (fds[0]); + test_destructors = true; + exit (0); + } + + close (fds[1]); + + unsigned char c; + ssize_t len; + int des_seen = 0, dso_des_seen = 0; + while ((len = TEMP_FAILURE_RETRY (read (fds[0], &c, 1))) > 0) + { + switch (c) + { + case 'B': + puts ("insufficient alignment in binary's destructor"); + result = 1; + /* FALLTHROUGH */ + case 'A': + des_seen++; + break; + case 'D': + puts ("insufficient alignment in DSO destructor"); + result = 1; + /* FALLTHROUGH */ + case 'C': + dso_des_seen++; + break; + default: + printf ("unexpected character %x read from pipe", c); + result = 1; + break; + } + } + + close (fds[0]); + + if (des_seen != 1) + { + printf ("binary destructor run %d times instead of once\n", des_seen); + result = 1; + } + + if (dso_des_seen != 1) + { + printf ("DSO destructor run %d times instead of once\n", dso_des_seen); + result = 1; + } + + int status; + pid_t termpid; + termpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)); + if (termpid == -1) + { + printf ("waitpid failed: %m\n"); + result = 1; + } + else if (termpid != pid) + { + printf ("waitpid returned %ld != %ld\n", + (long int) termpid, (long int) pid); + result = 1; + } + else if (!WIFEXITED (status) || WEXITSTATUS (status)) + { + puts ("child hasn't exited with exit status 0"); + result = 1; + } + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/elf/tst-alignmod2.c b/elf/tst-alignmod2.c new file mode 100644 index 0000000..21dcc53 --- /dev/null +++ b/elf/tst-alignmod2.c @@ -0,0 +1,60 @@ +/* Copyright (C) 2003, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2003. + + 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <stdbool.h> +#include <stdio.h> +#include <tst-stack-align.h> +#include <unistd.h> + +static int res, *fdp; +static bool *test_destructorsp; + +static void __attribute__((constructor)) +con (void) +{ + res = TEST_STACK_ALIGN () ? -1 : 1; +} + +void +in_dso (int *result, bool *test_destructors, int *fd) +{ + if (!res) + { + puts ("constructor has not been run"); + *result = 1; + } + else if (res != 1) + { + puts ("constructor has been run without sufficient alignment"); + *result = 1; + } + + test_destructorsp = test_destructors; + fdp = fd; +} + +static void __attribute__((destructor)) +des (void) +{ + if (!test_destructorsp || !*test_destructorsp) + return; + + char c = TEST_STACK_ALIGN () ? 'D' : 'C'; + write (*fdp, &c, 1); +} diff --git a/elf/tst-array1-static.c b/elf/tst-array1-static.c new file mode 100644 index 0000000..21539a4 --- /dev/null +++ b/elf/tst-array1-static.c @@ -0,0 +1 @@ +#include "tst-array1.c" diff --git a/elf/tst-array5-static.c b/elf/tst-array5-static.c new file mode 100644 index 0000000..4ef2aba --- /dev/null +++ b/elf/tst-array5-static.c @@ -0,0 +1 @@ +#include "tst-array5.c" diff --git a/elf/tst-array5-static.exp b/elf/tst-array5-static.exp new file mode 100644 index 0000000..b1dc9e4 --- /dev/null +++ b/elf/tst-array5-static.exp @@ -0,0 +1,2 @@ +preinit array in executable: tst-array5-static +init array in executable: tst-array5-static diff --git a/elf/tst-array5.c b/elf/tst-array5.c new file mode 100644 index 0000000..03a5668 --- /dev/null +++ b/elf/tst-array5.c @@ -0,0 +1,50 @@ +#include <string.h> +#include <unistd.h> + +static void +preinit_0 (int argc __attribute__ ((unused)), char **argv) +{ + char *p = strrchr (argv [0], '/'); + + if (p == NULL) + return; + + p++; + size_t len = strlen (p); + write (STDOUT_FILENO, "preinit array in executable: ", 29); + write (STDOUT_FILENO, p, len); + write (STDOUT_FILENO, "\n", 1); +} + +void (*const preinit_array []) (int, char **) + __attribute__ ((section (".preinit_array"), aligned (sizeof (void *)))) = +{ + &preinit_0, +}; + +static void +init_0 (int argc __attribute__ ((unused)), char **argv) +{ + char *p = strrchr (argv [0], '/'); + + if (p == NULL) + return; + + p++; + size_t len = strlen (p); + write (STDOUT_FILENO, "init array in executable: ", 26); + write (STDOUT_FILENO, p, len); + write (STDOUT_FILENO, "\n", 1); +} + +void (*const init_array []) (int, char **) + __attribute__ ((section (".init_array"), aligned (sizeof (void *)))) = +{ + &init_0, +}; + +int +main (void) +{ + return 0; +} diff --git a/elf/tst-array5.exp b/elf/tst-array5.exp new file mode 100644 index 0000000..28b4909 --- /dev/null +++ b/elf/tst-array5.exp @@ -0,0 +1,3 @@ +preinit array in executable: tst-array5 +init array in DSO: tst-array5 +init array in executable: tst-array5 diff --git a/elf/tst-array5dep.c b/elf/tst-array5dep.c new file mode 100644 index 0000000..570d282 --- /dev/null +++ b/elf/tst-array5dep.c @@ -0,0 +1,23 @@ +#include <string.h> +#include <unistd.h> + +static void +init_0 (int argc __attribute__ ((unused)), char **argv) +{ + char *p = strrchr (argv [0], '/'); + + if (p == NULL) + return; + + p++; + size_t len = strlen (p); + write (STDOUT_FILENO, "init array in DSO: ", 19); + write (STDOUT_FILENO, p, len); + write (STDOUT_FILENO, "\n", 1); +} + +void (*const init_array []) (int, char **) + __attribute__ ((section (".init_array"), aligned (sizeof (void *)))) = +{ + &init_0, +}; diff --git a/elf/tst-audit1.c b/elf/tst-audit1.c new file mode 100644 index 0000000..63656b4 --- /dev/null +++ b/elf/tst-audit1.c @@ -0,0 +1 @@ +#include "../io/pwd.c" diff --git a/elf/tst-audit2.c b/elf/tst-audit2.c new file mode 100644 index 0000000..fd089b6 --- /dev/null +++ b/elf/tst-audit2.c @@ -0,0 +1,50 @@ +/* Test case for early TLS initialization in dynamic linker. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if HAVE___THREAD +# define MAGIC1 0xabcdef72 +# define MAGIC2 0xd8675309 +static __thread unsigned int magic[] = { MAGIC1, MAGIC2 }; +#endif + +#undef calloc + +/* This calloc definition will be called by the dynamic linker itself. + We test that it has initialized our TLS block by the time it does so. */ + +void * +calloc (size_t n, size_t m) +{ +#if HAVE___THREAD + if (magic[0] != MAGIC1 || magic[1] != MAGIC2) + { + printf ("{%x, %x} != {%x, %x}\n", magic[0], magic[1], MAGIC1, MAGIC2); + abort (); + } + magic[0] = MAGIC2; + magic[1] = MAGIC1; +#endif + + n *= m; + void *ptr = malloc (n); + if (ptr != NULL) + memset (ptr, '\0', n); + return ptr; +} + +int +main (void) +{ +#if HAVE___THREAD + if (magic[1] != MAGIC1 || magic[0] != MAGIC2) + { + printf ("{%x, %x} != {%x, %x}\n", magic[0], magic[1], MAGIC2, MAGIC1); + return 1; + } +#endif + + return 0; +} diff --git a/elf/tst-auditmod1.c b/elf/tst-auditmod1.c new file mode 100644 index 0000000..2d39df2 --- /dev/null +++ b/elf/tst-auditmod1.c @@ -0,0 +1,200 @@ +#include <dlfcn.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <bits/wordsize.h> +#include <gnu/lib-names.h> + + +unsigned int +la_version (unsigned int v) +{ + setlinebuf (stdout); + + printf ("version: %u\n", v); + + char buf[20]; + sprintf (buf, "%u", v); + + return v; +} + +void +la_activity (uintptr_t *cookie, unsigned int flag) +{ + if (flag == LA_ACT_CONSISTENT) + printf ("activity: consistent\n"); + else if (flag == LA_ACT_ADD) + printf ("activity: add\n"); + else if (flag == LA_ACT_DELETE) + printf ("activity: delete\n"); + else + printf ("activity: unknown activity %u\n", flag); +} + +char * +la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag) +{ + char buf[100]; + const char *flagstr; + if (flag == LA_SER_ORIG) + flagstr = "LA_SET_ORIG"; + else if (flag == LA_SER_LIBPATH) + flagstr = "LA_SER_LIBPATH"; + else if (flag == LA_SER_RUNPATH) + flagstr = "LA_SER_RUNPATH"; + else if (flag == LA_SER_CONFIG) + flagstr = "LA_SER_CONFIG"; + else if (flag == LA_SER_DEFAULT) + flagstr = "LA_SER_DEFAULT"; + else if (flag == LA_SER_SECURE) + flagstr = "LA_SER_SECURE"; + else + { + sprintf (buf, "unknown flag %d", flag); + flagstr = buf; + } + printf ("objsearch: %s, %s\n", name, flagstr); + + return (char *) name; +} + +unsigned int +la_objopen (struct link_map *l, Lmid_t lmid, uintptr_t *cookie) +{ + printf ("objopen: %ld, %s\n", lmid, l->l_name); + + return 3; +} + +void +la_preinit (uintptr_t *cookie) +{ + printf ("preinit\n"); +} + +unsigned int +la_objclose (uintptr_t *cookie) +{ + printf ("objclose\n"); + return 0; +} + +uintptr_t +la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, const char *symname) +{ + printf ("symbind32: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n", + symname, (long int) sym->st_value, ndx, *flags); + + return sym->st_value; +} + +uintptr_t +la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, const char *symname) +{ + printf ("symbind64: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n", + symname, (long int) sym->st_value, ndx, *flags); + + return sym->st_value; +} + +#ifdef __i386__ +# define pltenter la_i86_gnu_pltenter +# define pltexit la_i86_gnu_pltexit +# define La_regs La_i86_regs +# define La_retval La_i86_retval +# define int_retval lrv_eax +#elif defined __x86_64__ +# define pltenter la_x86_64_gnu_pltenter +# define pltexit la_x86_64_gnu_pltexit +# define La_regs La_x86_64_regs +# define La_retval La_x86_64_retval +# define int_retval lrv_rax +#elif defined __powerpc__ && __WORDSIZE == 32 +# define pltenter la_ppc32_gnu_pltenter +# define pltexit la_ppc32_gnu_pltexit +# define La_regs La_ppc32_regs +# define La_retval La_ppc32_retval +# define int_retval lrv_r3 +#elif defined __powerpc__ && __WORDSIZE == 64 +# define pltenter la_ppc64_gnu_pltenter +# define pltexit la_ppc64_gnu_pltexit +# define La_regs La_ppc64_regs +# define La_retval La_ppc64_retval +# define int_retval lrv_r3 +#elif defined __sh__ +# define pltenter la_sh_gnu_pltenter +# define pltexit la_sh_gnu_pltexit +# define La_regs La_sh_regs +# define La_retval La_sh_retval +# define int_retval lrv_r0 +#elif defined __alpha__ +# define pltenter la_alpha_gnu_pltenter +# define pltexit la_alpha_gnu_pltexit +# define La_regs La_alpha_regs +# define La_retval La_alpha_retval +# define int_retval lrv_r0 +#elif defined __s390__ && __WORDSIZE == 32 +# define pltenter la_s390_32_gnu_pltenter +# define pltexit la_s390_32_gnu_pltexit +# define La_regs La_s390_32_regs +# define La_retval La_s390_32_retval +# define int_retval lrv_r2 +#elif defined __s390__ && __WORDSIZE == 64 +# define pltenter la_s390_64_gnu_pltenter +# define pltexit la_s390_64_gnu_pltexit +# define La_regs La_s390_64_regs +# define La_retval La_s390_64_retval +# define int_retval lrv_r2 +#elif defined __ia64__ +# define pltenter la_ia64_gnu_pltenter +# define pltexit la_ia64_gnu_pltexit +# define La_regs La_ia64_regs +# define La_retval La_ia64_retval +# define int_retval lrv_r8 +#elif defined __sparc__ && __WORDSIZE == 32 +# define pltenter la_sparc32_gnu_pltenter +# define pltexit la_sparc32_gnu_pltexit +# define La_regs La_sparc32_regs +# define La_retval La_sparc32_retval +# define int_retval lrv_reg[0] +#elif defined __sparc__ && __WORDSIZE == 64 +# define pltenter la_sparc64_gnu_pltenter +# define pltexit la_sparc64_gnu_pltexit +# define La_regs La_sparc64_regs +# define La_retval La_sparc64_retval +# define int_retval lrv_reg[0] +#endif + +#include <tst-audit.h> +#if (!defined (pltenter) || !defined (pltexit) || !defined (La_regs) \ + || !defined (La_retval) || !defined (int_retval)) +# error "architecture specific code needed in sysdeps/CPU/tst-audit.h or here" +#endif + + +ElfW(Addr) +pltenter (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, La_regs *regs, unsigned int *flags, + const char *symname, long int *framesizep) +{ + printf ("pltenter: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n", + symname, (long int) sym->st_value, ndx, *flags); + + return sym->st_value; +} + +unsigned int +pltexit (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, const La_regs *inregs, La_retval *outregs, + const char *symname) +{ + printf ("pltexit: symname=%s, st_value=%#lx, ndx=%u, retval=%tu\n", + symname, (long int) sym->st_value, ndx, outregs->int_retval); + + return 0; +} diff --git a/elf/tst-global1.c b/elf/tst-global1.c new file mode 100644 index 0000000..1611b51 --- /dev/null +++ b/elf/tst-global1.c @@ -0,0 +1,36 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +main (void) +{ + void *h1 = dlopen ("$ORIGIN/testobj6.so", RTLD_GLOBAL|RTLD_LAZY); + if (h1 == NULL) + { + puts ("cannot open testobj6"); + return 1; + } + + void *h2 = dlopen ("$ORIGIN/testobj2.so", + RTLD_GLOBAL|RTLD_DEEPBIND|RTLD_LAZY); + if (h2 == NULL) + { + puts ("cannot open testobj2"); + return 1; + } + + dlclose (h1); + + void (*f) (void) = dlsym (h2, "p"); + if (f == NULL) + { + puts ("cannot find p"); + return 1; + } + + f (); + + dlclose (h2); + + return 0; +} diff --git a/elf/tst-leaks1.c b/elf/tst-leaks1.c new file mode 100644 index 0000000..36e4aee --- /dev/null +++ b/elf/tst-leaks1.c @@ -0,0 +1,25 @@ +#include <stdio.h> +#include <dlfcn.h> +#include <mcheck.h> +#include <stdlib.h> + +int +main (void) +{ + mtrace (); + + int ret = 0; + for (int i = 0; i < 10; i++) + { + void *h = dlopen (i < 5 ? "./tst-leaks1.c" + : "$ORIGIN/tst-leaks1.o", RTLD_LAZY); + if (h != NULL) + { + puts ("dlopen unexpectedly succeeded"); + ret = 1; + dlclose (h); + } + } + + return ret; +} diff --git a/elf/tst-stackguard1-static.c b/elf/tst-stackguard1-static.c new file mode 100644 index 0000000..db1e215 --- /dev/null +++ b/elf/tst-stackguard1-static.c @@ -0,0 +1 @@ +#include "tst-stackguard1.c" diff --git a/elf/tst-stackguard1.c b/elf/tst-stackguard1.c new file mode 100644 index 0000000..50739e5 --- /dev/null +++ b/elf/tst-stackguard1.c @@ -0,0 +1,200 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2005. + + 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> +#include <stackguard-macros.h> +#include <unistd.h> + +static const char *command; +static bool child; +static uintptr_t stack_chk_guard_copy; +static bool stack_chk_guard_copy_set; +static int fds[2]; + +static void __attribute__ ((constructor)) +con (void) +{ + stack_chk_guard_copy = STACK_CHK_GUARD; + stack_chk_guard_copy_set = true; +} + +static int +uintptr_t_cmp (const void *a, const void *b) +{ + if (*(uintptr_t *) a < *(uintptr_t *) b) + return 1; + if (*(uintptr_t *) a > *(uintptr_t *) b) + return -1; + return 0; +} + +static int +do_test (void) +{ + if (!stack_chk_guard_copy_set) + { + puts ("constructor has not been run"); + return 1; + } + + if (stack_chk_guard_copy != STACK_CHK_GUARD) + { + puts ("STACK_CHK_GUARD changed between constructor and do_test"); + return 1; + } + + if (child) + { + write (2, &stack_chk_guard_copy, sizeof (stack_chk_guard_copy)); + return 0; + } + + if (command == NULL) + { + puts ("missing --command or --child argument"); + return 1; + } + +#define N 16 + uintptr_t child_stack_chk_guards[N + 1]; + child_stack_chk_guards[N] = stack_chk_guard_copy; + int i; + for (i = 0; i < N; ++i) + { + if (pipe (fds) < 0) + { + printf ("couldn't create pipe: %m\n"); + return 1; + } + + pid_t pid = fork (); + if (pid < 0) + { + printf ("fork failed: %m\n"); + return 1; + } + + if (!pid) + { + if (stack_chk_guard_copy != STACK_CHK_GUARD) + { + puts ("STACK_CHK_GUARD changed after fork"); + exit (1); + } + + close (fds[0]); + close (2); + dup2 (fds[1], 2); + close (fds[1]); + + system (command); + exit (0); + } + + close (fds[1]); + + if (TEMP_FAILURE_RETRY (read (fds[0], &child_stack_chk_guards[i], + sizeof (uintptr_t))) != sizeof (uintptr_t)) + { + puts ("could not read stack_chk_guard value from child"); + return 1; + } + + close (fds[0]); + + pid_t termpid; + int status; + termpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)); + if (termpid == -1) + { + printf ("waitpid failed: %m\n"); + return 1; + } + else if (termpid != pid) + { + printf ("waitpid returned %ld != %ld\n", + (long int) termpid, (long int) pid); + return 1; + } + else if (!WIFEXITED (status) || WEXITSTATUS (status)) + { + puts ("child hasn't exited with exit status 0"); + return 1; + } + } + + qsort (child_stack_chk_guards, N + 1, sizeof (uintptr_t), uintptr_t_cmp); + + uintptr_t default_guard = 0; + unsigned char *p = (unsigned char *) &default_guard; + p[sizeof (uintptr_t) - 1] = 255; + p[sizeof (uintptr_t) - 2] = '\n'; + p[0] = 0; + + /* Test if the stack guard canaries are either randomized, + or equal to the default stack guard canary value. + Even with randomized stack guards it might happen + that the random number generator generates the same + values, but if that happens in more than half from + the 16 runs, something is very wrong. */ + int ndifferences = 0; + int ndefaults = 0; + int npartlyrandomized = 0; + for (i = 0; i < N; ++i) + { + if (child_stack_chk_guards[i] != child_stack_chk_guards[i+1]) + ndifferences++; + else if (child_stack_chk_guards[i] == default_guard) + ndefaults++; + else if (*(char *) &child_stack_chk_guards[i] == 0) + npartlyrandomized++; + } + + printf ("differences %d defaults %d partly randomized %d\n", + ndifferences, ndefaults, npartlyrandomized); + + if ((ndifferences + ndefaults + npartlyrandomized) < 3 * N / 4) + { + puts ("stack guard canaries are not randomized enough"); + puts ("nor equal to the default canary value"); + return 1; + } + + return 0; +} + +#define OPT_COMMAND 10000 +#define OPT_CHILD 10001 +#define CMDLINE_OPTIONS \ + { "command", required_argument, NULL, OPT_COMMAND }, \ + { "child", no_argument, NULL, OPT_CHILD }, +#define CMDLINE_PROCESS \ + case OPT_COMMAND: \ + command = optarg; \ + break; \ + case OPT_CHILD: \ + child = true; \ + break; +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/elf/tst-thrlock.c b/elf/tst-thrlock.c new file mode 100644 index 0000000..71f1fbb --- /dev/null +++ b/elf/tst-thrlock.c @@ -0,0 +1,55 @@ +#include <dlfcn.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <gnu/lib-names.h> + +static void * +tf (void *arg) +{ + void *h = dlopen (LIBM_SO, RTLD_LAZY); + if (h == NULL) + { + printf ("dlopen failed: %s\n", dlerror ()); + exit (1); + } + if (dlsym (h, "sin") == NULL) + { + printf ("dlsym failed: %s\n", dlerror ()); + exit (1); + } + if (dlclose (h) != 0) + { + printf ("dlclose failed: %s\n", dlerror ()); + exit (1); + } + return NULL; +} + +int +main (void) +{ +#define N 10 + pthread_t th[N]; + for (int i = 0; i < N; ++i) + { + int e = pthread_create (&th[i], NULL, tf, NULL); + if (e != 0) + { + printf ("pthread_create failed with %d (%s)\n", e, strerror (e)); + return 1; + } + } + for (int i = 0; i < N; ++i) + { + void *res; + int e = pthread_join (th[i], &res); + if (e != 0 || res != NULL) + { + puts ("thread failed"); + return 1; + } + } + return 0; +} diff --git a/elf/tst-tls-dlinfo.c b/elf/tst-tls-dlinfo.c new file mode 100644 index 0000000..e97b508 --- /dev/null +++ b/elf/tst-tls-dlinfo.c @@ -0,0 +1,92 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <tls.h> + + +#define TEST_FUNCTION do_test () +static int +do_test (void) +{ +#ifdef USE_TLS + static const char modname[] = "tst-tlsmod2.so"; + int result = 0; + int *foop; + int (*fp) (int, int *); + void *h; + + h = dlopen (modname, RTLD_LAZY); + if (h == NULL) + { + printf ("cannot open '%s': %s\n", modname, dlerror ()); + exit (1); + } + + fp = dlsym (h, "in_dso"); + if (fp == NULL) + { + printf ("cannot get symbol 'in_dso': %s\n", dlerror ()); + exit (1); + } + + size_t modid = -1; + if (dlinfo (h, RTLD_DI_TLS_MODID, &modid)) + { + printf ("dlinfo RTLD_DI_TLS_MODID failed: %s\n", dlerror ()); + result = 1; + } + else + printf ("dlinfo says TLS module ID %Zu\n", modid); + + void *block; + if (dlinfo (h, RTLD_DI_TLS_DATA, &block)) + { + printf ("dlinfo RTLD_DI_TLS_DATA failed: %s\n", dlerror ()); + result = 1; + } + else if (block != NULL) + { + printf ("dlinfo RTLD_DI_TLS_DATA says %p but should be unallocated\n", + block); + result = 1; + } + + result |= fp (0, NULL); + + foop = dlsym (h, "foo"); + if (foop == NULL) + { + printf ("cannot get symbol 'foo' the second time: %s\n", dlerror ()); + exit (1); + } + if (*foop != 16) + { + puts ("foo != 16"); + result = 1; + } + + /* Now the module's TLS block has been used and should appear. */ + if (dlinfo (h, RTLD_DI_TLS_DATA, &block)) + { + printf ("dlinfo RTLD_DI_TLS_DATA failed the second time: %s\n", + dlerror ()); + result = 1; + } + else if (block != foop) + { + printf ("dlinfo RTLD_DI_TLS_DATA says %p but should be %p\n", + block, foop); + result = 1; + } + + dlclose (h); + + return result; +#else + return 0; +#endif +} + + +#include "../test-skeleton.c" diff --git a/elf/tst-tls13.c b/elf/tst-tls13.c index 55fb62e..06bfbac 100644 --- a/elf/tst-tls13.c +++ b/elf/tst-tls13.c @@ -2,17 +2,17 @@ #include <dlfcn.h> #include <stdio.h> #include <stdlib.h> +#include <unistd.h> static int do_test (void) { - int i; - for (i = 0; i < 1000;) + for (int i = 0; i < 1000;) { printf ("round %d\n",++i); - void *h = dlopen ("tst-tlsmod13a.so", RTLD_LAZY); + void *h = dlopen ("$ORIGIN/tst-tlsmod13a.so", RTLD_LAZY); if (h == NULL) { printf ("cannot load: %s\n", dlerror ()); diff --git a/elf/tst-tls15.c b/elf/tst-tls15.c new file mode 100644 index 0000000..7ac963a --- /dev/null +++ b/elf/tst-tls15.c @@ -0,0 +1,32 @@ +#include <dlfcn.h> +#include <stdio.h> + +static int +do_test (void) +{ + void *h = dlopen ("tst-tlsmod15a.so", RTLD_NOW); + if (h != NULL) + { + puts ("unexpectedly succeeded to open tst-tlsmod15a.so"); + exit (1); + } + + h = dlopen ("tst-tlsmod15b.so", RTLD_NOW); + if (h == NULL) + { + puts ("failed to open tst-tlsmod15b.so"); + exit (1); + } + + int (*fp) (void) = (int (*) (void)) dlsym (h, "in_dso"); + if (fp == NULL) + { + puts ("cannot find in_dso"); + exit (1); + } + + return fp (); +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/elf/tst-tls8.c b/elf/tst-tls8.c index dd896c4..ccc4e9f 100644 --- a/elf/tst-tls8.c +++ b/elf/tst-tls8.c @@ -11,8 +11,8 @@ static int do_test (void) { #ifdef USE_TLS - static const char modname1[] = "tst-tlsmod3.so"; - static const char modname2[] = "tst-tlsmod4.so"; + static const char modname1[] = "$ORIGIN/tst-tlsmod3.so"; + static const char modname2[] = "$ORIGIN/tst-tlsmod4.so"; int result = 0; int (*fp1) (void); int (*fp2) (int, int *); diff --git a/elf/tst-tlsmod15a.c b/elf/tst-tlsmod15a.c new file mode 100644 index 0000000..66c7071 --- /dev/null +++ b/elf/tst-tlsmod15a.c @@ -0,0 +1,6 @@ +extern int nonexistent_dummy_var; +int * +foo (void) +{ + return &nonexistent_dummy_var; +} diff --git a/elf/tst-tlsmod15b.c b/elf/tst-tlsmod15b.c new file mode 100644 index 0000000..4f63eab --- /dev/null +++ b/elf/tst-tlsmod15b.c @@ -0,0 +1,17 @@ +#include "tst-tls10.h" + +#ifdef USE_TLS__THREAD +__thread int mod15b_var __attribute__((tls_model("initial-exec"))); + +int +in_dso (void) +{ + return mod15b_var; +} +#else +int +in_dso (void) +{ + return 0; +} +#endif diff --git a/elf/unload.c b/elf/unload.c index 4fd82b7..4566f22 100644 --- a/elf/unload.c +++ b/elf/unload.c @@ -9,11 +9,13 @@ #include <stdio.h> #include <stdlib.h> +#define MAPS ((struct link_map *) _r_debug.r_map) + #define OUT \ - for (map = _r_debug.r_map; map != NULL; map = map->l_next) \ + for (map = MAPS; map != NULL; map = map->l_next) \ if (map->l_type == lt_loaded) \ - printf ("name = \"%s\", opencount = %d\n", \ - map->l_name, (int) map->l_opencount); \ + printf ("name = \"%s\", direct_opencount = %d\n", \ + map->l_name, (int) map->l_direct_opencount); \ fflush (stdout) typedef struct diff --git a/elf/unload2.c b/elf/unload2.c index 7a38053..eef2bfd 100644 --- a/elf/unload2.c +++ b/elf/unload2.c @@ -6,11 +6,13 @@ #include <stdio.h> #include <stdlib.h> +#define MAPS ((struct link_map *) _r_debug.r_map) + #define OUT \ - for (map = _r_debug.r_map; map != NULL; map = map->l_next) \ + for (map = MAPS; map != NULL; map = map->l_next) \ if (map->l_type == lt_loaded) \ - printf ("name = \"%s\", opencount = %d\n", \ - map->l_name, (int) map->l_opencount); \ + printf ("name = \"%s\", direct_opencount = %d\n", \ + map->l_name, (int) map->l_direct_opencount); \ fflush (stdout) int diff --git a/elf/unload3.c b/elf/unload3.c new file mode 100644 index 0000000..6f1af70 --- /dev/null +++ b/elf/unload3.c @@ -0,0 +1,41 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +main (void) +{ + void *g = dlopen ("unload3mod1.so", RTLD_GLOBAL | RTLD_NOW); + void *h = dlopen ("unload3mod2.so", RTLD_GLOBAL | RTLD_NOW); + if (g == NULL || h == NULL) + { + printf ("dlopen unload3mod{1,2}.so failed: %p %p\n", g, h); + return 1; + } + dlclose (h); + dlclose (g); + + g = dlopen ("unload3mod3.so", RTLD_GLOBAL | RTLD_NOW); + h = dlopen ("unload3mod4.so", RTLD_GLOBAL | RTLD_NOW); + if (g == NULL || h == NULL) + { + printf ("dlopen unload3mod{3,4}.so failed: %p %p\n", g, h); + return 1; + } + + int (*fn) (int); + fn = dlsym (h, "bar"); + if (fn == NULL) + { + puts ("dlsym failed"); + return 1; + } + + int val = fn (16); + if (val != 24) + { + printf ("bar returned %d != 24\n", val); + return 1; + } + + return 0; +} diff --git a/elf/unload3mod1.c b/elf/unload3mod1.c new file mode 100644 index 0000000..e886b11 --- /dev/null +++ b/elf/unload3mod1.c @@ -0,0 +1 @@ +int dummy1; diff --git a/elf/unload3mod2.c b/elf/unload3mod2.c new file mode 100644 index 0000000..03252a5 --- /dev/null +++ b/elf/unload3mod2.c @@ -0,0 +1 @@ +int dummy2; diff --git a/elf/unload3mod3.c b/elf/unload3mod3.c new file mode 100644 index 0000000..046022c --- /dev/null +++ b/elf/unload3mod3.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int +foo (int x) +{ + puts ("foo"); + return x * 2; +} diff --git a/elf/unload3mod4.c b/elf/unload3mod4.c new file mode 100644 index 0000000..52f808e --- /dev/null +++ b/elf/unload3mod4.c @@ -0,0 +1,13 @@ +#include <stdio.h> + +extern int foo (int x); + +int +bar (int x) +{ + puts ("bar"); + fflush (stdout); + x = foo (x - 4); + puts ("bar after foo"); + return x; +} diff --git a/elf/unload4.c b/elf/unload4.c new file mode 100644 index 0000000..6e171a2 --- /dev/null +++ b/elf/unload4.c @@ -0,0 +1,48 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <malloc.h> + +int +main (void) +{ +#ifdef M_PERTURB + mallopt (M_PERTURB, 0xaa); +#endif + + void *h; + int (*fn) (int); + h = dlopen ("unload4mod1.so", RTLD_LAZY); + if (h == NULL) + { + puts ("1st dlopen failed"); + return 1; + } + fn = dlsym (h, "foo"); + if (fn == NULL) + { + puts ("dlsym failed"); + return 1; + } + int n = fn (10); + if (n != 28) + { + printf ("foo (10) returned %d != 28\n", n); + return 1; + } + dlclose (h); + h = dlopen ("unload4mod3.so", RTLD_LAZY); + fn = dlsym (h, "mod3fn2"); + if (fn == NULL) + { + puts ("second dlsym failed"); + return 1; + } + n = fn (10); + if (n != 22) + { + printf ("mod3fn2 (10) returned %d != 22\n", n); + return 1; + } + dlclose (h); + return 0; +} diff --git a/elf/unload4mod1.c b/elf/unload4mod1.c new file mode 100644 index 0000000..38c5b01 --- /dev/null +++ b/elf/unload4mod1.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +extern int bar (int); + +int +foo (int x) +{ + puts ("in foo"); + return bar (x / 2) + 2; +} diff --git a/elf/unload4mod2.c b/elf/unload4mod2.c new file mode 100644 index 0000000..497ef5d --- /dev/null +++ b/elf/unload4mod2.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int +baz (int x) +{ + puts ("in baz"); + return x * 4; +} diff --git a/elf/unload4mod3.c b/elf/unload4mod3.c new file mode 100644 index 0000000..4b280bc --- /dev/null +++ b/elf/unload4mod3.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +int +__attribute__((noinline)) +mod3fn1 (int x) +{ + puts ("in mod3fn1"); + return x + 6; +} + +int +mod3fn2 (int x) +{ + puts ("in mod3fn2"); + return mod3fn1 (x / 2) * 2; +} diff --git a/elf/unload4mod4.c b/elf/unload4mod4.c new file mode 100644 index 0000000..ba5a144 --- /dev/null +++ b/elf/unload4mod4.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <stdlib.h> + +int +__attribute__((noinline)) +baz (int x) +{ + abort (); +} + +int +bar (int x) +{ + puts ("in bar"); + return baz (x + 1) + 2; +} diff --git a/elf/unload5.c b/elf/unload5.c new file mode 100644 index 0000000..0555052 --- /dev/null +++ b/elf/unload5.c @@ -0,0 +1,42 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +main (void) +{ + void *g = dlopen ("unload3mod1.so", RTLD_GLOBAL | RTLD_NOW); + void *h = dlopen ("unload3mod2.so", RTLD_GLOBAL | RTLD_NOW); + if (g == NULL || h == NULL) + { + printf ("dlopen unload3mod{1,2}.so failed: %p %p\n", g, h); + return 1; + } + dlopen ("unload3mod4.so", RTLD_GLOBAL | RTLD_NOW); + dlclose (h); + dlclose (g); + + g = dlopen ("unload3mod3.so", RTLD_GLOBAL | RTLD_NOW); + h = dlopen ("unload3mod4.so", RTLD_GLOBAL | RTLD_NOW); + if (g == NULL || h == NULL) + { + printf ("dlopen unload3mod{3,4}.so failed: %p %p\n", g, h); + return 1; + } + + int (*fn) (int); + fn = dlsym (h, "bar"); + if (fn == NULL) + { + puts ("dlsym failed"); + return 1; + } + + int val = fn (16); + if (val != 24) + { + printf ("bar returned %d != 24\n", val); + return 1; + } + + return 0; +} diff --git a/elf/unload6.c b/elf/unload6.c new file mode 100644 index 0000000..1efc7eb --- /dev/null +++ b/elf/unload6.c @@ -0,0 +1,30 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +main (void) +{ + void *h = dlopen ("unload6mod1.so", RTLD_LAZY); + if (h == NULL) + { + puts ("dlopen unload6mod1.so failed"); + return 1; + } + + int (*fn) (int); + fn = dlsym (h, "foo"); + if (fn == NULL) + { + puts ("dlsym failed"); + return 1; + } + + int val = fn (16); + if (val != 24) + { + printf ("foo returned %d != 24\n", val); + return 1; + } + + return 0; +} diff --git a/elf/unload6mod1.c b/elf/unload6mod1.c new file mode 100644 index 0000000..24f2e5a --- /dev/null +++ b/elf/unload6mod1.c @@ -0,0 +1,16 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +foo (int i) +{ + void *h = dlopen ("unload6mod2.so", RTLD_LAZY); + if (h == NULL) + { + puts ("dlopen unload6mod2.so failed"); + return 1; + } + + dlclose (h); + return i + 8; +} diff --git a/elf/unload6mod2.c b/elf/unload6mod2.c new file mode 100644 index 0000000..980efa4 --- /dev/null +++ b/elf/unload6mod2.c @@ -0,0 +1,23 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <unistd.h> + +static void *h; + +static void __attribute__((constructor)) +mod2init (void) +{ + h = dlopen ("unload6mod3.so", RTLD_LAZY); + if (h == NULL) + { + puts ("dlopen unload6mod3.so failed"); + fflush (stdout); + _exit (1); + } +} + +static void __attribute__((destructor)) +mod2fini (void) +{ + dlclose (h); +} diff --git a/elf/unload6mod3.c b/elf/unload6mod3.c new file mode 100644 index 0000000..7b29e1d --- /dev/null +++ b/elf/unload6mod3.c @@ -0,0 +1,23 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <unistd.h> + +static void *h; + +static void __attribute__((constructor)) +mod3init (void) +{ + h = dlopen ("unload6mod1.so", RTLD_LAZY); + if (h == NULL) + { + puts ("dlopen unload6mod1.so failed"); + fflush (stdout); + _exit (1); + } +} + +static void __attribute__((destructor)) +mod3fini (void) +{ + dlclose (h); +} diff --git a/elf/unload7.c b/elf/unload7.c new file mode 100644 index 0000000..198f7db --- /dev/null +++ b/elf/unload7.c @@ -0,0 +1,39 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +main (void) +{ + void *h = dlopen ("$ORIGIN/unload7mod1.so", RTLD_LAZY); + if (h == NULL) + { + puts ("dlopen unload7mod1.so failed"); + return 1; + } + + int (*fn) (void); + fn = dlsym (h, "foo"); + if (fn == NULL) + { + puts ("dlsym failed"); + return 1; + } + + int ret = 0; + if (fn () == 0) + ++ret; + + void *h2 = dlopen ("$ORIGIN/unload7mod2.so", RTLD_LAZY); + if (h2 == NULL) + { + puts ("dlopen unload7mod2.so failed"); + return 1; + } + dlclose (h2); + + if (fn () == 0) + ++ret; + + dlclose (h); + return ret; +} diff --git a/elf/unload7mod1.c b/elf/unload7mod1.c new file mode 100644 index 0000000..7435adc --- /dev/null +++ b/elf/unload7mod1.c @@ -0,0 +1,11 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +foo (int i) +{ + if (dlsym (RTLD_DEFAULT, "unload7_nonexistent_symbol") == NULL) + return 1; + puts ("dlsym returned non-NULL"); + return 0; +} diff --git a/elf/unload7mod2.c b/elf/unload7mod2.c new file mode 100644 index 0000000..6d1a0d4 --- /dev/null +++ b/elf/unload7mod2.c @@ -0,0 +1 @@ +int x; |