diff options
author | gcc <gcc@7b3dc134-2b1b-0410-93df-9e9f96275f8d> | 2006-08-17 01:18:26 +0000 |
---|---|---|
committer | gcc <gcc@7b3dc134-2b1b-0410-93df-9e9f96275f8d> | 2006-08-17 01:18:26 +0000 |
commit | 15f34685e7a9b5caf761af2ebf6afa20438d440b (patch) | |
tree | dc04ce3cdf040f198743c15b64557824de174680 /libc/elf | |
parent | 1e848e0e775a36f6359161f5deb890942ef42ff3 (diff) | |
download | eglibc2-15f34685e7a9b5caf761af2ebf6afa20438d440b.tar.gz |
Import glibc-mainline for 2006-08-16
git-svn-id: svn://svn.eglibc.org/fsf/trunk@4 7b3dc134-2b1b-0410-93df-9e9f96275f8d
Diffstat (limited to 'libc/elf')
284 files changed, 33256 insertions, 0 deletions
diff --git a/libc/elf/.cvsignore b/libc/elf/.cvsignore new file mode 100644 index 000000000..3fc9f4cdf --- /dev/null +++ b/libc/elf/.cvsignore @@ -0,0 +1,6 @@ +*.d *.o *.so *.po *.go stamp.* *.stamp *.ustamp *.udeps +*.gz *.Z *.tar *.tgz +=* +TODO COPYING* AUTHORS copyr-* copying.* +glibc-* +distinfo diff --git a/libc/elf/Makefile b/libc/elf/Makefile new file mode 100644 index 000000000..3b4ef26d4 --- /dev/null +++ b/libc/elf/Makefile @@ -0,0 +1,908 @@ +# 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 +# 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. + +# Makefile for elf subdirectory of GNU C Library. + +subdir := elf + +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 + +# The core dynamic linking functions are in libc for the static and +# profiled libraries. +dl-routines = $(addprefix dl-,load cache lookup object reloc deps \ + runtime error init fini debug misc \ + version profile conflict tls origin \ + 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 +shared-only-routines += dl-caller + +# ld.so uses those routines, plus some special stuff for being the program +# interpreter and operating independent of libc. +rtld-routines := rtld $(dl-routines) dl-sysdep dl-environ dl-minimal +all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines) + +distribute := rtld-Rules \ + $(rtld-routines:=.c) dynamic-link.h do-rel.h dl-machine.h \ + dl-cache.h dl-hash.h soinit.c sofini.c ldd.bash.in \ + genrtldtbl.awk atomicity.h dl-procinfo.h ldsodefs.h \ + dl-librecon.h interp.c sln.c dl-dst.h hp-timing.h \ + do-lookup.h dl-lookupcfg.h sprof.c gen-trusted-dirs.awk \ + testobj1.c testobj2.c testobj3.c testobj4.c testobj5.c \ + testobj6.c testobj1_1.c failobj.c unloadmod.c \ + ldconfig.h ldconfig.c cache.c readlib.c readelflib.c \ + chroot_canon.c gccframe.h \ + dep1.c dep2.c dep3.c dep4.c dl-dtprocnum.h unsecvars.h \ + vismain.c vismod1.c vismod2.c vismod3.c \ + constload2.c constload3.c filtmod1.c filtmod2.c \ + nodlopenmod.c nodelete.c nodelmod1.c nodelmod2.c \ + nodelmod3.c nodelmod4.c nodlopen.c dl-osinfo.h \ + reldepmod1.c reldepmod2.c reldepmod3.c reldepmod4.c \ + reldepmod5.c reldepmod6.c \ + reldep4mod1.c reldep4mod2.c reldep4mod3.c reldep4mod4.c \ + nextmod1.c nextmod2.c pathoptobj.c tst-pathopt.sh \ + neededobj1.c neededobj2.c neededobj3.c neededobj4.c \ + neededobj5.c neededobj6.c firstobj.c \ + unload2mod.c unload2dep.c ltglobmod1.c ltglobmod2.c \ + testobj.h vismod.h globalmod1.c \ + dblloadmod1.c dblloadmod2.c dblloadmod3.c \ + reldep6mod4.c reldep6mod3.c reldep6mod2.c reldep6mod1.c \ + reldep6mod0.c reldep7mod1.c reldep7mod2.c \ + unwind-dw2.c unwind-dw2-fde.c unwind.h unwind-pe.h \ + unwind-dw2-fde.h dwarf2.h dl-procinfo.c tls.h dl-tls.h \ + 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-alignmod2.c \ + circlemod1.c circlemod1a.c circlemod2.c circlemod2a.c \ + circlemod3.c circlemod3a.c nodlopenmod2.c \ + tls-macros.h \ + reldep8mod1.c reldep8mod2.c reldep8mod3.c \ + nodel2mod1.c nodel2mod2.c nodel2mod3.c \ + reldep9.c reldep9mod1.c reldep9mod2.c reldep9mod3.c \ + tst-array1.exp tst-array2.exp tst-array4.exp \ + 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 \ + unload3mod1.c unload3mod2.c unload3mod3.c unload3mod4.c \ + unload4mod1.c unload4mod2.c unload4mod3.c unload4mod4.c \ + unload6mod1.c unload6mod2.c unload6mod3.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 +CFLAGS-dl-iterate-phdr.c = $(uses-callbacks) + +include ../Makeconfig + +ifeq ($(unwind-find-fde),yes) +routines += unwind-dw2-fde-glibc +shared-only-routines += unwind-dw2-fde-glibc +endif + +before-compile = $(objpfx)trusted-dirs.h +generated := trusted-dirs.h trusted-dirs.st for-renamed/renamed.so +generated-dirs := for-renamed + +ifeq ($(versioning),yes) +ld-map = $(common-objpfx)ld.map +endif + +ifeq (yes,$(build-shared)) +extra-objs = $(all-rtld-routines:%=%.os) soinit.os sofini.os interp.os +generated += librtld.os dl-allobjs.os ld.so ldd +install-others = $(inst_slibdir)/$(rtld-installed-name) +install-bin-script = ldd +endif + +others = sprof sln +install-bin = sprof +others-static = sln +install-rootsbin = sln + +ifeq (yes,$(use-ldconfig)) +ifeq (yes,$(build-shared)) +others-static += ldconfig +others += ldconfig +install-rootsbin += ldconfig + +ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon +extra-objs += $(ldconfig-modules:=.o) + +# To find xmalloc.c and xstrdup.c +vpath %.c ../locale/programs + +endif +endif + +tests = tst-tls1 tst-tls2 tst-tls9 tst-leaks1 +ifeq (yes,$(have-initfini-array)) +tests += tst-array1 tst-array2 tst-array3 tst-array4 tst-array5 +endif +ifeq (yes,$(build-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 +endif +ifeq (yes,$(have-initfini-array)) +tests-static += tst-array1-static tst-array5-static +endif +tests += $(tests-static) +endif +ifeq (yes,$(build-shared)) +tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \ + constload1 order $(tests-vis-$(have-protected)) noload filter unload \ + reldep reldep2 reldep3 reldep4 $(tests-nodelete-$(have-z-nodelete)) \ + $(tests-nodlopen-$(have-z-nodlopen)) neededtest neededtest2 \ + 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-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 tst-global1 order2 \ + tst-audit1 tst-audit2 \ + tst-stackguard1 +# reldep9 +test-srcs = tst-pathopt +tests-vis-yes = vismain +tests-nodelete-yes = nodelete nodelete2 +tests-nodlopen-yes = nodlopen nodlopen2 +tests-execstack-yes = tst-execstack tst-execstack-needed tst-execstack-prog +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)) \ + $(modules-nodelete-$(have-z-nodelete)) \ + $(modules-nodlopen-$(have-z-nodlopen)) filtmod1 filtmod2 \ + reldepmod1 reldepmod2 reldepmod3 reldepmod4 nextmod1 nextmod2 \ + reldep4mod1 reldep4mod2 reldep4mod3 reldep4mod4 \ + neededobj1 neededobj2 neededobj3 neededobj4 \ + neededobj5 neededobj6 firstobj globalmod1 \ + unload2mod unload2dep ltglobmod1 ltglobmod2 pathoptobj \ + dblloadmod1 dblloadmod2 dblloadmod3 reldepmod5 reldepmod6 \ + reldep6mod0 reldep6mod1 reldep6mod2 reldep6mod3 reldep6mod4 \ + reldep7mod1 reldep7mod2 \ + tst-tlsmod1 tst-tlsmod2 tst-tlsmod3 tst-tlsmod4 \ + 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 tst-alignmod2 \ + $(modules-execstack-$(have-z-execstack)) \ + tst-dlopenrpathmod tst-deep1mod1 tst-deep1mod2 tst-deep1mod3 \ + tst-dlmopen1mod tst-auditmod1 \ + unload3mod1 unload3mod2 unload3mod3 unload3mod4 \ + unload4mod1 unload4mod2 unload4mod3 unload4mod4 \ + unload6mod1 unload6mod2 unload6mod3 \ + order2mod1 order2mod2 order2mod3 order2mod4 +ifeq (yes,$(have-initfini-array)) +modules-names += tst-array2dep tst-array5dep +endif +ifeq (yesyes,$(have-fpie)$(build-shared)) +modules-names += tst-piemod1 +endif +modules-vis-yes = vismod1 vismod2 vismod3 +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))) +# 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 +update-abi: update-abi-ld + +ifeq (yes,$(build-shared)) +# Make sure these things are built in the `make lib' pass so they can be used +# to run programs during the `make others' pass. +lib-noranlib: $(objpfx)$(rtld-installed-name) \ + $(addprefix $(objpfx),$(extra-objs)) +endif + +# Command to link into a larger single relocatable object. +reloc-link = $(LINK.o) -nostdlib -nostartfiles -r + +$(objpfx)dl-allobjs.os: $(all-rtld-routines:%=$(objpfx)%.os) + $(reloc-link) -o $@ $^ + +# Link together the dynamic linker into a single relocatable object. +# First we do a link against libc_pic.a just to get a link map, +# and discard the object produced by that link. From the link map +# we can glean all the libc modules that need to go into the dynamic +# linker. Then we do a recursive make that goes into all the subdirs +# those modules come from and builds special rtld-foo.os versions that +# are compiled with special flags, and puts these modules into rtld-libc.a +# for us. Then we do the real link using rtld-libc.a instead of libc_pic.a. + +$(objpfx)librtld.map: $(objpfx)dl-allobjs.os $(common-objpfx)libc_pic.a + @-rm -f $@T + $(reloc-link) -o $@.o '-Wl,-(' $^ -lgcc '-Wl,-)' -Wl,-Map,$@T + rm -f $@.o + mv -f $@T $@ + +$(objpfx)librtld.mk: $(objpfx)librtld.map Makefile + LC_ALL=C \ + sed -n 's@^$(common-objpfx)\([^(]*\)(\([^)]*\.os\)) *.*$$@\1 \2@p' \ + $< | \ + while read lib file; do \ + case $$lib in \ + libc_pic.a) \ + LC_ALL=C fgrep -l /$$file \ + $(common-objpfx)stamp.os $(common-objpfx)*/stamp.os | \ + LC_ALL=C \ + sed 's@^$(common-objpfx)\([^/]*\)/stamp\.os$$@rtld-\1'" +=$$file@"\ + ;; \ + */*.a) \ + echo rtld-$${lib%%/*} += $$file ;; \ + *) echo "Wasn't expecting $$lib($$file)" >&2; exit 1 ;; \ + esac; \ + done > $@T + echo rtld-subdirs = `LC_ALL=C sed 's/^rtld-\([^ ]*\).*$$/\1/' $@T \ + | LC_ALL=C sort -u` >> $@T + mv -f $@T $@ + +$(objpfx)rtld-libc.a: $(objpfx)librtld.mk FORCE + $(MAKE) -f $< -f rtld-Rules + +$(objpfx)librtld.os: $(objpfx)dl-allobjs.os $(objpfx)rtld-libc.a + $(LINK.o) -nostdlib -nostartfiles -r -o $@ '-Wl,-(' $^ -lgcc '-Wl,-)' \ + -Wl,-Map,$@.map + +generated += librtld.map librtld.mk rtld-libc.a librtld.os.map + +z-now-yes = -Wl,-z,now + +$(objpfx)ld.so: $(objpfx)librtld.os $(ld-map) + @rm -f $@.lds + $(LINK.o) -nostdlib -nostartfiles -shared $(z-now-$(bind-now)) \ + $(LDFLAGS-rtld) -Wl,-z,defs -Wl,--verbose 2>&1 | \ + LC_ALL=C \ + sed -e '/^=========/,/^=========/!d;/^=========/d' \ + -e 's/\. = 0 + SIZEOF_HEADERS;/& _begin = . - SIZEOF_HEADERS;/' \ + > $@.lds + $(LINK.o) -nostdlib -nostartfiles -shared -o $@ \ + $(LDFLAGS-rtld) -Wl,-z,defs $(z-now-$(bind-now)) \ + $(filter-out $(map-file),$^) $(load-map-file) \ + -Wl,-soname=$(rtld-installed-name) -T $@.lds + rm -f $@.lds + +# interp.c exists just to get this string into the libraries. +CFLAGS-interp.c = -D'RUNTIME_LINKER="$(slibdir)/$(rtld-installed-name)"' \ + -DNOT_IN_libc=1 +$(objpfx)interp.os: $(common-objpfx)config.make + +ifneq (ld.so,$(rtld-installed-name)) +# Make sure ld.so.1 exists in the build directory so we can link +# against it. +$(objpfx)$(rtld-installed-name): $(objpfx)ld.so + rm -f $@ + ln -s $(<F) $@ +generated += $(rtld-installed-name) +endif + +# Build a file mentioning all trustworthy directories to look for shared +# libraries when using LD_LIBRARY_PATH in a setuid program. The user can +# add directories to the list by defining $(user-defined-trusted-dirs) +# before starting make. +$(objpfx)trusted-dirs.h: $(objpfx)trusted-dirs.st; @: +$(objpfx)trusted-dirs.st: Makefile $(..)Makeconfig + $(make-target-directory) + echo "$(subst :, ,$(default-rpath) $(user-defined-trusted-dirs))" \ + | $(AWK) -f gen-trusted-dirs.awk > ${@:st=T}; + echo '#define DL_DST_LIB "$(notdir $(slibdir))"' >> ${@:st=T} + $(move-if-change) ${@:st=T} ${@:st=h} + touch $@ +CPPFLAGS-dl-load.c = -I$(objpfx). -I$(csu-objpfx). + +ifeq (yes,$(build-shared)) +$(inst_slibdir)/$(rtld-version-installed-name): $(objpfx)ld.so $(+force) + $(make-target-directory) + $(do-install-program) + +$(inst_slibdir)/$(rtld-installed-name): \ + $(inst_slibdir)/$(rtld-version-installed-name) \ + $(inst_slibdir)/libc-$(version).so + $(make-shlib-link) + +# Special target called by parent to install just the dynamic linker. +.PHONY: ldso_install +ldso_install: $(inst_slibdir)/$(rtld-installed-name) +endif + + +common-ldd-rewrite = -e 's%@RTLD@%$(slibdir)/$(rtld-installed-name)%g' \ + -e 's%@VERSION@%$(version)%g' +sh-ldd-rewrite = $(common-ldd-rewrite) -e 's%@BASH@%/bin/sh%g;s/\$$"/"/g' +bash-ldd-rewrite = $(common-ldd-rewrite) -e 's%@BASH@%$(BASH)%g' \ + -e 's%@TEXTDOMAINDIR@%$(msgcatdir)%g' + +ifneq ($(have-bash2),yes) +ldd-shell = sh +else +ldd-shell = bash +endif + +ifeq ($(ldd-rewrite-script),no) +define gen-ldd +LC_ALL=C sed $($(ldd-shell)-ldd-rewrite) < $< > $@.new +endef +else +define gen-ldd +LC_ALL=C sed $($(ldd-shell)-ldd-rewrite) < $< | LC_ALL=C sed -f $(ldd-rewrite-script) > $@.new +endef +endif + +$(objpfx)ldd: ldd.bash.in $(common-objpfx)soversions.mk \ + $(common-objpfx)config.make + $(gen-ldd) + chmod 555 $@.new + mv -f $@.new $@ + +$(objpfx)sprof: $(libdl) + +$(objpfx)ldconfig: $(ldconfig-modules:%=$(objpfx)%.o) +SYSCONF-FLAGS := -D'SYSCONFDIR="$(sysconfdir)"' +CFLAGS-ldconfig.c = $(SYSCONF-FLAGS) -D'LIBDIR="$(libdir)"' \ + -D'SLIBDIR="$(slibdir)"' -DIS_IN_ldconfig=1 +CFLAGS-dl-cache.c = $(SYSCONF-FLAGS) +CFLAGS-cache.c = $(SYSCONF-FLAGS) + +CPPFLAGS-.os += $(if $(filter $(@F),$(patsubst %,%.os,$(all-rtld-routines))),-DNOT_IN_libc=1 -DIS_IN_rtld=1) + +test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(strip $(modules-names)))) +generated += $(addsuffix .so,$(strip $(modules-names))) + +ifeq (yes,$(build-shared)) +ifeq ($(cross-compiling),no) +tests: $(objpfx)tst-pathopt.out +endif +endif + +$(objpfx)testobj1.so: $(libdl) +$(objpfx)testobj1_1.so: $(objpfx)testobj1.so $(libdl) +$(objpfx)testobj2.so: $(objpfx)testobj1.so $(libdl) +$(objpfx)testobj3.so: $(libdl) +$(objpfx)testobj4.so: $(libdl) +$(objpfx)testobj5.so: $(libdl) +$(objpfx)testobj6.so: $(objpfx)testobj1.so $(objpfx)testobj2.so $(libdl) +$(objpfx)failobj.so: $(objpfx)testobj6.so +$(objpfx)dep1.so: $(objpfx)dep2.so $(objpfx)dep4.so +$(objpfx)dep2.so: $(objpfx)dep3.so $(objpfx)dep4.so +$(objpfx)dep4.so: $(objpfx)dep3.so +$(objpfx)nodelmod3.so: $(objpfx)nodelmod4.so +$(objpfx)nextmod1.so: $(libdl) +$(objpfx)neededobj1.so: $(libdl) +$(objpfx)neededobj2.so: $(objpfx)neededobj1.so $(libdl) +$(objpfx)neededobj3.so: $(objpfx)neededobj1.so $(objpfx)neededobj2.so $(libdl) +$(objpfx)neededobj4.so: $(objpfx)neededobj1.so $(objpfx)neededobj2.so \ + $(objpfx)neededobj3.so $(libdl) +$(objpfx)neededobj6.so: $(objpfx)neededobj5.so +$(objpfx)unload2mod.so: $(objpfx)unload2dep.so +$(objpfx)ltglobmod2.so: $(libdl) +$(objpfx)firstobj.so: $(shared-thread-library) +$(objpfx)globalmod1.so: $(libdl) +$(objpfx)reldep4mod1.so: $(objpfx)reldep4mod3.so +$(objpfx)reldep4mod2.so: $(objpfx)reldep4mod4.so +$(objpfx)dblloadmod1.so: $(objpfx)dblloadmod3.so +$(objpfx)dblloadmod2.so: $(objpfx)dblloadmod3.so +$(objpfx)reldepmod5.so: $(objpfx)reldepmod2.so +$(objpfx)reldepmod6.so: $(objpfx)reldepmod2.so +$(objpfx)reldep6mod1.so: $(objpfx)reldep6mod0.so +$(objpfx)reldep6mod2.so: $(objpfx)reldep6mod1.so +$(objpfx)reldep6mod3.so: $(objpfx)reldep6mod2.so +$(objpfx)reldep6mod4.so: $(objpfx)reldep6mod1.so +$(objpfx)tst-tlsmod3.so: $(objpfx)tst-tlsmod2.so +$(objpfx)tst-tlsmod8.so: $(objpfx)tst-tlsmod7.so +$(objpfx)tst-tlsmod10.so: $(objpfx)tst-tlsmod9.so +$(objpfx)tst-tlsmod12.so: $(objpfx)tst-tlsmod11.so +$(objpfx)tst-tlsmod13a.so: $(objpfx)tst-tlsmod13.so +# For tst-tls9-static, make sure the modules it dlopens have libc.so in DT_NEEDED +$(objpfx)tst-tlsmod5.so: $(common-objpfx)libc.so +$(objpfx)tst-tlsmod6.so: $(common-objpfx)libc.so +$(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) + +LDFLAGS-tst-tlsmod5.so = -nostdlib +LDFLAGS-tst-tlsmod6.so = -nostdlib + +testobj1.so-no-z-defs = yes +testobj3.so-no-z-defs = yes +testobj4.so-no-z-defs = yes +testobj5.so-no-z-defs = yes +testobj6.so-no-z-defs = yes +failobj.so-no-z-defs = yes +constload2.so-no-z-defs = yes +constload3.so-no-z-defs = yes +nodelmod1.so-no-z-defs = yes +nodelmod2.so-no-z-defs = yes +nodelmod4.so-no-z-defs = yes +nodel2mod2.so-no-z-defs = yes +reldepmod2.so-no-z-defs = yes +reldepmod3.so-no-z-defs = yes +reldepmod4.so-no-z-defs = yes +reldep4mod4.so-no-z-defs = yes +reldep4mod2.so-no-z-defs = yes +ltglobmod2.so-no-z-defs = yes +dblloadmod3.so-no-z-defs = yes +tst-tlsmod1.so-no-z-defs = yes +tst-tlsmod2.so-no-z-defs = yes +tst-tlsmod3.so-no-z-defs = yes +tst-tlsmod4.so-no-z-defs = yes +tst-tlsmod7.so-no-z-defs = yes +tst-tlsmod8.so-no-z-defs = yes +tst-tlsmod9.so-no-z-defs = yes +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 +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. +tests: $(test-modules) +endif + +$(objpfx)loadtest: $(libdl) +LDFLAGS-loadtest = -rdynamic + +$(objpfx)loadtest.out: $(test-modules) + +$(objpfx)neededtest: $(libdl) +$(objpfx)neededtest.out: $(objpfx)neededobj1.so $(objpfx)neededobj2.so \ + $(objpfx)neededobj3.so + +$(objpfx)neededtest2: $(libdl) +$(objpfx)neededtest2.out: $(objpfx)neededobj1.so $(objpfx)neededobj2.so \ + $(objpfx)neededobj3.so + +$(objpfx)neededtest3: $(libdl) +$(objpfx)neededtest3.out: $(objpfx)neededobj1.so $(objpfx)neededobj2.so \ + $(objpfx)neededobj3.so $(objpfx)neededobj4.so + +neededtest4-ENV = LC_ALL=C LANGUAGE=C +$(objpfx)neededtest4: $(libdl) $(objpfx)neededobj1.so +$(objpfx)neededtest4.out: $(objpfx)neededobj5.so $(objpfx)neededobj6.so + +$(objpfx)restest1: $(objpfx)testobj1.so $(objpfx)testobj1_1.so $(libdl) +LDFLAGS-restest1 = -rdynamic + +$(objpfx)restest2: $(libdl) +LDFLAGS-restest2 = -rdynamic + +$(objpfx)restest1.out: $(test-modules) + +preloadtest-preloads = testobj1 testobj2 testobj3 testobj4 testobj5 +$(objpfx)preloadtest: $(objpfx)testobj6.so +LDFLAGS-preloadtest = -rdynamic +$(objpfx)preloadtest.out: $(preloadtest-preloads:%=$(objpfx)%.so) +preloadtest-ENV = \ + LD_PRELOAD=$(subst $(empty) ,:,$(strip $(preloadtest-preloads:=.so))) + +$(objpfx)loadfail: $(libdl) +LDFLAGS-loadfail = -rdynamic + +$(objpfx)loadfail.out: $(objpfx)failobj.so + +$(objpfx)multiload: $(libdl) +LDFLAGS-multiload = -rdynamic +CFLAGS-multiload.c = -DOBJDIR=\"$(elf-objpfx)\" + +$(objpfx)multiload.out: $(objpfx)testobj1.so + +$(objpfx)origtest: $(libdl) +LDFLAGS-origtest = -rdynamic +$(objpfx)origtest.out: $(objpfx)testobj1.so + +ifeq ($(have-thread-library),yes) +$(objpfx)resolvfail: $(libdl) $(shared-thread-library) +else +$(objpfx)resolvfail: $(libdl) +endif + +$(objpfx)constload1: $(libdl) +$(objpfx)constload1.out: $(objpfx)constload2.so $(objpfx)constload3.so + +$(objpfx)circleload1: $(libdl) +$(objpfx)circleload1.out: $(objpfx)circlemod1.so \ + $(objpfx)circlemod1a.so + +$(objpfx)circlemod1.so: $(objpfx)circlemod2.so +$(objpfx)circlemod2.so: $(objpfx)circlemod3.so +$(objpfx)circlemod1a.so: $(objpfx)circlemod2a.so +$(objpfx)circlemod2a.so: $(objpfx)circlemod3a.so + +$(objpfx)order: $(addprefix $(objpfx),dep4.so dep3.so dep2.so dep1.so) + +$(objpfx)order.out: $(objpfx)order + $(elf-objpfx)$(rtld-installed-name) \ + --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \ + $(objpfx)order > $@ + (echo "0123456789" | cmp $@ -) > /dev/null + +$(objpfx)vismain: $(addprefix $(objpfx),vismod1.so vismod2.so) +$(objpfx)vismain.out: $(addprefix $(objpfx),vismod3.so) +vismain-ENV = LD_PRELOAD=$(addprefix $(objpfx),vismod3.so) + +$(objpfx)noload: $(objpfx)testobj1.so +LDFLAGS-noload = -rdynamic +$(objpfx)noload.out: $(objpfx)testobj5.so + +LDFLAGS-nodelete = -rdynamic +LDFLAGS-nodelmod1.so = -Wl,--enable-new-dtags,-z,nodelete +LDFLAGS-nodelmod4.so = -Wl,--enable-new-dtags,-z,nodelete +$(objpfx)nodelete: $(libdl) +$(objpfx)nodelete.out: $(objpfx)nodelmod1.so $(objpfx)nodelmod2.so \ + $(objpfx)nodelmod3.so + +LDFLAGS-nodlopenmod.so = -Wl,--enable-new-dtags,-z,nodlopen +$(objpfx)nodlopen: $(libdl) +$(objpfx)nodlopen.out: $(objpfx)nodlopenmod.so + +$(objpfx)nodlopenmod2.so: $(objpfx)nodlopenmod.so +$(objpfx)nodlopen2: $(libdl) +$(objpfx)nodlopen2.out: $(objpfx)nodlopenmod2.so + +$(objpfx)filtmod1.so: $(objpfx)filtmod1.os $(objpfx)filtmod2.so + $(LINK.o) -shared -o $@ -B$(csu-objpfx) $(LDFLAGS.so) \ + -L$(subst :, -L,$(rpath-link)) \ + -Wl,-rpath-link=$(rpath-link) \ + $< -Wl,-F,$(objpfx)filtmod2.so +$(objpfx)filter: $(objpfx)filtmod1.so + +$(objpfx)unload: $(libdl) +$(objpfx)unload.out: $(objpfx)unloadmod.so + +$(objpfx)reldep: $(libdl) +$(objpfx)reldep.out: $(objpfx)reldepmod1.so $(objpfx)reldepmod2.so + +$(objpfx)reldep2: $(libdl) +$(objpfx)reldep2.out: $(objpfx)reldepmod1.so $(objpfx)reldepmod3.so + +$(objpfx)reldep3: $(libdl) +$(objpfx)reldep3.out: $(objpfx)reldepmod1.so $(objpfx)reldepmod4.so + +$(objpfx)reldep4: $(libdl) +$(objpfx)reldep4.out: $(objpfx)reldep4mod1.so $(objpfx)reldep4mod2.so + +$(objpfx)next: $(objpfx)nextmod1.so $(objpfx)nextmod2.so $(libdl) + +$(objpfx)unload2: $(libdl) +$(objpfx)unload2.out: $(objpfx)unload2mod.so $(objpfx)unload2dep.so + +$(objpfx)lateglobal: $(libdl) +$(objpfx)lateglobal.out: $(objpfx)ltglobmod1.so $(objpfx)ltglobmod2.so + +$(objpfx)tst-pathopt: $(libdl) +$(objpfx)tst-pathopt.out: tst-pathopt.sh $(objpfx)tst-pathopt \ + $(objpfx)pathoptobj.so + $(SHELL) -e $< $(common-objpfx) + +$(objpfx)initfirst: $(libdl) +$(objpfx)initfirst.out: $(objpfx)firstobj.so + +$(objpfx)global: $(objpfx)globalmod1.so +$(objpfx)global.out: $(objpfx)reldepmod1.so + +$(objpfx)dblload: $(libdl) +$(objpfx)dblload.out: $(objpfx)dblloadmod1.so $(objpfx)dblloadmod2.so + +$(objpfx)dblunload: $(libdl) +$(objpfx)dblunload.out: $(objpfx)dblloadmod1.so $(objpfx)dblloadmod2.so + +$(objpfx)reldep5: $(libdl) +$(objpfx)reldep5.out: $(objpfx)reldepmod5.so $(objpfx)reldepmod6.so + +$(objpfx)reldep6: $(libdl) +$(objpfx)reldep6.out: $(objpfx)reldep6mod3.so $(objpfx)reldep6mod4.so + +$(objpfx)reldep7: $(libdl) +$(objpfx)reldep7.out: $(objpfx)reldep7mod1.so $(objpfx)reldep7mod2.so + +$(objpfx)reldep8: $(libdl) +$(objpfx)reldep8.out: $(objpfx)reldep8mod3.so + +LDFLAGS-nodel2mod2.so = -Wl,--enable-new-dtags,-z,nodelete +$(objpfx)nodelete2: $(libdl) +$(objpfx)nodelete2.out: $(objpfx)nodel2mod3.so + +$(objpfx)reldep9: $(libdl) +$(objpfx)reldep9.out: $(objpfx)reldep9mod3.so + +$(objpfx)tst-tls3: $(objpfx)tst-tlsmod1.so + +$(objpfx)tst-tls4: $(libdl) +$(objpfx)tst-tls4.out: $(objpfx)tst-tlsmod2.so + +$(objpfx)tst-tls5: $(libdl) +$(objpfx)tst-tls5.out: $(objpfx)tst-tlsmod2.so + +$(objpfx)tst-tls6: $(libdl) +$(objpfx)tst-tls6.out: $(objpfx)tst-tlsmod2.so + +$(objpfx)tst-tls7: $(libdl) +$(objpfx)tst-tls7.out: $(objpfx)tst-tlsmod3.so + +$(objpfx)tst-tls8: $(libdl) +$(objpfx)tst-tls8.out: $(objpfx)tst-tlsmod3.so $(objpfx)tst-tlsmod4.so + +$(objpfx)tst-tls9: $(libdl) +$(objpfx)tst-tls9.out: $(objpfx)tst-tlsmod5.so $(objpfx)tst-tlsmod6.so + +$(objpfx)tst-tls10: $(objpfx)tst-tlsmod8.so + +$(objpfx)tst-tls11: $(objpfx)tst-tlsmod10.so + +$(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-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 + +ifdef libdl +$(objpfx)tst-tls9-static: $(common-objpfx)dlfcn/libdl.a +$(objpfx)tst-tls9-static.out: $(objpfx)tst-tlsmod5.so $(objpfx)tst-tlsmod6.so +endif + +ifeq ($(have-z-execstack),yes) +$(objpfx)tst-execstack: $(libdl) +$(objpfx)tst-execstack.out: $(objpfx)tst-execstack-mod.so +LDFLAGS-tst-execstack = -Wl,-z,noexecstack +LDFLAGS-tst-execstack-mod = -Wl,-z,execstack + +$(objpfx)tst-execstack-needed: $(objpfx)tst-execstack-mod.so +LDFLAGS-tst-execstack-needed = -Wl,-z,noexecstack + +LDFLAGS-tst-execstack-prog = -Wl,-z,execstack +endif + +$(objpfx)tst-array1.out: $(objpfx)tst-array1 + $(elf-objpfx)$(rtld-installed-name) \ + --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \ + $(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) \ + --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \ + $(objpfx)tst-array2 > $@ + cmp $@ tst-array2.exp > /dev/null + +$(objpfx)tst-array3.out: $(objpfx)tst-array3 + $(elf-objpfx)$(rtld-installed-name) \ + --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \ + $(objpfx)tst-array3 > $@ + cmp $@ tst-array1.exp > /dev/null + +$(objpfx)tst-array4: $(libdl) +$(objpfx)tst-array4.out: $(objpfx)tst-array4 $(objpfx)tst-array2dep.so + $(elf-objpfx)$(rtld-installed-name) \ + --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \ + $< > $@ + 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 + +$(objpfx)tst-pie1.out: $(objpfx)tst-pie1 + $(elf-objpfx)$(rtld-installed-name) \ + --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \ + $< > $@ + +$(objpfx)tst-pie1: $(objpfx)tst-pie1.o $(objpfx)tst-piemod1.so + $(LINK.o) -pie -Wl,-O1 \ + $(sysdep-LDFLAGS) $(config-LDFLAGS) \ + $(extra-B-$(@F:lib%.so=%).so) -B$(csu-objpfx) \ + $(extra-B-$(@F:lib%.so=%).so) $(load-map-file) \ + $(LDFLAGS) $(LDFLAGS-$(@F)) \ + -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 + +$(objpfx)check-textrel.out: $(objpfx)check-textrel + $(dir $<)$(notdir $<) $(common-objpfx)libc.so \ + $(sort $(wildcard $(common-objpfx)*/lib*.so \ + $(common-objpfx)iconvdata/*.so)) > $@ +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) +$(objpfx)tst-dlopenrpath: $(objpfx)tst-dlopenrpathmod.so $(libdl) +CFLAGS-tst-dlopenrpath.c += -DPFX=\"$(objpfx)\" +LDFLAGS-tst-dlopenrpathmod.so += -Wl,-rpath,\$$ORIGIN/test-subdir +$(objpfx)tst-dlopenrpath.out: $(objpfx)firstobj.so + +$(objpfx)tst-deep1mod2.so: $(objpfx)tst-deep1mod3.so +$(objpfx)tst-deep1: $(libdl) $(objpfx)tst-deep1mod1.so +$(objpfx)tst-deep1.out: $(objpfx)tst-deep1mod2.so +LDFLAGS-tst-deep1 += -rdynamic +tst-deep1mod3.so-no-z-defs = yes + +$(objpfx)tst-dlmopen1mod.so: $(libdl) +$(objpfx)tst-dlmopen1: $(libdl) +$(objpfx)tst-dlmopen1.out: $(objpfx)tst-dlmopen1mod.so + +$(objpfx)tst-dlmopen2: $(libdl) +$(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 diff --git a/libc/elf/Versions b/libc/elf/Versions new file mode 100644 index 000000000..967ebdb3a --- /dev/null +++ b/libc/elf/Versions @@ -0,0 +1,67 @@ +libc { + GLIBC_2.0 { +%ifdef EXPORT_UNWIND_FIND_FDE + __register_frame_info; __deregister_frame_info; +%endif + } + GLIBC_2.1 { + # functions used in other libraries + _dl_mcount_wrapper; _dl_mcount_wrapper_check; + } + GLIBC_2.2.4 { + dl_iterate_phdr; + } +%ifdef EXPORT_UNWIND_FIND_FDE + GCC_3.0 { + __register_frame_info_bases; __deregister_frame_info_bases; + __register_frame_info_table_bases; _Unwind_Find_FDE; + } +%endif + GLIBC_PRIVATE { + # functions used in other libraries + _dl_addr; + _dl_sym; _dl_vsym; + _dl_open_hook; + __libc_dlopen_mode; __libc_dlsym; __libc_dlclose; + } +} + +ld { + GLIBC_2.0 { + # Function from libc.so which must be shared with libc. + calloc; free; malloc; realloc; __libc_memalign; + + _r_debug; + } + GLIBC_2.1 { + # functions used in other libraries + _dl_mcount; + # historically used by Garbage Collectors + __libc_stack_end; + } + GLIBC_2.3 { + # 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; + _dl_argv; + _dl_out_of_memory; + _dl_starting_up; + _rtld_global; _rtld_global_ro; + _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/libc/elf/cache.c b/libc/elf/cache.c new file mode 100644 index 000000000..6730fb36e --- /dev/null +++ b/libc/elf/cache.c @@ -0,0 +1,523 @@ +/* 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. + + 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. + + 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 General Public License for more details. + + 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> +#include <dirent.h> +#include <inttypes.h> +#include <libintl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <ldconfig.h> +#include <dl-cache.h> + +struct cache_entry +{ + char *lib; /* Library name. */ + char *path; /* Path to find library. */ + int flags; /* Flags to indicate kind of library. */ + unsigned int osversion; /* Required OS version. */ + uint64_t hwcap; /* Important hardware capabilities. */ + int bits_hwcap; /* Number of bits set in hwcap. */ + struct cache_entry *next; /* Next entry in list. */ +}; + +/* List of all cache entries. */ +static struct cache_entry *entries; + +static const char *flag_descr[] = +{ "libc4", "ELF", "libc5", "libc6"}; + +/* Print a single entry. */ +static void +print_entry (const char *lib, int flag, unsigned int osversion, + uint64_t hwcap, const char *key) +{ + printf ("\t%s (", lib); + switch (flag & FLAG_TYPE_MASK) + { + case FLAG_LIBC4: + case FLAG_ELF: + case FLAG_ELF_LIBC5: + case FLAG_ELF_LIBC6: + fputs (flag_descr[flag & FLAG_TYPE_MASK], stdout); + break; + default: + fputs (_("unknown"), stdout); + break; + } + switch (flag & FLAG_REQUIRED_MASK) + { + case FLAG_SPARC_LIB64: + fputs (",64bit", stdout); + break; + case FLAG_IA64_LIB64: + fputs (",IA-64", stdout); + break; + case FLAG_X8664_LIB64: + fputs (",x86-64", stdout); + break; + case FLAG_S390_LIB64: + fputs(",64bit", stdout); + break; + case FLAG_POWERPC_LIB64: + fputs(",64bit", stdout); + break; + case FLAG_MIPS64_LIBN32: + fputs(",N32", stdout); + break; + case FLAG_MIPS64_LIBN64: + fputs(",64bit", stdout); + case 0: + break; + default: + printf (",%d", flag & FLAG_REQUIRED_MASK); + break; + } + if (hwcap != 0) + printf (", hwcap: %#.16" PRIx64, hwcap); + if (osversion != 0) + { + static const char *const abi_tag_os[] = + { + [0] = "Linux", + [1] = "Hurd", + [2] = "Solaris", + [3] = "FreeBSD", + [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; + + printf (_(", OS ABI: %s %d.%d.%d"), + _(abi_tag_os[os > MAXTAG ? MAXTAG : os]), + (osversion >> 16) & 0xff, + (osversion >> 8) & 0xff, + osversion & 0xff); + } + printf (") => %s\n", key); +} + + +/* Print the whole cache file, if a file contains the new cache format + hidden in the old one, print the contents of the new format. */ +void +print_cache (const char *cache_name) +{ + size_t cache_size; + struct stat64 st; + int fd; + unsigned int i; + struct cache_file *cache; + struct cache_file_new *cache_new = NULL; + const char *cache_data; + int format = 0; + + fd = open (cache_name, O_RDONLY); + if (fd < 0) + error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), cache_name); + + if (fstat64 (fd, &st) < 0 + /* No need to map the file if it is empty. */ + || st.st_size == 0) + { + close (fd); + return; + } + + cache = mmap (0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (cache == MAP_FAILED) + error (EXIT_FAILURE, errno, _("mmap of cache file failed.\n")); + cache_size = st.st_size; + + if (cache_size < sizeof (struct cache_file)) + error (EXIT_FAILURE, 0, _("File is not a cache file.\n")); + + if (memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1)) + { + /* This can only be the new format without the old one. */ + cache_new = (struct cache_file_new *) cache; + + if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1) + || memcmp (cache_new->version, CACHE_VERSION, + sizeof CACHE_VERSION - 1)) + error (EXIT_FAILURE, 0, _("File is not a cache file.\n")); + format = 1; + /* This is where the strings start. */ + cache_data = (const char *) cache_new; + } + else + { + size_t offset = ALIGN_CACHE (sizeof (struct cache_file) + + (cache->nlibs + * sizeof (struct file_entry))); + /* This is where the strings start. */ + cache_data = (const char *) &cache->libs[cache->nlibs]; + + /* Check for a new cache embedded in the old format. */ + if (cache_size > + (offset + sizeof (struct cache_file_new))) + { + + cache_new = (struct cache_file_new *) ((void *)cache + offset); + + if (memcmp (cache_new->magic, CACHEMAGIC_NEW, + sizeof CACHEMAGIC_NEW - 1) == 0 + && memcmp (cache_new->version, CACHE_VERSION, + sizeof CACHE_VERSION - 1) == 0) + { + cache_data = (const char *) cache_new; + format = 1; + } + } + } + + if (format == 0) + { + printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name); + + /* Print everything. */ + for (i = 0; i < cache->nlibs; i++) + print_entry (cache_data + cache->libs[i].key, + cache->libs[i].flags, 0, 0, + cache_data + cache->libs[i].value); + } + else if (format == 1) + { + printf (_("%d libs found in cache `%s'\n"), + cache_new->nlibs, cache_name); + + /* Print everything. */ + for (i = 0; i < cache_new->nlibs; i++) + print_entry (cache_data + cache_new->libs[i].key, + cache_new->libs[i].flags, + cache_new->libs[i].osversion, + cache_new->libs[i].hwcap, + cache_data + cache_new->libs[i].value); + } + /* Cleanup. */ + munmap (cache, cache_size); + close (fd); +} + +/* Initialize cache data structures. */ +void +init_cache (void) +{ + entries = NULL; +} + + + +static +int compare (const struct cache_entry *e1, const struct cache_entry *e2) +{ + int res; + + /* We need to swap entries here to get the correct sort order. */ + res = _dl_cache_libcmp (e2->lib, e1->lib); + if (res == 0) + { + if (e1->flags < e2->flags) + return 1; + else if (e1->flags > e2->flags) + return -1; + /* Sort by most specific hwcap. */ + else if (e2->bits_hwcap > e1->bits_hwcap) + return 1; + else if (e2->bits_hwcap < e1->bits_hwcap) + return -1; + else if (e2->hwcap > e1->hwcap) + return 1; + else if (e2->hwcap < e1->hwcap) + return -1; + if (e2->osversion > e1->osversion) + return 1; + if (e2->osversion < e1->osversion) + return -1; + } + return res; +} + +/* Save the contents of the cache. */ +void +save_cache (const char *cache_name) +{ + struct cache_entry *entry; + int fd, idx_old, idx_new; + size_t total_strlen, len; + char *strings, *str, *temp_name; + struct cache_file *file_entries = NULL; + struct cache_file_new *file_entries_new = NULL; + size_t file_entries_size = 0; + size_t file_entries_new_size = 0; + unsigned int str_offset; + /* Number of cache entries. */ + int cache_entry_count = 0; + /* Number of normal cache entries. */ + int cache_entry_old_count = 0; + /* Pad for alignment of cache_file_new. */ + size_t pad; + + /* The cache entries are sorted already, save them in this order. */ + + /* Count the length of all strings. */ + /* The old format doesn't contain hwcap entries and doesn't contain + libraries in subdirectories with hwcaps entries. Count therefore + also all entries with hwcap == 0. */ + total_strlen = 0; + for (entry = entries; entry != NULL; entry = entry->next) + { + /* Account the final NULs. */ + total_strlen += strlen (entry->lib) + strlen (entry->path) + 2; + ++cache_entry_count; + if (entry->hwcap == 0) + ++cache_entry_old_count; + } + + /* Create the on disk cache structure. */ + /* First an array for all strings. */ + strings = (char *)xmalloc (total_strlen); + + if (opt_format != 2) + { + /* struct cache_file_new is 64-bit aligned on some arches while + only 32-bit aligned on other arches. Duplicate last old + cache entry so that new cache in ld.so.cache can be used by + both. */ + if (opt_format != 0) + cache_entry_old_count = (cache_entry_old_count + 1) & ~1; + + /* And the list of all entries in the old format. */ + file_entries_size = sizeof (struct cache_file) + + cache_entry_old_count * sizeof (struct file_entry); + file_entries = (struct cache_file *) xmalloc (file_entries_size); + + /* Fill in the header. */ + memset (file_entries, 0, sizeof (struct cache_file)); + memcpy (file_entries->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1); + + file_entries->nlibs = cache_entry_old_count; + } + + if (opt_format != 0) + { + /* And the list of all entries in the new format. */ + file_entries_new_size = sizeof (struct cache_file_new) + + cache_entry_count * sizeof (struct file_entry_new); + file_entries_new = + (struct cache_file_new *) xmalloc (file_entries_new_size); + + /* Fill in the header. */ + memset (file_entries_new, 0, sizeof (struct cache_file_new)); + memcpy (file_entries_new->magic, CACHEMAGIC_NEW, + sizeof CACHEMAGIC_NEW - 1); + memcpy (file_entries_new->version, CACHE_VERSION, + sizeof CACHE_VERSION - 1); + + file_entries_new->nlibs = cache_entry_count; + file_entries_new->len_strings = total_strlen; + } + + pad = ALIGN_CACHE (file_entries_size) - file_entries_size; + + /* If we have both formats, we hide the new format in the strings + table, we have to adjust all string indices for this so that + old libc5/glibc 2 dynamic linkers just ignore them. */ + if (opt_format != 0) + str_offset = file_entries_new_size; + else + str_offset = 0; + + str = strings; + for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL; + entry = entry->next, ++idx_new) + { + /* First the library. */ + if (opt_format != 2 && entry->hwcap == 0) + { + file_entries->libs[idx_old].flags = entry->flags; + /* XXX: Actually we can optimize here and remove duplicates. */ + file_entries->libs[idx_old].key = str_offset + pad; + } + if (opt_format != 0) + { + /* We could subtract file_entries_new_size from str_offset - + not doing so makes the code easier, the string table + always begins at the beginning of the the new cache + struct. */ + file_entries_new->libs[idx_new].flags = entry->flags; + file_entries_new->libs[idx_new].osversion = entry->osversion; + file_entries_new->libs[idx_new].hwcap = entry->hwcap; + file_entries_new->libs[idx_new].key = str_offset; + } + len = strlen (entry->lib); + str = stpcpy (str, entry->lib); + /* Account the final NUL. */ + ++str; + str_offset += len + 1; + /* Then the path. */ + if (opt_format != 2 && entry->hwcap == 0) + file_entries->libs[idx_old].value = str_offset + pad; + if (opt_format != 0) + file_entries_new->libs[idx_new].value = str_offset; + len = strlen (entry->path); + str = stpcpy (str, entry->path); + /* Account the final NUL. */ + ++str; + str_offset += len + 1; + /* Ignore entries with hwcap for old format. */ + if (entry->hwcap == 0) + ++idx_old; + } + + /* Duplicate last old cache entry if needed. */ + if (opt_format != 2 + && idx_old < cache_entry_old_count) + file_entries->libs[idx_old] = file_entries->libs[idx_old - 1]; + + /* Write out the cache. */ + + /* Write cache first to a temporary file and rename it later. */ + temp_name = xmalloc (strlen (cache_name) + 2); + sprintf (temp_name, "%s~", cache_name); + /* First remove an old copy if it exists. */ + if (unlink (temp_name) && errno != ENOENT) + error (EXIT_FAILURE, errno, _("Can't remove old temporary cache file %s"), + temp_name); + + /* Create file. */ + fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, + S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR); + if (fd < 0) + error (EXIT_FAILURE, errno, _("Can't create temporary cache file %s"), + temp_name); + + /* Write contents. */ + if (opt_format != 2) + { + if (write (fd, file_entries, file_entries_size) + != (ssize_t) file_entries_size) + error (EXIT_FAILURE, errno, _("Writing of cache data failed")); + } + if (opt_format != 0) + { + /* Align cache. */ + if (opt_format != 2) + { + char zero[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) + error (EXIT_FAILURE, errno, _("Writing of cache data failed")); + } + + if (write (fd, strings, total_strlen) != (ssize_t) total_strlen) + error (EXIT_FAILURE, errno, _("Writing of cache data failed.")); + + close (fd); + + /* Make sure user can always read cache file */ + if (chmod (temp_name, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR)) + error (EXIT_FAILURE, errno, + _("Changing access rights of %s to %#o failed"), temp_name, + S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR); + + /* Move temporary to its final location. */ + if (rename (temp_name, cache_name)) + error (EXIT_FAILURE, errno, _("Renaming of %s to %s failed"), temp_name, + cache_name); + + /* Free all allocated memory. */ + free (file_entries_new); + free (file_entries); + free (strings); + + while (entries) + { + entry = entries; + free (entry->path); + free (entry->lib); + entries = entries->next; + free (entry); + } +} + + +/* Add one library to the cache. */ +void +add_to_cache (const char *path, const char *lib, int flags, + unsigned int osversion, uint64_t hwcap) +{ + struct cache_entry *new_entry, *ptr, *prev; + char *full_path; + size_t len, i; + + new_entry = (struct cache_entry *) xmalloc (sizeof (struct cache_entry)); + + len = strlen (lib) + strlen (path) + 2; + + full_path = (char *) xmalloc (len); + snprintf (full_path, len, "%s/%s", path, lib); + + new_entry->lib = xstrdup (lib); + new_entry->path = full_path; + new_entry->flags = flags; + new_entry->osversion = osversion; + new_entry->hwcap = hwcap; + new_entry->bits_hwcap = 0; + + /* Count the number of bits set in the masked value. */ + for (i = 0; (~((1ULL << i) - 1) & hwcap) != 0 && i < 8 * sizeof (hwcap); ++i) + if ((hwcap & (1ULL << i)) != 0) + ++new_entry->bits_hwcap; + + + /* Keep the list sorted - search for right place to insert. */ + ptr = entries; + prev = entries; + while (ptr != NULL) + { + if (compare (ptr, new_entry) > 0) + break; + prev = ptr; + ptr = ptr->next; + } + /* Is this the first entry? */ + if (ptr == entries) + { + new_entry->next = entries; + entries = new_entry; + } + else + { + new_entry->next = prev->next; + prev->next = new_entry; + } +} diff --git a/libc/elf/check-localplt.c b/libc/elf/check-localplt.c new file mode 100644 index 000000000..b4358a8a3 --- /dev/null +++ b/libc/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/libc/elf/check-textrel.c b/libc/elf/check-textrel.c new file mode 100644 index 000000000..1a9a5ecdf --- /dev/null +++ b/libc/elf/check-textrel.c @@ -0,0 +1,199 @@ +/* Check for text relocations in DSOs. + 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. + + 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 <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 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]; + 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 (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 (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 + bit set. */ + 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_TEXTREL + || (tag == DT_FLAGS + && (SWAP (dyn[cnt].d_un.d_val) & DF_TEXTREL) != 0)) + { + /* Urgh! The DSO has text relocations. */ + printf ("%s: text relocations used\n", fname); + return 1; + } + } + + printf ("%s: OK\n", fname); + + return 0; +} + +# undef BITS +#else + +# define BITS 32 +# include "check-textrel.c" + +# define BITS 64 +# include "check-textrel.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/libc/elf/chroot_canon.c b/libc/elf/chroot_canon.c new file mode 100644 index 000000000..3ef2fdf08 --- /dev/null +++ b/libc/elf/chroot_canon.c @@ -0,0 +1,183 @@ +/* Return the canonical absolute name of a given file inside chroot. + Copyright (C) 1996,1997,1998,1999,2000,2001,2004,2005 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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. + + 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 General Public License for more details. + + 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> +#include <unistd.h> +#include <limits.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <errno.h> +#include <stddef.h> +#include <stdint.h> + +#include <ldconfig.h> + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +/* Return the canonical absolute name of file NAME as if chroot(CHROOT) was + done first. A canonical name does not contain any `.', `..' components + nor any repeated path separators ('/') or symlinks. All path components + must exist and NAME must be absolute filename. The result is malloc'd. + The returned name includes the CHROOT prefix. */ + +char * +chroot_canon (const char *chroot, const char *name) +{ + char *rpath; + char *dest; + char *extra_buf = NULL; + char *rpath_root; + const char *start; + const char *end; + const char *rpath_limit; + int num_links = 0; + size_t chroot_len = strlen (chroot); + + if (chroot_len < 1) + { + __set_errno (EINVAL); + return NULL; + } + + rpath = malloc (chroot_len + PATH_MAX); + if (rpath == NULL) + return NULL; + + rpath_limit = rpath + chroot_len + PATH_MAX; + + rpath_root = (char *) mempcpy (rpath, chroot, chroot_len) - 1; + if (*rpath_root != '/') + *++rpath_root = '/'; + dest = rpath_root + 1; + + for (start = end = name; *start; start = end) + { + struct stat64 st; + int n; + + /* Skip sequence of multiple path-separators. */ + while (*start == '/') + ++start; + + /* Find end of path component. */ + for (end = start; *end && *end != '/'; ++end) + /* Nothing. */; + + if (end - start == 0) + break; + else if (end - start == 1 && start[0] == '.') + /* nothing */; + else if (end - start == 2 && start[0] == '.' && start[1] == '.') + { + /* Back up to previous component, ignore if at root already. */ + if (dest > rpath_root + 1) + while ((--dest)[-1] != '/'); + } + else + { + size_t new_size; + + if (dest[-1] != '/') + *dest++ = '/'; + + if (dest + (end - start) >= rpath_limit) + { + ptrdiff_t dest_offset = dest - rpath; + char *new_rpath; + + new_size = rpath_limit - rpath; + if (end - start + 1 > PATH_MAX) + new_size += end - start + 1; + else + new_size += PATH_MAX; + new_rpath = (char *) realloc (rpath, new_size); + if (new_rpath == NULL) + goto error; + rpath = new_rpath; + rpath_limit = rpath + new_size; + + dest = rpath + dest_offset; + } + + dest = mempcpy (dest, start, end - start); + *dest = '\0'; + + if (lstat64 (rpath, &st) < 0) + { + if (*end == '\0') + goto done; + goto error; + } + + if (S_ISLNK (st.st_mode)) + { + char *buf = alloca (PATH_MAX); + size_t len; + + if (++num_links > MAXSYMLINKS) + { + __set_errno (ELOOP); + goto error; + } + + n = readlink (rpath, buf, PATH_MAX); + if (n < 0) + { + if (*end == '\0') + goto done; + goto error; + } + buf[n] = '\0'; + + if (!extra_buf) + extra_buf = alloca (PATH_MAX); + + len = strlen (end); + if ((long int) (n + len) >= PATH_MAX) + { + __set_errno (ENAMETOOLONG); + goto error; + } + + /* Careful here, end may be a pointer into extra_buf... */ + memmove (&extra_buf[n], end, len + 1); + name = end = memcpy (extra_buf, buf, n); + + if (buf[0] == '/') + dest = rpath_root + 1; /* It's an absolute symlink */ + else + /* Back up to previous component, ignore if at root already: */ + if (dest > rpath_root + 1) + while ((--dest)[-1] != '/'); + } + } + } + done: + if (dest > rpath_root + 1 && dest[-1] == '/') + --dest; + *dest = '\0'; + + return rpath; + + error: + free (rpath); + return NULL; +} diff --git a/libc/elf/circleload1.c b/libc/elf/circleload1.c new file mode 100644 index 000000000..990ff84a8 --- /dev/null +++ b/libc/elf/circleload1.c @@ -0,0 +1,166 @@ +#include <dlfcn.h> +#include <libintl.h> +#include <link.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define MAPS ((struct link_map *) _r_debug.r_map) + +static int +check_loaded_objects (const char **loaded) +{ + struct link_map *lm; + int n; + int *found = NULL; + int errors = 0; + + for (n = 0; loaded[n]; n++) + /* NOTHING */; + + if (n) + { + found = (int *) alloca (sizeof (int) * n); + memset (found, 0, sizeof (int) * n); + } + + printf(" Name\n"); + printf(" --------------------------------------------------------\n"); + 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_direct_opencount); + if (lm->l_type == lt_loaded && lm->l_name) + { + int match = 0; + for (n = 0; loaded[n] != NULL; n++) + { + if (strcmp (basename (loaded[n]), basename (lm->l_name)) == 0) + { + found[n] = 1; + match = 1; + break; + } + } + + if (match == 0) + { + ++errors; + printf ("ERRORS: %s is not unloaded\n", lm->l_name); + } + } + } + + for (n = 0; loaded[n] != NULL; n++) + { + if (found[n] == 0) + { + ++errors; + printf ("ERRORS: %s is not loaded\n", loaded[n]); + } + } + + return errors; +} + +static int +load_dso (const char **loading, int undef, int flag) +{ + void *obj; + const char *loaded[] = { NULL, NULL, NULL, NULL }; + int errors = 0; + const char *errstring; + + printf ("\nThis is what is in memory now:\n"); + errors += check_loaded_objects (loaded); + + printf ("Loading shared object %s: %s\n", loading[0], + flag == RTLD_LAZY ? "RTLD_LAZY" : "RTLD_NOW"); + obj = dlopen (loading[0], flag); + if (obj == NULL) + { + if (flag == RTLD_LAZY) + { + ++errors; + printf ("ERRORS: dlopen shouldn't fail for RTLD_LAZY\n"); + } + + errstring = dlerror (); + if (strstr (errstring, "undefined symbol") == 0 + || strstr (errstring, "circlemod2_undefined") == 0) + { + ++errors; + printf ("ERRORS: dlopen: `%s': Invalid error string\n", + errstring); + } + else + printf ("dlopen: %s\n", errstring); + } + else + { + if (undef && flag == RTLD_NOW) + { + ++errors; + printf ("ERRORS: dlopen shouldn't work for RTLD_NOW\n"); + } + + if (!undef) + { + int (*func) (void); + + func = dlsym (obj, "circlemod1"); + if (func == NULL) + { + ++errors; + printf ("ERRORS: cannot get address of \"circlemod1\": %s\n", + dlerror ()); + } + else if (func () != 3) + { + ++errors; + printf ("ERRORS: function \"circlemod1\" returned wrong result\n"); + } + } + + 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]); + dlclose (obj); + loaded[0] = NULL; + loaded[1] = NULL; + loaded[2] = NULL; + errors += check_loaded_objects (loaded); + } + + return errors; +} + +int +main (void) +{ + int errors = 0; + const char *loading[3]; + + 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"; + errors += load_dso (loading, 1, RTLD_LAZY); + errors += load_dso (loading, 1, RTLD_NOW); + + if (errors != 0) + printf ("%d errors found\n", errors); + + return errors; +} diff --git a/libc/elf/circlemod1.c b/libc/elf/circlemod1.c new file mode 100644 index 000000000..933ccd3c0 --- /dev/null +++ b/libc/elf/circlemod1.c @@ -0,0 +1,7 @@ +extern int circlemod2 (void); + +int +circlemod1 (void) +{ + return circlemod2 (); +} diff --git a/libc/elf/circlemod1a.c b/libc/elf/circlemod1a.c new file mode 100644 index 000000000..45f229136 --- /dev/null +++ b/libc/elf/circlemod1a.c @@ -0,0 +1 @@ +#include "circlemod1.c" diff --git a/libc/elf/circlemod2.c b/libc/elf/circlemod2.c new file mode 100644 index 000000000..ed8c1175f --- /dev/null +++ b/libc/elf/circlemod2.c @@ -0,0 +1,9 @@ +extern void circlemod2_undefined (void); +extern int circlemod3 (void); + +int +circlemod2 (void) +{ + circlemod2_undefined (); + return circlemod3 (); +} diff --git a/libc/elf/circlemod2a.c b/libc/elf/circlemod2a.c new file mode 100644 index 000000000..dc6410b28 --- /dev/null +++ b/libc/elf/circlemod2a.c @@ -0,0 +1,7 @@ +extern int circlemod3 (void); + +int +circlemod2 (void) +{ + return circlemod3 (); +} diff --git a/libc/elf/circlemod3.c b/libc/elf/circlemod3.c new file mode 100644 index 000000000..8d16fe682 --- /dev/null +++ b/libc/elf/circlemod3.c @@ -0,0 +1,14 @@ +extern int circlemod1 (void); +extern int circlemod2 (void); + +int +circlemod3 (void) +{ + return 3; +} + +int +circlemod3a (void) +{ + return circlemod1 () + circlemod2 (); +} diff --git a/libc/elf/circlemod3a.c b/libc/elf/circlemod3a.c new file mode 100644 index 000000000..f1b166ef8 --- /dev/null +++ b/libc/elf/circlemod3a.c @@ -0,0 +1 @@ +#include "circlemod3.c" diff --git a/libc/elf/constload1.c b/libc/elf/constload1.c new file mode 100644 index 000000000..7381beea8 --- /dev/null +++ b/libc/elf/constload1.c @@ -0,0 +1,32 @@ +#include <dlfcn.h> +#include <errno.h> +#include <error.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + int (*foo) (void); + void *h; + int ret; + + mtrace (); + + h = dlopen ("constload2.so", RTLD_LAZY | RTLD_GLOBAL); + if (h == NULL) + error (EXIT_FAILURE, errno, "cannot load module \"constload2.so\""); + foo = dlsym (h, "foo"); + ret = foo (); + /* Note that the following dlclose() call cannot unload the objects. + Due to the introduced relocation dependency constload2.so depends + on constload3.so and the dependencies of constload2.so on constload3.so + is not visible to ld.so since it's done using dlopen(). */ + if (dlclose (h) != 0) + { + puts ("failed to close"); + exit (EXIT_FAILURE); + } + return ret; +} diff --git a/libc/elf/constload2.c b/libc/elf/constload2.c new file mode 100644 index 000000000..bf1bf182f --- /dev/null +++ b/libc/elf/constload2.c @@ -0,0 +1,50 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +extern int bar (void); +extern int baz (void); +extern int foo (void); +extern void __attribute__ ((__constructor__)) init (void); + +void *h; + +int +foo (void) +{ + return 42 + bar (); +} + +int +baz (void) +{ + return -21; +} + + +void +__attribute__ ((__constructor__)) +init (void) +{ + h = dlopen ("constload3.so", RTLD_GLOBAL | RTLD_LAZY); + if (h == NULL) + { + puts ("failed to load constload3"); + exit (1); + } + else + puts ("succeeded loading constload3"); +} + +static void +__attribute__ ((__destructor__)) +fini (void) +{ + if (dlclose (h) != 0) + { + puts ("failed to unload constload3"); + exit (1); + } + else + puts ("succeeded unloading constload3"); +} diff --git a/libc/elf/constload3.c b/libc/elf/constload3.c new file mode 100644 index 000000000..9c37620bb --- /dev/null +++ b/libc/elf/constload3.c @@ -0,0 +1,8 @@ +extern int baz (void); +extern int bar (void); + +int +bar (void) +{ + return -21 + baz (); +} diff --git a/libc/elf/dblload.c b/libc/elf/dblload.c new file mode 100644 index 000000000..52389a60c --- /dev/null +++ b/libc/elf/dblload.c @@ -0,0 +1,53 @@ +#include <dlfcn.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + + +int +main (void) +{ + void *p1; + void *p2; + int (*fp) (void); + int result; + + mtrace (); + + p1 = dlopen ("dblloadmod1.so", RTLD_LAZY); + if (p1 == NULL) + { + printf ("cannot open dblloadmod1.so: %s\n", dlerror ()); + exit (EXIT_FAILURE); + } + + p2 = dlopen ("dblloadmod2.so", RTLD_LAZY); + if (p1 == NULL) + { + printf ("cannot open dblloadmod2.so: %s\n", dlerror ()); + exit (EXIT_FAILURE); + } + + fp = dlsym (p1, "foo"); + if (fp == NULL) + { + printf ("cannot get function \"foo\": %s\n", dlerror ()); + exit (EXIT_FAILURE); + } + + result = fp (); + + if (dlclose (p1) != 0) + { + printf ("error while closing dblloadmod1.so: %s\n", dlerror ()); + exit (EXIT_FAILURE); + } + + if (dlclose (p2) != 0) + { + printf ("error while closing dblloadmod2.so: %s\n", dlerror ()); + exit (EXIT_FAILURE); + } + + return result; +} diff --git a/libc/elf/dblloadmod1.c b/libc/elf/dblloadmod1.c new file mode 100644 index 000000000..ecec29ec6 --- /dev/null +++ b/libc/elf/dblloadmod1.c @@ -0,0 +1,8 @@ +extern int bar (void); +extern int foo (void); + +int +foo (void) +{ + return 10 + bar (); +} diff --git a/libc/elf/dblloadmod2.c b/libc/elf/dblloadmod2.c new file mode 100644 index 000000000..3e20aa941 --- /dev/null +++ b/libc/elf/dblloadmod2.c @@ -0,0 +1,15 @@ +extern int bar (void); +extern int baz (void); +extern int xyzzy (void); + +int +baz (void) +{ + return -42; +} + +int +xyzzy (void) +{ + return 10 + bar (); +} diff --git a/libc/elf/dblloadmod3.c b/libc/elf/dblloadmod3.c new file mode 100644 index 000000000..80ac3a637 --- /dev/null +++ b/libc/elf/dblloadmod3.c @@ -0,0 +1,8 @@ +extern int bar (void); +extern int baz (void); + +int +bar (void) +{ + return 32 + baz (); +} diff --git a/libc/elf/dblunload.c b/libc/elf/dblunload.c new file mode 100644 index 000000000..ab0b2a5e9 --- /dev/null +++ b/libc/elf/dblunload.c @@ -0,0 +1,53 @@ +#include <dlfcn.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + + +int +main (void) +{ + void *p1; + void *p2; + int (*fp) (void); + int result; + + mtrace (); + + p1 = dlopen ("dblloadmod1.so", RTLD_LAZY); + if (p1 == NULL) + { + printf ("cannot load dblloadmod1.so: %s\n", dlerror ()); + exit (EXIT_FAILURE); + } + + p2 = dlopen ("dblloadmod2.so", RTLD_LAZY); + if (p2 == NULL) + { + printf ("cannot load dblloadmod2.so: %s\n", dlerror ()); + exit (EXIT_FAILURE); + } + + if (dlclose (p1) != 0) + { + printf ("error while closing dblloadmod1.so: %s\n", dlerror ()); + exit (EXIT_FAILURE); + } + + fp = dlsym (p2, "xyzzy"); + if (fp == NULL) + { + printf ("cannot get function \"xyzzy\": %s\n", dlerror ()); + exit (EXIT_FAILURE); + } + + result = fp (); + + if (dlclose (p2) != 0) + { + printf ("error while closing dblloadmod2.so: %s\n", dlerror ()); + exit (EXIT_FAILURE); + } + + return result; +} diff --git a/libc/elf/dep1.c b/libc/elf/dep1.c new file mode 100644 index 000000000..7ef47adb4 --- /dev/null +++ b/libc/elf/dep1.c @@ -0,0 +1,25 @@ +#include <unistd.h> + +extern int dep1 (void); +extern int dep2 (void); +extern int dep4 (void); + +static void +__attribute__ ((constructor)) +init (void) +{ + write (1, "3", 1); +} + +static void +__attribute__ ((destructor)) +fini (void) +{ + write (1, "6", 1); +} + +int +dep1 (void) +{ + return dep4 () - dep2 (); +} diff --git a/libc/elf/dep2.c b/libc/elf/dep2.c new file mode 100644 index 000000000..749036a4e --- /dev/null +++ b/libc/elf/dep2.c @@ -0,0 +1,25 @@ +#include <unistd.h> + +extern int dep2 (void); +extern int dep3 (void); +extern int dep4 (void); + +static void +__attribute__ ((constructor)) +init (void) +{ + write (1, "2", 1); +} + +static void +__attribute__ ((destructor)) +fini (void) +{ + write (1, "7", 1); +} + +int +dep2 (void) +{ + return dep3 () - dep4 (); +} diff --git a/libc/elf/dep3.c b/libc/elf/dep3.c new file mode 100644 index 000000000..3df628200 --- /dev/null +++ b/libc/elf/dep3.c @@ -0,0 +1,23 @@ +#include <unistd.h> + +extern int dep3 (void); + +static void +__attribute__ ((constructor)) +init (void) +{ + write (1, "0", 1); +} + +static void +__attribute__ ((destructor)) +fini (void) +{ + write (1, "9\n", 2); +} + +int +dep3 (void) +{ + return 42; +} diff --git a/libc/elf/dep4.c b/libc/elf/dep4.c new file mode 100644 index 000000000..c496d6f53 --- /dev/null +++ b/libc/elf/dep4.c @@ -0,0 +1,24 @@ +#include <unistd.h> + +extern int dep3 (void); +extern int dep4 (void); + +static void +__attribute__ ((constructor)) +init (void) +{ + write (1, "1", 1); +} + +static void +__attribute__ ((destructor)) +fini (void) +{ + write (1, "8", 1); +} + +int +dep4 (void) +{ + return dep3 (); +} diff --git a/libc/elf/dl-addr.c b/libc/elf/dl-addr.c new file mode 100644 index 000000000..5d3719adb --- /dev/null +++ b/libc/elf/dl-addr.c @@ -0,0 +1,162 @@ +/* Locate the shared object symbol nearest a given address. + 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 + 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 <dlfcn.h> +#include <stddef.h> +#include <ldsodefs.h> + + +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); + + /* Protect against concurrent loads and unloads. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + /* Find the highest-addressed object that ADDRESS is not below. */ + struct link_map *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) + { + /* 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) + { + 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; + } + + match = l; + break; + } + + int result = 0; + if (match != NULL) + { + /* 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]; + + 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) + { + Elf32_Word symndx = match->l_gnu_buckets[bucket]; + if (symndx != 0) + { + const Elf32_Word *hasharr = &match->l_gnu_chain_zero[symndx]; + + do + { + /* The hash table never references local symbols + so we can omit that test here. */ + if (symtab[symndx].st_shndx != SHN_UNDEF +#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); + } + } + } + else + { + const ElfW(Sym) *symtabend; + if (match->l_info[DT_HASH] != NULL) + symtabend = (symtab + + ((Elf_Symndx *) D_PTR (match, l_info[DT_HASH]))[1]); + else + /* There is no direct way to determine the number of symbols in the + dynamic symbol table and no hash table is present. The ELF + binary is ill-formed but what shall we do? Use the beginning of + the string table which generally follows the symbol table. */ + symtabend = (const ElfW(Sym) *) strtab; + + 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_shndx != SHN_UNDEF + && 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 (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 + { + /* No symbol matches. We return only the containing object. */ + info->dli_sname = NULL; + info->dli_saddr = NULL; + } + + result = 1; + } + + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + return result; +} +libc_hidden_def (_dl_addr) diff --git a/libc/elf/dl-brk.c b/libc/elf/dl-brk.c new file mode 100644 index 000000000..c37cdfec3 --- /dev/null +++ b/libc/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/libc/elf/dl-cache.c b/libc/elf/dl-cache.c new file mode 100644 index 000000000..29886e194 --- /dev/null +++ b/libc/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 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 && platform != -1 \ + && (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/libc/elf/dl-caller.c b/libc/elf/dl-caller.c new file mode 100644 index 000000000..b0c1264d0 --- /dev/null +++ b/libc/elf/dl-caller.c @@ -0,0 +1,87 @@ +/* Check whether caller comes from the right place. + Copyright (C) 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 <assert.h> +#include <ldsodefs.h> +#include <stddef.h> +#include <caller.h> +#include <gnu/lib-names.h> + + +int +attribute_hidden +_dl_check_caller (const void *caller, enum allowmask mask) +{ + static const char expected1[] = LIBC_SO; + static const char expected2[] = LIBDL_SO; +#ifdef LIBPTHREAD_SO + static const char expected3[] = LIBPTHREAD_SO; +#endif + static const char expected4[] = LD_SO; + + 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 >= (const void *) l->l_map_start + && caller < (const void *) l->l_text_end) + { + /* The address falls into this DSO's address range. Check the + name. */ + if ((mask & allow_libc) && strcmp (expected1, l->l_name) == 0) + return 0; + if ((mask & allow_libdl) && strcmp (expected2, l->l_name) == 0) + return 0; +#ifdef LIBPTHREAD_SO + if ((mask & allow_libpthread) && strcmp (expected3, l->l_name) == 0) + return 0; +#endif + if ((mask & allow_ldso) && strcmp (expected4, l->l_name) == 0) + return 0; + + struct libname_list *runp = l->l_libname; + + while (runp != NULL) + { + if ((mask & allow_libc) && strcmp (expected1, runp->name) == 0) + return 0; + if ((mask & allow_libdl) && strcmp (expected2, runp->name) == 0) + return 0; +#ifdef LIBPTHREAD_SO + if ((mask & allow_libpthread) + && strcmp (expected3, runp->name) == 0) + return 0; +#endif + if ((mask & allow_ldso) && strcmp (expected4, runp->name) == 0) + return 0; + + runp = runp->next; + } + + break; + } + + /* Maybe the dynamic linker is not yet on the list. */ + if ((mask & allow_ldso) != 0 + && caller >= (const void *) GL(dl_rtld_map).l_map_start + && caller < (const void *) GL(dl_rtld_map).l_text_end) + return 0; + + /* No valid caller. */ + return 1; +} diff --git a/libc/elf/dl-close.c b/libc/elf/dl-close.c new file mode 100644 index 000000000..754dd678f --- /dev/null +++ b/libc/elf/dl-close.c @@ -0,0 +1,690 @@ +/* Close a shared object opened by `_dl_open'. + Copyright (C) 1996-2002, 2003, 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. + + 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 <dlfcn.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> + + +/* Type of the constructor functions. */ +typedef void (*fini_t) (void); + + +#ifdef USE_TLS +/* Returns true we an non-empty was found. */ +static bool +remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, + bool should_be_there) +{ + if (idx - disp >= listp->len) + { + if (listp->next == NULL) + { + /* The index is not actually valid in the slotinfo list, + because this object was closed before it was fully set + up due to some error. */ + assert (! should_be_there); + } + else + { + if (remove_slotinfo (idx, listp->next, disp + listp->len, + should_be_there)) + return true; + + /* No non-empty entry. Search from the end of this element's + slotinfo array. */ + idx = disp + listp->len; + } + } + else + { + struct link_map *old_map = listp->slotinfo[idx - disp].map; + + /* The entry might still be in its unused state if we are closing an + object that wasn't fully set up. */ + if (__builtin_expect (old_map != NULL, 1)) + { + assert (old_map->l_tls_modid == idx); + + /* Mark the entry as unused. */ + listp->slotinfo[idx - disp].gen = GL(dl_tls_generation) + 1; + listp->slotinfo[idx - disp].map = NULL; + } + + /* If this is not the last currently used entry no need to look + further. */ + if (idx != GL(dl_tls_max_dtv_idx)) + return true; + } + + while (idx - disp > (disp == 0 ? 1 + GL(dl_tls_static_nelem) : 0)) + { + --idx; + + if (listp->slotinfo[idx - disp].map != NULL) + { + /* Found a new last used index. */ + GL(dl_tls_max_dtv_idx) = idx; + return true; + } + } + + /* No non-entry in this list element. */ + return false; +} +#endif + + +void +_dl_close (void *_map) +{ + struct link_map *map = _map; + Lmid_t ns = map->l_ns; + unsigned int i; + /* 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_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)); + + /* One less direct use. */ + --map->l_direct_opencount; + + /* 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)) + _dl_debug_printf ("\nclosing file=%s; direct_opencount=%u\n", + map->l_name, map->l_direct_opencount); + + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + return; + } + + retry: + dl_close_state = pending; + +#ifdef USE_TLS + bool any_tls = false; +#endif + const unsigned int nloaded = GL(dl_ns)[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 = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) + { + l->l_idx = idx; + maps[idx] = l; + ++idx; + } + 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 = -1; + + /* 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 != -1) + { + assert ((*lp)->l_idx >= 0 && (*lp)->l_idx < nloaded); + + if (!used[(*lp)->l_idx]) + { + used[(*lp)->l_idx] = 1; + if ((*lp)->l_idx - 1 < done_index) + done_index = (*lp)->l_idx - 1; + } + } + + ++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 != -1) + { + assert (jmap->l_idx >= 0 && jmap->l_idx < nloaded); + + if (!used[jmap->l_idx]) + { + used[jmap->l_idx] = 1; + if (jmap->l_idx - 1 < done_index) + done_index = jmap->l_idx - 1; + } + } + } + } + + /* Sort the entries. */ + _dl_sort_fini (GL(dl_ns)[ns]._ns_loaded, maps, nloaded, used, ns); + + /* Call all termination functions at once. */ +#ifdef SHARED + bool do_audit = GLRO(dl_naudit) > 0 && !GL(dl_ns)[ns]._ns_loaded->l_auditing; +#endif + bool unload_any = false; + unsigned int first_loaded = ~0; + for (i = 0; i < nloaded; ++i) + { + struct link_map *imap = maps[i]; + + /* All elements must be in the same namespace. */ + assert (imap->l_ns == ns); + + if (!used[i]) + { + 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, ns); + + if (imap->l_info[DT_FINI_ARRAY] != NULL) + { + ElfW(Addr) *array = + (ElfW(Addr) *) (imap->l_addr + + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr); + unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val + / sizeof (ElfW(Addr))); + + while (sz-- > 0) + ((fini_t) array[sz]) (); + } + + /* Next try the old-style destructor. */ + if (imap->l_info[DT_FINI] != NULL) + (*(void (*) (void)) DL_DT_FINI_ADDRESS + (imap, ((void *) imap->l_addr + + imap->l_info[DT_FINI]->d_un.d_ptr))) (); + } + +#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); + + afct = afct->next; + } + } +#endif + + /* This object must not be used anymore. */ + imap->l_removed = 1; + + /* We indeed have an object to remove. */ + unload_any = true; + + /* Remember where the first dynamically loaded object is. */ + if (i < first_loaded) + first_loaded = i; + } + /* Else used[i]. */ + else if (imap->l_type == lt_loaded) + { + 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; + + for (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 other map's + l_searchlist address. */ + if (imap->l_scope[cnt] != &imap->l_symbolic_searchlist) + { + struct link_map *tmap; + + tmap = (struct link_map *) ((char *) imap->l_scope[cnt] + - offsetof (struct link_map, + l_searchlist)); + assert (tmap->l_ns == ns); + if (tmap->l_idx != -1) + { + imap->l_scope[cnt] = &imap->l_searchlist; + break; + } + } + } + + /* The loader is gone, so mark the object as not having one. + Note: l_idx != -1 -> object will be removed. */ + if (imap->l_loader != NULL && imap->l_loader->l_idx != -1) + 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; + +#ifdef SHARED + /* Auditing checkpoint: we will start deleting objects. */ + if (__builtin_expect (do_audit, 0)) + { + struct link_map *head = GL(dl_ns)[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); + + afct = afct->next; + } + } + } +#endif + + /* Notify the debugger we are about to remove some loaded objects. */ + struct r_debug *r = _dl_debug_initialize (0, ns); + r->r_state = RT_DELETE; + _dl_debug_state (); + +#ifdef USE_TLS + size_t tls_free_start; + size_t tls_free_end; + tls_free_start = tls_free_end = NO_TLS_OFFSET; +#endif + + /* Check each element of the search list to see if all references to + it are gone. */ + for (i = first_loaded; i < nloaded; ++i) + { + struct link_map *imap = maps[i]; + if (!used[i]) + { + 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)[ns]._ns_main_searchlist->r_nlist; + + do + --cnt; + while (GL(dl_ns)[ns]._ns_main_searchlist->r_list[cnt] != imap); + + /* The object was already correctly registered. */ + while (++cnt + < GL(dl_ns)[ns]._ns_main_searchlist->r_nlist) + GL(dl_ns)[ns]._ns_main_searchlist->r_list[cnt - 1] + = GL(dl_ns)[ns]._ns_main_searchlist->r_list[cnt]; + + --GL(dl_ns)[ns]._ns_main_searchlist->r_nlist; + } + +#ifdef USE_TLS + /* Remove the object from the dtv slotinfo array if it uses TLS. */ + if (__builtin_expect (imap->l_tls_blocksize > 0, 0)) + { + any_tls = true; + + 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); + + if (imap->l_tls_offset != NO_TLS_OFFSET) + { + /* Collect a contiguous chunk built from the objects in + this search list, going in either direction. When the + whole chunk is at the end of the used area then we can + reclaim it. */ +# if TLS_TCB_AT_TP + if (tls_free_start == NO_TLS_OFFSET + || (size_t) imap->l_tls_offset == tls_free_start) + { + /* Extend the contiguous chunk being reclaimed. */ + tls_free_start + = imap->l_tls_offset - imap->l_tls_blocksize; + + if (tls_free_end == NO_TLS_OFFSET) + tls_free_end = imap->l_tls_offset; + } + else if (imap->l_tls_offset - imap->l_tls_blocksize + == tls_free_end) + /* Extend the chunk backwards. */ + tls_free_end = imap->l_tls_offset; + else + { + /* This isn't contiguous with the last chunk freed. + One of them will be leaked unless we can free + one block right away. */ + if (tls_free_end == GL(dl_tls_static_used)) + { + GL(dl_tls_static_used) = tls_free_start; + tls_free_end = imap->l_tls_offset; + tls_free_start + = tls_free_end - imap->l_tls_blocksize; + } + else if ((size_t) imap->l_tls_offset + == GL(dl_tls_static_used)) + GL(dl_tls_static_used) + = imap->l_tls_offset - imap->l_tls_blocksize; + else if (tls_free_end < (size_t) imap->l_tls_offset) + { + /* We pick the later block. It has a chance to + be freed. */ + tls_free_end = imap->l_tls_offset; + tls_free_start + = tls_free_end - imap->l_tls_blocksize; + } + } +# elif TLS_DTV_AT_TP + if ((size_t) imap->l_tls_offset == tls_free_end) + /* Extend the contiguous chunk being reclaimed. */ + tls_free_end -= imap->l_tls_blocksize; + else if (imap->l_tls_offset + imap->l_tls_blocksize + == tls_free_start) + /* Extend the chunk backwards. */ + tls_free_start = imap->l_tls_offset; + else + { + /* This isn't contiguous with the last chunk freed. + One of them will be leaked. */ + if (tls_free_end == GL(dl_tls_static_used)) + GL(dl_tls_static_used) = tls_free_start; + tls_free_start = imap->l_tls_offset; + tls_free_end = tls_free_start + imap->l_tls_blocksize; + } +# else +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" +# endif + } + } +#endif + + /* We can unmap all the maps at once. We determined the + start address and length when we loaded the object and + the `munmap' call does the rest. */ + DL_UNMAP (imap); + + /* Finally, unlink the data structure and free it. */ + if (imap->l_prev != NULL) + imap->l_prev->l_next = imap->l_next; + else + { +#ifdef SHARED + assert (ns != LM_ID_BASE); +#endif + GL(dl_ns)[ns]._ns_loaded = imap->l_next; + } + + --GL(dl_ns)[ns]._ns_nloaded; + if (imap->l_next != NULL) + imap->l_next->l_prev = imap->l_prev; + + free (imap->l_versions); + if (imap->l_origin != (char *) -1) + free ((char *) imap->l_origin); + + free (imap->l_reldeps); + + /* 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. */ + + struct libname_list *lnp = imap->l_libname; + do + { + struct libname_list *this = lnp; + lnp = lnp->next; + if (!this->dont_free) + free (this); + } + while (lnp != NULL); + + /* Remove the searchlists. */ + free (imap->l_initfini); + + /* Remove the scope array if we allocated it. */ + if (imap->l_scope != imap->l_scope_mem) + free (imap->l_scope); + + if (imap->l_phdr_allocated) + free ((void *) imap->l_phdr); + + if (imap->l_rpath_dirs.dirs != (void *) -1) + free (imap->l_rpath_dirs.dirs); + if (imap->l_runpath_dirs.dirs != (void *) -1) + free (imap->l_runpath_dirs.dirs); + + free (imap); + } + } + +#ifdef USE_TLS + /* If we removed any object which uses TLS bump the generation counter. */ + if (any_tls) + { + if (__builtin_expect (++GL(dl_tls_generation) == 0, 0)) + _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 = GL(dl_ns)[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->r_state = RT_CONSISTENT; + _dl_debug_state (); + + /* Recheck if we need to retry, release the lock. */ + out: + if (dl_close_state == rerun) + goto retry; + + dl_close_state = not_pending; + __rtld_lock_unlock_recursive (GL(dl_load_lock)); +} + + +#ifdef USE_TLS +static bool __libc_freeres_fn_section +free_slotinfo (struct dtv_slotinfo_list **elemp) +{ + size_t cnt; + + if (*elemp == NULL) + /* Nothing here, all is removed (or there never was anything). */ + return true; + + if (!free_slotinfo (&(*elemp)->next)) + /* We cannot free the entry. */ + return false; + + /* That cleared our next pointer for us. */ + + for (cnt = 0; cnt < (*elemp)->len; ++cnt) + if ((*elemp)->slotinfo[cnt].map != NULL) + /* Still used. */ + return false; + + /* We can remove the list element. */ + free (*elemp); + *elemp = NULL; + + return true; +} +#endif + + +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 + // 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; + + /* Put the old map in. */ + GL(dl_ns)[ns]._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; + + /* Now free the old map. */ + free (old); + } + +#ifdef USE_TLS + if (USE___THREAD || GL(dl_tls_dtv_slotinfo_list) != NULL) + { + /* Free the memory allocated for the dtv slotinfo array. We can do + this only if all modules which used this memory are unloaded. */ +# ifdef SHARED + if (GL(dl_initial_dtv) == NULL) + /* There was no initial TLS setup, it was set up later when + it used the normal malloc. */ + free_slotinfo (&GL(dl_tls_dtv_slotinfo_list)); + else +# endif + /* 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 +} diff --git a/libc/elf/dl-conflict.c b/libc/elf/dl-conflict.c new file mode 100644 index 000000000..9b49e77d2 --- /dev/null +++ b/libc/elf/dl-conflict.c @@ -0,0 +1,70 @@ +/* Resolve conflicts against already prelinked libraries. + 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. + + 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; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <libintl.h> +#include <stdlib.h> +#include <unistd.h> +#include <ldsodefs.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/types.h> +#include "dynamic-link.h" + +void +_dl_resolve_conflicts (struct link_map *l, ElfW(Rela) *conflict, + ElfW(Rela) *conflictend) +{ +#if ! ELF_MACHINE_NO_RELA + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_RELOC, 0)) + _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 + data. */ + + /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code. */ +#define RESOLVE_MAP(ref, version, flags) (*ref = NULL, NULL) +#define RESOLVE(ref, version, flags) (*ref = NULL, 0) +#define CHECK_STATIC_TLS(ref_map, sym_map) ((void) 0) +#define RESOLVE_CONFLICT_FIND_MAP(map, r_offset) \ + do { \ + while ((resolve_conflict_map->l_map_end < (ElfW(Addr)) (r_offset)) \ + || (resolve_conflict_map->l_map_start > (ElfW(Addr)) (r_offset))) \ + resolve_conflict_map = resolve_conflict_map->l_next; \ + \ + (map) = resolve_conflict_map; \ + } while (0) + + /* Prelinking makes no sense for anything but the main namespace. */ + assert (l->l_ns == LM_ID_BASE); + struct link_map *resolve_conflict_map __attribute__ ((__unused__)) + = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + +#include "dynamic-link.h" + + GL(dl_num_cache_relocations) += conflictend - conflict; + + for (; conflict < conflictend; ++conflict) + elf_machine_rela (l, conflict, NULL, NULL, (void *) conflict->r_offset); + } +#endif +} diff --git a/libc/elf/dl-debug.c b/libc/elf/dl-debug.c new file mode 100644 index 000000000..d00fe87fb --- /dev/null +++ b/libc/elf/dl-debug.c @@ -0,0 +1,78 @@ +/* Communicate dynamic linker state to the debugger at runtime. + 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 + 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> + + +/* 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 + to examine and it looks for this particular symbol name. */ +struct r_debug _r_debug; + + +/* Initialize _r_debug if it has not already been done. The argument is + the run-time load address of the dynamic linker, to be put in + _r_debug.r_ldbase. Returns the address of _r_debug. */ + +struct r_debug * +internal_function +_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) +{ + struct r_debug *r; + + if (ns == LM_ID_BASE) + r = &_r_debug; + else + r = &GL(dl_ns)[ns]._ns_debug; + + if (r->r_brk == 0 || ldbase != 0) + { + /* Tell the debugger where to find the map of loaded objects. */ + 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; +} + + +/* This function exists solely to have a breakpoint set on it by the + debugger. The debugger is supposed to find this function's address by + examining the r_brk member of struct r_debug, but GDB 4.15 in fact looks + for this particular symbol name in the PT_INTERP file. */ +void +_dl_debug_state (void) +{ +} +rtld_hidden_def (_dl_debug_state) diff --git a/libc/elf/dl-deps.c b/libc/elf/dl-deps.c new file mode 100644 index 000000000..c35cc977f --- /dev/null +++ b/libc/elf/dl-deps.c @@ -0,0 +1,643 @@ +/* Load the dependencies of a mapped object. + 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 + 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 <dlfcn.h> +#include <errno.h> +#include <libintl.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> +#include <ldsodefs.h> + +#include <dl-dst.h> + +/* Whether an shared object references one or more auxiliary objects + is signaled by the AUXTAG entry in l_info. */ +#define AUXTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \ + + DT_EXTRATAGIDX (DT_AUXILIARY)) +/* Whether an shared object references one or more auxiliary objects + is signaled by the AUXTAG entry in l_info. */ +#define FILTERTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \ + + DT_EXTRATAGIDX (DT_FILTER)) + + +/* When loading auxiliary objects we must ignore errors. It's ok if + an object is missing. */ +struct openaux_args + { + /* The arguments to openaux. */ + struct link_map *map; + int trace_mode; + int open_mode; + const char *strtab; + const char *name; + + /* The return value of openaux. */ + struct link_map *aux; + }; + +static void +openaux (void *a) +{ + struct openaux_args *args = (struct openaux_args *) a; + + args->aux = _dl_map_object (args->map, args->name, 0, + (args->map->l_type == lt_executable + ? lt_library : args->map->l_type), + args->trace_mode, args->open_mode, + args->map->l_ns); +} + +static ptrdiff_t +internal_function +_dl_build_local_scope (struct link_map **list, struct link_map *map) +{ + struct link_map **p = list; + struct link_map **q; + + *p++ = map; + map->l_reserved = 1; + if (map->l_initfini) + for (q = map->l_initfini + 1; *q; ++q) + if (! (*q)->l_reserved) + p += _dl_build_local_scope (p, *q); + return p - list; +} + + +/* We use a very special kind of list to track the path + through the list of loaded shared objects. We have to + produce a flat list with unique members of all involved objects. +*/ +struct list + { + int done; /* Nonzero if this map was processed. */ + struct link_map *map; /* The data. */ + struct list *next; /* Elements for normal list. */ + }; + + +/* Macro to expand DST. It is an macro since we use `alloca'. */ +#define expand_dst(l, str, fatal) \ + ({ \ + const char *__str = (str); \ + const char *__result = __str; \ + size_t __dst_cnt = DL_DST_COUNT (__str, 0); \ + \ + if (__dst_cnt != 0) \ + { \ + char *__newp; \ + \ + /* DST must not appear in SUID/SGID programs. */ \ + if (INTUSE(__libc_enable_secure)) \ + _dl_signal_error (0, __str, NULL, N_("\ +DST not allowed in SUID/SGID programs")); \ + \ + __newp = (char *) alloca (DL_DST_REQUIRED (l, __str, strlen (__str), \ + __dst_cnt)); \ + \ + __result = _dl_dst_substitute (l, __str, __newp, 0); \ + \ + if (*__result == '\0') \ + { \ + /* The replacement for the DST is not known. We can't \ + processed. */ \ + if (fatal) \ + _dl_signal_error (0, __str, NULL, N_("\ +empty dynamics string token substitution")); \ + else \ + { \ + /* This is for DT_AUXILIARY. */ \ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))\ + _dl_debug_printf (N_("\ +cannot load auxiliary `%s' because of empty dynamic string token " \ + "substitution\n"), __str); \ + continue; \ + } \ + } \ + } \ + \ + __result; }) + + +void +internal_function +_dl_map_object_deps (struct link_map *map, + struct link_map **preloads, unsigned int npreloads, + int trace_mode, int open_mode) +{ + struct list *known = __alloca (sizeof *known * (1 + npreloads + 1)); + struct list *runp, *tail; + unsigned int nlist, i; + /* Object name. */ + const char *name; + int errno_saved; + int errno_reason; + const char *errstring; + const char *objname; + + auto inline void preload (struct link_map *map); + + inline void preload (struct link_map *map) + { + known[nlist].done = 0; + known[nlist].map = map; + known[nlist].next = &known[nlist + 1]; + + ++nlist; + /* We use `l_reserved' as a mark bit to detect objects we have + already put in the search list and avoid adding duplicate + elements later in the list. */ + map->l_reserved = 1; + } + + /* No loaded object so far. */ + nlist = 0; + + /* First load MAP itself. */ + preload (map); + + /* Add the preloaded items after MAP but before any of its dependencies. */ + for (i = 0; i < npreloads; ++i) + preload (preloads[i]); + + /* Terminate the lists. */ + known[nlist - 1].next = NULL; + + /* Pointer to last unique object. */ + tail = &known[nlist - 1]; + + /* Process each element of the search list, loading each of its + auxiliary objects and immediate dependencies. Auxiliary objects + will be added in the list before the object itself and + dependencies will be appended to the list as we step through it. + This produces a flat, ordered list that represents a + breadth-first search of the dependency tree. + + The whole process is complicated by the fact that we better + should use alloca for the temporary list elements. But using + alloca means we cannot use recursive function calls. */ + errno_saved = errno; + errno_reason = 0; + errstring = NULL; + errno = 0; + name = NULL; + for (runp = known; runp; ) + { + struct link_map *l = runp->map; + struct link_map **needed = NULL; + unsigned int nneeded = 0; + + /* Unless otherwise stated, this object is handled. */ + runp->done = 1; + + /* Allocate a temporary record to contain the references to the + dependencies of this object. */ + if (l->l_searchlist.r_list == NULL && l->l_initfini == NULL + && l != map && l->l_ldnum > 0) + needed = (struct link_map **) alloca (l->l_ldnum + * sizeof (struct link_map *)); + + if (l->l_info[DT_NEEDED] || l->l_info[AUXTAG] || l->l_info[FILTERTAG]) + { + const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); + struct openaux_args args; + struct list *orig; + const ElfW(Dyn) *d; + + args.strtab = strtab; + args.map = l; + args.trace_mode = trace_mode; + args.open_mode = open_mode; + orig = runp; + + for (d = l->l_ld; d->d_tag != DT_NULL; ++d) + if (__builtin_expect (d->d_tag, DT_NEEDED) == DT_NEEDED) + { + /* Map in the needed object. */ + struct link_map *dep; + + /* Recognize DSTs. */ + name = expand_dst (l, strtab + d->d_un.d_val, 0); + /* Store the tag in the argument structure. */ + args.name = name; + + 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 + errno_reason = -1; + goto out; + } + else + dep = args.aux; + + if (! dep->l_reserved) + { + /* Allocate new entry. */ + struct list *newp; + + newp = alloca (sizeof (struct list)); + + /* Append DEP to the list. */ + newp->map = dep; + newp->done = 0; + newp->next = NULL; + tail->next = newp; + tail = newp; + ++nlist; + /* Set the mark bit that says it's already in the list. */ + dep->l_reserved = 1; + } + + /* Remember this dependency. */ + if (needed != NULL) + needed[nneeded++] = dep; + } + else if (d->d_tag == DT_AUXILIARY || d->d_tag == DT_FILTER) + { + struct list *newp; + + /* Recognize DSTs. */ + name = expand_dst (l, strtab + d->d_un.d_val, + d->d_tag == DT_AUXILIARY); + /* Store the tag in the argument structure. */ + args.name = name; + + if (d->d_tag == DT_AUXILIARY) + { + /* Say that we are about to load an auxiliary library. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, + 0)) + _dl_debug_printf ("load auxiliary object=%s" + " requested by file=%s\n", + name, + l->l_name[0] + ? l->l_name : rtld_progname); + + /* We must be prepared that the addressed shared + object is not available. */ + 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 (malloced) + free ((char *) errstring); + + /* Simply ignore this error and continue the work. */ + continue; + } + } + else + { + /* Say that we are about to load an auxiliary library. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, + 0)) + _dl_debug_printf ("load filtered object=%s" + " requested by file=%s\n", + name, + l->l_name[0] + ? l->l_name : rtld_progname); + + /* For filter objects the dependency must be available. */ + 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 + errno_reason = -1; + goto out; + } + } + + /* The auxiliary object is actually available. + Incorporate the map in all the lists. */ + + /* Allocate new entry. This always has to be done. */ + newp = alloca (sizeof (struct list)); + + /* We want to insert the new map before the current one, + but we have no back links. So we copy the contents of + the current entry over. Note that ORIG and NEWP now + have switched their meanings. */ + memcpy (newp, orig, sizeof (*newp)); + + /* Initialize new entry. */ + orig->done = 0; + orig->map = args.aux; + + /* Remember this dependency. */ + if (needed != NULL) + needed[nneeded++] = args.aux; + + /* We must handle two situations here: the map is new, + so we must add it in all three lists. If the map + is already known, we have two further possibilities: + - if the object is before the current map in the + search list, we do nothing. It is already found + early + - if the object is after the current one, we must + move it just before the current map to make sure + the symbols are found early enough + */ + if (args.aux->l_reserved) + { + /* The object is already somewhere in the list. + Locate it first. */ + struct list *late; + + /* This object is already in the search list we + are building. Don't add a duplicate pointer. + Just added by _dl_map_object. */ + for (late = newp; late->next != NULL; late = late->next) + if (late->next->map == args.aux) + break; + + if (late->next != NULL) + { + /* The object is somewhere behind the current + position in the search path. We have to + move it to this earlier position. */ + orig->next = newp; + + /* Now remove the later entry from the list + and adjust the tail pointer. */ + if (tail == late->next) + tail = late; + late->next = late->next->next; + + /* We must move the object earlier in the chain. */ + if (args.aux->l_prev != NULL) + args.aux->l_prev->l_next = args.aux->l_next; + if (args.aux->l_next != NULL) + args.aux->l_next->l_prev = args.aux->l_prev; + + args.aux->l_prev = newp->map->l_prev; + newp->map->l_prev = args.aux; + if (args.aux->l_prev != NULL) + args.aux->l_prev->l_next = args.aux; + args.aux->l_next = newp->map; + } + else + { + /* The object must be somewhere earlier in the + list. Undo to the current list element what + we did above. */ + memcpy (orig, newp, sizeof (*newp)); + continue; + } + } + else + { + /* This is easy. We just add the symbol right here. */ + orig->next = newp; + ++nlist; + /* Set the mark bit that says it's already in the list. */ + args.aux->l_reserved = 1; + + /* The only problem is that in the double linked + list of all objects we don't have this new + object at the correct place. Correct this here. */ + if (args.aux->l_prev) + args.aux->l_prev->l_next = args.aux->l_next; + if (args.aux->l_next) + args.aux->l_next->l_prev = args.aux->l_prev; + + args.aux->l_prev = newp->map->l_prev; + newp->map->l_prev = args.aux; + if (args.aux->l_prev != NULL) + args.aux->l_prev->l_next = args.aux; + args.aux->l_next = newp->map; + } + + /* Move the tail pointer if necessary. */ + if (orig == tail) + tail = newp; + + /* Move on the insert point. */ + orig = newp; + } + } + + /* Terminate the list of dependencies and store the array address. */ + if (needed != NULL) + { + needed[nneeded++] = NULL; + + l->l_initfini = (struct link_map **) + malloc ((2 * nneeded + 1) * sizeof needed[0]); + if (l->l_initfini == NULL) + _dl_signal_error (ENOMEM, map->l_name, NULL, + N_("cannot allocate dependency list")); + l->l_initfini[0] = l; + memcpy (&l->l_initfini[1], needed, nneeded * sizeof needed[0]); + memcpy (&l->l_initfini[nneeded + 1], l->l_initfini, + nneeded * sizeof needed[0]); + } + + /* If we have no auxiliary objects just go on to the next map. */ + if (runp->done) + do + runp = runp->next; + while (runp != NULL && runp->done); + } + + out: + if (errno == 0 && errno_saved != 0) + __set_errno (errno_saved); + + if (map->l_initfini != NULL && map->l_type == lt_loaded) + { + /* This object was previously loaded as a dependency and we have + a separate l_initfini list. We don't need it anymore. */ + assert (map->l_searchlist.r_list == NULL); + free (map->l_initfini); + } + + /* Store the search list we built in the object. It will be used for + searches in the scope of this object. */ + map->l_initfini = + (struct link_map **) malloc ((2 * nlist + 1) + * sizeof (struct link_map *)); + if (map->l_initfini == NULL) + _dl_signal_error (ENOMEM, map->l_name, NULL, + N_("cannot allocate symbol search list")); + + + map->l_searchlist.r_list = &map->l_initfini[nlist + 1]; + map->l_searchlist.r_nlist = nlist; + + for (nlist = 0, runp = known; runp; runp = runp->next) + { + if (__builtin_expect (trace_mode, 0) && runp->map->l_faked) + /* This can happen when we trace the loading. */ + --map->l_searchlist.r_nlist; + else + map->l_searchlist.r_list[nlist++] = runp->map; + + /* Now clear all the mark bits we set in the objects on the search list + to avoid duplicates, so the next call starts fresh. */ + runp->map->l_reserved = 0; + } + + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK, 0) != 0 + && map == GL(dl_ns)[LM_ID_BASE]._ns_loaded) + { + /* If we are to compute conflicts, we have to build local scope + for each library, not just the ultimate loader. */ + for (i = 0; i < nlist; ++i) + { + struct link_map *l = map->l_searchlist.r_list[i]; + unsigned int j, cnt; + + /* The local scope has been already computed. */ + if (l == map + || (l->l_local_scope[0] + && l->l_local_scope[0]->r_nlist) != 0) + continue; + + if (l->l_info[AUXTAG] || l->l_info[FILTERTAG]) + { + /* As current DT_AUXILIARY/DT_FILTER implementation needs to be + rewritten, no need to bother with prelinking the old + implementation. */ + _dl_signal_error (EINVAL, l->l_name, NULL, N_("\ +Filters not supported with LD_TRACE_PRELINKING")); + } + + cnt = _dl_build_local_scope (map->l_initfini, l); + assert (cnt <= nlist); + for (j = 0; j < cnt; j++) + map->l_initfini[j]->l_reserved = 0; + + l->l_local_scope[0] = + (struct r_scope_elem *) malloc (sizeof (struct r_scope_elem) + + (cnt + * sizeof (struct link_map *))); + if (l->l_local_scope[0] == NULL) + _dl_signal_error (ENOMEM, map->l_name, NULL, + N_("cannot allocate symbol search list")); + l->l_local_scope[0]->r_nlist = cnt; + l->l_local_scope[0]->r_list = + (struct link_map **) (l->l_local_scope[0] + 1); + memcpy (l->l_local_scope[0]->r_list, map->l_initfini, + cnt * sizeof (struct link_map *)); + } + } + + /* Maybe we can remove some relocation dependencies now. */ + assert (map->l_searchlist.r_list[0] == map); + for (i = 0; i < map->l_reldepsact; ++i) + { + unsigned int j; + + for (j = 1; j < nlist; ++j) + if (map->l_searchlist.r_list[j] == map->l_reldeps[i]) + { + /* A direct or transitive dependency is also on the list + of relocation dependencies. Remove the latter. */ + for (j = i + 1; j < map->l_reldepsact; ++j) + map->l_reldeps[j - 1] = map->l_reldeps[j]; + + --map->l_reldepsact; + + /* Account for the '++i' performed by the 'for'. */ + --i; + break; + } + } + + /* Now determine the order in which the initialization has to happen. */ + memcpy (map->l_initfini, map->l_searchlist.r_list, + nlist * sizeof (struct link_map *)); + /* We can skip looking for the binary itself which is at the front + of the search list. Look through the list backward so that circular + dependencies are not changing the order. */ + for (i = 1; i < nlist; ++i) + { + struct link_map *l = map->l_searchlist.r_list[i]; + unsigned int j; + unsigned int k; + + /* Find the place in the initfini list where the map is currently + located. */ + for (j = 1; map->l_initfini[j] != l; ++j) + ; + + /* Find all object for which the current one is a dependency and + move the found object (if necessary) in front. */ + for (k = j + 1; k < nlist; ++k) + { + struct link_map **runp; + + runp = map->l_initfini[k]->l_initfini; + if (runp != NULL) + { + while (*runp != NULL) + if (__builtin_expect (*runp++ == l, 0)) + { + struct link_map *here = map->l_initfini[k]; + + /* Move it now. */ + memmove (&map->l_initfini[j] + 1, + &map->l_initfini[j], + (k - j) * sizeof (struct link_map *)); + map->l_initfini[j] = here; + + /* Don't insert further matches before the last + entry moved to the front. */ + ++j; + + break; + } + } + } + } + /* Terminate the list of dependencies. */ + map->l_initfini[nlist] = NULL; + + if (errno_reason) + _dl_signal_error (errno_reason == -1 ? 0 : errno_reason, objname, + NULL, errstring); +} diff --git a/libc/elf/dl-dst.h b/libc/elf/dl-dst.h new file mode 100644 index 000000000..83d16bdb0 --- /dev/null +++ b/libc/elf/dl-dst.h @@ -0,0 +1,85 @@ +/* Handling of dynamic sring tokens. + Copyright (C) 1999,2001,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. */ + +/* Determine the number of DST elements in the name. Only if IS_PATH is + nonzero paths are recognized (i.e., multiple, ':' separated filenames). */ +#define DL_DST_COUNT(name, is_path) \ + ({ \ + size_t __cnt = 0; \ + const char *__sf = strchr (name, '$'); \ + \ + if (__builtin_expect (__sf != NULL, 0)) \ + __cnt = _dl_dst_count (__sf, is_path); \ + \ + __cnt; }) +#ifndef IS_IN_rtld +# define _dl_dst_count GLRO(dl_dst_count) +#endif + + +/* Guess from the number of DSTs the length of the result string. */ +#define DL_DST_REQUIRED(l, name, len, cnt) \ + ({ \ + size_t __len = (len); \ + size_t __cnt = (cnt); \ + \ + if (__cnt > 0) \ + { \ + size_t origin_len; \ + /* Now we make a guess how many extra characters on top of the \ + length of S we need to represent the result. We know that \ + we have CNT replacements. Each at most can use \ + MAX (strlen (ORIGIN), strlen (_dl_platform)) \ + minus 7 (which is the length of "$ORIGIN"). \ + \ + 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 \ + if ((l)->l_origin == NULL) \ + { \ + assert ((l)->l_name[0] == '\0'); \ + (l)->l_origin = _dl_get_origin (); \ + origin_len = ((l)->l_origin && (l)->l_origin != (char *) -1 \ + ? strlen ((l)->l_origin) : 0); \ + } \ + else \ + origin_len = (l)->l_origin == (char *) -1 \ + ? 0 : strlen ((l)->l_origin); \ + \ + __len += __cnt * (MAX (origin_len, GLRO(dl_platformlen)) - 7); \ + } \ + \ + __len; }) + +#ifdef SHARED +# define DL_DST_REQ_STATIC /* nothing */ +#else +# define DL_DST_REQ_STATIC \ + 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) +#endif diff --git a/libc/elf/dl-environ.c b/libc/elf/dl-environ.c new file mode 100644 index 000000000..089e89e6e --- /dev/null +++ b/libc/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/libc/elf/dl-error.c b/libc/elf/dl-error.c new file mode 100644 index 000000000..79ebaaf01 --- /dev/null +++ b/libc/elf/dl-error.c @@ -0,0 +1,214 @@ +/* Error handling for runtime dynamic linker. + 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 + 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 <setjmp.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ldsodefs.h> + +/* This structure communicates state between _dl_catch_error and + _dl_signal_error. */ +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. */ + }; + +/* Multiple threads at once can use the `_dl_catch_error' function. The + calls can come from `_dl_map_object_deps', `_dlerror_run', or from + any of the libc functionality which loads dynamic objects (NSS, iconv). + Therefore we have to be prepared to save the state in thread-local + memory. The _dl_error_catch_tsd function pointer is reset by the thread + library so that it returns the address of a thread-local variable. */ + + +/* 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. */ +static const char _dl_out_of_memory[] = "out of memory"; + + +/* This points to a function which is called when an continuable error is + received. Unlike the handling of `catch' this function may return. + The arguments will be the `errstring' and `objname'. + + Since this functionality is not used in normal programs (only in ld.so) + we do not care about multi-threaded programs here. We keep this as a + global variable. */ +static receiver_fct receiver; + +#ifdef _LIBC_REENTRANT +# define CATCH_HOOK (*(struct catch **) (*GL(dl_error_catch_tsd)) ()) +#else +static struct catch *catch_hook; +# define CATCH_HOOK catch_hook +#endif + +void +internal_function +_dl_signal_error (int errcode, const char *objname, const char *occation, + const char *errstring) +{ + struct catch *lcatch; + + if (! errstring) + errstring = N_("DYNAMIC LINKER BUG!!!"); + + lcatch = CATCH_HOOK; + if (objname == NULL) + objname = ""; + if (lcatch != NULL) + { + /* We are inside _dl_catch_error. Return to it. We have to + duplicate the error string since it might be allocated on the + stack. The object name is always a string constant. */ + size_t len_objname = strlen (objname) + 1; + size_t len_errstring = strlen (errstring) + 1; + + 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); + + /* 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 = _dl_out_of_memory; + lcatch->malloced = false; + } + /* We do not restore the signal mask because none was saved. */ + __longjmp (lcatch->env[0].__jmpbuf, errcode ?: -1); + } + else + { + /* Lossage while resolving the program's own symbols is always fatal. */ + char buffer[1024]; + _dl_fatal_printf ("%s: %s: %s%s%s%s%s\n", + rtld_progname ?: "<program name unknown>", + occation ?: N_("error while loading shared libraries"), + objname, *objname ? ": " : "", + errstring, errcode ? ": " : "", + (errcode + ? __strerror_r (errcode, buffer, sizeof buffer) + : "")); + } +} + + +void +internal_function +_dl_signal_cerror (int errcode, const char *objname, const char *occation, + const char *errstring) +{ + if (__builtin_expect (GLRO(dl_debug_mask) + & ~(DL_DEBUG_STATISTICS|DL_DEBUG_PRELINK), 0)) + _dl_debug_printf ("%s: error: %s: %s (%s)\n", objname, occation, + errstring, receiver ? "continued" : "fatal"); + + if (receiver) + { + /* We are inside _dl_receive_error. Call the user supplied + handler and resume the work. The receiver will still be + installed. */ + (*receiver) (errcode, objname, errstring); + } + else + _dl_signal_error (errcode, objname, occation, errstring); +} + + +int +internal_function +_dl_catch_error (const char **objname, const char **errstring, + bool *mallocedp, void (*operate) (void *), void *args) +{ + int errcode; + struct catch *volatile old; + struct catch c; + /* We need not handle `receiver' since setting a `catch' is handled + before it. */ + + /* Some systems (e.g., SPARC) handle constructors to local variables + inefficient. So we initialize `c' by hand. */ + c.errstring = NULL; + + struct catch **const catchp = &CATCH_HOOK; + old = *catchp; + /* Do not save the signal mask. */ + errcode = __sigsetjmp (c.env, 0); + if (__builtin_expect (errcode, 0) == 0) + { + *catchp = &c; + (*operate) (args); + *catchp = old; + *objname = NULL; + *errstring = NULL; + *mallocedp = false; + return 0; + } + + /* We get here only if we longjmp'd out of OPERATE. */ + *catchp = old; + *objname = c.objname; + *errstring = c.errstring; + *mallocedp = c.malloced; + return errcode == -1 ? 0 : errcode; +} + + +void +internal_function +_dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args) +{ + struct catch **const catchp = &CATCH_HOOK; + struct catch *old_catch; + receiver_fct old_receiver; + + old_catch = *catchp; + old_receiver = receiver; + + /* Set the new values. */ + *catchp = NULL; + receiver = fct; + + (*operate) (args); + + *catchp = old_catch; + receiver = old_receiver; +} diff --git a/libc/elf/dl-execstack.c b/libc/elf/dl-execstack.c new file mode 100644 index 000000000..6dce21e7a --- /dev/null +++ b/libc/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/libc/elf/dl-fini.c b/libc/elf/dl-fini.c new file mode 100644 index 000000000..3cd7e7bbf --- /dev/null +++ b/libc/elf/dl-fini.c @@ -0,0 +1,287 @@ +/* Call the termination functions of loaded shared objects. + 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 + 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 <alloca.h> +#include <assert.h> +#include <string.h> +#include <ldsodefs.h> + + +/* Type of the constructor functions. */ +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 + loaded objects, in all namespaces. The problem is that the ELF + specification now demands that dependencies between the modules + are taken into account. I.e., the destructor for a module is + called before the ones for any of its dependencies. + + To make things more complicated, we cannot simply use the reverse + order of the constructors. Since the user might have loaded objects + using `dlopen' there are possibly several other modules with its + dependencies to be taken into account. Therefore we have to start + determining the order of the modules once again from the beginning. */ + struct link_map **maps = NULL; + size_t maps_size = 0; + + /* 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. */ +#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 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 (ns != LM_ID_BASE || nloaded > 0); + + /* Now we can allocate an array to hold all the pointers and copy + the pointers in. */ + if (maps_size < nloaded * sizeof (struct link_map *)) + { + if (maps_size == 0) + { + maps_size = nloaded * sizeof (struct link_map *); + maps = (struct link_map **) alloca (maps_size); + } + else + maps = (struct link_map **) + extend_alloca (maps, maps_size, + nloaded * sizeof (struct link_map *)); + } + + unsigned int i; + struct link_map *l; + 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; + l->l_idx = i; + ++i; + + /* Bump l_direct_opencount of all objects so that they are + not dlclose()ed from underneath us. */ + ++l->l_direct_opencount; + } + 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. */ + _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 + members of this list cannot vanish since the open count is too + 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 + destructors. We have to process this array from the front. */ + for (i = 0; i < nmaps; ++i) + { + l = maps[i]; + + if (l->l_init_called) + { + /* Make sure nothing happens if we are called twice. */ + l->l_init_called = 0; + + /* Is there a destructor function? */ + if (l->l_info[DT_FINI_ARRAY] != NULL + || l->l_info[DT_FINI] != NULL) + { + /* 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)) (); + } + +#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_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/libc/elf/dl-fptr.c b/libc/elf/dl-fptr.c new file mode 100644 index 000000000..78beecfdc --- /dev/null +++ b/libc/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/libc/elf/dl-init.c b/libc/elf/dl-init.c new file mode 100644 index 000000000..e7b67570f --- /dev/null +++ b/libc/elf/dl-init.c @@ -0,0 +1,141 @@ +/* Return the next shared object initializer function not yet run. + Copyright (C) 1995, 1996, 1998-2002, 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 <stddef.h> +#include <ldsodefs.h> + + +/* Type of the initializer. */ +typedef void (*init_t) (int, char **, char **); + +#ifndef HAVE_INLINED_SYSCALLS +/* Flag, nonzero during startup phase. */ +extern int _dl_starting_up; +extern int _dl_starting_up_internal attribute_hidden; +#endif + + +static void +call_init (struct link_map *l, int argc, char **argv, char **env) +{ + if (l->l_init_called) + /* This object is all done. */ + return; + + /* Avoid handling this constructor again in case we have a circular + dependency. */ + l->l_init_called = 1; + + /* Check for object which constructors we do not run here. */ + if (__builtin_expect (l->l_name[0], 'a') == '\0' + && l->l_type == lt_executable) + return; + + /* Are there any constructors? */ + if (l->l_info[DT_INIT] == NULL + && __builtin_expect (l->l_info[DT_INIT_ARRAY] == NULL, 1)) + return; + + /* Print a debug message if wanted. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) + _dl_debug_printf ("\ncalling init: %s\n\n", + l->l_name[0] ? l->l_name : rtld_progname); + + /* Now run the local constructors. There are two forms of them: + - the one named by DT_INIT + - the others in the DT_INIT_ARRAY. + */ + if (l->l_info[DT_INIT] != NULL) + { + init_t init = (init_t) DL_DT_INIT_ADDRESS + (l, l->l_addr + l->l_info[DT_INIT]->d_un.d_ptr); + + /* Call the function. */ + init (argc, argv, env); + } + + /* Next see whether there is an array with initialization functions. */ + ElfW(Dyn) *init_array = l->l_info[DT_INIT_ARRAY]; + if (init_array != NULL) + { + unsigned int j; + unsigned int jm; + ElfW(Addr) *addrs; + + jm = l->l_info[DT_INIT_ARRAYSZ]->d_un.d_val / sizeof (ElfW(Addr)); + + addrs = (ElfW(Addr) *) (init_array->d_un.d_ptr + l->l_addr); + for (j = 0; j < jm; ++j) + ((init_t) addrs[j]) (argc, argv, env); + } +} + + +void +internal_function +_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]; + unsigned int i; + + if (__builtin_expect (GL(dl_initfirst) != NULL, 0)) + { + call_init (GL(dl_initfirst), argc, argv, env); + GL(dl_initfirst) = NULL; + } + + /* Don't do anything if there is no preinit array. */ + if (__builtin_expect (preinit_array != NULL, 0) + && preinit_array_size != NULL + && (i = preinit_array_size->d_un.d_val / sizeof (ElfW(Addr))) > 0) + { + ElfW(Addr) *addrs; + unsigned int cnt; + + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) + _dl_debug_printf ("\ncalling preinit: %s\n\n", + main_map->l_name[0] + ? main_map->l_name : rtld_progname); + + addrs = (ElfW(Addr) *) (preinit_array->d_un.d_ptr + main_map->l_addr); + for (cnt = 0; cnt < i; ++cnt) + ((init_t) addrs[cnt]) (argc, argv, env); + } + + /* 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 + for all dependencies of an object must run before the constructor + for the object itself. Circular dependencies are left unspecified. + + This is highly questionable since it puts the burden on the dynamic + loader which has to find the dependencies at runtime instead of + letting the user do it right. Stupidity rules! */ + + i = main_map->l_searchlist.r_nlist; + while (i-- > 0) + call_init (main_map->l_initfini[i], argc, argv, env); + +#ifndef HAVE_INLINED_SYSCALLS + /* Finished starting up. */ + INTUSE(_dl_starting_up) = 0; +#endif +} +INTDEF (_dl_init) diff --git a/libc/elf/dl-iteratephdr.c b/libc/elf/dl-iteratephdr.c new file mode 100644 index 000000000..52a114421 --- /dev/null +++ b/libc/elf/dl-iteratephdr.c @@ -0,0 +1,125 @@ +/* Get loaded objects program headers. + Copyright (C) 2001,2002,2003,2004,2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2001. + + 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; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <ldsodefs.h> +#include <stddef.h> +#include <bits/libc-lock.h> + +static void +cancel_handler (void *arg __attribute__((unused))) +{ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); +} + +hidden_proto (__dl_iterate_phdr) +int +__dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, + size_t size, void *data), void *data) +{ + struct link_map *l; + struct dl_phdr_info info; + int ret = 0; + + /* Make sure we are alone. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + __libc_cleanup_push (cancel_handler, 0); + + /* We have to determine the namespace of the caller since this determines + which namespace is reported. */ + const void *caller = RETURN_ADDRESS (0); + size_t nloaded = GL(dl_ns)[0]._ns_nloaded; + Lmid_t ns = 0; + for (Lmid_t cnt = DL_NNS - 1; cnt > 0; --cnt) + for (struct link_map *l = GL(dl_ns)[cnt]._ns_loaded; l; l = l->l_next) + { + /* We have to count the total number of loaded objects. */ + 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. */ + ns = cnt; + } + + for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) + { + info.dlpi_addr = l->l_addr; + info.dlpi_name = l->l_name; + info.dlpi_phdr = l->l_phdr; + 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; + } + + /* Release the lock. */ + __libc_cleanup_pop (0); + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + return ret; +} +hidden_def (__dl_iterate_phdr) + +#ifdef SHARED + +weak_alias (__dl_iterate_phdr, dl_iterate_phdr); + +#else + +/* dl-support.c defines these and initializes them early on. */ +extern ElfW(Phdr) *_dl_phdr; +extern size_t _dl_phnum; + +int +dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, + size_t size, void *data), void *data) +{ + if (_dl_phnum != 0) + { + /* This entry describes this statically-linked program itself. */ + struct dl_phdr_info info; + int ret; + info.dlpi_addr = 0; + info.dlpi_name = ""; + info.dlpi_phdr = _dl_phdr; + info.dlpi_phnum = _dl_phnum; + info.dlpi_adds = GL(dl_load_adds); + info.dlpi_subs = GL(dl_load_adds) - GL(dl_ns)[LM_ID_BASE]._ns_nloaded; + ret = (*callback) (&info, sizeof (struct dl_phdr_info), data); + if (ret) + return ret; + } + + return __dl_iterate_phdr (callback, data); +} + + +#endif diff --git a/libc/elf/dl-libc.c b/libc/elf/dl-libc.c new file mode 100644 index 000000000..1b995eda9 --- /dev/null +++ b/libc/elf/dl-libc.c @@ -0,0 +1,254 @@ +/* Handle loading and unloading shared objects for internal libc purposes. + 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. + + 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 <dlfcn.h> +#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 + string that dlerror() would return and just pass back a null pointer + for errors. This also lets the rest of libc not know about the error + handling mechanism. + + Much of this code came from gconv_dl.c with slight modifications. */ + +static int +internal_function +dlerror_run (void (*operate) (void *), void *args) +{ + const char *objname; + const char *last_errstring = NULL; + bool malloced; + + (void) GLRO(dl_catch_error) (&objname, &last_errstring, &malloced, + operate, args); + + int result = last_errstring != NULL; + if (result && malloced) + free ((char *) last_errstring); + + return result; +} + +/* These functions are called by dlerror_run... */ + +struct do_dlopen_args +{ + /* Argument to do_dlopen. */ + const char *name; + /* Opening mode. */ + int mode; + + /* Return from do_dlopen. */ + struct link_map *map; +}; + +struct do_dlsym_args +{ + /* Arguments to do_dlsym. */ + struct link_map *map; + const char *name; + + /* Return values of do_dlsym. */ + lookup_t loadbase; + const ElfW(Sym) *ref; +}; + +static void +do_dlopen (void *ptr) +{ + struct do_dlopen_args *args = (struct do_dlopen_args *) ptr; + /* Open and relocate the shared object. */ + args->map = GLRO(dl_open) (args->name, args->mode, NULL, __LM_ID_CALLER, + __libc_argc, __libc_argv, __environ); +} + +static void +do_dlsym (void *ptr) +{ + struct do_dlsym_args *args = (struct do_dlsym_args *) ptr; + args->ref = NULL; + args->loadbase = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref, + args->map->l_local_scope, NULL, 0, + DL_LOOKUP_RETURN_NEWEST, NULL); +} + +static void +do_dlclose (void *ptr) +{ + GLRO(dl_close) ((struct link_map *) ptr); +} + +/* This code is to support __libc_dlopen from __libc_dlopen'ed shared + libraries. We need to ensure the statically linked __libc_dlopen + etc. functions are used instead of the dynamically loaded. */ +struct dl_open_hook +{ + void *(*dlopen_mode) (const char *name, int mode); + void *(*dlsym) (void *map, const char *name); + int (*dlclose) (void *map); +}; + +#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)); +libc_hidden_data_def (_dl_open_hook); +#else +static void +do_dlsym_private (void *ptr) +{ + lookup_t l; + struct r_found_version vers; + vers.name = "GLIBC_PRIVATE"; + vers.hidden = 1; + /* vers.hash = _dl_elf_hash (vers.name); */ + vers.hash = 0x0963cf85; + vers.filename = NULL; + + struct do_dlsym_args *args = (struct do_dlsym_args *) ptr; + args->ref = NULL; + l = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref, + args->map->l_scope, &vers, 0, 0, NULL); + args->loadbase = l; +} + +static struct dl_open_hook _dl_open_hook = + { + .dlopen_mode = __libc_dlopen_mode, + .dlsym = __libc_dlsym, + .dlclose = __libc_dlclose + }; +#endif + +/* ... and these functions call dlerror_run. */ + +void * +__libc_dlopen_mode (const char *name, int mode) +{ + struct do_dlopen_args args; + args.name = name; + args.mode = mode; + +#ifdef SHARED + if (__builtin_expect (_dl_open_hook != NULL, 0)) + return _dl_open_hook->dlopen_mode (name, mode); + return (dlerror_run (do_dlopen, &args) ? NULL : (void *) args.map); +#else + if (dlerror_run (do_dlopen, &args)) + return NULL; + + __libc_register_dl_open_hook (args.map); + __libc_register_dlfcn_hook (args.map); + return (void *) args.map; +#endif +} +libc_hidden_def (__libc_dlopen_mode) + +#ifndef SHARED +void * +__libc_dlsym_private (struct link_map *map, const char *name) +{ + struct do_dlsym_args sargs; + sargs.map = map; + sargs.name = name; + + if (! dlerror_run (do_dlsym_private, &sargs)) + return DL_SYMBOL_ADDRESS (sargs.loadbase, sargs.ref); + return NULL; +} + +void +__libc_register_dl_open_hook (struct link_map *map) +{ + struct dl_open_hook **hook; + + hook = (struct dl_open_hook **) __libc_dlsym_private (map, "_dl_open_hook"); + if (hook != NULL) + *hook = &_dl_open_hook; +} +#endif + +void * +__libc_dlsym (void *map, const char *name) +{ + struct do_dlsym_args args; + args.map = map; + args.name = name; + +#ifdef SHARED + if (__builtin_expect (_dl_open_hook != NULL, 0)) + return _dl_open_hook->dlsym (map, name); +#endif + return (dlerror_run (do_dlsym, &args) ? NULL + : (void *) (DL_SYMBOL_ADDRESS (args.loadbase, args.ref))); +} +libc_hidden_def (__libc_dlsym) + +int +__libc_dlclose (void *map) +{ +#ifdef SHARED + if (__builtin_expect (_dl_open_hook != NULL, 0)) + return _dl_open_hook->dlclose (map); +#endif + return dlerror_run (do_dlclose, map); +} +libc_hidden_def (__libc_dlclose) + + +libc_freeres_fn (free_mem) +{ + struct link_map *l; + struct r_search_path_elem *d; + + /* Remove all search directories. */ + d = GL(dl_all_dirs); + while (d != GLRO(dl_init_all_dirs)) + { + struct r_search_path_elem *old = d; + d = d->next; + free (old); + } + + /* Remove all additional names added to the objects. */ + for (Lmid_t ns = 0; ns < DL_NNS; ++ns) + for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) + { + struct libname_list *lnp = l->l_libname->next; + + l->l_libname->next = NULL; + + while (lnp != NULL) + { + struct libname_list *old = lnp; + lnp = lnp->next; + if (! old->dont_free) + free (old); + } + } +} diff --git a/libc/elf/dl-load.c b/libc/elf/dl-load.c new file mode 100644 index 000000000..902ffc410 --- /dev/null +++ b/libc/elf/dl-load.c @@ -0,0 +1,2310 @@ +/* Map in a shared object's segments from the file. + 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 + 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 <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <libintl.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ldsodefs.h> +#include <bits/wordsize.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include "dynamic-link.h" +#include <abi-tag.h> +#include <stackinfo.h> +#include <caller.h> +#include <sysdep.h> + +#include <dl-dst.h> + +/* On some systems, no flag bits are given to specify file mapping. */ +#ifndef MAP_FILE +# define MAP_FILE 0 +#endif + +/* The right way to map in the shared library files is MAP_COPY, which + makes a virtual copy of the data at the time of the mmap call; this + guarantees the mapped pages will be consistent even if the file is + 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. + + 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 + than 0. We want to know the base address for these such that we can + subtract this address from the segment addresses during mapping. + This results in a more efficient address space usage. Defaults to + zero for almost all systems. */ +#ifndef MAP_BASE_ADDR +# define MAP_BASE_ADDR(l) 0 +#endif + + +#include <endian.h> +#if BYTE_ORDER == BIG_ENDIAN +# define byteorder ELFDATA2MSB +#elif BYTE_ORDER == LITTLE_ENDIAN +# define byteorder ELFDATA2LSB +#else +# error "Unknown BYTE_ORDER " BYTE_ORDER +# define byteorder ELFDATANONE +#endif + +#define STRING(x) __STRING (x) + +#ifdef MAP_ANON +/* The fd is not examined when using MAP_ANON. */ +# define ANONFD -1 +#else +int _dl_zerofd = -1; +# define ANONFD _dl_zerofd +#endif + +/* Handle situations where we have a preferred location in memory for + the shared objects. */ +#ifdef ELF_PREFERRED_ADDRESS_DATA +ELF_PREFERRED_ADDRESS_DATA; +#endif +#ifndef ELF_PREFERRED_ADDRESS +# define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref) +#endif +#ifndef ELF_FIXED_ADDRESS +# define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0) +#endif + + +int __stack_prot attribute_hidden attribute_relro +#if _STACK_GROWS_DOWN && defined PROT_GROWSDOWN + = PROT_GROWSDOWN; +#elif _STACK_GROWS_UP && defined PROT_GROWSUP + = PROT_GROWSUP; +#else + = 0; +#endif + + +/* Type for the buffer we put the ELF header and hopefully the program + header. This buffer does not really have to be too large. In most + cases the program header follows the ELF header directly. If this + is not the case all bets are off and we can make the header + arbitrarily large and still won't get it read. This means the only + 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 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 832 +#endif + char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr))))); +}; + +/* This is the decomposed LD_LIBRARY_PATH search path. */ +static struct r_search_path_struct env_path_list attribute_relro; + +/* List of the hardware capabilities we might end up using. */ +static const struct r_strlenpair *capstr attribute_relro; +static size_t ncapstr attribute_relro; +static size_t max_capstrlen attribute_relro; + + +/* Get the generated information about the trusted directories. */ +#include "trusted-dirs.h" + +static const char system_dirs[] = SYSTEM_DIRS; +static const size_t system_dirs_len[] = +{ + SYSTEM_DIRS_LEN +}; +#define nsystem_dirs_len \ + (sizeof (system_dirs_len) / sizeof (system_dirs_len[0])) + + +/* Local version of `strdup' function. */ +static inline char * +local_strdup (const char *s) +{ + size_t len = strlen (s) + 1; + void *new = malloc (len); + + if (new == NULL) + return NULL; + + return (char *) memcpy (new, s, len); +} + + +static size_t +is_dst (const char *start, const char *name, const char *str, + int is_path, int secure) +{ + size_t len; + bool is_curly = false; + + if (name[0] == '{') + { + is_curly = true; + ++name; + } + + len = 0; + while (name[len] == str[len] && name[len] != '\0') + ++len; + + if (is_curly) + { + if (name[len] != '}') + return 0; + + /* Point again at the beginning of the name. */ + --name; + /* Skip over closing curly brace and adjust for the --name. */ + len += 2; + } + else if (name[len] != '\0' && name[len] != '/' + && (!is_path || name[len] != ':')) + return 0; + + if (__builtin_expect (secure, 0) + && ((name[len] != '\0' && (!is_path || name[len] != ':')) + || (name != start + 1 && (!is_path || name[-2] != ':')))) + return 0; + + return len; +} + + +size_t +_dl_dst_count (const char *name, int is_path) +{ + const char *const start = name; + size_t cnt = 0; + + do + { + size_t len; + + /* $ORIGIN is not expanded for SUID/GUID programs (except if it + is $ORIGIN alone) and it must always appear first in path. */ + ++name; + if ((len = is_dst (start, name, "ORIGIN", is_path, + INTUSE(__libc_enable_secure))) != 0 + || (len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0 + || (len = is_dst (start, name, "LIB", is_path, 0)) != 0) + ++cnt; + + name = strchr (name + len, '$'); + } + while (name != NULL); + + return cnt; +} + + +char * +_dl_dst_substitute (struct link_map *l, const char *name, char *result, + int is_path) +{ + const char *const start = name; + char *last_elem, *wp; + + /* Now fill the result path. While copying over the string we keep + track of the start of the last path element. When we come accross + a DST we copy over the value or (if the value is not available) + leave the entire path element out. */ + last_elem = wp = result; + + do + { + if (__builtin_expect (*name == '$', 0)) + { + const char *repl = NULL; + size_t len; + + ++name; + if ((len = is_dst (start, name, "ORIGIN", is_path, + INTUSE(__libc_enable_secure))) != 0) + { +#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) + repl = DL_DST_LIB; + + if (repl != NULL && repl != (const char *) -1) + { + wp = __stpcpy (wp, repl); + name += len; + } + else if (len > 1) + { + /* We cannot use this path element, the value of the + replacement is unknown. */ + wp = last_elem; + name += len; + while (*name != '\0' && (!is_path || *name != ':')) + ++name; + } + else + /* No DST we recognize. */ + *wp++ = '$'; + } + else + { + *wp++ = *name++; + if (is_path && *name == ':') + last_elem = wp; + } + } + while (*name != '\0'); + + *wp = '\0'; + + return result; +} + + +/* Return copy of argument with all recognized dynamic string tokens + ($ORIGIN and $PLATFORM for now) replaced. On some platforms it + might not be possible to determine the path from which the object + belonging to the map is loaded. In this case the path element + containing $ORIGIN is left out. */ +static char * +expand_dynamic_string_token (struct link_map *l, const char *s) +{ + /* We make two runs over the string. First we determine how large the + resulting string is and then we copy it over. Since this is now + frequently executed operation we are looking here not for performance + but rather for code size. */ + size_t cnt; + size_t total; + char *result; + + /* Determine the number of DST elements. */ + cnt = DL_DST_COUNT (s, 1); + + /* If we do not have to replace anything simply copy the string. */ + if (__builtin_expect (cnt, 0) == 0) + return local_strdup (s); + + /* Determine the length of the substituted string. */ + total = DL_DST_REQUIRED (l, s, strlen (s), cnt); + + /* Allocate the necessary memory. */ + result = (char *) malloc (total + 1); + if (result == NULL) + return NULL; + + return _dl_dst_substitute (l, s, result, 1); +} + + +/* Add `name' to the list of names for a particular shared object. + `name' is expected to have been allocated with malloc and will + be freed if the shared object already has this name. + Returns false if the object already had this name. */ +static void +internal_function +add_name_to_object (struct link_map *l, const char *name) +{ + struct libname_list *lnp, *lastp; + struct libname_list *newname; + size_t name_len; + + lastp = NULL; + for (lnp = l->l_libname; lnp != NULL; lastp = lnp, lnp = lnp->next) + if (strcmp (name, lnp->name) == 0) + return; + + name_len = strlen (name) + 1; + newname = (struct libname_list *) malloc (sizeof *newname + name_len); + if (newname == NULL) + { + /* No more memory. */ + _dl_signal_error (ENOMEM, name, NULL, N_("cannot allocate name record")); + return; + } + /* The object should have a libname set from _dl_new_object. */ + assert (lastp != NULL); + + newname->name = memcpy (newname + 1, name, name_len); + newname->next = NULL; + newname->dont_free = 0; + lastp->next = newname; +} + +/* Standard search directories. */ +static struct r_search_path_struct rtld_search_dirs attribute_relro; + +static size_t max_dirnamelen; + +static struct r_search_path_elem ** +fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep, + int check_trusted, const char *what, const char *where) +{ + char *cp; + size_t nelems = 0; + + while ((cp = __strsep (&rpath, sep)) != NULL) + { + struct r_search_path_elem *dirp; + size_t len = strlen (cp); + + /* `strsep' can pass an empty string. This has to be + interpreted as `use the current directory'. */ + if (len == 0) + { + static const char curwd[] = "./"; + cp = (char *) curwd; + } + + /* Remove trailing slashes (except for "/"). */ + while (len > 1 && cp[len - 1] == '/') + --len; + + /* Now add one if there is none so far. */ + if (len > 0 && cp[len - 1] != '/') + cp[len++] = '/'; + + /* Make sure we don't use untrusted directories if we run SUID. */ + if (__builtin_expect (check_trusted, 0)) + { + const char *trun = system_dirs; + size_t idx; + int unsecure = 1; + + /* All trusted directories must be complete names. */ + if (cp[0] == '/') + { + for (idx = 0; idx < nsystem_dirs_len; ++idx) + { + if (len == system_dirs_len[idx] + && memcmp (trun, cp, len) == 0) + { + /* Found it. */ + unsecure = 0; + break; + } + + trun += system_dirs_len[idx] + 1; + } + } + + if (unsecure) + /* Simply drop this directory. */ + continue; + } + + /* See if this directory is already known. */ + for (dirp = GL(dl_all_dirs); dirp != NULL; dirp = dirp->next) + if (dirp->dirnamelen == len && memcmp (cp, dirp->dirname, len) == 0) + break; + + if (dirp != NULL) + { + /* It is available, see whether it's on our own list. */ + size_t cnt; + for (cnt = 0; cnt < nelems; ++cnt) + if (result[cnt] == dirp) + break; + + if (cnt == nelems) + result[nelems++] = dirp; + } + else + { + size_t cnt; + enum r_dir_status init_val; + size_t where_len = where ? strlen (where) + 1 : 0; + + /* It's a new directory. Create an entry and add it. */ + dirp = (struct r_search_path_elem *) + malloc (sizeof (*dirp) + ncapstr * sizeof (enum r_dir_status) + + where_len + len + 1); + if (dirp == NULL) + _dl_signal_error (ENOMEM, NULL, NULL, + N_("cannot create cache for search path")); + + dirp->dirname = ((char *) dirp + sizeof (*dirp) + + ncapstr * sizeof (enum r_dir_status)); + *((char *) __mempcpy ((char *) dirp->dirname, cp, len)) = '\0'; + dirp->dirnamelen = len; + + if (len > max_dirnamelen) + max_dirnamelen = len; + + /* We have to make sure all the relative directories are + never ignored. The current directory might change and + all our saved information would be void. */ + init_val = cp[0] != '/' ? existing : unknown; + for (cnt = 0; cnt < ncapstr; ++cnt) + dirp->status[cnt] = init_val; + + dirp->what = what; + if (__builtin_expect (where != NULL, 1)) + dirp->where = memcpy ((char *) dirp + sizeof (*dirp) + len + 1 + + (ncapstr * sizeof (enum r_dir_status)), + where, where_len); + else + dirp->where = NULL; + + dirp->next = GL(dl_all_dirs); + GL(dl_all_dirs) = dirp; + + /* Put it in the result array. */ + result[nelems++] = dirp; + } + } + + /* Terminate the array. */ + result[nelems] = NULL; + + return result; +} + + +static void +internal_function +decompose_rpath (struct r_search_path_struct *sps, + const char *rpath, struct link_map *l, const char *what) +{ + /* Make a copy we can work with. */ + const char *where = l->l_name; + char *copy; + char *cp; + struct r_search_path_elem **result; + size_t nelems; + /* Initialize to please the compiler. */ + const char *errstring = NULL; + + /* First see whether we must forget the RUNPATH and RPATH from this + object. */ + if (__builtin_expect (GLRO(dl_inhibit_rpath) != NULL, 0) + && !INTUSE(__libc_enable_secure)) + { + const char *inhp = GLRO(dl_inhibit_rpath); + + do + { + const char *wp = where; + + while (*inhp == *wp && *wp != '\0') + { + ++inhp; + ++wp; + } + + if (*wp == '\0' && (*inhp == '\0' || *inhp == ':')) + { + /* 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; + } + + while (*inhp != '\0') + if (*inhp++ == ':') + break; + } + while (*inhp != '\0'); + } + + /* Make a writable copy. At the same time expand possible dynamic + string tokens. */ + copy = expand_dynamic_string_token (l, rpath); + if (copy == NULL) + { + errstring = N_("cannot create RUNPATH/RPATH copy"); + goto signal_error; + } + + /* Count the number of necessary elements in the result array. */ + nelems = 0; + for (cp = copy; *cp != '\0'; ++cp) + if (*cp == ':') + ++nelems; + + /* Allocate room for the result. NELEMS + 1 is an upper limit for the + number of necessary entries. */ + result = (struct r_search_path_elem **) malloc ((nelems + 1 + 1) + * sizeof (*result)); + if (result == NULL) + goto signal_error_cache; + + fillin_rpath (copy, result, ":", 0, what, where); + + /* Free the copied RPATH string. `fillin_rpath' make own copies if + necessary. */ + free (copy); + + sps->dirs = result; + /* The caller will change this value if we haven't used a real malloc. */ + sps->malloced = 1; +} + +/* Make sure cached path information is stored in *SP + and return true if there are any paths to search there. */ +static bool +cache_rpath (struct link_map *l, + struct r_search_path_struct *sp, + int tag, + const char *what) +{ + if (sp->dirs == (void *) -1) + return false; + + if (sp->dirs != NULL) + return true; + + if (l->l_info[tag] == NULL) + { + /* There is no path. */ + sp->dirs = (void *) -1; + return false; + } + + /* 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; +} + + +void +internal_function +_dl_init_paths (const char *llp) +{ + size_t idx; + const char *strp; + struct r_search_path_elem *pelem, **aelem; + size_t round_size; +#ifdef SHARED + struct link_map *l; +#endif + /* Initialize to please the compiler. */ + const char *errstring = NULL; + + /* Fill in the information about the application's RPATH and the + directories addressed by the LD_LIBRARY_PATH environment variable. */ + + /* Get the capabilities. */ + capstr = _dl_important_hwcaps (GLRO(dl_platform), GLRO(dl_platformlen), + &ncapstr, &max_capstrlen); + + /* First set up the rest of the default search directory entries. */ + aelem = rtld_search_dirs.dirs = (struct r_search_path_elem **) + malloc ((nsystem_dirs_len + 1) * sizeof (struct r_search_path_elem *)); + if (rtld_search_dirs.dirs == NULL) + { + errstring = N_("cannot create search path array"); + signal_error: + _dl_signal_error (ENOMEM, NULL, NULL, errstring); + } + + round_size = ((2 * sizeof (struct r_search_path_elem) - 1 + + ncapstr * sizeof (enum r_dir_status)) + / sizeof (struct r_search_path_elem)); + + rtld_search_dirs.dirs[0] = (struct r_search_path_elem *) + malloc ((sizeof (system_dirs) / sizeof (system_dirs[0])) + * round_size * sizeof (struct r_search_path_elem)); + if (rtld_search_dirs.dirs[0] == NULL) + { + errstring = N_("cannot create cache for search path"); + goto signal_error; + } + + rtld_search_dirs.malloced = 0; + pelem = GL(dl_all_dirs) = rtld_search_dirs.dirs[0]; + strp = system_dirs; + idx = 0; + + do + { + size_t cnt; + + *aelem++ = pelem; + + pelem->what = "system search path"; + pelem->where = NULL; + + pelem->dirname = strp; + pelem->dirnamelen = system_dirs_len[idx]; + strp += system_dirs_len[idx] + 1; + + /* System paths must be absolute. */ + assert (pelem->dirname[0] == '/'); + for (cnt = 0; cnt < ncapstr; ++cnt) + pelem->status[cnt] = unknown; + + pelem->next = (++idx == nsystem_dirs_len ? NULL : (pelem + round_size)); + + pelem += round_size; + } + while (idx < nsystem_dirs_len); + + max_dirnamelen = SYSTEM_DIRS_MAX_LEN; + *aelem = NULL; + +#ifdef SHARED + /* This points to the map of the main object. */ + l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + if (l != NULL) + { + assert (l->l_type != lt_loaded); + + if (l->l_info[DT_RUNPATH]) + { + /* Allocate room for the search path and fill in information + from RUNPATH. */ + decompose_rpath (&l->l_runpath_dirs, + (const void *) (D_PTR (l, l_info[DT_STRTAB]) + + l->l_info[DT_RUNPATH]->d_un.d_val), + l, "RUNPATH"); + + /* The RPATH is ignored. */ + l->l_rpath_dirs.dirs = (void *) -1; + } + else + { + l->l_runpath_dirs.dirs = (void *) -1; + + if (l->l_info[DT_RPATH]) + { + /* Allocate room for the search path and fill in information + from RPATH. */ + decompose_rpath (&l->l_rpath_dirs, + (const void *) (D_PTR (l, l_info[DT_STRTAB]) + + l->l_info[DT_RPATH]->d_un.d_val), + l, "RPATH"); + l->l_rpath_dirs.malloced = 0; + } + else + l->l_rpath_dirs.dirs = (void *) -1; + } + } +#endif /* SHARED */ + + if (llp != NULL && *llp != '\0') + { + size_t nllp; + const char *cp = llp; + char *llp_tmp = strdupa (llp); + + /* Decompose the LD_LIBRARY_PATH contents. First determine how many + elements it has. */ + nllp = 1; + while (*cp) + { + if (*cp == ':' || *cp == ';') + ++nllp; + ++cp; + } + + env_path_list.dirs = (struct r_search_path_elem **) + malloc ((nllp + 1) * sizeof (struct r_search_path_elem *)); + if (env_path_list.dirs == NULL) + { + errstring = N_("cannot create cache for search path"); + goto signal_error; + } + + (void) fillin_rpath (llp_tmp, env_path_list.dirs, ":;", + INTUSE(__libc_enable_secure), "LD_LIBRARY_PATH", + NULL); + + if (env_path_list.dirs[0] == NULL) + { + free (env_path_list.dirs); + env_path_list.dirs = (void *) -1; + } + + env_path_list.malloced = 0; + } + else + env_path_list.dirs = (void *) -1; + + /* Remember the last search directory added at startup. */ + GLRO(dl_init_all_dirs) = GL(dl_all_dirs); +} + + +static void +__attribute__ ((noreturn, noinline)) +lose (int code, int fd, const char *name, char *realname, struct link_map *l, + const char *msg, struct r_debug *r) +{ + /* The file might already be closed. */ + if (fd != -1) + (void) __close (fd); + if (l != NULL) + { + /* Remove the stillborn object from the list and free it. */ + assert (l->l_next == NULL); + if (l->l_prev == NULL) + /* No other module loaded. This happens only in the static library, + or in rtld under --verify. */ + GL(dl_ns)[l->l_ns]._ns_loaded = NULL; + else + l->l_prev->l_next = NULL; + --GL(dl_ns)[l->l_ns]._ns_nloaded; + free (l); + } + free (realname); + + if (r != NULL) + { + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + } + + _dl_signal_error (code, name, NULL, msg); +} + + +/* Map in the shared object NAME, actually located in REALNAME, and already + opened on FD. */ + +#ifndef EXTERNAL_MAP_FROM_FD +static +#endif +struct link_map * +_dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, + char *realname, struct link_map *loader, int l_type, + int mode, void **stack_endp, Lmid_t nsid) +{ + struct link_map *l = NULL; + const ElfW(Ehdr) *header; + const ElfW(Phdr) *phdr; + const ElfW(Phdr) *ph; + size_t maplength; + int type; + struct stat64 st; + /* 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)) + { + errstring = N_("cannot stat shared object"); + call_lose_errno: + errval = errno; + call_lose: + 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_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. */ + __close (fd); + + /* If the name is not in the list of names for this object add + it. */ + free (realname); + add_name_to_object (l, name); + + return l; + } + +#ifdef SHARED + /* When loading into a namespace other than the base one we must + avoid loading ld.so since there can only be one copy. Ever. */ + if (__builtin_expect (nsid != LM_ID_BASE, 0) + && ((st.st_ino == GL(dl_rtld_map).l_ino + && st.st_dev == GL(dl_rtld_map).l_dev) + || _dl_name_match_p (name, &GL(dl_rtld_map)))) + { + /* This is indeed ld.so. Create a new link_map which refers to + the real one for almost everything. */ + l = _dl_new_object (realname, name, l_type, loader, mode, nsid); + if (l == NULL) + goto fail_new; + + /* Refer to the real descriptor. */ + l->l_real = &GL(dl_rtld_map); + + /* No need to bump the refcount of the real object, ld.so will + never be unloaded. */ + __close (fd); + + return l; + } +#endif + + if (mode & RTLD_NOLOAD) + /* We are not supposed to load the object unless it is already + loaded. So return now. */ + return NULL; + + /* Print debugging message. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) + _dl_debug_printf ("file=%s [%lu]; generating link map\n", name, nsid); + + /* This is the ELF header. We read it in `open_verify'. */ + header = (void *) fbp->buf; + +#ifndef MAP_ANON +# define MAP_ANON 0 + if (_dl_zerofd == -1) + { + _dl_zerofd = _dl_sysdep_open_zero_fill (); + if (_dl_zerofd == -1) + { + __close (fd); + _dl_signal_error (errno, NULL, NULL, + N_("cannot open zero fill device")); + } + } +#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)) + { +#ifdef SHARED + fail_new: +#endif + errstring = N_("cannot create shared object descriptor"); + goto call_lose_errno; + } + + /* Extract the remaining details we need from the ELF header + and then read in the program header table. */ + l->l_entry = header->e_entry; + type = header->e_type; + l->l_phnum = header->e_phnum; + + maplength = header->e_phnum * sizeof (ElfW(Phdr)); + if (header->e_phoff + maplength <= (size_t) fbp->len) + phdr = (void *) (fbp->buf + header->e_phoff); + else + { + phdr = alloca (maplength); + __lseek (fd, header->e_phoff, SEEK_SET); + if ((size_t) __libc_read (fd, (void *) phdr, maplength) != maplength) + { + errstring = N_("cannot read file data"); + goto call_lose_errno; + } + } + + /* Presumed absent PT_GNU_STACK. */ + uint_fast16_t stack_flags = PF_R|PF_W|PF_X; + + { + /* Scan the program header table, collecting its load commands. */ + struct loadcmd + { + ElfW(Addr) mapstart, mapend, dataend, allocend; + off_t mapoff; + int prot; + } loadcmds[l->l_phnum], *c; + size_t nloadcmds = 0; + bool has_holes = false; + + /* The struct is initialized to zero so this is not necessary: + l->l_ld = 0; + l->l_phdr = 0; + l->l_addr = 0; */ + for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph) + switch (ph->p_type) + { + /* These entries tell us where to find things once the file's + segments are mapped in. We record the addresses it says + verbatim, and later correct for the run-time load address. */ + case PT_DYNAMIC: + l->l_ld = (void *) ph->p_vaddr; + l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn)); + break; + + case PT_PHDR: + l->l_phdr = (void *) ph->p_vaddr; + break; + + case PT_LOAD: + /* A load command tells us to map in part of the file. + We record the load commands and process them all later. */ + if (__builtin_expect ((ph->p_align & (GLRO(dl_pagesize) - 1)) != 0, + 0)) + { + errstring = N_("ELF load command alignment not page-aligned"); + goto call_lose; + } + if (__builtin_expect (((ph->p_vaddr - ph->p_offset) + & (ph->p_align - 1)) != 0, 0)) + { + errstring + = N_("ELF load command address/offset not properly aligned"); + goto call_lose; + } + + c = &loadcmds[nloadcmds++]; + c->mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1); + c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1) + & ~(GLRO(dl_pagesize) - 1)); + c->dataend = ph->p_vaddr + ph->p_filesz; + c->allocend = ph->p_vaddr + ph->p_memsz; + c->mapoff = ph->p_offset & ~(GLRO(dl_pagesize) - 1); + + /* Determine whether there is a gap between the last segment + and this one. */ + if (nloadcmds > 1 && c[-1].mapend != c->mapstart) + has_holes = true; + + /* Optimize a common case. */ +#if (PF_R | PF_W | PF_X) == 7 && (PROT_READ | PROT_WRITE | PROT_EXEC) == 7 + c->prot = (PF_TO_PROT + >> ((ph->p_flags & (PF_R | PF_W | PF_X)) * 4)) & 0xf; +#else + c->prot = 0; + if (ph->p_flags & PF_R) + c->prot |= PROT_READ; + if (ph->p_flags & PF_W) + c->prot |= PROT_WRITE; + if (ph->p_flags & PF_X) + c->prot |= PROT_EXEC; +#endif + break; + + case PT_TLS: +#ifdef USE_TLS + if (ph->p_memsz == 0) + /* Nothing to do for an empty segment. */ + break; + + l->l_tls_blocksize = ph->p_memsz; + l->l_tls_align = ph->p_align; + if (ph->p_align == 0) + l->l_tls_firstbyte_offset = 0; + else + l->l_tls_firstbyte_offset = ph->p_vaddr & (ph->p_align - 1); + l->l_tls_initimage_size = ph->p_filesz; + /* Since we don't know the load address yet only store the + offset. We will adjust it later. */ + l->l_tls_initimage = (void *) ph->p_vaddr; + + /* If not loading the initial set of shared libraries, + check whether we should permit loading a TLS segment. */ + if (__builtin_expect (l->l_type == lt_library, 1) + /* If GL(dl_tls_dtv_slotinfo_list) == NULL, then rtld.c did + not set up TLS data structures, so don't use them now. */ + || __builtin_expect (GL(dl_tls_dtv_slotinfo_list) != NULL, 1)) + { + /* Assign the next available module ID. */ + l->l_tls_modid = _dl_next_tls_modid (); + break; + } + +# ifdef SHARED + 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; + + /* In a static binary there is no way to tell if we dynamically + loaded libpthread. */ + if (GL(dl_error_catch_tsd) == &_dl_initial_error_catch_tsd) +# endif + { + /* We have not yet loaded libpthread. + We can do the TLS setup right now! */ + + void *tcb; + + /* The first call allocates TLS bookkeeping data structures. + Then we allocate the TCB for the initial thread. */ + if (__builtin_expect (_dl_tls_setup (), 0) + || __builtin_expect ((tcb = _dl_allocate_tls (NULL)) == NULL, + 0)) + { + errval = ENOMEM; + errstring = N_("\ +cannot allocate TLS data structures for initial thread"); + goto call_lose; + } + + /* Now we install the TCB in the thread register. */ + errstring = TLS_INIT_TP (tcb, 0); + if (__builtin_expect (errstring == NULL, 1)) + { + /* Now we are all good. */ + l->l_tls_modid = ++GL(dl_tls_max_dtv_idx); + break; + } + + /* The kernel is too old or somesuch. */ + errval = 0; + _dl_deallocate_tls (tcb, 1); + goto call_lose; + } +#endif + + /* Uh-oh, the binary expects TLS support but we cannot + provide it. */ + errval = 0; + errstring = N_("cannot handle TLS data"); + goto call_lose; + break; + + case PT_GNU_STACK: + stack_flags = ph->p_flags; + break; + + case PT_GNU_RELRO: + l->l_relro_addr = ph->p_vaddr; + l->l_relro_size = ph->p_memsz; + break; + } + + if (__builtin_expect (nloadcmds == 0, 0)) + { + /* This only happens for a bogus object that will be caught with + another error below. But we don't want to go through the + calculations below using NLOADCMDS - 1. */ + errstring = N_("object file has no loadable segments"); + goto call_lose; + } + + /* Now process the load commands and map segments into memory. */ + c = loadcmds; + + /* Length of the sections to be loaded. */ + maplength = loadcmds[nloadcmds - 1].allocend - c->mapstart; + + if (__builtin_expect (type, ET_DYN) == ET_DYN) + { + /* This is a position-independent shared object. We can let the + kernel map it anywhere it likes, but we must have space for all + the segments in their specified positions relative to the first. + So we map the first segment without MAP_FIXED, but with its + extent increased to cover all the segments. Then we remove + access from excess portion, and there is known sufficient space + there to remap from the later segments. + + As a refinement, sometimes we have an address that we would + prefer to map such objects at; but this is only a preference, + the OS can do whatever it likes. */ + ElfW(Addr) mappref; + mappref = (ELF_PREFERRED_ADDRESS (loader, maplength, + c->mapstart & GLRO(dl_use_load_bias)) + - MAP_BASE_ADDR (l)); + + /* 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, + fd, c->mapoff); + if (__builtin_expect ((void *) l->l_map_start == MAP_FAILED, 0)) + { + map_error: + errstring = N_("failed to map segment from shared object"); + goto call_lose_errno; + } + + l->l_map_end = l->l_map_start + maplength; + l->l_addr = l->l_map_start - c->mapstart; + + if (has_holes) + /* Change protection on the excess portion to disallow all access; + the portions we do not remap later will be inaccessible as if + unallocated. Then jump into the normal segment-mapping loop to + handle the portion of the segment past the end of the file + mapping. */ + __mprotect ((caddr_t) (l->l_addr + c->mapend), + loadcmds[nloadcmds - 1].mapstart - c->mapend, + PROT_NONE); + + goto postmap; + } + + /* This object is loaded at a fixed address. This must never + happen for objects loaded with dlopen(). */ + if (__builtin_expect ((mode & __RTLD_OPENEXEC) == 0, 0)) + { + errstring = N_("cannot dynamically load executable"); + goto call_lose; + } + + /* Notify ELF_PREFERRED_ADDRESS that we have to load this one + fixed. */ + ELF_FIXED_ADDRESS (loader, c->mapstart); + + + /* 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; + + while (c < &loadcmds[nloadcmds]) + { + if (c->mapend > c->mapstart + /* 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, + fd, c->mapoff) + == MAP_FAILED)) + goto map_error; + + postmap: + if (c->prot & PROT_EXEC) + l->l_text_end = l->l_addr + c->mapend; + + if (l->l_phdr == 0 + && (ElfW(Off)) c->mapoff <= header->e_phoff + && ((size_t) (c->mapend - c->mapstart + c->mapoff) + >= header->e_phoff + header->e_phnum * sizeof (ElfW(Phdr)))) + /* Found the program header in this segment. */ + l->l_phdr = (void *) (c->mapstart + header->e_phoff - c->mapoff); + + if (c->allocend > c->dataend) + { + /* Extra zero pages should appear at the end of this segment, + after the data mapped from the file. */ + ElfW(Addr) zero, zeroend, zeropage; + + zero = l->l_addr + c->dataend; + zeroend = l->l_addr + c->allocend; + zeropage = ((zero + GLRO(dl_pagesize) - 1) + & ~(GLRO(dl_pagesize) - 1)); + + if (zeroend < zeropage) + /* All the extra data is in the last page of the segment. + We can just zero it. */ + zeropage = zeroend; + + if (zeropage > zero) + { + /* Zero the final part of the last page of the segment. */ + if (__builtin_expect ((c->prot & PROT_WRITE) == 0, 0)) + { + /* Dag nab it. */ + if (__mprotect ((caddr_t) (zero + & ~(GLRO(dl_pagesize) - 1)), + GLRO(dl_pagesize), c->prot|PROT_WRITE) < 0) + { + errstring = N_("cannot change memory protections"); + goto call_lose_errno; + } + } + memset ((void *) zero, '\0', zeropage - zero); + if (__builtin_expect ((c->prot & PROT_WRITE) == 0, 0)) + __mprotect ((caddr_t) (zero & ~(GLRO(dl_pagesize) - 1)), + GLRO(dl_pagesize), c->prot); + } + + if (zeroend > zeropage) + { + /* Map the remaining zero pages in from the zero fill FD. */ + caddr_t mapat; + mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage, + c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED, + ANONFD, 0); + if (__builtin_expect (mapat == MAP_FAILED, 0)) + { + errstring = N_("cannot map zero-fill pages"); + goto call_lose_errno; + } + } + } + + ++c; + } + } + + if (l->l_ld == 0) + { + if (__builtin_expect (type == ET_DYN, 0)) + { + errstring = N_("object file has no dynamic section"); + goto call_lose; + } + } + else + l->l_ld = (ElfW(Dyn) *) ((ElfW(Addr)) l->l_ld + l->l_addr); + + elf_get_dynamic_info (l, NULL); + + /* Make sure we are not dlopen'ing an object that has the + DF_1_NOOPEN flag set. */ + if (__builtin_expect (l->l_flags_1 & DF_1_NOOPEN, 0) + && (mode & __RTLD_DLOPEN)) + { + /* We are not supposed to load this object. Free all resources. */ + __munmap ((void *) l->l_map_start, l->l_map_end - l->l_map_start); + + if (!l->l_libname->dont_free) + free (l->l_libname); + + if (l->l_phdr_allocated) + free ((void *) l->l_phdr); + + errstring = N_("shared object cannot be dlopen()ed"); + goto call_lose; + } + + if (l->l_phdr == NULL) + { + /* The program header is not contained in any of the segments. + We have to allocate memory ourself and copy it over from out + temporary place. */ + ElfW(Phdr) *newp = (ElfW(Phdr) *) malloc (header->e_phnum + * sizeof (ElfW(Phdr))); + if (newp == NULL) + { + errstring = N_("cannot allocate memory for program header"); + goto call_lose_errno; + } + + l->l_phdr = memcpy (newp, phdr, + (header->e_phnum * sizeof (ElfW(Phdr)))); + l->l_phdr_allocated = 1; + } + else + /* Adjust the PT_PHDR value by the runtime load address. */ + l->l_phdr = (ElfW(Phdr) *) ((ElfW(Addr)) l->l_phdr + l->l_addr); + + 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. */ +#if defined HAVE_Z_RELRO && defined SHARED + if ((mode & (__RTLD_DLOPEN | __RTLD_AUDIT)) == __RTLD_DLOPEN) + { + 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_READ|PROT_WRITE|PROT_EXEC; + +#ifdef check_consistency + check_consistency (); +#endif + + errval = (*GL(dl_make_stack_executable_hook)) (stack_endp); + if (errval) + { + errstring = N_("\ +cannot enable executable stack as shared object requires"); + goto call_lose; + } + } + +#ifdef USE_TLS + /* Adjust the address of the TLS initialization image. */ + if (l->l_tls_initimage != NULL) + l->l_tls_initimage = (char *) l->l_tls_initimage + l->l_addr; +#endif + + /* We are done mapping in the file. We no longer need the descriptor. */ + if (__builtin_expect (__close (fd) != 0, 0)) + { + errstring = N_("cannot close file descriptor"); + goto call_lose_errno; + } + /* Signal that we closed the file. */ + fd = -1; + + if (l->l_type == lt_library && type == ET_EXEC) + l->l_type = lt_executable; + + l->l_entry += l->l_addr; + + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) + _dl_debug_printf ("\ + dynamic: 0x%0*lx base: 0x%0*lx size: 0x%0*Zx\n\ + entry: 0x%0*lx phdr: 0x%0*lx phnum: %*u\n\n", + (int) sizeof (void *) * 2, + (unsigned long int) l->l_ld, + (int) sizeof (void *) * 2, + (unsigned long int) l->l_addr, + (int) sizeof (void *) * 2, maplength, + (int) sizeof (void *) * 2, + (unsigned long int) l->l_entry, + (int) sizeof (void *) * 2, + (unsigned long int) l->l_phdr, + (int) sizeof (void *) * 2, l->l_phnum); + + /* Set up the symbol hash table. */ + _dl_setup_hash (l); + + /* If this object has DT_SYMBOLIC set modify now its scope. We don't + have to do this for the main map. */ + if ((mode & RTLD_DEEPBIND) == 0 + && __builtin_expect (l->l_info[DT_SYMBOLIC] != NULL, 0) + && &l->l_searchlist != l->l_scope[0]) + { + /* Create an appropriate searchlist. It contains only this map. + This is the definition of DT_SYMBOLIC in SysVr4. */ + l->l_symbolic_searchlist.r_list = + (struct link_map **) malloc (sizeof (struct link_map *)); + + if (l->l_symbolic_searchlist.r_list == NULL) + { + errstring = N_("cannot create searchlist"); + goto call_lose_errno; + } + + l->l_symbolic_searchlist.r_list[0] = l; + l->l_symbolic_searchlist.r_nlist = 1; + + /* Now move the existing entries one back. */ + memmove (&l->l_scope[1], &l->l_scope[0], + (l->l_scope_max - 1) * sizeof (l->l_scope[0])); + + /* Now add the new entry. */ + l->l_scope[0] = &l->l_symbolic_searchlist; + } + + /* Remember whether this object must be initialized first. */ + if (l->l_flags_1 & DF_1_INITFIRST) + GL(dl_initfirst) = l; + + /* Finally the file information. */ + l->l_dev = st.st_dev; + l->l_ino = st.st_ino; + + /* When we profile the SONAME might be needed for something else but + loading. Add it right away. */ + if (__builtin_expect (GLRO(dl_profile) != NULL, 0) + && l->l_info[DT_SONAME] != NULL) + 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; +} + +/* Print search path. */ +static void +print_search_path (struct r_search_path_elem **list, + const char *what, const char *name) +{ + char buf[max_dirnamelen + max_capstrlen]; + int first = 1; + + _dl_debug_printf (" search path="); + + while (*list != NULL && (*list)->what == what) /* Yes, ==. */ + { + char *endp = __mempcpy (buf, (*list)->dirname, (*list)->dirnamelen); + size_t cnt; + + for (cnt = 0; cnt < ncapstr; ++cnt) + if ((*list)->status[cnt] != nonexisting) + { + char *cp = __mempcpy (endp, capstr[cnt].str, capstr[cnt].len); + if (cp == buf || (cp == buf + 1 && buf[0] == '/')) + cp[0] = '\0'; + else + cp[-1] = '\0'; + + _dl_debug_printf_c (first ? "%s" : ":%s", buf); + first = 0; + } + + ++list; + } + + if (name != NULL) + _dl_debug_printf_c ("\t\t(%s from file %s)\n", what, + name[0] ? name : rtld_progname); + else + _dl_debug_printf_c ("\t\t(%s)\n", what); +} + +/* Open a file and verify it is an ELF file for this architecture. We + ignore only ELF files for other architectures. Non-ELF files and + ELF files with different header information cause fatal errors since + 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, struct link_map *loader, + int whatcode, bool *found_other_class, bool free_name) +{ + /* This is the expected ELF header. */ +#define ELF32_CLASS ELFCLASS32 +#define ELF64_CLASS ELFCLASS64 +#ifndef VALID_ELF_HEADER +# define VALID_ELF_HEADER(hdr,exp,size) (memcmp (hdr, exp, size) == 0) +# define VALID_ELF_OSABI(osabi) (osabi == ELFOSABI_SYSV) +# define VALID_ELF_ABIVERSION(ver) (ver == 0) +#endif + static const unsigned char expected[EI_PAD] = + { + [EI_MAG0] = ELFMAG0, + [EI_MAG1] = ELFMAG1, + [EI_MAG2] = ELFMAG2, + [EI_MAG3] = ELFMAG3, + [EI_CLASS] = ELFW(CLASS), + [EI_DATA] = byteorder, + [EI_VERSION] = EV_CURRENT, + [EI_OSABI] = ELFOSABI_SYSV, + [EI_ABIVERSION] = 0 + }; + static const struct + { + ElfW(Word) vendorlen; + ElfW(Word) datalen; + ElfW(Word) type; + char vendor[4]; + } expected_note = { 4, 16, 1, "GNU" }; + /* 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. */ + int fd = __open (name, O_RDONLY); + if (fd != -1) + { + ElfW(Ehdr) *ehdr; + ElfW(Phdr) *phdr, *ph; + ElfW(Word) *abi_note, abi_note_buf[8]; + unsigned int osversion; + size_t maplength; + + /* We successfully openened the file. Now verify it is a file + we can use. */ + __set_errno (0); + fbp->len = __libc_read (fd, fbp->buf, sizeof (fbp->buf)); + + /* This is where the ELF header is loaded. */ + assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr))); + ehdr = (ElfW(Ehdr) *) fbp->buf; + + /* Now run the tests. */ + if (__builtin_expect (fbp->len < (ssize_t) sizeof (ElfW(Ehdr)), 0)) + { + errval = errno; + errstring = (errval == 0 + ? N_("file too short") : N_("cannot read file data")); + call_lose: + 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. */ + if (__builtin_expect (! VALID_ELF_HEADER (ehdr->e_ident, expected, + EI_PAD), 0)) + { + /* Something is wrong. */ + if (*(Elf32_Word *) &ehdr->e_ident != +#if BYTE_ORDER == LITTLE_ENDIAN + ((ELFMAG0 << (EI_MAG0 * 8)) | + (ELFMAG1 << (EI_MAG1 * 8)) | + (ELFMAG2 << (EI_MAG2 * 8)) | + (ELFMAG3 << (EI_MAG3 * 8))) +#else + ((ELFMAG0 << (EI_MAG3 * 8)) | + (ELFMAG1 << (EI_MAG2 * 8)) | + (ELFMAG2 << (EI_MAG1 * 8)) | + (ELFMAG3 << (EI_MAG0 * 8))) +#endif + ) + 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. */ + *found_other_class = true; + goto close_and_out; + } + else if (ehdr->e_ident[EI_DATA] != byteorder) + { + if (BYTE_ORDER == BIG_ENDIAN) + errstring = N_("ELF file data encoding not big-endian"); + else + errstring = N_("ELF file data encoding not little-endian"); + } + else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) + errstring + = N_("ELF file version ident does not match current one"); + /* XXX We should be able so set system specific versions which are + allowed here. */ + else if (!VALID_ELF_OSABI (ehdr->e_ident[EI_OSABI])) + errstring = N_("ELF file OS ABI invalid"); + else if (!VALID_ELF_ABIVERSION (ehdr->e_ident[EI_ABIVERSION])) + errstring = N_("ELF file ABI version invalid"); + else + /* Otherwise we don't know what went wrong. */ + errstring = N_("internal error"); + + goto call_lose; + } + + if (__builtin_expect (ehdr->e_version, EV_CURRENT) != EV_CURRENT) + { + errstring = N_("ELF file version does not match current one"); + goto call_lose; + } + if (! __builtin_expect (elf_machine_matches_host (ehdr), 1)) + goto close_and_out; + else if (__builtin_expect (ehdr->e_type, ET_DYN) != ET_DYN + && __builtin_expect (ehdr->e_type, ET_EXEC) != ET_EXEC) + { + errstring = N_("only ET_DYN and ET_EXEC can be loaded"); + goto call_lose; + } + else if (__builtin_expect (ehdr->e_phentsize, sizeof (ElfW(Phdr))) + != sizeof (ElfW(Phdr))) + { + errstring = N_("ELF file's phentsize not the expected size"); + goto call_lose; + } + + maplength = ehdr->e_phnum * sizeof (ElfW(Phdr)); + if (ehdr->e_phoff + maplength <= (size_t) fbp->len) + phdr = (void *) (fbp->buf + ehdr->e_phoff); + else + { + phdr = alloca (maplength); + __lseek (fd, ehdr->e_phoff, SEEK_SET); + if ((size_t) __libc_read (fd, (void *) phdr, maplength) != maplength) + { + read_error: + errval = errno; + errstring = N_("cannot read file data"); + goto call_lose; + } + } + + /* Check .note.ABI-tag if present. */ + for (ph = phdr; ph < &phdr[ehdr->e_phnum]; ++ph) + if (ph->p_type == PT_NOTE && ph->p_filesz == 32 && ph->p_align >= 4) + { + if (ph->p_offset + 32 <= (size_t) fbp->len) + abi_note = (void *) (fbp->buf + ph->p_offset); + else + { + __lseek (fd, ph->p_offset, SEEK_SET); + if (__libc_read (fd, (void *) abi_note_buf, 32) != 32) + goto read_error; + + abi_note = abi_note_buf; + } + + if (memcmp (abi_note, &expected_note, sizeof (expected_note))) + continue; + + osversion = (abi_note[5] & 0xff) * 65536 + + (abi_note[6] & 0xff) * 256 + + (abi_note[7] & 0xff); + if (abi_note[4] != __ABI_TAG_OS + || (GLRO(dl_osversion) && GLRO(dl_osversion) < osversion)) + { + close_and_out: + __close (fd); + __set_errno (ENOENT); + fd = -1; + } + + break; + } + } + + return fd; +} + +/* Try to open NAME in one of the directories in *DIRSP. + Return the fd, or -1. If successful, fill in *REALNAME + with the malloc'd full directory name. If it turns out + that none of the directories in *DIRSP exists, *DIRSP is + replaced with (void *) -1, and the old value is free()d + if MAY_FREE_DIRS is true. */ + +static int +open_path (const char *name, size_t namelen, int preloaded, + struct r_search_path_struct *sps, char **realname, + struct filebuf *fbp, struct link_map *loader, int whatcode, + bool *found_other_class) +{ + struct r_search_path_elem **dirs = sps->dirs; + char *buf; + int fd = -1; + 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 + { + struct r_search_path_elem *this_dir = *dirs; + size_t buflen = 0; + size_t cnt; + char *edp; + int here_any = 0; + int err; + + /* If we are debugging the search for libraries print the path + now if it hasn't happened now. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0) + && current_what != this_dir->what) + { + current_what = this_dir->what; + print_search_path (dirs, current_what, this_dir->where); + } + + edp = (char *) __mempcpy (buf, this_dir->dirname, this_dir->dirnamelen); + for (cnt = 0; fd == -1 && cnt < ncapstr; ++cnt) + { + /* Skip this directory if we know it does not exist. */ + if (this_dir->status[cnt] == nonexisting) + continue; + + buflen = + ((char *) __mempcpy (__mempcpy (edp, capstr[cnt].str, + capstr[cnt].len), + name, namelen) + - buf); + + /* Print name we try if this is wanted. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) + _dl_debug_printf (" trying file=%s\n", buf); + + 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; + /* 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. */ + struct stat64 st; + + buf[buflen - namelen - 1] = '\0'; + + if (__xstat64 (_STAT_VER, buf, &st) != 0 + || ! S_ISDIR (st.st_mode)) + /* The directory does not exist or it is no directory. */ + this_dir->status[cnt] = nonexisting; + else + this_dir->status[cnt] = existing; + } + } + + /* Remember whether we found any existing directory. */ + here_any |= this_dir->status[cnt] != nonexisting; + + if (fd != -1 && __builtin_expect (preloaded, 0) + && INTUSE(__libc_enable_secure)) + { + /* This is an extra security effort to make sure nobody can + preload broken shared objects which are in the trusted + directories and so exploit the bugs. */ + struct stat64 st; + + if (__fxstat64 (_STAT_VER, fd, &st) != 0 + || (st.st_mode & S_ISUID) == 0) + { + /* The shared object cannot be tested for being SUID + or this bit is not set. In this case we must not + use this object. */ + __close (fd); + fd = -1; + /* We simply ignore the file, signal this by setting + the error value which would have been set by `open'. */ + errno = ENOENT; + } + } + } + + if (fd != -1) + { + *realname = (char *) malloc (buflen); + if (*realname != NULL) + { + memcpy (*realname, buf, buflen); + return fd; + } + else + { + /* No memory for the name, we certainly won't be able + to load and link it. */ + __close (fd); + return -1; + } + } + if (here_any && (err = errno) != ENOENT && err != EACCES) + /* The file exists and is readable, but something went wrong. */ + return -1; + + /* Remember whether we found anything. */ + any |= here_any; + } + while (*++dirs != NULL); + + /* Remove the whole path if none of the directories exists. */ + if (__builtin_expect (! any, 0)) + { + /* Paths which were allocated using the minimal malloc() in ld.so + must not be freed using the general free() in libc. */ + if (sps->malloced) + free (sps->dirs); +#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; +} + +/* Map in the shared object file NAME. */ + +struct link_map * +internal_function +_dl_map_object (struct link_map *loader, const char *name, int preloaded, + int type, int trace_mode, int mode, Lmid_t nsid) +{ + int fd; + char *realname; + char *name_copy; + struct link_map *l; + struct filebuf fb; + + assert (nsid >= 0); + assert (nsid < DL_NNS); + + /* Look for this name among those already loaded. */ + for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next) + { + /* 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 + || __builtin_expect (l->l_removed, 0) != 0) + continue; + if (!_dl_name_match_p (name, l)) + { + const char *soname; + + if (__builtin_expect (l->l_soname_added, 1) + || l->l_info[DT_SONAME] == NULL) + continue; + + soname = ((const char *) D_PTR (l, l_info[DT_STRTAB]) + + l->l_info[DT_SONAME]->d_un.d_val); + if (strcmp (name, soname) != 0) + continue; + + /* We have a match on a new name -- cache it. */ + add_name_to_object (l, soname); + l->l_soname_added = 1; + } + + /* We have a match. */ + return l; + } + + /* Display information if we are debugging. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0) + && loader != NULL) + _dl_debug_printf ("\nfile=%s [%lu]; needed by %s [%lu]\n", name, nsid, + 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. */ + + size_t namelen = strlen (name) + 1; + + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) + _dl_debug_printf ("find library=%s [%lu]; searching\n", name, nsid); + + fd = -1; + + /* When the object has the RUNPATH information we don't use any + 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; 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, 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 && !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, + 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, + LA_SER_RUNPATH, &found_other_class); + + if (fd == -1 + && (__builtin_expect (! preloaded, 1) + || ! INTUSE(__libc_enable_secure))) + { + /* Check the list of libraries in the file /etc/ld.so.cache, + for compatibility with Linux's ldconfig program. */ + const char *cached = _dl_load_cache_lookup (name); + + if (cached != NULL) + { +#ifdef SHARED + // XXX Correct to unconditionally default to namespace 0? + l = loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded; +#else + l = loader; +#endif + + /* If the loader has the DF_1_NODEFLIB flag set we must not + use a cache entry from any of these directories. */ + if ( +#ifndef SHARED + /* 'l' is always != NULL for dynamically linked objects. */ + l != NULL && +#endif + __builtin_expect (l->l_flags_1 & DF_1_NODEFLIB, 0)) + { + const char *dirp = system_dirs; + unsigned int cnt = 0; + + do + { + if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0) + { + /* The prefix matches. Don't use the entry. */ + cached = NULL; + break; + } + + dirp += system_dirs_len[cnt] + 1; + ++cnt; + } + while (cnt < nsystem_dirs_len); + } + + if (cached != NULL) + { + 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); + if (realname == NULL) + { + __close (fd); + fd = -1; + } + } + } + } + } + + /* Finally, try the default path. */ + if (fd == -1 + && ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL + || __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, 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)) + _dl_debug_printf ("\n"); + } + else + { + /* The path may contain dynamic string tokens. */ + realname = (loader + ? expand_dynamic_string_token (loader, name) + : local_strdup (name)); + if (realname == NULL) + fd = -1; + else + { + 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. */ + if (mode & __RTLD_CALLMAP) + loader = NULL; + + if (__builtin_expect (fd, 0) == -1) + { + if (trace_mode + && __builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK, 0) == 0) + { + /* We haven't found an appropriate library. But since we + are only interested in the list of libraries this isn't + so severe. Fake an entry with all the information we + have. */ + static const Elf_Symndx dummy_bucket = STN_UNDEF; + + /* Enter the new object in the list of loaded objects. */ + if ((name_copy = local_strdup (name)) == NULL + || (l = _dl_new_object (name_copy, name, type, loader, + mode, nsid)) == NULL) + { + 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 + have do this here. + l->l_reserved = 0; */ + l->l_buckets = &dummy_bucket; + l->l_nbuckets = 1; + l->l_relocated = 1; + + 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")); + } + + void *stack_end = __libc_stack_end; + return _dl_map_object_from_fd (name, fd, &fb, realname, loader, type, mode, + &stack_end, nsid); +} + + +void +internal_function +_dl_rtld_di_serinfo (struct link_map *loader, Dl_serinfo *si, bool counting) +{ + if (counting) + { + si->dls_cnt = 0; + si->dls_size = 0; + } + + unsigned int idx = 0; + char *allocptr = (char *) &si->dls_serpath[si->dls_cnt]; + void add_path (const struct r_search_path_struct *sps, unsigned int flags) +# define add_path(sps, flags) add_path(sps, 0) /* XXX */ + { + if (sps->dirs != (void *) -1) + { + struct r_search_path_elem **dirs = sps->dirs; + do + { + const struct r_search_path_elem *const r = *dirs++; + if (counting) + { + si->dls_cnt++; + si->dls_size += r->dirnamelen; + } + else + { + Dl_serpath *const sp = &si->dls_serpath[idx++]; + sp->dls_name = allocptr; + allocptr = __mempcpy (allocptr, + r->dirname, r->dirnamelen - 1); + *allocptr++ = '\0'; + sp->dls_flags = flags; + } + } + while (*dirs != NULL); + } + } + + /* When the object has the RUNPATH information we don't use any RPATHs. */ + if (loader->l_info[DT_RUNPATH] == NULL) + { + /* First try the DT_RPATH of the dependent object that caused NAME + to be loaded. Then that object's dependent, and on up. */ + + struct link_map *l = loader; + do + { + if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH")) + add_path (&l->l_rpath_dirs, XXX_RPATH); + l = l->l_loader; + } + while (l != NULL); + + /* If dynamically linked, try the DT_RPATH of the executable itself. */ + if (loader->l_ns == LM_ID_BASE) + { + l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + if (l != NULL && l->l_type != lt_loaded && l != loader) + if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH")) + add_path (&l->l_rpath_dirs, XXX_RPATH); + } + } + + /* Try the LD_LIBRARY_PATH environment variable. */ + add_path (&env_path_list, XXX_ENV); + + /* Look at the RUNPATH information for this binary. */ + if (cache_rpath (loader, &loader->l_runpath_dirs, DT_RUNPATH, "RUNPATH")) + add_path (&loader->l_runpath_dirs, XXX_RUNPATH); + + /* XXX + Here is where ld.so.cache gets checked, but we don't have + a way to indicate that in the results for Dl_serinfo. */ + + /* Finally, try the default path. */ + if (!(loader->l_flags_1 & DF_1_NODEFLIB)) + add_path (&rtld_search_dirs, XXX_default); + + if (counting) + /* Count the struct size before the string area, which we didn't + know before we completed dls_cnt. */ + si->dls_size += (char *) &si->dls_serpath[si->dls_cnt] - (char *) si; +} diff --git a/libc/elf/dl-lookup.c b/libc/elf/dl-lookup.c new file mode 100644 index 000000000..7cfcc620a --- /dev/null +++ b/libc/elf/dl-lookup.c @@ -0,0 +1,484 @@ +/* Look up a symbol in the loaded objects. + 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 + 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 <alloca.h> +#include <libintl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ldsodefs.h> +#include <dl-hash.h> +#include <dl-machine.h> +#include <bits/libc-lock.h> +#include <tls.h> + +#include <assert.h> + +#define VERSTAG(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (tag)) + +/* We need this string more than once. */ +static const char undefined_msg[] = "undefined symbol: "; + + +struct sym_val + { + const ElfW(Sym) *s; + struct link_map *m; + }; + + +#define make_string(string, rest...) \ + ({ \ + const char *all[] = { string, ## rest }; \ + size_t len, cnt; \ + char *result, *cp; \ + \ + len = 1; \ + for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ + len += strlen (all[cnt]); \ + \ + cp = result = alloca (len); \ + for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ + cp = __stpcpy (cp, all[cnt]); \ + \ + result; \ + }) + +/* Statistics function. */ +#ifdef SHARED +# define bump_num_relocations() ++GL(dl_num_relocations) +#else +# define bump_num_relocations() ((void) 0) +#endif + + +/* The actual lookup code. */ +#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 +add_dependency (struct link_map *undef_map, struct link_map *map) +{ + struct link_map **list; + struct link_map *runp; + unsigned int act; + unsigned int i; + int result = 0; + + /* Avoid self-references and references to objects which cannot be + unloaded anyway. */ + if (undef_map == map) + return 0; + + /* Make sure nobody can unload the object while we are at it. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + /* Avoid references to objects which cannot be unloaded anyway. */ + if (map->l_type != lt_loaded + || (map->l_flags_1 & DF_1_NODELETE) != 0) + goto out; + + /* If the object with the undefined reference cannot be removed ever + just make sure the same is true for the object which contains the + definition. */ + if (undef_map->l_type != lt_loaded + || (undef_map->l_flags_1 & DF_1_NODELETE) != 0) + { + 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_initfini != NULL) + { + list = undef_map->l_initfini; + + for (i = 0; list[i] != NULL; ++i) + if (list[i] == map) + goto out; + } + + /* No normal dependency. See whether we already had to add it + to the special list of dynamic dependencies. */ + list = undef_map->l_reldeps; + act = undef_map->l_reldepsact; + + for (i = 0; i < act; ++i) + if (list[i] == map) + goto out; + + /* The object is not yet in the dependency list. Before we add + it make sure just one more time the object we are about to + reference is still available. There is a brief period in + which the object could have been removed since we found the + definition. */ + runp = GL(dl_ns)[undef_map->l_ns]._ns_loaded; + while (runp != NULL && runp != map) + runp = runp->l_next; + + if (runp != NULL) + { + /* The object is still available. Add the reference now. */ + if (__builtin_expect (act >= undef_map->l_reldepsmax, 0)) + { + /* Allocate more memory for the dependency list. Since this + can never happen during the startup phase we can use + `realloc'. */ + void *newp; + + undef_map->l_reldepsmax += 5; + newp = realloc (undef_map->l_reldeps, + undef_map->l_reldepsmax + * sizeof (struct link_map *)); + + if (__builtin_expect (newp != NULL, 1)) + undef_map->l_reldeps = (struct link_map **) newp; + else + /* Correct the addition. */ + undef_map->l_reldepsmax -= 5; + } + + /* If we didn't manage to allocate memory for the list this is + no fatal mistake. We simply increment the use counter of the + referenced object and don't record the dependencies. This + means this increment can never be reverted and the object + will never be unloaded. This is semantically the correct + behavior. */ + if (__builtin_expect (act < undef_map->l_reldepsmax, 1)) + undef_map->l_reldeps[undef_map->l_reldepsact++] = map; + + /* Display information if we are debugging. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) + _dl_debug_printf ("\ +\nfile=%s [%lu]; needed by %s [%lu] (relocation dependency)\n\n", + map->l_name[0] ? map->l_name : rtld_progname, + map->l_ns, + undef_map->l_name[0] + ? undef_map->l_name : rtld_progname, + undef_map->l_ns); + } + else + /* Whoa, that was bad luck. We have to search again. */ + result = -1; + + out: + /* Release the lock. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + return result; +} + +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 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. */ +lookup_t +internal_function +_dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, + const ElfW(Sym) **ref, + struct r_scope_elem *symbol_scope[], + const struct r_found_version *version, + int type_class, int flags, struct link_map *skip_map) +{ + 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; + + bump_num_relocations (); + + /* 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); + + 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. */ + for (size_t start = i; *scope != NULL; start = 0, ++scope) + { + 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; + + if (__builtin_expect (res, 0) < 0 && skip_map == NULL) + { + /* Oh, oh. The file named in the relocation entry does not + contain the needed symbol. This code is never reached + for unversioned lookups. */ + assert (version != NULL); + const char *reference_name = undef_map ? undef_map->l_name : NULL; + + /* XXX We cannot translate the message. */ + _dl_signal_cerror (0, (reference_name[0] + ? reference_name + : (rtld_progname ?: "<main program>")), + N_("relocation error"), + make_string ("symbol ", undef_name, ", version ", + version->name, + " not defined in file ", + version->filename, + " with link time reference", + res == -2 + ? " (no version symbols)" : "")); + *ref = NULL; + return 0; + } + } + + if (__builtin_expect (current_value.s == NULL, 0)) + { + if ((*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK) + && skip_map == NULL) + { + /* We could find no value for a strong reference. */ + const char *reference_name = undef_map ? undef_map->l_name : ""; + const char *versionstr = version ? ", version " : ""; + const char *versionname = (version && version->name + ? version->name : ""); + + /* XXX We cannot translate the message. */ + _dl_signal_cerror (0, (reference_name[0] + ? reference_name + : (rtld_progname ?: "<main program>")), + N_("symbol lookup error"), + make_string (undefined_msg, undef_name, + versionstr, versionname)); + } + *ref = NULL; + return 0; + } + + int protected = (*ref + && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED); + if (__builtin_expect (protected != 0, 0)) + { + /* It is very tricky. We need to figure out what value to + return for the protected symbol. */ + if (type_class == ELF_RTYPE_CLASS_PLT) + { + if (current_value.s != NULL && current_value.m != undef_map) + { + current_value.s = *ref; + current_value.m = undef_map; + } + } + else + { + struct sym_val protected_value = { NULL, NULL }; + + for (scope = symbol_scope; *scope != NULL; i = 0, ++scope) + 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) + { + current_value.s = *ref; + current_value.m = undef_map; + } + } + } + + /* We have to check whether this would bind UNDEF_MAP to an object + in the global scope which was dynamically loaded. In this case + we have to prevent the latter from being unloaded unless the + UNDEF_MAP object is also unloaded. */ + if (__builtin_expect (current_value.m->l_type == lt_loaded, 0) + /* Don't do this for explicit lookups as opposed to implicit + runtime lookups. */ + && (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0 + /* Add UNDEF_MAP to the dependencies. */ + && 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); + + /* 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, + ¤t_value, version, type_class, protected); + + *ref = current_value.s; + return LOOKUP_VALUE (current_value.m); +} + + +/* Cache the location of MAP's hash table. */ + +void +internal_function +_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]); + + map->l_nbuckets = *hash++; + nchain = *hash++; + map->l_buckets = hash; + hash += map->l_nbuckets; + map->l_chain = hash; +} + + +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 struct r_found_version *version, int type_class, + int protected) +{ + const char *reference_name = undef_map->l_name; + + if (GLRO(dl_debug_mask) & DL_DEBUG_BINDINGS) + { + _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); + else + _dl_debug_printf_c ("\n"); + } +#ifdef SHARED + if (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) + { + int conflict = 0; + struct sym_val val = { NULL, NULL }; + + if ((GLRO(dl_trace_prelink_map) == NULL + || GLRO(dl_trace_prelink_map) == GL(dl_ns)[LM_ID_BASE]._ns_loaded) + && undef_map != GL(dl_ns)[LM_ID_BASE]._ns_loaded) + { + const uint_fast32_t new_hash = dl_new_hash (undef_name); + unsigned long int old_hash = 0xffffffff; + + do_lookup_x (undef_name, new_hash, &old_hash, *ref, &val, + undef_map->l_local_scope[0], 0, version, 0, NULL, + type_class); + + if (val.s != value->s || val.m != value->m) + conflict = 1; + } + +# ifdef USE_TLS + if (value->s + && (__builtin_expect (ELFW(ST_TYPE) (value->s->st_info) + == STT_TLS, 0))) + type_class = 4; +# endif + + if (conflict + || GLRO(dl_trace_prelink_map) == undef_map + || GLRO(dl_trace_prelink_map) == NULL + || type_class == 4) + { + _dl_printf ("%s 0x%0*Zx 0x%0*Zx -> 0x%0*Zx 0x%0*Zx ", + conflict ? "conflict" : "lookup", + (int) sizeof (ElfW(Addr)) * 2, + (size_t) undef_map->l_map_start, + (int) sizeof (ElfW(Addr)) * 2, + (size_t) (((ElfW(Addr)) *ref) - undef_map->l_map_start), + (int) sizeof (ElfW(Addr)) * 2, + (size_t) (value->s ? value->m->l_map_start : 0), + (int) sizeof (ElfW(Addr)) * 2, + (size_t) (value->s ? value->s->st_value : 0)); + + if (conflict) + _dl_printf ("x 0x%0*Zx 0x%0*Zx ", + (int) sizeof (ElfW(Addr)) * 2, + (size_t) (val.s ? val.m->l_map_start : 0), + (int) sizeof (ElfW(Addr)) * 2, + (size_t) (val.s ? val.s->st_value : 0)); + + _dl_printf ("/%x %s\n", type_class, undef_name); + } + } +#endif +} diff --git a/libc/elf/dl-minimal.c b/libc/elf/dl-minimal.c new file mode 100644 index 000000000..868d3bd2e --- /dev/null +++ b/libc/elf/dl-minimal.c @@ -0,0 +1,362 @@ +/* Minimal replacements for basic facilities used in the dynamic linker. + Copyright (C) 1995-1998,2000-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 + 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 <limits.h> +#include <string.h> +#include <tls.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/types.h> +#include <ldsodefs.h> +#include <stdio-common/_itoa.h> + +#include <assert.h> + +/* Minimal `malloc' allocator for use while loading shared libraries. + No block is ever freed. */ + +static void *alloc_ptr, *alloc_end, *alloc_last_block; + +/* Declarations of global functions. */ +extern void weak_function free (void *ptr); +extern void * weak_function realloc (void *ptr, size_t n); +extern unsigned long int weak_function __strtoul_internal (const char *nptr, + char **endptr, + int base, + int group); +extern unsigned long int weak_function strtoul (const char *nptr, + char **endptr, int base); + + +/* Allocate an aligned memory block. */ +void * weak_function +__libc_memalign (size_t align, size_t n) +{ +#ifdef MAP_ANON +#define _dl_zerofd (-1) +#else + extern int _dl_zerofd; + + if (_dl_zerofd == -1) + _dl_zerofd = _dl_sysdep_open_zero_fill (); +#define MAP_ANON 0 +#endif + + if (alloc_end == 0) + { + /* Consume any unused space in the last page of our data segment. */ + extern int _end attribute_hidden; + alloc_ptr = &_end; + alloc_end = (void *) 0 + (((alloc_ptr - (void *) 0) + + GLRO(dl_pagesize) - 1) + & ~(GLRO(dl_pagesize) - 1)); + } + + /* Make sure the allocation pointer is ideally aligned. */ + alloc_ptr = (void *) 0 + (((alloc_ptr - (void *) 0) + align - 1) + & ~(align - 1)); + + if (alloc_ptr + n >= alloc_end) + { + /* Insufficient space left; allocate another page. */ + caddr_t page; + size_t nup = (n + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1); + page = __mmap (0, nup, PROT_READ|PROT_WRITE, + MAP_ANON|MAP_PRIVATE, _dl_zerofd, 0); + assert (page != MAP_FAILED); + if (page != alloc_end) + alloc_ptr = page; + alloc_end = page + nup; + } + + alloc_last_block = (void *) alloc_ptr; + alloc_ptr += n; + return alloc_last_block; +} + +void * weak_function +malloc (size_t n) +{ + return __libc_memalign (sizeof (double), n); +} + +/* We use this function occasionally since the real implementation may + be optimized when it can assume the memory it returns already is + set to NUL. */ +void * weak_function +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); +} + +/* This will rarely be called. */ +void weak_function +free (void *ptr) +{ + /* We can free only the last block allocated. */ + if (ptr == alloc_last_block) + { + /* Since this is rare, we clear the freed block here + so that calloc can presume malloc returns cleared memory. */ + memset (alloc_last_block, '\0', alloc_ptr - alloc_last_block); + alloc_ptr = alloc_last_block; + } +} + +/* This is only called with the most recent block returned by malloc. */ +void * weak_function +realloc (void *ptr, size_t n) +{ + void *new; + if (ptr == NULL) + return malloc (n); + assert (ptr == alloc_last_block); + alloc_ptr = alloc_last_block; + new = malloc (n); + assert (new == ptr); + return new; +} + +/* Avoid signal frobnication in setjmp/longjmp. Keeps things smaller. */ + +#include <setjmp.h> + +int weak_function +__sigjmp_save (sigjmp_buf env, int savemask __attribute__ ((unused))) +{ + env[0].__mask_was_saved = 0; + return 0; +} + +/* Define our own version of the internal function used by strerror. We + only provide the messages for some common errors. This avoids pulling + in the whole error list. */ + +char * weak_function +__strerror_r (int errnum, char *buf, size_t buflen) +{ + char *msg; + + switch (errnum) + { + case ENOMEM: + msg = (char *) "Cannot allocate memory"; + break; + case EINVAL: + msg = (char *) "Invalid argument"; + break; + case ENOENT: + msg = (char *) "No such file or directory"; + break; + case EPERM: + msg = (char *) "Operation not permitted"; + break; + case EIO: + msg = (char *) "Input/output error"; + break; + case EACCES: + msg = (char *) "Permission denied"; + break; + default: + /* No need to check buffer size, all calls in the dynamic linker + provide enough space. */ + buf[buflen - 1] = '\0'; + msg = _itoa (errnum, buf + buflen - 1, 10, 0); + msg = memcpy (msg - (sizeof ("Error ") - 1), "Error ", + sizeof ("Error ") - 1); + break; + } + + return msg; +} + +#ifndef NDEBUG + +/* Define (weakly) our own assert failure function which doesn't use stdio. + If we are linked into the user program (-ldl), the normal __assert_fail + defn can override this one. */ + +void weak_function +__assert_fail (const char *assertion, + const char *file, unsigned int line, const char *function) +{ + _dl_fatal_printf ("\ +Inconsistency detected by ld.so: %s: %u: %s%sAssertion `%s' failed!\n", + file, line, function ?: "", function ? ": " : "", + assertion); + +} +rtld_hidden_weak(__assert_fail) + +void weak_function +__assert_perror_fail (int errnum, + const char *file, unsigned int line, + const char *function) +{ + char errbuf[400]; + _dl_fatal_printf ("\ +Inconsistency detected by ld.so: %s: %u: %s%sUnexpected error: %s.\n", + file, line, function ?: "", function ? ": " : "", + __strerror_r (errnum, errbuf, sizeof errbuf)); + +} +rtld_hidden_weak (__assert_perror_fail) +#endif + +unsigned long int weak_function +__strtoul_internal (const char *nptr, char **endptr, int base, int group) +{ + unsigned long int result = 0; + long int sign = 1; + + while (*nptr == ' ' || *nptr == '\t') + ++nptr; + + if (*nptr == '-') + { + sign = -1; + ++nptr; + } + else if (*nptr == '+') + ++nptr; + + if (*nptr < '0' || *nptr > '9') + { + if (endptr != NULL) + *endptr = (char *) nptr; + return 0UL; + } + + assert (base == 0); + base = 10; + if (*nptr == '0') + { + if (nptr[1] == 'x' || nptr[1] == 'X') + { + base = 16; + nptr += 2; + } + else + base = 8; + } + + while (*nptr >= '0' && *nptr <= '9') + { + unsigned long int digval = *nptr - '0'; + if (result > LONG_MAX / 10 + || (result == ULONG_MAX / 10 && digval > ULONG_MAX % 10)) + { + errno = ERANGE; + if (endptr != NULL) + *endptr = (char *) nptr; + return ULONG_MAX; + } + result *= base; + result += digval; + ++nptr; + } + + if (endptr != NULL) + *endptr = (char *) nptr; + return result * sign; +} + + +/* We always use _itoa instead of _itoa_word in ld.so since the former + also has to be present and it is never about speed when these + functions are used. */ +char * +_itoa (value, buflim, base, upper_case) + unsigned long long int value; + char *buflim; + unsigned int base; + int upper_case; +{ + extern const char INTUSE(_itoa_lower_digits)[] attribute_hidden; + + assert (! upper_case); + + do + *--buflim = INTUSE(_itoa_lower_digits)[value % base]; + while ((value /= base) != 0); + + return buflim; +} + + +/* The following is not a complete strsep implementation. It cannot + handle empty delimiter strings. But this isn't necessary for the + execution of ld.so. */ +#undef strsep +#undef __strsep +char * +__strsep (char **stringp, const char *delim) +{ + char *begin; + + assert (delim[0] != '\0'); + + begin = *stringp; + if (begin != NULL) + { + char *end = begin; + + while (*end != '\0' || (end = NULL)) + { + const char *dp = delim; + + do + if (*dp == *end) + break; + while (*++dp != '\0'); + + if (*dp != '\0') + { + *end++ = '\0'; + break; + } + + ++end; + } + + *stringp = end; + } + + return begin; +} +weak_alias (__strsep, strsep) +strong_alias (__strsep, __strsep_g) + +void +__attribute__ ((noreturn)) +__chk_fail (void) +{ + _exit (127); +} +rtld_hidden_def (__chk_fail) + +/* The '_itoa_lower_digits' variable in libc.so is able to handle bases + up to 36. We don't need this here. */ +const char INTUSE(_itoa_lower_digits)[16] attribute_hidden + = "0123456789abcdef"; diff --git a/libc/elf/dl-misc.c b/libc/elf/dl-misc.c new file mode 100644 index 000000000..6da1e2e4a --- /dev/null +++ b/libc/elf/dl-misc.c @@ -0,0 +1,325 @@ +/* Miscellaneous support functions for dynamic linker + 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 + 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 <fcntl.h> +#include <ldsodefs.h> +#include <limits.h> +#include <link.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sysdep.h> +#include <stdio-common/_itoa.h> +#include <bits/libc-lock.h> + +#ifndef MAP_ANON +/* This is the only dl-sysdep.c function that is actually needed at run-time + by _dl_map_object. */ + +int +_dl_sysdep_open_zero_fill (void) +{ + return __open ("/dev/zero", O_RDONLY); +} +#endif + +/* Read the whole contents of FILE into new mmap'd space with given + protections. *SIZEP gets the size of the file. On error MAP_FAILED + is returned. */ + +void * +internal_function +_dl_sysdep_read_whole_file (const char *file, size_t *sizep, int prot) +{ + void *result = MAP_FAILED; + struct stat64 st; + int fd = __open (file, O_RDONLY); + if (fd >= 0) + { + if (__fxstat64 (_STAT_VER, fd, &st) >= 0) + { + *sizep = st.st_size; + + /* No need to map the file if it is empty. */ + if (*sizep != 0) + /* Map a copy of the file contents. */ + result = __mmap (NULL, *sizep, prot, +#ifdef MAP_COPY + MAP_COPY +#else + MAP_PRIVATE +#endif +#ifdef MAP_FILE + | MAP_FILE +#endif + , fd, 0); + } + __close (fd); + } + return result; +} + + +/* Bare-bones printf implementation. This function only knows about + the formats and flags needed and can handle only up to 64 stripes in + the output. */ +static void +_dl_debug_vdprintf (int fd, int tag_p, const char *fmt, va_list arg) +{ +# define NIOVMAX 64 + struct iovec iov[NIOVMAX]; + int niov = 0; + pid_t pid = 0; + char pidbuf[12]; + + while (*fmt != '\0') + { + const char *startp = fmt; + + if (tag_p > 0) + { + /* Generate the tag line once. It consists of the PID and a + colon followed by a tab. */ + if (pid == 0) + { + char *p; + pid = __getpid (); + assert (pid >= 0 && sizeof (pid_t) <= 4); + p = _itoa (pid, &pidbuf[10], 10, 0); + while (p > pidbuf) + *--p = ' '; + pidbuf[10] = ':'; + pidbuf[11] = '\t'; + } + + /* Append to the output. */ + assert (niov < NIOVMAX); + iov[niov].iov_len = 12; + iov[niov++].iov_base = pidbuf; + + /* No more tags until we see the next newline. */ + tag_p = -1; + } + + /* Skip everything except % and \n (if tags are needed). */ + while (*fmt != '\0' && *fmt != '%' && (! tag_p || *fmt != '\n')) + ++fmt; + + /* Append constant string. */ + assert (niov < NIOVMAX); + if ((iov[niov].iov_len = fmt - startp) != 0) + iov[niov++].iov_base = (char *) startp; + + if (*fmt == '%') + { + /* It is a format specifier. */ + char fill = ' '; + int width = -1; + int prec = -1; +#if LONG_MAX != INT_MAX + int long_mod = 0; +#endif + + /* Recognize zero-digit fill flag. */ + if (*++fmt == '0') + { + fill = '0'; + ++fmt; + } + + /* See whether with comes from a parameter. Note that no other + way to specify the width is implemented. */ + if (*fmt == '*') + { + width = va_arg (arg, int); + ++fmt; + } + + /* Handle precision. */ + if (*fmt == '.' && fmt[1] == '*') + { + prec = va_arg (arg, int); + fmt += 2; + } + + /* Recognize the l modifier. It is only important on some + platforms where long and int have a different size. We + can use the same code for size_t. */ + if (*fmt == 'l' || *fmt == 'Z') + { +#if LONG_MAX != INT_MAX + long_mod = 1; +#endif + ++fmt; + } + + switch (*fmt) + { + /* Integer formatting. */ + case 'u': + case 'x': + { + /* We have to make a difference if long and int have a + different size. */ +#if LONG_MAX != INT_MAX + unsigned long int num = (long_mod + ? va_arg (arg, unsigned long int) + : va_arg (arg, unsigned int)); +#else + unsigned long int num = va_arg (arg, unsigned int); +#endif + /* We use alloca() to allocate the buffer with the most + pessimistic guess for the size. Using alloca() allows + having more than one integer formatting in a call. */ + char *buf = (char *) alloca (3 * sizeof (unsigned long int)); + char *endp = &buf[3 * sizeof (unsigned long int)]; + char *cp = _itoa (num, endp, *fmt == 'x' ? 16 : 10, 0); + + /* Pad to the width the user specified. */ + if (width != -1) + while (endp - cp < width) + *--cp = fill; + + iov[niov].iov_base = cp; + iov[niov].iov_len = endp - cp; + ++niov; + } + break; + + case 's': + /* Get the string argument. */ + iov[niov].iov_base = va_arg (arg, char *); + iov[niov].iov_len = strlen (iov[niov].iov_base); + if (prec != -1) + iov[niov].iov_len = MIN ((size_t) prec, iov[niov].iov_len); + ++niov; + break; + + case '%': + iov[niov].iov_base = (void *) fmt; + iov[niov].iov_len = 1; + ++niov; + break; + + default: + assert (! "invalid format specifier"); + } + ++fmt; + } + else if (*fmt == '\n') + { + /* See whether we have to print a single newline character. */ + if (fmt == startp) + { + iov[niov].iov_base = (char *) startp; + iov[niov++].iov_len = 1; + } + else + /* No, just add it to the rest of the string. */ + ++iov[niov - 1].iov_len; + + /* Next line, print a tag again. */ + tag_p = 1; + ++fmt; + } + } + + /* Finally write the result. */ +#ifdef HAVE_INLINED_SYSCALLS + INTERNAL_SYSCALL_DECL (err); + INTERNAL_SYSCALL (writev, err, 3, fd, &iov, niov); +#elif RTLD_PRIVATE_ERRNO + /* We have to take this lock just to be sure we don't clobber the private + errno when it's being used by another thread that cares about it. + Yet we must be sure not to try calling the lock functions before + the thread library is fully initialized. */ + if (__builtin_expect (INTUSE (_dl_starting_up), 0)) + __writev (fd, iov, niov); + else + { + __rtld_lock_lock_recursive (GL(dl_load_lock)); + __writev (fd, iov, niov); + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + } +#else + __writev (fd, iov, niov); +#endif +} + + +/* Write to debug file. */ +void +_dl_debug_printf (const char *fmt, ...) +{ + va_list arg; + + va_start (arg, fmt); + _dl_debug_vdprintf (GLRO(dl_debug_fd), 1, fmt, arg); + va_end (arg); +} + + +/* Write to debug file but don't start with a tag. */ +void +_dl_debug_printf_c (const char *fmt, ...) +{ + va_list arg; + + va_start (arg, fmt); + _dl_debug_vdprintf (GLRO(dl_debug_fd), -1, fmt, arg); + va_end (arg); +} + + +/* Write the given file descriptor. */ +void +_dl_dprintf (int fd, const char *fmt, ...) +{ + va_list arg; + + va_start (arg, fmt); + _dl_debug_vdprintf (fd, 0, fmt, arg); + va_end (arg); +} + + +/* Test whether given NAME matches any of the names of the given object. */ +int +internal_function +_dl_name_match_p (const char *name, const struct link_map *map) +{ + if (strcmp (name, map->l_name) == 0) + return 1; + + struct libname_list *runp = map->l_libname; + + while (runp != NULL) + if (strcmp (name, runp->name) == 0) + return 1; + else + runp = runp->next; + + return 0; +} diff --git a/libc/elf/dl-object.c b/libc/elf/dl-object.c new file mode 100644 index 000000000..86f7a8e4d --- /dev/null +++ b/libc/elf/dl-object.c @@ -0,0 +1,205 @@ +/* Storage management for the chain of loaded shared objects. + Copyright (C) 1995-2002, 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 <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <ldsodefs.h> + +#include <assert.h> + + +/* Allocate a `struct link_map' for a new object being loaded, + and enter it into the _dl_loaded list. */ + +struct link_map * +internal_function +_dl_new_object (char *realname, const char *libname, int type, + struct link_map *loader, int mode, Lmid_t nsid) +{ + struct link_map *l; + int idx; + 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) + audit_space + + sizeof (*newname) + libname_len, 1); + if (new == NULL) + return NULL; + + new->l_real = new; + 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; + + new->l_name = realname; + new->l_type = type; + new->l_loader = loader; +#if defined USE_TLS && NO_TLS_OFFSET != 0 + new->l_tls_offset = NO_TLS_OFFSET; +#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' + information. If we need more entries we will allocate a large + array dynamically. */ + new->l_scope = new->l_scope_mem; + new->l_scope_max = sizeof (new->l_scope_mem) / sizeof (new->l_scope_mem[0]); + + /* Counter for the scopes we have to handle. */ + idx = 0; + + if (GL(dl_ns)[nsid]._ns_loaded != NULL) + { + l = GL(dl_ns)[nsid]._ns_loaded; + while (l->l_next != NULL) + l = l->l_next; + new->l_prev = l; + /* new->l_next = NULL; Would be necessary but we use calloc. */ + l->l_next = new; + + /* Add the global scope. */ + new->l_scope[idx++] = &GL(dl_ns)[nsid]._ns_loaded->l_searchlist; + } + else + GL(dl_ns)[nsid]._ns_loaded = new; + ++GL(dl_ns)[nsid]._ns_nloaded; + ++GL(dl_load_adds); + + /* If we have no loader the new object acts as it. */ + if (loader == NULL) + loader = new; + else + /* Determine the local scope. */ + while (loader->l_loader != NULL) + loader = loader->l_loader; + + /* Insert the scope if it isn't the global scope we already added. */ + if (idx == 0 || &loader->l_searchlist != new->l_scope[0]) + { + if ((mode & RTLD_DEEPBIND) != 0 && idx != 0) + { + new->l_scope[1] = new->l_scope[0]; + idx = 0; + } + + new->l_scope[idx] = &loader->l_searchlist; + } + + new->l_local_scope[0] = &new->l_searchlist; + + /* Don't try to find the origin for the main map which has the name "". */ + if (realname[0] != '\0') + { + size_t realname_len = strlen (realname) + 1; + char *origin; + char *cp; + + if (realname[0] == '/') + { + /* It is an absolute path. Use it. But we have to make a + copy since we strip out the trailing slash. */ + cp = origin = (char *) malloc (realname_len); + if (origin == NULL) + { + origin = (char *) -1; + goto out; + } + } + else + { + size_t len = realname_len; + char *result = NULL; + + /* Get the current directory name. */ + origin = NULL; + do + { + char *new_origin; + + len += 128; + new_origin = (char *) realloc (origin, len); + if (new_origin == NULL) + /* We exit the loop. Note that result == NULL. */ + break; + origin = new_origin; + } + while ((result = __getcwd (origin, len - realname_len)) == NULL + && errno == ERANGE); + + if (result == NULL) + { + /* We were not able to determine the current directory. + Note that free(origin) is OK if origin == NULL. */ + free (origin); + origin = (char *) -1; + goto out; + } + + /* Find the end of the path and see whether we have to add a + slash. We could use rawmemchr but this need not be + fast. */ + cp = (strchr) (origin, '\0'); + if (cp[-1] != '/') + *cp++ = '/'; + } + + /* Add the real file name. */ + cp = __mempcpy (cp, realname, realname_len); + + /* Now remove the filename and the slash. Leave the slash if + the name is something like "/foo". */ + do + --cp; + while (*cp != '/'); + + if (cp == origin) + /* Keep the only slash which is the first character. */ + ++cp; + *cp = '\0'; + + out: + new->l_origin = origin; + } + + return new; +} diff --git a/libc/elf/dl-open.c b/libc/elf/dl-open.c new file mode 100644 index 000000000..cdbb6601d --- /dev/null +++ b/libc/elf/dl-open.c @@ -0,0 +1,646 @@ +/* Load a shared object at runtime, relocate it, and run its initializer. + 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 + 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 <dlfcn.h> +#include <errno.h> +#include <libintl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> /* Check whether MAP_COPY is defined. */ +#include <sys/param.h> +#include <bits/libc-lock.h> +#include <ldsodefs.h> +#include <bp-sym.h> +#include <caller.h> + +#include <dl-dst.h> + + +extern ElfW(Addr) _dl_sysdep_start (void **start_argptr, + void (*dl_main) (const ElfW(Phdr) *phdr, + ElfW(Word) phnum, + ElfW(Addr) *user_entry)); +weak_extern (BP_SYM (_dl_sysdep_start)) + +extern int __libc_multiple_libcs; /* Defined in init-first.c. */ + +/* Undefine the following for debugging. */ +/* #define SCOPE_DEBUG 1 */ +#ifdef SCOPE_DEBUG +static void show_scope (struct link_map *new); +#endif + +/* We must be carefull not to leave us in an inconsistent state. Thus we + catch any error and re-raise it after cleaning up. */ + +struct dl_open_args +{ + const char *file; + int mode; + /* This is the caller of the dlopen() function. */ + const void *caller_dlopen; + /* This is the caller if _dl_open(). */ + const void *caller_dl_open; + struct link_map *map; + /* Namespace ID. */ + Lmid_t nsid; + /* Original parameters to the program and the current environment. */ + int argc; + char **argv; + char **env; +}; + + +static int +add_to_global (struct link_map *new) +{ + struct link_map **new_global; + unsigned int to_add = 0; + unsigned int cnt; + + /* Count the objects we have to put in the global scope. */ + for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt) + if (new->l_searchlist.r_list[cnt]->l_global == 0) + ++to_add; + + /* The symbols of the new objects and its dependencies are to be + introduced into the global scope that will be used to resolve + references from other dynamically-loaded objects. + + The global scope is the searchlist in the main link map. We + extend this list if necessary. There is one problem though: + since this structure was allocated very early (before the libc + is loaded) the memory it uses is allocated by the malloc()-stub + in the ld.so. When we come here these functions are not used + anymore. Instead the malloc() implementation of the libc is + used. But this means the block from the main map cannot be used + 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) + { + /* 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; + new_global = (struct link_map **) + malloc (GL(dl_ns)[new->l_ns]._ns_global_scope_alloc + * sizeof (struct link_map *)); + if (new_global == NULL) + { + GL(dl_ns)[new->l_ns]._ns_global_scope_alloc = 0; + nomem: + _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 + * 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) + { + /* We have to extend the existing array of link maps in the + main map. */ + 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 *))); + 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; + } + + /* Now add the new entries. */ + for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt) + { + struct link_map *map = new->l_searchlist.r_list[cnt]; + + 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; + } + } + + return 0; +} + + +static void +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; + int lazy; + unsigned int i; +#ifdef USE_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|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 + the new object in, or when the file name has no path in which + case we need to look along the RUNPATH/RPATH of the caller. */ + const char *dst = strchr (file, '$'); + if (dst != NULL || args->nsid == __LM_ID_CALLER + || strchr (file, '/') == NULL) + { + const void *caller_dlopen = args->caller_dlopen; + + /* We have to find out from which object the caller is calling. + By default we assume this is the main application. */ + call_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + + 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) + { + /* 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; + } + + found_caller: + if (args->nsid == __LM_ID_CALLER) + { +#ifndef SHARED + /* In statically linked apps there might be no loaded object. */ + if (call_map == NULL) + args->nsid = LM_ID_BASE; + else +#endif + args->nsid = call_map->l_ns; + } + } + + assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); + + /* Maybe we have to expand a DST. */ + if (__builtin_expect (dst != NULL, 0)) + { + size_t len = strlen (file); + size_t required; + char *new_file; + + /* DSTs must not appear in SUID/SGID programs. */ + if (INTUSE(__libc_enable_secure)) + /* This is an error. */ + _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)); + + /* Get space for the new file name. */ + new_file = (char *) alloca (required + 1); + + /* Generate the new file name. */ + _dl_dst_substitute (call_map, file, new_file, 0); + + /* If the substitution failed don't try to load. */ + if (*new_file == '\0') + _dl_signal_error (0, "dlopen", NULL, + N_("empty dynamic string token substitution")); + + /* Now we have a new file name. */ + file = new_file; + + /* It does not matter whether call_map is set even if we + computed it only because of the DST. Since the path contains + a slash the value is not used. See dl-load.c. */ + } + + /* Load the named object. */ + 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. */ + if (new == NULL) + { + assert (mode & RTLD_NOLOAD); + return; + } + + if (__builtin_expect (mode & __RTLD_SPROF, 0)) + /* This happens only if we load a DSO for 'sprof'. */ + return; + + /* This object is directly loaded. */ + ++new->l_direct_opencount; + + /* It was already open. */ + if (__builtin_expect (new->l_searchlist.r_list != NULL, 0)) + { + /* Let the user know about the opencount. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) + _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); + + assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); + + return; + } + + /* Load that object's dependencies. */ + _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) _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; + while (l->l_next) + l = l->l_next; + while (1) + { + if (! l->l_real->l_relocated) + { +#ifdef SHARED + 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 + this is necessary or not by observing the `_dl_profile_map' + variable. If was NULL but is not NULL afterwars we must + start the profiling. */ + struct link_map *old_profile_map = GL(dl_profile_map); + + _dl_relocate_object (l, l->l_scope, 1, 1); + + if (old_profile_map == NULL && GL(dl_profile_map) != NULL) + { + /* We must prepare the profiling. */ + _dl_start_profile (); + + /* Prevent unloading the object. */ + GL(dl_profile_map)->l_flags_1 |= DF_1_NODELETE; + } + } + else +#endif + _dl_relocate_object (l, l->l_scope, lazy, 0); + } + + if (l == new) + break; + l = l->l_prev; + } + + /* 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) + { + 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) + { + ++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) + _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) + _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; + } +#if USE_TLS + /* 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 && __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. */ + _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) + /* Move the object in the global namespace. */ + if (add_to_global (new) != 0) + /* It failed. */ + return; + + /* Mark the object as not deletable if the RTLD_NODELETE flags was + passed. */ + if (__builtin_expect (mode & RTLD_NODELETE, 0)) + new->l_flags_1 |= DF_1_NODELETE; + +#ifndef SHARED + /* We must be the static _dl_open in libc.a. A static program that + has loaded a dynamic object now has competition. */ + __libc_multiple_libcs = 1; +#endif + + /* Let the user know about the opencount. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) + _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", + new->l_name, new->l_ns, new->l_direct_opencount); +} + + +void * +_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid, + int argc, char *argv[], char *env[]) +{ + if ((mode & RTLD_BINDING_MASK) == 0) + /* One of the flags must be set. */ + _dl_signal_error (EINVAL, file, NULL, N_("invalid mode for dlopen()")); + + /* Make sure we are alone. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + if (nsid == LM_ID_NEWLM) + { + /* Find a new namespace. */ + for (nsid = 1; nsid < DL_NNS; ++nsid) + if (GL(dl_ns)[nsid]._ns_loaded == NULL) + break; + + if (nsid == DL_NNS) + { + /* No more namespace available. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + _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. 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 + || 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; + 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. */ + _dl_unload_cache (); +#endif + + /* Release the lock. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + 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) + { +#ifdef USE_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. 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); + } + + /* Make a local copy of the error string so that we can release the + memory allocated for it. */ + len_errstring = strlen (errstring) + 1; + if (objname == errstring + len_errstring) + { + size_t total_len = len_errstring + strlen (objname) + 1; + local_errstring = alloca (total_len); + memcpy (local_errstring, errstring, total_len); + objname = local_errstring + len_errstring; + } + else + { + local_errstring = alloca (len_errstring); + memcpy (local_errstring, errstring, len_errstring); + } + + if (malloced) + free ((char *) errstring); + + assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT); + + /* Reraise the error. */ + _dl_signal_error (errcode, objname, NULL, local_errstring); + } + + assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT); + +#ifndef SHARED + DL_STATIC_INIT (args.map); +#endif + + return args.map; +} + + +#ifdef SCOPE_DEBUG +#include <unistd.h> + +static void +show_scope (struct link_map *new) +{ + int scope_cnt; + + for (scope_cnt = 0; new->l_scope[scope_cnt] != NULL; ++scope_cnt) + { + char numbuf[2]; + unsigned int cnt; + + numbuf[0] = '0' + scope_cnt; + numbuf[1] = '\0'; + _dl_printf ("scope %s:", numbuf); + + for (cnt = 0; cnt < new->l_scope[scope_cnt]->r_nlist; ++cnt) + if (*new->l_scope[scope_cnt]->r_list[cnt]->l_name) + _dl_printf (" %s", new->l_scope[scope_cnt]->r_list[cnt]->l_name); + else + _dl_printf (" <main>"); + + _dl_printf ("\n"); + } +} +#endif diff --git a/libc/elf/dl-origin.c b/libc/elf/dl-origin.c new file mode 100644 index 000000000..87619379b --- /dev/null +++ b/libc/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/libc/elf/dl-profile.c b/libc/elf/dl-profile.c new file mode 100644 index 000000000..41214c1b0 --- /dev/null +++ b/libc/elf/dl-profile.c @@ -0,0 +1,555 @@ +/* Profiling of shared libraries. + Copyright (C) 1997-2002, 2003, 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. + Based on the BSD mcount implementation. + + 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 <fcntl.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ldsodefs.h> +#include <sys/gmon.h> +#include <sys/gmon_out.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <atomic.h> + +/* The LD_PROFILE feature has to be implemented different to the + normal profiling using the gmon/ functions. The problem is that an + arbitrary amount of processes simulataneously can be run using + profiling and all write the results in the same file. To provide + this mechanism one could implement a complicated mechanism to merge + the content of two profiling runs or one could extend the file + format to allow more than one data set. For the second solution we + would have the problem that the file can grow in size beyond any + limit and both solutions have the problem that the concurrency of + writing the results is a big problem. + + Another much simpler method is to use mmap to map the same file in + all using programs and modify the data in the mmap'ed area and so + also automatically on the disk. Using the MAP_SHARED option of + mmap(2) this can be done without big problems in more than one + file. + + This approach is very different from the normal profiling. We have + to use the profiling data in exactly the way they are expected to + be written to disk. But the normal format used by gprof is not usable + to do this. It is optimized for size. It writes the tags as single + bytes but this means that the following 32/64 bit values are + unaligned. + + Therefore we use a new format. This will look like this + + 0 1 2 3 <- byte is 32 bit word + 0000 g m o n + 0004 *version* <- GMON_SHOBJ_VERSION + 0008 00 00 00 00 + 000c 00 00 00 00 + 0010 00 00 00 00 + + 0014 *tag* <- GMON_TAG_TIME_HIST + 0018 ?? ?? ?? ?? + ?? ?? ?? ?? <- 32/64 bit LowPC + 0018+A ?? ?? ?? ?? + ?? ?? ?? ?? <- 32/64 bit HighPC + 0018+2*A *histsize* + 001c+2*A *profrate* + 0020+2*A s e c o + 0024+2*A n d s \0 + 0028+2*A \0 \0 \0 \0 + 002c+2*A \0 \0 \0 + 002f+2*A s + + 0030+2*A ?? ?? ?? ?? <- Count data + ... ... + 0030+2*A+K ?? ?? ?? ?? + + 0030+2*A+K *tag* <- GMON_TAG_CG_ARC + 0034+2*A+K *lastused* + 0038+2*A+K ?? ?? ?? ?? + ?? ?? ?? ?? <- FromPC#1 + 0038+3*A+K ?? ?? ?? ?? + ?? ?? ?? ?? <- ToPC#1 + 0038+4*A+K ?? ?? ?? ?? <- Count#1 + ... ... ... + 0038+(2*(CN-1)+2)*A+(CN-1)*4+K ?? ?? ?? ?? + ?? ?? ?? ?? <- FromPC#CGN + 0038+(2*(CN-1)+3)*A+(CN-1)*4+K ?? ?? ?? ?? + ?? ?? ?? ?? <- ToPC#CGN + 0038+(2*CN+2)*A+(CN-1)*4+K ?? ?? ?? ?? <- Count#CGN + + We put (for now?) no basic block information in the file since this would + introduce rase conditions among all the processes who want to write them. + + `K' is the number of count entries which is computed as + + textsize / HISTFRACTION + + `CG' in the above table is the number of call graph arcs. Normally, + the table is sparse and the profiling code writes out only the those + entries which are really used in the program run. But since we must + not extend this table (the profiling file) we'll keep them all here. + So CN can be executed in advance as + + MINARCS <= textsize*(ARCDENSITY/100) <= MAXARCS + + Now the remaining question is: how to build the data structures we can + work with from this data. We need the from set and must associate the + froms with all the associated tos. We will do this by constructing this + data structures at the program start. To do this we'll simply visit all + entries in the call graph table and add it to the appropriate list. */ + +extern int __profile_frequency (void); +libc_hidden_proto (__profile_frequency) + +/* We define a special type to address the elements of the arc table. + This is basically the `gmon_cg_arc_record' format but it includes + the room for the tag and it uses real types. */ +struct here_cg_arc_record + { + uintptr_t from_pc; + uintptr_t self_pc; + uint32_t count; + } __attribute__ ((packed)); + +static struct here_cg_arc_record *data; + +/* Nonzero if profiling is under way. */ +static int running; + +/* This is the number of entry which have been incorporated in the toset. */ +static uint32_t narcs; +/* This is a pointer to the object representing the number of entries + currently in the mmaped file. At no point of time this has to be the + same as NARCS. If it is equal all entries from the file are in our + lists. */ +static volatile uint32_t *narcsp; + + +struct here_fromstruct + { + struct here_cg_arc_record volatile *here; + uint16_t link; + }; + +static volatile uint16_t *tos; + +static struct here_fromstruct *froms; +static uint32_t fromlimit; +static volatile uint32_t fromidx; + +static uintptr_t lowpc; +static size_t textsize; +static unsigned int log_hashfraction; + + + +/* Set up profiling data to profile object desribed by MAP. The output + file is found (or created) in OUTPUT_DIR. */ +void +internal_function +_dl_start_profile (void) +{ + char *filename; + int fd; + struct stat64 st; + const ElfW(Phdr) *ph; + ElfW(Addr) mapstart = ~((ElfW(Addr)) 0); + ElfW(Addr) mapend = 0; + struct gmon_hdr gmon_hdr; + struct gmon_hist_hdr hist_hdr; + char *hist, *cp; + size_t idx; + size_t tossize; + size_t fromssize; + uintptr_t highpc; + uint16_t *kcount; + size_t kcountsize; + struct gmon_hdr *addr = NULL; + off_t expected_size; + /* See profil(2) where this is described. */ + int s_scale; +#define SCALE_1_TO_1 0x10000L + const char *errstr = NULL; + + /* Compute the size of the sections which contain program code. */ + for (ph = GL(dl_profile_map)->l_phdr; + ph < &GL(dl_profile_map)->l_phdr[GL(dl_profile_map)->l_phnum]; ++ph) + if (ph->p_type == PT_LOAD && (ph->p_flags & PF_X)) + { + ElfW(Addr) start = (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1)); + ElfW(Addr) end = ((ph->p_vaddr + ph->p_memsz + GLRO(dl_pagesize) - 1) + & ~(GLRO(dl_pagesize) - 1)); + + if (start < mapstart) + mapstart = start; + if (end > mapend) + mapend = end; + } + + /* Now we can compute the size of the profiling data. This is done + with the same formulars as in `monstartup' (see gmon.c). */ + running = 0; + lowpc = ROUNDDOWN (mapstart + GL(dl_profile_map)->l_addr, + HISTFRACTION * sizeof (HISTCOUNTER)); + highpc = ROUNDUP (mapend + GL(dl_profile_map)->l_addr, + HISTFRACTION * sizeof (HISTCOUNTER)); + textsize = highpc - lowpc; + kcountsize = textsize / HISTFRACTION; + if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) + { + /* If HASHFRACTION is a power of two, mcount can use shifting + instead of integer division. Precompute shift amount. + + This is a constant but the compiler cannot compile the + expression away since the __ffs implementation is not known + to the compiler. Help the compiler by precomputing the + usual cases. */ + assert (HASHFRACTION == 2); + + if (sizeof (*froms) == 8) + log_hashfraction = 4; + else if (sizeof (*froms) == 16) + log_hashfraction = 5; + else + log_hashfraction = __ffs (HASHFRACTION * sizeof (*froms)) - 1; + } + else + log_hashfraction = -1; + tossize = textsize / HASHFRACTION; + fromlimit = textsize * ARCDENSITY / 100; + if (fromlimit < MINARCS) + fromlimit = MINARCS; + if (fromlimit > MAXARCS) + fromlimit = MAXARCS; + fromssize = fromlimit * sizeof (struct here_fromstruct); + + expected_size = (sizeof (struct gmon_hdr) + + 4 + sizeof (struct gmon_hist_hdr) + kcountsize + + 4 + 4 + fromssize * sizeof (struct here_cg_arc_record)); + + /* Create the gmon_hdr we expect or write. */ + memset (&gmon_hdr, '\0', sizeof (struct gmon_hdr)); + memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie)); + *(int32_t *) gmon_hdr.version = GMON_SHOBJ_VERSION; + + /* Create the hist_hdr we expect or write. */ + *(char **) hist_hdr.low_pc = (char *) mapstart; + *(char **) hist_hdr.high_pc = (char *) mapend; + *(int32_t *) hist_hdr.hist_size = kcountsize / sizeof (HISTCOUNTER); + *(int32_t *) hist_hdr.prof_rate = __profile_frequency (); + if (sizeof (hist_hdr.dimen) >= sizeof ("seconds")) + { + memcpy (hist_hdr.dimen, "seconds", sizeof ("seconds")); + memset (hist_hdr.dimen + sizeof ("seconds"), '\0', + sizeof (hist_hdr.dimen) - sizeof ("seconds")); + } + else + strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen)); + hist_hdr.dimen_abbrev = 's'; + + /* First determine the output name. We write in the directory + OUTPUT_DIR and the name is composed from the shared objects + soname (or the file name) and the ending ".profile". */ + filename = (char *) alloca (strlen (GLRO(dl_profile_output)) + 1 + + strlen (GLRO(dl_profile)) + sizeof ".profile"); + cp = __stpcpy (filename, GLRO(dl_profile_output)); + *cp++ = '/'; + __stpcpy (__stpcpy (cp, GLRO(dl_profile)), ".profile"); + +#ifdef O_NOFOLLOW +# define EXTRA_FLAGS | O_NOFOLLOW +#else +# define EXTRA_FLAGS +#endif + fd = __open (filename, O_RDWR | O_CREAT EXTRA_FLAGS, DEFFILEMODE); + if (fd == -1) + { + char buf[400]; + int errnum; + + /* We cannot write the profiling data so don't do anything. */ + errstr = "%s: cannot open file: %s\n"; + print_error: + errnum = errno; + if (fd != -1) + __close (fd); + _dl_error_printf (errstr, filename, + __strerror_r (errnum, buf, sizeof buf)); + return; + } + + if (__fxstat64 (_STAT_VER, fd, &st) < 0 || !S_ISREG (st.st_mode)) + { + /* Not stat'able or not a regular file => don't use it. */ + errstr = "%s: cannot stat file: %s\n"; + goto print_error; + } + + /* Test the size. If it does not match what we expect from the size + values in the map MAP we don't use it and warn the user. */ + if (st.st_size == 0) + { + /* We have to create the file. */ + char buf[GLRO(dl_pagesize)]; + + memset (buf, '\0', GLRO(dl_pagesize)); + + if (__lseek (fd, expected_size & ~(GLRO(dl_pagesize) - 1), SEEK_SET) == -1) + { + cannot_create: + errstr = "%s: cannot create file: %s\n"; + goto print_error; + } + + if (TEMP_FAILURE_RETRY (__libc_write (fd, buf, (expected_size + & (GLRO(dl_pagesize) + - 1)))) + < 0) + goto cannot_create; + } + else if (st.st_size != expected_size) + { + __close (fd); + wrong_format: + + if (addr != NULL) + __munmap ((void *) addr, expected_size); + + _dl_error_printf ("%s: file is no correct profile data file for `%s'\n", + filename, GLRO(dl_profile)); + return; + } + + addr = (struct gmon_hdr *) __mmap (NULL, expected_size, PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_FILE, fd, 0); + if (addr == (struct gmon_hdr *) MAP_FAILED) + { + errstr = "%s: cannot map file: %s\n"; + goto print_error; + } + + /* We don't need the file descriptor anymore. */ + __close (fd); + + /* Pointer to data after the header. */ + hist = (char *) (addr + 1); + kcount = (uint16_t *) ((char *) hist + sizeof (uint32_t) + + sizeof (struct gmon_hist_hdr)); + + /* Compute pointer to array of the arc information. */ + narcsp = (uint32_t *) ((char *) kcount + kcountsize + sizeof (uint32_t)); + data = (struct here_cg_arc_record *) ((char *) narcsp + sizeof (uint32_t)); + + if (st.st_size == 0) + { + /* Create the signature. */ + memcpy (addr, &gmon_hdr, sizeof (struct gmon_hdr)); + + *(uint32_t *) hist = GMON_TAG_TIME_HIST; + memcpy (hist + sizeof (uint32_t), &hist_hdr, + sizeof (struct gmon_hist_hdr)); + + narcsp[-1] = GMON_TAG_CG_ARC; + } + else + { + /* Test the signature in the file. */ + if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0 + || *(uint32_t *) hist != GMON_TAG_TIME_HIST + || memcmp (hist + sizeof (uint32_t), &hist_hdr, + sizeof (struct gmon_hist_hdr)) != 0 + || narcsp[-1] != GMON_TAG_CG_ARC) + goto wrong_format; + } + + /* Allocate memory for the froms data and the pointer to the tos records. */ + tos = (uint16_t *) calloc (tossize + fromssize, 1); + if (tos == NULL) + { + __munmap ((void *) addr, expected_size); + _dl_fatal_printf ("Out of memory while initializing profiler\n"); + /* NOTREACHED */ + } + + froms = (struct here_fromstruct *) ((char *) tos + tossize); + fromidx = 0; + + /* Now we have to process all the arc count entries. BTW: it is + not critical whether the *NARCSP value changes meanwhile. Before + we enter a new entry in to toset we will check that everything is + available in TOS. This happens in _dl_mcount. + + Loading the entries in reverse order should help to get the most + frequently used entries at the front of the list. */ + for (idx = narcs = MIN (*narcsp, fromlimit); idx > 0; ) + { + size_t to_index; + size_t newfromidx; + --idx; + to_index = (data[idx].self_pc / (HASHFRACTION * sizeof (*tos))); + newfromidx = fromidx++; + froms[newfromidx].here = &data[idx]; + froms[newfromidx].link = tos[to_index]; + tos[to_index] = newfromidx; + } + + /* Setup counting data. */ + if (kcountsize < highpc - lowpc) + { +#if 0 + s_scale = ((double) kcountsize / (highpc - lowpc)) * SCALE_1_TO_1; +#else + size_t range = highpc - lowpc; + size_t quot = range / kcountsize; + + if (quot >= SCALE_1_TO_1) + s_scale = 1; + else if (quot >= SCALE_1_TO_1 / 256) + s_scale = SCALE_1_TO_1 / quot; + else if (range > ULONG_MAX / 256) + s_scale = (SCALE_1_TO_1 * 256) / (range / (kcountsize / 256)); + else + s_scale = (SCALE_1_TO_1 * 256) / ((range * 256) / kcountsize); +#endif + } + else + s_scale = SCALE_1_TO_1; + + /* Start the profiler. */ + __profil ((void *) kcount, kcountsize, lowpc, s_scale); + + /* Turn on profiling. */ + running = 1; +} + + +void +_dl_mcount (ElfW(Addr) frompc, ElfW(Addr) selfpc) +{ + volatile uint16_t *topcindex; + size_t i, fromindex; + struct here_fromstruct *fromp; + + if (! running) + return; + + /* Compute relative addresses. The shared object can be loaded at + any address. The value of frompc could be anything. We cannot + restrict it in any way, just set to a fixed value (0) in case it + is outside the allowed range. These calls show up as calls from + <external> in the gprof output. */ + frompc -= lowpc; + if (frompc >= textsize) + frompc = 0; + selfpc -= lowpc; + if (selfpc >= textsize) + goto done; + + /* Getting here we now have to find out whether the location was + already used. If yes we are lucky and only have to increment a + counter (this also has to be atomic). If the entry is new things + are getting complicated... */ + + /* Avoid integer divide if possible. */ + if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) + i = selfpc >> log_hashfraction; + else + i = selfpc / (HASHFRACTION * sizeof (*tos)); + + topcindex = &tos[i]; + fromindex = *topcindex; + + if (fromindex == 0) + goto check_new_or_add; + + fromp = &froms[fromindex]; + + /* We have to look through the chain of arcs whether there is already + an entry for our arc. */ + while (fromp->here->from_pc != frompc) + { + if (fromp->link != 0) + do + fromp = &froms[fromp->link]; + while (fromp->link != 0 && fromp->here->from_pc != frompc); + + if (fromp->here->from_pc != frompc) + { + topcindex = &fromp->link; + + check_new_or_add: + /* Our entry is not among the entries we read so far from the + data file. Now see whether we have to update the list. */ + while (narcs != *narcsp && narcs < fromlimit) + { + size_t to_index; + size_t newfromidx; + to_index = (data[narcs].self_pc + / (HASHFRACTION * sizeof (*tos))); + newfromidx = atomic_exchange_and_add (&fromidx, 1) + 1; + froms[newfromidx].here = &data[narcs]; + froms[newfromidx].link = tos[to_index]; + tos[to_index] = newfromidx; + atomic_increment (&narcs); + } + + /* If we still have no entry stop searching and insert. */ + if (*topcindex == 0) + { + uint_fast32_t newarc = atomic_exchange_and_add (narcsp, 1); + + /* In rare cases it could happen that all entries in FROMS are + occupied. So we cannot count this anymore. */ + if (newarc >= fromlimit) + goto done; + + *topcindex = atomic_exchange_and_add (&fromidx, 1) + 1; + fromp = &froms[*topcindex]; + + fromp->here = &data[newarc]; + data[newarc].from_pc = frompc; + data[newarc].self_pc = selfpc; + data[newarc].count = 0; + fromp->link = 0; + atomic_increment (&narcs); + + break; + } + + fromp = &froms[*topcindex]; + } + else + /* Found in. */ + break; + } + + /* Increment the counter. */ + atomic_increment (&fromp->here->count); + + done: + ; +} +INTDEF(_dl_mcount) diff --git a/libc/elf/dl-profstub.c b/libc/elf/dl-profstub.c new file mode 100644 index 000000000..ad0f003ab --- /dev/null +++ b/libc/elf/dl-profstub.c @@ -0,0 +1,42 @@ +/* Helper definitions for profiling of shared libraries. + Copyright (C) 1998, 2000, 2002, 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. + + 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 <dlfcn.h> +#include <elf.h> +#include <ldsodefs.h> + +/* This is the map for the shared object we profile. It is defined here + only because we test for this value being NULL or not. */ + + +void +_dl_mcount_wrapper (void *selfpc) +{ + GLRO(dl_mcount) ((ElfW(Addr)) RETURN_ADDRESS (0), (ElfW(Addr)) selfpc); +} + + +void +_dl_mcount_wrapper_check (void *selfpc) +{ + if (GL(dl_profile_map) != NULL) + GLRO(dl_mcount) ((ElfW(Addr)) RETURN_ADDRESS (0), (ElfW(Addr)) selfpc); +} +libc_hidden_def (_dl_mcount_wrapper_check) diff --git a/libc/elf/dl-reloc.c b/libc/elf/dl-reloc.c new file mode 100644 index 000000000..117410e92 --- /dev/null +++ b/libc/elf/dl-reloc.c @@ -0,0 +1,371 @@ +/* Relocate a shared object and resolve its references to other loaded objects. + 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 + 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 <libintl.h> +#include <stdlib.h> +#include <unistd.h> +#include <ldsodefs.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/types.h> +#include "dynamic-link.h" + +/* Statistics function. */ +#ifdef SHARED +# define bump_num_cache_relocations() ++GL(dl_num_cache_relocations) +#else +# define bump_num_cache_relocations() ((void) 0) +#endif + + +#ifdef USE_TLS +/* We are trying to perform a static TLS relocation in MAP, but it was + dynamically loaded. This can only work if there is enough surplus in + the static TLS area already allocated for each running thread. If this + object's TLS segment is too big to fit, we fail. If it fits, + we set MAP->l_tls_offset and return. + This function intentionally does not return any value but signals error + directly, as static TLS should be rare and code handling it should + not be inlined as much as possible. */ +void +internal_function __attribute_noinline__ +_dl_allocate_static_tls (struct link_map *map) +{ + /* If the alignment requirements are too high fail. */ + if (map->l_tls_align > GL(dl_tls_static_align)) + { + fail: + _dl_signal_error (0, map->l_name, NULL, N_("\ +cannot allocate memory in static TLS block")); + } + +# if TLS_TCB_AT_TP + size_t freebytes; + size_t n; + size_t blsize; + + freebytes = GL(dl_tls_static_size) - GL(dl_tls_static_used) - TLS_TCB_SIZE; + + blsize = map->l_tls_blocksize + map->l_tls_firstbyte_offset; + if (freebytes < blsize) + goto fail; + + n = (freebytes - blsize) / map->l_tls_align; + + 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; + + 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. */ + + if (check > GL(dl_tls_static_size)) + goto fail; + + map->l_tls_offset = offset; + GL(dl_tls_static_used) = used; +# else +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" +# endif + + /* 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; +} + +/* Initialize static TLS area and DTV for current (only) thread. + libpthread implementations should provide their own hook + to handle all threads. */ +void +_dl_nothread_init_static_tls (struct link_map *map) +{ +# if TLS_TCB_AT_TP + void *dest = (char *) THREAD_SELF - map->l_tls_offset; +# elif TLS_DTV_AT_TP + void *dest = (char *) THREAD_SELF + map->l_tls_offset + TLS_PRE_TCB_SIZE; +# else +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" +# endif + + /* Fill in the DTV slot so that a later LD/GD access will find it. */ + 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), + '\0', map->l_tls_blocksize - map->l_tls_initimage_size); +} +#endif + + +void +_dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], + int lazy, int consider_profiling) +{ + struct textrels + { + caddr_t start; + size_t len; + int prot; + struct textrels *next; + } *textrels = NULL; + /* 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; + + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_RELOC, 0)) + _dl_debug_printf ("\nrelocation processing: %s%s\n", + l->l_name[0] ? l->l_name : rtld_progname, + lazy ? " (lazy)" : ""); + + /* DT_TEXTREL is now in level 2 and might phase out at some time. + But we rewrite the DT_FLAGS entry to a DT_TEXTREL entry to make + testing easier and therefore it will be available at all time. */ + if (__builtin_expect (l->l_info[DT_TEXTREL] != NULL, 0)) + { + /* Bletch. We must make read-only segments writable + long enough to relocate them. */ + const ElfW(Phdr) *ph; + for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph) + if (ph->p_type == PT_LOAD && (ph->p_flags & PF_W) == 0) + { + struct textrels *newp; + + newp = (struct textrels *) alloca (sizeof (*newp)); + newp->len = (((ph->p_vaddr + ph->p_memsz + GLRO(dl_pagesize) - 1) + & ~(GLRO(dl_pagesize) - 1)) + - (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1))); + newp->start = ((ph->p_vaddr & ~(GLRO(dl_pagesize) - 1)) + + (caddr_t) l->l_addr); + + if (__mprotect (newp->start, newp->len, PROT_READ|PROT_WRITE) < 0) + { + errstring = N_("cannot make segment writable for relocation"); + call_error: + _dl_signal_error (errno, l->l_name, NULL, errstring); + } + +#if (PF_R | PF_W | PF_X) == 7 && (PROT_READ | PROT_WRITE | PROT_EXEC) == 7 + newp->prot = (PF_TO_PROT + >> ((ph->p_flags & (PF_R | PF_W | PF_X)) * 4)) & 0xf; +#else + newp->prot = 0; + if (ph->p_flags & PF_R) + newp->prot |= PROT_READ; + if (ph->p_flags & PF_W) + newp->prot |= PROT_WRITE; + if (ph->p_flags & PF_X) + newp->prot |= PROT_EXEC; +#endif + newp->next = textrels; + textrels = newp; + } + } + + { + /* Do the actual relocation of the object's GOT and other data. */ + + /* String table object symbols. */ + const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); + + /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code. */ +#define RESOLVE_MAP(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) + + /* 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 + permit dynamic loading of objects that might use such relocs, we + have to check whether each use is actually doable. If the object + whose TLS segment the reference resolves to was allocated space in + the static TLS block at startup, then it's ok. Otherwise, we make + an attempt to allocate it in surplus space on the fly. If that + can't be done, we fall back to the error that DF_STATIC_TLS is + intended to produce. */ +#define CHECK_STATIC_TLS(map, sym_map) \ + do { \ + if (__builtin_expect ((sym_map)->l_tls_offset == NO_TLS_OFFSET, 0)) \ + _dl_allocate_static_tls (sym_map); \ + } while (0) + +#include "dynamic-link.h" + + ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling); + +#ifndef PROF + if (__builtin_expect (consider_profiling, 0)) + { + /* Allocate the array which will contain the already found + relocations. If the shared object lacks a PLT (for example + if it only contains lead function) the l_info[DT_PLTRELSZ] + will be NULL. */ + if (l->l_info[DT_PLTRELSZ] == NULL) + { + 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 = 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: out of memory to store relocation results for %s\n"); + goto fatal; + } + } +#endif + } + + /* Mark the object so we know this work has been done. */ + l->l_relocated = 1; + + /* Undo the segment protection changes. */ + while (__builtin_expect (textrels != NULL, 0)) + { + if (__mprotect (textrels->start, textrels->len, textrels->prot) < 0) + { + errstring = N_("cannot restore segment prot after reloc"); + goto call_error; + } + + textrels = textrels->next; + } + + /* In case we can protect the data now that the relocations are + done, do it. */ + if (l->l_relro_size != 0) + _dl_protect_relro (l); +} + + +void internal_function +_dl_protect_relro (struct link_map *l) +{ + ElfW(Addr) start = ((l->l_addr + l->l_relro_addr) + & ~(GLRO(dl_pagesize) - 1)); + ElfW(Addr) end = ((l->l_addr + l->l_relro_addr + l->l_relro_size) + & ~(GLRO(dl_pagesize) - 1)); + + if (start != end + && __mprotect ((void *) start, end - start, PROT_READ) < 0) + { + static const char errstring[] = N_("\ +cannot apply additional memory protection after relocation"); + _dl_signal_error (errno, l->l_name, NULL, errstring); + } +} + +void +internal_function __attribute_noinline__ +_dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt) +{ + extern const char INTUSE(_itoa_lower_digits)[] attribute_hidden; +#define DIGIT(b) INTUSE(_itoa_lower_digits)[(b) & 0xf]; + + /* XXX We cannot translate these messages. */ + static const char msg[2][32 +#if __ELF_NATIVE_CLASS == 64 + + 6 +#endif + ] = { "unexpected reloc type 0x", + "unexpected PLT reloc type 0x" }; + char msgbuf[sizeof (msg[0])]; + char *cp; + + cp = __stpcpy (msgbuf, msg[plt]); +#if __ELF_NATIVE_CLASS == 64 + if (__builtin_expect(type > 0xff, 0)) + { + *cp++ = DIGIT (type >> 28); + *cp++ = DIGIT (type >> 24); + *cp++ = DIGIT (type >> 20); + *cp++ = DIGIT (type >> 16); + *cp++ = DIGIT (type >> 12); + *cp++ = DIGIT (type >> 8); + } +#endif + *cp++ = DIGIT (type >> 4); + *cp++ = DIGIT (type); + *cp = '\0'; + + _dl_signal_error (0, map->l_name, NULL, msgbuf); +} diff --git a/libc/elf/dl-runtime.c b/libc/elf/dl-runtime.c new file mode 100644 index 000000000..f92cbe26b --- /dev/null +++ b/libc/elf/dl-runtime.c @@ -0,0 +1,415 @@ +/* On-demand PLT fixup for shared objects. + Copyright (C) 1995-2002,2003,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. + + 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. */ + +#define IN_DL_RUNTIME 1 /* This can be tested in dl-machine.h. */ + +#include <alloca.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/param.h> +#include <ldsodefs.h> +#include "dynamic-link.h" + +#if (!defined ELF_MACHINE_NO_RELA && !defined ELF_MACHINE_PLT_REL) \ + || ELF_MACHINE_NO_REL +# define PLTREL ElfW(Rela) +#else +# define PLTREL ElfW(Rel) +#endif + +#ifndef VERSYMIDX +# define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym)) +#endif + +/* The fixup functions might have need special attributes. If none + are provided define the macro as empty. */ +#ifndef ARCH_FIXUP_ATTRIBUTE +# define ARCH_FIXUP_ATTRIBUTE +#endif + + +/* This function is called through a special trampoline from the PLT the + first time each PLT entry is called. We must perform the relocation + specified in the PLT of the given shared object, and return the resolved + function address to the trampoline, which will restart the original call + to that address. Future calls will bounce directly from the PLT to the + function. */ + +#ifndef ELF_MACHINE_NO_PLT +DL_FIXUP_VALUE_TYPE +__attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE +_dl_fixup ( +# ifdef 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) +{ + 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 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)]; + void *const rel_addr = (void *)(l->l_addr + reloc->r_offset); + lookup_t result; + 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); + + /* Look up the target symbol. If the normal lookup rules are not + used don't look in the global scope. */ + if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) + { + const struct r_found_version *version = NULL; + + if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) + { + const ElfW(Half) *vernum = + (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); + ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff; + version = &l->l_versions[ndx]; + if (version->hash == 0) + version = NULL; + } + + result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, + l->l_scope, version, ELF_RTYPE_CLASS_PLT, + DL_LOOKUP_ADD_DEPENDENCY, NULL); + + /* Currently result contains the base load address (or link map) + of the object that defines sym. Now add in the symbol + offset. */ + 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 = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value); + result = l; + } + + /* And now perhaps the relocation addend. */ + value = elf_machine_plt_value (l, reloc, value); + + /* Finally, fix up the plt itself. */ + if (__builtin_expect (GLRO(dl_bind_not), 0)) + return value; + + return elf_machine_fixup_plt (l, result, reloc, rel_addr, value); +} +#endif + +#if !defined PROF && !defined ELF_MACHINE_NO_PLT && !__BOUNDED_POINTERS__ + +DL_FIXUP_VALUE_TYPE +__attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE +_dl_profile_fixup ( +#ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS + ELF_MACHINE_RUNTIME_FIXUP_ARGS, +#endif + 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); + + /* This is the address in the array where we store the result of previous + relocations. */ + struct reloc_result *reloc_result + = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)]; + DL_FIXUP_VALUE_TYPE *resultp = &reloc_result->addr; + + 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 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) *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) (refsym->st_other), 0) == 0) + { + const struct r_found_version *version = NULL; + + if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) + { + const ElfW(Half) *vernum = + (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); + ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff; + version = &l->l_versions[ndx]; + if (version->hash == 0) + version = NULL; + } + + result = _dl_lookup_symbol_x (strtab + refsym->st_name, l, &defsym, + l->l_scope, version, + ELF_RTYPE_CLASS_PLT, + DL_LOOKUP_ADD_DEPENDENCY, NULL); + + /* Currently result contains the base load address (or link map) + of the object that defines sym. Now add in the symbol + offset. */ + 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 = DL_FIXUP_MAKE_VALUE (l, l->l_addr + refsym->st_value); + result = l; + } + /* 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; + } + + /* 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; +} + +#endif /* PROF && ELF_MACHINE_NO_PLT */ + + +#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); + } + + afct = afct->next; + } +#endif +} diff --git a/libc/elf/dl-sbrk.c b/libc/elf/dl-sbrk.c new file mode 100644 index 000000000..4713a9269 --- /dev/null +++ b/libc/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/libc/elf/dl-support.c b/libc/elf/dl-support.c new file mode 100644 index 000000000..c3b6350ed --- /dev/null +++ b/libc/elf/dl-support.c @@ -0,0 +1,313 @@ +/* Support for dynamic linking code in static libc. + Copyright (C) 1996-2002, 2003, 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. + + 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 defines some things that for the dynamic linker are defined in + rtld.c and dl-sysdep.c in ways appropriate to bootstrap dynamic linking. */ + +#include <errno.h> +#include <libintl.h> +#include <stdlib.h> +#include <unistd.h> +#include <ldsodefs.h> +#include <dl-machine.h> +#include <bits/libc-lock.h> +#include <dl-cache.h> +#include <dl-librecon.h> +#include <unsecvars.h> +#include <hp-timing.h> + +extern char *__progname; +char **_dl_argv = &__progname; /* This is checked for some error messages. */ + +/* Name of the architecture. */ +const char *_dl_platform; +size_t _dl_platformlen; + +int _dl_debug_mask; +int _dl_lazy; +ElfW(Addr) _dl_use_load_bias = -2; +int _dl_dynamic_weak; + +/* If nonzero print warnings about problematic situations. */ +int _dl_verbose; + +/* We never do profiling. */ +const char *_dl_profile; +const char *_dl_profile_output; + +/* Names of shared object for which the RUNPATHs and RPATHs should be + ignored. */ +const char *_dl_inhibit_rpath; + +/* The map for the object we will profile. */ +struct link_map *_dl_profile_map; + +/* This is the address of the last stack address ever used. */ +void *__libc_stack_end; + +/* Path where the binary is found. */ +const char *_dl_origin_path; + +/* Nonzero if runtime lookup should not update the .got/.plt. */ +int _dl_bind_not; + +/* Namespace information. */ +struct link_namespaces _dl_ns[DL_NNS]; + +/* Incremented whenever something may have been added to dl_loaded. */ +unsigned long long _dl_load_adds; + +/* Fake scope. In dynamically linked binaries this is the scope of the + main application but here we don't have something like this. So + create a fake scope containing nothing. */ +struct r_scope_elem _dl_initial_searchlist; + +#ifndef HAVE_INLINED_SYSCALLS +/* Nonzero during startup. */ +int _dl_starting_up = 1; +#endif + +/* Get architecture specific initializer. */ +#include <dl-procinfo.c> + +/* We expect less than a second for relocation. */ +#ifdef HP_SMALL_TIMING_AVAIL +# undef HP_TIMING_AVAIL +# define HP_TIMING_AVAIL HP_SMALL_TIMING_AVAIL +#endif + +/* Initial value of the CPU clock. */ +#ifndef HP_TIMING_NONAVAIL +hp_timing_t _dl_cpuclock_offset; +#endif + +#ifdef USE_TLS +void (*_dl_init_static_tls) (struct link_map *) = &_dl_nothread_init_static_tls; +#endif + +size_t _dl_pagesize; + +unsigned int _dl_osversion; + +/* All known directories in sorted order. */ +struct r_search_path_elem *_dl_all_dirs; + +/* All directories after startup. */ +struct r_search_path_elem *_dl_init_all_dirs; + +/* The object to be initialized first. */ +struct link_map *_dl_initfirst; + +/* Descriptor to write debug messages to. */ +int _dl_debug_fd = STDERR_FILENO; + +int _dl_correct_cache_id = _DL_CACHE_DEFAULT_ID; + +ElfW(Phdr) *_dl_phdr; +size_t _dl_phnum; +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; + +/* If loading a shared object requires that we make the stack executable + when it was not, we do it by calling this function. + It returns an errno code or zero on success. */ +int (*_dl_make_stack_executable_hook) (void **) internal_function + = _dl_make_stack_executable; + + +#ifdef NEED_DL_SYSINFO +/* Needed for improved syscall handling on at least x86/Linux. */ +uintptr_t _dl_sysinfo = DL_SYSINFO_DEFAULT; +#endif +#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO +/* Address of the ELF headers in the vsyscall page. */ +const ElfW(Ehdr) *_dl_sysinfo_dso; +#endif + +/* During the program run we must not modify the global data of + loaded shared object simultanously in two threads. Therefore we + protect `_dl_open' and `_dl_close' in dl-close.c. + + This must be a recursive lock since the initializer function of + the loaded object might as well require a call to this function. + At this time it is not anymore a problem to modify the tables. */ +__rtld_lock_define_initialized_recursive (, _dl_load_lock) + + +#ifdef HAVE_AUX_VECTOR +int _dl_clktck; + +void +internal_function +_dl_aux_init (ElfW(auxv_t) *av) +{ + int seen = 0; + uid_t uid = 0; + gid_t gid = 0; + + for (; av->a_type != AT_NULL; ++av) + switch (av->a_type) + { + case AT_PAGESZ: + GLRO(dl_pagesize) = av->a_un.a_val; + break; + case AT_CLKTCK: + GLRO(dl_clktck) = av->a_un.a_val; + break; + case AT_PHDR: + 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) = (unsigned long int) av->a_un.a_val; + break; +#ifdef NEED_DL_SYSINFO + case AT_SYSINFO: + GL(dl_sysinfo) = av->a_un.a_val; + break; +#endif +#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO + case AT_SYSINFO_EHDR: + GL(dl_sysinfo_dso) = (void *) av->a_un.a_val; + break; +#endif + case AT_UID: + uid ^= av->a_un.a_val; + seen |= 1; + break; + case AT_EUID: + uid ^= av->a_un.a_val; + seen |= 2; + break; + case AT_GID: + gid ^= av->a_un.a_val; + seen |= 4; + break; + case AT_EGID: + gid ^= av->a_un.a_val; + seen |= 8; + break; + case AT_SECURE: + seen = -1; + __libc_enable_secure = av->a_un.a_val; + __libc_enable_secure_decided = 1; + break; + } + if (seen == 0xf) + { + __libc_enable_secure = uid != 0 || gid != 0; + __libc_enable_secure_decided = 1; + } +} +#endif + + +void +internal_function +_dl_non_dynamic_init (void) +{ + if (HP_TIMING_AVAIL) + HP_TIMING_NOW (_dl_cpuclock_offset); + + if (!_dl_pagesize) + _dl_pagesize = __getpagesize (); + + _dl_verbose = *(getenv ("LD_WARN") ?: "") == '\0' ? 0 : 1; + + /* Initialize the data structures for the search paths for shared + objects. */ + _dl_init_paths (getenv ("LD_LIBRARY_PATH")); + + _dl_lazy = *(getenv ("LD_BIND_NOW") ?: "") == '\0'; + + _dl_bind_not = *(getenv ("LD_BIND_NOT") ?: "") != '\0'; + + _dl_dynamic_weak = *(getenv ("LD_DYNAMIC_WEAK") ?: "") == '\0'; + + _dl_profile_output = getenv ("LD_PROFILE_OUTPUT"); + if (_dl_profile_output == NULL || _dl_profile_output[0] == '\0') + _dl_profile_output + = &"/var/tmp\0/var/profile"[__libc_enable_secure ? 9 : 0]; + + if (__libc_enable_secure) + { + static const char unsecure_envvars[] = + UNSECURE_ENVVARS +#ifdef EXTRA_UNSECURE_ENVVARS + EXTRA_UNSECURE_ENVVARS +#endif + ; + const char *cp = unsecure_envvars; + + while (cp < unsecure_envvars + sizeof (unsecure_envvars)) + { + __unsetenv (cp); + cp = (const char *) __rawmemchr (cp, '\0') + 1; + } + + if (__access ("/etc/suid-debug", F_OK) != 0) + __unsetenv ("MALLOC_CHECK_"); + } + +#ifdef DL_PLATFORM_INIT + DL_PLATFORM_INIT; +#endif + +#ifdef DL_OSVERSION_INIT + DL_OSVERSION_INIT; +#endif + + /* Now determine the length of the platform string. */ + if (_dl_platform != NULL) + _dl_platformlen = strlen (_dl_platform); + + /* Scan for a program header telling us the stack is nonexecutable. */ + if (_dl_phdr != NULL) + for (uint_fast16_t i = 0; i < _dl_phnum; ++i) + if (_dl_phdr[i].p_type == PT_GNU_STACK) + { + _dl_stack_flags = _dl_phdr[i].p_flags; + break; + } +} + + +const struct r_strlenpair * +internal_function +_dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz, + size_t *max_capstrlen) +{ + static struct r_strlenpair result; + static char buf[1]; + + result.str = buf; /* Does not really matter. */ + result.len = 0; + + *sz = 1; + return &result; +} + + +#ifdef DL_SYSINFO_IMPLEMENTATION +DL_SYSINFO_IMPLEMENTATION +#endif diff --git a/libc/elf/dl-sym.c b/libc/elf/dl-sym.c new file mode 100644 index 000000000..d2b0ec0da --- /dev/null +++ b/libc/elf/dl-sym.c @@ -0,0 +1,210 @@ +/* Look up a symbol in a shared object loaded by `dlopen'. + Copyright (C) 1999,2000,2001,2002,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 <stddef.h> +#include <setjmp.h> +#include <libintl.h> + +#include <dlfcn.h> +#include <ldsodefs.h> +#include <dl-hash.h> +#ifdef USE_TLS +# include <dl-tls.h> +#endif + + +#if defined USE_TLS && defined SHARED +/* Systems which do not have tls_index also probably have to define + DONT_USE_TLS_INDEX. */ + +# ifndef __TLS_GET_ADDR +# define __TLS_GET_ADDR __tls_get_addr +# endif + +/* Return the symbol address given the map of the module it is in and + the symbol record. This is used in dl-sym.c. */ +static void * +internal_function +_dl_tls_symaddr (struct link_map *map, const ElfW(Sym) *ref) +{ +# ifndef DONT_USE_TLS_INDEX + tls_index tmp = + { + .ti_module = map->l_tls_modid, + .ti_offset = ref->st_value + }; + + return __TLS_GET_ADDR (&tmp); +# else + return __TLS_GET_ADDR (map->l_tls_modid, ref->st_value); +# endif +} +#endif + + +static void * +internal_function +do_sym (void *handle, const char *name, void *who, + struct r_found_version *vers, int flags) +{ + const ElfW(Sym) *ref = NULL; + lookup_t result; + ElfW(Addr) caller = (ElfW(Addr)) who; + + /* If the address is not recognized the call comes from the main + program (we hope). */ + struct link_map *match = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + + /* Find the highest-addressed object that CALLER is not below. */ + 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) + { + /* 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); + else if (handle == RTLD_NEXT) + { + if (__builtin_expect (match == GL(dl_ns)[LM_ID_BASE]._ns_loaded, 0)) + { + if (match == NULL + || caller < match->l_map_start + || caller >= match->l_map_end) + GLRO(dl_signal_error) (0, NULL, NULL, N_("\ +RTLD_NEXT used in code not dynamically loaded")); + } + + struct link_map *l = match; + while (l->l_loader != NULL) + l = l->l_loader; + + result = GLRO(dl_lookup_symbol_x) (name, match, &ref, l->l_local_scope, + vers, 0, 0, match); + } + else + { + /* Search the scope of the given object. */ + struct link_map *map = handle; + result = GLRO(dl_lookup_symbol_x) (name, map, &ref, map->l_local_scope, + vers, 0, flags, NULL); + } + + 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. */ + 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 value; + } + + return NULL; +} + + +void * +internal_function +_dl_vsym (void *handle, const char *name, const char *version, void *who) +{ + struct r_found_version vers; + + /* Compute hash value to the version string. */ + vers.name = version; + vers.hidden = 1; + vers.hash = _dl_elf_hash (version); + /* We don't have a specific file where the symbol can be found. */ + vers.filename = NULL; + + return do_sym (handle, name, who, &vers, 0); +} + + +void * +internal_function +_dl_sym (void *handle, const char *name, void *who) +{ + return do_sym (handle, name, who, NULL, DL_LOOKUP_RETURN_NEWEST); +} diff --git a/libc/elf/dl-symaddr.c b/libc/elf/dl-symaddr.c new file mode 100644 index 000000000..3c850ca83 --- /dev/null +++ b/libc/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/libc/elf/dl-sysdep.c b/libc/elf/dl-sysdep.c new file mode 100644 index 000000000..985e2b8f7 --- /dev/null +++ b/libc/elf/dl-sysdep.c @@ -0,0 +1,590 @@ +/* Operating system support for run-time dynamic linker. Generic Unix version. + Copyright (C) 1995-1998, 2000-2003, 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. + + 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 = (1UL << (cnt - 2)) * (temp[0].len + temp[cnt - 1].len + 2); + for (n = 1; n + 1 < cnt; ++n) + total += (1UL << (cnt - 3)) * (temp[n].len + 1); + } + + /* 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/libc/elf/dl-tls.c b/libc/elf/dl-tls.c new file mode 100644 index 000000000..a0f4f77ff --- /dev/null +++ b/libc/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/libc/elf/dl-trampoline.c b/libc/elf/dl-trampoline.c new file mode 100644 index 000000000..3ca89f387 --- /dev/null +++ b/libc/elf/dl-trampoline.c @@ -0,0 +1 @@ +#error "Architecture specific PLT trampolines must be defined." diff --git a/libc/elf/dl-tsd.c b/libc/elf/dl-tsd.c new file mode 100644 index 000000000..f01f8d652 --- /dev/null +++ b/libc/elf/dl-tsd.c @@ -0,0 +1,58 @@ +/* Thread-local data used by error handling for runtime dynamic linker. + 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 + 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. */ + +#ifdef _LIBC_REENTRANT + +# include <ldsodefs.h> +# include <tls.h> + +# ifndef SHARED + +/* _dl_error_catch_tsd points to this for the single-threaded case. + It's reset by the thread library for multithreaded programs + if we're not using __thread. */ +void ** __attribute__ ((const)) +_dl_initial_error_catch_tsd (void) +{ +# if USE___THREAD + static __thread void *data; +# else + static void *data; +# endif + return &data; +} +void **(*_dl_error_catch_tsd) (void) __attribute__ ((const)) + = &_dl_initial_error_catch_tsd; + +# elif USE___THREAD + +/* libpthread sets _dl_error_catch_tsd to point to this function. + We define it here instead of in libpthread so that it doesn't + need to have a TLS segment of its own just for this one pointer. */ + +void ** __attribute__ ((const)) +__libc_dl_error_tsd (void) +{ + static __thread void *data attribute_tls_model_ie; + return &data; +} + +# endif /* SHARED */ + +#endif /* _LIBC_REENTRANT */ diff --git a/libc/elf/dl-version.c b/libc/elf/dl-version.c new file mode 100644 index 000000000..9e881162a --- /dev/null +++ b/libc/elf/dl-version.c @@ -0,0 +1,392 @@ +/* Handle symbol and library versioning. + 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. + + 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 <elf.h> +#include <errno.h> +#include <libintl.h> +#include <stdlib.h> +#include <string.h> +#include <ldsodefs.h> +#include <stdio-common/_itoa.h> + +#include <assert.h> + + +#ifndef VERSYMIDX +# define VERSYMIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (tag)) +#endif + + +#define make_string(string, rest...) \ + ({ \ + const char *all[] = { string, ## rest }; \ + size_t len, cnt; \ + char *result, *cp; \ + \ + len = 1; \ + for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ + len += strlen (all[cnt]); \ + \ + cp = result = alloca (len); \ + for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ + cp = __stpcpy (cp, all[cnt]); \ + \ + result; \ + }) + + +static inline struct link_map * +__attribute ((always_inline)) +find_needed (const char *name, struct link_map *map) +{ + struct link_map *tmap; + unsigned int n; + + for (tmap = GL(dl_ns)[map->l_ns]._ns_loaded; tmap != NULL; + tmap = tmap->l_next) + if (_dl_name_match_p (name, tmap)) + return tmap; + + /* The required object is not in the global scope, look to see if it is + a dependency of the current object. */ + for (n = 0; n < map->l_searchlist.r_nlist; n++) + if (_dl_name_match_p (name, map->l_searchlist.r_list[n])) + return map->l_searchlist.r_list[n]; + + /* Should never happen. */ + return NULL; +} + + +static int +internal_function +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]); + ElfW(Addr) def_offset; + ElfW(Verdef) *def; + /* Initialize to make the compiler happy. */ + const char *errstring = NULL; + int result = 0; + + /* 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 [%lu] required by file %s [%lu]\n", + string, map->l_name[0] ? map->l_name : rtld_progname, + map->l_ns, name, ns); + + if (__builtin_expect (map->l_info[VERSYMIDX (DT_VERDEF)] == NULL, 0)) + { + /* The file has no symbol versioning. I.e., the dependent + object was linked against another version of this file. We + only print a message if verbose output is requested. */ + if (verbose) + { + /* XXX We cannot translate the messages. */ + errstring = make_string ("\ +no version information available (required by ", name, ")"); + goto call_cerror; + } + return 0; + } + + def_offset = map->l_info[VERSYMIDX (DT_VERDEF)]->d_un.d_ptr; + assert (def_offset != 0); + + def = (ElfW(Verdef) *) ((char *) map->l_addr + def_offset); + while (1) + { + /* Currently the version number of the definition entry is 1. + Make sure all we see is this version. */ + if (__builtin_expect (def->vd_version, 1) != 1) + { + char buf[20]; + buf[sizeof (buf) - 1] = '\0'; + /* XXX We cannot translate the message. */ + errstring = make_string ("unsupported version ", + _itoa (def->vd_version, + &buf[sizeof (buf) - 1], 10, 0), + " of Verdef record"); + result = 1; + goto call_cerror; + } + + /* Compare the hash values. */ + if (hash == def->vd_hash) + { + ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) def + def->vd_aux); + + /* To be safe, compare the string as well. */ + if (__builtin_expect (strcmp (string, strtab + aux->vda_name), 0) + == 0) + /* Bingo! */ + return 0; + } + + /* If no more definitions we failed to find what we want. */ + if (def->vd_next == 0) + break; + + /* Next definition. */ + def = (ElfW(Verdef) *) ((char *) def + def->vd_next); + } + + /* Symbol not found. If it was a weak reference it is not fatal. */ + if (__builtin_expect (weak, 1)) + { + if (verbose) + { + /* XXX We cannot translate the message. */ + errstring = make_string ("weak version `", string, + "' not found (required by ", name, ")"); + goto call_cerror; + } + return 0; + } + + /* XXX We cannot translate the message. */ + errstring = make_string ("version `", string, "' not found (required by ", + name, ")"); + result = 1; + call_cerror: + _dl_signal_cerror (0, map->l_name[0] ? map->l_name : rtld_progname, + NULL, errstring); + return result; +} + + +int +internal_function +_dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) +{ + int result = 0; + const char *strtab; + /* Pointer to section with needed versions. */ + ElfW(Dyn) *dyn; + /* Pointer to dynamic section with definitions. */ + ElfW(Dyn) *def; + /* We need to find out which is the highest version index used + in a dependecy. */ + unsigned int ndx_high = 0; + /* Initialize to make the compiler happy. */ + const char *errstring = NULL; + int errval = 0; + + /* If we don't have a string table, we must be ok. */ + if (map->l_info[DT_STRTAB] == NULL) + return 0; + strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + + dyn = map->l_info[VERSYMIDX (DT_VERNEED)]; + def = map->l_info[VERSYMIDX (DT_VERDEF)]; + + if (dyn != NULL) + { + /* This file requires special versions from its dependencies. */ + ElfW(Verneed) *ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr); + + /* Currently the version number of the needed entry is 1. + Make sure all we see is this version. */ + if (__builtin_expect (ent->vn_version, 1) != 1) + { + char buf[20]; + buf[sizeof (buf) - 1] = '\0'; + /* XXX We cannot translate the message. */ + errstring = make_string ("unsupported version ", + _itoa (ent->vn_version, + &buf[sizeof (buf) - 1], 10, 0), + " of Verneed record\n"); + call_error: + _dl_signal_error (errval, *map->l_name ? map->l_name : rtld_progname, + NULL, errstring); + } + + while (1) + { + ElfW(Vernaux) *aux; + struct link_map *needed = find_needed (strtab + ent->vn_file, map); + + /* If NEEDED is NULL this means a dependency was not found + and no stub entry was created. This should never happen. */ + assert (needed != NULL); + + /* Make sure this is no stub we created because of a missing + dependency. */ + if (__builtin_expect (! trace_mode, 1) + || ! __builtin_expect (needed->l_faked, 0)) + { + /* NEEDED is the map for the file we need. Now look for the + dependency symbols. */ + aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux); + while (1) + { + /* Match the symbol. */ + result |= match_symbol ((*map->l_name + ? map->l_name : rtld_progname), + map->l_ns, aux->vna_hash, + strtab + aux->vna_name, + needed->l_real, verbose, + aux->vna_flags & VER_FLG_WEAK); + + /* Compare the version index. */ + if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high) + ndx_high = aux->vna_other & 0x7fff; + + if (aux->vna_next == 0) + /* No more symbols. */ + break; + + /* Next symbol. */ + aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next); + } + } + + if (ent->vn_next == 0) + /* No more dependencies. */ + break; + + /* Next dependency. */ + ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next); + } + } + + /* We also must store the names of the defined versions. Determine + the maximum index here as well. + + XXX We could avoid the loop by just taking the number of definitions + as an upper bound of new indeces. */ + if (def != NULL) + { + ElfW(Verdef) *ent; + ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr); + while (1) + { + if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high) + ndx_high = ent->vd_ndx & 0x7fff; + + if (ent->vd_next == 0) + /* No more definitions. */ + break; + + ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next); + } + } + + if (ndx_high > 0) + { + /* Now we are ready to build the array with the version names + which can be indexed by the version index in the VERSYM + section. */ + map->l_versions = (struct r_found_version *) + calloc (ndx_high + 1, sizeof (*map->l_versions)); + if (__builtin_expect (map->l_versions == NULL, 0)) + { + errstring = N_("cannot allocate version reference table"); + errval = ENOMEM; + goto call_error; + } + + /* Store the number of available symbols. */ + map->l_nversions = ndx_high + 1; + + /* Compute the pointer to the version symbols. */ + map->l_versyms = (void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]); + + if (dyn != NULL) + { + ElfW(Verneed) *ent; + ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr); + while (1) + { + ElfW(Vernaux) *aux; + aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux); + while (1) + { + ElfW(Half) ndx = aux->vna_other & 0x7fff; + map->l_versions[ndx].hash = aux->vna_hash; + map->l_versions[ndx].hidden = aux->vna_other & 0x8000; + map->l_versions[ndx].name = &strtab[aux->vna_name]; + map->l_versions[ndx].filename = &strtab[ent->vn_file]; + + if (aux->vna_next == 0) + /* No more symbols. */ + break; + + /* Advance to next symbol. */ + aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next); + } + + if (ent->vn_next == 0) + /* No more dependencies. */ + break; + + /* Advance to next dependency. */ + ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next); + } + } + + /* And insert the defined versions. */ + if (def != NULL) + { + ElfW(Verdef) *ent; + ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr); + while (1) + { + ElfW(Verdaux) *aux; + aux = (ElfW(Verdaux) *) ((char *) ent + ent->vd_aux); + + if ((ent->vd_flags & VER_FLG_BASE) == 0) + { + /* The name of the base version should not be + available for matching a versioned symbol. */ + ElfW(Half) ndx = ent->vd_ndx & 0x7fff; + map->l_versions[ndx].hash = ent->vd_hash; + map->l_versions[ndx].name = &strtab[aux->vda_name]; + map->l_versions[ndx].filename = NULL; + } + + if (ent->vd_next == 0) + /* No more definitions. */ + break; + + ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next); + } + } + } + + return result; +} + + +int +internal_function +_dl_check_all_versions (struct link_map *map, int verbose, int trace_mode) +{ + struct link_map *l; + int result = 0; + + for (l = map; l != NULL; l = l->l_next) + result |= (! l->l_faked + && _dl_check_map_versions (l, verbose, trace_mode)); + + return result; +} diff --git a/libc/elf/do-lookup.h b/libc/elf/do-lookup.h new file mode 100644 index 000000000..f40ab9d8d --- /dev/null +++ b/libc/elf/do-lookup.h @@ -0,0 +1,261 @@ +/* Look up a symbol in the loaded objects. + 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 + 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. */ + + +/* 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, 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; + + do + { + /* These variables are used in the nested function. */ + Elf_Symndx symidx; + int num_versions = 0; + const ElfW(Sym) *versioned_sym = NULL; + + const struct link_map *map = list[i]->l_real; + + /* Here come the extra test needed for `_dl_lookup_symbol_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 [%lu]\n", + undef_name, + map->l_name[0] ? map->l_name : rtld_progname, + map->l_ns); + + /* If the hash table is empty there is nothing to do here. */ + if (map->l_nbuckets == 0) + continue; + + /* 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; + + 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. + + 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. */ + return NULL; + } + } + else + { + /* No specific version is selected. There are two ways we + can got here: + + - 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) + { + const Elf32_Word *hasharr = &map->l_gnu_chain_zero[bucket]; + + 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); + } + } + } + else + { + if (*old_hash == 0xffffffff) + *old_hash = _dl_elf_hash (undef_name); + + /* 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 + looking for an unversioned symbol and the version is not the + default version we still accept this symbol since there are + no possible ambiguities. */ + sym = num_versions == 1 ? versioned_sym : NULL; + + if (sym != NULL) + { + found_it: + switch (ELFW(ST_BIND) (sym->st_info)) + { + case STB_WEAK: + /* Weak definition. Use this value if we don't find another. */ + if (__builtin_expect (GLRO(dl_dynamic_weak), 0)) + { + if (! result->s) + { + result->s = sym; + result->m = (struct link_map *) map; + } + break; + } + /* FALLTHROUGH */ + case STB_GLOBAL: + /* Global definition. Just what we need. */ + result->s = sym; + result->m = (struct link_map *) map; + return 1; + default: + /* Local symbols are ignored. */ + break; + } + } + + /* If this current map is the one mentioned in the verneed entry + and we have not found a weak entry, it is a bug. */ + if (symidx == STN_UNDEF && version != NULL && version->filename != NULL + && __builtin_expect (_dl_name_match_p (version->filename, map), 0)) + return -1; + } + while (++i < n); + + /* We have not found anything until now. */ + return 0; +} diff --git a/libc/elf/do-rel.h b/libc/elf/do-rel.h new file mode 100644 index 000000000..990b9615e --- /dev/null +++ b/libc/elf/do-rel.h @@ -0,0 +1,139 @@ +/* Do relocations for ELF dynamic linking. + Copyright (C) 1995-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. */ + +/* This file may be included twice, to define both + `elf_dynamic_do_rel' and `elf_dynamic_do_rela'. */ + +#ifdef DO_RELA +# define elf_dynamic_do_rel elf_dynamic_do_rela +# define RELCOUNT_IDX VERSYMIDX (DT_RELACOUNT) +# define Rel Rela +# define elf_machine_rel elf_machine_rela +# define elf_machine_rel_relative elf_machine_rela_relative +#else +# define RELCOUNT_IDX VERSYMIDX (DT_RELCOUNT) +#endif + +#ifndef DO_ELF_MACHINE_REL_RELATIVE +# define DO_ELF_MACHINE_REL_RELATIVE(map, l_addr, relative) \ + elf_machine_rel_relative (l_addr, relative, \ + (void *) (l_addr + relative->r_offset)) +#endif + +#ifndef VERSYMIDX +# define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym)) +#endif +#ifndef VALIDX +# define VALIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \ + + DT_EXTRANUM + DT_VALTAGIDX (tag)) +#endif + +/* Perform the relocations in MAP on the running program image as specified + by RELTAG, SZTAG. If LAZY is nonzero, this is the first pass on PLT + relocations; they should be set up to call _dl_runtime_resolve, rather + than fully resolved now. */ + +auto inline void __attribute__ ((always_inline)) +elf_dynamic_do_rel (struct link_map *map, + ElfW(Addr) reladdr, ElfW(Addr) relsize, + int lazy) +{ + const ElfW(Rel) *r = (const void *) reladdr; + const ElfW(Rel) *end = (const void *) (reladdr + relsize); + ElfW(Addr) l_addr = map->l_addr; + +#if (!defined DO_RELA || !defined ELF_MACHINE_PLT_REL) && !defined RTLD_BOOTSTRAP + /* We never bind lazily during ld.so bootstrap. Unfortunately gcc is + not clever enough to see through all the function calls to realize + that. */ + if (lazy) + { + /* Doing lazy PLT relocations; they need very little info. */ + for (; r < end; ++r) + elf_machine_lazy_rel (map, l_addr, r); + } + else +#endif + { + const ElfW(Sym) *const symtab = + (const void *) D_PTR (map, l_info[DT_SYMTAB]); + ElfW(Word) nrelative = (map->l_info[RELCOUNT_IDX] == NULL + ? 0 : map->l_info[RELCOUNT_IDX]->d_un.d_val); + const ElfW(Rel) *relative = r; + r = r + MIN (nrelative, relsize / sizeof (ElfW(Rel))); + +#ifndef RTLD_BOOTSTRAP + /* This is defined in rtld.c, but nowhere in the static libc.a; make + the reference weak so static programs can still link. This + declaration cannot be done when compiling rtld.c (i.e. #ifdef + RTLD_BOOTSTRAP) because rtld.c contains the common defn for + _dl_rtld_map, which is incompatible with a weak decl in the same + file. */ +# ifndef SHARED + weak_extern (GL(dl_rtld_map)); +# endif + if (map != &GL(dl_rtld_map)) /* Already done in rtld itself. */ +# if !defined DO_RELA || defined ELF_MACHINE_REL_RELATIVE + /* Rela platforms get the offset from r_addend and this must + be copied in the relocation address. Therefore we can skip + the relative relocations only if this is for rel + relocations or rela relocations if they are computed as + memory_loc += l_addr... */ + if (l_addr != 0) +# else + /* ...or we know the object has been prelinked. */ + if (l_addr != 0 || ! map->l_info[VALIDX(DT_GNU_PRELINKED)]) +# endif +#endif + for (; relative < r; ++relative) + DO_ELF_MACHINE_REL_RELATIVE (map, l_addr, relative); + +#ifdef RTLD_BOOTSTRAP + /* The dynamic linker always uses versioning. */ + assert (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL); +#else + if (map->l_info[VERSYMIDX (DT_VERSYM)]) +#endif + { + const ElfW(Half) *const version = + (const void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]); + + for (; r < end; ++r) + { + ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff; + elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], + &map->l_versions[ndx], + (void *) (l_addr + r->r_offset)); + } + } +#ifndef RTLD_BOOTSTRAP + else + for (; r < end; ++r) + elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], NULL, + (void *) (l_addr + r->r_offset)); +#endif + } +} + +#undef elf_dynamic_do_rel +#undef Rel +#undef elf_machine_rel +#undef elf_machine_rel_relative +#undef DO_ELF_MACHINE_REL_RELATIVE +#undef RELCOUNT_IDX diff --git a/libc/elf/dynamic-link.h b/libc/elf/dynamic-link.h new file mode 100644 index 000000000..7eb9a3613 --- /dev/null +++ b/libc/elf/dynamic-link.h @@ -0,0 +1,343 @@ +/* Inline functions for dynamic linking. + 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 + 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 <elf.h> +#include <assert.h> + +#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 + entitled to assume that a pointer to a type is properly aligned for + the type. Even if we cast the pointer back to some other type with + less strict alignment requirements, the compiler might still + remember that the pointer was originally more aligned, thereby + optimizing away alignment tests or using word instructions for + copying memory, breaking the very code written to handle the + unaligned cases. */ +# 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 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 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 inline void __attribute__((always_inline)) +elf_machine_lazy_rel (struct link_map *map, + ElfW(Addr) l_addr, const ElfW(Rel) *reloc); +# else +auto inline void __attribute__((always_inline)) +elf_machine_lazy_rel (struct link_map *map, + ElfW(Addr) l_addr, const ElfW(Rela) *reloc); +# endif +#endif + +#include <dl-machine.h> + +#ifndef VERSYMIDX +# define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym)) +#endif + + +/* Read the dynamic section at DYN and fill in INFO with indices DT_*. */ +#ifndef RESOLVE_MAP +static +#else +auto +#endif +inline void __attribute__ ((unused, always_inline)) +elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp) +{ + ElfW(Dyn) *dyn = l->l_ld; + ElfW(Dyn) **info; + +#ifndef RTLD_BOOTSTRAP + if (dyn == NULL) + return; +#endif + + info = l->l_info; + + while (dyn->d_tag != DT_NULL) + { + if (dyn->d_tag < DT_NUM) + info[dyn->d_tag] = dyn; + else if (dyn->d_tag >= DT_LOPROC && + dyn->d_tag < DT_LOPROC + DT_THISPROCNUM) + info[dyn->d_tag - DT_LOPROC + DT_NUM] = dyn; + else if ((Elf32_Word) DT_VERSIONTAGIDX (dyn->d_tag) < DT_VERSIONTAGNUM) + info[VERSYMIDX (dyn->d_tag)] = dyn; + else if ((Elf32_Word) DT_EXTRATAGIDX (dyn->d_tag) < DT_EXTRANUM) + info[DT_EXTRATAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM + + DT_VERSIONTAGNUM] = dyn; + else if ((Elf32_Word) DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM) + info[DT_VALTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM + + DT_VERSIONTAGNUM + DT_EXTRANUM] = dyn; + else if ((Elf32_Word) DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM) + info[DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM + + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn; + ++dyn; + } + +#define DL_RO_DYN_TEMP_CNT 8 + +#ifndef DL_RO_DYN_SECTION + /* Don't adjust .dynamic unnecessarily. */ + if (l->l_addr != 0) + { + ElfW(Addr) l_addr = l->l_addr; + int cnt = 0; + +# define ADJUST_DYN_INFO(tag) \ + do \ + if (info[tag] != NULL) \ + { \ + if (temp) \ + { \ + temp[cnt].d_tag = info[tag]->d_tag; \ + temp[cnt].d_un.d_ptr = info[tag]->d_un.d_ptr + l_addr; \ + info[tag] = temp + cnt++; \ + } \ + else \ + info[tag]->d_un.d_ptr += l_addr; \ + } \ + while (0) + + ADJUST_DYN_INFO (DT_HASH); + ADJUST_DYN_INFO (DT_PLTGOT); + ADJUST_DYN_INFO (DT_STRTAB); + ADJUST_DYN_INFO (DT_SYMTAB); +# if ! ELF_MACHINE_NO_RELA + ADJUST_DYN_INFO (DT_RELA); +# endif +# if ! ELF_MACHINE_NO_REL + ADJUST_DYN_INFO (DT_REL); +# 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); + } +#endif + if (info[DT_PLTREL] != NULL) + { +#if ELF_MACHINE_NO_RELA + assert (info[DT_PLTREL]->d_un.d_val == DT_REL); +#elif ELF_MACHINE_NO_REL + assert (info[DT_PLTREL]->d_un.d_val == DT_RELA); +#else + assert (info[DT_PLTREL]->d_un.d_val == DT_REL + || info[DT_PLTREL]->d_un.d_val == DT_RELA); +#endif + } +#if ! ELF_MACHINE_NO_RELA + if (info[DT_RELA] != NULL) + assert (info[DT_RELAENT]->d_un.d_val == sizeof (ElfW(Rela))); +# endif +# if ! ELF_MACHINE_NO_REL + if (info[DT_REL] != NULL) + assert (info[DT_RELENT]->d_un.d_val == sizeof (ElfW(Rel))); +#endif +#ifdef RTLD_BOOTSTRAP + /* Only the bind now flags are allowed. */ + assert (info[VERSYMIDX (DT_FLAGS_1)] == NULL + || info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val == DF_1_NOW); + assert (info[DT_FLAGS] == NULL + || info[DT_FLAGS]->d_un.d_val == DF_BIND_NOW); + /* Flags must not be set for ld.so. */ + assert (info[DT_RUNPATH] == NULL); + assert (info[DT_RPATH] == NULL); +#else + if (info[DT_FLAGS] != NULL) + { + /* Flags are used. Translate to the old form where available. + Since these l_info entries are only tested for NULL pointers it + is ok if they point to the DT_FLAGS entry. */ + l->l_flags = info[DT_FLAGS]->d_un.d_val; + + if (l->l_flags & DF_SYMBOLIC) + info[DT_SYMBOLIC] = info[DT_FLAGS]; + if (l->l_flags & DF_TEXTREL) + info[DT_TEXTREL] = info[DT_FLAGS]; + if (l->l_flags & DF_BIND_NOW) + info[DT_BIND_NOW] = info[DT_FLAGS]; + } + if (info[VERSYMIDX (DT_FLAGS_1)] != NULL) + { + l->l_flags_1 = info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val; + + if (l->l_flags_1 & DF_1_NOW) + info[DT_BIND_NOW] = info[VERSYMIDX (DT_FLAGS_1)]; + } + if (info[DT_RUNPATH] != NULL) + /* If both RUNPATH and RPATH are given, the latter is ignored. */ + info[DT_RPATH] = NULL; +#endif +} + +#ifdef RESOLVE_MAP + +# ifdef RTLD_BOOTSTRAP +# define ELF_DURING_STARTUP (1) +# else +# define ELF_DURING_STARTUP (0) +# endif + +/* Get the definitions of `elf_dynamic_do_rel' and `elf_dynamic_do_rela'. + These functions are almost identical, so we use cpp magic to avoid + duplicating their code. It cannot be done in a more general function + because we must be able to completely inline. */ + +/* On some machines, notably SPARC, DT_REL* includes DT_JMPREL in its + range. Note that according to the ELF spec, this is completely legal! + But conditionally define things so that on machines we know this will + not happen we do something more optimal. */ + +# ifdef ELF_MACHINE_PLTREL_OVERLAP +# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, do_lazy, test_rel) \ + do { \ + struct { ElfW(Addr) start, size; int lazy; } ranges[3]; \ + int ranges_index; \ + \ + ranges[0].lazy = ranges[2].lazy = 0; \ + ranges[1].lazy = 1; \ + ranges[0].size = ranges[1].size = ranges[2].size = 0; \ + \ + if ((map)->l_info[DT_##RELOC]) \ + { \ + ranges[0].start = D_PTR ((map), l_info[DT_##RELOC]); \ + ranges[0].size = (map)->l_info[DT_##RELOC##SZ]->d_un.d_val; \ + } \ + \ + if ((do_lazy) \ + && (map)->l_info[DT_PLTREL] \ + && (!test_rel || (map)->l_info[DT_PLTREL]->d_un.d_val == DT_##RELOC)) \ + { \ + ranges[1].start = D_PTR ((map), l_info[DT_JMPREL]); \ + ranges[1].size = (map)->l_info[DT_PLTRELSZ]->d_un.d_val; \ + ranges[2].start = ranges[1].start + ranges[1].size; \ + ranges[2].size = ranges[0].start + ranges[0].size - ranges[2].start; \ + ranges[0].size = ranges[1].start - ranges[0].start; \ + } \ + \ + for (ranges_index = 0; ranges_index < 3; ++ranges_index) \ + elf_dynamic_do_##reloc ((map), \ + ranges[ranges_index].start, \ + ranges[ranges_index].size, \ + ranges[ranges_index].lazy); \ + } while (0) +# else +# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, do_lazy, test_rel) \ + do { \ + struct { ElfW(Addr) start, size; int lazy; } ranges[2]; \ + ranges[0].lazy = 0; \ + ranges[0].size = ranges[1].size = 0; \ + ranges[0].start = 0; \ + \ + if ((map)->l_info[DT_##RELOC]) \ + { \ + ranges[0].start = D_PTR ((map), l_info[DT_##RELOC]); \ + ranges[0].size = (map)->l_info[DT_##RELOC##SZ]->d_un.d_val; \ + } \ + if ((map)->l_info[DT_PLTREL] \ + && (!test_rel || (map)->l_info[DT_PLTREL]->d_un.d_val == DT_##RELOC)) \ + { \ + ElfW(Addr) start = D_PTR ((map), l_info[DT_JMPREL]); \ + \ + if (! ELF_DURING_STARTUP \ + && ((do_lazy) \ + /* This test does not only detect whether the relocation \ + sections are in the right order, it also checks whether \ + there is a DT_REL/DT_RELA section. */ \ + || ranges[0].start + ranges[0].size != start)) \ + { \ + ranges[1].start = start; \ + ranges[1].size = (map)->l_info[DT_PLTRELSZ]->d_un.d_val; \ + ranges[1].lazy = (do_lazy); \ + } \ + else \ + { \ + /* Combine processing the sections. */ \ + assert (ranges[0].start + ranges[0].size == start); \ + ranges[0].size += (map)->l_info[DT_PLTRELSZ]->d_un.d_val; \ + } \ + } \ + \ + if (ELF_DURING_STARTUP) \ + elf_dynamic_do_##reloc ((map), ranges[0].start, ranges[0].size, 0); \ + else \ + { \ + int ranges_index; \ + for (ranges_index = 0; ranges_index < 2; ++ranges_index) \ + elf_dynamic_do_##reloc ((map), \ + ranges[ranges_index].start, \ + ranges[ranges_index].size, \ + ranges[ranges_index].lazy); \ + } \ + } while (0) +# endif + +# if ELF_MACHINE_NO_REL || ELF_MACHINE_NO_RELA +# define _ELF_CHECK_REL 0 +# else +# define _ELF_CHECK_REL 1 +# endif + +# if ! ELF_MACHINE_NO_REL +# include "do-rel.h" +# define ELF_DYNAMIC_DO_REL(map, lazy) \ + _ELF_DYNAMIC_DO_RELOC (REL, rel, map, lazy, _ELF_CHECK_REL) +# else +# define ELF_DYNAMIC_DO_REL(map, lazy) /* Nothing to do. */ +# endif + +# if ! ELF_MACHINE_NO_RELA +# define DO_RELA +# include "do-rel.h" +# define ELF_DYNAMIC_DO_RELA(map, lazy) \ + _ELF_DYNAMIC_DO_RELOC (RELA, rela, map, lazy, _ELF_CHECK_REL) +# else +# define ELF_DYNAMIC_DO_RELA(map, lazy) /* Nothing to do. */ +# endif + +/* This can't just be an inline function because GCC is too dumb + to inline functions containing inlines themselves. */ +# define ELF_DYNAMIC_RELOCATE(map, lazy, consider_profile) \ + do { \ + int edr_lazy = elf_machine_runtime_setup ((map), (lazy), \ + (consider_profile)); \ + ELF_DYNAMIC_DO_REL ((map), edr_lazy); \ + ELF_DYNAMIC_DO_RELA ((map), edr_lazy); \ + } while (0) + +#endif diff --git a/libc/elf/elf.h b/libc/elf/elf.h new file mode 100644 index 000000000..dae359778 --- /dev/null +++ b/libc/elf/elf.h @@ -0,0 +1,2608 @@ +/* This file defines standard ELF types, structures, and macros. + 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 + 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. */ + +#ifndef _ELF_H +#define _ELF_H 1 + +#include <features.h> + +__BEGIN_DECLS + +/* Standard ELF types. */ + +#include <stdint.h> + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_LINUX 3 /* Linux. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_NUM 95 + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_BEFORE 0xff00 /* Order section before all others + (Solaris). */ +#define SHN_AFTER 0xff01 /* Order section after all others + (Solaris). */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#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_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. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ +#define SHF_ORDERED (1 << 30) /* Special ordering requirement + (Solaris). */ +#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless + referenced or allocated (Solaris).*/ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* Symbol table entry. */ + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct +{ + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct +{ + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object*/ +#define STT_NUM 7 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_ASRS 8 /* Contains copy of asrset struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + + +/* Dynamic section entry. */ + +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path (deprecated) */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used */ +#define DT_LOOS 0x6000000d /* Start of OS-specific */ +#define DT_HIOS 0x6ffff000 /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + 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. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 11 + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not + generally available. */ + +/* Version definition sections. */ + +typedef struct +{ + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* Auxialiary version information. */ + +typedef struct +{ + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + + +/* Version dependency section. */ + +typedef struct +{ + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct +{ + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct +{ + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct +{ + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + + +/* Legal values for vna_flags. */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard <elf.h> file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct +{ + uint32_t a_type; /* Entry type */ + union + { + 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 +{ + uint64_t a_type; /* Entry type */ + union + { + 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; + +/* Legal values for a_type (entry type). */ + +#define AT_NULL 0 /* End of vector */ +#define AT_IGNORE 1 /* Entry should be ignored */ +#define AT_EXECFD 2 /* File descriptor of program */ +#define AT_PHDR 3 /* Program headers for program */ +#define AT_PHENT 4 /* Size of program header entry */ +#define AT_PHNUM 5 /* Number of program headers */ +#define AT_PAGESZ 6 /* System page size */ +#define AT_BASE 7 /* Base address of interpreter */ +#define AT_FLAGS 8 /* Flags */ +#define AT_ENTRY 9 /* Entry point of program */ +#define AT_NOTELF 10 /* Program is not ELF */ +#define AT_UID 11 /* Real uid */ +#define AT_EUID 12 /* Effective uid */ +#define AT_GID 13 /* Real gid */ +#define AT_EGID 14 /* Effective gid */ +#define AT_CLKTCK 17 /* Frequency of times() */ + +/* Some more special a_type values describing the hardware. */ +#define AT_PLATFORM 15 /* String identifying platform. */ +#define AT_HWCAP 16 /* Machine dependent hints about + processor capabilities. */ + +/* This entry gives some information about the FPU initialization + performed by the kernel. */ +#define AT_FPUCW 18 /* Used FPU control word. */ + +/* Cache block sizes. */ +#define AT_DCACHEBSIZE 19 /* Data cache block size. */ +#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ +#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ + +/* A special ignored value for PPC, used by the kernel to control the + interpretation of the AUXV. Must be > 16. */ +#define AT_IGNOREPPC 22 /* Entry should be ignored. */ + +#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ + +/* Pointer to the global system page used for system calls and other + nice things. */ +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + +/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains + log2 of line size; mask those to get cache size. */ +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define ELF_NOTE_ABI 1 + +/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI + note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + + +/* Move records. */ +typedef struct +{ + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct +{ + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + + +/* Motorola 68k specific definitions. */ + +/* Values for Elf32_Ehdr.e_flags. */ +#define EF_CPU32 0x00810000 + +/* m68k relocs. */ + +#define R_68K_NONE 0 /* No reloc */ +#define R_68K_32 1 /* Direct 32 bit */ +#define R_68K_16 2 /* Direct 16 bit */ +#define R_68K_8 3 /* Direct 8 bit */ +#define R_68K_PC32 4 /* PC relative 32 bit */ +#define R_68K_PC16 5 /* PC relative 16 bit */ +#define R_68K_PC8 6 /* PC relative 8 bit */ +#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ +#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ +#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ +#define R_68K_GOT32O 10 /* 32 bit GOT offset */ +#define R_68K_GOT16O 11 /* 16 bit GOT offset */ +#define R_68K_GOT8O 12 /* 8 bit GOT offset */ +#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ +#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ +#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ +#define R_68K_PLT32O 16 /* 32 bit PLT offset */ +#define R_68K_PLT16O 17 /* 16 bit PLT offset */ +#define R_68K_PLT8O 18 /* 8 bit PLT offset */ +#define R_68K_COPY 19 /* Copy symbol at runtime */ +#define R_68K_GLOB_DAT 20 /* Create GOT entry */ +#define R_68K_JMP_SLOT 21 /* Create PLT entry */ +#define R_68K_RELATIVE 22 /* Adjust by program base */ +/* Keep this the last entry. */ +#define R_68K_NUM 23 + +/* Intel 80386 specific definitions. */ + +/* i386 relocs. */ + +#define R_386_NONE 0 /* No reloc */ +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_GOT32 3 /* 32 bit GOT entry */ +#define R_386_PLT32 4 /* 32 bit PLT address */ +#define R_386_COPY 5 /* Copy symbol at runtime */ +#define R_386_GLOB_DAT 6 /* Create GOT entry */ +#define R_386_JMP_SLOT 7 /* Create PLT entry */ +#define R_386_RELATIVE 8 /* Adjust by program base */ +#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ +#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS + block offset */ +#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block + offset */ +#define R_386_TLS_LE 17 /* Offset relative to static TLS + block */ +#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of + general dynamic thread local data */ +#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of + local dynamic thread local data + in LE code */ +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic + thread local data */ +#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ +#define R_386_TLS_GD_CALL 26 /* Relocation for call to + __tls_get_addr() */ +#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ +#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic + thread local data in LE code */ +#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ +#define R_386_TLS_LDM_CALL 30 /* Relocation for call to + __tls_get_addr() in LDM code */ +#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ +#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ +#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS + block offset */ +#define R_386_TLS_LE_32 34 /* Negated offset relative to static + TLS block */ +#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ +#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ +#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ +/* Keep this the last entry. */ +#define R_386_NUM 38 + +/* SUN SPARC specific definitions. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + +/* SPARC relocs. */ + +#define R_SPARC_NONE 0 /* No reloc */ +#define R_SPARC_8 1 /* Direct 8 bit */ +#define R_SPARC_16 2 /* Direct 16 bit */ +#define R_SPARC_32 3 /* Direct 32 bit */ +#define R_SPARC_DISP8 4 /* PC relative 8 bit */ +#define R_SPARC_DISP16 5 /* PC relative 16 bit */ +#define R_SPARC_DISP32 6 /* PC relative 32 bit */ +#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ +#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ +#define R_SPARC_HI22 9 /* High 22 bit */ +#define R_SPARC_22 10 /* Direct 22 bit */ +#define R_SPARC_13 11 /* Direct 13 bit */ +#define R_SPARC_LO10 12 /* Truncated 10 bit */ +#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ +#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ +#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ +#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ +#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ +#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ +#define R_SPARC_COPY 19 /* Copy symbol at runtime */ +#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ +#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ +#define R_SPARC_RELATIVE 22 /* Adjust by program base */ +#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ + +/* Additional Sparc64 relocs. */ + +#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ +#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ +#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ +#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ +#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ +#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ +#define R_SPARC_10 30 /* Direct 10 bit */ +#define R_SPARC_11 31 /* Direct 11 bit */ +#define R_SPARC_64 32 /* Direct 64 bit */ +#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ +#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ +#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ +#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ +#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ +#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ +#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ +#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ +#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ +#define R_SPARC_7 43 /* Direct 7 bit */ +#define R_SPARC_5 44 /* Direct 5 bit */ +#define R_SPARC_6 45 /* Direct 6 bit */ +#define R_SPARC_DISP64 46 /* PC relative 64 bit */ +#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ +#define R_SPARC_HIX22 48 /* High 22 bit complemented */ +#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ +#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ +#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ +#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ +#define R_SPARC_REGISTER 53 /* Global register usage */ +#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ +#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +/* Keep this the last entry. */ +#define R_SPARC_NUM 80 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* Bits present in AT_HWCAP on SPARC. */ + +#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_ULTRA3 32 +#define HWCAP_SPARC_BLKINIT 64 /* Sun4v with block-init/load-twin. */ + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ +#define EF_MIPS_PIC 2 /* Contains PIC code */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* The following are non-official names and should not be used. */ + +#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* Special section indices. */ + +#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ +#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ +#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ +#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ +#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ +#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ +#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ +#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + +/* Symbol tables. */ + +/* MIPS specific values for `st_other'. */ +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + +/* MIPS specific values for `st_info'. */ +#define STB_MIPS_SPLIT_COMMON 13 + +/* Entries found in sections of type SHT_MIPS_GPTAB. */ + +typedef union +{ + struct + { + Elf32_Word gt_current_g_value; /* -G value used for compilation */ + Elf32_Word gt_unused; /* Not used */ + } gt_header; /* First entry in section */ + struct + { + Elf32_Word gt_g_value; /* If this value were used for -G */ + Elf32_Word gt_bytes; /* This many bytes would be used */ + } gt_entry; /* Subsequent entries in section */ +} Elf32_gptab; + +/* Entry found in sections of type SHT_MIPS_REGINFO. */ + +typedef struct +{ + Elf32_Word ri_gprmask; /* General registers used */ + Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ + Elf32_Sword ri_gp_value; /* $gp register value */ +} Elf32_RegInfo; + +/* Entries found in sections of type SHT_MIPS_OPTIONS. */ + +typedef struct +{ + unsigned char kind; /* Determines interpretation of the + variable part of descriptor. */ + unsigned char size; /* Size of descriptor, including header. */ + Elf32_Section section; /* Section header index of section affected, + 0 for global options. */ + Elf32_Word info; /* Kind-specific information. */ +} Elf_Options; + +/* Values for `kind' field in Elf_Options. */ + +#define ODK_NULL 0 /* Undefined. */ +#define ODK_REGINFO 1 /* Register usage information. */ +#define ODK_EXCEPTIONS 2 /* Exception processing options. */ +#define ODK_PAD 3 /* Section padding options. */ +#define ODK_HWPATCH 4 /* Hardware workarounds performed */ +#define ODK_FILL 5 /* record the fill value used by the linker. */ +#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ +#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ +#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ + +/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ + +#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ +#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ +#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ +#define OEX_SMM 0x20000 /* Force sequential memory mode? */ +#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + +/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ + +#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ +#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ +#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ +#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + +/* Entry found in `.options' section. */ + +typedef struct +{ + Elf32_Word hwp_flags1; /* Extra flags. */ + Elf32_Word hwp_flags2; /* Extra flags. */ +} Elf_Options_Hw; + +/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + +/* MIPS relocs. */ + +#define R_MIPS_NONE 0 /* No reloc */ +#define R_MIPS_16 1 /* Direct 16 bit */ +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_REL32 3 /* PC relative 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ +#define R_MIPS_HI16 5 /* High 16 bit */ +#define R_MIPS_LO16 6 /* Low 16 bit */ +#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ +#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ +#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ +#define R_MIPS_PC16 10 /* PC relative 16 bit */ +#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ +#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#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 51 + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + +/* Legal values for d_tag field of Elf32_Dyn. */ + +#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ +#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ +#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ +#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ +#define DT_MIPS_FLAGS 0x70000005 /* Flags */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ +#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ +#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ +#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ +#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ +#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ +#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ +#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ +#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ +#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in + DT_MIPS_DELTA_CLASS. */ +#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in + DT_MIPS_DELTA_INSTANCE. */ +#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in + DT_MIPS_DELTA_RELOC. */ +#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta + relocations refer to. */ +#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in + DT_MIPS_DELTA_SYM. */ +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the + class declaration. */ +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in + DT_MIPS_DELTA_CLASSSYM. */ +#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ +#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve + function stored in GOT. */ +#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added + by rld on dlopen() calls. */ +#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ +#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ +#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ +#define DT_MIPS_NUM 0x32 + +/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ + +#define RHF_NONE 0 /* No flags */ +#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ +#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + +/* Entries found in sections of type SHT_MIPS_LIBLIST. */ + +typedef struct +{ + Elf32_Word l_name; /* Name (string table index) */ + Elf32_Word l_time_stamp; /* Timestamp */ + Elf32_Word l_checksum; /* Checksum */ + Elf32_Word l_version; /* Interface version */ + Elf32_Word l_flags; /* Flags */ +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; /* Name (string table index) */ + Elf64_Word l_time_stamp; /* Timestamp */ + Elf64_Word l_checksum; /* Checksum */ + Elf64_Word l_version; /* Interface version */ + Elf64_Word l_flags; /* Flags */ +} Elf64_Lib; + + +/* Legal values for l_flags. */ + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ +#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + +/* Entries found in sections of type SHT_MIPS_CONFLICT. */ + +typedef Elf32_Addr Elf32_Conflict; + + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indeces. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_HIRESERVE 255 + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + +/* Legal values for sh_type field of Elf64_Shdr. */ + +/* These two are primerily concerned with ECOFF debugging info. */ +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + +/* Legal values for sh_flags field of Elf64_Shdr. */ + +#define SHF_ALPHA_GPREL 0x10000000 + +/* Legal values for st_other field of Elf64_Sym. */ +#define STO_ALPHA_NOPV 0x80 /* No PV required. */ +#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ + +/* Alpha relocs. */ + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 +/* Keep this the last entry. */ +#define R_ALPHA_NUM 46 + +/* Magic values of the LITUSE relocation addend. */ +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#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 */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + +/* PowerPC relocations defined for the TLS access ABI. */ +#define R_PPC_TLS 67 /* none (sym+add)@tls */ +#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ +#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ +#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ +#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ +#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ +#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ +#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ +#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ +#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ + +/* Keep this the last entry. */ +#define R_PPC_NUM 95 + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#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 +#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ +#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ +#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ +#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ +#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ +#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ +#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ +#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ +#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ +#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ +#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ +#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ +#define R_PPC64_PLT64 45 /* doubleword64 L + A */ +#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ +#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ +#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ +#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ +#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ +#define R_PPC64_TOC 51 /* doubleword64 .TOC */ +#define R_PPC64_PLTGOT16 52 /* half16* M + A */ +#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ +#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ +#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ + +#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ +#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ +#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ +#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ +#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ +#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ +#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ +#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ +#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ +#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ +#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ + +/* PowerPC64 relocations defined for the TLS access ABI. */ +#define R_PPC64_TLS 67 /* none (sym+add)@tls */ +#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ +#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ +#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ +#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ +#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ +#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ +#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ +#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ +#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ +#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ +#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ +#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ +#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ +#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ +#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ +#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ +#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ + +/* Keep this the last entry. */ +#define R_PPC64_NUM 107 + +/* PowerPC64 specific values for the Dyn d_tag field. */ +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPD (DT_LOPROC + 1) +#define DT_PPC64_OPDSZ (DT_LOPROC + 2) +#define DT_PPC64_NUM 3 + + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 + +/* Additional symbol types for Thumb */ +#define STT_ARM_TFUNC 0xd + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step */ + +/* ARM-specific program header flags */ +#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 */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 +#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 */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* 32 bit PLT address */ +#define R_ARM_ALU_PCREL_7_0 32 +#define R_ARM_ALU_PCREL_15_8 33 +#define R_ARM_ALU_PCREL_23_15 34 +#define R_ARM_LDR_SBREL_11_0 35 +#define R_ARM_ALU_SBREL_19_12 36 +#define R_ARM_ALU_SBREL_27_20 37 +#define R_ARM_GNU_VTENTRY 100 +#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 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ +#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) +#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) +#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +/* SH specific declarations */ + +/* SH relocs. */ +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +/* Keep this the last entry. */ +#define R_SH_NUM 256 + +/* Additional s390 relocs */ + +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS + block. */ +#define R_390_20 57 /* Direct 20 bit. */ +#define R_390_GOT20 58 /* 20 bit GOT offset. */ +#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ +#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS + block offset. */ +/* Keep this the last entry. */ +#define R_390_NUM 61 + + +/* CRIS relocations. */ +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ +#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset + to two GOT entries for GD symbol */ +#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset + to two GOT entries for LD symbol */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset + to GOT entry for IE symbol */ +#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ + +#define R_X86_64_NUM 24 + + +/* AM33 relocations. */ +#define R_MN10300_NONE 0 /* No reloc. */ +#define R_MN10300_32 1 /* Direct 32 bit. */ +#define R_MN10300_16 2 /* Direct 16 bit. */ +#define R_MN10300_8 3 /* Direct 8 bit. */ +#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ +#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ +#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ +#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ +#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ +#define R_MN10300_24 9 /* Direct 24 bit. */ +#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ +#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ +#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ +#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ +#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ +#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ +#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ +#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ +#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ +#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ +#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ +#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ +#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ +#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ + +#define R_MN10300_NUM 24 + + +/* M32R relocs. */ +#define R_M32R_NONE 0 /* No reloc. */ +#define R_M32R_16 1 /* Direct 16 bit. */ +#define R_M32R_32 2 /* Direct 32 bit. */ +#define R_M32R_24 3 /* Direct 24 bit. */ +#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ +#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ +#define R_M32R_LO16 9 /* Low 16 bit. */ +#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ +#define R_M32R_GNU_VTINHERIT 11 +#define R_M32R_GNU_VTENTRY 12 +/* M32R relocs use SHT_RELA. */ +#define R_M32R_16_RELA 33 /* Direct 16 bit. */ +#define R_M32R_32_RELA 34 /* Direct 32 bit. */ +#define R_M32R_24_RELA 35 /* Direct 24 bit. */ +#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ +#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ +#define R_M32R_LO16_RELA 41 /* Low 16 bit */ +#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 */ +#define R_M32R_COPY 50 /* Copy symbol at runtime */ +#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ +#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ +#define R_M32R_RELATIVE 53 /* Adjust by program base */ +#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ +#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ +#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned + low */ +#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed + low */ +#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ +#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to + GOT with unsigned low */ +#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to + GOT with signed low */ +#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to + GOT */ +#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT + with unsigned low */ +#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT + with signed low */ +#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ +#define R_M32R_NUM 256 /* Keep this the last entry. */ + + +__END_DECLS + +#endif /* elf.h */ diff --git a/libc/elf/enbl-secure.c b/libc/elf/enbl-secure.c new file mode 100644 index 000000000..fac3b9c52 --- /dev/null +++ b/libc/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/libc/elf/failobj.c b/libc/elf/failobj.c new file mode 100644 index 000000000..500606382 --- /dev/null +++ b/libc/elf/failobj.c @@ -0,0 +1,10 @@ +/* This function is supposed to not exist. */ +extern int xyzzy (int); + +extern int foo (int); + +int +foo (int a) +{ + return xyzzy (a); +} diff --git a/libc/elf/filter.c b/libc/elf/filter.c new file mode 100644 index 000000000..46aa15ba1 --- /dev/null +++ b/libc/elf/filter.c @@ -0,0 +1,19 @@ +#include <mcheck.h> +#include <stdio.h> +#include <string.h> + +extern const char *foo (void); + +int +main (void) +{ + const char *s; + + mtrace (); + + s = foo (); + + printf ("called `foo' from `%s'\n", s); + + return strcmp (s, "filtmod2.c"); +} diff --git a/libc/elf/filtmod1.c b/libc/elf/filtmod1.c new file mode 100644 index 000000000..1d9b19481 --- /dev/null +++ b/libc/elf/filtmod1.c @@ -0,0 +1,7 @@ +extern const char *foo (void); + +const char * +foo (void) +{ + return __FILE__; +} diff --git a/libc/elf/filtmod2.c b/libc/elf/filtmod2.c new file mode 100644 index 000000000..1d9b19481 --- /dev/null +++ b/libc/elf/filtmod2.c @@ -0,0 +1,7 @@ +extern const char *foo (void); + +const char * +foo (void) +{ + return __FILE__; +} diff --git a/libc/elf/firstobj.c b/libc/elf/firstobj.c new file mode 100644 index 000000000..2e6033eab --- /dev/null +++ b/libc/elf/firstobj.c @@ -0,0 +1,10 @@ +#include <errno.h> + +extern int foo (void); + +int +foo (void) +{ + errno = 0; + return 0; +} diff --git a/libc/elf/gen-trusted-dirs.awk b/libc/elf/gen-trusted-dirs.awk new file mode 100644 index 000000000..59f10a485 --- /dev/null +++ b/libc/elf/gen-trusted-dirs.awk @@ -0,0 +1,37 @@ +BEGIN { + FS = " "; +} + +{ + for (i = 1; i <= NF; ++i) { + s[cnt++] = $i"/"; + } +} + +END { + printf ("#define SYSTEM_DIRS \\\n"); + + printf (" \"%s\"", s[0]); + + for (i = 1; i < cnt; ++i) { + printf (" \"\\0\" \"%s\"", s[i]); + } + + printf ("\n\n"); + + printf ("#define SYSTEM_DIRS_LEN \\\n"); + + printf (" %d", length (s[0])); + m = length (s[0]); + + for (i = 1; i < cnt; ++i) { + printf (", %d", length(s[i])); + if (length(s[i]) > m) { + m = length(s[i]); + } + } + + printf ("\n\n"); + + printf ("#define SYSTEM_DIRS_MAX_LEN\t%d\n", m); +} diff --git a/libc/elf/genrtldtbl.awk b/libc/elf/genrtldtbl.awk new file mode 100644 index 000000000..54cdc8c58 --- /dev/null +++ b/libc/elf/genrtldtbl.awk @@ -0,0 +1,29 @@ +#! /usr/bin/awk +BEGIN { + FS=":"; + count=0; +} +{ + for (i = 1; i <= NF; ++i) { + gsub (/\/*$/, "", $i); + dir[count++] = $i; + } +} +END { + for (i = 0; i < count; ++i) { + printf ("static struct r_search_path_elem rtld_search_dir%d =\n", i+1); + printf (" { \"%s/\", %d, unknown, 0, nonexisting, NULL, NULL, ", + dir[i], length (dir[i]) + 1); + if (i== 0) + printf ("NULL };\n"); + else + printf ("&rtld_search_dir%d };\n", i); + } + printf ("\nstatic struct r_search_path_elem *rtld_search_dirs[] =\n{\n"); + for (i = 0; i < count; ++i) { + printf (" &rtld_search_dir%d,\n", i + 1); + } + printf (" NULL\n};\n\n"); + printf ("static struct r_search_path_elem *all_dirs = &rtld_search_dir%d;\n", + count); +} diff --git a/libc/elf/global.c b/libc/elf/global.c new file mode 100644 index 000000000..c675858b6 --- /dev/null +++ b/libc/elf/global.c @@ -0,0 +1,7 @@ +extern int test (void); + +int +main (void) +{ + return test (); +} diff --git a/libc/elf/globalmod1.c b/libc/elf/globalmod1.c new file mode 100644 index 000000000..3f8082226 --- /dev/null +++ b/libc/elf/globalmod1.c @@ -0,0 +1,17 @@ +#include <dlfcn.h> +#include <stdio.h> + +extern int test (void); + +int +test (void) +{ + (void) dlopen ("reldepmod4.so", RTLD_LAZY | RTLD_GLOBAL); + if (dlsym (RTLD_DEFAULT, "call_me") != NULL) + { + puts ("found \"call_me\""); + return 0; + } + puts ("didn't find \"call_me\""); + return 1; +} diff --git a/libc/elf/initfirst.c b/libc/elf/initfirst.c new file mode 100644 index 000000000..5ca83d21b --- /dev/null +++ b/libc/elf/initfirst.c @@ -0,0 +1,22 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +main (void) +{ + void *h = dlopen ("firstobj.so", RTLD_LAZY); + void *f; + if (! h) + { + printf ("cannot find firstobj.so: %s\n", dlerror ()); + return 1; + } + f = dlsym (h, "foo"); + if (! f) + { + printf ("cannot find symbol foo: %s\n", dlerror ()); + return 2; + } + ((void (*) (void)) f) (); + return 0; +} diff --git a/libc/elf/interp.c b/libc/elf/interp.c new file mode 100644 index 000000000..cdc6bb429 --- /dev/null +++ b/libc/elf/interp.c @@ -0,0 +1,21 @@ +/* interp - add information about dynamic loader to shared library objects. + Copyright (C) 1996 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. */ + +const char __invoke_dynamic_linker__[] __attribute__ ((section (".interp"))) + = RUNTIME_LINKER; diff --git a/libc/elf/lateglobal.c b/libc/elf/lateglobal.c new file mode 100644 index 000000000..4a1a7cd08 --- /dev/null +++ b/libc/elf/lateglobal.c @@ -0,0 +1,47 @@ +#include <dlfcn.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + void *h[2]; + int fail; + int (*fp) (void); + + mtrace (); + + h[0] = dlopen ("ltglobmod1.so", RTLD_LAZY); + if (h[0] == NULL) + { + printf ("%s: cannot open %s: %s", + __FUNCTION__, "ltglobmod1.so", dlerror ()); + exit (EXIT_FAILURE); + } + h[1] = dlopen ("ltglobmod2.so", RTLD_LAZY); + if (h[1] == NULL) + { + printf ("%s: cannot open %s: %s", + __FUNCTION__, "ltglobmod2.so", dlerror ()); + exit (EXIT_FAILURE); + } + + puts ("loaded \"ltglobmod1.so\" without RTLD_GLOBAL"); + + fp = dlsym (h[1], "foo"); + if (fp == NULL) + { + printf ("cannot get address of `foo': %s", dlerror ()); + exit (EXIT_FAILURE); + } + + fail = fp (); + + puts ("back in main"); + + dlclose (h[1]); + dlclose (h[0]); + + return fail; +} diff --git a/libc/elf/ldconfig.c b/libc/elf/ldconfig.c new file mode 100644 index 000000000..5b9a5b174 --- /dev/null +++ b/libc/elf/ldconfig.c @@ -0,0 +1,1297 @@ +/* Copyright (C) 1999-2004, 2005, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Andreas Jaeger <aj@suse.de>, 1999. + + 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. + + 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 General Public License for more details. + + 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> +#include <argp.h> +#include <dirent.h> +#include <elf.h> +#include <error.h> +#include <errno.h> +#include <inttypes.h> +#include <libintl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <glob.h> +#include <libgen.h> + +#include <ldconfig.h> +#include <dl-cache.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" +#endif + +/* Get libc version number. */ +#include <version.h> + +#define PACKAGE _libc_intl_domainname + +static const struct +{ + const char *name; + int flag; +} lib_types[] = +{ + {"libc4", FLAG_LIBC4}, + {"libc5", FLAG_ELF_LIBC5}, + {"libc6", FLAG_ELF_LIBC6}, + {"glibc2", FLAG_ELF_LIBC6} +}; + + +/* List of directories to handle. */ +struct dir_entry +{ + char *path; + int flag; + ino64_t ino; + dev_t dev; + struct dir_entry *next; +}; + +/* The list is unsorted, contains no duplicates. Entries are added at + the end. */ +static struct dir_entry *dir_entries; + +/* Flags for different options. */ +/* Print Cache. */ +static int opt_print_cache; + +/* Be verbose. */ +int opt_verbose; + +/* Format to support. */ +/* 0: only libc5/glibc2; 1: both; 2: only glibc 2.2. */ +int opt_format = 1; + +/* Build cache. */ +static int opt_build_cache = 1; + +/* Generate links. */ +static int opt_link = 1; + +/* Only process directories specified on the command line. */ +static int opt_only_cline; + +/* Path to root for chroot. */ +static char *opt_chroot; + +/* Manually link given shared libraries. */ +static int opt_manual_link; + +/* Cache file to use. */ +static char *cache_file; + +/* Configuration file. */ +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 *) + = print_version; + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { "print-cache", 'p', NULL, 0, N_("Print cache"), 0}, + { "verbose", 'v', NULL, 0, N_("Generate verbose messages"), 0}, + { NULL, 'N', NULL, 0, N_("Don't build cache"), 0}, + { NULL, 'X', NULL, 0, N_("Don't generate links"), 0}, + { NULL, 'r', N_("ROOT"), 0, N_("Change to and use ROOT as root directory"), 0}, + { NULL, 'C', N_("CACHE"), 0, N_("Use CACHE as cache file"), 0}, + { NULL, 'f', N_("CONF"), 0, N_("Use CONF as configuration file"), 0}, + { NULL, 'n', NULL, 0, N_("Only process directories specified on the command line. Don't build cache."), 0}, + { NULL, 'l', NULL, 0, N_("Manually link individual libraries."), 0}, + { "format", 'c', N_("FORMAT"), 0, N_("Format to use: new, old or compat (default)"), 0}, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +#define PROCINFO_CLASS static +#include <dl-procinfo.c> + +/* Short description of program. */ +static const char doc[] = N_("Configure Dynamic Linker Run Time Bindings."); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, NULL, doc, NULL, NULL, NULL +}; + +/* Check if string corresponds to an important hardware capability or + a platform. */ +static int +is_hwcap_platform (const char *name) +{ + int hwcap_idx = _dl_string_hwcap (name); + + if (hwcap_idx != -1 && ((1 << hwcap_idx) & hwcap_mask)) + return 1; + + hwcap_idx = _dl_string_platform (name); + if (hwcap_idx != -1) + return 1; + + 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; +} + +/* Get hwcap (including platform) encoding of path. */ +static uint64_t +path_hwcap (const char *path) +{ + char *str = xstrdup (path); + char *ptr; + uint64_t hwcap = 0; + uint64_t h; + + size_t len; + + len = strlen (str); + if (str[len] == '/') + str[len] = '\0'; + + /* Search pathname from the end and check for hwcap strings. */ + for (;;) + { + ptr = strrchr (str, '/'); + + if (ptr == NULL) + break; + + h = _dl_string_hwcap (ptr + 1); + + if (h == (uint64_t) -1) + { + h = _dl_string_platform (ptr + 1); + if (h == (uint64_t) -1) + { + 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; + } + } + hwcap += 1ULL << h; + + /* Search the next part of the path. */ + *ptr = '\0'; + } + + free (str); + return hwcap; +} + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'C': + cache_file = arg; + break; + case 'f': + config_file = arg; + break; + case 'l': + opt_manual_link = 1; + break; + case 'N': + opt_build_cache = 0; + break; + case 'n': + opt_build_cache = 0; + opt_only_cline = 1; + break; + case 'p': + opt_print_cache = 1; + break; + case 'r': + opt_chroot = arg; + break; + case 'v': + opt_verbose = 1; + break; + case 'X': + opt_link = 0; + break; + case 'c': + if (strcmp (arg, "old") == 0) + opt_format = 0; + else if (strcmp (arg, "compat") == 0) + opt_format = 1; + else if (strcmp (arg, "new") == 0) + opt_format = 2; + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state) +{ + fprintf (stream, "ldconfig (GNU %s) %s\n", PACKAGE, VERSION); + fprintf (stream, gettext ("\ +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\ +"), "2006"); + fprintf (stream, gettext ("Written by %s.\n"), + "Andreas Jaeger"); +} + +/* Add a single directory entry. */ +static void +add_single_dir (struct dir_entry *entry, int verbose) +{ + struct dir_entry *ptr, *prev; + + ptr = dir_entries; + prev = ptr; + while (ptr != NULL) + { + /* Check for duplicates. */ + if (ptr->ino == entry->ino && ptr->dev == entry->dev) + { + if (opt_verbose && verbose) + error (0, 0, _("Path `%s' given more than once"), entry->path); + /* Use the newer information. */ + ptr->flag = entry->flag; + free (entry->path); + free (entry); + break; + } + prev = ptr; + ptr = ptr->next; + } + /* Is this the first entry? */ + if (ptr == NULL && dir_entries == NULL) + dir_entries = entry; + else if (ptr == NULL) + prev->next = entry; +} + +/* Add one directory to the list of directories to process. */ +static void +add_dir (const char *line) +{ + unsigned int i; + struct dir_entry *entry = xmalloc (sizeof (struct dir_entry)); + entry->next = NULL; + + /* Search for an '=' sign. */ + entry->path = xstrdup (line); + char *equal_sign = strchr (entry->path, '='); + if (equal_sign) + { + *equal_sign = '\0'; + ++equal_sign; + entry->flag = FLAG_ANY; + for (i = 0; i < sizeof (lib_types) / sizeof (lib_types[0]); ++i) + if (strcmp (equal_sign, lib_types[i].name) == 0) + { + entry->flag = lib_types[i].flag; + break; + } + if (entry->flag == FLAG_ANY) + error (0, 0, _("%s is not a known library type"), equal_sign); + } + else + { + entry->flag = FLAG_ANY; + } + + /* Canonify path: for now only remove leading and trailing + whitespace and the trailing slashes slashes. */ + i = strlen (entry->path) - 1; + + while (isspace (entry->path[i]) && i > 0) + entry->path[i--] = '\0'; + + while (entry->path[i] == '/' && i > 0) + entry->path[i--] = '\0'; + + char *path = entry->path; + if (opt_chroot) + path = chroot_canon (opt_chroot, path); + + struct stat64 stat_buf; + if (path == NULL || stat64 (path, &stat_buf)) + { + if (opt_verbose) + error (0, errno, _("Can't stat %s"), entry->path); + free (entry->path); + free (entry); + } + else + { + entry->ino = stat_buf.st_ino; + entry->dev = stat_buf.st_dev; + + add_single_dir (entry, 1); + } + + if (opt_chroot) + free (path); +} + + +static int +chroot_stat (const char *real_path, const char *path, struct stat64 *st) +{ + int ret; + char *canon_path; + + if (!opt_chroot) + return stat64 (real_path, st); + + ret = lstat64 (real_path, st); + if (ret || !S_ISLNK (st->st_mode)) + return ret; + + canon_path = chroot_canon (opt_chroot, path); + if (canon_path == NULL) + return -1; + + ret = stat64 (canon_path, st); + free (canon_path); + return ret; +} + +/* Create a symbolic link from soname to libname in directory path. */ +static void +create_links (const char *real_path, const char *path, const char *libname, + const char *soname) +{ + char *full_libname, *full_soname; + char *real_full_libname, *real_full_soname; + struct stat64 stat_lib, stat_so, lstat_so; + int do_link = 1; + int do_remove = 1; + /* XXX: The logics in this function should be simplified. */ + + /* Get complete path. */ + full_libname = alloca (strlen (path) + strlen (libname) + 2); + full_soname = alloca (strlen (path) + strlen (soname) + 2); + sprintf (full_libname, "%s/%s", path, libname); + sprintf (full_soname, "%s/%s", path, soname); + if (opt_chroot) + { + real_full_libname = alloca (strlen (real_path) + strlen (libname) + 2); + real_full_soname = alloca (strlen (real_path) + strlen (soname) + 2); + sprintf (real_full_libname, "%s/%s", real_path, libname); + sprintf (real_full_soname, "%s/%s", real_path, soname); + } + else + { + real_full_libname = full_libname; + real_full_soname = full_soname; + } + + /* Does soname already exist and point to the right library? */ + if (chroot_stat (real_full_soname, full_soname, &stat_so) == 0) + { + if (chroot_stat (real_full_libname, full_libname, &stat_lib)) + { + error (0, 0, _("Can't stat %s\n"), full_libname); + return; + } + if (stat_lib.st_dev == stat_so.st_dev + && stat_lib.st_ino == stat_so.st_ino) + /* Link is already correct. */ + do_link = 0; + else if (lstat64 (full_soname, &lstat_so) == 0 + && !S_ISLNK (lstat_so.st_mode)) + { + error (0, 0, _("%s is not a symbolic link\n"), full_soname); + do_link = 0; + do_remove = 0; + } + } + else if (lstat64 (real_full_soname, &lstat_so) != 0 + || !S_ISLNK (lstat_so.st_mode)) + /* Unless it is a stale symlink, there is no need to remove. */ + do_remove = 0; + + if (opt_verbose) + printf ("\t%s -> %s", soname, libname); + + if (do_link && opt_link) + { + /* Remove old link. */ + if (do_remove) + if (unlink (real_full_soname)) + { + error (0, 0, _("Can't unlink %s"), full_soname); + do_link = 0; + } + /* Create symbolic link. */ + if (do_link && symlink (libname, real_full_soname)) + { + error (0, 0, _("Can't link %s to %s"), full_soname, libname); + do_link = 0; + } + if (opt_verbose) + { + if (do_link) + fputs (_(" (changed)\n"), stdout); + else + fputs (_(" (SKIPPED)\n"), stdout); + } + } + else if (opt_verbose) + fputs ("\n", stdout); +} + +/* Manually link the given library. */ +static void +manual_link (char *library) +{ + char *path; + char *real_path; + char *real_library; + char *libname; + char *soname; + struct stat64 stat_buf; + int flag; + unsigned int osversion; + + /* Prepare arguments for create_links call. Split library name in + directory and filename first. Since path is allocated, we've got + to be careful to free at the end. */ + path = xstrdup (library); + libname = strrchr (path, '/'); + + if (libname) + { + /* Successfully split names. Check if path is just "/" to avoid + an empty path. */ + if (libname == path) + { + libname = library + 1; + path = xrealloc (path, 2); + strcpy (path, "/"); + } + else + { + *libname = '\0'; + ++libname; + } + } + else + { + /* There's no path, construct one. */ + libname = library; + path = xrealloc (path, 2); + strcpy (path, "."); + } + + if (opt_chroot) + { + real_path = chroot_canon (opt_chroot, path); + if (real_path == NULL) + { + error (0, errno, _("Can't find %s"), path); + free (path); + return; + } + real_library = alloca (strlen (real_path) + strlen (libname) + 2); + sprintf (real_library, "%s/%s", real_path, libname); + } + else + { + real_path = path; + real_library = library; + } + + /* Do some sanity checks first. */ + if (lstat64 (real_library, &stat_buf)) + { + error (0, errno, _("Can't lstat %s"), library); + free (path); + return; + } + /* We don't want links here! */ + else if (!S_ISREG (stat_buf.st_mode)) + { + error (0, 0, _("Ignored file %s since it is not a regular file."), + library); + free (path); + return; + } + if (process_file (real_library, library, libname, &flag, &osversion, + &soname, 0)) + { + error (0, 0, _("No link created since soname could not be found for %s"), + library); + free (path); + return; + } + create_links (real_path, path, libname, soname); + free (soname); + free (path); +} + + +/* Read a whole directory and search for libraries. + The purpose is two-fold: + - search for libraries which will be added to the cache + - create symbolic links to the soname for each library + + This has to be done separatly for each directory. + + To keep track of which libraries to add to the cache and which + links to create, we save a list of all libraries. + + The algorithm is basically: + for all libraries in the directory do + get soname of library + if soname is already in list + if new library is newer, replace entry + otherwise ignore this library + otherwise add library to list + + For example, if the two libraries libxy.so.1.1 and libxy.so.1.2 + exist and both have the same soname, e.g. libxy.so, a symbolic link + is created from libxy.so.1.2 (the newer one) to libxy.so. + libxy.so.1.2 and libxy.so are added to the cache - but not + libxy.so.1.1. */ + +/* Information for one library. */ +struct dlib_entry +{ + char *name; + char *soname; + int flag; + int is_link; + unsigned int osversion; + struct dlib_entry *next; +}; + + +static void +search_dir (const struct dir_entry *entry) +{ + DIR *dir; + struct dirent64 *direntry; + char *file_name, *dir_name, *real_file_name, *real_name; + int file_name_len, real_file_name_len, len; + char *soname; + struct dlib_entry *dlibs; + struct dlib_entry *dlib_ptr; + struct stat64 lstat_buf, stat_buf; + int is_link, is_dir; + uint64_t hwcap = path_hwcap (entry->path); + unsigned int osversion; + + file_name_len = PATH_MAX; + file_name = alloca (file_name_len); + + dlibs = NULL; + + if (opt_verbose) + { + if (hwcap != 0) + printf ("%s: (hwcap: %#.16" PRIx64 ")\n", entry->path, hwcap); + else + printf ("%s:\n", entry->path); + } + + if (opt_chroot) + { + dir_name = chroot_canon (opt_chroot, entry->path); + real_file_name_len = PATH_MAX; + real_file_name = alloca (real_file_name_len); + } + else + { + dir_name = entry->path; + real_file_name_len = 0; + real_file_name = file_name; + } + + if (dir_name == NULL || (dir = opendir (dir_name)) == NULL) + { + if (opt_verbose) + error (0, errno, _("Can't open directory %s"), entry->path); + if (opt_chroot && dir_name) + free (dir_name); + return; + } + + while ((direntry = readdir64 (dir)) != NULL) + { + int flag; +#ifdef _DIRENT_HAVE_D_TYPE + /* We only look at links and regular files. */ + if (direntry->d_type != DT_UNKNOWN + && direntry->d_type != DT_LNK + && direntry->d_type != DT_REG + && direntry->d_type != DT_DIR) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + /* Does this file look like a shared library or is it a hwcap + subdirectory? The dynamic linker is also considered as + shared library. */ + if (((strncmp (direntry->d_name, "lib", 3) != 0 + && strncmp (direntry->d_name, "ld-", 3) != 0) + || strstr (direntry->d_name, ".so") == NULL) + && ( +#ifdef _DIRENT_HAVE_D_TYPE + direntry->d_type == DT_REG || +#endif + !is_hwcap_platform (direntry->d_name))) + continue; + 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); + if (len > file_name_len) + { + file_name_len = len + 1; + file_name = alloca (file_name_len); + if (!opt_chroot) + real_file_name = file_name; + } + sprintf (file_name, "%s/%s", entry->path, direntry->d_name); + if (opt_chroot) + { + len = strlen (dir_name) + strlen (direntry->d_name); + if (len > real_file_name_len) + { + real_file_name_len = len + 1; + real_file_name = alloca (real_file_name_len); + } + sprintf (real_file_name, "%s/%s", dir_name, direntry->d_name); + } +#ifdef _DIRENT_HAVE_D_TYPE + if (direntry->d_type != DT_UNKNOWN) + lstat_buf.st_mode = DTTOIF (direntry->d_type); + else +#endif + if (__builtin_expect (lstat64 (real_file_name, &lstat_buf), 0)) + { + error (0, errno, _("Cannot lstat %s"), file_name); + continue; + } + + is_link = S_ISLNK (lstat_buf.st_mode); + if (is_link) + { + /* In case of symlink, we check if the symlink refers to + a directory. */ + if (__builtin_expect (stat64 (real_file_name, &stat_buf), 0)) + { + if (opt_verbose) + error (0, errno, _("Cannot stat %s"), file_name); + + /* Remove stale symlinks. */ + if (strstr (direntry->d_name, ".so.")) + unlink (real_file_name); + continue; + } + is_dir = S_ISDIR (stat_buf.st_mode); + } + else + is_dir = S_ISDIR (lstat_buf.st_mode); + + if (is_dir && is_hwcap_platform (direntry->d_name)) + { + /* Handle subdirectory later. */ + struct dir_entry *new_entry; + + new_entry = xmalloc (sizeof (struct dir_entry)); + new_entry->path = xstrdup (file_name); + new_entry->flag = entry->flag; + new_entry->next = NULL; + if (is_link) + { + new_entry->ino = stat_buf.st_ino; + new_entry->dev = stat_buf.st_dev; + } + else + { +#ifdef _DIRENT_HAVE_D_TYPE + /* We have filled in lstat only #ifndef + _DIRENT_HAVE_D_TYPE. Fill it in if needed. */ + if (direntry->d_type != DT_UNKNOWN + && __builtin_expect (lstat64 (real_file_name, &lstat_buf), + 0)) + { + error (0, errno, _("Cannot lstat %s"), file_name); + free (new_entry->path); + free (new_entry); + continue; + } +#endif + + new_entry->ino = lstat_buf.st_ino; + new_entry->dev = lstat_buf.st_dev; + } + add_single_dir (new_entry, 0); + continue; + } + else if (!S_ISREG (lstat_buf.st_mode) && !is_link) + continue; + + if (opt_chroot && is_link) + { + real_name = chroot_canon (opt_chroot, file_name); + if (real_name == NULL) + { + if (strstr (file_name, ".so") == NULL) + error (0, 0, _("Input file %s not found.\n"), file_name); + continue; + } + } + else + real_name = real_file_name; + + if (process_file (real_name, file_name, direntry->d_name, &flag, + &osversion, &soname, is_link)) + { + if (real_name != real_file_name) + free (real_name); + continue; + } + + + /* A link may just point to itself. */ + if (is_link) + { + /* If the path the link points to isn't its soname and it is not + .so symlink for ld(1) only, we treat it as a normal file. */ + const char *real_base_name = basename (real_file_name); + + if (strcmp (real_base_name, soname) != 0) + { + len = strlen (real_base_name); + if (len < strlen (".so") + || strcmp (real_base_name + len - strlen (".so"), ".so") != 0 + || strncmp (real_base_name, soname, len) != 0) + is_link = 0; + } + } + + if (real_name != real_file_name) + free (real_name); + + if (is_link) + { + free (soname); + soname = xstrdup (direntry->d_name); + } + + if (flag == FLAG_ELF + && (entry->flag == FLAG_ELF_LIBC5 + || entry->flag == FLAG_ELF_LIBC6)) + flag = entry->flag; + /* Some sanity checks to print warnings. */ + if (opt_verbose) + { + if (flag == FLAG_ELF_LIBC5 && entry->flag != FLAG_ELF_LIBC5 + && entry->flag != FLAG_ANY) + error (0, 0, _("libc5 library %s in wrong directory"), file_name); + if (flag == FLAG_ELF_LIBC6 && entry->flag != FLAG_ELF_LIBC6 + && entry->flag != FLAG_ANY) + error (0, 0, _("libc6 library %s in wrong directory"), file_name); + if (flag == FLAG_LIBC4 && entry->flag != FLAG_LIBC4 + && entry->flag != FLAG_ANY) + error (0, 0, _("libc4 library %s in wrong directory"), file_name); + } + + /* Add library to list. */ + for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next) + { + /* Is soname already in list? */ + if (strcmp (dlib_ptr->soname, soname) == 0) + { + /* Prefer a file to a link, otherwise check which one + is newer. */ + if ((!is_link && dlib_ptr->is_link) + || (is_link == dlib_ptr->is_link + && _dl_cache_libcmp (dlib_ptr->name, direntry->d_name) < 0)) + { + /* It's newer - add it. */ + /* Flag should be the same - sanity check. */ + if (dlib_ptr->flag != flag) + { + if (dlib_ptr->flag == FLAG_ELF + && (flag == FLAG_ELF_LIBC5 || flag == FLAG_ELF_LIBC6)) + dlib_ptr->flag = flag; + else if ((dlib_ptr->flag == FLAG_ELF_LIBC5 + || dlib_ptr->flag == FLAG_ELF_LIBC6) + && flag == FLAG_ELF) + dlib_ptr->flag = flag; + else + error (0, 0, _("libraries %s and %s in directory %s have same soname but different type."), + dlib_ptr->name, direntry->d_name, entry->path); + } + free (dlib_ptr->name); + dlib_ptr->osversion = osversion; + dlib_ptr->name = xstrdup (direntry->d_name); + dlib_ptr->is_link = is_link; + } + /* Don't add this library, abort loop. */ + /* Also free soname, since it's dynamically allocated. */ + free (soname); + break; + } + } + /* Add the library if it's not already in. */ + if (dlib_ptr == NULL) + { + dlib_ptr = (struct dlib_entry *)xmalloc (sizeof (struct dlib_entry)); + dlib_ptr->name = xstrdup (direntry->d_name); + dlib_ptr->flag = flag; + dlib_ptr->osversion = osversion; + dlib_ptr->soname = soname; + dlib_ptr->is_link = is_link; + /* Add at head of list. */ + dlib_ptr->next = dlibs; + dlibs = dlib_ptr; + } + } + + closedir (dir); + + /* Now dlibs contains a list of all libs - add those to the cache + and created all symbolic links. */ + for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next) + { + /* Don't create links to links. */ + if (dlib_ptr->is_link == 0) + create_links (dir_name, entry->path, dlib_ptr->name, + dlib_ptr->soname); + if (opt_build_cache) + add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag, + dlib_ptr->osversion, hwcap); + } + + /* Free all resources. */ + while (dlibs) + { + dlib_ptr = dlibs; + free (dlib_ptr->soname); + free (dlib_ptr->name); + dlibs = dlibs->next; + free (dlib_ptr); + } + + if (opt_chroot && dir_name) + free (dir_name); +} + +/* Search through all libraries. */ +static void +search_dirs (void) +{ + struct dir_entry *entry; + + for (entry = dir_entries; entry != NULL; entry = entry->next) + search_dir (entry); + + /* Free all allocated memory. */ + while (dir_entries) + { + entry = dir_entries; + dir_entries = dir_entries->next; + free (entry->path); + free (entry); + } +} + + +static void parse_conf_include (const char *config_file, unsigned int lineno, + bool do_chroot, const char *pattern); + +/* Parse configuration file. */ +static void +parse_conf (const char *filename, bool do_chroot) +{ + FILE *file = NULL; + char *line = NULL; + const char *canon; + size_t len = 0; + unsigned int lineno; + + if (do_chroot && opt_chroot) + { + canon = chroot_canon (opt_chroot, filename); + if (canon) + file = fopen (canon, "r"); + else + canon = filename; + } + else + { + canon = filename; + file = fopen (filename, "r"); + } + + if (file == NULL) + { + error (0, errno, _("Can't open configuration file %s"), canon); + if (canon != filename) + free ((char *) canon); + return; + } + + /* No threads use this stream. */ + __fsetlocking (file, FSETLOCKING_BYCALLER); + + if (canon != filename) + free ((char *) canon); + + lineno = 0; + do + { + ssize_t n = getline (&line, &len, file); + if (n < 0) + break; + + ++lineno; + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + + /* Because the file format does not know any form of quoting we + can search forward for the next '#' character and if found + make it terminating the line. */ + *strchrnul (line, '#') = '\0'; + + /* Remove leading whitespace. NUL is no whitespace character. */ + char *cp = line; + while (isspace (*cp)) + ++cp; + + /* If the line is blank it is ignored. */ + if (cp[0] == '\0') + continue; + + if (!strncmp (cp, "include", 7) && isblank (cp[7])) + { + char *dir; + cp += 8; + while ((dir = strsep (&cp, " \t")) != NULL) + if (dir[0] != '\0') + parse_conf_include (filename, lineno, do_chroot, dir); + } + 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); + } + while (!feof_unlocked (file)); + + /* Free buffer and close file. */ + free (line); + fclose (file); +} + +/* Handle one word in an `include' line, a glob pattern of additional + config files to read. */ +static void +parse_conf_include (const char *config_file, unsigned int lineno, + bool do_chroot, const char *pattern) +{ + if (opt_chroot && pattern[0] != '/') + error (EXIT_FAILURE, 0, + _("need absolute file name for configuration file when using -r")); + + char *copy = NULL; + if (pattern[0] != '/' && strchr (config_file, '/') != NULL) + { + if (asprintf (©, "%s/%s", dirname (strdupa (config_file)), + pattern) < 0) + error (EXIT_FAILURE, 0, _("memory exhausted")); + pattern = copy; + } + + glob64_t gl; + int result; + if (do_chroot && opt_chroot) + { + char *canon = chroot_canon (opt_chroot, pattern); + result = glob64 (canon ?: pattern, 0, NULL, &gl); + free (canon); + } + else + result = glob64 (pattern, 0, NULL, &gl); + + switch (result) + { + case 0: + for (size_t i = 0; i < gl.gl_pathc; ++i) + parse_conf (gl.gl_pathv[i], false); + globfree64 (&gl); + break; + + case GLOB_NOMATCH: + break; + + case GLOB_NOSPACE: + errno = ENOMEM; + case GLOB_ABORTED: + if (opt_verbose) + error (0, errno, _("%s:%u: cannot read directory %s"), + config_file, lineno, pattern); + break; + + default: + abort (); + break; + } + + if (copy) + free (copy); +} + +/* Honour LD_HWCAP_MASK. */ +static void +set_hwcap (void) +{ + char *mask = getenv ("LD_HWCAP_MASK"); + + if (mask) + hwcap_mask = strtoul (mask, NULL, 0); +} + + +int +main (int argc, char **argv) +{ + int remaining; + + /* Parse and process arguments. */ + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + /* Remaining arguments are additional directories if opt_manual_link + is not set. */ + if (remaining != argc && !opt_manual_link) + { + int i; + for (i = remaining; i < argc; ++i) + if (opt_build_cache && argv[i][0] != '/') + error (EXIT_FAILURE, 0, + _("relative path `%s' used to build cache"), + argv[i]); + else + 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 = rawmemchr (opt_chroot, '\0'); + while (endp > opt_chroot && endp[-1] == '/') + --endp; + *endp = '\0'; + if (endp == opt_chroot) + opt_chroot = NULL; + + if (opt_chroot) + { + /* It is faster to use chroot if we can. */ + if (!chroot (opt_chroot)) + { + if (chdir ("/")) + error (EXIT_FAILURE, errno, _("Can't chdir to /")); + opt_chroot = NULL; + } + } + } + + if (cache_file == NULL) + { + cache_file = alloca (strlen (LD_SO_CACHE) + 1); + strcpy (cache_file, LD_SO_CACHE); + } + + if (config_file == NULL) + config_file = LD_SO_CONF; + + if (opt_print_cache) + { + if (opt_chroot) + { + char *p = chroot_canon (opt_chroot, cache_file); + if (p == NULL) + error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), + cache_file); + cache_file = p; + } + print_cache (cache_file); + if (opt_chroot) + free (cache_file); + exit (0); + } + + if (opt_chroot) + { + /* Canonicalize the directory name of cache_file, not cache_file, + because we'll rename a temporary cache file to it. */ + char *p = strrchr (cache_file, '/'); + char *canon = chroot_canon (opt_chroot, + p ? (*p = '\0', cache_file) : "/"); + + if (canon == NULL) + { + error (EXIT_FAILURE, errno, + _("Can't open cache file directory %s\n"), + p ? cache_file : "/"); + } + + if (p) + ++p; + else + p = cache_file; + + cache_file = alloca (strlen (canon) + strlen (p) + 2); + sprintf (cache_file, "%s/%s", canon, p); + free (canon); + } + + if (opt_manual_link) + { + /* Link all given libraries manually. */ + int i; + + for (i = remaining; i < argc; ++i) + manual_link (argv[i]); + + exit (0); + } + + + if (opt_build_cache) + init_cache (); + + if (!opt_only_cline) + { + parse_conf (config_file, true); + + /* Always add the standard search paths. */ + add_system_dir (SLIBDIR); + if (strcmp (SLIBDIR, LIBDIR)) + add_system_dir (LIBDIR); + } + + search_dirs (); + + if (opt_build_cache) + save_cache (cache_file); + + return 0; +} diff --git a/libc/elf/ldd.bash.in b/libc/elf/ldd.bash.in new file mode 100644 index 000000000..d1591a578 --- /dev/null +++ b/libc/elf/ldd.bash.in @@ -0,0 +1,204 @@ +#! @BASH@ +# 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 +# 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 is the `ldd' command, which lists what shared libraries are +# used by given dynamically-linked executables. It works by invoking the +# run-time dynamic linker as a command and setting the environment +# variable LD_TRACE_LOADED_OBJECTS to a non-empty value. + +# We should be able to find the translation right at the beginning. +TEXTDOMAIN=libc +TEXTDOMAINDIR=@TEXTDOMAINDIR@ + +RTLDLIST=@RTLD@ +warn= +bind_now= +verbose= + +while test $# -gt 0; do + case "$1" in + --vers | --versi | --versio | --version) + echo 'ldd (GNU libc) @VERSION@' + 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. +" "2006" + printf $"Written by %s and %s. +" "Roland McGrath" "Ulrich Drepper" + exit 0 + ;; + --h | --he | --hel | --help) + echo $"Usage: ldd [OPTION]... FILE... + --help print this help and exit + --version print version information and exit + -d, --data-relocs process data relocations + -r, --function-relocs process data and function relocations + -u, --unused print unused direct dependencies + -v, --verbose print all information +For bug reporting instructions, please see: +<http://www.gnu.org/software/libc/bugs.html>." + exit 0 + ;; + -d | --d | --da | --dat | --data | --data- | --data-r | --data-re | \ + --data-rel | --data-relo | --data-reloc | --data-relocs) + warn=yes + shift + ;; + -r | --f | --fu | --fun | --func | --funct | --functi | --functio | \ + --function | --function- | --function-r | --function-re | --function-rel | \ + --function-relo | --function-reloc | --function-relocs) + warn=yes + bind_now=yes + shift + ;; + -v | --verb | --verbo | --verbos | --verbose) + verbose=yes + shift + ;; + -u | --u | --un | --unu | --unus | --unuse | --unused) + unused=yes + shift + ;; + --v | --ve | --ver) + echo >&2 $"ldd: option \`$1' is ambiguous" + exit 1 + ;; + --) # Stop option processing. + shift; break + ;; + -*) + echo >&2 'ldd:' $"unrecognized option" "\`$1'" + echo >&2 $"Try \`ldd --help' for more information." + exit 1 + ;; + *) + break + ;; + esac +done + +nonelf () +{ + # Maybe extra code for non-ELF binaries. + return 1; +} + +add_env="LD_TRACE_LOADED_OBJECTS=1 LD_WARN=$warn LD_BIND_NOW=$bind_now" +add_env="$add_env LD_VERBOSE=$verbose" +if test "$unused" = yes; then + add_env="$add_env LD_DEBUG=\"$LD_DEBUG${LD_DEBUG:+,}unused\"" +fi + +# The following use of cat is needed to make ldd work in SELinux +# environments where the executed program might not have permissions +# to write to the console/tty. But only bash 3.x supports the pipefail +# option, and we don't bother to handle the case for older bash versions. +if set -o pipefail 2> /dev/null; then + try_trace() { + eval $add_env '"$@"' | cat + } +else + try_trace() { + eval $add_env '"$@"' + } +fi + +case $# in +0) + echo >&2 'ldd:' $"missing file arguments" + echo >&2 $"Try \`ldd --help' for more information." + exit 1 + ;; +1) + single_file=t + ;; +*) + single_file=f + ;; +esac + +result=0 +for file do + # We don't list the file name when there is only one. + test $single_file = t || echo "${file}:" + case $file in + */*) : + ;; + *) file=./$file + ;; + esac + 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"` + ret=$? + case $ret in + [02]) RTLD=${rtld}; break;; + esac + fi + done + case $ret in + 0) + # If the program exits with exit code 5, it means the process has been + # invoked with __libc_enable_secure. Fall back to running it through + # the dynamic linker. + try_trace "$file" + rc=$? + if [ $rc = 5 ]; then + try_trace "$RTLD" "$file" + rc=$? + fi + [ $rc = 0 ] || result=1 + ;; + 1) + # This can be a non-ELF binary or no binary at all. + nonelf "$file" || { + echo $" not a dynamic executable" + result=1 + } + ;; + 2) + try_trace "$RTLD" "$file" || result=1 + ;; + *) + echo 'ldd:' ${RTLD} $"exited with unknown exit code" "($ret)" >&2 + exit 1 + ;; + esac + else + echo 'ldd:' $"error: you do not have read permission for" "\`$file'" >&2 + result=1 + fi +done + +exit $result +# Local Variables: +# mode:ksh +# End: diff --git a/libc/elf/link.h b/libc/elf/link.h new file mode 100644 index 000000000..076531d6e --- /dev/null +++ b/libc/elf/link.h @@ -0,0 +1,194 @@ +/* Data structure for communication from the run-time dynamic linker for + loaded ELF shared objects. + 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 + 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. */ + +#ifndef _LINK_H +#define _LINK_H 1 + +#include <features.h> +#include <elf.h> +#include <dlfcn.h> +#include <sys/types.h> + +/* We use this macro to refer to ELF types independent of the native wordsize. + `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */ +#define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type) +#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t) +#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 + dynamic section has a DT_DEBUG element, the run-time linker sets that + element's value to the address where this structure can be found. */ + +struct r_debug + { + int r_version; /* Version number for this protocol. */ + + struct link_map *r_map; /* Head of the chain of loaded objects. */ + + /* This is the address of a function internal to the run-time linker, + that will always be called when the linker begins to map in a + library or unmap it, and again when the mapping change is complete. + The debugger can set a breakpoint at this address if it wants to + notice shared object mapping changes. */ + ElfW(Addr) r_brk; + enum + { + /* This state value describes the mapping change taking place when + the `r_brk' address is called. */ + RT_CONSISTENT, /* Mapping change is complete. */ + RT_ADD, /* Beginning to add a new object. */ + RT_DELETE /* Beginning to remove an object mapping. */ + } r_state; + + ElfW(Addr) r_ldbase; /* Base address the linker is loaded at. */ + }; + +/* This is the instance of that structure used by the dynamic linker. */ +extern struct r_debug _r_debug; + +/* This symbol refers to the "dynamic structure" in the `.dynamic' section + of whatever module refers to `_DYNAMIC'. So, to find its own + `struct r_debug', a program could do: + for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn) + if (dyn->d_tag == DT_DEBUG) + r_debug = (struct r_debug *) dyn->d_un.d_ptr; + */ +extern ElfW(Dyn) _DYNAMIC[]; + +/* Structure describing a loaded shared object. The `l_next' and `l_prev' + members form a chain of all the shared objects loaded at startup. + + These data structures exist in space used by the run-time dynamic linker; + modifying them may have disastrous results. */ + +struct link_map + { + /* These first few members are part of the protocol with the debugger. + This is the same format used in SVR4. */ + + ElfW(Addr) l_addr; /* Base address shared object is loaded at. */ + char *l_name; /* Absolute file name object was found in. */ + ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. */ + struct link_map *l_next, *l_prev; /* Chain of loaded objects. */ + }; + +#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; + const char *dlpi_name; + const ElfW(Phdr) *dlpi_phdr; + ElfW(Half) dlpi_phnum; + + /* 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 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 *, + 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 + +#endif + +#endif /* link.h */ diff --git a/libc/elf/loadfail.c b/libc/elf/loadfail.c new file mode 100644 index 000000000..7531aa958 --- /dev/null +++ b/libc/elf/loadfail.c @@ -0,0 +1,42 @@ +#include <dlfcn.h> +#include <error.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + void *su[5]; + void *h; + int n; + + mtrace (); + + if ((su[0] = dlopen ("testobj1.so", RTLD_GLOBAL | RTLD_NOW)) == NULL + || (su[1] = dlopen ("testobj2.so", RTLD_GLOBAL | RTLD_NOW)) == NULL + || (su[2] = dlopen ("testobj3.so", RTLD_GLOBAL | RTLD_NOW)) == NULL + || (su[3] = dlopen ("testobj4.so", RTLD_GLOBAL | RTLD_NOW)) == NULL + || (su[4] = dlopen ("testobj5.so", RTLD_GLOBAL | RTLD_NOW)) == NULL) + error (EXIT_FAILURE, 0, "failed to load shared object: %s", dlerror ()); + + h = dlopen ("failobj.so", RTLD_GLOBAL | RTLD_NOW); + + printf ("h = %p, %s\n", h, h == NULL ? "ok" : "fail"); + + for (n = 0; n < 5; ++n) + if (dlclose (su[n]) != 0) + { + printf ("failed to unload su[%d]: %s\n", n, dlerror ()); + exit (EXIT_FAILURE); + } + + return h != NULL; +} + +extern int foo (int a); +int +foo (int a) +{ + return 10; +} diff --git a/libc/elf/loadtest.c b/libc/elf/loadtest.c new file mode 100644 index 000000000..727469b49 --- /dev/null +++ b/libc/elf/loadtest.c @@ -0,0 +1,202 @@ +#include <assert.h> +#include <dlfcn.h> +#include <errno.h> +#include <error.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +/* How many load/unload operations do we do. */ +#define TEST_ROUNDS 1000 + + +static struct +{ + /* Name of the module. */ + const char *name; + /* The handle. */ + void *handle; +} testobjs[] = +{ + { "testobj1.so", NULL }, + { "testobj2.so", NULL }, + { "testobj3.so", NULL }, + { "testobj4.so", NULL }, + { "testobj5.so", NULL }, + { "testobj6.so", NULL }, +}; +#define NOBJS (sizeof (testobjs) / sizeof (testobjs[0])) + + +static const struct +{ + /* Name of a function to call. */ + const char *fname; + /* Index in status and handle array. */ + int index; + /* Options while loading the module. */ + int options; +} tests[] = +{ + { "obj1func2", 0, RTLD_LAZY }, + { "obj1func1", 0, RTLD_LAZY | RTLD_GLOBAL }, + { "obj1func1", 0, RTLD_NOW, }, + { "obj1func2", 0, RTLD_NOW | RTLD_GLOBAL }, + { "obj2func2", 1, RTLD_LAZY }, + { "obj2func1", 1, RTLD_LAZY | RTLD_GLOBAL, }, + { "obj2func1", 1, RTLD_NOW, }, + { "obj2func2", 1, RTLD_NOW | RTLD_GLOBAL }, + { "obj3func2", 2, RTLD_LAZY }, + { "obj3func1", 2, RTLD_LAZY | RTLD_GLOBAL }, + { "obj3func1", 2, RTLD_NOW }, + { "obj3func2", 2, RTLD_NOW | RTLD_GLOBAL }, + { "obj4func2", 3, RTLD_LAZY }, + { "obj4func1", 3, RTLD_LAZY | RTLD_GLOBAL }, + { "obj4func1", 3, RTLD_NOW }, + { "obj4func2", 3, RTLD_NOW | RTLD_GLOBAL }, + { "obj5func2", 4, RTLD_LAZY }, + { "obj5func1", 4, RTLD_LAZY | RTLD_GLOBAL }, + { "obj5func1", 4, RTLD_NOW }, + { "obj5func2", 4, RTLD_NOW | RTLD_GLOBAL }, + { "obj6func2", 5, RTLD_LAZY }, + { "obj6func1", 5, RTLD_LAZY | RTLD_GLOBAL }, + { "obj6func1", 5, RTLD_NOW }, + { "obj6func2", 5, RTLD_NOW | RTLD_GLOBAL }, +}; +#define NTESTS (sizeof (tests) / sizeof (tests[0])) + + +#include <include/link.h> + +#define MAPS ((struct link_map *) _r_debug.r_map) + +#define OUT \ + for (map = MAPS; map != NULL; map = map->l_next) \ + if (map->l_type == lt_loaded) \ + printf ("name = \"%s\", direct_opencount = %d\n", \ + map->l_name, (int) map->l_direct_opencount); \ + fflush (stdout) + + +int +main (int argc, char *argv[]) +{ + int debug = argc > 1 && argv[1][0] != '\0'; + int count = TEST_ROUNDS; + int result = 0; + struct link_map *map; + + mtrace (); + + /* Just a seed. */ + srandom (TEST_ROUNDS); + + if (debug) + { + puts ("in the beginning"); + OUT; + } + + while (count--) + { + int nr = random () % NTESTS; + int index = tests[nr].index; + + printf ("%4d: %4d: ", count + 1, nr); + fflush (stdout); + + if (testobjs[index].handle == NULL) + { + int (*fct) (int); + + /* Load the object. */ + testobjs[index].handle = dlopen (testobjs[index].name, + tests[nr].options); + if (testobjs[index].handle == NULL) + error (EXIT_FAILURE, 0, "cannot load `%s': %s", + testobjs[index].name, dlerror ()); + + /* Test the function call. */ + fct = dlsym (testobjs[index].handle, tests[nr].fname); + if (fct == NULL) + error (EXIT_FAILURE, 0, + "cannot get function `%s' from shared object `%s': %s", + tests[nr].fname, testobjs[index].name, dlerror ()); + + fct (10); + + printf ("successfully loaded `%s', handle %p\n", + testobjs[index].name, testobjs[index].handle); + } + else + { + if (dlclose (testobjs[index].handle) != 0) + { + printf ("failed to close %s\n", testobjs[index].name); + result = 1; + } + else + printf ("successfully unloaded `%s', handle %p\n", + testobjs[index].name, testobjs[index].handle); + + testobjs[index].handle = NULL; + + if (testobjs[0].handle == NULL + && testobjs[1].handle == NULL + && testobjs[5].handle == NULL) + { + /* In this case none of the objects above should be + present. */ + 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 + || strstr (map->l_name, testobjs[5].name) != NULL)) + { + printf ("`%s' is still loaded\n", map->l_name); + result = 1; + } + } + } + + if (debug) + OUT; + } + + /* Unload all loaded modules. */ + for (count = 0; count < (int) NOBJS; ++count) + if (testobjs[count].handle != NULL) + { + printf ("\nclose: %s: l_initfini = %p, l_versions = %p\n", + testobjs[count].name, + ((struct link_map *) testobjs[count].handle)->l_initfini, + ((struct link_map *) testobjs[count].handle)->l_versions); + + if (dlclose (testobjs[count].handle) != 0) + { + printf ("failed to close %s\n", testobjs[count].name); + result = 1; + } + } + + /* Check whether all files are unloaded. */ + for (map = MAPS; map != NULL; map = map->l_next) + if (map->l_type == lt_loaded) + { + printf ("name = \"%s\", direct_opencount = %d\n", + map->l_name, (int) map->l_direct_opencount); + result = 1; + } + + return result; +} + + +extern int foo (int a); +int +foo (int a) +{ + return a - 1; +} diff --git a/libc/elf/ltglobmod1.c b/libc/elf/ltglobmod1.c new file mode 100644 index 000000000..300fa9a89 --- /dev/null +++ b/libc/elf/ltglobmod1.c @@ -0,0 +1,7 @@ +extern int bar (void); + +int +bar (void) +{ + return 42; +} diff --git a/libc/elf/ltglobmod2.c b/libc/elf/ltglobmod2.c new file mode 100644 index 000000000..33f14cc98 --- /dev/null +++ b/libc/elf/ltglobmod2.c @@ -0,0 +1,33 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +extern int bar (void); +extern int foo (void); + +int +foo (void) +{ + void *h; + int res; + + /* Load ltglobalmod1 in the global namespace. */ + h = dlopen ("ltglobmod1.so", RTLD_GLOBAL | RTLD_LAZY); + if (h == NULL) + { + printf ("%s: cannot open %s: %s", + __FUNCTION__, "ltglobmod1.so", dlerror ()); + exit (EXIT_FAILURE); + } + + /* Call bar. This is undefined in the DSO. */ + puts ("about to call `bar'"); + fflush (stdout); + res = bar (); + + printf ("bar returned %d\n", res); + + dlclose (h); + + return res != 42; +} diff --git a/libc/elf/multiload.c b/libc/elf/multiload.c new file mode 100644 index 000000000..e85cc9658 --- /dev/null +++ b/libc/elf/multiload.c @@ -0,0 +1,105 @@ +#include <dlfcn.h> +#include <errno.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int +main (void) +{ + void *a; + void *b; + void *c; + void *d; + char *wd; + char *base; + char *buf; + + mtrace (); + + /* Change to the binary directory. */ + if (chdir (OBJDIR) != 0) + { + printf ("cannot change to `%s': %m", OBJDIR); + exit (EXIT_FAILURE); + } + + wd = getcwd (NULL, 0); + base = basename (wd); + buf = alloca (strlen (wd) + strlen (base) + 5 + sizeof "testobj1.so"); + + printf ("loading `%s'\n", "./testobj1.so"); + a = dlopen ("./testobj1.so", RTLD_NOW); + if (a == NULL) + { + printf ("cannot load `./testobj1.so': %s\n", dlerror ()); + exit (EXIT_FAILURE); + } + + stpcpy (stpcpy (stpcpy (buf, "../"), base), "/testobj1.so"); + printf ("loading `%s'\n", buf); + b = dlopen (buf, RTLD_NOW); + if (b == NULL) + { + printf ("cannot load `%s': %s\n", buf, dlerror ()); + exit (EXIT_FAILURE); + } + + stpcpy (stpcpy (buf, wd), "/testobj1.so"); + printf ("loading `%s'\n", buf); + c = dlopen (buf, RTLD_NOW); + if (c == NULL) + { + printf ("cannot load `%s': %s\n", buf, dlerror ()); + exit (EXIT_FAILURE); + } + + stpcpy (stpcpy (stpcpy (stpcpy (buf, wd), "/../"), base), "/testobj1.so"); + printf ("loading `%s'\n", buf); + d = dlopen (buf, RTLD_NOW); + if (d == NULL) + { + printf ("cannot load `%s': %s\n", buf, dlerror ()); + exit (EXIT_FAILURE); + } + + if (a != b || b != c || c != d) + { + puts ("shared object loaded more than once"); + exit (EXIT_FAILURE); + } + + if (dlclose (a) != 0) + { + puts ("closing `a' failed"); + exit (EXIT_FAILURE); + } + if (dlclose (b) != 0) + { + puts ("closing `a' failed"); + exit (EXIT_FAILURE); + } + if (dlclose (c) != 0) + { + puts ("closing `a' failed"); + exit (EXIT_FAILURE); + } + if (dlclose (d) != 0) + { + puts ("closing `a' failed"); + exit (EXIT_FAILURE); + } + + free (wd); + + return 0; +} + +extern int foo (int a); +int +foo (int a) +{ + return a; +} diff --git a/libc/elf/neededobj1.c b/libc/elf/neededobj1.c new file mode 100644 index 000000000..eb55adab3 --- /dev/null +++ b/libc/elf/neededobj1.c @@ -0,0 +1,6 @@ +extern void c_function (void); + +void +c_function (void) +{ +} diff --git a/libc/elf/neededobj2.c b/libc/elf/neededobj2.c new file mode 100644 index 000000000..5ad8a51d6 --- /dev/null +++ b/libc/elf/neededobj2.c @@ -0,0 +1,8 @@ +extern void b_function (void); +extern void c_function (void); + +void +b_function (void) +{ + c_function(); +} diff --git a/libc/elf/neededobj3.c b/libc/elf/neededobj3.c new file mode 100644 index 000000000..da25329aa --- /dev/null +++ b/libc/elf/neededobj3.c @@ -0,0 +1,10 @@ +extern void a_function (void); +extern void b_function (void); +extern void c_function (void); + +void +a_function (void) +{ + b_function (); + c_function (); +} diff --git a/libc/elf/neededobj4.c b/libc/elf/neededobj4.c new file mode 100644 index 000000000..3ea854004 --- /dev/null +++ b/libc/elf/neededobj4.c @@ -0,0 +1,12 @@ +extern void a_function (void); +extern void b_function (void); +extern void c_function (void); +extern void d_function (void); + +void +d_function (void) +{ + a_function (); + b_function (); + c_function (); +} diff --git a/libc/elf/neededobj5.c b/libc/elf/neededobj5.c new file mode 100644 index 000000000..2d629b088 --- /dev/null +++ b/libc/elf/neededobj5.c @@ -0,0 +1,5 @@ +extern void a1_function (void); + +void a1_function (void) +{ +} diff --git a/libc/elf/neededobj6.c b/libc/elf/neededobj6.c new file mode 100644 index 000000000..639b6f195 --- /dev/null +++ b/libc/elf/neededobj6.c @@ -0,0 +1,7 @@ +extern void a1_function (void); +extern void a2_function (void); + +void a2_function (void) +{ + a1_function (); +} diff --git a/libc/elf/neededtest.c b/libc/elf/neededtest.c new file mode 100644 index 000000000..3cea49931 --- /dev/null +++ b/libc/elf/neededtest.c @@ -0,0 +1,125 @@ +#include <dlfcn.h> +#include <libintl.h> +#include <link.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define MAPS ((struct link_map *) _r_debug.r_map) + +static int +check_loaded_objects (const char **loaded) +{ + struct link_map *lm; + int n; + int *found = NULL; + int errors = 0; + + for (n = 0; loaded[n]; n++) + /* NOTHING */; + + if (n) + { + found = (int *) alloca (sizeof (int) * n); + memset (found, 0, sizeof (int) * n); + } + + printf(" Name\n"); + printf(" --------------------------------------------------------\n"); + 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_direct_opencount); + if (lm->l_type == lt_loaded && lm->l_name) + { + int match = 0; + for (n = 0; loaded[n] != NULL; n++) + { + if (strcmp (basename (loaded[n]), basename (lm->l_name)) == 0) + { + found[n] = 1; + match = 1; + break; + } + } + + if (match == 0) + { + ++errors; + printf ("ERRORS: %s is not unloaded\n", lm->l_name); + } + } + } + + for (n = 0; loaded[n] != NULL; n++) + { + if (found[n] == 0) + { + ++errors; + printf ("ERRORS: %s is not loaded\n", loaded[n]); + } + } + + return errors; +} + +int +main (void) +{ + void *obj2[2]; + void *obj3; + const char *loaded[] = { NULL, NULL, NULL, NULL }; + int errors = 0; + + printf ("\nThis is what is in memory now:\n"); + errors += check_loaded_objects (loaded); + + printf( "Loading shared object neededobj3.so\n"); + obj3 = dlopen( "neededobj3.so", RTLD_LAZY); + if (obj3 == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + loaded[0] = "neededobj1.so"; + loaded[1] = "neededobj2.so"; + loaded[2] = "neededobj3.so"; + errors += check_loaded_objects (loaded); + + printf ("Now loading shared object neededobj2.so\n"); + obj2[0] = dlopen ("neededobj2.so", RTLD_LAZY); + if (obj2[0] == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + errors += check_loaded_objects (loaded); + + printf ("And loading shared object neededobj2.so again\n"); + obj2[1] = dlopen ("neededobj2.so", RTLD_LAZY); + if (obj2[1] == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + errors += check_loaded_objects (loaded); + + printf ("Closing neededobj2.so for the first time\n"); + dlclose (obj2[0]); + errors += check_loaded_objects (loaded); + + printf ("Closing neededobj3.so\n"); + dlclose (obj3); + loaded[2] = NULL; + errors += check_loaded_objects (loaded); + + printf ("Closing neededobj2.so for the second time\n"); + dlclose (obj2[1]); + loaded[0] = NULL; + loaded[1] = NULL; + errors += check_loaded_objects (loaded); + + if (errors != 0) + printf ("%d errors found\n", errors); + return errors; +} diff --git a/libc/elf/neededtest2.c b/libc/elf/neededtest2.c new file mode 100644 index 000000000..17c75f2ba --- /dev/null +++ b/libc/elf/neededtest2.c @@ -0,0 +1,118 @@ +#include <dlfcn.h> +#include <libintl.h> +#include <link.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define MAPS ((struct link_map *) _r_debug.r_map) + +static int +check_loaded_objects (const char **loaded) +{ + struct link_map *lm; + int n; + int *found = NULL; + int errors = 0; + + for (n = 0; loaded[n]; n++) + /* NOTHING */; + + if (n) + { + found = (int *) alloca (sizeof (int) * n); + memset (found, 0, sizeof (int) * n); + } + + printf(" Name\n"); + printf(" --------------------------------------------------------\n"); + 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_direct_opencount); + if (lm->l_type == lt_loaded && lm->l_name) + { + int match = 0; + for (n = 0; loaded[n] != NULL; n++) + { + if (strcmp (basename (loaded[n]), basename (lm->l_name)) == 0) + { + found[n] = 1; + match = 1; + break; + } + } + + if (match == 0) + { + ++errors; + printf ("ERRORS: %s is not unloaded\n", lm->l_name); + } + } + } + + for (n = 0; loaded[n] != NULL; n++) + { + if (found[n] == 0) + { + ++errors; + printf ("ERRORS: %s is not loaded\n", loaded[n]); + } + } + + return errors; +} + +int +main (void) +{ + void *obj2; + void *obj3[2]; + const char *loaded[] = { NULL, NULL, NULL, NULL }; + int errors = 0; + + printf ("\nThis is what is in memory now:\n"); + errors += check_loaded_objects (loaded); + printf ("\nLoading shared object neededobj2.so\n"); + obj2 = dlopen ("neededobj2.so", RTLD_LAZY); + if (obj2 == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + loaded[0] = "neededobj1.so"; + loaded[1] = "neededobj2.so"; + errors += check_loaded_objects (loaded); + printf ("\nLoading shared object neededobj3.so\n"); + obj3[0] = dlopen( "neededobj3.so", RTLD_LAZY); + if (obj3[0] == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + loaded[2] = "neededobj3.so"; + errors += check_loaded_objects (loaded); + printf ("\nNow loading shared object neededobj3.so again\n"); + obj3[1] = dlopen ("neededobj3.so", RTLD_LAZY); + if (obj3[1] == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + errors += check_loaded_objects (loaded); + printf ("\nClosing neededobj3.so once\n"); + dlclose (obj3[0]); + errors += check_loaded_objects (loaded); + printf ("\nClosing neededobj2.so\n"); + dlclose (obj2); + errors += check_loaded_objects (loaded); + printf ("\nClosing neededobj3.so for the second time\n"); + dlclose (obj3[1]); + loaded[0] = NULL; + loaded[1] = NULL; + loaded[2] = NULL; + errors += check_loaded_objects (loaded); + if (errors != 0) + printf ("%d errors found\n", errors); + return errors; +} diff --git a/libc/elf/neededtest3.c b/libc/elf/neededtest3.c new file mode 100644 index 000000000..41970cf2c --- /dev/null +++ b/libc/elf/neededtest3.c @@ -0,0 +1,129 @@ +#include <dlfcn.h> +#include <libintl.h> +#include <link.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define MAPS ((struct link_map *) _r_debug.r_map) + +static int +check_loaded_objects (const char **loaded) +{ + struct link_map *lm; + int n; + int *found = NULL; + int errors = 0; + + for (n = 0; loaded[n]; n++) + /* NOTHING */; + + if (n) + { + found = (int *) alloca (sizeof (int) * n); + memset (found, 0, sizeof (int) * n); + } + + printf(" Name\n"); + printf(" --------------------------------------------------------\n"); + 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_direct_opencount); + if (lm->l_type == lt_loaded && lm->l_name) + { + int match = 0; + for (n = 0; loaded[n] != NULL; n++) + { + if (strcmp (basename (loaded[n]), basename (lm->l_name)) == 0) + { + found[n] = 1; + match = 1; + break; + } + } + + if (match == 0) + { + ++errors; + printf ("ERRORS: %s is not unloaded\n", lm->l_name); + } + } + } + + for (n = 0; loaded[n] != NULL; n++) + { + if (found[n] == 0) + { + ++errors; + printf ("ERRORS: %s is not loaded\n", loaded[n]); + } + } + + return errors; +} + +int +main (void) +{ + void *obj2; + void *obj3; + void *obj4; + const char *loaded[] = { NULL, NULL, NULL, NULL, NULL }; + int errors = 0; + + printf ("\nThis is what is in memory now:\n"); + errors += check_loaded_objects (loaded); + + printf ("Now loading shared object neededobj2.so\n"); + obj2 = dlopen ("neededobj2.so", RTLD_LAZY); + if (obj2 == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + loaded[0] = "neededobj1.so"; + loaded[1] = "neededobj2.so"; + errors += check_loaded_objects (loaded); + + printf( "Loading shared object neededobj3.so\n"); + obj3 = dlopen( "neededobj3.so", RTLD_LAZY); + if (obj3 == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + loaded[2] = "neededobj3.so"; + errors += check_loaded_objects (loaded); + + + printf( "Loading shared object neededobj4.so\n"); + obj4 = dlopen( "neededobj4.so", RTLD_LAZY); + if (obj4 == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + loaded[3] = "neededobj4.so"; + errors += check_loaded_objects (loaded); + + printf ("Closing neededobj2.so\n"); + dlclose (obj2); + errors += check_loaded_objects (loaded); + + printf ("Closing neededobj3.so\n"); + dlclose (obj3); + errors += check_loaded_objects (loaded); + + printf ("Closing neededobj4.so\n"); + dlclose (obj4); + loaded[0] = NULL; + loaded[1] = NULL; + loaded[2] = NULL; + loaded[3] = NULL; + errors += check_loaded_objects (loaded); + + if (errors != 0) + printf ("%d errors found\n", errors); + return errors; +} diff --git a/libc/elf/neededtest4.c b/libc/elf/neededtest4.c new file mode 100644 index 000000000..bd79341fb --- /dev/null +++ b/libc/elf/neededtest4.c @@ -0,0 +1,158 @@ +#include <dlfcn.h> +#include <libintl.h> +#include <link.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define MAPS ((struct link_map *) _r_debug.r_map) + +static int +check_loaded_objects (const char **loaded) +{ + struct link_map *lm; + int n; + int *found = NULL; + int errors = 0; + + for (n = 0; loaded[n]; n++) + /* NOTHING */; + + if (n) + { + found = (int *) alloca (sizeof (int) * n); + memset (found, 0, sizeof (int) * n); + } + + printf(" Name\n"); + printf(" --------------------------------------------------------\n"); + 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_direct_opencount); + if (lm->l_type == lt_loaded && lm->l_name) + { + int match = 0; + for (n = 0; loaded[n] != NULL; n++) + { + if (strcmp (basename (loaded[n]), basename (lm->l_name)) == 0) + { + found[n] = 1; + match = 1; + break; + } + } + + if (match == 0) + { + ++errors; + printf ("ERRORS: %s is not unloaded\n", lm->l_name); + } + } + } + + for (n = 0; loaded[n] != NULL; n++) + { + if (found[n] == 0) + { + ++errors; + printf ("ERRORS: %s is not loaded\n", loaded[n]); + } + } + + return errors; +} + +extern void c_function (void); +extern char *dirname (__const char *__filename); + +int +main (int argc, char **argv) +{ + void *obj; + const char *loaded[] = { NULL, NULL, NULL}; + int errors = 0; + void (*f) (void); + const char *dir = dirname (argv [0]); + char *oldfilename; + char *newfilename; + + c_function (); + + printf ("\nThis is what is in memory now:\n"); + errors += check_loaded_objects (loaded); + + printf( "Loading shared object neededobj6.so\n"); + obj = dlopen( "neededobj6.so", RTLD_LAZY); + if (obj == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + f = dlsym (obj, "a2_function"); + if (f == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + f (); + loaded[0] = "neededobj5.so"; + loaded[1] = "neededobj6.so"; + errors += check_loaded_objects (loaded); + + printf ("Closing neededobj6.so\n"); + dlclose (obj); + loaded[0] = NULL; + errors += check_loaded_objects (loaded); + + printf ("Rename neededobj5.so\n"); + oldfilename = alloca (strlen (dir) + 1 + sizeof ("neededobj5.so")); + strcpy (oldfilename, dir); + strcat (oldfilename, "/"); + strcat (oldfilename, "neededobj5.so"); + newfilename = alloca (strlen (oldfilename) + sizeof (".renamed")); + strcpy (newfilename, oldfilename); + strcat (newfilename, ".renamed"); + if (rename (oldfilename, newfilename)) + { + perror ("rename"); + exit (1); + } + + printf( "Loading shared object neededobj6.so\n"); + obj = dlopen( "neededobj6.so", RTLD_LAZY); + if (obj == NULL) + printf ("%s\n", dlerror ()); + else + { + printf ("neededobj6.so should fail to load\n"); + exit (1); + } + + printf( "Loading shared object neededobj1.so\n"); + obj = dlopen( "neededobj1.so", RTLD_LAZY); + if (obj == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + errors += check_loaded_objects (loaded); + f = dlsym (obj, "c_function"); + if (f == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + f (); + + printf ("Restore neededobj5.so\n"); + if (rename (newfilename, oldfilename)) + { + perror ("rename"); + exit (1); + } + + if (errors != 0) + printf ("%d errors found\n", errors); + return errors; +} diff --git a/libc/elf/next.c b/libc/elf/next.c new file mode 100644 index 000000000..6a3670c21 --- /dev/null +++ b/libc/elf/next.c @@ -0,0 +1,44 @@ +#include <stdio.h> + + +extern int successful_rtld_next_test (void); +extern void *failing_rtld_next_use (void); + + +static int +do_test (void) +{ + int result; + void *addr; + + /* First try call a function which uses RTLD_NEXT and calls that + function. */ + result = successful_rtld_next_test (); + if (result == 42) + { + puts ("RTLD_NEXT seems to work for existing functions"); + result = 0; + } + else + { + printf ("Heh? `successful_rtld_next_test' returned %d\n", result); + result = 1; + } + + /* Next try a function which tries to get a function with RTLD_NEXT + but that fails. This dlsym() call should return a NULL pointer + and do nothing else. */ + addr = failing_rtld_next_use (); + if (addr == NULL) + puts ("dlsym returned NULL for non-existing function. Good"); + else + { + puts ("dlsym found something !?"); + result = 1; + } + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/libc/elf/nextmod1.c b/libc/elf/nextmod1.c new file mode 100644 index 000000000..56de3536a --- /dev/null +++ b/libc/elf/nextmod1.c @@ -0,0 +1,30 @@ +#include <dlfcn.h> + +extern int successful_rtld_next_test (void); +extern void *failing_rtld_next_use (void); + +int nextmod1_dummy_var; + +int +successful_rtld_next_test (void) +{ + int (*fp) (void); + + /* Get the next function... */ + fp = (int (*) (void)) dlsym (RTLD_NEXT, __FUNCTION__); + + /* ...and simply call it. */ + return fp (); +} + + +void * +failing_rtld_next_use (void) +{ + void *ret = dlsym (RTLD_NEXT, __FUNCTION__); + + /* Ensure we are not tail call optimized, because then RTLD_NEXT + might return this function. */ + ++nextmod1_dummy_var; + return ret; +} diff --git a/libc/elf/nextmod2.c b/libc/elf/nextmod2.c new file mode 100644 index 000000000..b2c435f34 --- /dev/null +++ b/libc/elf/nextmod2.c @@ -0,0 +1,10 @@ +/* Very elaborated function. */ + +extern int successful_rtld_next_test (void); + + +int +successful_rtld_next_test (void) +{ + return 42; +} diff --git a/libc/elf/nodel2mod1.c b/libc/elf/nodel2mod1.c new file mode 100644 index 000000000..acddc4cf8 --- /dev/null +++ b/libc/elf/nodel2mod1.c @@ -0,0 +1,19 @@ +#include <stdlib.h> +void +foo (void) +{ + exit (0); +} + +void +__attribute__((destructor)) +bar (void) +{ + static int i; + foo (); + ++i; +} +void +baz (void) +{ +} diff --git a/libc/elf/nodel2mod2.c b/libc/elf/nodel2mod2.c new file mode 100644 index 000000000..d0020240a --- /dev/null +++ b/libc/elf/nodel2mod2.c @@ -0,0 +1,7 @@ +void +__attribute__((constructor)) +xxx (void) +{ + extern void baz (void); + baz (); +} diff --git a/libc/elf/nodel2mod3.c b/libc/elf/nodel2mod3.c new file mode 100644 index 000000000..6d1a0d47b --- /dev/null +++ b/libc/elf/nodel2mod3.c @@ -0,0 +1 @@ +int x; diff --git a/libc/elf/nodelete.c b/libc/elf/nodelete.c new file mode 100644 index 000000000..78364a278 --- /dev/null +++ b/libc/elf/nodelete.c @@ -0,0 +1,211 @@ +#include <dlfcn.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> + + +static sigjmp_buf jmpbuf; + + +int fini_ran; + + +static void +__attribute__ ((noreturn)) +handler (int sig) +{ + siglongjmp (jmpbuf, 1); +} + + +#define TEST_FUNCTION do_test () +static int +do_test (void) +{ + /* We are testing the two possibilities to mark an object as not deletable: + - marked on the linker commandline with `-z nodelete' + - with the RTLD_NODELETE flag at dlopen()-time. + + The test we are performing should be safe. We are loading the objects, + get the address of variables in the respective object, unload the object + and then try to read the variable. If the object is unloaded this + should lead to an segmentation fault. */ + int result = 0; + void *p; + struct sigaction sa; + + sa.sa_handler = handler; + sigfillset (&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction (SIGSEGV, &sa, NULL) == -1) + printf ("cannot install signal handler: %m\n"); + + p = dlopen ("nodelmod1.so", RTLD_LAZY); + if (p == NULL) + { + printf ("failed to load \"nodelmod1.so\": %s\n", dlerror ()); + result = 1; + } + else + { + int *varp; + + puts ("succeeded loading \"nodelmod1.so\""); + + varp = dlsym (p, "var1"); + if (varp == NULL) + { + puts ("failed to get address of \"var1\" in \"nodelmod1.so\""); + result = 1; + } + else + { + *varp = 20000720; + + /* Now close the object. */ + fini_ran = 0; + if (dlclose (p) != 0) + { + puts ("failed to close \"nodelmod1.so\""); + result = 1; + } + else if (! sigsetjmp (jmpbuf, 1)) + { + /* Access the variable again. */ + if (*varp != 20000720) + { + puts ("\"var1\" value not correct"); + result = 1; + } + else if (fini_ran != 0) + { + puts ("destructor of \"nodelmod1.so\" ran"); + result = 1; + } + else + puts ("-z nodelete test succeeded"); + } + else + { + /* We caught an segmentation fault. */ + puts ("\"nodelmod1.so\" got deleted"); + result = 1; + } + } + } + + p = dlopen ("nodelmod2.so", RTLD_LAZY | RTLD_NODELETE); + if (p == NULL) + { + printf ("failed to load \"nodelmod2.so\": %s\n", dlerror ()); + result = 1; + } + else + { + int *varp; + + puts ("succeeded loading \"nodelmod2.so\""); + + varp = dlsym (p, "var2"); + if (varp == NULL) + { + puts ("failed to get address of \"var2\" in \"nodelmod2.so\""); + result = 1; + } + else + { + *varp = 42; + + /* Now close the object. */ + fini_ran = 0; + if (dlclose (p) != 0) + { + puts ("failed to close \"nodelmod2.so\""); + result = 1; + } + else if (! sigsetjmp (jmpbuf, 1)) + { + /* Access the variable again. */ + if (*varp != 42) + { + puts ("\"var2\" value not correct"); + result = 1; + } + else if (fini_ran != 0) + { + puts ("destructor of \"nodelmod2.so\" ran"); + result = 1; + } + else + puts ("RTLD_NODELETE test succeeded"); + } + else + { + /* We caught an segmentation fault. */ + puts ("\"nodelmod2.so\" got deleted"); + result = 1; + } + } + } + + p = dlopen ("nodelmod3.so", RTLD_LAZY); + if (p == NULL) + { + printf ("failed to load \"nodelmod3.so\": %s\n", dlerror ()); + result = 1; + } + else + { + int *(*fctp) (void); + + puts ("succeeded loading \"nodelmod3.so\""); + + fctp = dlsym (p, "addr"); + if (fctp == NULL) + { + puts ("failed to get address of \"addr\" in \"nodelmod3.so\""); + result = 1; + } + else + { + int *varp = fctp (); + + *varp = -1; + + /* Now close the object. */ + fini_ran = 0; + if (dlclose (p) != 0) + { + puts ("failed to close \"nodelmod3.so\""); + result = 1; + } + else if (! sigsetjmp (jmpbuf, 1)) + { + /* Access the variable again. */ + if (*varp != -1) + { + puts ("\"var_in_mod4\" value not correct"); + result = 1; + } + else if (fini_ran != 0) + { + puts ("destructor of \"nodelmod4.so\" ran"); + result = 1; + } + else + puts ("-z nodelete in dependency succeeded"); + } + else + { + /* We caught an segmentation fault. */ + puts ("\"nodelmod4.so\" got deleted"); + result = 1; + } + } + } + + return result; +} + +#include "../test-skeleton.c" diff --git a/libc/elf/nodelete2.c b/libc/elf/nodelete2.c new file mode 100644 index 000000000..b3d7e31a0 --- /dev/null +++ b/libc/elf/nodelete2.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> + +int +main (void) +{ + void *handle = dlopen ("nodel2mod3.so", RTLD_LAZY); + if (handle == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + dlclose (handle); + exit (1); +} diff --git a/libc/elf/nodelmod1.c b/libc/elf/nodelmod1.c new file mode 100644 index 000000000..fc24a7b17 --- /dev/null +++ b/libc/elf/nodelmod1.c @@ -0,0 +1,9 @@ +int var1 = 42; + +static void +__attribute__ ((__destructor__)) +destr (void) +{ + extern int fini_ran; + fini_ran = 1; +} diff --git a/libc/elf/nodelmod2.c b/libc/elf/nodelmod2.c new file mode 100644 index 000000000..6bd7108a1 --- /dev/null +++ b/libc/elf/nodelmod2.c @@ -0,0 +1,9 @@ +int var2 = 100; + +static void +__attribute__ ((__destructor__)) +destr (void) +{ + extern int fini_ran; + fini_ran = 1; +} diff --git a/libc/elf/nodelmod3.c b/libc/elf/nodelmod3.c new file mode 100644 index 000000000..817c94db6 --- /dev/null +++ b/libc/elf/nodelmod3.c @@ -0,0 +1,8 @@ +extern int var_in_mod4; +extern int *addr (void); + +int * +addr (void) +{ + return &var_in_mod4; +} diff --git a/libc/elf/nodelmod4.c b/libc/elf/nodelmod4.c new file mode 100644 index 000000000..2cca43a6e --- /dev/null +++ b/libc/elf/nodelmod4.c @@ -0,0 +1,9 @@ +int var_in_mod4 = 99; + +static void +__attribute__ ((__destructor__)) +destr (void) +{ + extern int fini_ran; + fini_ran = 1; +} diff --git a/libc/elf/nodlopen.c b/libc/elf/nodlopen.c new file mode 100644 index 000000000..642bdb301 --- /dev/null +++ b/libc/elf/nodlopen.c @@ -0,0 +1,15 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +main (void) +{ + if (dlopen ("nodlopenmod.so", RTLD_LAZY) != NULL) + { + puts ("opening \"nodlopenmod.so\" succeeded, FAIL"); + return 1; + } + + puts ("opening \"nodlopenmod.so\" failed, OK"); + return 0; +} diff --git a/libc/elf/nodlopen2.c b/libc/elf/nodlopen2.c new file mode 100644 index 000000000..a223f3683 --- /dev/null +++ b/libc/elf/nodlopen2.c @@ -0,0 +1,15 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +main (void) +{ + if (dlopen ("nodlopenmod2.so", RTLD_LAZY) != NULL) + { + puts ("opening \"nodlopenmod2.so\" succeeded, FAIL"); + return 1; + } + + puts ("opening \"nodlopenmod2.so\" failed, OK"); + return 0; +} diff --git a/libc/elf/nodlopenmod.c b/libc/elf/nodlopenmod.c new file mode 100644 index 000000000..4bcf8c978 --- /dev/null +++ b/libc/elf/nodlopenmod.c @@ -0,0 +1 @@ +int a = 42; diff --git a/libc/elf/nodlopenmod2.c b/libc/elf/nodlopenmod2.c new file mode 100644 index 000000000..e72ae53e9 --- /dev/null +++ b/libc/elf/nodlopenmod2.c @@ -0,0 +1,9 @@ +extern int a; + +extern int foo (void); + +int +foo (void) +{ + return a; +} diff --git a/libc/elf/noload.c b/libc/elf/noload.c new file mode 100644 index 000000000..9281ec714 --- /dev/null +++ b/libc/elf/noload.c @@ -0,0 +1,71 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +main (void) +{ + int result = 0; + + /* First try to load an object which is a dependency. This should + succeed. */ + if (dlopen ("testobj1.so", RTLD_LAZY | RTLD_NOLOAD) == NULL) + { + printf ("cannot open \"testobj1.so\": %s\n", dlerror ()); + result = 1; + } + else + puts ("loading \"testobj1.so\" succeeded, OK"); + + /* Now try loading an object which is not already loaded. */ + if (dlopen ("testobj5.so", RTLD_LAZY | RTLD_NOLOAD) != NULL) + { + puts ("succeeded in loading \"testobj5.so\""); + result = 1; + } + else + { + /* Load the object and run the same test again. */ + void *p; + + puts ("\"testobj5.so\" wasn't loaded and RTLD_NOLOAD prevented it, OK"); + + p = dlopen ("testobj5.so", RTLD_LAZY); + + if (p == NULL) + { + printf ("cannot open \"testobj5.so\" without RTLD_NOLOAD: %s\n", + dlerror ()); + result = 1; + } + else + { + puts ("loading \"testobj5.so\" succeeded, OK"); + + if (dlopen ("testobj5.so", RTLD_LAZY | RTLD_NOLOAD) == NULL) + { + printf ("cannot open \"testobj5.so\": %s\n", dlerror ()); + result = 1; + } + else + puts ("loading \"testobj5.so\" with RTLD_NOLOAD succeeded, OK"); + + if (dlclose (p) != 0) + { + printf ("cannot close \"testobj5.so\": %s\n", dlerror ()); + result = 1; + } + else + puts ("closing \"testobj5.so\" succeeded, OK"); + } + } + + return result; +} + + +extern int foo (int a); +int +foo (int a) +{ + return 42 + a; +} diff --git a/libc/elf/order.c b/libc/elf/order.c new file mode 100644 index 000000000..ca617cbc0 --- /dev/null +++ b/libc/elf/order.c @@ -0,0 +1,25 @@ +#include <unistd.h> + +void init (void) __attribute__ ((constructor)); +void +__attribute__ ((constructor)) +init (void) +{ + write (1, "4", 1); +} + +void fini (void) __attribute__ ((destructor)); +void +__attribute__ ((destructor)) +fini (void) +{ + write (1, "5", 1); +} + +extern int dep1 (void); + +int +main (void) +{ + return dep1 () != 42; +} diff --git a/libc/elf/order2.c b/libc/elf/order2.c new file mode 100644 index 000000000..3dbfdd153 --- /dev/null +++ b/libc/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/libc/elf/order2mod1.c b/libc/elf/order2mod1.c new file mode 100644 index 000000000..b695db29b --- /dev/null +++ b/libc/elf/order2mod1.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +static void +__attribute__ ((destructor)) +fini (void) +{ + putchar ('1'); +} diff --git a/libc/elf/order2mod2.c b/libc/elf/order2mod2.c new file mode 100644 index 000000000..026cd2acc --- /dev/null +++ b/libc/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/libc/elf/order2mod3.c b/libc/elf/order2mod3.c new file mode 100644 index 000000000..7913a7992 --- /dev/null +++ b/libc/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/libc/elf/order2mod4.c b/libc/elf/order2mod4.c new file mode 100644 index 000000000..4f2026f04 --- /dev/null +++ b/libc/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/libc/elf/origtest.c b/libc/elf/origtest.c new file mode 100644 index 000000000..1cacabcc3 --- /dev/null +++ b/libc/elf/origtest.c @@ -0,0 +1,39 @@ +#include <dlfcn.h> +#include <error.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + void *h; + int (*fp) (int); + int res; + + h = dlopen ("${ORIGIN}/testobj1.so", RTLD_LAZY); + if (h == NULL) + error (EXIT_FAILURE, 0, "while loading `%s': %s", "testobj1.so", + dlerror ()); + + fp = dlsym (h, "obj1func1"); + if (fp == NULL) + error (EXIT_FAILURE, 0, "getting `obj1func1' in `%s': %s", + "testobj1.so", dlerror ()); + + res = fp (10); + printf ("fp(10) = %d\n", res); + + if (dlclose (h) != 0) + error (EXIT_FAILURE, 0, "while close `%s': %s", + "testobj1.so", dlerror ()); + + return res != 42; +} + + +extern int foo (int a); +int +foo (int a) +{ + return a + 10; +} diff --git a/libc/elf/pathoptobj.c b/libc/elf/pathoptobj.c new file mode 100644 index 000000000..a452c2d7d --- /dev/null +++ b/libc/elf/pathoptobj.c @@ -0,0 +1,8 @@ +extern int in_renamed (int); + + +int +in_renamed (int a) +{ + return a - 10; +} diff --git a/libc/elf/preloadtest.c b/libc/elf/preloadtest.c new file mode 100644 index 000000000..7ea10b9b5 --- /dev/null +++ b/libc/elf/preloadtest.c @@ -0,0 +1,19 @@ +#include <stdio.h> + +#include "testobj.h" + +int +main (void) +{ + int res = preload (42); + + printf ("preload (42) = %d, %s\n", res, res == 92 ? "ok" : "wrong"); + + return res != 92; +} + +int +foo (int a) +{ + return a; +} diff --git a/libc/elf/readelflib.c b/libc/elf/readelflib.c new file mode 100644 index 000000000..26444ad6b --- /dev/null +++ b/libc/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/libc/elf/readlib.c b/libc/elf/readlib.c new file mode 100644 index 000000000..8896bbdac --- /dev/null +++ b/libc/elf/readlib.c @@ -0,0 +1,181 @@ +/* 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. + + 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. + + 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 General Public License for more details. + + 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 + development version. Besides the simplification, it has also been + modified to read some other file formats. */ + +#include <a.out.h> +#include <elf.h> +#include <error.h> +#include <libintl.h> +#include <link.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <gnu/lib-names.h> + +#include <ldconfig.h> + +#define Elf32_CLASS ELFCLASS32 +#define Elf64_CLASS ELFCLASS64 + +struct known_names +{ + const char *soname; + int flag; +}; + +static struct known_names interpreters[] = +{ + { "/lib/" LD_SO, FLAG_ELF_LIBC6 }, +#ifdef SYSDEP_KNOWN_INTERPRETER_NAMES + SYSDEP_KNOWN_INTERPRETER_NAMES +#endif +}; + +static struct known_names known_libs[] = +{ + { LIBC_SO, FLAG_ELF_LIBC6 }, + { LIBM_SO, FLAG_ELF_LIBC6 }, +#ifdef SYSDEP_KNOWN_LIBRARY_NAMES + SYSDEP_KNOWN_LIBRARY_NAMES +#endif +}; + + + +/* Returns 0 if everything is ok, != 0 in case of error. */ +int +process_file (const char *real_file_name, const char *file_name, + const char *lib, int *flag, unsigned int *osversion, + char **soname, int is_link) +{ + FILE *file; + struct stat64 statbuf; + void *file_contents; + int ret; + ElfW(Ehdr) *elf_header; + struct exec *aout_header; + + ret = 0; + *flag = FLAG_ANY; + *soname = NULL; + + file = fopen (real_file_name, "rb"); + if (file == NULL) + { + /* No error for stale symlink. */ + if (is_link && strstr (file_name, ".so") != NULL) + return 1; + error (0, 0, _("Input file %s not found.\n"), file_name); + return 1; + } + + if (fstat64 (fileno (file), &statbuf) < 0) + { + error (0, 0, _("Cannot fstat file %s.\n"), file_name); + fclose (file); + return 1; + } + + /* Check that the file is large enough so that we can access the + information. We're only checking the size of the headers here. */ + if ((size_t) statbuf.st_size < sizeof (struct exec) + || (size_t) statbuf.st_size < sizeof (ElfW(Ehdr))) + { + if (statbuf.st_size == 0) + error (0, 0, _("File %s is empty, not checked."), file_name); + else + { + char buf[SELFMAG]; + size_t n = MIN (statbuf.st_size, SELFMAG); + if (fread (buf, n, 1, file) == 1 && memcmp (buf, ELFMAG, n) == 0) + error (0, 0, _("File %s is too small, not checked."), file_name); + } + fclose (file); + return 1; + } + + file_contents = mmap (0, statbuf.st_size, PROT_READ, MAP_SHARED, + fileno (file), 0); + if (file_contents == MAP_FAILED) + { + error (0, 0, _("Cannot mmap file %s.\n"), file_name); + fclose (file); + return 1; + } + + /* First check if this is an aout file. */ + aout_header = (struct exec *) file_contents; + if (N_MAGIC (*aout_header) == ZMAGIC +#ifdef QMAGIC /* Linuxism. */ + || N_MAGIC (*aout_header) == QMAGIC +#endif + ) + { + /* Aout files don't have a soname, just return the name + including the major number. */ + char *copy, *major, *dot; + copy = xstrdup (lib); + major = strstr (copy, ".so."); + if (major) + { + dot = strstr (major + 4, "."); + if (dot) + *dot = '\0'; + } + *soname = copy; + *flag = FLAG_LIBC4; + goto done; + } + + elf_header = (ElfW(Ehdr) *) file_contents; + if (memcmp (elf_header->e_ident, ELFMAG, SELFMAG) != 0) + { + /* The file is neither ELF nor aout. Check if it's a linker + script, like libc.so - otherwise complain. Only search the + beginning of the file. */ + size_t len = MIN (statbuf.st_size, 512); + if (memmem (file_contents, len, "GROUP", 5) == NULL + && memmem (file_contents, len, "GNU ld script", 13) == NULL) + error (0, 0, _("%s is not an ELF file - it has the wrong magic bytes at the start.\n"), + file_name); + ret = 1; + } + /* Libraries have to be shared object files. */ + else if (elf_header->e_type != ET_DYN) + ret = 1; + else if (process_elf_file (file_name, lib, flag, osversion, soname, + file_contents, statbuf.st_size)) + ret = 1; + + done: + /* Clean up allocated memory and resources. */ + munmap (file_contents, statbuf.st_size); + fclose (file); + + return ret; +} + +/* Get architecture specific version of process_elf_file. */ +#include <readelflib.c> diff --git a/libc/elf/reldep.c b/libc/elf/reldep.c new file mode 100644 index 000000000..44b239b6c --- /dev/null +++ b/libc/elf/reldep.c @@ -0,0 +1,111 @@ +#include <dlfcn.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + void *h1; + void *h2; + int (*fp) (void); + int *vp; + + mtrace (); + + /* Open the two objects. */ + h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL); + if (h1 == NULL) + { + printf ("cannot open reldepmod1.so: %s\n", dlerror ()); + exit (1); + } + h2 = dlopen ("reldepmod2.so", RTLD_LAZY); + if (h2 == NULL) + { + printf ("cannot open reldepmod2.so: %s\n", dlerror ()); + exit (1); + } + + /* Get the address of the variable in reldepmod1.so. */ + vp = dlsym (h1, "some_var"); + if (vp == NULL) + { + printf ("cannot get address of \"some_var\": %s\n", dlerror ()); + exit (1); + } + + *vp = 42; + + /* Get the function `call_me' in the second object. This has a + dependency which is resolved by a definition in reldepmod1.so. */ + fp = dlsym (h2, "call_me"); + if (fp == NULL) + { + printf ("cannot get address of \"call_me\": %s\n", dlerror ()); + exit (1); + } + + /* Call the function. */ + if (fp () != 0) + { + puts ("function \"call_me\" returned wrong result"); + exit (1); + } + + /* Now close the first object. If must still be around since we have + a implicit dependency. */ + if (dlclose (h1) != 0) + { + printf ("closing h1 failed: %s\n", dlerror ()); + exit (1); + } + + /* Try calling the function again. This will fail if the first object + got unloaded. */ + if (fp () != 0) + { + puts ("second call of function \"call_me\" returned wrong result"); + exit (1); + } + + /* Now close the second file as well. */ + if (dlclose (h2) != 0) + { + printf ("closing h2 failed: %s\n", dlerror ()); + exit (1); + } + + /* Finally, open the first object again. */ + h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL); + if (h1 == NULL) + { + printf ("cannot open reldepmod1.so the second time: %s\n", dlerror ()); + exit (1); + } + + /* And get the variable address again. */ + vp = dlsym (h1, "some_var"); + if (vp == NULL) + { + printf ("cannot get address of \"some_var\" the second time: %s\n", + dlerror ()); + exit (1); + } + + /* The variable now must have its originial value. */ + if (*vp != 0) + { + puts ("variable \"some_var\" not reset"); + exit (1); + } + + /* Close the first object again, we are done. */ + if (dlclose (h1) != 0) + { + printf ("closing h1 failed: %s\n", dlerror ()); + exit (1); + } + + return 0; +} diff --git a/libc/elf/reldep2.c b/libc/elf/reldep2.c new file mode 100644 index 000000000..ba5ab222f --- /dev/null +++ b/libc/elf/reldep2.c @@ -0,0 +1,101 @@ +#include <dlfcn.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + void *h1; + void *h2; + int (*fp) (void); + int *vp; + + mtrace (); + + /* Open the two objects. */ + h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL); + if (h1 == NULL) + { + printf ("cannot open reldepmod1.so: %s\n", dlerror ()); + exit (1); + } + h2 = dlopen ("reldepmod3.so", RTLD_LAZY); + if (h2 == NULL) + { + printf ("cannot open reldepmod3.so: %s\n", dlerror ()); + exit (1); + } + + /* Get the address of the variable in reldepmod1.so. */ + vp = dlsym (h1, "some_var"); + if (vp == NULL) + { + printf ("cannot get address of \"some_var\": %s\n", dlerror ()); + exit (1); + } + + *vp = 42; + + /* Get the function `call_me' in the second object. This has a + dependency which is resolved by a definition in reldepmod1.so. */ + fp = dlsym (h2, "call_me"); + if (fp == NULL) + { + printf ("cannot get address of \"call_me\": %s\n", dlerror ()); + exit (1); + } + + /* Call the function. */ + if (fp () != 0) + { + puts ("function \"call_me\" returned wrong result"); + exit (1); + } + + /* Now close the first object. It must still be around since we have + an implicit dependency. */ + if (dlclose (h1) != 0) + { + printf ("closing h1 failed: %s\n", dlerror ()); + exit (1); + } + + /* Open the first object again. */ + h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL); + if (h1 == NULL) + { + printf ("cannot open reldepmod1.so the second time: %s\n", dlerror ()); + exit (1); + } + + /* Get the variable address again. */ + vp = dlsym (h1, "some_var"); + if (vp == NULL) + { + printf ("cannot get address of \"some_var\" the second time: %s\n", + dlerror ()); + exit (1); + } + + /* The variable now must have its originial value. */ + if (*vp != 42) + { + puts ("variable \"some_var\" reset"); + exit (1); + } + + /* Close the first object again, we are done. */ + if (dlclose (h1) != 0) + { + printf ("closing h1 failed: %s\n", dlerror ()); + exit (1); + } + if (dlclose (h2) != 0) + { + printf ("closing h2 failed: %s\n", dlerror ()); + exit (1); + } + + return 0; +} diff --git a/libc/elf/reldep3.c b/libc/elf/reldep3.c new file mode 100644 index 000000000..b051c41db --- /dev/null +++ b/libc/elf/reldep3.c @@ -0,0 +1,101 @@ +#include <dlfcn.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + void *h1; + void *h2; + int (*fp) (void); + int *vp; + + mtrace (); + + /* Open the two objects. */ + h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL); + if (h1 == NULL) + { + printf ("cannot open reldepmod1.so: %s\n", dlerror ()); + exit (1); + } + h2 = dlopen ("reldepmod4.so", RTLD_LAZY); + if (h2 == NULL) + { + printf ("cannot open reldepmod4.so: %s\n", dlerror ()); + exit (1); + } + + /* Get the address of the variable in reldepmod1.so. */ + vp = dlsym (h1, "some_var"); + if (vp == NULL) + { + printf ("cannot get address of \"some_var\": %s\n", dlerror ()); + exit (1); + } + + *vp = 42; + + /* Get the function `call_me' in the second object. This has a + dependency which is resolved by a definition in reldepmod1.so. */ + fp = dlsym (h2, "call_me"); + if (fp == NULL) + { + printf ("cannot get address of \"call_me\": %s\n", dlerror ()); + exit (1); + } + + /* Call the function. */ + if (fp () != 0) + { + puts ("function \"call_me\" returned wrong result"); + exit (1); + } + + /* Now close the first object. If must still be around since we have + a implicit dependency. */ + if (dlclose (h1) != 0) + { + printf ("closing h1 failed: %s\n", dlerror ()); + exit (1); + } + + /* Open the first object again. */ + h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL); + if (h1 == NULL) + { + printf ("cannot open reldepmod1.so the second time: %s\n", dlerror ()); + exit (1); + } + + /* Get the variable address again. */ + vp = dlsym (h1, "some_var"); + if (vp == NULL) + { + printf ("cannot get address of \"some_var\" the second time: %s\n", + dlerror ()); + exit (1); + } + + /* The variable now must have its originial value. */ + if (*vp != 0) + { + puts ("variable \"some_var\" not reset"); + exit (1); + } + + /* Close the first object again, we are done. */ + if (dlclose (h1) != 0) + { + printf ("closing h1 failed: %s\n", dlerror ()); + exit (1); + } + if (dlclose (h2) != 0) + { + printf ("closing h2 failed: %s\n", dlerror ()); + exit (1); + } + + return 0; +} diff --git a/libc/elf/reldep4.c b/libc/elf/reldep4.c new file mode 100644 index 000000000..ba12e7d9c --- /dev/null +++ b/libc/elf/reldep4.c @@ -0,0 +1,40 @@ +#include <dlfcn.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + int i; + void *h1, *h2; + + mtrace (); + + for (i = 0; i < 3; i++) + { + h1 = dlopen ("reldep4mod1.so", RTLD_NOW | RTLD_GLOBAL); + if (h1 == NULL) + { + printf ("cannot open reldep4mod1.so: %s\n", dlerror ()); + exit (1); + } + h2 = dlopen ("reldep4mod2.so", RTLD_NOW | RTLD_GLOBAL); + if (h2 == NULL) + { + printf ("cannot open reldep4mod2.so: %s\n", dlerror ()); + exit (1); + } + if (dlclose (h1) != 0) + { + printf ("closing h1 failed: %s\n", dlerror ()); + exit (1); + } + if (dlclose (h2) != 0) + { + printf ("closing h2 failed: %s\n", dlerror ()); + exit (1); + } + } + return 0; +} diff --git a/libc/elf/reldep4mod1.c b/libc/elf/reldep4mod1.c new file mode 100644 index 000000000..934a68096 --- /dev/null +++ b/libc/elf/reldep4mod1.c @@ -0,0 +1,7 @@ +int foo (void); + +int foo (void) +{ + return 0; +} + diff --git a/libc/elf/reldep4mod2.c b/libc/elf/reldep4mod2.c new file mode 100644 index 000000000..26ce6bf13 --- /dev/null +++ b/libc/elf/reldep4mod2.c @@ -0,0 +1,8 @@ +extern int foo (void); +extern int bar (void); + +int +bar (void) +{ + return foo (); +} diff --git a/libc/elf/reldep4mod3.c b/libc/elf/reldep4mod3.c new file mode 100644 index 000000000..934a68096 --- /dev/null +++ b/libc/elf/reldep4mod3.c @@ -0,0 +1,7 @@ +int foo (void); + +int foo (void) +{ + return 0; +} + diff --git a/libc/elf/reldep4mod4.c b/libc/elf/reldep4mod4.c new file mode 100644 index 000000000..26ce6bf13 --- /dev/null +++ b/libc/elf/reldep4mod4.c @@ -0,0 +1,8 @@ +extern int foo (void); +extern int bar (void); + +int +bar (void) +{ + return foo (); +} diff --git a/libc/elf/reldep5.c b/libc/elf/reldep5.c new file mode 100644 index 000000000..881d519ff --- /dev/null +++ b/libc/elf/reldep5.c @@ -0,0 +1,70 @@ +#include <dlfcn.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + void *h1; + void *h2; + int (*fp) (void); + + mtrace (); + + /* Open the two objects. */ + h1 = dlopen ("reldepmod5.so", RTLD_LAZY); + if (h1 == NULL) + { + printf ("cannot open reldepmod5.so: %s\n", dlerror ()); + exit (1); + } + h2 = dlopen ("reldepmod6.so", RTLD_LAZY); + if (h2 == NULL) + { + printf ("cannot open reldepmod6.so: %s\n", dlerror ()); + exit (1); + } + + /* Get the address of the variable in reldepmod1.so. */ + fp = dlsym (h2, "bar"); + if (fp == NULL) + { + printf ("cannot get address of \"bar\": %s\n", dlerror ()); + exit (1); + } + + /* Call the function. */ + puts ("calling fp for the first time"); + if (fp () != 0) + { + puts ("function \"call_me\" returned wrong result"); + exit (1); + } + + /* Now close the first object. It must still be around since we have + an implicit dependency. */ + if (dlclose (h1) != 0) + { + printf ("closing h1 failed: %s\n", dlerror ()); + exit (1); + } + + /* Calling the function must still work. */ + puts ("calling fp for the second time"); + if (fp () != 0) + { + puts ("function \"call_me\" the second time returned wrong result"); + exit (1); + } + puts ("second call suceeded as well"); + + /* Close the second object, we are done. */ + if (dlclose (h2) != 0) + { + printf ("closing h2 failed: %s\n", dlerror ()); + exit (1); + } + + return 0; +} diff --git a/libc/elf/reldep6.c b/libc/elf/reldep6.c new file mode 100644 index 000000000..1eeec6c86 --- /dev/null +++ b/libc/elf/reldep6.c @@ -0,0 +1,109 @@ +#include <dlfcn.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + +typedef int (*fn)(void); +#define CHUNKS 1024 +#define REPEAT 64 + +int +main (void) +{ + void *h1; + void *h2; + fn **foopp; + fn bar, baz; + int i, j; + int n; + void *allocs[REPEAT][CHUNKS]; + + mtrace (); + + /* Open the two objects. */ + h1 = dlopen ("reldep6mod3.so", RTLD_LAZY); + if (h1 == NULL) + { + printf ("cannot open reldep6mod3.so: %s\n", dlerror ()); + exit (1); + } + + foopp = dlsym (h1, "foopp"); + if (foopp == NULL) + { + printf ("cannot get address of \"foopp\": %s\n", dlerror ()); + exit (1); + } + n = (**foopp) (); + if (n != 20) + { + printf ("(**foopp)() return %d, not return 20\n", n); + exit (1); + } + + h2 = dlopen ("reldep6mod4.so", RTLD_LAZY); + if (h2 == NULL) + { + printf ("cannot open reldep6mod4.so: %s\n", dlerror ()); + exit (1); + } + + baz = dlsym (h2, "baz"); + if (baz == NULL) + { + printf ("cannot get address of \"baz\": %s\n", dlerror ()); + exit (1); + } + if (baz () != 31) + { + printf ("baz() did not return 31\n"); + exit (1); + } + + if (dlclose (h1) != 0) + { + printf ("closing h1 failed: %s\n", dlerror ()); + exit (1); + } + + /* Clobber memory. */ + for (i = 0; i < REPEAT; ++i) + for (j = 0; j < CHUNKS; ++j) + allocs[i][j] = calloc (1, j + 1); + + bar = dlsym (h2, "bar"); + if (bar == NULL) + { + printf ("cannot get address of \"bar\": %s\n", dlerror ()); + exit (1); + } + if (bar () != 40) + { + printf ("bar() did not return 40\n"); + exit (1); + } + + baz = dlsym (h2, "baz"); + if (baz == NULL) + { + printf ("cannot get address of \"baz\": %s\n", dlerror ()); + exit (1); + } + if (baz () != 31) + { + printf ("baz() did not return 31\n"); + exit (1); + } + + for (i = 0; i < REPEAT; ++i) + for (j = 0; j < CHUNKS; ++j) + free (allocs[i][j]); + + if (dlclose (h2) != 0) + { + printf ("closing h2 failed: %s\n", dlerror ()); + exit (1); + } + + return 0; +} diff --git a/libc/elf/reldep6mod0.c b/libc/elf/reldep6mod0.c new file mode 100644 index 000000000..58f3745fb --- /dev/null +++ b/libc/elf/reldep6mod0.c @@ -0,0 +1,8 @@ +int bar (void); +extern void free (void *); + +int bar (void) +{ + free (0); + return 40; +} diff --git a/libc/elf/reldep6mod1.c b/libc/elf/reldep6mod1.c new file mode 100644 index 000000000..037a73a19 --- /dev/null +++ b/libc/elf/reldep6mod1.c @@ -0,0 +1,14 @@ +int foo (void); +int baz (void); +extern int weak (void); +asm (".weak weak"); + +int foo (void) +{ + return 20; +} + +int baz (void) +{ + return weak () + 1; +} diff --git a/libc/elf/reldep6mod2.c b/libc/elf/reldep6mod2.c new file mode 100644 index 000000000..c2ef3f9bc --- /dev/null +++ b/libc/elf/reldep6mod2.c @@ -0,0 +1,3 @@ +extern int foo (void); + +void *foop = (void *) foo; diff --git a/libc/elf/reldep6mod3.c b/libc/elf/reldep6mod3.c new file mode 100644 index 000000000..881828ef6 --- /dev/null +++ b/libc/elf/reldep6mod3.c @@ -0,0 +1,3 @@ +extern void *foop; + +void **foopp = &foop; diff --git a/libc/elf/reldep6mod4.c b/libc/elf/reldep6mod4.c new file mode 100644 index 000000000..8fa89de64 --- /dev/null +++ b/libc/elf/reldep6mod4.c @@ -0,0 +1,12 @@ +int foo (void); +int weak (void); + +int foo (void) +{ + return 10; +} + +int weak (void) +{ + return 30; +} diff --git a/libc/elf/reldep7.c b/libc/elf/reldep7.c new file mode 100644 index 000000000..5275a0e8f --- /dev/null +++ b/libc/elf/reldep7.c @@ -0,0 +1,58 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + void *h1; + void *h2; + void *mod1_bar, *mod2_bar; + + h1 = dlopen ("reldep7mod1.so", RTLD_GLOBAL | RTLD_LAZY); + if (h1 == NULL) + { + printf ("cannot open reldep7mod1.so: %s\n", dlerror ()); + exit (1); + } + + h2 = dlopen ("reldep7mod2.so", RTLD_GLOBAL | RTLD_LAZY); + if (h2 == NULL) + { + printf ("cannot open reldep7mod1.so: %s\n", dlerror ()); + exit (1); + } + + mod1_bar = dlsym (h1, "mod1_bar"); + if (mod1_bar == NULL) + { + printf ("cannot get address of \"mod1_bar\": %s\n", dlerror ()); + exit (1); + } + + mod2_bar = dlsym (h2, "mod2_bar"); + if (mod2_bar == NULL) + { + printf ("cannot get address of \"mod2_bar\": %s\n", dlerror ()); + exit (1); + } + + printf ("%d\n", ((int (*) (void)) mod1_bar) ()); + printf ("%d\n", ((int (*) (void)) mod2_bar) ()); + + if (dlclose (h1) != 0) + { + printf ("closing h1 failed: %s\n", dlerror ()); + exit (1); + } + + printf ("%d\n", ((int (*) (void)) mod2_bar) ()); + + if (dlclose (h2) != 0) + { + printf ("closing h2 failed: %s\n", dlerror ()); + exit (1); + } + + return 0; +} diff --git a/libc/elf/reldep7mod1.c b/libc/elf/reldep7mod1.c new file mode 100644 index 000000000..de1bb3a6c --- /dev/null +++ b/libc/elf/reldep7mod1.c @@ -0,0 +1,12 @@ +int foo (void) __attribute__ ((weak)); +int +foo (void) +{ + return 1; +} + +int +mod1_bar (void) +{ + return foo (); +} diff --git a/libc/elf/reldep7mod2.c b/libc/elf/reldep7mod2.c new file mode 100644 index 000000000..3fa336879 --- /dev/null +++ b/libc/elf/reldep7mod2.c @@ -0,0 +1,12 @@ +int foo (void) __attribute__ ((weak)); +int +foo (void) +{ + return 2; +} + +int +mod2_bar (void) +{ + return foo (); +} diff --git a/libc/elf/reldep8.c b/libc/elf/reldep8.c new file mode 100644 index 000000000..90009a560 --- /dev/null +++ b/libc/elf/reldep8.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> + +int +main (void) +{ + void *handle = dlopen ("reldep8mod3.so", RTLD_LAZY); + if (handle == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + dlclose (handle); + abort (); +} diff --git a/libc/elf/reldep8mod1.c b/libc/elf/reldep8mod1.c new file mode 100644 index 000000000..acddc4cf8 --- /dev/null +++ b/libc/elf/reldep8mod1.c @@ -0,0 +1,19 @@ +#include <stdlib.h> +void +foo (void) +{ + exit (0); +} + +void +__attribute__((destructor)) +bar (void) +{ + static int i; + foo (); + ++i; +} +void +baz (void) +{ +} diff --git a/libc/elf/reldep8mod2.c b/libc/elf/reldep8mod2.c new file mode 100644 index 000000000..d0020240a --- /dev/null +++ b/libc/elf/reldep8mod2.c @@ -0,0 +1,7 @@ +void +__attribute__((constructor)) +xxx (void) +{ + extern void baz (void); + baz (); +} diff --git a/libc/elf/reldep8mod3.c b/libc/elf/reldep8mod3.c new file mode 100644 index 000000000..6d1a0d47b --- /dev/null +++ b/libc/elf/reldep8mod3.c @@ -0,0 +1 @@ +int x; diff --git a/libc/elf/reldep9.c b/libc/elf/reldep9.c new file mode 100644 index 000000000..51c7a8bb9 --- /dev/null +++ b/libc/elf/reldep9.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> + +int +main (void) +{ + void *handle = dlopen ("reldep9mod3.so", RTLD_LAZY); + if (handle == NULL) + { + printf ("%s\n", dlerror ()); + exit (1); + } + dlclose (handle); + abort (); +} diff --git a/libc/elf/reldep9mod1.c b/libc/elf/reldep9mod1.c new file mode 100644 index 000000000..249a2bae1 --- /dev/null +++ b/libc/elf/reldep9mod1.c @@ -0,0 +1,23 @@ +#include <stdlib.h> +void +foo (void) +{ + exit (0); +} + +void +__attribute__((destructor)) +bar (void) +{ + static int i; + foo (); + ++i; +} + +void +__attribute__((constructor)) +destr (void) +{ + extern void baz (void); + baz (); +} diff --git a/libc/elf/reldep9mod2.c b/libc/elf/reldep9mod2.c new file mode 100644 index 000000000..090966e3e --- /dev/null +++ b/libc/elf/reldep9mod2.c @@ -0,0 +1,3 @@ +void baz (void) +{ +} diff --git a/libc/elf/reldep9mod3.c b/libc/elf/reldep9mod3.c new file mode 100644 index 000000000..6d1a0d47b --- /dev/null +++ b/libc/elf/reldep9mod3.c @@ -0,0 +1 @@ +int x; diff --git a/libc/elf/reldepmod1.c b/libc/elf/reldepmod1.c new file mode 100644 index 000000000..b8ef6401e --- /dev/null +++ b/libc/elf/reldepmod1.c @@ -0,0 +1,10 @@ +extern int foo (void); + +int some_var; + + +int +foo (void) +{ + return some_var; +} diff --git a/libc/elf/reldepmod2.c b/libc/elf/reldepmod2.c new file mode 100644 index 000000000..b7edebae8 --- /dev/null +++ b/libc/elf/reldepmod2.c @@ -0,0 +1,8 @@ +extern int foo (void); +extern int call_me (void); + +int +call_me (void) +{ + return foo () - 42; +} diff --git a/libc/elf/reldepmod3.c b/libc/elf/reldepmod3.c new file mode 100644 index 000000000..66a996cd9 --- /dev/null +++ b/libc/elf/reldepmod3.c @@ -0,0 +1,20 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +extern int call_me (void); + +int +call_me (void) +{ + int (*fp) (void); + + fp = dlsym (RTLD_DEFAULT, "foo"); + if (fp == NULL) + { + printf ("cannot get address of foo in global scope: %s\n", dlerror ()); + exit (1); + } + + return fp () - 42; +} diff --git a/libc/elf/reldepmod4.c b/libc/elf/reldepmod4.c new file mode 100644 index 000000000..dcb503bba --- /dev/null +++ b/libc/elf/reldepmod4.c @@ -0,0 +1,37 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +extern int call_me (void); + +int +call_me (void) +{ + void *h; + int (*fp) (void); + int res; + + h = dlopen ("reldepmod1.so", RTLD_LAZY); + if (h == NULL) + { + printf ("cannot open reldepmod1.so in %s: %s\n", __FILE__, dlerror ()); + exit (1); + } + + fp = dlsym (h, "foo"); + if (fp == NULL) + { + printf ("cannot get address of foo in global scope: %s\n", dlerror ()); + exit (1); + } + + res = fp () - 42; + + if (dlclose (h) != 0) + { + printf ("failure when closing h in %s: %s\n", __FILE__, dlerror ()); + exit (1); + } + + return res; +} diff --git a/libc/elf/reldepmod5.c b/libc/elf/reldepmod5.c new file mode 100644 index 000000000..62df69716 --- /dev/null +++ b/libc/elf/reldepmod5.c @@ -0,0 +1,7 @@ +extern int foo (void); + +int +foo (void) +{ + return 42; +} diff --git a/libc/elf/reldepmod6.c b/libc/elf/reldepmod6.c new file mode 100644 index 000000000..cd2aeb400 --- /dev/null +++ b/libc/elf/reldepmod6.c @@ -0,0 +1,8 @@ +extern int call_me (void); +extern int bar (void); + +int +bar (void) +{ + return call_me (); +} diff --git a/libc/elf/resolvfail.c b/libc/elf/resolvfail.c new file mode 100644 index 000000000..ebd635d15 --- /dev/null +++ b/libc/elf/resolvfail.c @@ -0,0 +1,31 @@ +#include <dlfcn.h> +#include <stdio.h> + +static const char obj[] = "testobj1.so"; + +int +main (void) +{ + void *d = dlopen (obj, RTLD_LAZY); + int n; + + if (d == NULL) + { + printf ("cannot load %s: %s\n", obj, dlerror ()); + return 1; + } + + for (n = 0; n < 10000; ++n) + if (dlsym (d, "does not exist") != NULL) + { + puts ("dlsym() did not fail"); + return 1; + } + else if (dlerror () == NULL) + { + puts ("dlerror() didn't return a string"); + return 1; + } + + return 0; +} diff --git a/libc/elf/restest1.c b/libc/elf/restest1.c new file mode 100644 index 000000000..eb5aeca59 --- /dev/null +++ b/libc/elf/restest1.c @@ -0,0 +1,57 @@ +#include <dlfcn.h> +#include <error.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + void *h1; + int (*fp1) (int); + void *h2; + int (*fp2) (int); + int res1; + int res2; + + mtrace (); + + h1 = dlopen ("testobj1.so", RTLD_LAZY); + if (h1 == NULL) + error (EXIT_FAILURE, 0, "while loading `%s': %s", "testobj1.so", + dlerror ()); + + h2 = dlopen ("testobj1_1.so", RTLD_LAZY); + if (h1 == NULL) + error (EXIT_FAILURE, 0, "while loading `%s': %s", "testobj1_1.so", + dlerror ()); + + fp1 = dlsym (h1, "obj1func1"); + if (fp1 == NULL) + error (EXIT_FAILURE, 0, "getting `obj1func1' in `%s': %s", + "testobj1.so", dlerror ()); + + fp2 = dlsym (h2, "obj1func1"); + if (fp2 == NULL) + error (EXIT_FAILURE, 0, "getting `obj1func1' in `%s': %s", + "testobj1_1.so", dlerror ()); + + res1 = fp1 (10); + res2 = fp2 (10); + printf ("fp1(10) = %d\nfp2(10) = %d\n", res1, res2); + + if (dlclose (h1) != 0) + error (EXIT_FAILURE, 0, "cannot close testobj1.so: %s\n", dlerror ()); + if (dlclose (h2) != 0) + error (EXIT_FAILURE, 0, "cannot close testobj1_1.so: %s\n", dlerror ()); + + return res1 != 42 || res2 != 72; +} + + +extern int foo (int a); +int +foo (int a) +{ + return a + 10; +} diff --git a/libc/elf/restest2.c b/libc/elf/restest2.c new file mode 100644 index 000000000..f959f030a --- /dev/null +++ b/libc/elf/restest2.c @@ -0,0 +1,33 @@ +#include <sys/types.h> +#include <dlfcn.h> +#include <error.h> +#include <mcheck.h> +#include <stdlib.h> +#include <unistd.h> + +pid_t pid, pid2; + +pid_t getpid(void) +{ + pid_t (*f)(void); + f = (pid_t (*)(void)) dlsym (RTLD_NEXT, "getpid"); + if (f == NULL) + error (EXIT_FAILURE, 0, "dlsym (RTLD_NEXT, \"getpid\"): %s", dlerror ()); + return (pid2 = f()) + 26; +} + +int +main (void) +{ + pid_t (*f)(void); + + mtrace (); + + f = (pid_t (*)(void)) dlsym (RTLD_DEFAULT, "getpid"); + if (f == NULL) + error (EXIT_FAILURE, 0, "dlsym (RTLD_DEFAULT, \"getpid\"): %s", dlerror ()); + pid = f(); + if (pid != pid2 + 26) + error (EXIT_FAILURE, 0, "main getpid() not called"); + return 0; +} diff --git a/libc/elf/rtld-Rules b/libc/elf/rtld-Rules new file mode 100644 index 000000000..01fbbdf0c --- /dev/null +++ b/libc/elf/rtld-Rules @@ -0,0 +1,133 @@ +# Subroutine makefile for compiling libc modules linked into dynamic linker. + +# 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 +# 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 makefile is never used by itself, but only from the rtld-libc.a +# rule in Makefile, which does make -f librtld.mk -f rtld-Rules. +# librtld.mk is the generated file containing variable definitions for +# `rtld-subdirs', a subset of the top-level $(subdirs) list; and for each +# SUBDIR in $(rtld-subdirs), `rtld-SUBDIR' listing `module.os' file names. + +.PHONY: rtld-all +rtld-all: + +# When run from the elf/Makefile to build rtld-libc.a, $(subdir) is 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 +endif + +include ../Makeconfig + +rtld-all: $(objpfx)rtld-libc.a + +$(objpfx)rtld-libc.a: $(foreach dir,$(rtld-subdirs),\ + $(addprefix $(common-objpfx)$(dir)/rtld-,\ + $(rtld-$(dir)))) + @-rm -f $@T + $(AR) cq$(verbose) $@T $^ + $(RANLIB) $@T + mv -f $@T $@ + +# Use the verbose option of ar and tar when not running silently. +ifeq "$(findstring s,$(MAKEFLAGS))" "" # if not -s +verbose := v +else # -s +verbose := +endif # not -s + + +# For each subdirectory, define a pattern rule that makes all of that +# subdirectory's modules at once with one recursive make command. +object-suffixes-left := $(rtld-subdirs) +define o-iterator-doit +$(foreach obj,$(rtld-$o),$(common-objpfx)%/rtld-$(obj)): FORCE ; \ + +$$(rtld-subdir-make) +endef +include $(patsubst %,../o-iterator.mk,$(object-suffixes-left)) + +# This is how we descend into each subdirectory. See below. +define rtld-subdir-make +$(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 + +# In this case we are being run by $(rtld-subdir-make), above. +# Some other subdir's Makefile has provided all its normal rules, +# and we just provide some additional definitions. + +# These are the basic compilation rules corresponding to the Makerules ones. +# The sysd-rules generated makefile already defines pattern rules for rtld-% +# targets built from sysdeps source files. +$(objpfx)rtld-%.os: %.S $(before-compile); $(compile-command.S) +$(objpfx)rtld-%.os: %.s $(before-compile); $(compile-command.s) +$(objpfx)rtld-%.os: %.c $(before-compile); $(compile-command.c) + +# The rules for generated source files. +$(objpfx)rtld-%.os: $(objpfx)%.S $(before-compile); $(compile-command.S) +$(objpfx)rtld-%.os: $(objpfx)%.s $(before-compile); $(compile-command.s) +$(objpfx)rtld-%.os: $(objpfx)%.c $(before-compile); $(compile-command.c) + +# The command line setting of rtld-modules (see above) tells us +# what we need to build, and that tells us what dependency files we need. +rtld-all: $(addprefix $(objpfx),$(rtld-modules)) + +# Figure out the dependency files we need. After respecting the $(omit-deps) +# list as applied to the names without the `rtld-', there may be none left. +rtld-depfiles := $(patsubst %,$(objpfx)rtld-%.os.d,\ + $(filter-out $(omit-deps),\ + $(rtld-modules:rtld-%.os=%))) +rtld-depfiles := $(strip $(wildcard $(rtld-depfiles)) \ + $(patsubst %.dt,%.d,\ + $(wildcard $(rtld-depfiles:.d=.dt)))) +ifdef rtld-depfiles +-include $(rtld-depfiles) +endif + +# Just in case we wind up e.g. regenerating dependencies for non-rtld objects, +# we don't unconditionally modify the flags. For rtld-% targets, use the +# special flags set below. +CFLAGS += $(if $(filter rtld-%,$(@F)),$(CFLAGS-rtld)) +CPPFLAGS += $(if $(filter rtld-%,$(@F)),$(CPPFLAGS-rtld)) + + +# This here is the whole point of all the shenanigans. +CPPFLAGS-rtld := -DNOT_IN_libc=1 -DIS_IN_rtld=1 +CFLAGS-rtld := # blah + + +endif diff --git a/libc/elf/rtld.c b/libc/elf/rtld.c new file mode 100644 index 000000000..9a21b8bc6 --- /dev/null +++ b/libc/elf/rtld.c @@ -0,0 +1,2826 @@ +/* Run time dynamic linker. + Copyright (C) 1995-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 <errno.h> +#include <dlfcn.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> /* Check if MAP_ANON is defined. */ +#include <sys/param.h> +#include <sys/stat.h> +#include <ldsodefs.h> +#include <stdio-common/_itoa.h> +#include <entry.h> +#include <fpu_control.h> +#include <hp-timing.h> +#include <bits/libc-lock.h> +#include "dynamic-link.h" +#include <dl-librecon.h> +#include <unsecvars.h> +#include <dl-cache.h> +#include <dl-osinfo.h> +#include <dl-procinfo.h> +#include <tls.h> + +#include <assert.h> + +/* Avoid PLT use for our local calls at startup. */ +extern __typeof (__mempcpy) __mempcpy attribute_hidden; + +/* GCC has mental blocks about _exit. */ +extern __typeof (_exit) exit_internal asm ("_exit") attribute_hidden; +#define _exit exit_internal + +/* Helper function to handle errors while resolving symbols. */ +static void print_unresolved (int errcode, const char *objname, + const char *errsting); + +/* Helper function to handle errors when a version is missing. */ +static void print_missing_version (int errcode, const char *objname, + const char *errsting); + +/* 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 }; + +/* 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. */ +static void process_envvars (enum mode *modep); + +#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) + +#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 + libraries, cleared before the executable's entry point runs. This + must not be initialized to nonzero, because the unused dynamic + linker loaded in for libc.so's "ld.so.1" dep will provide the + definition seen by libc.so's initializer; that value must be zero, + and will be since that dynamic linker's _dl_start and dl_main will + never be called. */ +int _dl_starting_up = 0; +INTVARDEF(_dl_starting_up) +#endif + +/* This is the structure which defines all variables global to ld.so + (except those which cannot be added for some reason). */ +struct rtld_global _rtld_global = + { + /* Default presumption without further information is executable stack. */ + ._dl_stack_flags = PF_R|PF_W|PF_X, +#ifdef _LIBC_REENTRANT + ._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER +#endif + }; +/* If we would use strong_alias here the compiler would see a + non-hidden definition. This would undo the effect of the previous + declaration. So spell out was strong_alias does plus add the + visibility attribute. */ +extern struct rtld_global _rtld_local + __attribute__ ((alias ("_rtld_global"), visibility ("hidden"))); + + +/* This variable is similar to _rtld_local, but all values are + read-only after relocation. */ +struct rtld_global_ro _rtld_global_ro attribute_relro = + { + /* Get architecture specific initializer. */ +#include <dl-procinfo.c> +#ifdef NEED_DL_SYSINFO + ._dl_sysinfo = DL_SYSINFO_DEFAULT, +#endif + ._dl_debug_fd = STDERR_FILENO, + ._dl_use_load_bias = -2, + ._dl_correct_cache_id = _DL_CACHE_DEFAULT_ID, + ._dl_hwcap_mask = HWCAP_IMPORTANT, + ._dl_lazy = 1, + ._dl_fpu_control = _FPU_DEFAULT, + ._dl_pointer_guard = 1, + + /* Function pointers. */ + ._dl_debug_printf = _dl_debug_printf, + ._dl_catch_error = _dl_catch_error, + ._dl_signal_error = _dl_signal_error, + ._dl_mcount = _dl_mcount_internal, + ._dl_lookup_symbol_x = _dl_lookup_symbol_x, + ._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 + declaration. So spell out was strong_alias does plus add the + visibility attribute. */ +extern struct rtld_global_ro _rtld_local_ro + __attribute__ ((alias ("_rtld_global_ro"), visibility ("hidden"))); + + +static void dl_main (const ElfW(Phdr) *phdr, ElfW(Word) phnum, + ElfW(Addr) *user_entry); + +/* These two variables cannot be moved into .data.rel.ro. */ +static struct libname_list _dl_rtld_libname; +static struct libname_list _dl_rtld_libname2; + +/* We expect less than a second for relocation. */ +#ifdef HP_SMALL_TIMING_AVAIL +# undef HP_TIMING_AVAIL +# define HP_TIMING_AVAIL HP_SMALL_TIMING_AVAIL +#endif + +/* Variable for statistics. */ +#ifndef HP_TIMING_NONAVAIL +static hp_timing_t relocate_time; +static hp_timing_t load_time attribute_relro; +static hp_timing_t start_time attribute_relro; +#endif + +/* Additional definitions needed by TLS initialization. */ +#ifdef TLS_INIT_HELPER +TLS_INIT_HELPER +#endif + +/* Helper function for syscall implementation. */ +#ifdef DL_SYSINFO_IMPLEMENTATION +DL_SYSINFO_IMPLEMENTATION +#endif + +/* Before ld.so is relocated we must not access variables which need + relocations. This means variables which are exported. Variables + declared as static are fine. If we can mark a variable hidden this + is fine, too. The latter is important here. We can avoid setting + up a temporary link map for ld.so if we can mark _rtld_global as + hidden. */ +#if defined PI_STATIC_AND_HIDDEN && defined HAVE_HIDDEN \ + && defined HAVE_VISIBILITY_ATTRIBUTE +# define DONT_USE_BOOTSTRAP_MAP 1 +#endif + +#ifdef DONT_USE_BOOTSTRAP_MAP +static ElfW(Addr) _dl_start_final (void *arg); +#else +struct dl_start_final_info +{ + struct link_map l; +#if !defined HP_TIMING_NONAVAIL && HP_TIMING_INLINE + hp_timing_t start_time; +#endif +}; +static ElfW(Addr) _dl_start_final (void *arg, + struct dl_start_final_info *info); +#endif + +/* These defined magically in the linker script. */ +extern char _begin[] attribute_hidden; +extern char _etext[] attribute_hidden; +extern char _end[] attribute_hidden; + + +#ifdef RTLD_START +RTLD_START +#else +# error "sysdeps/MACHINE/dl-machine.h fails to define RTLD_START" +#endif + +#ifndef VALIDX +# define VALIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \ + + DT_EXTRANUM + DT_VALTAGIDX (tag)) +#endif +#ifndef ADDRIDX +# define ADDRIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \ + + DT_EXTRANUM + DT_VALNUM + DT_ADDRTAGIDX (tag)) +#endif + +/* This is the second half of _dl_start (below). It can be inlined safely + under DONT_USE_BOOTSTRAP_MAP, where it is careful not to make any GOT + references. When the tools don't permit us to avoid using a GOT entry + for _dl_rtld_global (no attribute_hidden support), we must make sure + this function is not inlined (see below). */ + +#ifdef DONT_USE_BOOTSTRAP_MAP +static inline ElfW(Addr) __attribute__ ((always_inline)) +_dl_start_final (void *arg) +#else +static ElfW(Addr) __attribute__ ((noinline)) +_dl_start_final (void *arg, struct dl_start_final_info *info) +#endif +{ + ElfW(Addr) start_addr; + + if (HP_TIMING_AVAIL) + { + /* If it hasn't happen yet record the startup time. */ + if (! HP_TIMING_INLINE) + HP_TIMING_NOW (start_time); +#if !defined DONT_USE_BOOTSTRAP_MAP && !defined HP_TIMING_NONAVAIL + else + start_time = info->start_time; +#endif + + /* Initialize the timing functions. */ + HP_TIMING_DIFF_INIT (); + } + + /* Transfer data about ourselves to the permanent link_map structure. */ +#ifndef DONT_USE_BOOTSTRAP_MAP + GL(dl_rtld_map).l_addr = info->l.l_addr; + GL(dl_rtld_map).l_ld = info->l.l_ld; + 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_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; + /* Copy the TLS related data if necessary. */ +#if USE_TLS && !defined DONT_USE_BOOTSTRAP_MAP +# if USE___THREAD + assert (info->l.l_tls_modid != 0); + GL(dl_rtld_map).l_tls_blocksize = info->l.l_tls_blocksize; + GL(dl_rtld_map).l_tls_align = info->l.l_tls_align; + GL(dl_rtld_map).l_tls_firstbyte_offset = info->l.l_tls_firstbyte_offset; + GL(dl_rtld_map).l_tls_initimage_size = info->l.l_tls_initimage_size; + GL(dl_rtld_map).l_tls_initimage = info->l.l_tls_initimage; + 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 +# endif + +#endif + +#if HP_TIMING_AVAIL + HP_TIMING_NOW (GL(dl_cpuclock_offset)); +#endif + + /* Initialize the stack end variable. */ + __libc_stack_end = __builtin_frame_address (0); + + /* Call the OS-dependent function to set up life so we can do things like + file access. It will call `dl_main' (below) to do all the real work + of the dynamic linker, and then unwind our frame and run the user + entry point on the same stack we entered on. */ + start_addr = _dl_sysdep_start (arg, &dl_main); + +#ifndef HP_TIMING_NONAVAIL + hp_timing_t rtld_total_time; + if (HP_TIMING_AVAIL) + { + hp_timing_t end_time; + + /* Get the current time. */ + HP_TIMING_NOW (end_time); + + /* Compute the difference. */ + HP_TIMING_DIFF (rtld_total_time, start_time, end_time); + } +#endif + + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS, 0)) + { +#ifndef HP_TIMING_NONAVAIL + print_statistics (&rtld_total_time); +#else + print_statistics (NULL); +#endif + } + + return start_addr; +} + +static ElfW(Addr) __attribute_used__ internal_function +_dl_start (void *arg) +{ +#ifdef DONT_USE_BOOTSTRAP_MAP +# define bootstrap_map GL(dl_rtld_map) +#else + struct dl_start_final_info info; +# define bootstrap_map info.l +#endif + + /* This #define produces dynamic linking inline functions for + bootstrap relocation instead of general-purpose relocation. */ +#define RTLD_BOOTSTRAP +#define RESOLVE_MAP(sym, version, flags) \ + ((*(sym))->st_shndx == SHN_UNDEF ? 0 : &bootstrap_map) +#include "dynamic-link.h" + + if (HP_TIMING_INLINE && HP_TIMING_AVAIL) +#ifdef DONT_USE_BOOTSTRAP_MAP + HP_TIMING_NOW (start_time); +#else + HP_TIMING_NOW (info.start_time); +#endif + + /* Partly clean the `bootstrap_map' structure up. Don't use + `memset' since it might not be built in or inlined and we cannot + make function calls at this point. Use '__builtin_memset' if we + know it is available. We do not have to clear the memory if we + do not have to use the temporary bootstrap_map. Global variables + are initialized to zero by default. */ +#ifndef DONT_USE_BOOTSTRAP_MAP +# ifdef HAVE_BUILTIN_MEMSET + __builtin_memset (bootstrap_map.l_info, '\0', sizeof (bootstrap_map.l_info)); +# else + for (size_t cnt = 0; + cnt < sizeof (bootstrap_map.l_info) / sizeof (bootstrap_map.l_info[0]); + ++cnt) + bootstrap_map.l_info[cnt] = 0; +# endif +#endif + + /* Figure out the run-time load address of the dynamic linker itself. */ + bootstrap_map.l_addr = elf_machine_load_address (); + + /* Read our own dynamic section and fill in the info array. */ + bootstrap_map.l_ld = (void *) bootstrap_map.l_addr + elf_machine_dynamic (); + elf_get_dynamic_info (&bootstrap_map, NULL); + +#if defined USE_TLS && NO_TLS_OFFSET != 0 + bootstrap_map.l_tls_offset = NO_TLS_OFFSET; +#endif + + /* Get the dynamic linker's own program header. First we need the ELF + file header. The `_begin' symbol created by the linker script points + to it. When we have something like GOTOFF relocs, we can use a plain + reference to find the runtime address. Without that, we have to rely + on the `l_addr' value, which is not the value we want when prelinked. */ +#if USE___THREAD + dtv_t initdtv[3]; + ElfW(Ehdr) *ehdr +# ifdef DONT_USE_BOOTSTRAP_MAP + = (ElfW(Ehdr) *) &_begin; +# else +# error This will not work with prelink. + = (ElfW(Ehdr) *) bootstrap_map.l_addr; +# endif + ElfW(Phdr) *phdr = (ElfW(Phdr) *) ((void *) ehdr + ehdr->e_phoff); + size_t cnt = ehdr->e_phnum; /* PT_TLS is usually the last phdr. */ + while (cnt-- > 0) + if (phdr[cnt].p_type == PT_TLS) + { + void *tlsblock; + size_t max_align = MAX (TLS_INIT_TCB_ALIGN, phdr[cnt].p_align); + char *p; + + bootstrap_map.l_tls_blocksize = phdr[cnt].p_memsz; + bootstrap_map.l_tls_align = phdr[cnt].p_align; + if (phdr[cnt].p_align == 0) + bootstrap_map.l_tls_firstbyte_offset = 0; + else + bootstrap_map.l_tls_firstbyte_offset = (phdr[cnt].p_vaddr + & (phdr[cnt].p_align - 1)); + assert (bootstrap_map.l_tls_blocksize != 0); + bootstrap_map.l_tls_initimage_size = phdr[cnt].p_filesz; + bootstrap_map.l_tls_initimage = (void *) (bootstrap_map.l_addr + + phdr[cnt].p_vaddr); + + /* We can now allocate the initial TLS block. This can happen + on the stack. We'll get the final memory later when we + know all about the various objects loaded at startup + time. */ +# if TLS_TCB_AT_TP + tlsblock = alloca (roundup (bootstrap_map.l_tls_blocksize, + TLS_INIT_TCB_ALIGN) + + TLS_INIT_TCB_SIZE + + max_align); +# elif TLS_DTV_AT_TP + tlsblock = alloca (roundup (TLS_INIT_TCB_SIZE, + bootstrap_map.l_tls_align) + + bootstrap_map.l_tls_blocksize + + max_align); +# else + /* In case a model with a different layout for the TCB and DTV + is defined add another #elif here and in the following #ifs. */ +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" +# endif + /* Align the TLS block. */ + tlsblock = (void *) (((uintptr_t) tlsblock + max_align - 1) + & ~(max_align - 1)); + + /* Initialize the dtv. [0] is the length, [1] the generation + counter. */ + initdtv[0].counter = 1; + initdtv[1].counter = 0; + + /* Initialize the TLS block. */ +# if TLS_TCB_AT_TP + initdtv[2].pointer = tlsblock; +# elif TLS_DTV_AT_TP + bootstrap_map.l_tls_offset = roundup (TLS_INIT_TCB_SIZE, + bootstrap_map.l_tls_align); + initdtv[2].pointer = (char *) tlsblock + bootstrap_map.l_tls_offset; +# else +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" +# endif + p = __mempcpy (initdtv[2].pointer, bootstrap_map.l_tls_initimage, + bootstrap_map.l_tls_initimage_size); +# ifdef HAVE_BUILTIN_MEMSET + __builtin_memset (p, '\0', (bootstrap_map.l_tls_blocksize + - bootstrap_map.l_tls_initimage_size)); +# else + { + size_t remaining = (bootstrap_map.l_tls_blocksize + - bootstrap_map.l_tls_initimage_size); + while (remaining-- > 0) + *p++ = '\0'; + } +# endif + + /* Install the pointer to the dtv. */ + + /* Initialize the thread pointer. */ +# if TLS_TCB_AT_TP + bootstrap_map.l_tls_offset + = roundup (bootstrap_map.l_tls_blocksize, TLS_INIT_TCB_ALIGN); + + INSTALL_DTV ((char *) tlsblock + bootstrap_map.l_tls_offset, + initdtv); + + const char *lossage = TLS_INIT_TP ((char *) tlsblock + + bootstrap_map.l_tls_offset, 0); +# elif TLS_DTV_AT_TP + INSTALL_DTV (tlsblock, initdtv); + const char *lossage = TLS_INIT_TP (tlsblock, 0); +# else +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" +# endif + if (__builtin_expect (lossage != NULL, 0)) + _dl_fatal_printf ("cannot set up thread-local storage: %s\n", + lossage); + + /* So far this is module number one. */ + bootstrap_map.l_tls_modid = 1; + + /* There can only be one PT_TLS entry. */ + break; + } +#endif /* USE___THREAD */ + +#ifdef ELF_MACHINE_BEFORE_RTLD_RELOC + ELF_MACHINE_BEFORE_RTLD_RELOC (bootstrap_map.l_info); +#endif + + if (bootstrap_map.l_addr || ! bootstrap_map.l_info[VALIDX(DT_GNU_PRELINKED)]) + { + /* Relocate ourselves so we can do normal function calls and + data access using the global offset table. */ + + 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 + for the relocation results (as done in dl-reloc.c). */ + + /* Now life is sane; we can call functions and access global data. + Set up to use the operating system facilities, and find out from + the operating system's program loader where to find the program + header table in core. Put the rest of _dl_start into a separate + function, that way the compiler cannot put accesses to the GOT + before ELF_DYNAMIC_RELOCATE. */ + { +#ifdef DONT_USE_BOOTSTRAP_MAP + ElfW(Addr) entry = _dl_start_final (arg); +#else + ElfW(Addr) entry = _dl_start_final (arg, &info); +#endif + +#ifndef ELF_MACHINE_START_ADDRESS +# define ELF_MACHINE_START_ADDRESS(map, start) (start) +#endif + + return ELF_MACHINE_START_ADDRESS (GL(dl_ns)[LM_ID_BASE]._ns_loaded, entry); + } +} + + + +/* Now life is peachy; we can do all normal operations. + On to the real work. */ + +/* Some helper functions. */ + +/* Arguments to relocate_doit. */ +struct relocate_args +{ + struct link_map *l; + int lazy; +}; + +struct map_args +{ + /* Argument to map_doit. */ + char *str; + struct link_map *loader; + int is_preloaded; + int mode; + /* Return value of map_doit. */ + 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 +{ + int doexit; + int dotrace; +}; + +static void +relocate_doit (void *a) +{ + struct relocate_args *args = (struct relocate_args *) a; + + _dl_relocate_object (args->l, args->l->l_scope, args->lazy, 0); +} + +static void +map_doit (void *a) +{ + struct map_args *args = (struct map_args *) a; + args->map = _dl_map_object (args->loader, args->str, + args->is_preloaded, lt_library, 0, args->mode, + LM_ID_BASE); +} + +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; + if (_dl_check_all_versions (GL(dl_ns)[LM_ID_BASE]._ns_loaded, 1, + args->dotrace) && args->doexit) + /* We cannot start the application. Abort now. */ + _exit (1); +} + + +static inline struct link_map * +find_needed (const char *name) +{ + struct r_scope_elem *scope = &GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_searchlist; + unsigned int n = scope->r_nlist; + + while (n-- > 0) + if (_dl_name_match_p (name, scope->r_list[n])) + return scope->r_list[n]; + + /* Should never happen. */ + return NULL; +} + +static int +match_version (const char *string, struct link_map *map) +{ + const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + ElfW(Verdef) *def; + +#define VERDEFTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (DT_VERDEF)) + if (map->l_info[VERDEFTAG] == NULL) + /* The file has no symbol versioning. */ + return 0; + + def = (ElfW(Verdef) *) ((char *) map->l_addr + + map->l_info[VERDEFTAG]->d_un.d_ptr); + while (1) + { + ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) def + def->vd_aux); + + /* Compare the version strings. */ + if (strcmp (string, strtab + aux->vda_name) == 0) + /* Bingo! */ + return 1; + + /* If no more definitions we failed to find what we want. */ + if (def->vd_next == 0) + break; + + /* Next definition. */ + def = (ElfW(Verdef) *) ((char *) def + def->vd_next); + } + + 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. */ +void ** __attribute__ ((const)) +_dl_initial_error_catch_tsd (void) +{ + static void *data; + return &data; +} +#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) +{ + __rtld_lock_default_lock_recursive (lock); +} + +static void +rtld_lock_default_unlock_recursive (void *lock) +{ + __rtld_lock_default_unlock_recursive (lock); +} +#endif + + +/* The library search path. */ +static const char *library_path attribute_relro; +/* The list preloaded objects. */ +static const char *preloadlist attribute_relro; +/* Nonzero if information about versions has to be printed. */ +static int version_info attribute_relro; + +static void +dl_main (const ElfW(Phdr) *phdr, + ElfW(Word) phnum, + ElfW(Addr) *user_entry) +{ + const ElfW(Phdr) *ph; + enum mode mode; + struct link_map *main_map; + size_t file_size; + char *file; + bool has_interp = false; + unsigned int i; + bool prelinked = false; + bool rtld_is_main = false; +#ifndef HP_TIMING_NONAVAIL + hp_timing_t start; + hp_timing_t stop; + hp_timing_t diff; +#endif +#ifdef USE_TLS + void *tcbp = NULL; +#endif + +#ifdef _LIBC_REENTRANT + /* Explicit initialization since the reloc would just be more work. */ + GL(dl_error_catch_tsd) = &_dl_initial_error_catch_tsd; +#endif + +#ifdef USE_TLS + GL(dl_init_static_tls) = &_dl_nothread_init_static_tls; +#endif + +#if defined SHARED && defined _LIBC_REENTRANT \ + && defined __rtld_lock_default_lock_recursive + GL(dl_rtld_lock_recursive) = rtld_lock_default_lock_recursive; + GL(dl_rtld_unlock_recursive) = rtld_lock_default_unlock_recursive; +#endif + + /* The explicit initialization here is cheaper than processing the reloc + in the _rtld_local definition's initializer. */ + GL(dl_make_stack_executable_hook) = &_dl_make_stack_executable; + + /* Process the environment variable which control the behaviour. */ + process_envvars (&mode); + +#ifndef HAVE_INLINED_SYSCALLS + /* Set up a flag which tells we are just starting. */ + INTUSE(_dl_starting_up) = 1; +#endif + + if (*user_entry == (ElfW(Addr)) ENTRY_POINT) + { + /* Ho ho. We are not the program interpreter! We are the program + itself! This means someone ran ld.so as a command. Well, that + might be convenient to do sometimes. We support it by + interpreting the args like this: + + ld.so PROGRAM ARGS... + + The first argument is the name of a file containing an ELF + executable we will load and run with the following arguments. + To simplify life here, PROGRAM is searched for using the + normal rules for shared objects, rather than $PATH or anything + like that. We just load it and use its entry point; we don't + pay attention to its PT_INTERP command (we are the interpreter + ourselves). This is an easy way to test a new ld.so before + installing it. */ + rtld_is_main = true; + + /* Note the place where the dynamic linker actually came from. */ + GL(dl_rtld_map).l_name = rtld_progname; + + while (_dl_argc > 1) + if (! strcmp (INTUSE(_dl_argv)[1], "--list")) + { + mode = list; + GLRO(dl_lazy) = -1; /* This means do no dependency analysis. */ + + ++_dl_skip_args; + --_dl_argc; + ++INTUSE(_dl_argv); + } + else if (! strcmp (INTUSE(_dl_argv)[1], "--verify")) + { + mode = verify; + + ++_dl_skip_args; + --_dl_argc; + ++INTUSE(_dl_argv); + } + else if (! strcmp (INTUSE(_dl_argv)[1], "--library-path") + && _dl_argc > 2) + { + library_path = INTUSE(_dl_argv)[2]; + + _dl_skip_args += 2; + _dl_argc -= 2; + INTUSE(_dl_argv) += 2; + } + else if (! strcmp (INTUSE(_dl_argv)[1], "--inhibit-rpath") + && _dl_argc > 2) + { + GLRO(dl_inhibit_rpath) = INTUSE(_dl_argv)[2]; + + _dl_skip_args += 2; + _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; + + /* If we have no further argument the program was called incorrectly. + Grant the user some education. */ + if (_dl_argc < 2) + _dl_fatal_printf ("\ +Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\ +You have invoked `ld.so', the helper program for shared library executables.\n\ +This program usually lives in the file `/lib/ld.so', and special directives\n\ +in executable files using ELF shared libraries tell the system's program\n\ +loader to load the helper program from this file. This helper program loads\n\ +the shared libraries needed by the program executable, prepares the program\n\ +to run, and runs it. You may invoke this helper program directly from the\n\ +command line to load and run an ELF executable file; this is like executing\n\ +that file itself, but always uses this helper program from the file you\n\ +specified, instead of the helper program file specified in the executable\n\ +file you run. This is mostly of use for maintainers to test new versions\n\ +of this helper program; chances are you did not intend to run this program.\n\ +\n\ + --list list all dependencies and how they are resolved\n\ + --verify verify that given object really is a dynamically linked\n\ + object we can handle\n\ + --library-path PATH use given PATH instead of content of the environment\n\ + variable LD_LIBRARY_PATH\n\ + --inhibit-rpath LIST ignore RUNPATH and RPATH information in object names\n\ + in LIST\n"); + + ++_dl_skip_args; + --_dl_argc; + ++INTUSE(_dl_argv); + + /* 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 + execute permission. However, this is not really true if the + dynamic linker is the executable the kernel loaded. For this + case, we must reinitialize _dl_stack_flags to match the dynamic + linker itself. If the dynamic linker was built with a + PT_GNU_STACK, then the kernel may have loaded us with a + nonexecutable stack that we will have to make executable when we + load the program below unless it has a PT_GNU_STACK indicating + nonexecutable stack is ok. */ + + for (ph = phdr; ph < &phdr[phnum]; ++ph) + if (ph->p_type == PT_GNU_STACK) + { + GL(dl_stack_flags) = ph->p_flags; + break; + } + + if (__builtin_expect (mode, normal) == verify) + { + 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, &malloced, map_doit, + &args); + if (__builtin_expect (err_str != NULL, 0)) + /* We don't free the returned string, the programs stops + anyway. */ + _exit (EXIT_FAILURE); + } + else + { + HP_TIMING_NOW (start); + _dl_map_object (NULL, rtld_progname, 0, lt_library, 0, + __RTLD_OPENEXEC, LM_ID_BASE); + HP_TIMING_NOW (stop); + + HP_TIMING_DIFF (load_time, start, stop); + } + + /* Now the map for the main executable is available. */ + main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + + phdr = main_map->l_phdr; + phnum = main_map->l_phnum; + /* We overwrite here a pointer to a malloc()ed string. But since + the malloc() implementation used at this point is the dummy + implementations which has no real free() function it does not + makes sense to free the old string first. */ + main_map->l_name = (char *) ""; + *user_entry = main_map->l_entry; + } + else + { + /* Create a link_map for the executable itself. + This will be what dlopen on "" returns. */ + 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; + + /* At this point we are in a bit of trouble. We would have to + fill in the values for l_dev and l_ino. But in general we + do not know where the file is. We also do not handle AT_EXECFD + even if it would be passed up. + + We leave the values here defined to 0. This is normally no + problem as the program code itself is normally no shared + object and therefore cannot be loaded dynamically. Nothing + prevent the use of dynamic binaries and in these situations + we might get problems. We might not be able to find out + whether the object is already loaded. But since there is no + easy way out and because the dynamic binary must also not + have an SONAME we ignore this program for now. If it becomes + a problem we can force people using SONAMEs. */ + + /* We delay initializing the path structure until we got the dynamic + information for the program. */ + } + + main_map->l_map_end = 0; + main_map->l_text_end = 0; + /* Perhaps the executable has no PT_LOAD header entries at all. */ + main_map->l_map_start = ~0; + /* And it was opened directly. */ + ++main_map->l_direct_opencount; + + /* Scan the program header table for the dynamic section. */ + for (ph = phdr; ph < &phdr[phnum]; ++ph) + switch (ph->p_type) + { + case PT_PHDR: + /* Find out the load address. */ + main_map->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr; + break; + case PT_DYNAMIC: + /* This tells us where to find the dynamic section, + which tells us everything we need to do. */ + main_map->l_ld = (void *) main_map->l_addr + ph->p_vaddr; + break; + case PT_INTERP: + /* This "interpreter segment" was used by the program loader to + find the program interpreter, which is this program itself, the + dynamic linker. We note what name finds us, so that a future + dlopen call or DT_NEEDED entry, for something that wants to link + against the dynamic linker as a shared library, will know that + the shared object is already loaded. */ + _dl_rtld_libname.name = ((const char *) main_map->l_addr + + ph->p_vaddr); + /* _dl_rtld_libname.next = NULL; Already zero. */ + GL(dl_rtld_map).l_libname = &_dl_rtld_libname; + + /* Ordinarilly, we would get additional names for the loader from + our DT_SONAME. This can't happen if we were actually linked as + a static executable (detect this case when we have no DYNAMIC). + If so, assume the filename component of the interpreter path to + be our SONAME, and add it to our name list. */ + if (GL(dl_rtld_map).l_ld == NULL) + { + const char *p = NULL; + const char *cp = _dl_rtld_libname.name; + + /* Find the filename part of the path. */ + while (*cp != '\0') + if (*cp++ == '/') + p = cp; + + if (p != NULL) + { + _dl_rtld_libname2.name = p; + /* _dl_rtld_libname2.next = NULL; Already zero. */ + _dl_rtld_libname.next = &_dl_rtld_libname2; + } + } + + has_interp = true; + break; + case PT_LOAD: + { + ElfW(Addr) mapstart; + ElfW(Addr) allocend; + + /* Remember where the main program starts in memory. */ + mapstart = (main_map->l_addr + (ph->p_vaddr & ~(ph->p_align - 1))); + if (main_map->l_map_start > mapstart) + main_map->l_map_start = mapstart; + + /* Also where it ends. */ + allocend = main_map->l_addr + ph->p_vaddr + ph->p_memsz; + if (main_map->l_map_end < allocend) + main_map->l_map_end = allocend; + if ((ph->p_flags & PF_X) && allocend > main_map->l_text_end) + main_map->l_text_end = allocend; + } + break; + + case PT_TLS: +#ifdef USE_TLS + if (ph->p_memsz > 0) + { + /* Note that in the case the dynamic linker we duplicate work + here since we read the PT_TLS entry already in + _dl_start_final. But the result is repeatable so do not + check for this special but unimportant case. */ + main_map->l_tls_blocksize = ph->p_memsz; + main_map->l_tls_align = ph->p_align; + if (ph->p_align == 0) + main_map->l_tls_firstbyte_offset = 0; + else + main_map->l_tls_firstbyte_offset = (ph->p_vaddr + & (ph->p_align - 1)); + main_map->l_tls_initimage_size = ph->p_filesz; + main_map->l_tls_initimage = (void *) ph->p_vaddr; + + /* This image gets the ID one. */ + GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1; + } +#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; + + case PT_GNU_RELRO: + main_map->l_relro_addr = ph->p_vaddr; + main_map->l_relro_size = ph->p_memsz; + break; + } +#ifdef USE_TLS + /* Adjust the address of the TLS initialization image in case + the executable is actually an ET_DYN object. */ + if (main_map->l_tls_initimage != NULL) + main_map->l_tls_initimage + = (char *) main_map->l_tls_initimage + main_map->l_addr; +#endif + if (! main_map->l_map_end) + main_map->l_map_end = ~0; + if (! main_map->l_text_end) + main_map->l_text_end = ~0; + if (! GL(dl_rtld_map).l_libname && GL(dl_rtld_map).l_name) + { + /* We were invoked directly, so the program might not have a + PT_INTERP. */ + _dl_rtld_libname.name = GL(dl_rtld_map).l_name; + /* _dl_rtld_libname.next = NULL; Already zero. */ + GL(dl_rtld_map).l_libname = &_dl_rtld_libname; + } + 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. */ + elf_get_dynamic_info (main_map, NULL); + /* Set up our cache of pointers into the hash table. */ + _dl_setup_hash (main_map); + } + + if (__builtin_expect (mode, normal) == verify) + { + /* We were called just to verify that this is a dynamic + executable using us as the program interpreter. Exit with an + error if we were not able to load the binary or no interpreter + is specified (i.e., this is no dynamically linked binary. */ + if (main_map->l_ld == NULL) + _exit (1); + + /* We allow here some platform specific code. */ +#ifdef DISTINGUISH_LIB_VERSIONS + DISTINGUISH_LIB_VERSIONS; +#endif + _exit (has_interp ? 0 : 2); + } + + 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 + exactly one element, which is pointed to by dl_loaded. */ + if (! GL(dl_rtld_map).l_name) + /* If not invoked directly, the dynamic linker shared object file was + found by the PT_INTERP name. */ + GL(dl_rtld_map).l_name = (char *) GL(dl_rtld_map).l_libname->name; + GL(dl_rtld_map).l_type = lt_library; + main_map->l_next = &GL(dl_rtld_map); + GL(dl_rtld_map).l_prev = main_map; + ++GL(dl_ns)[LM_ID_BASE]._ns_nloaded; + ++GL(dl_load_adds); + + /* If LD_USE_LOAD_BIAS env variable has not been seen, default + to not using bias for non-prelinked PIEs and libraries + and using it for executables or prelinked PIEs or libraries. */ + if (GLRO(dl_use_load_bias) == (ElfW(Addr)) -2) + GLRO(dl_use_load_bias) = main_map->l_addr == 0 ? -1 : 0; + + /* Set up the program header information for the dynamic linker + itself. It is needed in the dl_iterate_phdr() callbacks. */ + ElfW(Ehdr) *rtld_ehdr = (ElfW(Ehdr) *) GL(dl_rtld_map).l_map_start; + ElfW(Phdr) *rtld_phdr = (ElfW(Phdr) *) (GL(dl_rtld_map).l_map_start + + rtld_ehdr->e_phoff); + 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) + if (rtld_phdr[cnt].p_type == PT_GNU_RELRO) + { + GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr; + GL(dl_rtld_map).l_relro_size = rtld_phdr[cnt].p_memsz; + 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; + 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. */ + + /* Since we start using the auditing DSOs right away we need to + initialize the data structures now. */ + tcbp = init_tls (); +#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. */ + assert (*first_preload == NULL); + struct link_map **preloads = NULL; + unsigned int npreloads = 0; + + if (__builtin_expect (preloadlist != NULL, 0)) + { + /* The LD_PRELOAD environment variable gives list of libraries + separated by white space or colons that are loaded before the + executable's dependencies and prepended to the global scope + list. If the binary is running setuid all elements + containing a '/' are ignored since it is insecure. */ + char *list = strdupa (preloadlist); + char *p; + + HP_TIMING_NOW (start); + + /* Prevent optimizing strsep. Speed is not important here. */ + while ((p = (strsep) (&list, " :")) != NULL) + if (p[0] != '\0' + && (__builtin_expect (! INTUSE(__libc_enable_secure), 1) + || strchr (p, '/') == NULL)) + npreloads += do_preload (p, main_map, "LD_PRELOAD"); + + HP_TIMING_NOW (stop); + HP_TIMING_DIFF (diff, start, stop); + HP_TIMING_ACCUM_NT (load_time, diff); + } + + /* There usually is no ld.so.preload file, it should only be used + for emergencies and testing. So the open call etc should usually + fail. Using access() on a non-existing file is faster than using + open(). So we do this first. If it succeeds we do almost twice + the work but this does not matter, since it is not for production + use. */ + static const char preload_file[] = "/etc/ld.so.preload"; + if (__builtin_expect (__access (preload_file, R_OK) == 0, 0)) + { + /* Read the contents of the file. */ + file = _dl_sysdep_read_whole_file (preload_file, &file_size, + PROT_READ | PROT_WRITE); + if (__builtin_expect (file != MAP_FAILED, 0)) + { + /* Parse the file. It contains names of libraries to be loaded, + separated by white spaces or `:'. It may also contain + comments introduced by `#'. */ + char *problem; + char *runp; + size_t rest; + + /* Eliminate comments. */ + runp = file; + rest = file_size; + while (rest > 0) + { + char *comment = memchr (runp, '#', rest); + if (comment == NULL) + break; + + rest -= comment - runp; + do + *comment = ' '; + while (--rest > 0 && *++comment != '\n'); + } + + /* We have one problematic case: if we have a name at the end of + the file without a trailing terminating characters, we cannot + place the \0. Handle the case separately. */ + if (file[file_size - 1] != ' ' && file[file_size - 1] != '\t' + && file[file_size - 1] != '\n' && file[file_size - 1] != ':') + { + problem = &file[file_size]; + while (problem > file && problem[-1] != ' ' + && problem[-1] != '\t' + && problem[-1] != '\n' && problem[-1] != ':') + --problem; + + if (problem > file) + problem[-1] = '\0'; + } + else + { + problem = NULL; + file[file_size - 1] = '\0'; + } + + HP_TIMING_NOW (start); + + if (file != problem) + { + char *p; + runp = file; + while ((p = strsep (&runp, ": \t\n")) != NULL) + if (p[0] != '\0') + npreloads += do_preload (p, main_map, preload_file); + } + + if (problem != NULL) + { + char *p = strndupa (problem, file_size - (problem - file)); + + npreloads += do_preload (p, main_map, preload_file); + } + + HP_TIMING_NOW (stop); + HP_TIMING_DIFF (diff, start, stop); + HP_TIMING_ACCUM_NT (load_time, diff); + + /* We don't need the file anymore. */ + __munmap (file, file_size); + } + } + + if (__builtin_expect (*first_preload != NULL, 0)) + { + /* Set up PRELOADS with a vector of the preloaded libraries. */ + struct link_map *l = *first_preload; + preloads = __alloca (npreloads * sizeof preloads[0]); + i = 0; + do + { + preloads[i++] = l; + l = l->l_next; + } while (l); + assert (i == npreloads); + } + + /* 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. */ + HP_TIMING_NOW (start); + _dl_map_object_deps (main_map, preloads, npreloads, mode == trace, 0); + HP_TIMING_NOW (stop); + HP_TIMING_DIFF (diff, start, stop); + HP_TIMING_ACCUM_NT (load_time, diff); + + /* Mark all objects as being in the global scope. */ + for (i = main_map->l_searchlist.r_nlist; i > 0; ) + main_map->l_searchlist.r_list[--i]->l_global = 1; + +#ifndef MAP_ANON + /* We are done mapping things, so close the zero-fill descriptor. */ + __close (_dl_zerofd); + _dl_zerofd = -1; +#endif + + /* 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 != NULL) + GL(dl_rtld_map).l_next->l_prev = GL(dl_rtld_map).l_prev; + + 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. */ + rtld_multiple_ref = true; + + GL(dl_rtld_map).l_prev = main_map->l_searchlist.r_list[i - 1]; + if (__builtin_expect (mode, normal) == normal) + { + GL(dl_rtld_map).l_next = (i + 1 < main_map->l_searchlist.r_nlist + ? main_map->l_searchlist.r_list[i + 1] + : NULL); +#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO + 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 + /* In trace mode there might be an invisible object (which we + could not find) after the previous one in the search list. + In this case it doesn't matter much where we put the + interpreter object, so we just initialize the list pointer so + that the assertion below holds. */ + GL(dl_rtld_map).l_next = GL(dl_rtld_map).l_prev->l_next; + + assert (GL(dl_rtld_map).l_prev->l_next == GL(dl_rtld_map).l_next); + GL(dl_rtld_map).l_prev->l_next = &GL(dl_rtld_map); + if (GL(dl_rtld_map).l_next != NULL) + { + assert (GL(dl_rtld_map).l_next->l_prev == GL(dl_rtld_map).l_prev); + GL(dl_rtld_map).l_next->l_prev = &GL(dl_rtld_map); + } + } + + /* Now let us see whether all libraries are available in the + versions we need. */ + { + struct version_check_args args; + args.doexit = mode == normal; + args.dotrace = mode == trace; + _dl_receive_error (print_missing_version, version_check_doit, &args); + } + +#ifdef USE_TLS + /* 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 + now or lazily doing it later. Doing it now makes us incompatible with + 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). */ + bool was_tls_init_tp_called = tls_init_tp_called; + if (tcbp == NULL) + tcbp = init_tls (); +#endif + + /* 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 + important that we do this before real relocation, because the + functions we call below for output may no longer work properly + after relocation. */ + struct link_map *l; + + if (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) + { + struct r_scope_elem *scope = &main_map->l_searchlist; + + for (i = 0; i < scope->r_nlist; i++) + { + l = scope->r_list [i]; + if (l->l_faked) + { + _dl_printf ("\t%s => not found\n", l->l_libname->name); + continue; + } + if (_dl_name_match_p (GLRO(dl_trace_prelink), l)) + GLRO(dl_trace_prelink_map) = l; + _dl_printf ("\t%s => %s (0x%0*Zx, 0x%0*Zx)", + l->l_libname->name[0] ? l->l_libname->name + : rtld_progname ?: "<main program>", + l->l_name[0] ? l->l_name + : rtld_progname ?: "<main program>", + (int) sizeof l->l_map_start * 2, + (size_t) l->l_map_start, + (int) sizeof l->l_addr * 2, + (size_t) l->l_addr); +#ifdef USE_TLS + if (l->l_tls_modid) + _dl_printf (" TLS(0x%Zx, 0x%0*Zx)\n", l->l_tls_modid, + (int) sizeof l->l_tls_offset * 2, + (size_t) l->l_tls_offset); + else +#endif + _dl_printf ("\n"); + } + } + else if (GLRO(dl_debug_mask) & DL_DEBUG_UNUSED) + { + /* Look through the dependencies of the main executable + and determine which of them is not actually + required. */ + struct link_map *l = main_map; + + /* Relocate the main executable. */ + struct relocate_args args = { .l = l, .lazy = GLRO(dl_lazy) }; + _dl_receive_error (print_unresolved, relocate_doit, &args); + + /* This loop depends on the dependencies of the executable to + correspond in number and order to the DT_NEEDED entries. */ + ElfW(Dyn) *dyn = main_map->l_ld; + bool first = true; + while (dyn->d_tag != DT_NULL) + { + if (dyn->d_tag == DT_NEEDED) + { + l = l->l_next; + + if (!l->l_used) + { + if (first) + { + _dl_printf ("Unused direct dependencies:\n"); + first = false; + } + + _dl_printf ("\t%s\n", l->l_name); + } + } + + ++dyn; + } + + _exit (first != true); + } + else if (! main_map->l_info[DT_NEEDED]) + _dl_printf ("\tstatically linked\n"); + else + { + for (l = main_map->l_next; l; l = l->l_next) + if (l->l_faked) + /* The library was not found. */ + _dl_printf ("\t%s => not found\n", l->l_libname->name); + else if (strcmp (l->l_libname->name, l->l_name) == 0) + _dl_printf ("\t%s (0x%0*Zx)\n", l->l_libname->name, + (int) sizeof l->l_map_start * 2, + (size_t) l->l_map_start); + else + _dl_printf ("\t%s => %s (0x%0*Zx)\n", l->l_libname->name, + l->l_name, (int) sizeof l->l_map_start * 2, + (size_t) l->l_map_start); + } + + if (__builtin_expect (mode, trace) != trace) + for (i = 1; i < (unsigned int) _dl_argc; ++i) + { + const ElfW(Sym) *ref = NULL; + ElfW(Addr) loadbase; + lookup_t result; + + result = _dl_lookup_symbol_x (INTUSE(_dl_argv)[i], main_map, + &ref, main_map->l_scope, NULL, + ELF_RTYPE_CLASS_PLT, + DL_LOOKUP_ADD_DEPENDENCY, NULL); + + loadbase = LOOKUP_VALUE_ADDRESS (result); + + _dl_printf ("%s found at 0x%0*Zd in object at 0x%0*Zd\n", + INTUSE(_dl_argv)[i], + (int) sizeof ref->st_value * 2, + (size_t) ref->st_value, + (int) sizeof loadbase * 2, (size_t) loadbase); + } + else + { + /* If LD_WARN is set, warn about undefined symbols. */ + if (GLRO(dl_lazy) >= 0 && GLRO(dl_verbose)) + { + /* We have to do symbol dependency testing. */ + struct relocate_args args; + struct link_map *l; + + args.lazy = GLRO(dl_lazy); + + l = main_map; + while (l->l_next != NULL) + l = l->l_next; + do + { + if (l != &GL(dl_rtld_map) && ! l->l_faked) + { + args.l = l; + _dl_receive_error (print_unresolved, relocate_doit, + &args); + } + l = l->l_prev; + } + while (l != NULL); + + if ((GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) + && 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) + { + /* Print more information. This means here, print information + about the versions needed. */ + int first = 1; + struct link_map *map; + + for (map = main_map; map != NULL; map = map->l_next) + { + const char *strtab; + ElfW(Dyn) *dyn = map->l_info[VERNEEDTAG]; + ElfW(Verneed) *ent; + + if (dyn == NULL) + continue; + + strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr); + + if (first) + { + _dl_printf ("\n\tVersion information:\n"); + first = 0; + } + + _dl_printf ("\t%s:\n", + map->l_name[0] ? map->l_name : rtld_progname); + + while (1) + { + ElfW(Vernaux) *aux; + struct link_map *needed; + + needed = find_needed (strtab + ent->vn_file); + aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux); + + while (1) + { + const char *fname = NULL; + + if (needed != NULL + && match_version (strtab + aux->vna_name, + needed)) + fname = needed->l_name; + + _dl_printf ("\t\t%s (%s) %s=> %s\n", + strtab + ent->vn_file, + strtab + aux->vna_name, + aux->vna_flags & VER_FLG_WEAK + ? "[WEAK] " : "", + fname ?: "not found"); + + if (aux->vna_next == 0) + /* No more symbols. */ + break; + + /* Next symbol. */ + aux = (ElfW(Vernaux) *) ((char *) aux + + aux->vna_next); + } + + if (ent->vn_next == 0) + /* No more dependencies. */ + break; + + /* Next dependency. */ + ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next); + } + } + } + } + + _exit (0); + } + + if (main_map->l_info[ADDRIDX (DT_GNU_LIBLIST)] + && ! __builtin_expect (GLRO(dl_profile) != NULL, 0)) + { + ElfW(Lib) *liblist, *liblistend; + struct link_map **r_list, **r_listend, *l; + const char *strtab = (const void *) D_PTR (main_map, l_info[DT_STRTAB]); + + assert (main_map->l_info[VALIDX (DT_GNU_LIBLISTSZ)] != NULL); + liblist = (ElfW(Lib) *) + main_map->l_info[ADDRIDX (DT_GNU_LIBLIST)]->d_un.d_ptr; + liblistend = (ElfW(Lib) *) + ((char *) liblist + + main_map->l_info[VALIDX (DT_GNU_LIBLISTSZ)]->d_un.d_val); + r_list = main_map->l_searchlist.r_list; + r_listend = r_list + main_map->l_searchlist.r_nlist; + + for (; r_list < r_listend && liblist < liblistend; r_list++) + { + l = *r_list; + + if (l == main_map) + continue; + + /* If the library is not mapped where it should, fail. */ + if (l->l_addr) + break; + + /* Next, check if checksum matches. */ + if (l->l_info [VALIDX(DT_CHECKSUM)] == NULL + || l->l_info [VALIDX(DT_CHECKSUM)]->d_un.d_val + != liblist->l_checksum) + break; + + if (l->l_info [VALIDX(DT_GNU_PRELINKED)] == NULL + || l->l_info [VALIDX(DT_GNU_PRELINKED)]->d_un.d_val + != liblist->l_time_stamp) + break; + + if (! _dl_name_match_p (strtab + liblist->l_name, l)) + break; + + ++liblist; + } + + + if (r_list == r_listend && liblist == liblistend) + prelinked = true; + + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) + _dl_debug_printf ("\nprelink checking: %s\n", + prelinked ? "ok" : "failed"); + } + + + /* 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. */ + GLRO(dl_initial_searchlist) = *GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist; + + if (prelinked) + { + if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL) + { + ElfW(Rela) *conflict, *conflictend; +#ifndef HP_TIMING_NONAVAIL + hp_timing_t start; + hp_timing_t stop; +#endif + + HP_TIMING_NOW (start); + assert (main_map->l_info [VALIDX (DT_GNU_CONFLICTSZ)] != NULL); + conflict = (ElfW(Rela) *) + main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)]->d_un.d_ptr; + conflictend = (ElfW(Rela) *) + ((char *) conflict + + main_map->l_info [VALIDX (DT_GNU_CONFLICTSZ)]->d_un.d_val); + _dl_resolve_conflicts (main_map, conflict, conflictend); + HP_TIMING_NOW (stop); + HP_TIMING_DIFF (relocate_time, start, stop); + } + + + /* Mark all the objects so we know they have been already relocated. */ + 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 (); + } + else + { + /* Now we have all the objects loaded. Relocate them all except for + the dynamic linker itself. We do this in reverse order so that copy + relocs of earlier objects overwrite the data written by later + objects. We do not re-relocate the dynamic linker itself in this + loop because that could result in the GOT entries for functions we + call being changed, and that would break us. It is safe to relocate + the dynamic linker out of order because it has no copy relocs (we + know that because it is self-contained). */ + + int consider_profiling = GLRO(dl_profile) != NULL; +#ifndef HP_TIMING_NONAVAIL + hp_timing_t start; + hp_timing_t stop; +#endif + + /* If we are profiling we also must do lazy reloaction. */ + GLRO(dl_lazy) |= consider_profiling; + + struct link_map *l = main_map; + while (l->l_next) + l = l->l_next; + + HP_TIMING_NOW (start); + do + { + /* While we are at it, help the memory handling a bit. We have to + mark some data structures as allocated with the fake malloc() + implementation in ld.so. */ + struct libname_list *lnp = l->l_libname->next; + + while (__builtin_expect (lnp != NULL, 0)) + { + lnp->dont_free = 1; + lnp = lnp->next; + } + + if (l != &GL(dl_rtld_map)) + _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); + HP_TIMING_NOW (stop); + + HP_TIMING_DIFF (relocate_time, start, stop); + + /* Do any necessary cleanups for the startup OS interface code. + We do these now so that no calls are made after rtld re-relocation + which might be resolved to different functions than we expect. + We cannot do this before relocating the other objects because + _dl_relocate_object might need to call `mprotect' for DT_TEXTREL. */ + _dl_sysdep_start_cleanup (); + + /* Now enable profiling if needed. Like the previous call, + this has to go here because the calls it makes should use the + rtld versions of the functions (particularly calloc()), but it + needs to have _dl_profile_map set up by the relocator. */ + if (__builtin_expect (GL(dl_profile_map) != NULL, 0)) + /* We must prepare the profiling. */ + _dl_start_profile (); + } + +#ifndef NONTLS_INIT_TP +# define NONTLS_INIT_TP do { } while (0) +#endif + +#ifdef USE_TLS + 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. */ + 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); + } +#else + NONTLS_INIT_TP; +#endif + + 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 + /* We must munmap() the cache file. */ + _dl_unload_cache (); +#endif + + /* Once we return, _dl_sysdep_start will invoke + the DT_INIT functions and then *USER_ENTRY. */ +} + +/* This is a little helper function for resolving symbols while + tracing the binary. */ +static void +print_unresolved (int errcode __attribute__ ((unused)), const char *objname, + const char *errstring) +{ + if (objname[0] == '\0') + objname = rtld_progname ?: "<main program>"; + _dl_error_printf ("%s (%s)\n", errstring, objname); +} + +/* This is a little helper function for resolving symbols while + tracing the binary. */ +static void +print_missing_version (int errcode __attribute__ ((unused)), + const char *objname, const char *errstring) +{ + _dl_error_printf ("%s: %s: %s\n", rtld_progname ?: "<program name unknown>", + objname, errstring); +} + +/* Nonzero if any of the debugging options is enabled. */ +static int any_debug attribute_relro; + +/* Process the string given as the parameter which explains which debugging + options are enabled. */ +static void +process_dl_debug (const char *dl_debug) +{ + /* When adding new entries make sure that the maximal length of a name + is correctly handled in the LD_DEBUG_HELP code below. */ + static const struct + { + unsigned char len; + const char name[10]; + const char helptext[41]; + unsigned short int mask; + } debopts[] = + { +#define LEN_AND_STR(str) sizeof (str) - 1, str + { LEN_AND_STR ("libs"), "display library search paths", + DL_DEBUG_LIBS | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("reloc"), "display relocation processing", + DL_DEBUG_RELOC | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("files"), "display progress for input file", + DL_DEBUG_FILES | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("symbols"), "display symbol table processing", + DL_DEBUG_SYMBOLS | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("bindings"), "display information about symbol binding", + DL_DEBUG_BINDINGS | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("versions"), "display version dependencies", + DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("all"), "all previous options combined", + DL_DEBUG_LIBS | DL_DEBUG_RELOC | DL_DEBUG_FILES | DL_DEBUG_SYMBOLS + | DL_DEBUG_BINDINGS | DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("statistics"), "display relocation statistics", + DL_DEBUG_STATISTICS }, + { LEN_AND_STR ("unused"), "determined unused DSOs", + DL_DEBUG_UNUSED }, + { LEN_AND_STR ("help"), "display this help message and exit", + DL_DEBUG_HELP }, + }; +#define ndebopts (sizeof (debopts) / sizeof (debopts[0])) + + /* Skip separating white spaces and commas. */ + while (*dl_debug != '\0') + { + if (*dl_debug != ' ' && *dl_debug != ',' && *dl_debug != ':') + { + size_t cnt; + size_t len = 1; + + while (dl_debug[len] != '\0' && dl_debug[len] != ' ' + && dl_debug[len] != ',' && dl_debug[len] != ':') + ++len; + + for (cnt = 0; cnt < ndebopts; ++cnt) + if (debopts[cnt].len == len + && memcmp (dl_debug, debopts[cnt].name, len) == 0) + { + GLRO(dl_debug_mask) |= debopts[cnt].mask; + any_debug = 1; + break; + } + + if (cnt == ndebopts) + { + /* Display a warning and skip everything until next + separator. */ + char *copy = strndupa (dl_debug, len); + _dl_error_printf ("\ +warning: debug option `%s' unknown; try LD_DEBUG=help\n", copy); + } + + dl_debug += len; + continue; + } + + ++dl_debug; + } + + if (GLRO(dl_debug_mask) & DL_DEBUG_HELP) + { + size_t cnt; + + _dl_printf ("\ +Valid options for the LD_DEBUG environment variable are:\n\n"); + + for (cnt = 0; cnt < ndebopts; ++cnt) + _dl_printf (" %.*s%s%s\n", debopts[cnt].len, debopts[cnt].name, + " " + debopts[cnt].len - 3, + debopts[cnt].helptext); + + _dl_printf ("\n\ +To direct the debugging output into a file instead of standard output\n\ +a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n"); + _exit (0); + } +} + +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. */ +extern char **_environ attribute_hidden; + + +static void +process_envvars (enum mode *modep) +{ + char **runp = _environ; + char *envline; + enum mode mode = normal; + char *debug_output = NULL; + + /* This is the default place for profiling data file. */ + GLRO(dl_profile_output) + = &"/var/tmp\0/var/profile"[INTUSE(__libc_enable_secure) ? 9 : 0]; + + while ((envline = _dl_next_ld_env_entry (&runp)) != NULL) + { + size_t len = 0; + + while (envline[len] != '\0' && envline[len] != '=') + ++len; + + if (envline[len] != '=') + /* This is a "LD_" variable at the end of the string without + a '=' character. Ignore it since otherwise we will access + invalid memory below. */ + continue; + + switch (len) + { + case 4: + /* Warning level, verbose or not. */ + if (memcmp (envline, "WARN", 4) == 0) + GLRO(dl_verbose) = envline[5] != '\0'; + break; + + case 5: + /* Debugging of the dynamic linker? */ + if (memcmp (envline, "DEBUG", 5) == 0) + { + process_dl_debug (&envline[6]); + break; + } + if (memcmp (envline, "AUDIT", 5) == 0) + process_dl_audit (&envline[6]); + break; + + case 7: + /* Print information about versions. */ + if (memcmp (envline, "VERBOSE", 7) == 0) + { + version_info = envline[8] != '\0'; + break; + } + + /* List of objects to be preloaded. */ + if (memcmp (envline, "PRELOAD", 7) == 0) + { + preloadlist = &envline[8]; + break; + } + + /* Which shared object shall be profiled. */ + if (memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0') + GLRO(dl_profile) = &envline[8]; + break; + + case 8: + /* Do we bind early? */ + if (memcmp (envline, "BIND_NOW", 8) == 0) + { + GLRO(dl_lazy) = envline[9] == '\0'; + break; + } + if (memcmp (envline, "BIND_NOT", 8) == 0) + GLRO(dl_bind_not) = envline[9] != '\0'; + break; + + case 9: + /* Test whether we want to see the content of the auxiliary + array passed up from the kernel. */ + if (!INTUSE(__libc_enable_secure) + && memcmp (envline, "SHOW_AUXV", 9) == 0) + _dl_show_auxv (); + break; + + case 10: + /* Mask for the important hardware capabilities. */ + if (memcmp (envline, "HWCAP_MASK", 10) == 0) + GLRO(dl_hwcap_mask) = __strtoul_internal (&envline[11], NULL, + 0, 0); + break; + + case 11: + /* Path where the binary is found. */ + if (!INTUSE(__libc_enable_secure) + && memcmp (envline, "ORIGIN_PATH", 11) == 0) + GLRO(dl_origin_path) = &envline[12]; + break; + + case 12: + /* The library search path. */ + if (memcmp (envline, "LIBRARY_PATH", 12) == 0) + { + library_path = &envline[13]; + break; + } + + /* Where to place the profiling data file. */ + if (memcmp (envline, "DEBUG_OUTPUT", 12) == 0) + { + debug_output = &envline[13]; + break; + } + + if (!INTUSE(__libc_enable_secure) + && memcmp (envline, "DYNAMIC_WEAK", 12) == 0) + GLRO(dl_dynamic_weak) = 1; + break; + + case 13: + /* We might have some extra environment variable with length 13 + to handle. */ +#ifdef EXTRA_LD_ENVVARS_13 + EXTRA_LD_ENVVARS_13 +#endif + if (!INTUSE(__libc_enable_secure) + && memcmp (envline, "USE_LOAD_BIAS", 13) == 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: + /* Where to place the profiling data file. */ + if (!INTUSE(__libc_enable_secure) + && memcmp (envline, "PROFILE_OUTPUT", 14) == 0 + && envline[15] != '\0') + GLRO(dl_profile_output) = &envline[15]; + break; + + case 16: + /* The mode of the dynamic linker can be set. */ + if (memcmp (envline, "TRACE_PRELINKING", 16) == 0) + { + mode = trace; + GLRO(dl_verbose) = 1; + GLRO(dl_debug_mask) |= DL_DEBUG_PRELINK; + GLRO(dl_trace_prelink) = &envline[17]; + } + break; + + case 20: + /* The mode of the dynamic linker can be set. */ + if (memcmp (envline, "TRACE_LOADED_OBJECTS", 20) == 0) + mode = trace; + break; + + /* We might have some extra environment variable to handle. This + is tricky due to the pre-processing of the length of the name + in the switch statement here. The code here assumes that added + environment variables have a different length. */ +#ifdef EXTRA_LD_ENVVARS + EXTRA_LD_ENVVARS +#endif + } + } + + /* The caller wants this information. */ + *modep = mode; + + /* Extra security for SUID binaries. Remove all dangerous environment + variables. */ + if (__builtin_expect (INTUSE(__libc_enable_secure), 0)) + { + static const char unsecure_envvars[] = +#ifdef EXTRA_UNSECURE_ENVVARS + EXTRA_UNSECURE_ENVVARS +#endif + UNSECURE_ENVVARS; + const char *nextp; + + nextp = unsecure_envvars; + do + { + unsetenv (nextp); + /* We could use rawmemchr but this need not be fast. */ + nextp = (char *) (strchr) (nextp, '\0') + 1; + } + while (*nextp != '\0'); + + if (__access ("/etc/suid-debug", F_OK) != 0) + { + unsetenv ("MALLOC_CHECK_"); + GLRO(dl_debug_mask) = 0; + } + + if (mode != normal) + _exit (5); + } + /* If we have to run the dynamic linker in debugging mode and the + LD_DEBUG_OUTPUT environment variable is given, we write the debug + messages to this file. */ + else if (any_debug && debug_output != NULL) + { +#ifdef O_NOFOLLOW + const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW; +#else + const int flags = O_WRONLY | O_APPEND | O_CREAT; +#endif + size_t name_len = strlen (debug_output); + char buf[name_len + 12]; + char *startp; + + buf[name_len + 11] = '\0'; + startp = _itoa (__getpid (), &buf[name_len + 11], 10, 0); + *--startp = '.'; + startp = memcpy (startp - name_len, debug_output, name_len); + + GLRO(dl_debug_fd) = __open (startp, flags, DEFFILEMODE); + if (GLRO(dl_debug_fd) == -1) + /* We use standard output if opening the file failed. */ + GLRO(dl_debug_fd) = STDOUT_FILENO; + } +} + + +/* Print the various times we collected. */ +static void +__attribute ((noinline)) +print_statistics (hp_timing_t *rtld_total_timep) +{ +#ifndef HP_TIMING_NONAVAIL + char buf[200]; + char *cp; + char *wp; + + /* Total time rtld used. */ + if (HP_TIMING_AVAIL) + { + HP_TIMING_PRINT (buf, sizeof (buf), *rtld_total_timep); + _dl_debug_printf ("\nruntime linker statistics:\n" + " total startup time in dynamic loader: %s\n", buf); + + /* Print relocation statistics. */ + char pbuf[30]; + HP_TIMING_PRINT (buf, sizeof (buf), relocate_time); + cp = _itoa ((1000ULL * relocate_time) / *rtld_total_timep, + pbuf + sizeof (pbuf), 10, 0); + wp = pbuf; + switch (pbuf + sizeof (pbuf) - cp) + { + case 3: + *wp++ = *cp++; + case 2: + *wp++ = *cp++; + case 1: + *wp++ = '.'; + *wp++ = *cp++; + } + *wp = '\0'; + _dl_debug_printf ("\ + time needed for relocation: %s (%s%%)\n", buf, pbuf); + } +#endif + + unsigned long int num_relative_relocations = 0; + for (Lmid_t ns = 0; ns < DL_NNS; ++ns) + { + if (GL(dl_ns)[ns]._ns_loaded == NULL) + continue; + + struct r_scope_elem *scope = &GL(dl_ns)[ns]._ns_loaded->l_searchlist; + + for (unsigned int i = 0; i < scope->r_nlist; i++) + { + struct link_map *l = scope->r_list [i]; + + if (l->l_addr != 0 && l->l_info[VERSYMIDX (DT_RELCOUNT)]) + num_relative_relocations + += l->l_info[VERSYMIDX (DT_RELCOUNT)]->d_un.d_val; +#ifndef ELF_MACHINE_REL_RELATIVE + /* Relative relocations are processed on these architectures if + library is loaded to different address than p_vaddr or + if not prelinked. */ + if ((l->l_addr != 0 || !l->l_info[VALIDX(DT_GNU_PRELINKED)]) + && l->l_info[VERSYMIDX (DT_RELACOUNT)]) +#else + /* On e.g. IA-64 or Alpha, relative relocations are processed + only if library is loaded to different address than p_vaddr. */ + if (l->l_addr != 0 && l->l_info[VERSYMIDX (DT_RELACOUNT)]) +#endif + num_relative_relocations + += l->l_info[VERSYMIDX (DT_RELACOUNT)]->d_un.d_val; + } + } + + _dl_debug_printf (" number of relocations: %lu\n" + " number of relocations from cache: %lu\n" + " number of relative relocations: %lu\n", + GL(dl_num_relocations), + GL(dl_num_cache_relocations), + num_relative_relocations); + +#ifndef HP_TIMING_NONAVAIL + /* Time spend while loading the object and the dependencies. */ + if (HP_TIMING_AVAIL) + { + char pbuf[30]; + HP_TIMING_PRINT (buf, sizeof (buf), load_time); + cp = _itoa ((1000ULL * load_time) / *rtld_total_timep, + pbuf + sizeof (pbuf), 10, 0); + wp = pbuf; + switch (pbuf + sizeof (pbuf) - cp) + { + case 3: + *wp++ = *cp++; + case 2: + *wp++ = *cp++; + case 1: + *wp++ = '.'; + *wp++ = *cp++; + } + *wp = '\0'; + _dl_debug_printf ("\ + time needed to load objects: %s (%s%%)\n", + buf, pbuf); + } +#endif +} diff --git a/libc/elf/sln.c b/libc/elf/sln.c new file mode 100644 index 000000000..8e66510bb --- /dev/null +++ b/libc/elf/sln.c @@ -0,0 +1,189 @@ +/* `sln' program to create symbolic links between files. + Copyright (C) 1998, 1999, 2001 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 <error.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> + +#if !defined S_ISDIR && defined S_IFDIR +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +static int makesymlink (const char *src, const char *dest); +static int makesymlinks (const char *file); + +int +main (int argc, char **argv) +{ + switch (argc) + { + case 2: + return makesymlinks (argv [1]); + break; + + case 3: + return makesymlink (argv [1], argv [2]); + break; + + default: + printf ("Usage: %s src dest|file\n", argv [0]); + return 1; + break; + } +} + +static int +makesymlinks (file) + const char *file; +{ +#ifndef PATH_MAX +#define PATH_MAX 4095 +#endif + char *buffer = NULL; + size_t bufferlen = 0; + int ret; + int lineno; + FILE *fp; + + if (strcmp (file, "-") == 0) + fp = stdin; + else + { + fp = fopen (file, "r"); + if (fp == NULL) + { + fprintf (stderr, "%s: file open error: %m\n", file); + return 1; + } + } + + ret = 0; + lineno = 0; + while (!feof_unlocked (fp)) + { + ssize_t n = getline (&buffer, &bufferlen, fp); + char *src; + char *dest; + char *cp = buffer; + + if (n < 0) + break; + if (buffer[n - 1] == '\n') + buffer[n - 1] = '\0'; + + ++lineno; + while (isspace (*cp)) + ++cp; + if (*cp == '\0') + /* Ignore empty lines. */ + continue; + src = cp; + + do + ++cp; + while (*cp != '\0' && ! isspace (*cp)); + if (*cp != '\0') + *cp++ = '\0'; + + while (isspace (*cp)) + ++cp; + if (*cp == '\0') + { + fprintf (stderr, "No target in line %d\n", lineno); + ret = 1; + continue; + } + dest = cp; + + do + ++cp; + while (*cp != '\0' && ! isspace (*cp)); + if (*cp != '\0') + *cp++ = '\0'; + + ret |= makesymlink (src, dest); + } + fclose (fp); + + return ret; +} + +static int +makesymlink (src, dest) + const char *src; + const char *dest; +{ + struct stat stats; + const char *error; + + /* Destination must not be a directory. */ + if (lstat (dest, &stats) == 0) + { + if (S_ISDIR (stats.st_mode)) + { + fprintf (stderr, "%s: destination must not be a directory\n", + dest); + return 1; + } + else if (unlink (dest) && errno != ENOENT) + { + fprintf (stderr, "%s: failed to remove the old destination\n", + dest); + return 1; + } + } + else if (errno != ENOENT) + { + error = strerror (errno); + fprintf (stderr, "%s: invalid destination: %s\n", dest, error); + return -1; + } + +#ifdef S_ISLNK + if (symlink (src, dest) == 0) +#else + if (link (src, dest) == 0) +#endif + { + /* Destination must exist by now. */ + if (access (dest, F_OK)) + { + error = strerror (errno); + unlink (dest); + fprintf (stderr, "Invalid link from \"%s\" to \"%s\": %s\n", + src, dest, error); + return 1; + } + return 0; + } + else + { + error = strerror (errno); + fprintf (stderr, "Invalid link from \"%s\" to \"%s\": %s\n", + src, dest, error); + return 1; + } +} diff --git a/libc/elf/sofini.c b/libc/elf/sofini.c new file mode 100644 index 000000000..5e06f0ca9 --- /dev/null +++ b/libc/elf/sofini.c @@ -0,0 +1,17 @@ +/* Finalizer module for ELF shared C library. This provides terminating + null pointer words in the `.ctors' and `.dtors' sections. */ + +static void (*const __CTOR_END__[1]) (void) + __attribute__ ((used, section (".ctors"))) + = { 0 }; +static void (*const __DTOR_END__[1]) (void) + __attribute__ ((used, section (".dtors"))) + = { 0 }; + +/* 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 const ui32 __FRAME_END__[1] + __attribute__ ((used, section (".eh_frame"))) + = { 0 }; diff --git a/libc/elf/soinit.c b/libc/elf/soinit.c new file mode 100644 index 000000000..c0a881ef5 --- /dev/null +++ b/libc/elf/soinit.c @@ -0,0 +1,46 @@ +/* Initializer module for building the ELF shared C library. This file and + sofini.c do the work normally done by crtbeginS.o and crtendS.o, to wrap + the `.ctors' and `.dtors' sections so the lists are terminated, and + calling those lists of functions. */ + +#include <libc-internal.h> +#include <stdlib.h> + +static void (*const __CTOR_LIST__[1]) (void) + __attribute__ ((section (".ctors"))) + = { (void (*) (void)) -1 }; +static void (*const __DTOR_LIST__[1]) (void) + __attribute__ ((section (".dtors"))) + = { (void (*) (void)) -1 }; + +static inline void +run_hooks (void (*const list[]) (void)) +{ + while (*++list) + (**list) (); +} + +static const char __EH_FRAME_BEGIN__[] + __attribute__ ((used, section (".eh_frame"))) + = { }; + +/* This function will be called from _init in init-first.c. */ +void +__libc_global_ctors (void) +{ + /* Call constructor functions. */ + run_hooks (__CTOR_LIST__); +} + + +/* This function becomes the DT_FINI termination function + for the C library. */ +void +__libc_fini (void) +{ + /* Call destructor functions. */ + run_hooks (__DTOR_LIST__); +} + +void (*_fini_ptr) (void) __attribute__ ((section (".fini_array"))) + = &__libc_fini; diff --git a/libc/elf/sprof.c b/libc/elf/sprof.c new file mode 100644 index 000000000..e53a7ba7a --- /dev/null +++ b/libc/elf/sprof.c @@ -0,0 +1,1383 @@ +/* Read and display shared object profiling data. + 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. + + 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 <argp.h> +#include <dlfcn.h> +#include <elf.h> +#include <error.h> +#include <fcntl.h> +#include <inttypes.h> +#include <libintl.h> +#include <locale.h> +#include <obstack.h> +#include <search.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ldsodefs.h> +#include <sys/gmon.h> +#include <sys/gmon_out.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> + +/* Get libc version number. */ +#include "../version.h" + +#define PACKAGE _libc_intl_domainname + + +#include <endian.h> +#if BYTE_ORDER == BIG_ENDIAN +# define byteorder ELFDATA2MSB +# define byteorder_name "big-endian" +#elif BYTE_ORDER == LITTLE_ENDIAN +# define byteorder ELFDATA2LSB +# define byteorder_name "little-endian" +#else +# error "Unknown BYTE_ORDER " BYTE_ORDER +# define byteorder ELFDATANONE +#endif + +#ifndef PATH_MAX +# define PATH_MAX 1024 +#endif + + +extern int __profile_frequency (void); + +/* Name and version of program. */ +static void print_version (FILE *stream, struct argp_state *state); +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +#define OPT_TEST 1 + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, N_("Output selection:") }, + { "call-pairs", 'c', NULL, 0, + N_("print list of count paths and their number of use") }, + { "flat-profile", 'p', NULL, 0, + N_("generate flat profile with counts and ticks") }, + { "graph", 'q', NULL, 0, N_("generate call graph") }, + + { "test", OPT_TEST, NULL, OPTION_HIDDEN, NULL }, + { NULL, 0, NULL, 0, NULL } +}; + +/* Short description of program. */ +static const char doc[] = N_("Read and display shared object profiling data.\v\ +For bug reporting instructions, please see:\n\ +<http://www.gnu.org/software/libc/bugs.html>.\n"); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("SHOBJ [PROFDATA]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc +}; + + +/* Operation modes. */ +static enum +{ + NONE = 0, + FLAT_MODE = 1 << 0, + CALL_GRAPH_MODE = 1 << 1, + CALL_PAIRS = 1 << 2, + + DEFAULT_MODE = FLAT_MODE | CALL_GRAPH_MODE +} mode; + +/* Nozero for testing. */ +static int do_test; + +/* Strcuture describing calls. */ +struct here_fromstruct +{ + struct here_cg_arc_record volatile *here; + uint16_t link; +}; + +/* We define a special type to address the elements of the arc table. + This is basically the `gmon_cg_arc_record' format but it includes + the room for the tag and it uses real types. */ +struct here_cg_arc_record +{ + uintptr_t from_pc; + uintptr_t self_pc; + uint32_t count; +} __attribute__ ((packed)); + + +struct known_symbol; +struct arc_list +{ + size_t idx; + uintmax_t count; + + struct arc_list *next; +}; + +static struct obstack ob_list; + + +struct known_symbol +{ + const char *name; + uintptr_t addr; + size_t size; + bool weak; + bool hidden; + + uintmax_t ticks; + uintmax_t calls; + + struct arc_list *froms; + struct arc_list *tos; +}; + + +struct shobj +{ + const char *name; /* User-provided name. */ + + struct link_map *map; + const char *dynstrtab; /* Dynamic string table of shared object. */ + const char *soname; /* Soname of shared object. */ + + uintptr_t lowpc; + uintptr_t highpc; + unsigned long int kcountsize; + size_t expected_size; /* Expected size of profiling file. */ + size_t tossize; + size_t fromssize; + size_t fromlimit; + unsigned int hashfraction; + int s_scale; + + void *symbol_map; + size_t symbol_mapsize; + const ElfW(Sym) *symtab; + size_t symtab_size; + const char *strtab; + + struct obstack ob_str; + struct obstack ob_sym; +}; + + +struct profdata +{ + void *addr; + off_t size; + + char *hist; + struct gmon_hist_hdr *hist_hdr; + uint16_t *kcount; + uint32_t narcs; /* Number of arcs in toset. */ + struct here_cg_arc_record *data; + uint16_t *tos; + struct here_fromstruct *froms; +}; + +/* Search tree for symbols. */ +static void *symroot; +static struct known_symbol **sortsym; +static size_t symidx; +static uintmax_t total_ticks; + +/* Prototypes for local functions. */ +static struct shobj *load_shobj (const char *name); +static void unload_shobj (struct shobj *shobj); +static struct profdata *load_profdata (const char *name, struct shobj *shobj); +static void unload_profdata (struct profdata *profdata); +static void count_total_ticks (struct shobj *shobj, struct profdata *profdata); +static void count_calls (struct shobj *shobj, struct profdata *profdata); +static void read_symbols (struct shobj *shobj); +static void add_arcs (struct profdata *profdata); +static void generate_flat_profile (struct profdata *profdata); +static void generate_call_graph (struct profdata *profdata); +static void generate_call_pair_list (struct profdata *profdata); + + +int +main (int argc, char *argv[]) +{ + const char *shobj; + const char *profdata; + struct shobj *shobj_handle; + struct profdata *profdata_handle; + int remaining; + + setlocale (LC_ALL, ""); + + /* Initialize the message catalog. */ + textdomain (_libc_intl_domainname); + + /* Parse and process arguments. */ + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + if (argc - remaining == 0 || argc - remaining > 2) + { + /* We need exactly two non-option parameter. */ + argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, + program_invocation_short_name); + exit (1); + } + + /* Get parameters. */ + shobj = argv[remaining]; + if (argc - remaining == 2) + profdata = argv[remaining + 1]; + else + /* No filename for the profiling data given. We will determine it + from the soname of the shobj, later. */ + profdata = NULL; + + /* First see whether we can load the shared object. */ + shobj_handle = load_shobj (shobj); + if (shobj_handle == NULL) + exit (1); + + /* We can now determine the filename for the profiling data, if + nececessary. */ + if (profdata == NULL) + { + char *newp; + const char *soname; + size_t soname_len; + + soname = shobj_handle->soname ?: basename (shobj); + soname_len = strlen (soname); + newp = (char *) alloca (soname_len + sizeof ".profile"); + stpcpy (mempcpy (newp, soname, soname_len), ".profile"); + profdata = newp; + } + + /* Now see whether the profiling data file matches the given object. */ + profdata_handle = load_profdata (profdata, shobj_handle); + if (profdata_handle == NULL) + { + unload_shobj (shobj_handle); + + exit (1); + } + + read_symbols (shobj_handle); + + /* Count the ticks. */ + count_total_ticks (shobj_handle, profdata_handle); + + /* Count the calls. */ + count_calls (shobj_handle, profdata_handle); + + /* Add the arc information. */ + add_arcs (profdata_handle); + + /* If no mode is specified fall back to the default mode. */ + if (mode == NONE) + mode = DEFAULT_MODE; + + /* Do some work. */ + if (mode & FLAT_MODE) + generate_flat_profile (profdata_handle); + + if (mode & CALL_GRAPH_MODE) + generate_call_graph (profdata_handle); + + if (mode & CALL_PAIRS) + generate_call_pair_list (profdata_handle); + + /* Free the resources. */ + unload_shobj (shobj_handle); + unload_profdata (profdata_handle); + + return 0; +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'c': + mode |= CALL_PAIRS; + break; + case 'p': + mode |= FLAT_MODE; + break; + case 'q': + mode |= CALL_GRAPH_MODE; + break; + case OPT_TEST: + do_test = 1; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state) +{ + fprintf (stream, "sprof (GNU %s) %s\n", PACKAGE, VERSION); + fprintf (stream, gettext ("\ +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\ +"), + "2006"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* Note that we must not use `dlopen' etc. The shobj object must not + be loaded for use. */ +static struct shobj * +load_shobj (const char *name) +{ + struct link_map *map = NULL; + struct shobj *result; + ElfW(Addr) mapstart = ~((ElfW(Addr)) 0); + ElfW(Addr) mapend = 0; + const ElfW(Phdr) *ph; + size_t textsize; + unsigned int log_hashfraction; + ElfW(Ehdr) *ehdr; + int fd; + ElfW(Shdr) *shdr; + size_t pagesize = getpagesize (); + + /* Since we use dlopen() we must be prepared to work around the sometimes + strange lookup rules for the shared objects. If we have a file foo.so + in the current directory and the user specfies foo.so on the command + line (without specifying a directory) we should load the file in the + current directory even if a normal dlopen() call would read the other + file. We do this by adding a directory portion to the name. */ + if (strchr (name, '/') == NULL) + { + char *load_name = (char *) alloca (strlen (name) + 3); + stpcpy (stpcpy (load_name, "./"), name); + + map = (struct link_map *) dlopen (load_name, RTLD_LAZY | __RTLD_SPROF); + } + if (map == NULL) + { + map = (struct link_map *) dlopen (name, RTLD_LAZY | __RTLD_SPROF); + if (map == NULL) + { + error (0, errno, _("failed to load shared object `%s'"), name); + return NULL; + } + } + + /* Prepare the result. */ + result = (struct shobj *) calloc (1, sizeof (struct shobj)); + if (result == NULL) + { + error (0, errno, _("cannot create internal descriptors")); + dlclose (map); + return NULL; + } + result->name = name; + result->map = map; + + /* Compute the size of the sections which contain program code. + This must match the code in dl-profile.c (_dl_start_profile). */ + for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph) + if (ph->p_type == PT_LOAD && (ph->p_flags & PF_X)) + { + ElfW(Addr) start = (ph->p_vaddr & ~(pagesize - 1)); + ElfW(Addr) end = ((ph->p_vaddr + ph->p_memsz + pagesize - 1) + & ~(pagesize - 1)); + + if (start < mapstart) + mapstart = start; + if (end > mapend) + mapend = end; + } + + result->lowpc = ROUNDDOWN ((uintptr_t) (mapstart + map->l_addr), + HISTFRACTION * sizeof (HISTCOUNTER)); + result->highpc = ROUNDUP ((uintptr_t) (mapend + map->l_addr), + HISTFRACTION * sizeof (HISTCOUNTER)); + if (do_test) + printf ("load addr: %0#*" PRIxPTR "\n" + "lower bound PC: %0#*" PRIxPTR "\n" + "upper bound PC: %0#*" PRIxPTR "\n", + __ELF_NATIVE_CLASS == 32 ? 10 : 18, map->l_addr, + __ELF_NATIVE_CLASS == 32 ? 10 : 18, result->lowpc, + __ELF_NATIVE_CLASS == 32 ? 10 : 18, result->highpc); + + textsize = result->highpc - result->lowpc; + result->kcountsize = textsize / HISTFRACTION; + result->hashfraction = HASHFRACTION; + if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) + /* If HASHFRACTION is a power of two, mcount can use shifting + instead of integer division. Precompute shift amount. */ + log_hashfraction = __builtin_ffs (result->hashfraction + * sizeof (struct here_fromstruct)) - 1; + else + log_hashfraction = -1; + if (do_test) + printf ("hashfraction = %d\ndivider = %Zu\n", + result->hashfraction, + result->hashfraction * sizeof (struct here_fromstruct)); + result->tossize = textsize / HASHFRACTION; + result->fromlimit = textsize * ARCDENSITY / 100; + if (result->fromlimit < MINARCS) + result->fromlimit = MINARCS; + if (result->fromlimit > MAXARCS) + result->fromlimit = MAXARCS; + result->fromssize = result->fromlimit * sizeof (struct here_fromstruct); + + result->expected_size = (sizeof (struct gmon_hdr) + + 4 + sizeof (struct gmon_hist_hdr) + + result->kcountsize + + 4 + 4 + + (result->fromssize + * sizeof (struct here_cg_arc_record))); + + if (do_test) + printf ("expected size: %Zd\n", result->expected_size); + +#define SCALE_1_TO_1 0x10000L + + if (result->kcountsize < result->highpc - result->lowpc) + { + size_t range = result->highpc - result->lowpc; + size_t quot = range / result->kcountsize; + + if (quot >= SCALE_1_TO_1) + result->s_scale = 1; + else if (quot >= SCALE_1_TO_1 / 256) + result->s_scale = SCALE_1_TO_1 / quot; + else if (range > ULONG_MAX / 256) + result->s_scale = ((SCALE_1_TO_1 * 256) + / (range / (result->kcountsize / 256))); + else + result->s_scale = ((SCALE_1_TO_1 * 256) + / ((range * 256) / result->kcountsize)); + } + else + result->s_scale = SCALE_1_TO_1; + + if (do_test) + printf ("s_scale: %d\n", result->s_scale); + + /* Determine the dynamic string table. */ + if (map->l_info[DT_STRTAB] == NULL) + result->dynstrtab = NULL; + else + result->dynstrtab = (const char *) D_PTR (map, l_info[DT_STRTAB]); + if (do_test) + printf ("string table: %p\n", result->dynstrtab); + + /* Determine the soname. */ + if (map->l_info[DT_SONAME] == NULL) + result->soname = NULL; + else + result->soname = result->dynstrtab + map->l_info[DT_SONAME]->d_un.d_val; + if (do_test && result->soname != NULL) + printf ("soname: %s\n", result->soname); + + /* Now we have to load the symbol table. + + First load the section header table. */ + ehdr = (ElfW(Ehdr) *) map->l_map_start; + + /* Make sure we are on the right party. */ + if (ehdr->e_shentsize != sizeof (ElfW(Shdr))) + abort (); + + /* And we need the shared object file descriptor again. */ + fd = open (map->l_name, O_RDONLY); + if (fd == -1) + /* Dooh, this really shouldn't happen. We know the file is available. */ + error (EXIT_FAILURE, errno, _("Reopening shared object `%s' failed"), + map->l_name); + + /* Map the section header. */ + size_t size = ehdr->e_shnum * sizeof (ElfW(Shdr)); + shdr = (ElfW(Shdr) *) alloca (size); + if (pread (fd, shdr, size, ehdr->e_shoff) != size) + error (EXIT_FAILURE, errno, _("reading of section headers failed")); + + /* Get the section header string table. */ + char *shstrtab = (char *) alloca (shdr[ehdr->e_shstrndx].sh_size); + if (pread (fd, shstrtab, shdr[ehdr->e_shstrndx].sh_size, + shdr[ehdr->e_shstrndx].sh_offset) + != shdr[ehdr->e_shstrndx].sh_size) + error (EXIT_FAILURE, errno, + _("reading of section header string table failed")); + + /* Search for the ".symtab" section. */ + ElfW(Shdr) *symtab_entry = NULL; + ElfW(Shdr) *debuglink_entry = NULL; + for (int idx = 0; idx < ehdr->e_shnum; ++idx) + if (shdr[idx].sh_type == SHT_SYMTAB + && strcmp (shstrtab + shdr[idx].sh_name, ".symtab") == 0) + { + symtab_entry = &shdr[idx]; + break; + } + else if (shdr[idx].sh_type == SHT_PROGBITS + && strcmp (shstrtab + shdr[idx].sh_name, ".gnu_debuglink") == 0) + debuglink_entry = &shdr[idx]; + + /* Get the file name of the debuginfo file if necessary. */ + int symfd = fd; + if (symtab_entry == NULL && debuglink_entry != NULL) + { + size_t size = debuglink_entry->sh_size; + char *debuginfo_fname = (char *) alloca (size + 1); + debuginfo_fname[size] = '\0'; + if (pread (fd, debuginfo_fname, size, debuglink_entry->sh_offset) + != size) + { + fprintf (stderr, _("*** Cannot read debuginfo file name: %m\n")); + goto no_debuginfo; + } + + static const char procpath[] = "/proc/self/fd/%d"; + char origprocname[sizeof (procpath) + sizeof (int) * 3]; + snprintf (origprocname, sizeof (origprocname), procpath, fd); + char *origlink = (char *) alloca (PATH_MAX + 1); + origlink[PATH_MAX] = '\0'; + if (readlink (origprocname, origlink, PATH_MAX) == -1) + goto no_debuginfo; + + /* Try to find the actual file. There are three places: + 1. the same directory the DSO is in + 2. in a subdir named .debug of the directory the DSO is in + 3. in /usr/lib/debug/PATH-OF-DSO + */ + char *realname = canonicalize_file_name (origlink); + char *cp = NULL; + if (realname == NULL || (cp = strrchr (realname, '/')) == NULL) + error (EXIT_FAILURE, errno, _("cannot determine file name")); + + /* Leave the last slash in place. */ + *++cp = '\0'; + + /* First add the debuginfo file name only. */ + static const char usrlibdebug[]= "/usr/lib/debug/"; + char *workbuf = (char *) alloca (sizeof (usrlibdebug) + + (cp - realname) + + strlen (debuginfo_fname)); + strcpy (stpcpy (workbuf, realname), debuginfo_fname); + + int fd2 = open (workbuf, O_RDONLY); + if (fd2 == -1) + { + strcpy (stpcpy (stpcpy (workbuf, realname), ".debug/"), + debuginfo_fname); + fd2 = open (workbuf, O_RDONLY); + if (fd2 == -1) + { + strcpy (stpcpy (stpcpy (workbuf, usrlibdebug), realname), + debuginfo_fname); + fd2 = open (workbuf, O_RDONLY); + } + } + + if (fd2 != -1) + { + ElfW(Ehdr) ehdr2; + + /* Read the ELF header. */ + if (pread (fd2, &ehdr2, sizeof (ehdr2), 0) != sizeof (ehdr2)) + error (EXIT_FAILURE, errno, + _("reading of ELF header failed")); + + /* Map the section header. */ + size_t size = ehdr2.e_shnum * sizeof (ElfW(Shdr)); + ElfW(Shdr) *shdr2 = (ElfW(Shdr) *) alloca (size); + if (pread (fd2, shdr2, size, ehdr2.e_shoff) != size) + error (EXIT_FAILURE, errno, + _("reading of section headers failed")); + + /* Get the section header string table. */ + shstrtab = (char *) alloca (shdr2[ehdr2.e_shstrndx].sh_size); + if (pread (fd2, shstrtab, shdr2[ehdr2.e_shstrndx].sh_size, + shdr2[ehdr2.e_shstrndx].sh_offset) + != shdr2[ehdr2.e_shstrndx].sh_size) + error (EXIT_FAILURE, errno, + _("reading of section header string table failed")); + + /* Search for the ".symtab" section. */ + for (int idx = 0; idx < ehdr2.e_shnum; ++idx) + if (shdr2[idx].sh_type == SHT_SYMTAB + && strcmp (shstrtab + shdr2[idx].sh_name, ".symtab") == 0) + { + symtab_entry = &shdr2[idx]; + shdr = shdr2; + symfd = fd2; + break; + } + + if (fd2 != symfd) + close (fd2); + } + } + + no_debuginfo: + if (symtab_entry == NULL) + { + fprintf (stderr, _("\ +*** The file `%s' is stripped: no detailed analysis possible\n"), + name); + result->symtab = NULL; + result->strtab = NULL; + } + else + { + ElfW(Off) min_offset, max_offset; + ElfW(Shdr) *strtab_entry; + + strtab_entry = &shdr[symtab_entry->sh_link]; + + /* Find the minimum and maximum offsets that include both the symbol + table and the string table. */ + if (symtab_entry->sh_offset < strtab_entry->sh_offset) + { + min_offset = symtab_entry->sh_offset & ~(pagesize - 1); + max_offset = strtab_entry->sh_offset + strtab_entry->sh_size; + } + else + { + min_offset = strtab_entry->sh_offset & ~(pagesize - 1); + max_offset = symtab_entry->sh_offset + symtab_entry->sh_size; + } + + result->symbol_map = mmap (NULL, max_offset - min_offset, + PROT_READ, MAP_SHARED|MAP_FILE, symfd, + min_offset); + if (result->symbol_map == MAP_FAILED) + error (EXIT_FAILURE, errno, _("failed to load symbol data")); + + result->symtab + = (const ElfW(Sym) *) ((const char *) result->symbol_map + + (symtab_entry->sh_offset - min_offset)); + result->symtab_size = symtab_entry->sh_size; + result->strtab = ((const char *) result->symbol_map + + (strtab_entry->sh_offset - min_offset)); + result->symbol_mapsize = max_offset - min_offset; + } + + /* Free the descriptor for the shared object. */ + close (fd); + if (symfd != fd) + close (symfd); + + return result; +} + + +static void +unload_shobj (struct shobj *shobj) +{ + munmap (shobj->symbol_map, shobj->symbol_mapsize); + dlclose (shobj->map); +} + + +static struct profdata * +load_profdata (const char *name, struct shobj *shobj) +{ + struct profdata *result; + int fd; + struct stat st; + void *addr; + struct gmon_hdr gmon_hdr; + struct gmon_hist_hdr hist_hdr; + uint32_t *narcsp; + size_t fromlimit; + struct here_cg_arc_record *data; + struct here_fromstruct *froms; + uint16_t *tos; + size_t fromidx; + size_t idx; + + fd = open (name, O_RDONLY); + if (fd == -1) + { + char *ext_name; + + if (errno != ENOENT || strchr (name, '/') != NULL) + /* The file exists but we are not allowed to read it or the + file does not exist and the name includes a path + specification.. */ + return NULL; + + /* A file with the given name does not exist in the current + directory, try it in the default location where the profiling + files are created. */ + ext_name = (char *) alloca (strlen (name) + sizeof "/var/tmp/"); + stpcpy (stpcpy (ext_name, "/var/tmp/"), name); + name = ext_name; + + fd = open (ext_name, O_RDONLY); + if (fd == -1) + { + /* Even this file does not exist. */ + error (0, errno, _("cannot load profiling data")); + return NULL; + } + } + + /* We have found the file, now make sure it is the right one for the + data file. */ + if (fstat (fd, &st) < 0) + { + error (0, errno, _("while stat'ing profiling data file")); + close (fd); + return NULL; + } + + if ((size_t) st.st_size != shobj->expected_size) + { + error (0, 0, + _("profiling data file `%s' does not match shared object `%s'"), + name, shobj->name); + close (fd); + return NULL; + } + + /* The data file is most probably the right one for our shared + object. Map it now. */ + addr = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0); + if (addr == MAP_FAILED) + { + error (0, errno, _("failed to mmap the profiling data file")); + close (fd); + return NULL; + } + + /* We don't need the file desriptor anymore. */ + if (close (fd) < 0) + { + error (0, errno, _("error while closing the profiling data file")); + munmap (addr, st.st_size); + return NULL; + } + + /* Prepare the result. */ + result = (struct profdata *) calloc (1, sizeof (struct profdata)); + if (result == NULL) + { + error (0, errno, _("cannot create internal descriptor")); + munmap (addr, st.st_size); + return NULL; + } + + /* Store the address and size so that we can later free the resources. */ + result->addr = addr; + result->size = st.st_size; + + /* Pointer to data after the header. */ + result->hist = (char *) ((struct gmon_hdr *) addr + 1); + result->hist_hdr = (struct gmon_hist_hdr *) ((char *) result->hist + + sizeof (uint32_t)); + result->kcount = (uint16_t *) ((char *) result->hist + sizeof (uint32_t) + + sizeof (struct gmon_hist_hdr)); + + /* Compute pointer to array of the arc information. */ + narcsp = (uint32_t *) ((char *) result->kcount + shobj->kcountsize + + sizeof (uint32_t)); + result->narcs = *narcsp; + result->data = (struct here_cg_arc_record *) ((char *) narcsp + + sizeof (uint32_t)); + + /* Create the gmon_hdr we expect or write. */ + memset (&gmon_hdr, '\0', sizeof (struct gmon_hdr)); + memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie)); + *(int32_t *) gmon_hdr.version = GMON_SHOBJ_VERSION; + + /* Create the hist_hdr we expect or write. */ + *(char **) hist_hdr.low_pc = (char *) shobj->lowpc - shobj->map->l_addr; + *(char **) hist_hdr.high_pc = (char *) shobj->highpc - shobj->map->l_addr; + if (do_test) + printf ("low_pc = %p\nhigh_pc = %p\n", + *(char **) hist_hdr.low_pc, *(char **) hist_hdr.high_pc); + *(int32_t *) hist_hdr.hist_size = shobj->kcountsize / sizeof (HISTCOUNTER); + *(int32_t *) hist_hdr.prof_rate = __profile_frequency (); + strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen)); + hist_hdr.dimen_abbrev = 's'; + + /* Test whether the header of the profiling data is ok. */ + if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0 + || *(uint32_t *) result->hist != GMON_TAG_TIME_HIST + || memcmp (result->hist_hdr, &hist_hdr, + sizeof (struct gmon_hist_hdr)) != 0 + || narcsp[-1] != GMON_TAG_CG_ARC) + { + error (0, 0, _("`%s' is no correct profile data file for `%s'"), + name, shobj->name); + if (do_test) + { + if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0) + puts ("gmon_hdr differs"); + if (*(uint32_t *) result->hist != GMON_TAG_TIME_HIST) + puts ("result->hist differs"); + if (memcmp (result->hist_hdr, &hist_hdr, + sizeof (struct gmon_hist_hdr)) != 0) + puts ("hist_hdr differs"); + if (narcsp[-1] != GMON_TAG_CG_ARC) + puts ("narcsp[-1] differs"); + } + free (result); + munmap (addr, st.st_size); + return NULL; + } + + /* We are pretty sure now that this is a correct input file. Set up + the remaining information in the result structure and return. */ + result->tos = (uint16_t *) calloc (shobj->tossize + shobj->fromssize, 1); + if (result->tos == NULL) + { + error (0, errno, _("cannot create internal descriptor")); + munmap (addr, st.st_size); + free (result); + return NULL; + } + + result->froms = (struct here_fromstruct *) ((char *) result->tos + + shobj->tossize); + fromidx = 0; + + /* Now we have to process all the arc count entries. */ + fromlimit = shobj->fromlimit; + data = result->data; + froms = result->froms; + tos = result->tos; + for (idx = 0; idx < MIN (*narcsp, fromlimit); ++idx) + { + size_t to_index; + size_t newfromidx; + to_index = (data[idx].self_pc / (shobj->hashfraction * sizeof (*tos))); + newfromidx = fromidx++; + froms[newfromidx].here = &data[idx]; + froms[newfromidx].link = tos[to_index]; + tos[to_index] = newfromidx; + } + + return result; +} + + +static void +unload_profdata (struct profdata *profdata) +{ + free (profdata->tos); + munmap (profdata->addr, profdata->size); + free (profdata); +} + + +static void +count_total_ticks (struct shobj *shobj, struct profdata *profdata) +{ + volatile uint16_t *kcount = profdata->kcount; + size_t maxkidx = shobj->kcountsize; + size_t factor = 2 * (65536 / shobj->s_scale); + size_t kidx = 0; + size_t sidx = 0; + + while (sidx < symidx) + { + uintptr_t start = sortsym[sidx]->addr; + uintptr_t end = start + sortsym[sidx]->size; + + while (kidx < maxkidx && factor * kidx < start) + ++kidx; + if (kidx == maxkidx) + break; + + while (kidx < maxkidx && factor * kidx < end) + sortsym[sidx]->ticks += kcount[kidx++]; + if (kidx == maxkidx) + break; + + total_ticks += sortsym[sidx++]->ticks; + } +} + + +static size_t +find_symbol (uintptr_t addr) +{ + size_t sidx = 0; + + while (sidx < symidx) + { + uintptr_t start = sortsym[sidx]->addr; + uintptr_t end = start + sortsym[sidx]->size; + + if (addr >= start && addr < end) + return sidx; + + if (addr < start) + break; + + ++sidx; + } + + return (size_t) -1l; +} + + +static void +count_calls (struct shobj *shobj, struct profdata *profdata) +{ + struct here_cg_arc_record *data = profdata->data; + uint32_t narcs = profdata->narcs; + uint32_t cnt; + + for (cnt = 0; cnt < narcs; ++cnt) + { + uintptr_t here = data[cnt].self_pc; + size_t symbol_idx; + + /* Find the symbol for this address. */ + symbol_idx = find_symbol (here); + if (symbol_idx != (size_t) -1l) + sortsym[symbol_idx]->calls += data[cnt].count; + } +} + + +static int +symorder (const void *o1, const void *o2) +{ + const struct known_symbol *p1 = (const struct known_symbol *) o1; + const struct known_symbol *p2 = (const struct known_symbol *) o2; + + return p1->addr - p2->addr; +} + + +static void +printsym (const void *node, VISIT value, int level) +{ + if (value == leaf || value == postorder) + sortsym[symidx++] = *(struct known_symbol **) node; +} + + +static void +read_symbols (struct shobj *shobj) +{ + int n = 0; + + /* Initialize the obstacks. */ +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free + obstack_init (&shobj->ob_str); + obstack_init (&shobj->ob_sym); + obstack_init (&ob_list); + + /* Process the symbols. */ + if (shobj->symtab != NULL) + { + const ElfW(Sym) *sym = shobj->symtab; + const ElfW(Sym) *sym_end + = (const ElfW(Sym) *) ((const char *) sym + shobj->symtab_size); + for (; sym < sym_end; sym++) + if ((ELFW(ST_TYPE) (sym->st_info) == STT_FUNC + || ELFW(ST_TYPE) (sym->st_info) == STT_NOTYPE) + && sym->st_size != 0) + { + struct known_symbol **existp; + struct known_symbol *newsym + = (struct known_symbol *) obstack_alloc (&shobj->ob_sym, + sizeof (*newsym)); + if (newsym == NULL) + error (EXIT_FAILURE, errno, _("cannot allocate symbol data")); + + newsym->name = &shobj->strtab[sym->st_name]; + newsym->addr = sym->st_value; + newsym->size = sym->st_size; + newsym->weak = ELFW(ST_BIND) (sym->st_info) == STB_WEAK; + newsym->hidden = (ELFW(ST_VISIBILITY) (sym->st_other) + != STV_DEFAULT); + newsym->ticks = 0; + newsym->calls = 0; + + existp = tfind (newsym, &symroot, symorder); + if (existp == NULL) + { + /* New function. */ + tsearch (newsym, &symroot, symorder); + ++n; + } + else + { + /* The function is already defined. See whether we have + a better name here. */ + if (((*existp)->hidden && !newsym->hidden) + || ((*existp)->name[0] == '_' && newsym->name[0] != '_') + || ((*existp)->name[0] != '_' && newsym->name[0] != '_' + && ((*existp)->weak && !newsym->weak))) + *existp = newsym; + else + /* We don't need the allocated memory. */ + obstack_free (&shobj->ob_sym, newsym); + } + } + } + else + { + /* Blarg, the binary is stripped. We have to rely on the + information contained in the dynamic section of the object. */ + const ElfW(Sym) *symtab = (ElfW(Sym) *) D_PTR (shobj->map, + l_info[DT_SYMTAB]); + const char *strtab = (const char *) D_PTR (shobj->map, + l_info[DT_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 without looking at the section headers. */ + while ((void *) symtab < (void *) strtab) + { + if ((ELFW(ST_TYPE)(symtab->st_info) == STT_FUNC + || ELFW(ST_TYPE)(symtab->st_info) == STT_NOTYPE) + && symtab->st_size != 0) + { + struct known_symbol *newsym; + struct known_symbol **existp; + + newsym = + (struct known_symbol *) obstack_alloc (&shobj->ob_sym, + sizeof (*newsym)); + if (newsym == NULL) + error (EXIT_FAILURE, errno, _("cannot allocate symbol data")); + + newsym->name = &strtab[symtab->st_name]; + newsym->addr = symtab->st_value; + newsym->size = symtab->st_size; + newsym->weak = ELFW(ST_BIND) (symtab->st_info) == STB_WEAK; + newsym->hidden = (ELFW(ST_VISIBILITY) (symtab->st_other) + != STV_DEFAULT); + newsym->ticks = 0; + newsym->froms = NULL; + newsym->tos = NULL; + + existp = tfind (newsym, &symroot, symorder); + if (existp == NULL) + { + /* New function. */ + tsearch (newsym, &symroot, symorder); + ++n; + } + else + { + /* The function is already defined. See whether we have + a better name here. */ + if (((*existp)->hidden && !newsym->hidden) + || ((*existp)->name[0] == '_' && newsym->name[0] != '_') + || ((*existp)->name[0] != '_' && newsym->name[0] != '_' + && ((*existp)->weak && !newsym->weak))) + *existp = newsym; + else + /* We don't need the allocated memory. */ + obstack_free (&shobj->ob_sym, newsym); + } + } + + ++symtab; + } + } + + sortsym = malloc (n * sizeof (struct known_symbol *)); + if (sortsym == NULL) + abort (); + + twalk (symroot, printsym); +} + + +static void +add_arcs (struct profdata *profdata) +{ + uint32_t narcs = profdata->narcs; + struct here_cg_arc_record *data = profdata->data; + uint32_t cnt; + + for (cnt = 0; cnt < narcs; ++cnt) + { + /* First add the incoming arc. */ + size_t sym_idx = find_symbol (data[cnt].self_pc); + + if (sym_idx != (size_t) -1l) + { + struct known_symbol *sym = sortsym[sym_idx]; + struct arc_list *runp = sym->froms; + + while (runp != NULL + && ((data[cnt].from_pc == 0 && runp->idx != (size_t) -1l) + || (data[cnt].from_pc != 0 + && (runp->idx == (size_t) -1l + || data[cnt].from_pc < sortsym[runp->idx]->addr + || (data[cnt].from_pc + >= (sortsym[runp->idx]->addr + + sortsym[runp->idx]->size)))))) + runp = runp->next; + + if (runp == NULL) + { + /* We need a new entry. */ + struct arc_list *newp = (struct arc_list *) + obstack_alloc (&ob_list, sizeof (struct arc_list)); + + if (data[cnt].from_pc == 0) + newp->idx = (size_t) -1l; + else + newp->idx = find_symbol (data[cnt].from_pc); + newp->count = data[cnt].count; + newp->next = sym->froms; + sym->froms = newp; + } + else + /* Increment the counter for the found entry. */ + runp->count += data[cnt].count; + } + + /* Now add it to the appropriate outgoing list. */ + sym_idx = find_symbol (data[cnt].from_pc); + if (sym_idx != (size_t) -1l) + { + struct known_symbol *sym = sortsym[sym_idx]; + struct arc_list *runp = sym->tos; + + while (runp != NULL + && (runp->idx == (size_t) -1l + || data[cnt].self_pc < sortsym[runp->idx]->addr + || data[cnt].self_pc >= (sortsym[runp->idx]->addr + + sortsym[runp->idx]->size))) + runp = runp->next; + + if (runp == NULL) + { + /* We need a new entry. */ + struct arc_list *newp = (struct arc_list *) + obstack_alloc (&ob_list, sizeof (struct arc_list)); + + newp->idx = find_symbol (data[cnt].self_pc); + newp->count = data[cnt].count; + newp->next = sym->tos; + sym->tos = newp; + } + else + /* Increment the counter for the found entry. */ + runp->count += data[cnt].count; + } + } +} + + +static int +countorder (const void *p1, const void *p2) +{ + struct known_symbol *s1 = (struct known_symbol *) p1; + struct known_symbol *s2 = (struct known_symbol *) p2; + + if (s1->ticks != s2->ticks) + return (int) (s2->ticks - s1->ticks); + + if (s1->calls != s2->calls) + return (int) (s2->calls - s1->calls); + + return strcmp (s1->name, s2->name); +} + + +static double tick_unit; +static uintmax_t cumu_ticks; + +static void +printflat (const void *node, VISIT value, int level) +{ + if (value == leaf || value == postorder) + { + struct known_symbol *s = *(struct known_symbol **) node; + + cumu_ticks += s->ticks; + + printf ("%6.2f%10.2f%9.2f%9" PRIdMAX "%9.2f %s\n", + total_ticks ? (100.0 * s->ticks) / total_ticks : 0.0, + tick_unit * cumu_ticks, + tick_unit * s->ticks, + s->calls, + s->calls ? (s->ticks * 1000000) * tick_unit / s->calls : 0, + /* FIXME: don't know about called functions. */ + s->name); + } +} + + +/* ARGUSED */ +static void +freenoop (void *p) +{ +} + + +static void +generate_flat_profile (struct profdata *profdata) +{ + size_t n; + void *data = NULL; + + tick_unit = 1.0 / *(uint32_t *) profdata->hist_hdr->prof_rate; + + printf ("Flat profile:\n\n" + "Each sample counts as %g %s.\n", + tick_unit, profdata->hist_hdr->dimen); + fputs (" % cumulative self self total\n" + " time seconds seconds calls us/call us/call name\n", + stdout); + + for (n = 0; n < symidx; ++n) + if (sortsym[n]->calls != 0 || sortsym[n]->ticks != 0) + tsearch (sortsym[n], &data, countorder); + + twalk (data, printflat); + + tdestroy (data, freenoop); +} + + +static void +generate_call_graph (struct profdata *profdata) +{ + size_t cnt; + + puts ("\nindex % time self children called name\n"); + + for (cnt = 0; cnt < symidx; ++cnt) + if (sortsym[cnt]->froms != NULL || sortsym[cnt]->tos != NULL) + { + struct arc_list *runp; + size_t n; + + /* First print the from-information. */ + runp = sortsym[cnt]->froms; + while (runp != NULL) + { + printf (" %8.2f%8.2f%9" PRIdMAX "/%-9" PRIdMAX " %s", + (runp->idx != (size_t) -1l + ? sortsym[runp->idx]->ticks * tick_unit : 0.0), + 0.0, /* FIXME: what's time for the children, recursive */ + runp->count, sortsym[cnt]->calls, + (runp->idx != (size_t) -1l ? + sortsym[runp->idx]->name : "<UNKNOWN>")); + + if (runp->idx != (size_t) -1l) + printf (" [%Zd]", runp->idx); + putchar_unlocked ('\n'); + + runp = runp->next; + } + + /* Info abount the function itself. */ + n = printf ("[%Zu]", cnt); + printf ("%*s%5.1f%8.2f%8.2f%9" PRIdMAX " %s [%Zd]\n", + (int) (7 - n), " ", + total_ticks ? (100.0 * sortsym[cnt]->ticks) / total_ticks : 0, + sortsym[cnt]->ticks * tick_unit, + 0.0, /* FIXME: what's time for the children, recursive */ + sortsym[cnt]->calls, + sortsym[cnt]->name, cnt); + + /* Info about the functions this function calls. */ + runp = sortsym[cnt]->tos; + while (runp != NULL) + { + printf (" %8.2f%8.2f%9" PRIdMAX "/", + (runp->idx != (size_t) -1l + ? sortsym[runp->idx]->ticks * tick_unit : 0.0), + 0.0, /* FIXME: what's time for the children, recursive */ + runp->count); + + if (runp->idx != (size_t) -1l) + printf ("%-9" PRIdMAX " %s [%Zd]\n", + sortsym[runp->idx]->calls, + sortsym[runp->idx]->name, + runp->idx); + else + fputs ("??? <UNKNOWN>\n\n", stdout); + + runp = runp->next; + } + + fputs ("-----------------------------------------------\n", stdout); + } +} + + +static void +generate_call_pair_list (struct profdata *profdata) +{ + size_t cnt; + + for (cnt = 0; cnt < symidx; ++cnt) + if (sortsym[cnt]->froms != NULL || sortsym[cnt]->tos != NULL) + { + struct arc_list *runp; + + /* First print the incoming arcs. */ + runp = sortsym[cnt]->froms; + while (runp != NULL) + { + if (runp->idx == (size_t) -1l) + printf ("\ +<UNKNOWN> %-34s %9" PRIdMAX "\n", + sortsym[cnt]->name, runp->count); + runp = runp->next; + } + + /* Next the outgoing arcs. */ + runp = sortsym[cnt]->tos; + while (runp != NULL) + { + printf ("%-34s %-34s %9" PRIdMAX "\n", + sortsym[cnt]->name, + (runp->idx != (size_t) -1l + ? sortsym[runp->idx]->name : "<UNKNOWN>"), + runp->count); + runp = runp->next; + } + } +} diff --git a/libc/elf/stackguard-macros.h b/libc/elf/stackguard-macros.h new file mode 100644 index 000000000..97db8bc22 --- /dev/null +++ b/libc/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/libc/elf/testobj.h b/libc/elf/testobj.h new file mode 100644 index 000000000..12f18effc --- /dev/null +++ b/libc/elf/testobj.h @@ -0,0 +1,28 @@ +extern int preload (int a); + +extern int foo (int); + +extern int obj1func1 (int); + +extern int obj1func2 (int); + +extern int obj2func1 (int); + +extern int obj2func2 (int); + +extern int obj3func1 (int); + +extern int obj3func2 (int); + +extern int obj4func1 (int); + +extern int obj4func2 (int); + +extern int obj5func1 (int); + +extern int obj5func2 (int); + +extern int obj6func1 (int); + +extern int obj6func2 (int); + diff --git a/libc/elf/testobj1.c b/libc/elf/testobj1.c new file mode 100644 index 000000000..5ab20efd6 --- /dev/null +++ b/libc/elf/testobj1.c @@ -0,0 +1,25 @@ +#include <dlfcn.h> +#include <stdlib.h> + +#include "testobj.h" + +int +obj1func1 (int a __attribute__ ((unused))) +{ + return 42; +} + +int +obj1func2 (int a) +{ + return foo (a) + 10; +} + +int +preload (int a) +{ + int (*fp) (int) = dlsym (RTLD_NEXT, "preload"); + if (fp != NULL) + return fp (a) + 10; + return 10; +} diff --git a/libc/elf/testobj1_1.c b/libc/elf/testobj1_1.c new file mode 100644 index 000000000..2541a5ad1 --- /dev/null +++ b/libc/elf/testobj1_1.c @@ -0,0 +1,7 @@ +#include "testobj.h" + +int +obj1func1 (int a) +{ + return 42 + obj1func2 (a); +} diff --git a/libc/elf/testobj2.c b/libc/elf/testobj2.c new file mode 100644 index 000000000..7e4b61098 --- /dev/null +++ b/libc/elf/testobj2.c @@ -0,0 +1,32 @@ +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> + +#include "testobj.h" + +int +obj2func1 (int a __attribute__ ((unused))) +{ + return 43; +} + +int +obj2func2 (int a) +{ + return obj1func1 (a) + 10; +} + +int +preload (int a) +{ + int (*fp) (int) = dlsym (RTLD_NEXT, "preload"); + if (fp != NULL) + return fp (a) + 10; + return 10; +} + +void +p (void) +{ + puts ("hello world"); +} diff --git a/libc/elf/testobj3.c b/libc/elf/testobj3.c new file mode 100644 index 000000000..c025ff631 --- /dev/null +++ b/libc/elf/testobj3.c @@ -0,0 +1,26 @@ +#include <dlfcn.h> +#include <stdlib.h> + +#include "testobj.h" + + +int +obj3func1 (int a __attribute__ ((unused))) +{ + return 44; +} + +int +obj3func2 (int a) +{ + return foo (a) + 42; +} + +int +preload (int a) +{ + int (*fp) (int) = dlsym (RTLD_NEXT, "preload"); + if (fp != NULL) + return fp (a) + 10; + return 10; +} diff --git a/libc/elf/testobj4.c b/libc/elf/testobj4.c new file mode 100644 index 000000000..2729ba32b --- /dev/null +++ b/libc/elf/testobj4.c @@ -0,0 +1,25 @@ +#include <dlfcn.h> +#include <stdlib.h> + +#include "testobj.h" + +int +obj4func1 (int a __attribute__ ((unused))) +{ + return 55; +} + +int +obj4func2 (int a) +{ + return foo (a) + 43; +} + +int +preload (int a) +{ + int (*fp) (int) = dlsym (RTLD_NEXT, "preload"); + if (fp != NULL) + return fp (a) + 10; + return 10; +} diff --git a/libc/elf/testobj5.c b/libc/elf/testobj5.c new file mode 100644 index 000000000..9675cad88 --- /dev/null +++ b/libc/elf/testobj5.c @@ -0,0 +1,26 @@ +#include <dlfcn.h> +#include <stdlib.h> + +#include "testobj.h" + + +int +obj5func1 (int a __attribute__ ((unused))) +{ + return 66; +} + +int +obj5func2 (int a) +{ + return foo (a) + 44; +} + +int +preload (int a) +{ + int (*fp) (int) = dlsym (RTLD_NEXT, "preload"); + if (fp != NULL) + return fp (a) + 10; + return 10; +} diff --git a/libc/elf/testobj6.c b/libc/elf/testobj6.c new file mode 100644 index 000000000..fcba01631 --- /dev/null +++ b/libc/elf/testobj6.c @@ -0,0 +1,19 @@ +#include "testobj.h" + +int +obj6func1 (int a __attribute__ ((unused))) +{ + return 77; +} + +int +obj6func2 (int a) +{ + return foo (a) + 46; +} + +int +preload (int a) +{ + return a; +} diff --git a/libc/elf/tls-macros.h b/libc/elf/tls-macros.h new file mode 100644 index 000000000..37cbe7514 --- /dev/null +++ b/libc/elf/tls-macros.h @@ -0,0 +1,851 @@ +/* Macros to support TLS testing in times of missing compiler support. */ + +#define COMMON_INT_DEF(x) \ + asm (".tls_common " #x ",4,4") +/* XXX Until we get compiler support we don't need declarations. */ +#define COMMON_INT_DECL(x) + +/* XXX This definition will probably be machine specific, too. */ +#define VAR_INT_DEF(x) \ + asm (".section .tdata\n\t" \ + ".globl " #x "\n" \ + ".balign 4\n" \ + #x ":\t.long 0\n\t" \ + ".size " #x ",4\n\t" \ + ".previous") +/* 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__ +# define TLS_LE(x) \ + ({ int *__l; \ + asm ("movl %%gs:0,%0\n\t" \ + "subl $" #x "@tpoff,%0" \ + : "=r" (__l)); \ + __l; }) + +# ifdef PIC +# define TLS_IE(x) \ + ({ int *__l; \ + asm ("movl %%gs:0,%0\n\t" \ + "subl " #x "@gottpoff(%%ebx),%0" \ + : "=r" (__l)); \ + __l; }) +# else +# define TLS_IE(x) \ + ({ int *__l, __b; \ + asm ("call 1f\n\t" \ + ".subsection 1\n" \ + "1:\tmovl (%%esp), %%ebx\n\t" \ + "ret\n\t" \ + ".previous\n\t" \ + "addl $_GLOBAL_OFFSET_TABLE_, %%ebx\n\t" \ + "movl %%gs:0,%0\n\t" \ + "subl " #x "@gottpoff(%%ebx),%0" \ + : "=r" (__l), "=&b" (__b)); \ + __l; }) +# endif + +# ifdef PIC +# define TLS_LD(x) \ + ({ int *__l, __c, __d; \ + asm ("leal " #x "@tlsldm(%%ebx),%%eax\n\t" \ + "call ___tls_get_addr@plt\n\t" \ + "leal " #x "@dtpoff(%%eax), %%eax" \ + : "=a" (__l), "=&c" (__c), "=&d" (__d)); \ + __l; }) +# else +# define TLS_LD(x) \ + ({ int *__l, __b, __c, __d; \ + asm ("call 1f\n\t" \ + ".subsection 1\n" \ + "1:\tmovl (%%esp), %%ebx\n\t" \ + "ret\n\t" \ + ".previous\n\t" \ + "addl $_GLOBAL_OFFSET_TABLE_, %%ebx\n\t" \ + "leal " #x "@tlsldm(%%ebx),%%eax\n\t" \ + "call ___tls_get_addr@plt\n\t" \ + "leal " #x "@dtpoff(%%eax), %%eax" \ + : "=a" (__l), "=&b" (__b), "=&c" (__c), "=&d" (__d)); \ + __l; }) +# endif + +# ifdef PIC +# define TLS_GD(x) \ + ({ int *__l, __c, __d; \ + asm ("leal " #x "@tlsgd(%%ebx),%%eax\n\t" \ + "call ___tls_get_addr@plt\n\t" \ + "nop" \ + : "=a" (__l), "=&c" (__c), "=&d" (__d)); \ + __l; }) +# else +# define TLS_GD(x) \ + ({ int *__l, __b, __c, __d; \ + asm ("call 1f\n\t" \ + ".subsection 1\n" \ + "1:\tmovl (%%esp), %%ebx\n\t" \ + "ret\n\t" \ + ".previous\n\t" \ + "addl $_GLOBAL_OFFSET_TABLE_, %%ebx\n\t" \ + "leal " #x "@tlsgd(%%ebx),%%eax\n\t" \ + "call ___tls_get_addr@plt\n\t" \ + "nop" \ + : "=a" (__l), "=&b" (__b), "=&c" (__c), "=&d" (__d)); \ + __l; }) +# endif + +#elif defined __x86_64__ + +# define TLS_LE(x) \ + ({ int *__l; \ + asm ("movq %%fs:0,%0\n\t" \ + "leaq " #x "@tpoff(%0), %0" \ + : "=r" (__l)); \ + __l; }) + +# define TLS_IE(x) \ + ({ int *__l; \ + asm ("movq %%fs:0,%0\n\t" \ + "addq " #x "@gottpoff(%%rip),%0" \ + : "=r" (__l)); \ + __l; }) + +# define TLS_LD(x) \ + ({ int *__l, __c, __d; \ + asm ("leaq " #x "@tlsld(%%rip),%%rdi\n\t" \ + "call __tls_get_addr@plt\n\t" \ + "leaq " #x "@dtpoff(%%rax), %%rax" \ + : "=a" (__l), "=&c" (__c), "=&d" (__d) \ + : : "rdi", "rsi", "r8", "r9", "r10", "r11"); \ + __l; }) + +# define TLS_GD(x) \ + ({ int *__l, __c, __d; \ + asm (".byte 0x66\n\t" \ + "leaq " #x "@tlsgd(%%rip),%%rdi\n\t" \ + ".word 0x6666\n\t" \ + "rex64\n\t" \ + "call __tls_get_addr@plt" \ + : "=a" (__l), "=&c" (__c), "=&d" (__d) \ + : : "rdi", "rsi", "r8", "r9", "r10", "r11"); \ + __l; }) + +#elif defined __sh__ + +# define TLS_LE(x) \ + ({ int *__l; void *__tp; \ + asm ("stc gbr,%1\n\t" \ + "mov.l 1f,%0\n\t" \ + "bra 2f\n\t" \ + " add %1,%0\n\t" \ + ".align 2\n\t" \ + "1: .long " #x "@tpoff\n\t" \ + "2:" \ + : "=r" (__l), "=r" (__tp)); \ + __l; }) + +# ifdef PIC +# define TLS_IE(x) \ + ({ int *__l; void *__tp; \ + register void *__gp __asm__("r12"); \ + asm ("mov.l 1f,r0\n\t" \ + "stc gbr,%1\n\t" \ + "mov.l @(r0,r12),%0\n\t" \ + "bra 2f\n\t" \ + " add %1,%0\n\t" \ + ".align 2\n\t" \ + "1: .long " #x "@gottpoff\n\t" \ + "2:" \ + : "=r" (__l), "=r" (__tp) : "r" (__gp) : "r0"); \ + __l; }) +# else +# define TLS_IE(x) \ + ({ int *__l; void *__tp; \ + asm ("mov.l r12,@-r15\n\t" \ + "mova 0f,r0\n\t" \ + "mov.l 0f,r12\n\t" \ + "add r0,r12\n\t" \ + "mov.l 1f,r0\n\t" \ + "stc gbr,%1\n\t" \ + "mov.l @(r0,r12),%0\n\t" \ + "bra 2f\n\t" \ + " add %1,%0\n\t" \ + ".align 2\n\t" \ + "1: .long " #x "@gottpoff\n\t" \ + "0: .long _GLOBAL_OFFSET_TABLE_\n\t" \ + "2: mov.l @r15+,r12" \ + : "=r" (__l), "=r" (__tp) : : "r0"); \ + __l; }) +#endif + +# ifdef PIC +# define TLS_LD(x) \ + ({ int *__l; \ + register void *__gp __asm__("r12"); \ + asm ("mov.l 1f,r4\n\t" \ + "mova 2f,r0\n\t" \ + "mov.l 2f,r1\n\t" \ + "add r0,r1\n\t" \ + "jsr @r1\n\t" \ + " add r12,r4\n\t" \ + "bra 4f\n\t" \ + " nop\n\t" \ + ".align 2\n\t" \ + "1: .long " #x "@tlsldm\n\t" \ + "2: .long __tls_get_addr@plt\n\t" \ + "4: mov.l 3f,%0\n\t" \ + "bra 5f\n\t" \ + " add r0,%0\n\t" \ + ".align 2\n\t" \ + "3: .long " #x "@dtpoff\n\t" \ + "5:" \ + : "=r" (__l) : "r" (__gp) : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "pr", "t"); \ + __l; }) +# else +# define TLS_LD(x) \ + ({ int *__l; \ + asm ("mov.l r12,@-r15\n\t" \ + "mova 0f,r0\n\t" \ + "mov.l 0f,r12\n\t" \ + "add r0,r12\n\t" \ + "mov.l 1f,r4\n\t" \ + "mova 2f,r0\n\t" \ + "mov.l 2f,r1\n\t" \ + "add r0,r1\n\t" \ + "jsr @r1\n\t" \ + " add r12,r4\n\t" \ + "bra 4f\n\t" \ + " nop\n\t" \ + ".align 2\n\t" \ + "1: .long " #x "@tlsldm\n\t" \ + "2: .long __tls_get_addr@plt\n\t" \ + "0: .long _GLOBAL_OFFSET_TABLE_\n\t" \ + "4: mov.l 3f,%0\n\t" \ + "bra 5f\n\t" \ + " add r0,%0\n\t" \ + ".align 2\n\t" \ + "3: .long " #x "@dtpoff\n\t" \ + "5: mov.l @r15+,r12" \ + : "=r" (__l) : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ + "pr", "t"); \ + __l; }) +#endif + +# ifdef PIC +# define TLS_GD(x) \ + ({ int *__l; \ + register void *__gp __asm__("r12"); \ + asm ("mov.l 1f,r4\n\t" \ + "mova 2f,r0\n\t" \ + "mov.l 2f,r1\n\t" \ + "add r0,r1\n\t" \ + "jsr @r1\n\t" \ + " add r12,r4\n\t" \ + "bra 3f\n\t" \ + " mov r0,%0\n\t" \ + ".align 2\n\t" \ + "1: .long " #x "@tlsgd\n\t" \ + "2: .long __tls_get_addr@plt\n\t" \ + "3:" \ + : "=r" (__l) : "r" (__gp) : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "pr", "t"); \ + __l; }) +# else +# define TLS_GD(x) \ + ({ int *__l; \ + asm ("mov.l r12,@-r15\n\t" \ + "mova 0f,r0\n\t" \ + "mov.l 0f,r12\n\t" \ + "add r0,r12\n\t" \ + "mov.l 1f,r4\n\t" \ + "mova 2f,r0\n\t" \ + "mov.l 2f,r1\n\t" \ + "add r0,r1\n\t" \ + "jsr @r1\n\t" \ + " add r12,r4\n\t" \ + "bra 3f\n\t" \ + " mov r0,%0\n\t" \ + ".align 2\n\t" \ + "1: .long " #x "@tlsgd\n\t" \ + "2: .long __tls_get_addr@plt\n\t" \ + "0: .long _GLOBAL_OFFSET_TABLE_\n\t" \ + "3: mov.l @r15+,r12" \ + : "=r" (__l) : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ + "pr", "t"); \ + __l; }) +#endif + +#elif defined __alpha__ + +register void *__gp __asm__("$29"); + +# define TLS_LE(x) \ + ({ int *__l; \ + asm ("call_pal 158\n\tlda $0," #x "($0)\t\t!tprel" : "=v"(__l)); \ + __l; }) + +# define TLS_IE(x) \ + ({ char *__tp; unsigned long __o; \ + asm ("call_pal 158\n\tldq %1," #x "($gp)\t\t!gottprel" \ + : "=v"(__tp), "=r"(__o) : "r"(__gp)); \ + (int *)(__tp + __o); }) + +# define TLS_LD(x) \ + ({ extern void *__tls_get_addr(void *); int *__l; void *__i; \ + asm ("lda %0," #x "($gp)\t\t!tlsldm" : "=r" (__i) : "r"(__gp)); \ + __i = __tls_get_addr(__i); \ + asm ("lda %0, " #x "(%1)\t\t!dtprel" : "=r"(__l) : "r"(__i)); \ + __l; }) + +# define TLS_GD(x) \ + ({ extern void *__tls_get_addr(void *); void *__i; \ + asm ("lda %0," #x "($gp)\t\t!tlsgd" : "=r" (__i) : "r"(__gp)); \ + (int *) __tls_get_addr(__i); }) + + +#elif defined __ia64__ + +# define TLS_LE(x) \ + ({ void *__l; \ + asm ("mov r2=r13\n\t" \ + ";;\n\t" \ + "addl %0=@tprel(" #x "),r2\n\t" \ + : "=r" (__l) : : "r2" ); __l; }) + +# define TLS_IE(x) \ + ({ void *__l; \ + register long __gp asm ("gp"); \ + asm (";;\n\t" \ + "addl r16=@ltoff(@tprel(" #x ")),gp\n\t" \ + ";;\n\t" \ + "ld8 r17=[r16]\n\t" \ + ";;\n\t" \ + "add %0=r13,r17\n\t" \ + ";;\n\t" \ + : "=r" (__l) : "r" (__gp) : "r16", "r17" ); __l; }) + +# define __TLS_CALL_CLOBBERS \ + "r2", "r3", "r8", "r9", "r10", "r11", "r14", "r15", "r16", "r17", \ + "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", \ + "r27", "r28", "r29", "r30", "r31", \ + "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15", \ + "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", \ + "b6", "b7", \ + "out0", "out1", "out2", "out3", "out4", "out5", "out6", "out7" + +# define TLS_LD(x) \ + ({ void *__l; \ + register long __gp asm ("gp"); \ + asm (";;\n\t" \ + "mov loc0=gp\n\t" \ + "addl r16=@ltoff(@dtpmod(" #x ")),gp\n\t" \ + "addl out1=@dtprel(" #x "),r0\n\t" \ + ";;\n\t" \ + "ld8 out0=[r16]\n\t" \ + "br.call.sptk.many b0=__tls_get_addr" \ + ";;\n\t" \ + "mov gp=loc0\n\t" \ + "mov %0=r8\n\t" \ + ";;\n\t" \ + : "=r" (__l) : "r" (__gp) : "loc0", __TLS_CALL_CLOBBERS); \ + __l; }) + +# define TLS_GD(x) \ + ({ void *__l; \ + register long __gp asm ("gp"); \ + asm (";;\n\t" \ + "mov loc0=gp\n\t" \ + "addl r16=@ltoff(@dtpmod(" #x ")),gp\n\t" \ + "addl r17=@ltoff(@dtprel(" #x ")),gp\n\t" \ + ";;\n\t" \ + "ld8 out0=[r16]\n\t" \ + "ld8 out1=[r17]\n\t" \ + "br.call.sptk.many b0=__tls_get_addr" \ + ";;\n\t" \ + "mov gp=loc0\n\t" \ + "mov %0=r8\n\t" \ + ";;\n\t" \ + : "=r" (__l) : "r" (__gp) : "loc0", __TLS_CALL_CLOBBERS); \ + __l; }) + +#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 \ + ({ register long pc __asm__ ("%o7"); \ + long got; \ + asm ("sethi %%hi(_GLOBAL_OFFSET_TABLE_-4), %1\n\t" \ + "call .+8\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 ("ld [%1 + %2], %0, %%tie_ld(" #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 __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) \ + ({ unsigned long __offset; \ + asm ("bras %0,1f\n" \ + "0:\t.quad " #x "@ntpoff\n" \ + "1:\tlg %0,0(%0)" \ + : "=a" (__offset) : : "cc" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) + +# ifdef PIC +# define TLS_IE(x) \ + ({ unsigned long __offset; \ + asm ("bras %0,1f\n" \ + "0:\t.quad " #x "@gotntpoff\n" \ + "1:\tlg %0,0(%0)\n\t" \ + "lg %0,0(%0,%%r12):tls_load:" #x \ + : "=&a" (__offset) : : "cc" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# else +# define TLS_IE(x) \ + ({ unsigned long __offset; \ + asm ("bras %0,1f\n" \ + "0:\t.quad " #x "@indntpoff\n" \ + "1:\t lg %0,0(%0)\n\t" \ + "lg %0,0(%0):tls_load:" #x \ + : "=&a" (__offset) : : "cc" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# endif + +# ifdef PIC +# define TLS_LD(x) \ + ({ unsigned long __offset, __save12; \ + asm ("bras %0,1f\n" \ + "0:\t.quad " #x "@tlsldm\n\t" \ + ".quad " #x "@dtpoff\n" \ + "1:\tlgr %1,%%r12\n\t" \ + "larl %%r12,_GLOBAL_OFFSET_TABLE_\n\t" \ + "lg %%r2,0(%0)\n\t" \ + "brasl %%r14,__tls_get_offset@plt:tls_ldcall:" #x "\n\t" \ + "lg %0,8(%0)\n\t" \ + "algr %0,%%r2\n\t" \ + "lgr %%r12,%1" \ + : "=&a" (__offset), "=&a" (__save12) \ + : : "cc", "0", "1", "2", "3", "4", "5", "14" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# else +# define TLS_LD(x) \ + ({ unsigned long __offset; \ + asm ("bras %0,1f\n" \ + "0:\t.quad " #x "@tlsldm\n\t" \ + ".quad " #x "@dtpoff\n" \ + "1:\tlarl %%r12,_GLOBAL_OFFSET_TABLE_\n\t" \ + "lg %%r2,0(%0)\n\t" \ + "brasl %%r14,__tls_get_offset@plt:tls_ldcall:" #x "\n\t" \ + "lg %0,8(%0)\n\t" \ + "algr %0,%%r2" \ + : "=&a" (__offset) \ + : : "cc", "0", "1", "2", "3", "4", "5", "12", "14" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# endif + +# ifdef PIC +# define TLS_GD(x) \ + ({ unsigned long __offset, __save12; \ + asm ("bras %0,1f\n" \ + "0:\t.quad " #x "@tlsgd\n" \ + "1:\tlgr %1,%%r12\n\t" \ + "larl %%r12,_GLOBAL_OFFSET_TABLE_\n\t" \ + "lg %%r2,0(%0)\n\t" \ + "brasl %%r14,__tls_get_offset@plt:tls_gdcall:" #x "\n\t" \ + "lgr %0,%%r2\n\t" \ + "lgr %%r12,%1" \ + : "=&a" (__offset), "=&a" (__save12) \ + : : "cc", "0", "1", "2", "3", "4", "5", "14" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# else +# define TLS_GD(x) \ + ({ unsigned long __offset; \ + asm ("bras %0,1f\n" \ + "0:\t.quad " #x "@tlsgd\n" \ + "1:\tlarl %%r12,_GLOBAL_OFFSET_TABLE_\n\t" \ + "lg %%r2,0(%0)\n\t" \ + "brasl %%r14,__tls_get_offset@plt:tls_gdcall:" #x "\n\t" \ + "lgr %0,%%r2" \ + : "=&a" (__offset) \ + : : "cc", "0", "1", "2", "3", "4", "5", "12", "14" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# endif + +#elif defined __s390__ + +# define TLS_LE(x) \ + ({ unsigned long __offset; \ + asm ("bras %0,1f\n" \ + "0:\t.long " #x "@ntpoff\n" \ + "1:\tl %0,0(%0)" \ + : "=a" (__offset) : : "cc" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) + +# ifdef PIC +# define TLS_IE(x) \ + ({ unsigned long __offset; \ + asm ("bras %0,1f\n" \ + "0:\t.long " #x "@gotntpoff\n" \ + "1:\tl %0,0(%0)\n\t" \ + "l %0,0(%0,%%r12):tls_load:" #x \ + : "=&a" (__offset) : : "cc" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# else +# define TLS_IE(x) \ + ({ unsigned long __offset; \ + asm ("bras %0,1f\n" \ + "0:\t.long " #x "@indntpoff\n" \ + "1:\t l %0,0(%0)\n\t" \ + "l %0,0(%0):tls_load:" #x \ + : "=&a" (__offset) : : "cc" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# endif + +# ifdef PIC +# define TLS_LD(x) \ + ({ unsigned long __offset, __save12; \ + asm ("bras %0,1f\n" \ + "0:\t.long _GLOBAL_OFFSET_TABLE_-0b\n\t" \ + ".long __tls_get_offset@plt-0b\n\t" \ + ".long " #x "@tlsldm\n\t" \ + ".long " #x "@dtpoff\n" \ + "1:\tlr %1,%%r12\n\t" \ + "l %%r12,0(%0)\n\t" \ + "la %%r12,0(%%r12,%0)\n\t" \ + "l %%r1,4(%0)\n\t" \ + "l %%r2,8(%0)\n\t" \ + "bas %%r14,0(%%r1,%0):tls_ldcall:" #x "\n\t" \ + "l %0,12(%0)\n\t" \ + "alr %0,%%r2\n\t" \ + "lr %%r12,%1" \ + : "=&a" (__offset), "=&a" (__save12) \ + : : "cc", "0", "1", "2", "3", "4", "5" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# else +# define TLS_LD(x) \ + ({ unsigned long __offset; \ + asm ("bras %0,1f\n" \ + "0:\t.long _GLOBAL_OFFSET_TABLE_\n\t" \ + ".long __tls_get_offset@plt\n\t" \ + ".long " #x "@tlsldm\n\t" \ + ".long " #x "@dtpoff\n" \ + "1:\tl %%r12,0(%0)\n\t" \ + "l %%r1,4(%0)\n\t" \ + "l %%r2,8(%0)\n\t" \ + "bas %%r14,0(%%r1):tls_ldcall:" #x "\n\t" \ + "l %0,12(%0)\n\t" \ + "alr %0,%%r2" \ + : "=&a" (__offset) : : "cc", "0", "1", "2", "3", "4", "5", "12" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# endif + +# ifdef PIC +# define TLS_GD(x) \ + ({ unsigned long __offset, __save12; \ + asm ("bras %0,1f\n" \ + "0:\t.long _GLOBAL_OFFSET_TABLE_-0b\n\t" \ + ".long __tls_get_offset@plt-0b\n\t" \ + ".long " #x "@tlsgd\n" \ + "1:\tlr %1,%%r12\n\t" \ + "l %%r12,0(%0)\n\t" \ + "la %%r12,0(%%r12,%0)\n\t" \ + "l %%r1,4(%0)\n\t" \ + "l %%r2,8(%0)\n\t" \ + "bas %%r14,0(%%r1,%0):tls_gdcall:" #x "\n\t" \ + "lr %0,%%r2\n\t" \ + "lr %%r12,%1" \ + : "=&a" (__offset), "=&a" (__save12) \ + : : "cc", "0", "1", "2", "3", "4", "5" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# else +# define TLS_GD(x) \ + ({ unsigned long __offset; \ + asm ("bras %0,1f\n" \ + "0:\t.long _GLOBAL_OFFSET_TABLE_\n\t" \ + ".long __tls_get_offset@plt\n\t" \ + ".long " #x "@tlsgd\n" \ + "1:\tl %%r12,0(%0)\n\t" \ + "l %%r1,4(%0)\n\t" \ + "l %%r2,8(%0)\n\t" \ + "bas %%r14,0(%%r1):tls_gdcall:" #x "\n\t" \ + "lr %0,%%r2" \ + : "=&a" (__offset) : : "cc", "0", "1", "2", "3", "4", "5", "12" ); \ + (int *) (__builtin_thread_pointer() + __offset); }) +# endif + +#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" + +/* PowerPC32 Local Exec TLS access. */ +# define TLS_LE(x) \ + ({ int *__result; \ + asm ("addi %0,2," #x "@tprel" \ + : "=r" (__result)); \ + __result; }) + +/* PowerPC32 Initial Exec TLS access. */ +# 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" \ + "lwz %0," #x "@got@tprel(%0)\n\t" \ + "add %0,%0," #x "@tls" \ + : "=b" (__result) : \ + : "lr"); \ + __result; }) +# endif + +/* PowerPC32 Local Dynamic TLS access. */ +# 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" \ + "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; }) +# endif + +/* PowerPC32 General Dynamic TLS access. */ +# 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" \ + "addi 3,3," #x "@got@tlsgd\n\t" \ + "bl __tls_get_addr@plt" \ + : : \ + : __TLS_CALL_CLOBBERS); \ + __result; }) +# endif + +#elif defined __powerpc__ && defined __powerpc64__ + +/* PowerPC64 Local Exec TLS access. */ +# define TLS_LE(x) \ + ({ int * __result; \ + asm ( \ + " addis %0,13," #x "@tprel@ha\n" \ + " addi %0,%0," #x "@tprel@l\n" \ + : "=b" (__result) ); \ + __result; \ + }) +/* PowerPC64 Initial Exec TLS access. */ +# define TLS_IE(x) \ + ({ int * __result; \ + asm ( \ + " ld %0," #x "@got@tprel(2)\n" \ + " add %0,%0," #x "@tls\n" \ + : "=b" (__result) ); \ + __result; \ + }) +/* PowerPC64 Local Dynamic TLS access. */ +# define TLS_LD(x) \ + ({ int * __result; \ + asm ( \ + " addi 3,2," #x "@got@tlsld\n" \ + " bl .__tls_get_addr\n" \ + " nop \n" \ + " addis %0,3," #x "@dtprel@ha\n" \ + " addi %0,%0," #x "@dtprel@l\n" \ + : "=b" (__result) : \ + : "0", "3", "4", "5", "6", "7", \ + "8", "9", "10", "11", "12", \ + "lr", "ctr", \ + "cr0", "cr1", "cr5", "cr6", "cr7"); \ + __result; \ + }) +/* PowerPC64 General Dynamic TLS access. */ +# define TLS_GD(x) \ + ({ int * __result; \ + asm ( \ + " addi 3,2," #x "@got@tlsgd\n" \ + " bl .__tls_get_addr\n" \ + " nop \n" \ + " mr %0,3\n" \ + : "=b" (__result) : \ + : "0", "3", "4", "5", "6", "7", \ + "8", "9", "10", "11", "12", \ + "lr", "ctr", \ + "cr0", "cr1", "cr5", "cr6", "cr7"); \ + __result; \ + }) + +#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/libc/elf/tst-align.c b/libc/elf/tst-align.c new file mode 100644 index 000000000..e4d77763c --- /dev/null +++ b/libc/elf/tst-align.c @@ -0,0 +1,54 @@ +/* Copyright (C) 2003 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 <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +static int +do_test (void) +{ + static const char modname[] = "tst-alignmod.so"; + int result = 0; + void (*fp) (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); + } + + fp (&result); + + dlclose (h); + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/libc/elf/tst-align2.c b/libc/elf/tst-align2.c new file mode 100644 index 000000000..4fc0330e3 --- /dev/null +++ b/libc/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/libc/elf/tst-alignmod.c b/libc/elf/tst-alignmod.c new file mode 100644 index 000000000..d1305c1a6 --- /dev/null +++ b/libc/elf/tst-alignmod.c @@ -0,0 +1,53 @@ +/* Copyright (C) 2003 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 <stdio.h> +#include <tst-stack-align.h> + +static int res, *resp; + +static void __attribute__((constructor)) +con (void) +{ + res = TEST_STACK_ALIGN () ? -1 : 1; +} + +void +in_dso (int *result) +{ + if (!res) + { + puts ("constructor has not been run"); + *result = 1; + } + else if (res != 1) + { + puts ("constructor has been run without sufficient alignment"); + *result = 1; + } + + resp = result; +} + +static void __attribute__((destructor)) +des (void) +{ + if (TEST_STACK_ALIGN ()) + *resp = 1; +} diff --git a/libc/elf/tst-alignmod2.c b/libc/elf/tst-alignmod2.c new file mode 100644 index 000000000..21dcc535c --- /dev/null +++ b/libc/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/libc/elf/tst-array1-static.c b/libc/elf/tst-array1-static.c new file mode 100644 index 000000000..21539a421 --- /dev/null +++ b/libc/elf/tst-array1-static.c @@ -0,0 +1 @@ +#include "tst-array1.c" diff --git a/libc/elf/tst-array1.c b/libc/elf/tst-array1.c new file mode 100644 index 000000000..4d78db616 --- /dev/null +++ b/libc/elf/tst-array1.c @@ -0,0 +1,101 @@ +#include <unistd.h> + +static void init (void) __attribute__ ((constructor)); + +static void +init (void) +{ + write (STDOUT_FILENO, "init\n", 5); +} + +static void fini (void) __attribute__ ((destructor)); + +static void +fini (void) +{ + write (STDOUT_FILENO, "fini\n", 5); +} + +static void +preinit_0 (void) +{ + write (STDOUT_FILENO, "preinit array 0\n", 16); +} + +static void +preinit_1 (void) +{ + write (STDOUT_FILENO, "preinit array 1\n", 16); +} + +static void +preinit_2 (void) +{ + write (STDOUT_FILENO, "preinit array 2\n", 16); +} + +void (*const preinit_array []) (void) + __attribute__ ((section (".preinit_array"), aligned (sizeof (void *)))) = +{ + &preinit_0, + &preinit_1, + &preinit_2 +}; + +static void +init_0 (void) +{ + write (STDOUT_FILENO, "init array 0\n", 13); +} + +static void +init_1 (void) +{ + write (STDOUT_FILENO, "init array 1\n", 13); +} + +static void +init_2 (void) +{ + write (STDOUT_FILENO, "init array 2\n", 13); +} + +void (*const init_array []) (void) + __attribute__ ((section (".init_array"), aligned (sizeof (void *)))) = +{ + &init_0, + &init_1, + &init_2 +}; + +static void +fini_0 (void) +{ + write (STDOUT_FILENO, "fini array 0\n", 13); +} + +static void +fini_1 (void) +{ + write (STDOUT_FILENO, "fini array 1\n", 13); +} + +static void +fini_2 (void) +{ + write (STDOUT_FILENO, "fini array 2\n", 13); +} + +void (*const fini_array []) (void) + __attribute__ ((section (".fini_array"), aligned (sizeof (void *)))) = +{ + &fini_0, + &fini_1, + &fini_2 +}; + +int +main (void) +{ + return 0; +} diff --git a/libc/elf/tst-array1.exp b/libc/elf/tst-array1.exp new file mode 100644 index 000000000..cfcec9de0 --- /dev/null +++ b/libc/elf/tst-array1.exp @@ -0,0 +1,11 @@ +preinit array 0 +preinit array 1 +preinit array 2 +init +init array 0 +init array 1 +init array 2 +fini array 2 +fini array 1 +fini array 0 +fini diff --git a/libc/elf/tst-array2.c b/libc/elf/tst-array2.c new file mode 100644 index 000000000..21539a421 --- /dev/null +++ b/libc/elf/tst-array2.c @@ -0,0 +1 @@ +#include "tst-array1.c" diff --git a/libc/elf/tst-array2.exp b/libc/elf/tst-array2.exp new file mode 100644 index 000000000..ed203525b --- /dev/null +++ b/libc/elf/tst-array2.exp @@ -0,0 +1,19 @@ +preinit array 0 +preinit array 1 +preinit array 2 +DSO init +DSO init array 0 +DSO init array 1 +DSO init array 2 +init +init array 0 +init array 1 +init array 2 +fini array 2 +fini array 1 +fini array 0 +fini +DSO fini array 2 +DSO fini array 1 +DSO fini array 0 +DSO fini diff --git a/libc/elf/tst-array2dep.c b/libc/elf/tst-array2dep.c new file mode 100644 index 000000000..e1596b5b3 --- /dev/null +++ b/libc/elf/tst-array2dep.c @@ -0,0 +1,69 @@ +#include <unistd.h> + +static void init (void) __attribute__ ((constructor)); + +static void +init (void) +{ + write (STDOUT_FILENO, "DSO init\n", 9); +} + +static void fini (void) __attribute__ ((destructor)); + +static void +fini (void) +{ + write (STDOUT_FILENO, "DSO fini\n", 9); +} + +static void +init_0 (void) +{ + write (STDOUT_FILENO, "DSO init array 0\n", 17); +} + +static void +init_1 (void) +{ + write (STDOUT_FILENO, "DSO init array 1\n", 17); +} + +static void +init_2 (void) +{ + write (STDOUT_FILENO, "DSO init array 2\n", 17); +} + +void (*const init_array []) (void) + __attribute__ ((section (".init_array"), aligned (sizeof (void *)))) = +{ + &init_0, + &init_1, + &init_2 +}; + +static void +fini_0 (void) +{ + write (STDOUT_FILENO, "DSO fini array 0\n", 17); +} + +static void +fini_1 (void) +{ + write (STDOUT_FILENO, "DSO fini array 1\n", 17); +} + +static void +fini_2 (void) +{ + write (STDOUT_FILENO, "DSO fini array 2\n", 17); +} + +void (*const fini_array []) (void) + __attribute__ ((section (".fini_array"), aligned (sizeof (void *)))) = +{ + &fini_0, + &fini_1, + &fini_2 +}; diff --git a/libc/elf/tst-array3.c b/libc/elf/tst-array3.c new file mode 100644 index 000000000..21539a421 --- /dev/null +++ b/libc/elf/tst-array3.c @@ -0,0 +1 @@ +#include "tst-array1.c" diff --git a/libc/elf/tst-array4.c b/libc/elf/tst-array4.c new file mode 100644 index 000000000..ac3d4eb71 --- /dev/null +++ b/libc/elf/tst-array4.c @@ -0,0 +1,18 @@ +#include <dlfcn.h> + +#define main array1_main +#include "tst-array1.c" +#undef main + +int +main (void) +{ + void *handle = dlopen ("tst-array2dep.so", RTLD_LAZY); + + array1_main (); + + if (handle != NULL) + dlclose (handle); + + return 0; +} diff --git a/libc/elf/tst-array4.exp b/libc/elf/tst-array4.exp new file mode 100644 index 000000000..560444d2e --- /dev/null +++ b/libc/elf/tst-array4.exp @@ -0,0 +1,19 @@ +preinit array 0 +preinit array 1 +preinit array 2 +init +init array 0 +init array 1 +init array 2 +DSO init +DSO init array 0 +DSO init array 1 +DSO init array 2 +DSO fini array 2 +DSO fini array 1 +DSO fini array 0 +DSO fini +fini array 2 +fini array 1 +fini array 0 +fini diff --git a/libc/elf/tst-array5-static.c b/libc/elf/tst-array5-static.c new file mode 100644 index 000000000..4ef2aba3f --- /dev/null +++ b/libc/elf/tst-array5-static.c @@ -0,0 +1 @@ +#include "tst-array5.c" diff --git a/libc/elf/tst-array5-static.exp b/libc/elf/tst-array5-static.exp new file mode 100644 index 000000000..b1dc9e467 --- /dev/null +++ b/libc/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/libc/elf/tst-array5.c b/libc/elf/tst-array5.c new file mode 100644 index 000000000..03a566832 --- /dev/null +++ b/libc/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/libc/elf/tst-array5.exp b/libc/elf/tst-array5.exp new file mode 100644 index 000000000..28b490983 --- /dev/null +++ b/libc/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/libc/elf/tst-array5dep.c b/libc/elf/tst-array5dep.c new file mode 100644 index 000000000..570d282af --- /dev/null +++ b/libc/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/libc/elf/tst-audit1.c b/libc/elf/tst-audit1.c new file mode 100644 index 000000000..63656b4ee --- /dev/null +++ b/libc/elf/tst-audit1.c @@ -0,0 +1 @@ +#include "../io/pwd.c" diff --git a/libc/elf/tst-audit2.c b/libc/elf/tst-audit2.c new file mode 100644 index 000000000..fd089b6f6 --- /dev/null +++ b/libc/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/libc/elf/tst-auditmod1.c b/libc/elf/tst-auditmod1.c new file mode 100644 index 000000000..2d39df21e --- /dev/null +++ b/libc/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/libc/elf/tst-deep1.c b/libc/elf/tst-deep1.c new file mode 100644 index 000000000..5428d13de --- /dev/null +++ b/libc/elf/tst-deep1.c @@ -0,0 +1,36 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +xyzzy (void) +{ + printf ("%s:%s\n", __FILE__, __func__); + return 21; +} + +int +back (void) +{ + printf ("%s:%s\n", __FILE__, __func__); + return 1; +} + +extern int foo (void); + +static int +do_test (void) +{ + void *p = dlopen ("$ORIGIN/tst-deep1mod2.so", RTLD_LAZY|RTLD_DEEPBIND); + + int (*f) (void) = dlsym (p, "bar"); + if (f == NULL) + { + puts (dlerror ()); + return 1; + } + + return foo () + f (); +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/libc/elf/tst-deep1mod1.c b/libc/elf/tst-deep1mod1.c new file mode 100644 index 000000000..cc922e6ea --- /dev/null +++ b/libc/elf/tst-deep1mod1.c @@ -0,0 +1,14 @@ +#include <stdio.h> +int +foo (void) +{ + printf ("%s:%s\n", __FILE__, __func__); + return 1; +} + +int +baz (void) +{ + printf ("%s:%s\n", __FILE__, __func__); + return 20; +} diff --git a/libc/elf/tst-deep1mod2.c b/libc/elf/tst-deep1mod2.c new file mode 100644 index 000000000..b99caf032 --- /dev/null +++ b/libc/elf/tst-deep1mod2.c @@ -0,0 +1,16 @@ +#include <stdio.h> +extern int baz (void); +extern int xyzzy (void); +int +bar (void) +{ + printf ("%s:%s\n", __FILE__, __func__); + return baz () + xyzzy ();; +} + +int +back (void) +{ + printf ("%s:%s\n", __FILE__, __func__); + return -1; +} diff --git a/libc/elf/tst-deep1mod3.c b/libc/elf/tst-deep1mod3.c new file mode 100644 index 000000000..eee7d5c97 --- /dev/null +++ b/libc/elf/tst-deep1mod3.c @@ -0,0 +1,17 @@ +#include <stdio.h> + +extern int back (void); + +int +baz (void) +{ + printf ("%s:%s\n", __FILE__, __func__); + return back (); +} + +int +xyzzy (void) +{ + printf ("%s:%s\n", __FILE__, __func__); + return 0; +} diff --git a/libc/elf/tst-dlmodcount.c b/libc/elf/tst-dlmodcount.c new file mode 100644 index 000000000..781aad51d --- /dev/null +++ b/libc/elf/tst-dlmodcount.c @@ -0,0 +1,107 @@ +/* Copyright (C) 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by David Mosberger <davidm@hpl.hp.com>, 2004. + + 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 <link.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> + +#define SET 0 +#define ADD 1 +#define REMOVE 2 + +#define leq(l,r) (((r) - (l)) <= ~0ULL / 2) + +static int +callback (struct dl_phdr_info *info, size_t size, void *ptr) +{ + static int last_adds = 0, last_subs = 0; + intptr_t cmd = (intptr_t) ptr; + + printf (" size = %Zu\n", size); + if (size < (offsetof (struct dl_phdr_info, dlpi_subs) + + sizeof (info->dlpi_subs))) + { + fprintf (stderr, "dl_iterate_phdr failed to pass dlpi_adds/dlpi_subs\n"); + exit (5); + } + + printf (" dlpi_adds = %Lu dlpi_subs = %Lu\n", + info->dlpi_adds, info->dlpi_subs); + + switch (cmd) + { + case SET: + break; + + case ADD: + if (leq (info->dlpi_adds, last_adds)) + { + fprintf (stderr, "dlpi_adds failed to get incremented!\n"); + exit (3); + } + break; + + case REMOVE: + if (leq (info->dlpi_subs, last_subs)) + { + fprintf (stderr, "dlpi_subs failed to get incremented!\n"); + exit (4); + } + break; + } + last_adds = info->dlpi_adds; + last_subs = info->dlpi_subs; + return -1; +} + +static void * +load (const char *path) +{ + void *handle; + + printf ("loading `%s'\n", path); + handle = dlopen (path, RTLD_LAZY); + if (!handle) + exit (1); + dl_iterate_phdr (callback, (void *)(intptr_t) ADD); + return handle; +} + +static void +unload (const char *path, void *handle) +{ + printf ("unloading `%s'\n", path); + if (dlclose (handle) < 0) + exit (2); + dl_iterate_phdr (callback, (void *)(intptr_t) REMOVE); +} + +int +main (int argc, char **argv) +{ + void *handle1, *handle2; + + dl_iterate_phdr (callback, (void *)(intptr_t) SET); + handle1 = load ("firstobj.so"); + handle2 = load ("globalmod1.so"); + unload ("firstobj.so", handle1); + unload ("globalmod1.so", handle2); + return 0; +} diff --git a/libc/elf/tst-dlmopen1.c b/libc/elf/tst-dlmopen1.c new file mode 100644 index 000000000..9839267d8 --- /dev/null +++ b/libc/elf/tst-dlmopen1.c @@ -0,0 +1,80 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <gnu/lib-names.h> + + +static int +do_test (void) +{ + void *h = dlopen (LIBC_SO, RTLD_LAZY|RTLD_NOLOAD); + if (h == NULL) + { + printf ("cannot get handle for %s: %s\n", LIBC_SO, dlerror ()); + return 1; + } + + Lmid_t ns = -10; + if (dlinfo (h, RTLD_DI_LMID, &ns) != 0) + { + printf ("dlinfo for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + return 1; + } + + if (ns != LM_ID_BASE) + { + printf ("namespace for %s not LM_ID_BASE\n", LIBC_SO); + return 1; + } + + if (dlclose (h) != 0) + { + printf ("dlclose for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + return 1; + } + + h = dlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so", RTLD_LAZY); + if (h == NULL) + { + printf ("cannot get handle for %s: %s\n", + "tst-dlmopen1mod.so", dlerror ()); + return 1; + } + + ns = -10; + if (dlinfo (h, RTLD_DI_LMID, &ns) != 0) + { + printf ("dlinfo for %s in %s failed: %s\n", + "tst-dlmopen1mod.so", __func__, dlerror ()); + return 1; + } + + if (ns == LM_ID_BASE) + { + printf ("namespace for %s is LM_ID_BASE\n", LIBC_SO); + return 1; + } + + int (*fct) (Lmid_t) = dlsym (h, "foo"); + if (fct == NULL) + { + printf ("could not find %s: %s\n", "foo", dlerror ()); + return 1; + } + + if (fct (ns) != 0) + return 1; + + if (dlclose (h) != 0) + { + printf ("dlclose for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + return 1; + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/libc/elf/tst-dlmopen1mod.c b/libc/elf/tst-dlmopen1mod.c new file mode 100644 index 000000000..142488098 --- /dev/null +++ b/libc/elf/tst-dlmopen1mod.c @@ -0,0 +1,59 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <gnu/lib-names.h> + + +static int cnt; + +static void +__attribute ((constructor)) +constr (void) +{ + ++cnt; +} + + +int +foo (Lmid_t ns2) +{ + void *h = dlopen (LIBC_SO, RTLD_LAZY|RTLD_NOLOAD); + if (h == NULL) + { + printf ("cannot get handle for %s: %s\n", LIBC_SO, dlerror ()); + return 1; + } + + Lmid_t ns = -10; + if (dlinfo (h, RTLD_DI_LMID, &ns) != 0) + { + printf ("dlinfo for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + return 1; + } + + if (ns != ns2) + { + printf ("namespace for %s not LM_ID_BASE\n", LIBC_SO); + return 1; + } + + if (dlclose (h) != 0) + { + printf ("dlclose for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + return 1; + } + + if (cnt == 0) + { + puts ("constructor did not run"); + return 1; + } + else if (cnt != 1) + { + puts ("constructor did not run exactly once"); + return 1; + } + + return 0; +} diff --git a/libc/elf/tst-dlmopen2.c b/libc/elf/tst-dlmopen2.c new file mode 100644 index 000000000..056999725 --- /dev/null +++ b/libc/elf/tst-dlmopen2.c @@ -0,0 +1,70 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> +#include <gnu/lib-names.h> +#include <ldsodefs.h> + + +static int +do_test (void) +{ + int result = 0; + + for (int i = 1; i <= 10; ++i) + { + void *h[DL_NNS - 1]; + char used[DL_NNS]; + + printf ("round %d\n", i); + + memset (used, '\0', sizeof (used)); + used[LM_ID_BASE] = 1; + + for (int j = 0; j < DL_NNS - 1; ++j) + { + h[j] = dlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so", + RTLD_LAZY); + if (h[j] == NULL) + { + printf ("round %d, namespace %d: load failed: %s\n", + i, j, dlerror ()); + return 1; + } + Lmid_t ns; + if (dlinfo (h[j], RTLD_DI_LMID, &ns) != 0) + { + printf ("round %d, namespace %d: dlinfo failed: %s\n", + i, j, dlerror ()); + return 1; + } + if (ns < 0 || ns >= DL_NNS) + { + printf ("round %d, namespace %d: invalid namespace %ld", + i, j, (long int) ns); + result = 1; + } + else if (used[ns] != 0) + { + printf ("\ +round %d, namespace %d: duplicate allocate of namespace %ld", + i, j, (long int) ns); + result = 1; + } + else + used[ns] = 1; + } + + for (int j = 0; j < DL_NNS - 1; ++j) + if (dlclose (h[j]) != 0) + { + printf ("round %d, namespace %d: close failed: %s\n", + i, j, dlerror ()); + return 1; + } + } + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/libc/elf/tst-dlmopen3.c b/libc/elf/tst-dlmopen3.c new file mode 100644 index 000000000..26c86b2dc --- /dev/null +++ b/libc/elf/tst-dlmopen3.c @@ -0,0 +1,22 @@ +#include <dlfcn.h> +#include <stdio.h> + + +static int +do_test (void) +{ + void *h = dlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so", RTLD_LAZY); + if (h == NULL) + { + printf ("cannot get handle for %s: %s\n", + "tst-dlmopen1mod.so", dlerror ()); + return 1; + } + + /* Do not unload. */ + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/libc/elf/tst-dlopenrpath.c b/libc/elf/tst-dlopenrpath.c new file mode 100644 index 000000000..bd4d888f3 --- /dev/null +++ b/libc/elf/tst-dlopenrpath.c @@ -0,0 +1,72 @@ +/* Copyright (C) 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2004. + + 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 <dlfcn.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> + + +extern int foo (void); + +static const char testsubdir[] = PFX "test-subdir"; + + +static int +do_test (void) +{ + struct stat64 st; + int result = 1; + + if (mkdir (testsubdir, 0777) != 0 + && (errno != EEXIST + || stat64 (testsubdir, &st) != 0 + || !S_ISDIR (st.st_mode))) + { + printf ("cannot create directory %s\n", testsubdir); + return 1; + } + + if (system ("cp " PFX "firstobj.so " PFX "test-subdir/in-subdir.so") != 0) + { + puts ("cannot copy DSO"); + return 1; + } + + void *p = dlopen ("in-subdir.so", RTLD_LAZY|RTLD_LOCAL); + if (p != NULL) + { + puts ("succeeded in opening in-subdir.so from do_test"); + dlclose (p); + goto out; + } + + result = foo (); + + out: + unlink (PFX "test-subdir/in-subdir.so"); + rmdir (testsubdir); + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/libc/elf/tst-dlopenrpathmod.c b/libc/elf/tst-dlopenrpathmod.c new file mode 100644 index 000000000..8fe7873c9 --- /dev/null +++ b/libc/elf/tst-dlopenrpathmod.c @@ -0,0 +1,36 @@ +/* Copyright (C) 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2004. + + 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 <dlfcn.h> +#include <stdio.h> + + +int +foo (void) +{ + void *p = dlopen ("in-subdir.so", RTLD_LAZY|RTLD_LOCAL); + if (p != NULL) + { + dlclose (p); + return 0; + } + + puts ("couldn't open in-subdir.so from foo"); + return 1; +} diff --git a/libc/elf/tst-execstack-mod.c b/libc/elf/tst-execstack-mod.c new file mode 100644 index 000000000..038e6550b --- /dev/null +++ b/libc/elf/tst-execstack-mod.c @@ -0,0 +1,30 @@ +/* Test module for making nonexecutable stacks executable + on load of a DSO that requires executable stacks. */ + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +void callme (void (*callback) (void)); + +/* This is a function that makes use of executable stack by + using a local function trampoline. */ +void +tryme (void) +{ + bool ok = false; + void callback (void) { ok = true; } + + callme (&callback); + + if (ok) + printf ("DSO called ok (local %p, trampoline %p)\n", &ok, &callback); + else + abort (); +} + +void +callme (void (*callback) (void)) +{ + (*callback) (); +} diff --git a/libc/elf/tst-execstack-needed.c b/libc/elf/tst-execstack-needed.c new file mode 100644 index 000000000..03090f7dd --- /dev/null +++ b/libc/elf/tst-execstack-needed.c @@ -0,0 +1,36 @@ +/* Test program for making nonexecutable stacks executable + on DT_NEEDED load of a DSO that requires executable stacks. */ + +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <error.h> + +extern void tryme (void); /* from tst-execstack-mod.so */ + +static void deeper (void (*f) (void)); + +static int +do_test (void) +{ + tryme (); + + /* Test that growing the stack region gets new executable pages too. */ + deeper (&tryme); + + return 0; +} + +static void +deeper (void (*f) (void)) +{ + char stack[1100 * 1024]; + memfrob (stack, sizeof stack); + (*f) (); + memfrob (stack, sizeof stack); +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/libc/elf/tst-execstack-prog.c b/libc/elf/tst-execstack-prog.c new file mode 100644 index 000000000..5a66d63ca --- /dev/null +++ b/libc/elf/tst-execstack-prog.c @@ -0,0 +1,35 @@ +/* Test program for executable stacks in an executable itself. */ + +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <error.h> + +#include "tst-execstack-mod.c" /* This defines the `tryme' test function. */ + +static void deeper (void (*f) (void)); + +static int +do_test (void) +{ + tryme (); + + /* Test that growing the stack region gets new executable pages too. */ + deeper (&tryme); + + return 0; +} + +static void +deeper (void (*f) (void)) +{ + char stack[1100 * 1024]; + memfrob (stack, sizeof stack); + (*f) (); + memfrob (stack, sizeof stack); +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/libc/elf/tst-execstack.c b/libc/elf/tst-execstack.c new file mode 100644 index 000000000..4b0661545 --- /dev/null +++ b/libc/elf/tst-execstack.c @@ -0,0 +1,133 @@ +/* Test program for making nonexecutable stacks executable + on load of a DSO that requires executable stacks. */ + +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <error.h> + +static void +print_maps (void) +{ +#if 0 + char *cmd = NULL; + asprintf (&cmd, "cat /proc/%d/maps", getpid ()); + system (cmd); + free (cmd); +#endif +} + +static void deeper (void (*f) (void)); + +#if USE_PTHREADS +# include <pthread.h> + +static void * +tryme_thread (void *f) +{ + (*((void (*) (void)) f)) (); + + return 0; +} + +static pthread_barrier_t startup_barrier, go_barrier; +static void * +waiter_thread (void *arg) +{ + void **f = arg; + pthread_barrier_wait (&startup_barrier); + pthread_barrier_wait (&go_barrier); + + (*((void (*) (void)) *f)) (); + + return 0; +} +#endif + +static int +do_test (void) +{ + static void *f; /* Address of this is used in other threads. */ + +#if USE_PTHREADS + /* Create some threads while stacks are nonexecutable. */ + #define N 5 + pthread_t thr[N]; + + pthread_barrier_init (&startup_barrier, NULL, N + 1); + pthread_barrier_init (&go_barrier, NULL, N + 1); + + for (int i = 0; i < N; ++i) + { + int rc = pthread_create (&thr[i], NULL, &waiter_thread, &f); + if (rc) + error (1, rc, "pthread_create"); + } + + /* Make sure they are all there using their stacks. */ + pthread_barrier_wait (&startup_barrier); + puts ("threads waiting"); +#endif + + print_maps (); + + /* Loading this module should force stacks to become executable. */ + void *h = dlopen ("tst-execstack-mod.so", RTLD_LAZY); + if (h == NULL) + { + printf ("cannot load: %s\n", dlerror ()); + return 1; + } + + f = dlsym (h, "tryme"); + if (f == NULL) + { + printf ("symbol not found: %s\n", dlerror ()); + return 1; + } + + /* Test if that really made our stack executable. + The `tryme' function should crash if not. */ + + (*((void (*) (void)) f)) (); + + print_maps (); + + /* Test that growing the stack region gets new executable pages too. */ + deeper ((void (*) (void)) f); + + print_maps (); + +#if USE_PTHREADS + /* Test that a fresh thread now gets an executable stack. */ + { + pthread_t th; + int rc = pthread_create (&th, NULL, &tryme_thread, f); + if (rc) + error (1, rc, "pthread_create"); + } + + puts ("threads go"); + /* The existing threads' stacks should have been changed. + Let them run to test it. */ + pthread_barrier_wait (&go_barrier); + + pthread_exit (0); +#endif + + return 0; +} + +static void +deeper (void (*f) (void)) +{ + char stack[1100 * 1024]; + memfrob (stack, sizeof stack); + (*f) (); + memfrob (stack, sizeof stack); +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/libc/elf/tst-global1.c b/libc/elf/tst-global1.c new file mode 100644 index 000000000..1611b51b6 --- /dev/null +++ b/libc/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/libc/elf/tst-leaks1.c b/libc/elf/tst-leaks1.c new file mode 100644 index 000000000..36e4aee9c --- /dev/null +++ b/libc/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/libc/elf/tst-pathopt.c b/libc/elf/tst-pathopt.c new file mode 100644 index 000000000..1f7aac2a4 --- /dev/null +++ b/libc/elf/tst-pathopt.c @@ -0,0 +1,39 @@ +#include <dlfcn.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + + +int +main (void) +{ + void *h; + int (*fp) (int); + int result; + + mtrace (); + + h = dlopen ("renamed.so", RTLD_LAZY); + if (h == NULL) + { + printf ("failed to load \"%s\": %s\n", "renamed.so", dlerror ()); + exit (1); + } + + fp = dlsym (h, "in_renamed"); + if (fp == NULL) + { + printf ("lookup of \"%s\" failed: %s\n", "in_renamed", dlerror ()); + exit (1); + } + + result = fp (10); + + if (dlclose (h) != 0) + { + printf ("failed to close \"%s\": %s\n", "renamed.so", dlerror ()); + exit (1); + } + + return result; +} diff --git a/libc/elf/tst-pathopt.sh b/libc/elf/tst-pathopt.sh new file mode 100755 index 000000000..0fe504e1a --- /dev/null +++ b/libc/elf/tst-pathopt.sh @@ -0,0 +1,37 @@ +#! /bin/sh +# Test lookup path optimization. +# Copyright (C) 2000 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. + +common_objpfx=$1 +run_program_prefix=$2 + +test -e ${common_objpfx}elf/will-be-empty && + rm -fr ${common_objpfx}elf/will-be-empty +test -d ${common_objpfx}elf/for-renamed || + mkdir ${common_objpfx}elf/for-renamed + +cp ${common_objpfx}elf/pathoptobj.so ${common_objpfx}elf/for-renamed/renamed.so + +LOCPATH=${common_objpfx}localedata GCONV_PATH=${common_objpfx}iconvdata \ +LC_ALL=C LD_LIBRARY_PATH=${common_objpfx}elf/will-be-empty:${common_objpfx}elf/for-renamed:${common_objpfx}.:${common_objpfx}dlfcn \ + ${common_objpfx}elf/ld.so ${common_objpfx}elf/tst-pathopt \ + > ${common_objpfx}elf/tst-pathopt.out + +exit $? diff --git a/libc/elf/tst-pie1.c b/libc/elf/tst-pie1.c new file mode 100644 index 000000000..75d941f21 --- /dev/null +++ b/libc/elf/tst-pie1.c @@ -0,0 +1,5 @@ +int +foo (void) +{ + return 34; +} diff --git a/libc/elf/tst-piemod1.c b/libc/elf/tst-piemod1.c new file mode 100644 index 000000000..ad439da80 --- /dev/null +++ b/libc/elf/tst-piemod1.c @@ -0,0 +1,20 @@ +#include <stdio.h> + +int +foo (void) +{ + return 21; +} + +int +main (void) +{ + int val = foo (); + if (val != 34) + { + printf ("foo () returned %d\n", val); + return 1; + } + + return 0; +} diff --git a/libc/elf/tst-stackguard1-static.c b/libc/elf/tst-stackguard1-static.c new file mode 100644 index 000000000..db1e21554 --- /dev/null +++ b/libc/elf/tst-stackguard1-static.c @@ -0,0 +1 @@ +#include "tst-stackguard1.c" diff --git a/libc/elf/tst-stackguard1.c b/libc/elf/tst-stackguard1.c new file mode 100644 index 000000000..480f9297d --- /dev/null +++ b/libc/elf/tst-stackguard1.c @@ -0,0 +1,196 @@ +/* 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; + 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++; + } + + printf ("differences %d defaults %d\n", ndifferences, ndefaults); + + if (ndifferences < N / 2 && ndefaults < N / 2) + { + 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/libc/elf/tst-tls-dlinfo.c b/libc/elf/tst-tls-dlinfo.c new file mode 100644 index 000000000..e97b5081f --- /dev/null +++ b/libc/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/libc/elf/tst-tls1-static.c b/libc/elf/tst-tls1-static.c new file mode 100644 index 000000000..a01008073 --- /dev/null +++ b/libc/elf/tst-tls1-static.c @@ -0,0 +1 @@ +#include "tst-tls1.c" diff --git a/libc/elf/tst-tls1.c b/libc/elf/tst-tls1.c new file mode 100644 index 000000000..478f5bbdc --- /dev/null +++ b/libc/elf/tst-tls1.c @@ -0,0 +1,91 @@ +/* glibc test for TLS in ld.so. */ +#include <stdio.h> + +#include <tls.h> + +#ifdef USE_TLS +# include "tls-macros.h" + + +/* Two common 'int' variables in TLS. */ +COMMON_INT_DEF(foo); +COMMON_INT_DEF(bar); +#endif + + +#define TEST_FUNCTION do_test () +static int +do_test (void) +{ +#ifdef USE_TLS + int result = 0; + int *ap, *bp; + + + /* Set the variable using the local exec model. */ + puts ("set bar to 1 (LE)"); + ap = TLS_LE (bar); + *ap = 1; + + + /* Get variables using initial exec model. */ + fputs ("get sum of foo and bar (IE)", stdout); + ap = TLS_IE (foo); + bp = TLS_IE (bar); + printf (" = %d\n", *ap + *bp); + result |= *ap + *bp != 1; + if (*ap != 0) + { + printf ("foo = %d\n", *ap); + result = 1; + } + if (*bp != 1) + { + printf ("bar = %d\n", *bp); + result = 1; + } + + + /* Get variables using local dynamic model. */ + fputs ("get sum of foo and bar (LD)", stdout); + ap = TLS_LD (foo); + bp = TLS_LD (bar); + printf (" = %d\n", *ap + *bp); + result |= *ap + *bp != 1; + if (*ap != 0) + { + printf ("foo = %d\n", *ap); + result = 1; + } + if (*bp != 1) + { + printf ("bar = %d\n", *bp); + result = 1; + } + + + /* Get variables using generic dynamic model. */ + fputs ("get sum of foo and bar (GD)", stdout); + ap = TLS_GD (foo); + bp = TLS_GD (bar); + printf (" = %d\n", *ap + *bp); + result |= *ap + *bp != 1; + if (*ap != 0) + { + printf ("foo = %d\n", *ap); + result = 1; + } + if (*bp != 1) + { + printf ("bar = %d\n", *bp); + result = 1; + } + + return result; +#else + return 0; +#endif +} + + +#include "../test-skeleton.c" diff --git a/libc/elf/tst-tls10.c b/libc/elf/tst-tls10.c new file mode 100644 index 000000000..dbcc69727 --- /dev/null +++ b/libc/elf/tst-tls10.c @@ -0,0 +1,40 @@ +#include "tst-tls10.h" + +#ifdef USE_TLS__THREAD +__thread int dummy __attribute__((visibility ("hidden"))) = 12; +__thread struct A local = { 1, 2, 3 }; +#endif + +#define CHECK(N, S) \ + p = f##N##a (); \ + if (p->a != S || p->b != S + 1 || p->c != S + 2) \ + abort () + +int +main (void) +{ +#ifdef USE_TLS__THREAD + struct A *p; + if (local.a != 1 || local.b != 2 || local.c != 3) + abort (); + if (a1.a != 4 || a1.b != 5 || a1.c != 6) + abort (); + if (a2.a != 22 || a2.b != 23 || a2.c != 24) + abort (); + if (a3.a != 10 || a3.b != 11 || a3.c != 12) + abort (); + if (a4.a != 25 || a4.b != 26 || a4.c != 27) + abort (); + check1 (); + check2 (); + if (f1a () != &a1 || f2a () != &a2 || f3a () != &a3 || f4a () != &a4) + abort (); + CHECK (5, 16); + CHECK (6, 19); + if (f7a () != &a2 || f8a () != &a4) + abort (); + CHECK (9, 28); + CHECK (10, 31); +#endif + exit (0); +} diff --git a/libc/elf/tst-tls10.h b/libc/elf/tst-tls10.h new file mode 100644 index 000000000..1be6adc29 --- /dev/null +++ b/libc/elf/tst-tls10.h @@ -0,0 +1,38 @@ +#include <tls.h> +#include <stdlib.h> + +#if defined USE_TLS && defined HAVE___THREAD \ + && defined HAVE_TLS_MODEL_ATTRIBUTE +# define USE_TLS__THREAD + +struct A +{ + char a; + int b; + long long c; +}; + +extern __thread struct A a1, a2, a3, a4; +extern struct A *f1a (void); +extern struct A *f2a (void); +extern struct A *f3a (void); +extern struct A *f4a (void); +extern struct A *f5a (void); +extern struct A *f6a (void); +extern struct A *f7a (void); +extern struct A *f8a (void); +extern struct A *f9a (void); +extern struct A *f10a (void); +extern int f1b (void); +extern int f2b (void); +extern int f3b (void); +extern int f4b (void); +extern int f5b (void); +extern int f6b (void); +extern int f7b (void); +extern int f8b (void); +extern int f9b (void); +extern int f10b (void); +extern void check1 (void); +extern void check2 (void); +#endif diff --git a/libc/elf/tst-tls11.c b/libc/elf/tst-tls11.c new file mode 100644 index 000000000..816cf5cc2 --- /dev/null +++ b/libc/elf/tst-tls11.c @@ -0,0 +1,27 @@ +#include "tst-tls10.h" + +#define CHECK(N, S) \ + p = f##N##a (); \ + if (p->a != S || p->b != S + 1 || p->c != S + 2) \ + abort () + +int +main (void) +{ +#ifdef USE_TLS__THREAD + struct A *p; + check1 (); + check2 (); + CHECK (1, 4); + CHECK (2, 22); + CHECK (3, 10); + CHECK (4, 25); + CHECK (5, 16); + CHECK (6, 19); + CHECK (7, 22); + CHECK (8, 25); + CHECK (9, 28); + CHECK (10, 31); +#endif + exit (0); +} diff --git a/libc/elf/tst-tls12.c b/libc/elf/tst-tls12.c new file mode 100644 index 000000000..84aa7d35c --- /dev/null +++ b/libc/elf/tst-tls12.c @@ -0,0 +1,18 @@ +#include "tst-tls10.h" + +#define CHECK(N, S) \ + p = &a##N; \ + if (p->a != S || p->b != S + 1 || p->c != S + 2) \ + abort () + +int +main (void) +{ +#ifdef USE_TLS__THREAD + struct A *p; + check1 (); + CHECK (1, 4); + CHECK (2, 7); +#endif + exit (0); +} diff --git a/libc/elf/tst-tls13.c b/libc/elf/tst-tls13.c new file mode 100644 index 000000000..06bfbacb5 --- /dev/null +++ b/libc/elf/tst-tls13.c @@ -0,0 +1,30 @@ +/* Check unloading modules with data in static TLS block. */ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + + +static int +do_test (void) +{ + for (int i = 0; i < 1000;) + { + printf ("round %d\n",++i); + + void *h = dlopen ("$ORIGIN/tst-tlsmod13a.so", RTLD_LAZY); + if (h == NULL) + { + printf ("cannot load: %s\n", dlerror ()); + exit (1); + } + + dlclose (h); + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#define TIMEOUT 3 +#include "../test-skeleton.c" diff --git a/libc/elf/tst-tls14.c b/libc/elf/tst-tls14.c new file mode 100644 index 000000000..428fd5293 --- /dev/null +++ b/libc/elf/tst-tls14.c @@ -0,0 +1,66 @@ +/* Check alignment of TLS variable. */ +#include <dlfcn.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <tls.h> + +#if USE_TLS && HAVE___THREAD + +#define AL 4096 +struct foo +{ + int i; +} __attribute ((aligned (AL))); + +static __thread struct foo f; +static struct foo g; + + +extern int in_dso1 (void); + + +static int +do_test (void) +{ + int result = 0; + + int fail = (((uintptr_t) &f) & (AL - 1)) != 0; + printf ("&f = %p %s\n", &f, fail ? "FAIL" : "OK"); + result |= fail; + + fail = (((uintptr_t) &g) & (AL - 1)) != 0; + printf ("&g = %p %s\n", &g, fail ? "FAIL" : "OK"); + result |= fail; + + result |= in_dso1 (); + + void *h = dlopen ("tst-tlsmod14b.so", RTLD_LAZY); + if (h == NULL) + { + printf ("cannot open tst-tlsmod14b.so: %m\n"); + exit (1); + } + + int (*fp) (void) = (int (*) (void)) dlsym (h, "in_dso2"); + if (fp == NULL) + { + puts ("cannot find in_dso2"); + exit (1); + } + + result |= fp (); + + return result; +} + +#define TEST_FUNCTION do_test () + +#else + +#define TEST_FUNCTION 0 + +#endif + +#include "../test-skeleton.c" diff --git a/libc/elf/tst-tls15.c b/libc/elf/tst-tls15.c new file mode 100644 index 000000000..7ac963aa2 --- /dev/null +++ b/libc/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/libc/elf/tst-tls2-static.c b/libc/elf/tst-tls2-static.c new file mode 100644 index 000000000..55ffa5744 --- /dev/null +++ b/libc/elf/tst-tls2-static.c @@ -0,0 +1 @@ +#include "tst-tls2.c" diff --git a/libc/elf/tst-tls2.c b/libc/elf/tst-tls2.c new file mode 100644 index 000000000..417489968 --- /dev/null +++ b/libc/elf/tst-tls2.c @@ -0,0 +1,91 @@ +/* glibc test for TLS in ld.so. */ +#include <stdio.h> + +#include <tls.h> + +#ifdef USE_TLS +# include "tls-macros.h" + + +/* Two 'int' variables in TLS. */ +VAR_INT_DEF(foo); +VAR_INT_DEF(bar); +#endif + + +#define TEST_FUNCTION do_test () +static int +do_test (void) +{ +#ifdef USE_TLS + int result = 0; + int *ap, *bp; + + + /* Set the variable using the local exec model. */ + puts ("set bar to 1 (LE)"); + ap = TLS_LE (bar); + *ap = 1; + + + /* Get variables using initial exec model. */ + fputs ("get sum of foo and bar (IE)", stdout); + ap = TLS_IE (foo); + bp = TLS_IE (bar); + printf (" = %d\n", *ap + *bp); + result |= *ap + *bp != 1; + if (*ap != 0) + { + printf ("foo = %d\n", *ap); + result = 1; + } + if (*bp != 1) + { + printf ("bar = %d\n", *bp); + result = 1; + } + + + /* Get variables using local dynamic model. */ + fputs ("get sum of foo and bar (LD)", stdout); + ap = TLS_LD (foo); + bp = TLS_LD (bar); + printf (" = %d\n", *ap + *bp); + result |= *ap + *bp != 1; + if (*ap != 0) + { + printf ("foo = %d\n", *ap); + result = 1; + } + if (*bp != 1) + { + printf ("bar = %d\n", *bp); + result = 1; + } + + + /* Get variables using generic dynamic model. */ + fputs ("get sum of foo and bar (GD)", stdout); + ap = TLS_GD (foo); + bp = TLS_GD (bar); + printf (" = %d\n", *ap + *bp); + result |= *ap + *bp != 1; + if (*ap != 0) + { + printf ("foo = %d\n", *ap); + result = 1; + } + if (*bp != 1) + { + printf ("bar = %d\n", *bp); + result = 1; + } + + return result; +#else + return 0; +#endif +} + + +#include "../test-skeleton.c" diff --git a/libc/elf/tst-tls3.c b/libc/elf/tst-tls3.c new file mode 100644 index 000000000..84be43575 --- /dev/null +++ b/libc/elf/tst-tls3.c @@ -0,0 +1,76 @@ +/* glibc test for TLS in ld.so. */ +#include <stdio.h> + +#include <tls.h> + +#ifdef USE_TLS +# include "tls-macros.h" + + +/* One define int variable, two externs. */ +COMMON_INT_DECL(foo); +VAR_INT_DECL(bar); +VAR_INT_DEF(baz); +#endif + + +extern int in_dso (void); + + +#define TEST_FUNCTION do_test () +static int +do_test (void) +{ +#ifdef USE_TLS + int result = 0; + int *ap, *bp, *cp; + + + /* Set the variable using the local exec model. */ + puts ("set baz to 3 (LE)"); + ap = TLS_LE (baz); + *ap = 3; + + + /* Get variables using initial exec model. */ + puts ("set variables foo and bar (IE)"); + ap = TLS_IE (foo); + *ap = 1; + bp = TLS_IE (bar); + *bp = 2; + + + /* Get variables using local dynamic model. */ + fputs ("get sum of foo, bar (GD) and baz (LD)", stdout); + ap = TLS_GD (foo); + bp = TLS_GD (bar); + cp = TLS_LD (baz); + printf (" = %d\n", *ap + *bp + *cp); + result |= *ap + *bp + *cp != 6; + if (*ap != 1) + { + printf ("foo = %d\n", *ap); + result = 1; + } + if (*bp != 2) + { + printf ("bar = %d\n", *bp); + result = 1; + } + if (*cp != 3) + { + printf ("baz = %d\n", *cp); + result = 1; + } + + + result |= in_dso (); + + return result; +#else + return 0; +#endif +} + + +#include "../test-skeleton.c" diff --git a/libc/elf/tst-tls4.c b/libc/elf/tst-tls4.c new file mode 100644 index 000000000..f92ee53ce --- /dev/null +++ b/libc/elf/tst-tls4.c @@ -0,0 +1,56 @@ +#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); + } + + 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; + } + + dlclose (h); + + return result; +#else + return 0; +#endif +} + + +#include "../test-skeleton.c" diff --git a/libc/elf/tst-tls5.c b/libc/elf/tst-tls5.c new file mode 100644 index 000000000..a571d2cd3 --- /dev/null +++ b/libc/elf/tst-tls5.c @@ -0,0 +1,72 @@ +#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 *foop2; + int (*fp) (int, int *); + void *h; + + h = dlopen (modname, RTLD_LAZY); + if (h == NULL) + { + printf ("cannot open '%s': %s\n", modname, dlerror ()); + exit (1); + } + + foop = dlsym (h, "foo"); + if (foop == NULL) + { + printf ("cannot get symbol 'foo': %s\n", dlerror ()); + exit (1); + } + + *foop = 42; + + fp = dlsym (h, "in_dso"); + if (fp == NULL) + { + printf ("cannot get symbol 'in_dso': %s\n", dlerror ()); + exit (1); + } + + result |= fp (42, foop); + + foop2 = dlsym (h, "foo"); + if (foop2 == NULL) + { + printf ("cannot get symbol 'foo' the second time: %s\n", dlerror ()); + exit (1); + } + + if (foop != foop2) + { + puts ("address of 'foo' different the second time"); + result = 1; + } + else if (*foop != 16) + { + puts ("foo != 16"); + result = 1; + } + + dlclose (h); + + return result; +#else + return 0; +#endif +} + + +#include "../test-skeleton.c" diff --git a/libc/elf/tst-tls6.c b/libc/elf/tst-tls6.c new file mode 100644 index 000000000..68d706538 --- /dev/null +++ b/libc/elf/tst-tls6.c @@ -0,0 +1,90 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <link.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 *foop2; + int (*fp) (int, int *); + void *h; + int i; + int modid = -1; + + for (i = 0; i < 10; ++i) + { + h = dlopen (modname, RTLD_LAZY); + if (h == NULL) + { + printf ("cannot open '%s': %s\n", modname, dlerror ()); + exit (1); + } + + /* Dirty test code here: we peek into a private data structure. + We make sure that the module gets assigned the same ID every + time. The value of the first round is used. */ + if (modid == -1) + modid = ((struct link_map *) h)->l_tls_modid; + else if (((struct link_map *) h)->l_tls_modid != modid) + { + printf ("round %d: modid now %zd, initially %d\n", + i, ((struct link_map *) h)->l_tls_modid, modid); + result = 1; + } + + foop = dlsym (h, "foo"); + if (foop == NULL) + { + printf ("cannot get symbol 'foo': %s\n", dlerror ()); + exit (1); + } + + *foop = 42 + i; + + fp = dlsym (h, "in_dso"); + if (fp == NULL) + { + printf ("cannot get symbol 'in_dso': %s\n", dlerror ()); + exit (1); + } + + result |= fp (42 + i, foop); + + foop2 = dlsym (h, "foo"); + if (foop2 == NULL) + { + printf ("cannot get symbol 'foo' the second time: %s\n", dlerror ()); + exit (1); + } + + if (foop != foop2) + { + puts ("address of 'foo' different the second time"); + result = 1; + } + else if (*foop != 16) + { + puts ("foo != 16"); + result = 1; + } + + dlclose (h); + } + + return result; +#else + return 0; +#endif +} + + +#include "../test-skeleton.c" diff --git a/libc/elf/tst-tls7.c b/libc/elf/tst-tls7.c new file mode 100644 index 000000000..37f1a63e1 --- /dev/null +++ b/libc/elf/tst-tls7.c @@ -0,0 +1,61 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <link.h> +#include <tls.h> + + +#define TEST_FUNCTION do_test () +static int +do_test (void) +{ +#ifdef USE_TLS + static const char modname[] = "tst-tlsmod3.so"; + int result = 0; + int (*fp) (void); + void *h; + int i; + int modid = -1; + + for (i = 0; i < 10; ++i) + { + h = dlopen (modname, RTLD_LAZY); + if (h == NULL) + { + printf ("cannot open '%s': %s\n", modname, dlerror ()); + exit (1); + } + + /* Dirty test code here: we peek into a private data structure. + We make sure that the module gets assigned the same ID every + time. The value of the first round is used. */ + if (modid == -1) + modid = ((struct link_map *) h)->l_tls_modid; + else if (((struct link_map *) h)->l_tls_modid != (size_t) modid) + { + printf ("round %d: modid now %zu, initially %d\n", + i, ((struct link_map *) h)->l_tls_modid, modid); + result = 1; + } + + fp = dlsym (h, "in_dso2"); + if (fp == NULL) + { + printf ("cannot get symbol 'in_dso2': %s\n", dlerror ()); + exit (1); + } + + result |= fp (); + + dlclose (h); + } + + return result; +#else + return 0; +#endif +} + + +#include "../test-skeleton.c" diff --git a/libc/elf/tst-tls8.c b/libc/elf/tst-tls8.c new file mode 100644 index 000000000..ccc4e9f6f --- /dev/null +++ b/libc/elf/tst-tls8.c @@ -0,0 +1,174 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <link.h> +#include <tls.h> + + +#define TEST_FUNCTION do_test () +static int +do_test (void) +{ +#ifdef USE_TLS + 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 *); + void *h1; + void *h2; + int i; + size_t modid1 = (size_t) -1; + size_t modid2 = (size_t) -1; + int *bazp; + + for (i = 0; i < 10; ++i) + { + h1 = dlopen (modname1, RTLD_LAZY); + if (h1 == NULL) + { + printf ("cannot open '%s': %s\n", modname1, dlerror ()); + exit (1); + } + + /* Dirty test code here: we peek into a private data structure. + We make sure that the module gets assigned the same ID every + time. The value of the first round is used. */ + if (modid1 == (size_t) -1) + modid1 = ((struct link_map *) h1)->l_tls_modid; + else if (((struct link_map *) h1)->l_tls_modid != modid1) + { + printf ("round %d: modid now %zd, initially %zd\n", + i, ((struct link_map *) h1)->l_tls_modid, modid1); + result = 1; + } + + fp1 = dlsym (h1, "in_dso2"); + if (fp1 == NULL) + { + printf ("cannot get symbol 'in_dso2' in %s\n", modname1); + exit (1); + } + + result |= fp1 (); + + + + h2 = dlopen (modname2, RTLD_LAZY); + if (h2 == NULL) + { + printf ("cannot open '%s': %s\n", modname2, dlerror ()); + exit (1); + } + + /* Dirty test code here: we peek into a private data structure. + We make sure that the module gets assigned the same ID every + time. The value of the first round is used. */ + if (modid2 == (size_t) -1) + modid2 = ((struct link_map *) h1)->l_tls_modid; + else if (((struct link_map *) h1)->l_tls_modid != modid2) + { + printf ("round %d: modid now %zd, initially %zd\n", + i, ((struct link_map *) h1)->l_tls_modid, modid2); + result = 1; + } + + bazp = dlsym (h2, "baz"); + if (bazp == NULL) + { + printf ("cannot get symbol 'baz' in %s\n", modname2); + exit (1); + } + + *bazp = 42 + i; + + fp2 = dlsym (h2, "in_dso"); + if (fp2 == NULL) + { + printf ("cannot get symbol 'in_dso' in %s\n", modname2); + exit (1); + } + + result |= fp2 (42 + i, bazp); + + dlclose (h1); + dlclose (h2); + + + h1 = dlopen (modname1, RTLD_LAZY); + if (h1 == NULL) + { + printf ("cannot open '%s': %s\n", modname1, dlerror ()); + exit (1); + } + + /* Dirty test code here: we peek into a private data structure. + We make sure that the module gets assigned the same ID every + time. The value of the first round is used. */ + if (((struct link_map *) h1)->l_tls_modid != modid1) + { + printf ("round %d: modid now %zd, initially %zd\n", + i, ((struct link_map *) h1)->l_tls_modid, modid1); + result = 1; + } + + fp1 = dlsym (h1, "in_dso2"); + if (fp1 == NULL) + { + printf ("cannot get symbol 'in_dso2' in %s\n", modname1); + exit (1); + } + + result |= fp1 (); + + + + h2 = dlopen (modname2, RTLD_LAZY); + if (h2 == NULL) + { + printf ("cannot open '%s': %s\n", modname2, dlerror ()); + exit (1); + } + + /* Dirty test code here: we peek into a private data structure. + We make sure that the module gets assigned the same ID every + time. The value of the first round is used. */ + if (((struct link_map *) h1)->l_tls_modid != modid2) + { + printf ("round %d: modid now %zd, initially %zd\n", + i, ((struct link_map *) h1)->l_tls_modid, modid2); + result = 1; + } + + bazp = dlsym (h2, "baz"); + if (bazp == NULL) + { + printf ("cannot get symbol 'baz' in %s\n", modname2); + exit (1); + } + + *bazp = 62 + i; + + fp2 = dlsym (h2, "in_dso"); + if (fp2 == NULL) + { + printf ("cannot get symbol 'in_dso' in %s\n", modname2); + exit (1); + } + + result |= fp2 (62 + i, bazp); + + /* This time the dlclose calls are in reverse order. */ + dlclose (h2); + dlclose (h1); + } + + return result; +#else + return 0; +#endif +} + + +#include "../test-skeleton.c" diff --git a/libc/elf/tst-tls9-static.c b/libc/elf/tst-tls9-static.c new file mode 100644 index 000000000..51812ccc7 --- /dev/null +++ b/libc/elf/tst-tls9-static.c @@ -0,0 +1 @@ +#include "tst-tls9.c" diff --git a/libc/elf/tst-tls9.c b/libc/elf/tst-tls9.c new file mode 100644 index 000000000..e317696df --- /dev/null +++ b/libc/elf/tst-tls9.c @@ -0,0 +1,42 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <link.h> +#include <tls.h> + +#define TEST_FUNCTION do_test () +static int +do_test (void) +{ +#ifdef USE_TLS + static const char modname1[] = "tst-tlsmod5.so"; + static const char modname2[] = "tst-tlsmod6.so"; + int result = 0; + + void *h1 = dlopen (modname1, RTLD_LAZY); + if (h1 == NULL) + { + printf ("cannot open '%s': %s\n", modname1, dlerror ()); + result = 1; + } + void *h2 = dlopen (modname2, RTLD_LAZY); + if (h2 == NULL) + { + printf ("cannot open '%s': %s\n", modname2, dlerror ()); + result = 1; + } + + if (h1 != NULL) + dlclose (h1); + if (h2 != NULL) + dlclose (h2); + + return result; +#else + return 0; +#endif +} + + +#include "../test-skeleton.c" diff --git a/libc/elf/tst-tlsmod1.c b/libc/elf/tst-tlsmod1.c new file mode 100644 index 000000000..c74a617b7 --- /dev/null +++ b/libc/elf/tst-tlsmod1.c @@ -0,0 +1,68 @@ +#include <stdio.h> + +#include <tls.h> + +#ifdef USE_TLS +#include "tls-macros.h" + + +/* One define int variable, two externs. */ +COMMON_INT_DEF(foo); +VAR_INT_DEF(bar); +VAR_INT_DECL(baz); +#endif + +extern int in_dso (void); + +int +in_dso (void) +{ + int result = 0; +#ifdef USE_TLS + int *ap, *bp, *cp; + + /* Get variables using initial exec model. */ + fputs ("get sum of foo and bar (IE)", stdout); + asm ("" ::: "memory"); + ap = TLS_IE (foo); + bp = TLS_IE (bar); + printf (" = %d\n", *ap + *bp); + result |= *ap + *bp != 3; + if (*ap != 1) + { + printf ("foo = %d\n", *ap); + result = 1; + } + if (*bp != 2) + { + printf ("bar = %d\n", *bp); + result = 1; + } + + + /* Get variables using generic dynamic model. */ + fputs ("get sum of foo and bar and baz (GD)", stdout); + ap = TLS_GD (foo); + bp = TLS_GD (bar); + cp = TLS_GD (baz); + printf (" = %d\n", *ap + *bp + *cp); + result |= *ap + *bp + *cp != 6; + if (*ap != 1) + { + printf ("foo = %d\n", *ap); + result = 1; + } + if (*bp != 2) + { + printf ("bar = %d\n", *bp); + result = 1; + } + if (*cp != 3) + { + printf ("baz = %d\n", *cp); + result = 1; + } +#endif + + return result; +} diff --git a/libc/elf/tst-tlsmod10.c b/libc/elf/tst-tlsmod10.c new file mode 100644 index 000000000..32e54f3c0 --- /dev/null +++ b/libc/elf/tst-tlsmod10.c @@ -0,0 +1 @@ +#include "tst-tlsmod8.c" diff --git a/libc/elf/tst-tlsmod11.c b/libc/elf/tst-tlsmod11.c new file mode 100644 index 000000000..9938b5753 --- /dev/null +++ b/libc/elf/tst-tlsmod11.c @@ -0,0 +1,6 @@ +#include "tst-tls10.h" + +#ifdef USE_TLS__THREAD +__thread struct A a1 = { 4, 5, 6 }; +__thread struct A a2 = { 7, 8, 9 }; +#endif diff --git a/libc/elf/tst-tlsmod12.c b/libc/elf/tst-tlsmod12.c new file mode 100644 index 000000000..4602709a1 --- /dev/null +++ b/libc/elf/tst-tlsmod12.c @@ -0,0 +1,14 @@ +#include "tst-tls10.h" + +#ifdef USE_TLS__THREAD +extern __thread struct A a2 __attribute__((tls_model("initial-exec"))); + +void +check1 (void) +{ + if (a1.a != 4 || a1.b != 5 || a1.c != 6) + abort (); + if (a2.a != 7 || a2.b != 8 || a2.c != 9) + abort (); +} +#endif diff --git a/libc/elf/tst-tlsmod13.c b/libc/elf/tst-tlsmod13.c new file mode 100644 index 000000000..beca89f6f --- /dev/null +++ b/libc/elf/tst-tlsmod13.c @@ -0,0 +1,14 @@ +#include <tls.h> + +#if defined USE_TLS && defined HAVE___THREAD \ + && defined HAVE_TLS_MODEL_ATTRIBUTE +__thread int a[2] __attribute__ ((tls_model ("initial-exec"))); +#else +int a[2]; +#endif + +int +foo (void) +{ + return a[0]; +} diff --git a/libc/elf/tst-tlsmod13a.c b/libc/elf/tst-tlsmod13a.c new file mode 100644 index 000000000..14b12b032 --- /dev/null +++ b/libc/elf/tst-tlsmod13a.c @@ -0,0 +1,16 @@ +#include <tls.h> + +#if defined USE_TLS && defined HAVE___THREAD \ + && defined HAVE_TLS_MODEL_ATTRIBUTE +__thread int b[2] __attribute__ ((tls_model ("initial-exec"))); +#else +int b[2]; +#endif + +extern int foo (void); + +int +bar (void) +{ + return foo () + b[0]; +} diff --git a/libc/elf/tst-tlsmod14a.c b/libc/elf/tst-tlsmod14a.c new file mode 100644 index 000000000..0bb393d9c --- /dev/null +++ b/libc/elf/tst-tlsmod14a.c @@ -0,0 +1,41 @@ +#include <stdint.h> +#include <stdio.h> + +#include <tls.h> + +#if USE_TLS && HAVE___THREAD + +#define AL 4096 +struct foo +{ + int i; +} __attribute ((aligned (AL))); + +static __thread struct foo f; +static struct foo g; + + +#ifndef FCT +# define FCT in_dso1 +#endif + + +int +FCT (void) +{ + puts (__func__); + + int result = 0; + + int fail = (((uintptr_t) &f) & (AL - 1)) != 0; + printf ("&f = %p %s\n", &f, fail ? "FAIL" : "OK"); + result |= fail; + + fail = (((uintptr_t) &g) & (AL - 1)) != 0; + printf ("&g = %p %s\n", &g, fail ? "FAIL" : "OK"); + result |= fail; + + return result; +} + +#endif diff --git a/libc/elf/tst-tlsmod14b.c b/libc/elf/tst-tlsmod14b.c new file mode 100644 index 000000000..24d9ceaf7 --- /dev/null +++ b/libc/elf/tst-tlsmod14b.c @@ -0,0 +1,2 @@ +#define FCT in_dso2 +#include "tst-tlsmod14a.c" diff --git a/libc/elf/tst-tlsmod15a.c b/libc/elf/tst-tlsmod15a.c new file mode 100644 index 000000000..66c707129 --- /dev/null +++ b/libc/elf/tst-tlsmod15a.c @@ -0,0 +1,6 @@ +extern int nonexistent_dummy_var; +int * +foo (void) +{ + return &nonexistent_dummy_var; +} diff --git a/libc/elf/tst-tlsmod15b.c b/libc/elf/tst-tlsmod15b.c new file mode 100644 index 000000000..4f63eab14 --- /dev/null +++ b/libc/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/libc/elf/tst-tlsmod2.c b/libc/elf/tst-tlsmod2.c new file mode 100644 index 000000000..98d9d3e51 --- /dev/null +++ b/libc/elf/tst-tlsmod2.c @@ -0,0 +1,38 @@ +#include <stdio.h> + +#include <tls.h> + +#ifdef USE_TLS +#include "tls-macros.h" + + +COMMON_INT_DEF(foo); + + +int +in_dso (int n, int *caller_foop) +{ + int *foop; + int result = 0; + + puts ("foo"); /* Make sure PLT is used before macros. */ + asm ("" ::: "memory"); + + foop = TLS_GD (foo); + + if (caller_foop != NULL && foop != caller_foop) + { + printf ("callers address of foo differs: %p vs %p\n", caller_foop, foop); + result = 1; + } + else if (*foop != n) + { + printf ("foo != %d\n", n); + result = 1; + } + + *foop = 16; + + return result; +} +#endif diff --git a/libc/elf/tst-tlsmod3.c b/libc/elf/tst-tlsmod3.c new file mode 100644 index 000000000..4a8aad659 --- /dev/null +++ b/libc/elf/tst-tlsmod3.c @@ -0,0 +1,41 @@ +#include <stdio.h> + +#include <tls.h> + +#ifdef USE_TLS +# include "tls-macros.h" + +extern int in_dso (int n, int *caller_foop); + +COMMON_INT_DEF(comm_n); + + + + +int +in_dso2 (void) +{ + int *foop; + int result = 0; + static int n; + int *np; + + puts ("foo"); /* Make sure PLT is used before macros. */ + asm ("" ::: "memory"); + + foop = TLS_GD (foo); + np = TLS_GD (comm_n); + + if (n != *np) + { + printf ("n = %d != comm_n = %d\n", n, *np); + result = 1; + } + + result |= in_dso (*foop = 42 + n++, foop); + + *foop = 16; + + return result; +} +#endif diff --git a/libc/elf/tst-tlsmod4.c b/libc/elf/tst-tlsmod4.c new file mode 100644 index 000000000..5285e821b --- /dev/null +++ b/libc/elf/tst-tlsmod4.c @@ -0,0 +1,38 @@ +#include <stdio.h> + +#include <tls.h> + +#ifdef USE_TLS +# include "tls-macros.h" + + +COMMON_INT_DEF(baz); + + +int +in_dso (int n, int *caller_bazp) +{ + int *bazp; + int result = 0; + + puts ("foo"); /* Make sure PLT is used before macros. */ + asm ("" ::: "memory"); + + bazp = TLS_GD (baz); + + if (caller_bazp != NULL && bazp != caller_bazp) + { + printf ("callers address of baz differs: %p vs %p\n", caller_bazp, bazp); + result = 1; + } + else if (*bazp != n) + { + printf ("baz != %d\n", n); + result = 1; + } + + *bazp = 16; + + return result; +} +#endif diff --git a/libc/elf/tst-tlsmod5.c b/libc/elf/tst-tlsmod5.c new file mode 100644 index 000000000..2ec69e13b --- /dev/null +++ b/libc/elf/tst-tlsmod5.c @@ -0,0 +1,7 @@ +#include <tls.h> + +#ifdef USE_TLS +#include "tls-macros.h" + +COMMON_INT_DEF(foo); +#endif diff --git a/libc/elf/tst-tlsmod6.c b/libc/elf/tst-tlsmod6.c new file mode 100644 index 000000000..0fda51b22 --- /dev/null +++ b/libc/elf/tst-tlsmod6.c @@ -0,0 +1,7 @@ +#include <tls.h> + +#ifdef USE_TLS +#include "tls-macros.h" + +COMMON_INT_DEF(bar); +#endif diff --git a/libc/elf/tst-tlsmod7.c b/libc/elf/tst-tlsmod7.c new file mode 100644 index 000000000..944b97f9c --- /dev/null +++ b/libc/elf/tst-tlsmod7.c @@ -0,0 +1,103 @@ +#include "tst-tls10.h" + +#ifdef USE_TLS__THREAD +__thread int dummy __attribute__((visibility ("hidden"))) = 12; +__thread struct A a1 = { 4, 5, 6 }; +__thread struct A a2 = { 7, 8, 9 }; +__thread struct A a3 __attribute__((tls_model("initial-exec"))) + = { 10, 11, 12 }; +__thread struct A a4 __attribute__((tls_model("initial-exec"))) + = { 13, 14, 15 }; +static __thread struct A local1 = { 16, 17, 18 }; +static __thread struct A local2 __attribute__((tls_model("initial-exec"))) + = { 19, 20, 21 }; + +void +check1 (void) +{ + if (a1.a != 4 || a1.b != 5 || a1.c != 6) + abort (); + if (a2.a != 22 || a2.b != 23 || a2.c != 24) + abort (); + if (a3.a != 10 || a3.b != 11 || a3.c != 12) + abort (); + if (a4.a != 25 || a4.b != 26 || a4.c != 27) + abort (); + if (local1.a != 16 || local1.b != 17 || local1.c != 18) + abort (); + if (local2.a != 19 || local2.b != 20 || local2.c != 21) + abort (); +} + +struct A * +f1a (void) +{ + return &a1; +} + +struct A * +f2a (void) +{ + return &a2; +} + +struct A * +f3a (void) +{ + return &a3; +} + +struct A * +f4a (void) +{ + return &a4; +} + +struct A * +f5a (void) +{ + return &local1; +} + +struct A * +f6a (void) +{ + return &local2; +} + +int +f1b (void) +{ + return a1.a; +} + +int +f2b (void) +{ + return a2.b; +} + +int +f3b (void) +{ + return a3.c; +} + +int +f4b (void) +{ + return a4.a; +} + +int +f5b (void) +{ + return local1.b; +} + +int +f6b (void) +{ + return local2.c; +} +#endif diff --git a/libc/elf/tst-tlsmod8.c b/libc/elf/tst-tlsmod8.c new file mode 100644 index 000000000..c1822fc0c --- /dev/null +++ b/libc/elf/tst-tlsmod8.c @@ -0,0 +1,72 @@ +#include "tst-tls10.h" + +#ifdef USE_TLS__THREAD +__thread long long dummy __attribute__((visibility ("hidden"))) = 12; +__thread struct A a2 = { 22, 23, 24 }; +__thread struct A a4 __attribute__((tls_model("initial-exec"))) + = { 25, 26, 27 }; +static __thread struct A local1 = { 28, 29, 30 }; +static __thread struct A local2 __attribute__((tls_model("initial-exec"))) + = { 31, 32, 33 }; + +void +check2 (void) +{ + if (a2.a != 22 || a2.b != 23 || a2.c != 24) + abort (); + if (a4.a != 25 || a4.b != 26 || a4.c != 27) + abort (); + if (local1.a != 28 || local1.b != 29 || local1.c != 30) + abort (); + if (local2.a != 31 || local2.b != 32 || local2.c != 33) + abort (); +} + +struct A * +f7a (void) +{ + return &a2; +} + +struct A * +f8a (void) +{ + return &a4; +} + +struct A * +f9a (void) +{ + return &local1; +} + +struct A * +f10a (void) +{ + return &local2; +} + +int +f7b (void) +{ + return a2.b; +} + +int +f8b (void) +{ + return a4.a; +} + +int +f9b (void) +{ + return local1.b; +} + +int +f10b (void) +{ + return local2.c; +} +#endif diff --git a/libc/elf/tst-tlsmod9.c b/libc/elf/tst-tlsmod9.c new file mode 100644 index 000000000..e124144e4 --- /dev/null +++ b/libc/elf/tst-tlsmod9.c @@ -0,0 +1,101 @@ +#include "tst-tls10.h" + +#ifdef USE_TLS__THREAD +__thread int dummy __attribute__((visibility ("hidden"))) = 12; +__thread struct A a1 = { 4, 5, 6 }; +__thread struct A a3 __attribute__((tls_model("initial-exec"))) + = { 10, 11, 12 }; +extern __thread struct A a4 __attribute__((tls_model("initial-exec"))); +static __thread struct A local1 = { 16, 17, 18 }; +static __thread struct A local2 __attribute__((tls_model("initial-exec"))) + = { 19, 20, 21 }; + +void +check1 (void) +{ + if (a1.a != 4 || a1.b != 5 || a1.c != 6) + abort (); + if (a2.a != 22 || a2.b != 23 || a2.c != 24) + abort (); + if (a3.a != 10 || a3.b != 11 || a3.c != 12) + abort (); + if (a4.a != 25 || a4.b != 26 || a4.c != 27) + abort (); + if (local1.a != 16 || local1.b != 17 || local1.c != 18) + abort (); + if (local2.a != 19 || local2.b != 20 || local2.c != 21) + abort (); +} + +struct A * +f1a (void) +{ + return &a1; +} + +struct A * +f2a (void) +{ + return &a2; +} + +struct A * +f3a (void) +{ + return &a3; +} + +struct A * +f4a (void) +{ + return &a4; +} + +struct A * +f5a (void) +{ + return &local1; +} + +struct A * +f6a (void) +{ + return &local2; +} + +int +f1b (void) +{ + return a1.a; +} + +int +f2b (void) +{ + return a2.b; +} + +int +f3b (void) +{ + return a3.c; +} + +int +f4b (void) +{ + return a4.a; +} + +int +f5b (void) +{ + return local1.b; +} + +int +f6b (void) +{ + return local2.c; +} +#endif diff --git a/libc/elf/unload.c b/libc/elf/unload.c new file mode 100644 index 000000000..4566f226f --- /dev/null +++ b/libc/elf/unload.c @@ -0,0 +1,91 @@ +/* Test for unloading (really unmapping) of objects. By Franz Sirl. + This test does not have to passed in all dlopen() et.al. implementation + since it is not required the unloading actually happens. But we + require it for glibc. */ + +#include <dlfcn.h> +#include <link.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + +#define MAPS ((struct link_map *) _r_debug.r_map) + +#define OUT \ + for (map = MAPS; map != NULL; map = map->l_next) \ + if (map->l_type == lt_loaded) \ + printf ("name = \"%s\", direct_opencount = %d\n", \ + map->l_name, (int) map->l_direct_opencount); \ + fflush (stdout) + +typedef struct +{ + void *next; +} strct; + +int +main (void) +{ + void *sohandle; + strct *testdat; + int ret; + int result = 0; + struct link_map *map; + + mtrace (); + + puts ("\nBefore"); + OUT; + + sohandle = dlopen ("unloadmod.so", RTLD_NOW | RTLD_GLOBAL); + if (sohandle == NULL) + { + printf ("*** first dlopen failed: %s\n", dlerror ()); + exit (1); + } + + puts ("\nAfter loading unloadmod.so"); + OUT; + + testdat = dlsym (sohandle, "testdat"); + testdat->next = (void *) -1; + + ret = dlclose (sohandle); + if (ret != 0) + { + puts ("*** first dlclose failed"); + result = 1; + } + + puts ("\nAfter closing unloadmod.so"); + OUT; + + sohandle = dlopen ("unloadmod.so", RTLD_NOW | RTLD_GLOBAL); + if (sohandle == NULL) + { + printf ("*** second dlopen failed: %s\n", dlerror ()); + exit (1); + } + + puts ("\nAfter loading unloadmod.so the second time"); + OUT; + + testdat = dlsym (sohandle, "testdat"); + if (testdat->next == (void *) -1) + { + puts ("*** testdat->next == (void *) -1"); + result = 1; + } + + ret = dlclose (sohandle); + if (ret != 0) + { + puts ("*** second dlclose failed"); + result = 1; + } + + puts ("\nAfter closing unloadmod.so again"); + OUT; + + return result; +} diff --git a/libc/elf/unload2.c b/libc/elf/unload2.c new file mode 100644 index 000000000..eef2bfd42 --- /dev/null +++ b/libc/elf/unload2.c @@ -0,0 +1,59 @@ +#include <dlfcn.h> +#include <elf.h> +#include <errno.h> +#include <error.h> +#include <link.h> +#include <stdio.h> +#include <stdlib.h> + +#define MAPS ((struct link_map *) _r_debug.r_map) + +#define OUT \ + for (map = MAPS; map != NULL; map = map->l_next) \ + if (map->l_type == lt_loaded) \ + printf ("name = \"%s\", direct_opencount = %d\n", \ + map->l_name, (int) map->l_direct_opencount); \ + fflush (stdout) + +int +main (void) +{ + void *h[3]; + struct link_map *map; + void (*fp) (void); + + h[0] = dlopen ("unload2mod.so", RTLD_LAZY); + h[1] = dlopen ("unload2mod.so", RTLD_LAZY); + if (h[0] == NULL || h[1] == NULL) + error (EXIT_FAILURE, errno, "cannot load \"unload2mod.so\""); + h[2] = dlopen ("unload2dep.so", RTLD_LAZY); + if (h[2] == NULL) + error (EXIT_FAILURE, errno, "cannot load \"unload2dep.so\""); + + puts ("\nAfter loading everything:"); + OUT; + + dlclose (h[0]); + + puts ("\nAfter unloading \"unload2mod.so\" once:"); + OUT; + + dlclose (h[1]); + + puts ("\nAfter unloading \"unload2mod.so\" twice:"); + OUT; + + fp = dlsym (h[2], "foo"); + puts ("\nnow calling `foo'"); + fflush (stdout); + fp (); + puts ("managed to call `foo'"); + fflush (stdout); + + dlclose (h[2]); + + puts ("\nAfter unloading \"unload2dep.so\":"); + OUT; + + return 0; +} diff --git a/libc/elf/unload2dep.c b/libc/elf/unload2dep.c new file mode 100644 index 000000000..0d319515d --- /dev/null +++ b/libc/elf/unload2dep.c @@ -0,0 +1,6 @@ +extern void foo (void); + +void +foo (void) +{ +} diff --git a/libc/elf/unload2mod.c b/libc/elf/unload2mod.c new file mode 100644 index 000000000..9c2ea586b --- /dev/null +++ b/libc/elf/unload2mod.c @@ -0,0 +1,8 @@ +extern void foo (void); +extern void bar (void); + +void +bar (void) +{ + foo (); +} diff --git a/libc/elf/unload3.c b/libc/elf/unload3.c new file mode 100644 index 000000000..6f1af707e --- /dev/null +++ b/libc/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/libc/elf/unload3mod1.c b/libc/elf/unload3mod1.c new file mode 100644 index 000000000..e886b11c1 --- /dev/null +++ b/libc/elf/unload3mod1.c @@ -0,0 +1 @@ +int dummy1; diff --git a/libc/elf/unload3mod2.c b/libc/elf/unload3mod2.c new file mode 100644 index 000000000..03252a523 --- /dev/null +++ b/libc/elf/unload3mod2.c @@ -0,0 +1 @@ +int dummy2; diff --git a/libc/elf/unload3mod3.c b/libc/elf/unload3mod3.c new file mode 100644 index 000000000..046022c55 --- /dev/null +++ b/libc/elf/unload3mod3.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int +foo (int x) +{ + puts ("foo"); + return x * 2; +} diff --git a/libc/elf/unload3mod4.c b/libc/elf/unload3mod4.c new file mode 100644 index 000000000..52f808e79 --- /dev/null +++ b/libc/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/libc/elf/unload4.c b/libc/elf/unload4.c new file mode 100644 index 000000000..6e171a22e --- /dev/null +++ b/libc/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/libc/elf/unload4mod1.c b/libc/elf/unload4mod1.c new file mode 100644 index 000000000..38c5b0168 --- /dev/null +++ b/libc/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/libc/elf/unload4mod2.c b/libc/elf/unload4mod2.c new file mode 100644 index 000000000..497ef5d93 --- /dev/null +++ b/libc/elf/unload4mod2.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int +baz (int x) +{ + puts ("in baz"); + return x * 4; +} diff --git a/libc/elf/unload4mod3.c b/libc/elf/unload4mod3.c new file mode 100644 index 000000000..4b280bc05 --- /dev/null +++ b/libc/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/libc/elf/unload4mod4.c b/libc/elf/unload4mod4.c new file mode 100644 index 000000000..ba5a144d3 --- /dev/null +++ b/libc/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/libc/elf/unload5.c b/libc/elf/unload5.c new file mode 100644 index 000000000..0555052ce --- /dev/null +++ b/libc/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/libc/elf/unload6.c b/libc/elf/unload6.c new file mode 100644 index 000000000..1efc7eb84 --- /dev/null +++ b/libc/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/libc/elf/unload6mod1.c b/libc/elf/unload6mod1.c new file mode 100644 index 000000000..24f2e5a19 --- /dev/null +++ b/libc/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/libc/elf/unload6mod2.c b/libc/elf/unload6mod2.c new file mode 100644 index 000000000..980efa4b0 --- /dev/null +++ b/libc/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/libc/elf/unload6mod3.c b/libc/elf/unload6mod3.c new file mode 100644 index 000000000..7b29e1d62 --- /dev/null +++ b/libc/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/libc/elf/unloadmod.c b/libc/elf/unloadmod.c new file mode 100644 index 000000000..3aa5403ed --- /dev/null +++ b/libc/elf/unloadmod.c @@ -0,0 +1,4 @@ +struct testdat +{ + void *next; +} testdat; diff --git a/libc/elf/vismain.c b/libc/elf/vismain.c new file mode 100644 index 000000000..ebf9b65c3 --- /dev/null +++ b/libc/elf/vismain.c @@ -0,0 +1,257 @@ +/* Copyright (C) 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. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "vismod.h" + +/* Prototype for our test function. */ +extern int do_test (void); + +#define TEST_FUNCTION do_test () + +/* This defines the `main' function and some more. */ +#include <test-skeleton.c> + + +/* Prototypes for local functions. */ +extern int protlocal (void); + +const char *protvarlocal = __FILE__; +extern const char *protvarinmod; +extern const char *protvaritcpt; + +int +do_test (void) +{ + int res = 0; + int val; + + /* First test: check whether .protected is handled correctly by the + assembler/linker. The uses of `protlocal' in the DSOs and in the + main program should all be resolved with the local definitions. */ + val = protlocal () + calllocal1 () + calllocal2 (); + if (val != 0x155) + { + puts ("\ +The handling of `.protected' seems to be implemented incorrectly: giving up"); + abort (); + } + puts ("`.protected' seems to be handled correctly, good!"); + + /* Function pointers: for functions which are marked local and for + which definitions are available all function pointers must be + distinct. */ + if (protlocal == getlocal1 ()) + { + puts ("`protlocal' in main and mod1 have same address"); + res = 1; + } + if (protlocal == getlocal2 ()) + { + puts ("`protlocal' in main and mod2 have same address"); + res = 1; + } + if (getlocal1 () == getlocal2 ()) + { + puts ("`protlocal' in mod1 and mod2 have same address"); + res = 1; + } + if (getlocal1 () () + getlocal2 () () != 0x44) + { + puts ("pointers to `protlocal' in mod1 or mod2 incorrect"); + res = 1; + } + + /* Next test. This is similar to the last one but the function we + are calling is not defined in the main object. This means that + the invocation in the main object uses the definition in the + first DSO. */ + if (protinmod != getinmod1 ()) + { + printf ("&protinmod in main (%p) != &protinmod in mod1 (%p)\n", + protinmod, getinmod1 ()); + res = 1; + } + if (protinmod == getinmod2 ()) + { + puts ("`protinmod' in main and mod2 have same address"); + res = 1; + } + if (getinmod1 () == getinmod2 ()) + { + puts ("`protinmod' in mod1 and mod2 have same address"); + res = 1; + } + if (protinmod () + getinmod1 () () + getinmod2 () () != 0x4800) + { + puts ("pointers to `protinmod' in mod1 or mod2 incorrect"); + res = 1; + } + val = protinmod () + callinmod1 () + callinmod2 (); + if (val != 0x15800) + { + printf ("calling of `protinmod' leads to wrong result (%#x)\n", val); + res = 1; + } + + /* A very similar text. Same setup for the main object and the modules + but this time we have another definition in a preloaded module. This + one intercepts the references from the main object. */ + if (protitcpt != getitcpt3 ()) + { + printf ("&protitcpt in main (%p) != &protitcpt in mod3 (%p)\n", + &protitcpt, getitcpt3 ()); + res = 1; + } + if (protitcpt == getitcpt1 ()) + { + puts ("`protitcpt' in main and mod1 have same address"); + res = 1; + } + if (protitcpt == getitcpt2 ()) + { + puts ("`protitcpt' in main and mod2 have same address"); + res = 1; + } + if (getitcpt1 () == getitcpt2 ()) + { + puts ("`protitcpt' in mod1 and mod2 have same address"); + res = 1; + } + val = protitcpt () + getitcpt1 () () + getitcpt2 () () + getitcpt3 () (); + if (val != 0x8440000) + { + printf ("\ +pointers to `protitcpt' in mod1 or mod2 or mod3 incorrect (%#x)\n", val); + res = 1; + } + val = protitcpt () + callitcpt1 () + callitcpt2 () + callitcpt3 (); + if (val != 0x19540000) + { + printf ("calling of `protitcpt' leads to wrong result (%#x)\n", val); + res = 1; + } + + /* Now look at variables. First a variable which is available + everywhere. We must have three different addresses. */ + if (&protvarlocal == getvarlocal1 ()) + { + puts ("`protvarlocal' in main and mod1 have same address"); + res = 1; + } + if (&protvarlocal == getvarlocal2 ()) + { + puts ("`protvarlocal' in main and mod2 have same address"); + res = 1; + } + if (getvarlocal1 () == getvarlocal2 ()) + { + puts ("`protvarlocal' in mod1 and mod2 have same address"); + res = 1; + } + if (strcmp (protvarlocal, __FILE__) != 0) + { + puts ("`protvarlocal in main has wrong value"); + res = 1; + } + if (strcmp (*getvarlocal1 (), "vismod1.c") != 0) + { + puts ("`getvarlocal1' returns wrong value"); + res = 1; + } + if (strcmp (*getvarlocal2 (), "vismod2.c") != 0) + { + puts ("`getvarlocal2' returns wrong value"); + res = 1; + } + + /* Now the case where there is no local definition. */ + if (&protvarinmod != getvarinmod1 ()) + { + printf ("&protvarinmod in main (%p) != &protitcpt in mod1 (%p)\n", + &protvarinmod, getvarinmod1 ()); + // XXX Possibly enable once fixed. + // res = 1; + } + if (&protvarinmod == getvarinmod2 ()) + { + puts ("`protvarinmod' in main and mod2 have same address"); + res = 1; + } + if (strcmp (*getvarinmod1 (), "vismod1.c") != 0) + { + puts ("`getvarinmod1' returns wrong value"); + res = 1; + } + if (strcmp (*getvarinmod2 (), "vismod2.c") != 0) + { + puts ("`getvarinmod2' returns wrong value"); + res = 1; + } + + /* And a test where a variable definition is intercepted. */ + if (&protvaritcpt == getvaritcpt1 ()) + { + puts ("`protvaritcpt' in main and mod1 have same address"); + res = 1; + } + if (&protvaritcpt == getvaritcpt2 ()) + { + puts ("`protvaritcpt' in main and mod2 have same address"); + res = 1; + } + if (&protvaritcpt != getvaritcpt3 ()) + { + printf ("&protvaritcpt in main (%p) != &protvaritcpt in mod3 (%p)\n", + &protvaritcpt, getvaritcpt3 ()); + // XXX Possibly enable once fixed. + // res = 1; + } + if (getvaritcpt1 () == getvaritcpt2 ()) + { + puts ("`protvaritcpt' in mod1 and mod2 have same address"); + res = 1; + } + if (strcmp (protvaritcpt, "vismod3.c") != 0) + { + puts ("`protvaritcpt in main has wrong value"); + res = 1; + } + if (strcmp (*getvaritcpt1 (), "vismod1.c") != 0) + { + puts ("`getvaritcpt1' returns wrong value"); + res = 1; + } + if (strcmp (*getvaritcpt2 (), "vismod2.c") != 0) + { + puts ("`getvaritcpt2' returns wrong value"); + res = 1; + } + + return res; +} + + +int +protlocal (void) +{ + return 0x1; +} diff --git a/libc/elf/vismod.h b/libc/elf/vismod.h new file mode 100644 index 000000000..ef05ffd5e --- /dev/null +++ b/libc/elf/vismod.h @@ -0,0 +1,27 @@ +/* Prototypes for the functions in the DSOs. */ +extern int calllocal1 (void); +extern int (*getlocal1 (void)) (void); +extern int callinmod1 (void); +extern int (*getinmod1 (void)) (void); +extern int callitcpt1 (void); +extern int (*getitcpt1 (void)) (void); +extern const char **getvarlocal1 (void); +extern const char **getvarinmod1 (void); +extern const char **getvaritcpt1 (void); +extern int calllocal2 (void); +extern int (*getlocal2 (void)) (void); +extern int callinmod2 (void); +extern int (*getinmod2 (void)) (void); +extern int callitcpt2 (void); +extern int (*getitcpt2 (void)) (void); +extern const char **getvarlocal2 (void); +extern const char **getvarinmod2 (void); +extern const char **getvaritcpt2 (void); +extern int callitcpt3 (void); +extern int (*getitcpt3 (void)) (void); +extern const char **getvaritcpt3 (void); + +extern int protinmod (void); +extern int protitcpt (void); +extern int protlocal (void); + diff --git a/libc/elf/vismod1.c b/libc/elf/vismod1.c new file mode 100644 index 000000000..6f9c1a59f --- /dev/null +++ b/libc/elf/vismod1.c @@ -0,0 +1,104 @@ +/* Copyright (C) 2000 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 "vismod.h" + +int +protlocal (void) +{ + return 0x4; +} +asm (".protected protlocal"); + + +int +calllocal1 (void) +{ + return protlocal () + 0x10; +} + +int +(*getlocal1 (void)) (void) +{ + return protlocal; +} + +int +protinmod (void) +{ + return 0x400; +} +asm (".protected protinmod"); + +int +callinmod1 (void) +{ + return protinmod () + 0x1000; +} + +int +(*getinmod1 (void)) (void) +{ + return protinmod; +} + +int +protitcpt (void) +{ + return 0x40000; +} +asm (".protected protitcpt"); + +int +callitcpt1 (void) +{ + return protitcpt () + 0x100000; +} + +int +(*getitcpt1 (void)) (void) +{ + return protitcpt; +} + +const char *protvarlocal = __FILE__; +asm (".protected protvarlocal"); + +const char ** +getvarlocal1 (void) +{ + return &protvarlocal; +} + +const char *protvarinmod = __FILE__; +asm (".protected protvarinmod"); + +const char ** +getvarinmod1 (void) +{ + return &protvarinmod; +} + +const char *protvaritcpt = __FILE__; +asm (".protected protvaritcpt"); + +const char ** +getvaritcpt1 (void) +{ + return &protvaritcpt; +} diff --git a/libc/elf/vismod2.c b/libc/elf/vismod2.c new file mode 100644 index 000000000..5443e7c8a --- /dev/null +++ b/libc/elf/vismod2.c @@ -0,0 +1,124 @@ +/* Copyright (C) 2000 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 <stdlib.h> +#include "vismod.h" + +int +protlocal (void) +{ + return 0x40; +} +asm (".protected protlocal"); + + +int +calllocal2 (void) +{ + return protlocal () + 0x100; +} + +int +(*getlocal2 (void)) (void) +{ + return protlocal; +} + +int +protinmod (void) +{ + return 0x4000; +} +asm (".protected protinmod"); + +int +callinmod2 (void) +{ + return protinmod () + 0x10000; +} + +int +(*getinmod2 (void)) (void) +{ + return protinmod; +} + +int +protitcpt (void) +{ + return 0x400000; +} +asm (".protected protitcpt"); + +int +callitcpt2 (void) +{ + return protitcpt () + 0x1000000; +} + +int +(*getitcpt2 (void)) (void) +{ + return protitcpt; +} + +const char *protvarlocal = __FILE__; +asm (".protected protvarlocal"); + +const char ** +getvarlocal2 (void) +{ + return &protvarlocal; +} + +const char *protvarinmod = __FILE__; +asm (".protected protvarinmod"); + +const char ** +getvarinmod2 (void) +{ + return &protvarinmod; +} + +const char *protvaritcpt = __FILE__; +asm (".protected protvaritcpt"); + +const char ** +getvaritcpt2 (void) +{ + return &protvaritcpt; +} + +/* We must never call these functions. */ +int +callitcpt3 (void) +{ + abort (); +} + +int +(*getitcpt3 (void)) (void) +{ + abort (); +} + +const char ** +getvaritcpt3 (void) +{ + abort (); +} diff --git a/libc/elf/vismod3.c b/libc/elf/vismod3.c new file mode 100644 index 000000000..c9df95cb0 --- /dev/null +++ b/libc/elf/vismod3.c @@ -0,0 +1,47 @@ +/* Copyright (C) 2000 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 "vismod.h" + +int +protitcpt (void) +{ + return 0x4000000; +} +asm (".protected protitcpt"); + +int +callitcpt3 (void) +{ + return protitcpt () + 0x10000000; +} + +int +(*getitcpt3 (void)) (void) +{ + return protitcpt; +} + +const char *protvaritcpt = __FILE__; +asm (".protected protvaritcpt"); + +const char ** +getvaritcpt3 (void) +{ + return &protvaritcpt; +} |