diff options
Diffstat (limited to 'REORG.TODO/elf')
461 files changed, 43439 insertions, 0 deletions
diff --git a/REORG.TODO/elf/Makefile b/REORG.TODO/elf/Makefile new file mode 100644 index 0000000000..201b328f88 --- /dev/null +++ b/REORG.TODO/elf/Makefile @@ -0,0 +1,1412 @@ +# Copyright (C) 1995-2017 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# <http://www.gnu.org/licenses/>. + +# Makefile for elf subdirectory of GNU C Library. + +subdir := elf + +include ../Makeconfig + +headers = elf.h bits/elfclass.h link.h bits/link.h +routines = $(all-dl-routines) dl-support dl-iteratephdr \ + dl-addr dl-addr-obj enbl-secure dl-profstub \ + dl-origin dl-libc dl-sym dl-sysdep dl-error + +# The core dynamic linking functions are in libc for the static and +# profiled libraries. +dl-routines = $(addprefix dl-,load lookup object reloc deps hwcaps \ + runtime init fini debug misc \ + version profile conflict tls origin scope \ + execstack caller open close trampoline) +ifeq (yes,$(use-ldconfig)) +dl-routines += dl-cache +endif + +ifneq (no,$(have-tunables)) +dl-routines += dl-tunables +tunables-type = $(addprefix TUNABLES_FRONTEND_,$(have-tunables)) +CPPFLAGS-dl-tunables.c = -DTUNABLES_FRONTEND=$(tunables-type) + +# Make sure that the compiler does not insert any library calls in tunables +# code paths. +ifeq (yes,$(have-loop-to-function)) +CFLAGS-dl-tunables.c = -fno-tree-loop-distribute-patterns +endif +endif + +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 \ + dl-sysdep +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 $(all-dl-routines) dl-sysdep dl-environ dl-minimal \ + dl-error-minimal +all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines) + +CFLAGS-dl-runtime.c = -fexceptions -fasynchronous-unwind-tables +CFLAGS-dl-lookup.c = -fexceptions -fasynchronous-unwind-tables +CFLAGS-dl-iterate-phdr.c = $(uses-callbacks) + +# Compile rtld itself without stack protection. +# Also compile all routines in the static library that are elided from +# the shared libc because they are in libc.a in the same way. + +define elide-stack-protector +$(if $(filter $(@F),$(patsubst %,%$(1),$(2))), $(no-stack-protector)) +endef + +CFLAGS-.o += $(call elide-stack-protector,.o,$(elide-routines.os)) +CFLAGS-.op += $(call elide-stack-protector,.op,$(elide-routines.os)) +CFLAGS-.os += $(call elide-stack-protector,.os,$(all-rtld-routines)) + +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 ($(build-shared),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_rtlddir)/$(rtld-installed-name) +install-bin-script = ldd +endif + +others = sprof sln +install-bin = sprof +others-static = sln +install-rootsbin = sln +sln-modules := static-stubs +extra-objs += $(sln-modules:=.o) + +ifeq (yes,$(use-ldconfig)) +ifeq (yes,$(build-shared)) +others-static += ldconfig +others += ldconfig +install-rootsbin += ldconfig + +ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon static-stubs +extra-objs += $(ldconfig-modules:=.o) +endif +endif + +# To find xmalloc.c and xstrdup.c +vpath %.c ../locale/programs + +ifeq ($(build-shared),yes) +extra-objs += sotruss-lib.os sotruss-lib.so +install-others += $(inst_auditdir)/sotruss-lib.so +install-bin-script += sotruss +generated += sotruss +libof-sotruss-lib = extramodules +$(objpfx)sotruss-lib.so: $(objpfx)sotruss-lib.os + $(build-module-asneeded) +$(objpfx)sotruss-lib.so: $(common-objpfx)libc.so $(objpfx)ld.so \ + $(common-objpfx)libc_nonshared.a + +$(objpfx)sotruss: sotruss.sh $(common-objpfx)config.make + sed -e 's%@BASH@%$(BASH)%g' \ + -e 's%@VERSION@%$(version)%g' \ + -e 's%@TEXTDOMAINDIR@%$(localedir)%g' \ + -e 's%@PREFIX@%$(prefix)%g' \ + -e 's|@PKGVERSION@|$(PKGVERSION)|g' \ + -e 's|@REPORT_BUGS_TO@|$(REPORT_BUGS_TO)|g' \ + < $< > $@.new + chmod 555 $@.new + mv -f $@.new $@ +$(inst_auditdir)/sotruss-lib.so: $(objpfx)sotruss-lib.so $(+force) + $(do-install-program) +endif + +tests-static-normal := tst-leaks1-static tst-array1-static tst-array5-static \ + tst-dl-iter-static \ + tst-tlsalign-static tst-tlsalign-extern-static \ + tst-linkall-static tst-env-setuid tst-env-setuid-tunables +tests-static-internal := tst-tls1-static tst-tls2-static \ + tst-ptrguard1-static tst-stackguard1-static + +tests := tst-tls9 tst-leaks1 \ + tst-array1 tst-array2 tst-array3 tst-array4 tst-array5 \ + tst-auxv +tests-internal := tst-tls1 tst-tls2 $(tests-static-internal) +tests-static := $(tests-static-normal) $(tests-static-internal) + +ifeq (yes,$(build-shared)) +tests-static += tst-tls9-static +tst-tls9-static-ENV = \ + LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn + +tests += restest1 preloadtest loadfail multiload origtest resolvfail \ + constload1 order noload filter \ + reldep reldep2 reldep3 reldep4 nodelete nodelete2 \ + nodlopen nodlopen2 lateglobal initfirst global \ + restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \ + tst-tls4 tst-tls5 \ + tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-tls15 \ + tst-tls16 tst-tls17 tst-tls18 tst-tls19 tst-tls-dlinfo \ + tst-align tst-align2 $(tests-execstack-$(have-z-execstack)) \ + tst-dlmodcount tst-dlopenrpath tst-deep1 \ + tst-dlmopen1 tst-dlmopen3 \ + unload3 unload4 unload5 unload6 unload7 unload8 tst-global1 order2 \ + tst-audit1 tst-audit2 tst-audit8 tst-audit9 \ + tst-addr1 tst-thrlock \ + tst-unique1 tst-unique2 $(if $(CXX),tst-unique3 tst-unique4 \ + tst-nodelete) \ + tst-initorder tst-initorder2 tst-relsort1 tst-null-argv \ + tst-tlsalign tst-tlsalign-extern tst-nodelete-opened \ + tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \ + tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose +# reldep9 +tests-internal += loadtest unload unload2 circleload1 \ + neededtest neededtest2 neededtest3 neededtest4 \ + tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \ + tst-ptrguard1 tst-stackguard1 +ifeq ($(build-hardcoded-path-in-tests),yes) +tests += tst-dlopen-aout +LDFLAGS-tst-dlopen-aout = $(no-pie-ldflag) +endif +test-srcs = tst-pathopt +selinux-enabled := $(shell cat /selinux/enforce 2> /dev/null) +ifneq ($(selinux-enabled),1) +tests-execstack-yes = tst-execstack tst-execstack-needed tst-execstack-prog +endif +endif +ifeq ($(run-built-tests),yes) +tests-special += $(objpfx)tst-leaks1-mem.out \ + $(objpfx)tst-leaks1-static-mem.out $(objpfx)noload-mem.out \ + $(objpfx)tst-ldconfig-X.out +endif +tlsmod17a-suffixes = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 +tlsmod18a-suffixes = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 +tlsmod17a-modules = $(addprefix tst-tlsmod17a, $(tlsmod17a-suffixes)) +tlsmod18a-modules = $(addprefix tst-tlsmod18a, $(tlsmod17a-suffixes)) +one-hundred = $(foreach x,0 1 2 3 4 5 6 7 8 9, \ + 0$x 1$x 2$x 3$x 4$x 5$x 6$x 7$x 8$x 9$x) +tst-tls-many-dynamic-modules := \ + $(foreach n,$(one-hundred),tst-tls-manydynamic$(n)mod) +extra-test-objs += $(tlsmod17a-modules:=.os) $(tlsmod18a-modules:=.os) \ + tst-tlsalign-vars.o +test-extras += tst-tlsmod17a tst-tlsmod18a tst-tlsalign-vars +modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ + testobj1_1 failobj constload2 constload3 unloadmod \ + dep1 dep2 dep3 dep4 vismod1 vismod2 vismod3 \ + nodelmod1 nodelmod2 nodelmod3 nodelmod4 \ + nodel2mod1 nodel2mod2 nodel2mod3 \ + nodlopenmod nodlopenmod2 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 tst-tlsmod16a tst-tlsmod16b \ + $(tlsmod17a-modules) tst-tlsmod17b $(tlsmod18a-modules) \ + tst-tls19mod1 tst-tls19mod2 tst-tls19mod3 \ + 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 \ + unload7mod1 unload7mod2 \ + unload8mod1 unload8mod1x unload8mod2 unload8mod3 \ + order2mod1 order2mod2 order2mod3 order2mod4 \ + tst-unique1mod1 tst-unique1mod2 \ + tst-unique2mod1 tst-unique2mod2 \ + tst-auditmod9a tst-auditmod9b \ + $(if $(CXX),tst-unique3lib tst-unique3lib2 tst-unique4lib \ + tst-nodelete-uniquemod tst-nodelete-rtldmod \ + tst-nodelete-zmod) \ + tst-initordera1 tst-initorderb1 \ + tst-initordera2 tst-initorderb2 \ + tst-initordera3 tst-initordera4 \ + tst-initorder2a tst-initorder2b tst-initorder2c \ + tst-initorder2d \ + tst-relsort1mod1 tst-relsort1mod2 tst-array2dep \ + tst-array5dep tst-null-argv-lib \ + tst-tlsalign-lib tst-nodelete-opened-lib tst-nodelete2mod \ + tst-audit11mod1 tst-audit11mod2 tst-auditmod11 \ + tst-audit12mod1 tst-audit12mod2 tst-audit12mod3 tst-auditmod12 \ + tst-latepthreadmod $(tst-tls-many-dynamic-modules) \ + tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin +ifeq (yes,$(have-mtls-dialect-gnu2)) +tests += tst-gnu2-tls1 +modules-names += tst-gnu2-tls1mod +$(objpfx)tst-gnu2-tls1: $(objpfx)tst-gnu2-tls1mod.so +tst-gnu2-tls1mod.so-no-z-defs = yes +CFLAGS-tst-gnu2-tls1mod.c += -mtls-dialect=gnu2 +endif +ifeq (yes,$(have-protected-data)) +modules-names += tst-protected1moda tst-protected1modb +tests += tst-protected1a tst-protected1b +$(objpfx)tst-protected1a: $(addprefix $(objpfx),tst-protected1moda.so tst-protected1modb.so) +$(objpfx)tst-protected1b: $(addprefix $(objpfx),tst-protected1modb.so tst-protected1moda.so) +tst-protected1modb.so-no-z-defs = yes +# These tests fail with GCC versions prior to 5.1 and with some versions +# of binutils. See https://sourceware.org/bugzilla/show_bug.cgi?id=17709 +# and https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65248 for details. +# Perhaps in future we can make these XFAILs conditional on some detection +# of compiler/linker behavior/version. +test-xfail-tst-protected1a = yes +test-xfail-tst-protected1b = yes +endif +ifeq (yesyes,$(have-fpie)$(build-shared)) +modules-names += tst-piemod1 +tests += tst-pie1 tst-pie2 +tests-pie += tst-pie1 tst-pie2 +tests += vismain +tests-pie += vismain +CFLAGS-vismain.c = $(PIE-ccflag) +endif +modules-execstack-yes = tst-execstack-mod +extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) + +# filtmod1.so has a special rule +modules-names-nobuild := filtmod1 + +tests += $(tests-static) + +ifneq (no,$(multi-arch)) +tests-ifuncstatic := ifuncmain1static ifuncmain1picstatic \ + ifuncmain2static ifuncmain2picstatic \ + ifuncmain4static ifuncmain4picstatic \ + ifuncmain5static ifuncmain5picstatic \ + ifuncmain7static ifuncmain7picstatic +tests-static += $(tests-ifuncstatic) +tests-internal += $(tests-ifuncstatic) +ifeq (yes,$(build-shared)) +tests-internal += \ + ifuncmain1 ifuncmain1pic ifuncmain1vis ifuncmain1vispic \ + ifuncmain1staticpic \ + ifuncmain2 ifuncmain2pic ifuncmain3 ifuncmain4 \ + ifuncmain5 ifuncmain5pic ifuncmain5staticpic \ + ifuncmain7 ifuncmain7pic +ifunc-test-modules = ifuncdep1 ifuncdep1pic ifuncdep2 ifuncdep2pic \ + ifuncdep5 ifuncdep5pic +extra-test-objs += $(ifunc-test-modules:=.o) +test-internal-extras += $(ifunc-test-modules) +ifeq (yes,$(have-fpie)) +ifunc-pie-tests = ifuncmain1pie ifuncmain1vispie ifuncmain1staticpie \ + ifuncmain5pie ifuncmain6pie ifuncmain7pie +tests-internal += $(ifunc-pie-tests) +tests-pie += $(ifunc-pie-tests) +endif +modules-names += ifuncmod1 ifuncmod3 ifuncmod5 ifuncmod6 +endif +endif + +ifeq (yes,$(build-shared)) +ifeq ($(run-built-tests),yes) +tests-special += $(objpfx)tst-pathopt.out $(objpfx)tst-rtld-load-self.out +endif +tests-special += $(objpfx)check-textrel.out $(objpfx)check-execstack.out \ + $(objpfx)check-localplt.out +endif + +ifeq ($(run-built-tests),yes) +tests-special += $(objpfx)order-cmp.out $(objpfx)tst-array1-cmp.out \ + $(objpfx)tst-array1-static-cmp.out \ + $(objpfx)tst-array2-cmp.out $(objpfx)tst-array3-cmp.out \ + $(objpfx)tst-array4-cmp.out $(objpfx)tst-array5-cmp.out \ + $(objpfx)tst-array5-static-cmp.out $(objpfx)order2-cmp.out \ + $(objpfx)tst-initorder-cmp.out \ + $(objpfx)tst-initorder2-cmp.out $(objpfx)tst-unused-dep.out \ + $(objpfx)tst-unused-dep-cmp.out +endif + +check-abi: $(objpfx)check-abi-ld.out +tests-special += $(objpfx)check-abi-ld.out +update-abi: update-abi-ld +update-all-abi: update-all-abi-ld + +ifeq ($(have-glob-dat-reloc),yes) +tests += tst-prelink +ifeq ($(run-built-tests),yes) +tests-special += $(objpfx)tst-prelink-cmp.out +endif +endif + +# The test requires shared _and_ PIE because the executable +# unit test driver must be able to link with the shared object +# that is going to eventually go into an installed DSO. +ifeq (yesyes,$(have-fpie)$(build-shared)) +tests-internal += tst-_dl_addr_inside_object +tests-pie += tst-_dl_addr_inside_object +$(objpfx)tst-_dl_addr_inside_object: $(objpfx)dl-addr-obj.os +CFLAGS-tst-_dl_addr_inside_object.c += $(PIE-ccflag) +endif + +# By default tst-linkall-static should try to use crypt routines to test +# static libcrypt use. +CFLAGS-tst-linkall-static.c = -DUSE_CRYPT=1 +# However, if we are using NSS crypto and we don't have a static +# library, then we exclude the use of crypt functions in the test. +# We similarly exclude libcrypt.a from the static link (see below). +ifeq (yesno,$(nss-crypt)$(static-nss-crypt)) +CFLAGS-tst-linkall-static.c = -DUSE_CRYPT=0 +endif + +include ../Rules + +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)sotruss-lib.so: $(shlib-lds) + +$(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. + +# If the compiler can do SSP, build the mapfile with dummy __stack_chk_fail +# and __stack_chk_fail_local symbols defined, to prevent the real things +# being dragged into rtld even though rtld is never built with stack- +# protection. + +ifeq ($(have-ssp),yes) +dummy-stack-chk-fail := -Wl,--defsym='__stack_chk_fail=0' \ + -Wl,--defsym='__stack_chk_fail_local=0' +else +dummy-stack-chk-fail := +endif + +$(objpfx)librtld.map: $(objpfx)dl-allobjs.os $(common-objpfx)libc_pic.a + @-rm -f $@T + $(reloc-link) -o $@.o $(dummy-stack-chk-fail) \ + '-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) +# Link into a temporary file so that we don't touch $@ at all +# if the sanity check below fails. + $(LINK.o) -nostdlib -nostartfiles -shared -o $@.new \ + $(LDFLAGS-rtld) -Wl,-z,defs $(z-now-$(bind-now)) \ + $(filter-out $(map-file),$^) $(load-map-file) \ + -Wl,-soname=$(rtld-installed-name) \ + -Wl,-defsym=_begin=0 + $(call after-link,$@.new) + $(READELF) -s $@.new \ + | $(AWK) '($$7 ~ /^UND(|EF)$$/ && $$1 != "0:" && $$4 != "REGISTER") { print; p=1 } END { exit p != 0 }' + mv -f $@.new $@ + +ifeq (yes,$(build-shared)) +# interp.c exists just to get the runtime linker path into libc.so. +$(objpfx)interp.os: $(common-objpfx)runtime-linker.h +endif + +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 + $(make-link) +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_rtlddir)/$(rtld-installed-name): \ + $(inst_slibdir)/$(rtld-version-installed-name) \ + $(inst_slibdir)/libc-$(version).so + $(make-target-directory) + $(make-shlib-link) + +# Special target called by parent to install just the dynamic linker. +.PHONY: ldso_install +ldso_install: $(inst_rtlddir)/$(rtld-installed-name) +endif + + +ldd-rewrite = -e 's%@RTLD@%$(rtlddir)/$(rtld-installed-name)%g' \ + -e 's%@VERSION@%$(version)%g' \ + -e 's|@PKGVERSION@|$(PKGVERSION)|g' \ + -e 's|@REPORT_BUGS_TO@|$(REPORT_BUGS_TO)|g' \ + -e 's%@BASH@%$(BASH)%g' \ + -e 's%@TEXTDOMAINDIR@%$(localedir)%g' + +ifeq ($(ldd-rewrite-script),no) +define gen-ldd +LC_ALL=C sed $(ldd-rewrite) < $< > $@.new +endef +else +define gen-ldd +LC_ALL=C sed $(ldd-rewrite) < $< \ +| LC_ALL=C sed -f $(patsubst $(..)/%,/%,$(..)$(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)sln: $(sln-modules:%=$(objpfx)%.o) + +$(objpfx)ldconfig: $(ldconfig-modules:%=$(objpfx)%.o) + +SYSCONF-FLAGS := -D'SYSCONFDIR="$(sysconfdir)"' +CFLAGS-ldconfig.c = $(SYSCONF-FLAGS) -D'LIBDIR="$(libdir)"' \ + -D'SLIBDIR="$(slibdir)"' +libof-ldconfig = ldconfig +CFLAGS-dl-cache.c = $(SYSCONF-FLAGS) +CFLAGS-cache.c = $(SYSCONF-FLAGS) +CFLAGS-rtld.c = $(SYSCONF-FLAGS) + +cpp-srcs-left := $(all-rtld-routines:=.os) +lib := rtld +include $(patsubst %,$(..)libof-iterator.mk,$(cpp-srcs-left)) + +test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(strip $(modules-names)))) +generated += $(addsuffix .so,$(strip $(modules-names))) + +$(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)tst-tls19mod1.so: $(objpfx)tst-tls19mod2.so $(objpfx)tst-tls19mod3.so +$(objpfx)tst-tls19mod3.so: $(objpfx)ld.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) +$(objpfx)unload7mod1.so: $(libdl) +$(objpfx)unload7mod2.so: $(objpfx)unload7mod1.so +$(objpfx)unload8mod1.so: $(objpfx)unload8mod2.so +$(objpfx)unload8mod2.so: $(objpfx)unload8mod3.so +$(objpfx)unload8mod3.so: $(libdl) +$(objpfx)tst-initordera2.so: $(objpfx)tst-initordera1.so +$(objpfx)tst-initorderb2.so: $(objpfx)tst-initorderb1.so $(objpfx)tst-initordera2.so +$(objpfx)tst-initordera3.so: $(objpfx)tst-initorderb2.so $(objpfx)tst-initorderb1.so +$(objpfx)tst-initordera4.so: $(objpfx)tst-initordera3.so +$(objpfx)tst-initorder: $(objpfx)tst-initordera4.so $(objpfx)tst-initordera1.so $(objpfx)tst-initorderb2.so +$(objpfx)tst-null-argv: $(objpfx)tst-null-argv-lib.so +$(objpfx)tst-tlsalign: $(objpfx)tst-tlsalign-lib.so +$(objpfx)tst-nodelete-opened.out: $(objpfx)tst-nodelete-opened-lib.so +$(objpfx)tst-nodelete-opened: $(libdl) +$(objpfx)tst-noload: $(libdl) + +$(objpfx)tst-tlsalign-extern: $(objpfx)tst-tlsalign-vars.o +$(objpfx)tst-tlsalign-extern-static: $(objpfx)tst-tlsalign-vars.o + +tst-null-argv-ENV = LD_DEBUG=all LD_DEBUG_OUTPUT=$(objpfx)tst-null-argv.debug.out +LDFLAGS-nodel2mod3.so = $(no-as-needed) +LDFLAGS-reldepmod5.so = $(no-as-needed) +LDFLAGS-reldep6mod1.so = $(no-as-needed) +LDFLAGS-reldep6mod4.so = $(no-as-needed) +LDFLAGS-reldep8mod3.so = $(no-as-needed) +LDFLAGS-unload4mod1.so = $(no-as-needed) +LDFLAGS-unload4mod2.so = $(no-as-needed) +LDFLAGS-tst-initorder = $(no-as-needed) +LDFLAGS-tst-initordera2.so = $(no-as-needed) +LDFLAGS-tst-initordera3.so = $(no-as-needed) +LDFLAGS-tst-initordera4.so = $(no-as-needed) +LDFLAGS-tst-initorderb2.so = $(no-as-needed) +LDFLAGS-tst-tlsmod5.so = -nostdlib $(no-as-needed) +LDFLAGS-tst-tlsmod6.so = -nostdlib $(no-as-needed) + +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 +tst-tlsmod16b.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 +ifuncmod1.so-no-z-defs = yes +ifuncmod5.so-no-z-defs = yes +ifuncmod6.so-no-z-defs = yes +tst-auditmod9a.so-no-z-defs = yes +tst-auditmod9b.so-no-z-defs = yes +tst-nodelete-uniquemod.so-no-z-defs = yes +tst-nodelete-rtldmod.so-no-z-defs = yes +tst-nodelete-zmod.so-no-z-defs = yes +tst-nodelete2mod.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 + +$(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-cmp.out: $(objpfx)order.out + (echo "0123456789" | cmp $< -) > $@; \ + $(evaluate-test) + +$(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 $(libdl) +LDFLAGS-noload = -rdynamic $(no-as-needed) +$(objpfx)noload.out: $(objpfx)testobj5.so + +$(objpfx)noload-mem.out: $(objpfx)noload.out + $(common-objpfx)malloc/mtrace $(objpfx)noload.mtrace > $@; \ + $(evaluate-test) +noload-ENV = MALLOC_TRACE=$(objpfx)noload.mtrace + +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 + +# This does not link against libc. +CFLAGS-filtmod1.c = $(no-stack-protector) + +$(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) +LDFLAGS-next = $(no-as-needed) + +$(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) $< $(common-objpfx) '$(test-wrapper-env)' \ + '$(run-program-env)'; \ + $(evaluate-test) + +$(objpfx)tst-rtld-load-self.out: tst-rtld-load-self.sh $(objpfx)ld.so + $(SHELL) $^ '$(test-wrapper)' '$(test-wrapper-env)' > $@; \ + $(evaluate-test) + +$(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-tlsmod7.so + +$(objpfx)tst-tls11: $(objpfx)tst-tlsmod10.so $(objpfx)tst-tlsmod9.so + +$(objpfx)tst-tls12: $(objpfx)tst-tlsmod12.so $(objpfx)tst-tlsmod11.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 + + + +$(objpfx)tst-tls16: $(libdl) +$(objpfx)tst-tls16.out: $(objpfx)tst-tlsmod16a.so $(objpfx)tst-tlsmod16b.so + +$(objpfx)tst-tls17: $(libdl) +$(objpfx)tst-tls17.out: $(objpfx)tst-tlsmod17b.so +$(patsubst %,$(objpfx)%.os,$(tlsmod17a-modules)): $(objpfx)tst-tlsmod17a%.os: tst-tlsmod17a.c + $(compile-command.c) -DN=$* +$(patsubst %,$(objpfx)%.so,$(tlsmod17a-modules)): $(objpfx)tst-tlsmod17a%.so: $(objpfx)ld.so +$(objpfx)tst-tlsmod17b.so: $(patsubst %,$(objpfx)%.so,$(tlsmod17a-modules)) + +$(objpfx)tst-tls18: $(libdl) +$(objpfx)tst-tls18.out: $(patsubst %,$(objpfx)%.so,$(tlsmod18a-modules)) +$(patsubst %,$(objpfx)%.os,$(tlsmod18a-modules)): $(objpfx)tst-tlsmod18a%.os : tst-tlsmod18a.c + $(compile-command.c) -DN=$* +$(patsubst %,$(objpfx)%.so,$(tlsmod18a-modules)): $(objpfx)tst-tlsmod18a%.so: $(objpfx)ld.so + +$(objpfx)tst-tls19: $(libdl) +$(objpfx)tst-tls19.out: $(objpfx)tst-tls19mod1.so + +CFLAGS-tst-align.c = $(stack-align-test-flags) +CFLAGS-tst-align2.c = $(stack-align-test-flags) +CFLAGS-tst-alignmod.c = $(stack-align-test-flags) +CFLAGS-tst-alignmod2.c = $(stack-align-test-flags) +$(objpfx)tst-align: $(libdl) +$(objpfx)tst-align.out: $(objpfx)tst-alignmod.so +$(objpfx)tst-align2: $(objpfx)tst-alignmod2.so + +$(objpfx)unload3: $(libdl) +$(objpfx)unload3.out: $(objpfx)unload3mod1.so $(objpfx)unload3mod2.so \ + $(objpfx)unload3mod3.so $(objpfx)unload3mod4.so + +$(objpfx)unload4: $(libdl) +$(objpfx)unload4.out: $(objpfx)unload4mod1.so $(objpfx)unload4mod3.so + +$(objpfx)unload5: $(libdl) +$(objpfx)unload5.out: $(objpfx)unload3mod1.so $(objpfx)unload3mod2.so \ + $(objpfx)unload3mod3.so $(objpfx)unload3mod4.so + +$(objpfx)unload6: $(libdl) +$(objpfx)unload6.out: $(objpfx)unload6mod1.so $(objpfx)unload6mod2.so \ + $(objpfx)unload6mod3.so + +$(objpfx)unload7: $(libdl) +$(objpfx)unload7.out: $(objpfx)unload7mod1.so $(objpfx)unload7mod2.so +unload7-ENV = MALLOC_PERTURB_=85 + +$(objpfx)unload8: $(libdl) +$(objpfx)unload8.out: $(objpfx)unload8mod1.so $(objpfx)unload8mod1x.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 +CPPFLAGS-tst-execstack.c = -DUSE_PTHREADS=0 +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 +CFLAGS-tst-execstack-prog.c += -Wno-trampolines +CFLAGS-tst-execstack-mod.c += -Wno-trampolines +endif + +LDFLAGS-tst-array2 = $(no-as-needed) +LDFLAGS-tst-array5 = $(no-as-needed) + +$(objpfx)tst-array1-cmp.out: tst-array1.exp $(objpfx)tst-array1.out + cmp $^ > $@; \ + $(evaluate-test) + +$(objpfx)tst-array1-static-cmp.out: tst-array1.exp \ + $(objpfx)tst-array1-static.out + cmp $^ > $@; \ + $(evaluate-test) + +$(objpfx)tst-array2: $(objpfx)tst-array2dep.so +$(objpfx)tst-array2-cmp.out: tst-array2.exp $(objpfx)tst-array2.out + cmp $^ > $@; \ + $(evaluate-test) + +$(objpfx)tst-array3-cmp.out: tst-array1.exp $(objpfx)tst-array3.out + cmp $^ > $@; \ + $(evaluate-test) + +$(objpfx)tst-array4: $(libdl) +$(objpfx)tst-array4.out: $(objpfx)tst-array2dep.so +$(objpfx)tst-array4-cmp.out: tst-array4.exp $(objpfx)tst-array4.out + cmp $^ > $@; \ + $(evaluate-test) + +$(objpfx)tst-array5: $(objpfx)tst-array5dep.so +$(objpfx)tst-array5-cmp.out: tst-array5.exp $(objpfx)tst-array5.out + cmp $^ > $@; \ + $(evaluate-test) + +$(objpfx)tst-array5-static-cmp.out: tst-array5-static.exp \ + $(objpfx)tst-array5-static.out + cmp $^ > $@; \ + $(evaluate-test) + +CFLAGS-tst-pie1.c += $(pie-ccflag) +CFLAGS-tst-pie2.c += $(pie-ccflag) + +$(objpfx)tst-piemod1.so: $(libsupport) +$(objpfx)tst-pie1: $(objpfx)tst-piemod1.so + +ifeq (yes,$(build-shared)) +all-built-dso := $(common-objpfx)elf/ld.so $(common-objpfx)libc.so \ + $(filter-out $(common-objpfx)linkobj/libc.so, \ + $(sort $(wildcard $(addprefix $(common-objpfx), \ + */lib*.so \ + iconvdata/*.so)))) + +$(all-built-dso:=.dyn): %.dyn: % + @rm -f $@T + LC_ALL=C $(READELF) -W -d $< > $@T + test -s $@T + mv -f $@T $@ +common-generated += $(all-built-dso:$(common-objpfx)%=%.dyn) + +$(objpfx)check-textrel.out: $(..)scripts/check-textrel.awk \ + $(all-built-dso:=.dyn) + LC_ALL=C $(AWK) -f $^ > $@; \ + $(evaluate-test) +generated += check-textrel.out + +$(objpfx)execstack-default: $(first-word $(wildcard $(sysdirs:%=%/stackinfo.h))) + $(make-target-directory) + { echo '#include <elf.h>'; \ + echo '#include <stackinfo.h>'; \ + echo '#if (DEFAULT_STACK_PERMS & PF_X) == 0'; \ + echo '@@@execstack-no@@@'; \ + echo '#else'; \ + echo '@@@execstack-yes@@@'; \ + echo '#endif'; } | \ + $(CC) $(CFLAGS) $(CPPFLAGS) -E -x c-header - | \ + sed -n -e 's/^@@@\(.*\)@@@/\1/p' > $@T + mv -f $@T $@ +generated += execstack-default + +$(all-built-dso:=.phdr): %.phdr: % + @rm -f $@T + LC_ALL=C $(READELF) -W -l $< > $@T + test -s $@T + mv -f $@T $@ +common-generated += $(all-built-dso:$(common-objpfx)%=%.phdr) + +$(objpfx)check-execstack.out: $(..)scripts/check-execstack.awk \ + $(objpfx)execstack-default \ + $(all-built-dso:=.phdr) + LC_ALL=C $(AWK) -f $^ > $@; \ + $(evaluate-test) +generated += check-execstack.out + +$(objpfx)tst-dlmodcount: $(libdl) +$(objpfx)tst-dlmodcount.out: $(test-modules) + +$(all-built-dso:=.jmprel): %.jmprel: % Makefile + @rm -f $@T + LC_ALL=C $(READELF) -W -S -d -r $< > $@T + test -s $@T + mv -f $@T $@ +common-generated += $(all-built-dso:$(common-objpfx)%=%.jmprel) + +localplt-built-dso := $(addprefix $(common-objpfx),\ + libc.so \ + elf/ld.so \ + math/libm.so \ + rt/librt.so \ + dlfcn/libdl.so \ + resolv/libresolv.so \ + crypt/libcrypt.so \ + ) +ifeq ($(build-mathvec),yes) +localplt-built-dso += $(addprefix $(common-objpfx), mathvec/libmvec.so) +endif +ifeq ($(have-thread-library),yes) +localplt-built-dso += $(filter-out %_nonshared.a, $(shared-thread-library)) +endif + +vpath localplt.data $(+sysdep_dirs) + +$(objpfx)check-localplt.out: $(..)scripts/check-localplt.awk \ + $(..)scripts/localplt.awk \ + $(localplt-built-dso:=.jmprel) \ + localplt.data + LC_ALL=C $(AWK) -f $(filter-out $< %localplt.data,$^) | \ + LC_ALL=C $(AWK) -f $< $(filter %localplt.data,$^) - \ + > $@; \ + $(evaluate-test) +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: $(libdl) +$(objpfx)tst-audit2.out: $(objpfx)tst-auditmod1.so $(objpfx)tst-auditmod9b.so +# Prevent GCC-5 from translating a malloc/memset pair into calloc +CFLAGS-tst-audit2.c += -fno-builtin +tst-audit2-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + +$(objpfx)tst-audit9: $(libdl) +$(objpfx)tst-audit9.out: $(objpfx)tst-auditmod9a.so $(objpfx)tst-auditmod9b.so +tst-audit9-ENV = LD_AUDIT=$(objpfx)tst-auditmod9a.so + +$(objpfx)tst-audit8: $(libm) +$(objpfx)tst-audit8.out: $(objpfx)tst-auditmod1.so +tst-audit8-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)order2mod1.so $(objpfx)order2mod2.so +$(objpfx)order2-cmp.out: $(objpfx)order2.out + (echo "12345" | cmp $< -) > $@; \ + $(evaluate-test) +$(objpfx)order2mod1.so: $(objpfx)order2mod4.so +$(objpfx)order2mod4.so: $(objpfx)order2mod3.so +$(objpfx)order2mod2.so: $(objpfx)order2mod3.so +order2mod2.so-no-z-defs = yes +LDFLAGS-order2mod1.so = $(no-as-needed) +LDFLAGS-order2mod2.so = $(no-as-needed) + +tst-stackguard1-ARGS = --command "$(host-test-program-cmd) --child" +tst-stackguard1-static-ARGS = --command "$(objpfx)tst-stackguard1-static --child" + +tst-ptrguard1-ARGS = --command "$(host-test-program-cmd) --child" +# When built statically, the pointer guard interface uses +# __pointer_chk_guard_local. +CFLAGS-tst-ptrguard1-static.c = -DPTRGUARD_LOCAL +tst-ptrguard1-static-ARGS = --command "$(objpfx)tst-ptrguard1-static --child" + +$(objpfx)tst-leaks1: $(libdl) +$(objpfx)tst-leaks1-mem.out: $(objpfx)tst-leaks1.out + $(common-objpfx)malloc/mtrace $(objpfx)tst-leaks1.mtrace > $@; \ + $(evaluate-test) + +$(objpfx)tst-leaks1-static: $(common-objpfx)dlfcn/libdl.a +$(objpfx)tst-leaks1-static-mem.out: $(objpfx)tst-leaks1-static.out + $(common-objpfx)malloc/mtrace $(objpfx)tst-leaks1-static.mtrace > $@; \ + $(evaluate-test) + +tst-leaks1-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1.mtrace +tst-leaks1-static-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1-static.mtrace + +$(objpfx)tst-addr1: $(libdl) + +$(objpfx)tst-thrlock: $(libdl) $(shared-thread-library) +$(objpfx)tst-dlopen-aout: $(libdl) $(shared-thread-library) + +CFLAGS-ifuncmain1pic.c += $(pic-ccflag) +CFLAGS-ifuncmain1picstatic.c += $(pic-ccflag) +CFLAGS-ifuncmain1staticpic.c += $(pic-ccflag) +CFLAGS-ifuncdep1pic.c += $(pic-ccflag) +CFLAGS-ifuncmain1vispic.c += $(pic-ccflag) +CFLAGS-ifuncmain2pic.c += $(pic-ccflag) +CFLAGS-ifuncmain2picstatic.c += $(pic-ccflag) +CFLAGS-ifuncdep2pic.c += $(pic-ccflag) +CFLAGS-ifuncmain4picstatic.c += $(pic-ccflag) +CFLAGS-ifuncmain5pic.c += $(pic-ccflag) +CFLAGS-ifuncmain5picstatic.c += $(pic-ccflag) +CFLAGS-ifuncmain5staticpic.c += $(pic-ccflag) +CFLAGS-ifuncdep5pic.c += $(pic-ccflag) +CFLAGS-ifuncmain7pic.c += $(pic-ccflag) +CFLAGS-ifuncmain7picstatic.c += $(pic-ccflag) + +LDFLAGS-ifuncmain3 = -Wl,-export-dynamic + +CFLAGS-ifuncmain1pie.c += $(pie-ccflag) +CFLAGS-ifuncmain1vispie.c += $(pie-ccflag) +CFLAGS-ifuncmain1staticpie.c += $(pie-ccflag) +CFLAGS-ifuncmain5pie.c += $(pie-ccflag) +CFLAGS-ifuncmain6pie.c += $(pie-ccflag) +CFLAGS-ifuncmain7pie.c += $(pie-ccflag) + +$(objpfx)ifuncmain1pie: $(objpfx)ifuncmod1.so +$(objpfx)ifuncmain1staticpie: $(objpfx)ifuncdep1pic.o +$(objpfx)ifuncmain1vispie: $(objpfx)ifuncmod1.so +$(objpfx)ifuncmain5pie: $(objpfx)ifuncmod5.so +$(objpfx)ifuncmain6pie: $(objpfx)ifuncmod6.so + +$(objpfx)ifuncmain1: $(addprefix $(objpfx),ifuncmod1.so) +$(objpfx)ifuncmain1pic: $(addprefix $(objpfx),ifuncmod1.so) +$(objpfx)ifuncmain1staticpic: $(addprefix $(objpfx),ifuncdep1pic.o) +$(objpfx)ifuncmain1static: $(addprefix $(objpfx),ifuncdep1.o) +$(objpfx)ifuncmain1picstatic: $(addprefix $(objpfx),ifuncdep1pic.o) +$(objpfx)ifuncmain1vis: $(addprefix $(objpfx),ifuncmod1.so) +$(objpfx)ifuncmain1vispic: $(addprefix $(objpfx),ifuncmod1.so) +$(objpfx)ifuncmain2: $(addprefix $(objpfx),ifuncdep2.o) +$(objpfx)ifuncmain2pic: $(addprefix $(objpfx),ifuncdep2pic.o) +$(objpfx)ifuncmain2static: $(addprefix $(objpfx),ifuncdep2.o) +$(objpfx)ifuncmain2picstatic: $(addprefix $(objpfx),ifuncdep2pic.o) + +$(objpfx)ifuncmain3: $(libdl) +$(objpfx)ifuncmain3.out: $(objpfx)ifuncmod3.so + +$(objpfx)ifuncmain5: $(addprefix $(objpfx),ifuncmod5.so) +$(objpfx)ifuncmain5pic: $(addprefix $(objpfx),ifuncmod5.so) +$(objpfx)ifuncmain5static: $(addprefix $(objpfx),ifuncdep5.o) +$(objpfx)ifuncmain5staticpic: $(addprefix $(objpfx),ifuncdep5pic.o) +$(objpfx)ifuncmain5picstatic: $(addprefix $(objpfx),ifuncdep5pic.o) + +$(objpfx)tst-unique1: $(libdl) +$(objpfx)tst-unique1.out: $(objpfx)tst-unique1mod1.so \ + $(objpfx)tst-unique1mod2.so + +$(objpfx)tst-unique2: $(libdl) $(objpfx)tst-unique2mod1.so +$(objpfx)tst-unique2.out: $(objpfx)tst-unique2mod2.so + +$(objpfx)tst-unique3: $(libdl) $(objpfx)tst-unique3lib.so +$(objpfx)tst-unique3.out: $(objpfx)tst-unique3lib2.so + +$(objpfx)tst-unique4: $(objpfx)tst-unique4lib.so + +$(objpfx)tst-nodelete: $(libdl) +$(objpfx)tst-nodelete.out: $(objpfx)tst-nodelete-uniquemod.so \ + $(objpfx)tst-nodelete-rtldmod.so \ + $(objpfx)tst-nodelete-zmod.so + +LDFLAGS-tst-nodelete = -rdynamic +LDFLAGS-tst-nodelete-zmod.so = -Wl,--enable-new-dtags,-z,nodelete + +$(objpfx)tst-nodelete2: $(libdl) +$(objpfx)tst-nodelete2.out: $(objpfx)tst-nodelete2mod.so + +LDFLAGS-tst-nodelete2 = -rdynamic + +$(objpfx)tst-initorder-cmp.out: tst-initorder.exp $(objpfx)tst-initorder.out + cmp $^ > $@; \ + $(evaluate-test) + +$(objpfx)tst-initorder2: $(objpfx)tst-initorder2a.so $(objpfx)tst-initorder2d.so $(objpfx)tst-initorder2c.so +$(objpfx)tst-initorder2a.so: $(objpfx)tst-initorder2b.so +$(objpfx)tst-initorder2b.so: $(objpfx)tst-initorder2c.so +$(objpfx)tst-initorder2c.so: $(objpfx)tst-initorder2d.so +LDFLAGS-tst-initorder2 = $(no-as-needed) +LDFLAGS-tst-initorder2a.so = $(no-as-needed) +LDFLAGS-tst-initorder2b.so = $(no-as-needed) +LDFLAGS-tst-initorder2c.so = $(no-as-needed) +define o-iterator-doit +$(objpfx)tst-initorder2$o.os: tst-initorder2.c; \ +$$(compile-command.c) -DNAME=\"$o\" +endef +object-suffixes-left := a b c d +include $(o-iterator) + +$(objpfx)tst-initorder2-cmp.out: tst-initorder2.exp $(objpfx)tst-initorder2.out + cmp $^ > $@; \ + $(evaluate-test) + +$(objpfx)tst-relsort1: $(libdl) +$(objpfx)tst-relsort1mod1.so: $(libm) $(objpfx)tst-relsort1mod2.so +$(objpfx)tst-relsort1mod2.so: $(libm) +$(objpfx)tst-relsort1.out: $(objpfx)tst-relsort1mod1.so \ + $(objpfx)tst-relsort1mod2.so + +$(objpfx)tst-unused-dep.out: $(objpfx)testobj1.so + $(test-wrapper-env) \ + LD_TRACE_LOADED_OBJECTS=1 \ + LD_DEBUG=unused \ + LD_PRELOAD= \ + $(rtld-prefix) \ + $< > $@; \ + $(evaluate-test) + +$(objpfx)tst-unused-dep-cmp.out: $(objpfx)tst-unused-dep.out + cmp $< /dev/null > $@; \ + $(evaluate-test) + +$(objpfx)tst-audit11.out: $(objpfx)tst-auditmod11.so $(objpfx)tst-audit11mod1.so +$(objpfx)tst-audit11: $(libdl) +tst-audit11-ENV = LD_AUDIT=$(objpfx)tst-auditmod11.so +$(objpfx)tst-audit11mod1.so: $(objpfx)tst-audit11mod2.so +LDFLAGS-tst-audit11mod2.so = -Wl,--version-script=tst-audit11mod2.map,-soname,tst-audit11mod2.so + +$(objpfx)tst-audit12.out: $(objpfx)tst-auditmod12.so $(objpfx)tst-audit12mod1.so $(objpfx)tst-audit12mod3.so +$(objpfx)tst-audit12: $(libdl) +tst-audit12-ENV = LD_AUDIT=$(objpfx)tst-auditmod12.so +$(objpfx)tst-audit12mod1.so: $(objpfx)tst-audit12mod2.so +LDFLAGS-tst-audit12mod2.so = -Wl,--version-script=tst-audit12mod2.map + +# Override -z defs, so that we can reference an undefined symbol. +# Force lazy binding for the same reason. +LDFLAGS-tst-latepthreadmod.so = \ + -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all +# Do not optimize sibling calls as the test relies on a JMP_SLOT relocation for +# function this_function_is_not_defined. +CFLAGS-tst-latepthreadmod.c = -fno-optimize-sibling-calls +$(objpfx)tst-latepthreadmod.so: $(shared-thread-library) +$(objpfx)tst-latepthread: $(libdl) +$(objpfx)tst-latepthread.out: $(objpfx)tst-latepthreadmod.so + +# The test modules are parameterized by preprocessor macros. +$(patsubst %,$(objpfx)%.os,$(tst-tls-many-dynamic-modules)): \ + $(objpfx)tst-tls-manydynamic%mod.os : tst-tls-manydynamicmod.c + $(compile-command.c) \ + -DNAME=tls_global_$* -DSETTER=set_value_$* -DGETTER=get_value_$* +$(objpfx)tst-tls-manydynamic: $(libdl) $(shared-thread-library) +$(objpfx)tst-tls-manydynamic.out: \ + $(patsubst %,$(objpfx)%.so,$(tst-tls-many-dynamic-modules)) + +tst-prelink-ENV = LD_TRACE_PRELINKING=1 + +$(objpfx)tst-prelink-conflict.out: $(objpfx)tst-prelink.out + grep stdout $< | grep conflict | $(AWK) '{ print $$10, $$11 }' > $@ + +$(objpfx)tst-prelink-cmp.out: tst-prelink.exp \ + $(objpfx)tst-prelink-conflict.out + cmp $^ > $@; \ + $(evaluate-test) + +$(objpfx)tst-ldconfig-X.out : tst-ldconfig-X.sh $(objpfx)ldconfig + $(SHELL) $< '$(common-objpfx)' '$(test-wrapper-env)' \ + '$(run-program-env)' > $@; \ + $(evaluate-test) + +$(objpfx)tst-dlsym-error: $(libdl) + +# Test static linking of all the libraries we can possibly link +# together. Note that in some configurations this may be less than the +# complete list of libraries we build but we try to maxmimize this list. +$(objpfx)tst-linkall-static: \ + $(common-objpfx)math/libm.a \ + $(common-objpfx)resolv/libresolv.a \ + $(common-objpfx)dlfcn/libdl.a \ + $(common-objpfx)login/libutil.a \ + $(common-objpfx)rt/librt.a \ + $(common-objpfx)resolv/libanl.a \ + $(static-thread-library) + +# If we are using NSS crypto and we have the ability to link statically +# then we include libcrypt.a, otherwise we leave out libcrypt.a and +# link as much as we can into the tst-linkall-static test. This assumes +# that linking with libcrypt.a does everything required to include the +# static NSS crypto library. +ifeq (yesyes,$(nss-crypt)$(static-nss-crypt)) +$(objpfx)tst-linkall-static: \ + $(common-objpfx)crypt/libcrypt.a +endif +# If we are not using NSS crypto then we always have the ability to link +# with libcrypt.a. +ifeq (no,$(nss-crypt)) +$(objpfx)tst-linkall-static: \ + $(common-objpfx)crypt/libcrypt.a +endif + +# The application depends on the DSO, and the DSO loads the plugin. +# The plugin also depends on the DSO. This creates the circular +# dependency via dlopen that we're testing to make sure works. +$(objpfx)tst-nodelete-dlclose-dso.so: $(libdl) +$(objpfx)tst-nodelete-dlclose-plugin.so: $(objpfx)tst-nodelete-dlclose-dso.so +$(objpfx)tst-nodelete-dlclose: $(objpfx)tst-nodelete-dlclose-dso.so +$(objpfx)tst-nodelete-dlclose.out: $(objpfx)tst-nodelete-dlclose-dso.so \ + $(objpfx)tst-nodelete-dlclose-plugin.so + +tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096 \ + LD_HWCAP_MASK=0x1 +tst-env-setuid-tunables-ENV = \ + GLIBC_TUNABLES=glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096 diff --git a/REORG.TODO/elf/Versions b/REORG.TODO/elf/Versions new file mode 100644 index 0000000000..c59facdbd7 --- /dev/null +++ b/REORG.TODO/elf/Versions @@ -0,0 +1,77 @@ +libc { + GLIBC_2.0 { +%ifdef EXPORT_UNWIND_FIND_FDE + __deregister_frame_info; __register_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 + # Needed for SHLIB_COMPAT calls using this version. + GLIBC_2.2.5 { + } + GCC_3.0 { + __deregister_frame_info_bases; __register_frame_info_bases; + __register_frame_info_table_bases; _Unwind_Find_FDE; + } +%endif + GLIBC_PRIVATE { + # functions used in other libraries + _dl_addr; + _dl_open_hook; + _dl_sym; _dl_vsym; + __libc_dlclose; __libc_dlopen_mode; __libc_dlsym; + + # Internal error handling support. Interposes the functions in ld.so. + _dl_signal_error; _dl_catch_error; + } +} + +ld { + GLIBC_2.0 { + # Functions which are interposed from libc.so. + calloc; free; malloc; realloc; + + _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_allocate_tls; _dl_allocate_tls_init; + _dl_argv; _dl_find_dso_for_object; _dl_get_tls_static_info; + _dl_deallocate_tls; _dl_make_stack_executable; _dl_out_of_memory; + _dl_rtld_di_serinfo; _dl_starting_up; + _rtld_global; _rtld_global_ro; + + # Only here for gdb while a better method is developed. + _dl_debug_state; + + # Pointer protection. + __pointer_chk_guard; + + # Internal error handling support. Interposed by libc.so. + _dl_signal_error; _dl_catch_error; + + # Set value of a tunable. + __tunable_get_val; + } +} diff --git a/REORG.TODO/elf/cache.c b/REORG.TODO/elf/cache.c new file mode 100644 index 0000000000..a76f89281d --- /dev/null +++ b/REORG.TODO/elf/cache.c @@ -0,0 +1,829 @@ +/* Copyright (C) 1999-2017 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 as published + by the Free Software Foundation; version 2 of the License, or + (at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <error.h> +#include <dirent.h> +#include <inttypes.h> +#include <libgen.h> +#include <libintl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdint.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); + break; + case FLAG_X8664_LIBX32: + fputs (",x32", stdout); + break; + case FLAG_ARM_LIBHF: + fputs (",hard-float", stdout); + break; + case FLAG_AARCH64_LIB64: + fputs (",AArch64", stdout); + break; + /* Uses the ARM soft-float ABI. */ + case FLAG_ARM_LIBSF: + fputs (",soft-float", stdout); + break; + case FLAG_MIPS_LIB32_NAN2008: + fputs (",nan2008", stdout); + break; + case FLAG_MIPS64_LIBN32_NAN2008: + fputs (",N32,nan2008", stdout); + break; + case FLAG_MIPS64_LIBN64_NAN2008: + fputs (",64bit,nan2008", stdout); + break; + 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) +{ + int fd = open (cache_name, O_RDONLY); + if (fd < 0) + error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), cache_name); + + struct stat64 st; + if (fstat64 (fd, &st) < 0 + /* No need to map the file if it is empty. */ + || st.st_size == 0) + { + close (fd); + return; + } + + struct cache_file *cache + = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (cache == MAP_FAILED) + error (EXIT_FAILURE, errno, _("mmap of cache file failed.\n")); + + size_t cache_size = st.st_size; + if (cache_size < sizeof (struct cache_file)) + error (EXIT_FAILURE, 0, _("File is not a cache file.\n")); + + struct cache_file_new *cache_new = NULL; + const char *cache_data; + int format = 0; + + 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 (unsigned int 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 (unsigned int 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) +{ + /* We need to swap entries here to get the correct sort order. */ + int 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) +{ + /* 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. */ + size_t total_strlen = 0; + struct cache_entry *entry; + /* Number of cache entries. */ + int cache_entry_count = 0; + /* Number of normal cache entries. */ + int cache_entry_old_count = 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. */ + struct cache_file *file_entries = NULL; + size_t file_entries_size = 0; + + 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 = 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; + } + + struct cache_file_new *file_entries_new = NULL; + size_t file_entries_new_size = 0; + + 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 = 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 for alignment of cache_file_new. */ + size_t 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. */ + unsigned int str_offset; + if (opt_format != 0) + str_offset = file_entries_new_size; + else + str_offset = 0; + + /* An array for all strings. */ + char *strings = xmalloc (total_strlen); + char *str = strings; + int idx_old; + int idx_new; + + 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 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; + } + + size_t len = strlen (entry->lib) + 1; + str = mempcpy (str, entry->lib, len); + str_offset += len; + /* 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) + 1; + str = mempcpy (str, entry->path, len); + str_offset += len; + /* 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. */ + char *temp_name = xmalloc (strlen (cache_name) + 2); + sprintf (temp_name, "%s~", cache_name); + + /* Create file. */ + int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, + 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 + || close (fd)) + error (EXIT_FAILURE, errno, _("Writing of cache data failed")); + + /* 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; + 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) +{ + size_t liblen = strlen (lib) + 1; + size_t len = liblen + strlen (path) + 1; + struct cache_entry *new_entry + = xmalloc (sizeof (struct cache_entry) + liblen + len); + + new_entry->lib = memcpy ((char *) (new_entry + 1), lib, liblen); + new_entry->path = new_entry->lib + liblen; + snprintf (new_entry->path, len, "%s/%s", path, lib); + 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 (size_t 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. */ + struct cache_entry *ptr = entries; + struct cache_entry *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; + } +} + + +/* Auxiliary cache. */ + +struct aux_cache_entry_id +{ + uint64_t ino; + uint64_t ctime; + uint64_t size; + uint64_t dev; +}; + +struct aux_cache_entry +{ + struct aux_cache_entry_id id; + int flags; + unsigned int osversion; + int used; + char *soname; + struct aux_cache_entry *next; +}; + +#define AUX_CACHEMAGIC "glibc-ld.so.auxcache-1.0" + +struct aux_cache_file_entry +{ + struct aux_cache_entry_id id; /* Unique id of entry. */ + int32_t flags; /* This is 1 for an ELF library. */ + uint32_t soname; /* String table indice. */ + uint32_t osversion; /* Required OS version. */ + int32_t pad; +}; + +/* ldconfig maintains an auxiliary cache file that allows + only reading those libraries that have changed since the last iteration. + For this for each library some information is cached in the auxiliary + cache. */ +struct aux_cache_file +{ + char magic[sizeof AUX_CACHEMAGIC - 1]; + uint32_t nlibs; /* Number of entries. */ + uint32_t len_strings; /* Size of string table. */ + struct aux_cache_file_entry libs[0]; /* Entries describing libraries. */ + /* After this the string table of size len_strings is found. */ +}; + +static const unsigned int primes[] = +{ + 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, + 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, + 67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647 +}; + +static size_t aux_hash_size; +static struct aux_cache_entry **aux_hash; + +/* Simplistic hash function for aux_cache_entry_id. */ +static unsigned int +aux_cache_entry_id_hash (struct aux_cache_entry_id *id) +{ + uint64_t ret = ((id->ino * 11 + id->ctime) * 11 + id->size) * 11 + id->dev; + return ret ^ (ret >> 32); +} + +static size_t nextprime (size_t x) +{ + for (unsigned int i = 0; i < sizeof (primes) / sizeof (primes[0]); ++i) + if (primes[i] >= x) + return primes[i]; + return x; +} + +void +init_aux_cache (void) +{ + aux_hash_size = primes[3]; + aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *)); +} + +int +search_aux_cache (struct stat64 *stat_buf, int *flags, + unsigned int *osversion, char **soname) +{ + struct aux_cache_entry_id id; + id.ino = (uint64_t) stat_buf->st_ino; + id.ctime = (uint64_t) stat_buf->st_ctime; + id.size = (uint64_t) stat_buf->st_size; + id.dev = (uint64_t) stat_buf->st_dev; + + unsigned int hash = aux_cache_entry_id_hash (&id); + struct aux_cache_entry *entry; + for (entry = aux_hash[hash % aux_hash_size]; entry; entry = entry->next) + if (id.ino == entry->id.ino + && id.ctime == entry->id.ctime + && id.size == entry->id.size + && id.dev == entry->id.dev) + { + *flags = entry->flags; + *osversion = entry->osversion; + if (entry->soname != NULL) + *soname = xstrdup (entry->soname); + else + *soname = NULL; + entry->used = 1; + return 1; + } + + return 0; +} + +static void +insert_to_aux_cache (struct aux_cache_entry_id *id, int flags, + unsigned int osversion, const char *soname, int used) +{ + size_t hash = aux_cache_entry_id_hash (id) % aux_hash_size; + struct aux_cache_entry *entry; + for (entry = aux_hash[hash]; entry; entry = entry->next) + if (id->ino == entry->id.ino + && id->ctime == entry->id.ctime + && id->size == entry->id.size + && id->dev == entry->id.dev) + abort (); + + size_t len = soname ? strlen (soname) + 1 : 0; + entry = xmalloc (sizeof (struct aux_cache_entry) + len); + entry->id = *id; + entry->flags = flags; + entry->osversion = osversion; + entry->used = used; + if (soname != NULL) + entry->soname = memcpy ((char *) (entry + 1), soname, len); + else + entry->soname = NULL; + entry->next = aux_hash[hash]; + aux_hash[hash] = entry; +} + +void +add_to_aux_cache (struct stat64 *stat_buf, int flags, + unsigned int osversion, const char *soname) +{ + struct aux_cache_entry_id id; + id.ino = (uint64_t) stat_buf->st_ino; + id.ctime = (uint64_t) stat_buf->st_ctime; + id.size = (uint64_t) stat_buf->st_size; + id.dev = (uint64_t) stat_buf->st_dev; + insert_to_aux_cache (&id, flags, osversion, soname, 1); +} + +/* Load auxiliary cache to search for unchanged entries. */ +void +load_aux_cache (const char *aux_cache_name) +{ + int fd = open (aux_cache_name, O_RDONLY); + if (fd < 0) + { + init_aux_cache (); + return; + } + + struct stat64 st; + if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (struct aux_cache_file)) + { + close (fd); + init_aux_cache (); + return; + } + + size_t aux_cache_size = st.st_size; + struct aux_cache_file *aux_cache + = mmap (NULL, aux_cache_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (aux_cache == MAP_FAILED + || aux_cache_size < sizeof (struct aux_cache_file) + || memcmp (aux_cache->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1) + || aux_cache_size != (sizeof(struct aux_cache_file) + + aux_cache->nlibs * sizeof(struct aux_cache_file_entry) + + aux_cache->len_strings)) + { + close (fd); + init_aux_cache (); + return; + } + + aux_hash_size = nextprime (aux_cache->nlibs); + aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *)); + + const char *aux_cache_data + = (const char *) &aux_cache->libs[aux_cache->nlibs]; + for (unsigned int i = 0; i < aux_cache->nlibs; ++i) + insert_to_aux_cache (&aux_cache->libs[i].id, + aux_cache->libs[i].flags, + aux_cache->libs[i].osversion, + aux_cache->libs[i].soname == 0 + ? NULL : aux_cache_data + aux_cache->libs[i].soname, + 0); + + munmap (aux_cache, aux_cache_size); + close (fd); +} + +/* Save the contents of the auxiliary cache. */ +void +save_aux_cache (const char *aux_cache_name) +{ + /* Count the length of all sonames. We start with empty string. */ + size_t total_strlen = 1; + /* Number of cache entries. */ + int cache_entry_count = 0; + + for (size_t i = 0; i < aux_hash_size; ++i) + for (struct aux_cache_entry *entry = aux_hash[i]; + entry != NULL; entry = entry->next) + if (entry->used) + { + ++cache_entry_count; + if (entry->soname != NULL) + total_strlen += strlen (entry->soname) + 1; + } + + /* Auxiliary cache. */ + size_t file_entries_size + = sizeof (struct aux_cache_file) + + cache_entry_count * sizeof (struct aux_cache_file_entry); + struct aux_cache_file *file_entries + = xmalloc (file_entries_size + total_strlen); + + /* Fill in the header of the auxiliary cache. */ + memset (file_entries, '\0', sizeof (struct aux_cache_file)); + memcpy (file_entries->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1); + + file_entries->nlibs = cache_entry_count; + file_entries->len_strings = total_strlen; + + /* Initial String offset for auxiliary cache is always after the + special empty string. */ + unsigned int str_offset = 1; + + /* An array for all strings. */ + char *str = (char *) file_entries + file_entries_size; + *str++ = '\0'; + + size_t idx = 0; + for (size_t i = 0; i < aux_hash_size; ++i) + for (struct aux_cache_entry *entry = aux_hash[i]; + entry != NULL; entry = entry->next) + if (entry->used) + { + file_entries->libs[idx].id = entry->id; + file_entries->libs[idx].flags = entry->flags; + if (entry->soname == NULL) + file_entries->libs[idx].soname = 0; + else + { + file_entries->libs[idx].soname = str_offset; + + size_t len = strlen (entry->soname) + 1; + str = mempcpy (str, entry->soname, len); + str_offset += len; + } + file_entries->libs[idx].osversion = entry->osversion; + file_entries->libs[idx++].pad = 0; + } + + /* Write out auxiliary cache file. */ + /* Write auxiliary cache first to a temporary file and rename it later. */ + + char *temp_name = xmalloc (strlen (aux_cache_name) + 2); + sprintf (temp_name, "%s~", aux_cache_name); + + /* Check that directory exists and create if needed. */ + char *dir = strdupa (aux_cache_name); + dir = dirname (dir); + + struct stat64 st; + if (stat64 (dir, &st) < 0) + { + if (mkdir (dir, 0700) < 0) + goto out_fail; + } + + /* Create file. */ + int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, + S_IRUSR|S_IWUSR); + if (fd < 0) + goto out_fail; + + if (write (fd, file_entries, file_entries_size + total_strlen) + != (ssize_t) (file_entries_size + total_strlen) + || close (fd)) + { + unlink (temp_name); + goto out_fail; + } + + /* Move temporary to its final location. */ + if (rename (temp_name, aux_cache_name)) + unlink (temp_name); + +out_fail: + /* Free allocated memory. */ + free (temp_name); + free (file_entries); +} diff --git a/REORG.TODO/elf/chroot_canon.c b/REORG.TODO/elf/chroot_canon.c new file mode 100644 index 0000000000..78cd6f4a5e --- /dev/null +++ b/REORG.TODO/elf/chroot_canon.c @@ -0,0 +1,177 @@ +/* Return the canonical absolute name of a given file inside chroot. + Copyright (C) 1996-2017 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 as published + by the Free Software Foundation; version 2 of the License, or + (at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. */ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <sys/stat.h> +#include <errno.h> +#include <stddef.h> +#include <stdint.h> + +#include <eloop-threshold.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 = xmalloc (chroot_len + PATH_MAX); + + 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; + + /* 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 *) xrealloc (rpath, new_size); + 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 > __eloop_threshold ()) + { + __set_errno (ELOOP); + goto error; + } + + ssize_t n = readlink (rpath, buf, PATH_MAX - 1); + 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 (len >= PATH_MAX - n) + { + __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/REORG.TODO/elf/circleload1.c b/REORG.TODO/elf/circleload1.c new file mode 100644 index 0000000000..990ff84a84 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/circlemod1.c b/REORG.TODO/elf/circlemod1.c new file mode 100644 index 0000000000..933ccd3c02 --- /dev/null +++ b/REORG.TODO/elf/circlemod1.c @@ -0,0 +1,7 @@ +extern int circlemod2 (void); + +int +circlemod1 (void) +{ + return circlemod2 (); +} diff --git a/REORG.TODO/elf/circlemod1a.c b/REORG.TODO/elf/circlemod1a.c new file mode 100644 index 0000000000..45f229136d --- /dev/null +++ b/REORG.TODO/elf/circlemod1a.c @@ -0,0 +1 @@ +#include "circlemod1.c" diff --git a/REORG.TODO/elf/circlemod2.c b/REORG.TODO/elf/circlemod2.c new file mode 100644 index 0000000000..ed8c1175fb --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/circlemod2a.c b/REORG.TODO/elf/circlemod2a.c new file mode 100644 index 0000000000..dc6410b28b --- /dev/null +++ b/REORG.TODO/elf/circlemod2a.c @@ -0,0 +1,7 @@ +extern int circlemod3 (void); + +int +circlemod2 (void) +{ + return circlemod3 (); +} diff --git a/REORG.TODO/elf/circlemod3.c b/REORG.TODO/elf/circlemod3.c new file mode 100644 index 0000000000..8d16fe682f --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/circlemod3a.c b/REORG.TODO/elf/circlemod3a.c new file mode 100644 index 0000000000..f1b166ef84 --- /dev/null +++ b/REORG.TODO/elf/circlemod3a.c @@ -0,0 +1 @@ +#include "circlemod3.c" diff --git a/REORG.TODO/elf/constload1.c b/REORG.TODO/elf/constload1.c new file mode 100644 index 0000000000..7381beea88 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/constload2.c b/REORG.TODO/elf/constload2.c new file mode 100644 index 0000000000..bf1bf182f3 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/constload3.c b/REORG.TODO/elf/constload3.c new file mode 100644 index 0000000000..9c37620bba --- /dev/null +++ b/REORG.TODO/elf/constload3.c @@ -0,0 +1,8 @@ +extern int baz (void); +extern int bar (void); + +int +bar (void) +{ + return -21 + baz (); +} diff --git a/REORG.TODO/elf/dblload.c b/REORG.TODO/elf/dblload.c new file mode 100644 index 0000000000..52389a60ce --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/dblloadmod1.c b/REORG.TODO/elf/dblloadmod1.c new file mode 100644 index 0000000000..ecec29ec63 --- /dev/null +++ b/REORG.TODO/elf/dblloadmod1.c @@ -0,0 +1,8 @@ +extern int bar (void); +extern int foo (void); + +int +foo (void) +{ + return 10 + bar (); +} diff --git a/REORG.TODO/elf/dblloadmod2.c b/REORG.TODO/elf/dblloadmod2.c new file mode 100644 index 0000000000..3e20aa941b --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/dblloadmod3.c b/REORG.TODO/elf/dblloadmod3.c new file mode 100644 index 0000000000..80ac3a6375 --- /dev/null +++ b/REORG.TODO/elf/dblloadmod3.c @@ -0,0 +1,8 @@ +extern int bar (void); +extern int baz (void); + +int +bar (void) +{ + return 32 + baz (); +} diff --git a/REORG.TODO/elf/dblunload.c b/REORG.TODO/elf/dblunload.c new file mode 100644 index 0000000000..ab0b2a5e9e --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/dep1.c b/REORG.TODO/elf/dep1.c new file mode 100644 index 0000000000..7ef47adb43 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/dep2.c b/REORG.TODO/elf/dep2.c new file mode 100644 index 0000000000..749036a4ec --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/dep3.c b/REORG.TODO/elf/dep3.c new file mode 100644 index 0000000000..3df6282009 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/dep4.c b/REORG.TODO/elf/dep4.c new file mode 100644 index 0000000000..c496d6f531 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/dl-addr-obj.c b/REORG.TODO/elf/dl-addr-obj.c new file mode 100644 index 0000000000..62aa630ce5 --- /dev/null +++ b/REORG.TODO/elf/dl-addr-obj.c @@ -0,0 +1,75 @@ +/* Determine if address is inside object load segments. + Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <link.h> +#include <elf.h> + +/* Return non-zero if ADDR lies within one of L's loadable segments. + We have three cases we care about. + + Case 1: addr is above a segment. + +==================+<- l_map_end + | |<- addr + |------------------|<- l_addr + p_vaddr + p_memsz + | | + | | + |------------------|<- l_addr + p_vaddr + |------------------|<- l_addr + | | + +==================+<- l_map_start + + Case 2: addr is within a segments. + +==================+<- l_map_end + | | + |------------------|<- l_addr + p_vaddr + p_memsz + | |<- addr + | | + |------------------|<- l_addr + p_vaddr + |------------------|<- l_addr + | | + +==================+<- l_map_start + + Case 3: addr is below a segments. + +==================+<- l_map_end + | | + |------------------|<- l_addr + p_vaddr + p_memsz + | | + | | + |------------------|<- l_addr + p_vaddr + |------------------|<- l_addr + | |<- addr + +==================+<- l_map_start + + All the arithmetic is unsigned and we shift all the values down by + l_addr + p_vaddr and then compare the normalized addr to the range + of interest i.e. 0 <= addr < p_memsz. + +*/ +int +internal_function +_dl_addr_inside_object (struct link_map *l, const ElfW(Addr) addr) +{ + int n = l->l_phnum; + const ElfW(Addr) reladdr = addr - l->l_addr; + + while (--n >= 0) + if (l->l_phdr[n].p_type == PT_LOAD + && reladdr - l->l_phdr[n].p_vaddr < l->l_phdr[n].p_memsz) + return 1; + return 0; +} diff --git a/REORG.TODO/elf/dl-addr.c b/REORG.TODO/elf/dl-addr.c new file mode 100644 index 0000000000..1fac63d1a9 --- /dev/null +++ b/REORG.TODO/elf/dl-addr.c @@ -0,0 +1,146 @@ +/* Locate the shared object symbol nearest a given address. + Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <dlfcn.h> +#include <stddef.h> +#include <ldsodefs.h> + + +static inline void +__attribute ((always_inline)) +determine_info (const ElfW(Addr) addr, struct link_map *match, Dl_info *info, + struct link_map **mapp, const ElfW(Sym) **symbolp) +{ + /* 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 + || symtab[symndx].st_value != 0) + && ELFW(ST_TYPE) (symtab[symndx].st_info) != STT_TLS + && 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) + && __glibc_likely (!dl_symbol_visibility_binds_local_p (symtab)) + && ELFW(ST_TYPE) (symtab->st_info) != STT_TLS + && (symtab->st_shndx != SHN_UNDEF + || symtab->st_value != 0) + && DL_ADDR_SYM_MATCH (match, symtab, matchsym, addr) + && symtab->st_name < strtabsize) + matchsym = (ElfW(Sym) *) symtab; + } + + if (mapp) + *mapp = match; + if (symbolp) + *symbolp = matchsym; + + if (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; + } +} + + +int +internal_function +_dl_addr (const void *address, Dl_info *info, + struct link_map **mapp, const ElfW(Sym) **symbolp) +{ + const ElfW(Addr) addr = DL_LOOKUP_ADDRESS (address); + int result = 0; + + /* Protect against concurrent loads and unloads. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + struct link_map *l = _dl_find_dso_for_object (addr); + + if (l) + { + determine_info (addr, l, info, mapp, symbolp); + result = 1; + } + + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + return result; +} +libc_hidden_def (_dl_addr) diff --git a/REORG.TODO/elf/dl-brk.c b/REORG.TODO/elf/dl-brk.c new file mode 100644 index 0000000000..c37cdfec33 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/dl-cache.c b/REORG.TODO/elf/dl-cache.c new file mode 100644 index 0000000000..e9632da0b3 --- /dev/null +++ b/REORG.TODO/elf/dl-cache.c @@ -0,0 +1,325 @@ +/* Support for reading /etc/ld.so.cache files written by Linux ldconfig. + Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <unistd.h> +#include <ldsodefs.h> +#include <sys/mman.h> +#include <dl-cache.h> +#include <dl-procinfo.h> +#include <stdint.h> +#include <_itoa.h> +#include <dl-hwcaps.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 (__glibc_unlikely (cmpres == 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. The cache is loaded if it was not already. If loading + the cache previously failed there will be no more attempts to load it. + The caller is responsible for freeing the returned string. The ld.so.cache + may be unmapped at any time by a completing recursive dlopen and + this function must take care that it does not return references to + any data in the mapping. */ +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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) + _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; + + uint64_t hwcap_mask = GET_HWCAP_MASK(); + +#define _DL_HWCAP_TLS_MASK (1LL << 63) + uint64_t hwcap_exclude = ~((GLRO(dl_hwcap) & hwcap_mask) + | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK); + + /* Only accept hwcap if it's for the right platform. */ +#define HWCAP_CHECK \ + if (lib->hwcap & hwcap_exclude) \ + continue; \ + if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion)) \ + continue; \ + if (_DL_PLATFORMS_COUNT \ + && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0 \ + && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform) \ + continue + 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); + + if (best == NULL) + return NULL; + + /* The double copy is *required* since malloc may be interposed + and call dlopen itself whose completion would unmap the data + we are accessing. Therefore we must make the copy of the + mapping data without using malloc. */ + char *temp; + temp = alloca (strlen (best) + 1); + strcpy (temp, best); + return __strdup (temp); +} + +#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/REORG.TODO/elf/dl-caller.c b/REORG.TODO/elf/dl-caller.c new file mode 100644 index 0000000000..b4c5335baa --- /dev/null +++ b/REORG.TODO/elf/dl-caller.c @@ -0,0 +1,86 @@ +/* Check whether caller comes from the right place. + Copyright (C) 2004-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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 < GL(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/REORG.TODO/elf/dl-close.c b/REORG.TODO/elf/dl-close.c new file mode 100644 index 0000000000..2b46b7cf8b --- /dev/null +++ b/REORG.TODO/elf/dl-close.c @@ -0,0 +1,843 @@ +/* Close a shared object opened by `_dl_open'. + Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <dlfcn.h> +#include <errno.h> +#include <libintl.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <libc-lock.h> +#include <ldsodefs.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sysdep-cancel.h> +#include <tls.h> +#include <stap-probe.h> + +#include <dl-unmap-segments.h> + + +/* Type of the constructor functions. */ +typedef void (*fini_t) (void); + + +/* Special l_idx value used to indicate which objects remain loaded. */ +#define IDX_STILL_USED -1 + + +/* 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 (__glibc_likely (old_map != NULL)) + { + 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; +} + + +void +_dl_close_worker (struct link_map *map, bool force) +{ + /* 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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("\nclosing file=%s; direct_opencount=%u\n", + map->l_name, map->l_direct_opencount); + + return; + } + + Lmid_t nsid = map->l_ns; + struct link_namespaces *ns = &GL(dl_ns)[nsid]; + + retry: + dl_close_state = pending; + + bool any_tls = false; + const unsigned int nloaded = ns->_ns_nloaded; + char used[nloaded]; + char done[nloaded]; + struct link_map *maps[nloaded]; + + /* Clear DF_1_NODELETE to force object deletion. We don't need to touch + l_tls_dtor_count because forced object deletion only happens when an + error occurs during object load. Destructor registration for TLS + non-POD objects should not have happened till then for this + object. */ + if (force) + map->l_flags_1 &= ~DF_1_NODELETE; + + /* Run over the list and assign indexes to the link maps and enter + them into the MAPS array. */ + int idx = 0; + for (struct link_map *l = ns->_ns_loaded; l != NULL; l = l->l_next) + { + 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 + /* See CONCURRENCY NOTES in cxa_thread_atexit_impl.c to know why + acquire is sufficient and correct. */ + && atomic_load_acquire (&l->l_tls_dtor_count) == 0 + && !used[done_index]) + continue; + + /* We need this object and we handle it now. */ + done[done_index] = 1; + used[done_index] = 1; + /* Signal the object is still needed. */ + l->l_idx = IDX_STILL_USED; + + /* Mark all dependencies as used. */ + if (l->l_initfini != NULL) + { + /* We are always the zeroth entry, and since we don't include + ourselves in the dependency analysis start at 1. */ + struct link_map **lp = &l->l_initfini[1]; + while (*lp != NULL) + { + if ((*lp)->l_idx != IDX_STILL_USED) + { + assert ((*lp)->l_idx >= 0 && (*lp)->l_idx < nloaded); + + if (!used[(*lp)->l_idx]) + { + used[(*lp)->l_idx] = 1; + /* If we marked a new object as used, and we've + already processed it, then we need to go back + and process again from that point forward to + ensure we keep all of its dependencies also. */ + 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_reldeps->act; ++j) + { + struct link_map *jmap = l->l_reldeps->list[j]; + + if (jmap->l_idx != IDX_STILL_USED) + { + 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 (maps, nloaded, used, nsid); + + /* Call all termination functions at once. */ +#ifdef SHARED + bool do_audit = GLRO(dl_naudit) > 0 && !ns->_ns_loaded->l_auditing; +#endif + bool unload_any = false; + bool scope_mem_left = false; + unsigned int unload_global = 0; + unsigned int first_loaded = ~0; + for (unsigned int i = 0; i < nloaded; ++i) + { + struct link_map *imap = maps[i]; + + /* All elements must be in the same namespace. */ + assert (imap->l_ns == nsid); + + 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, nsid); + + 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) + DL_CALL_DT_FINI (imap, ((void *) imap->l_addr + + imap->l_info[DT_FINI]->d_un.d_ptr)); + } + +#ifdef SHARED + /* Auditing checkpoint: we remove an object. */ + if (__glibc_unlikely (do_audit)) + { + 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; + + if (imap->l_global) + ++unload_global; + + /* 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) + { + struct r_scope_elem *new_list = NULL; + + if (imap->l_searchlist.r_list == NULL && imap->l_initfini != NULL) + { + /* The object is still used. But one of the objects we are + unloading right now is responsible for loading it. If + the current object does not have it's own scope yet we + have to create one. This has to be done before running + the finalizers. + + To do this count the number of dependencies. */ + unsigned int cnt; + for (cnt = 1; imap->l_initfini[cnt] != NULL; ++cnt) + ; + + /* We simply reuse the l_initfini list. */ + imap->l_searchlist.r_list = &imap->l_initfini[cnt + 1]; + imap->l_searchlist.r_nlist = cnt; + + new_list = &imap->l_searchlist; + } + + /* Count the number of scopes which remain after the unload. + When we add the local search list count it. Always add + one for the terminating NULL pointer. */ + size_t remain = (new_list != NULL) + 1; + bool removed_any = false; + for (size_t cnt = 0; imap->l_scope[cnt] != NULL; ++cnt) + /* This relies on l_scope[] entries being always set either + to its own l_symbolic_searchlist address, or some map's + l_searchlist address. */ + if (imap->l_scope[cnt] != &imap->l_symbolic_searchlist) + { + struct link_map *tmap = (struct link_map *) + ((char *) imap->l_scope[cnt] + - offsetof (struct link_map, l_searchlist)); + assert (tmap->l_ns == nsid); + if (tmap->l_idx == IDX_STILL_USED) + ++remain; + else + removed_any = true; + } + else + ++remain; + + if (removed_any) + { + /* Always allocate a new array for the scope. This is + necessary since we must be able to determine the last + user of the current array. If possible use the link map's + memory. */ + size_t new_size; + struct r_scope_elem **newp; + +#define SCOPE_ELEMS(imap) \ + (sizeof (imap->l_scope_mem) / sizeof (imap->l_scope_mem[0])) + + if (imap->l_scope != imap->l_scope_mem + && remain < SCOPE_ELEMS (imap)) + { + new_size = SCOPE_ELEMS (imap); + newp = imap->l_scope_mem; + } + else + { + new_size = imap->l_scope_max; + newp = (struct r_scope_elem **) + malloc (new_size * sizeof (struct r_scope_elem *)); + if (newp == NULL) + _dl_signal_error (ENOMEM, "dlclose", NULL, + N_("cannot create scope list")); + } + + /* Copy over the remaining scope elements. */ + remain = 0; + for (size_t cnt = 0; imap->l_scope[cnt] != NULL; ++cnt) + { + if (imap->l_scope[cnt] != &imap->l_symbolic_searchlist) + { + struct link_map *tmap = (struct link_map *) + ((char *) imap->l_scope[cnt] + - offsetof (struct link_map, l_searchlist)); + if (tmap->l_idx != IDX_STILL_USED) + { + /* Remove the scope. Or replace with own map's + scope. */ + if (new_list != NULL) + { + newp[remain++] = new_list; + new_list = NULL; + } + continue; + } + } + + newp[remain++] = imap->l_scope[cnt]; + } + newp[remain] = NULL; + + struct r_scope_elem **old = imap->l_scope; + + imap->l_scope = newp; + + /* No user anymore, we can free it now. */ + if (old != imap->l_scope_mem) + { + if (_dl_scope_free (old)) + /* If _dl_scope_free used THREAD_GSCOPE_WAIT (), + no need to repeat it. */ + scope_mem_left = false; + } + else + scope_mem_left = true; + + imap->l_scope_max = new_size; + } + else if (new_list != NULL) + { + /* We didn't change the scope array, so reset the search + list. */ + imap->l_searchlist.r_list = NULL; + imap->l_searchlist.r_nlist = 0; + } + + /* The loader is gone, so mark the object as not having one. + Note: l_idx != IDX_STILL_USED -> object will be removed. */ + if (imap->l_loader != NULL + && imap->l_loader->l_idx != IDX_STILL_USED) + imap->l_loader = NULL; + + /* Remember where the first dynamically loaded object is. */ + if (i < first_loaded) + first_loaded = i; + } + } + + /* If there are no objects to unload, do nothing further. */ + if (!unload_any) + goto out; + +#ifdef SHARED + /* Auditing checkpoint: we will start deleting objects. */ + if (__glibc_unlikely (do_audit)) + { + struct link_map *head = ns->_ns_loaded; + struct audit_ifaces *afct = GLRO(dl_audit); + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_DELETE); + + afct = afct->next; + } + } + } +#endif + + /* Notify the debugger we are about to remove some loaded objects. */ + struct r_debug *r = _dl_debug_initialize (0, nsid); + r->r_state = RT_DELETE; + _dl_debug_state (); + LIBC_PROBE (unmap_start, 2, nsid, r); + + if (unload_global) + { + /* Some objects are in the global scope list. Remove them. */ + struct r_scope_elem *ns_msl = ns->_ns_main_searchlist; + unsigned int i; + unsigned int j = 0; + unsigned int cnt = ns_msl->r_nlist; + + while (cnt > 0 && ns_msl->r_list[cnt - 1]->l_removed) + --cnt; + + if (cnt + unload_global == ns_msl->r_nlist) + /* Speed up removing most recently added objects. */ + j = cnt; + else + for (i = 0; i < cnt; i++) + if (ns_msl->r_list[i]->l_removed == 0) + { + if (i != j) + ns_msl->r_list[j] = ns_msl->r_list[i]; + j++; + } + ns_msl->r_nlist = j; + } + + if (!RTLD_SINGLE_THREAD_P + && (unload_global + || scope_mem_left + || (GL(dl_scope_free_list) != NULL + && GL(dl_scope_free_list)->count))) + { + THREAD_GSCOPE_WAIT (); + + /* Now we can free any queued old scopes. */ + struct dl_scope_free_list *fsl = GL(dl_scope_free_list); + if (fsl != NULL) + while (fsl->count > 0) + free (fsl->list[--fsl->count]); + } + + size_t tls_free_start; + size_t tls_free_end; + tls_free_start = tls_free_end = NO_TLS_OFFSET; + + /* We modify the list of loaded objects. */ + __rtld_lock_lock_recursive (GL(dl_load_write_lock)); + + /* Check each element of the search list to see if all references to + it are gone. */ + for (unsigned int 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. */ + + /* Remove the object from the dtv slotinfo array if it uses TLS. */ + if (__glibc_unlikely (imap->l_tls_blocksize > 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 + && imap->l_tls_offset != FORCED_DYNAMIC_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 (tls_free_start == NO_TLS_OFFSET) + { + tls_free_start = imap->l_tls_firstbyte_offset; + tls_free_end = (imap->l_tls_offset + + imap->l_tls_blocksize); + } + else if (imap->l_tls_firstbyte_offset == tls_free_end) + /* Extend the contiguous chunk being reclaimed. */ + tls_free_end = imap->l_tls_offset + 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_firstbyte_offset; + /* This isn't contiguous with the last chunk freed. + One of them will be leaked unless we can free + one block right away. */ + else if (imap->l_tls_offset + imap->l_tls_blocksize + == GL(dl_tls_static_used)) + GL(dl_tls_static_used) = imap->l_tls_firstbyte_offset; + else if (tls_free_end == GL(dl_tls_static_used)) + { + GL(dl_tls_static_used) = tls_free_start; + tls_free_start = imap->l_tls_firstbyte_offset; + tls_free_end = imap->l_tls_offset + imap->l_tls_blocksize; + } + else if (tls_free_end < imap->l_tls_firstbyte_offset) + { + /* We pick the later block. It has a chance to + be freed. */ + tls_free_start = imap->l_tls_firstbyte_offset; + tls_free_end = imap->l_tls_offset + imap->l_tls_blocksize; + } +#else +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" +#endif + } + } + + /* Reset unique symbols if forced. */ + if (force) + { + struct unique_sym_table *tab = &ns->_ns_unique_sym_table; + __rtld_lock_lock_recursive (tab->lock); + struct unique_sym *entries = tab->entries; + if (entries != NULL) + { + size_t idx, size = tab->size; + for (idx = 0; idx < size; ++idx) + { + /* Clear unique symbol entries that belong to this + object. */ + if (entries[idx].name != NULL + && entries[idx].map == imap) + { + entries[idx].name = NULL; + entries[idx].hashval = 0; + tab->n_elements--; + } + } + } + __rtld_lock_unlock_recursive (tab->lock); + } + + /* 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 DL_NNS == 1 + /* The assert in the (imap->l_prev == NULL) case gives + the compiler license to warn that NS points outside + the dl_ns array bounds in that case (as nsid != LM_ID_BASE + is tantamount to nsid >= DL_NNS). That should be impossible + in this configuration, so just assert about it instead. */ + assert (nsid == LM_ID_BASE); + assert (imap->l_prev != NULL); +#else + if (imap->l_prev == NULL) + { + assert (nsid != LM_ID_BASE); + ns->_ns_loaded = imap->l_next; + + /* Update the pointer to the head of the list + we leave for debuggers to examine. */ + r->r_map = (void *) ns->_ns_loaded; + } + else +#endif + imap->l_prev->l_next = imap->l_next; + + --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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _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); + } + } + + __rtld_lock_unlock_recursive (GL(dl_load_write_lock)); + + /* If we removed any object which uses TLS bump the generation counter. */ + if (any_tls) + { + if (__glibc_unlikely (++GL(dl_tls_generation) == 0)) + _dl_fatal_printf ("TLS generation counter wrapped! Please report as described in "REPORT_BUGS_TO".\n"); + + if (tls_free_end == GL(dl_tls_static_used)) + GL(dl_tls_static_used) = tls_free_start; + } + +#ifdef SHARED + /* Auditing checkpoint: we have deleted all objects. */ + if (__glibc_unlikely (do_audit)) + { + struct link_map *head = ns->_ns_loaded; + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT); + + afct = afct->next; + } + } + } +#endif + + if (__builtin_expect (ns->_ns_loaded == NULL, 0) + && nsid == GL(dl_nns) - 1) + do + --GL(dl_nns); + while (GL(dl_ns)[GL(dl_nns) - 1]._ns_loaded == NULL); + + /* Notify the debugger those objects are finalized and gone. */ + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + LIBC_PROBE (unmap_complete, 2, nsid, r); + + /* Recheck if we need to retry, release the lock. */ + out: + if (dl_close_state == rerun) + goto retry; + + dl_close_state = not_pending; +} + + +void +_dl_close (void *_map) +{ + struct link_map *map = _map; + + /* We must take the lock to examine the contents of map and avoid + concurrent dlopens. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + /* At this point we are guaranteed nobody else is touching the list of + loaded maps, but a concurrent dlclose might have freed our map + before we took the lock. There is no way to detect this (see below) + so we proceed assuming this isn't the case. First see whether we + can remove the object at all. */ + if (__glibc_unlikely (map->l_flags_1 & DF_1_NODELETE)) + { + /* Nope. Do nothing. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + return; + } + + /* At present this is an unreliable check except in the case where the + caller has recursively called dlclose and we are sure the link map + has not been freed. In a non-recursive dlclose the map itself + might have been freed and this access is potentially a data race + with whatever other use this memory might have now, or worse we + might silently corrupt memory if it looks enough like a link map. + POSIX has language in dlclose that appears to guarantee that this + should be a detectable case and given that dlclose should be threadsafe + we need this to be a reliable detection. + This is bug 20990. */ + if (__builtin_expect (map->l_direct_opencount, 1) == 0) + { + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + _dl_signal_error (0, map->l_name, NULL, N_("shared object not open")); + } + + _dl_close_worker (map, false); + + __rtld_lock_unlock_recursive (GL(dl_load_lock)); +} diff --git a/REORG.TODO/elf/dl-conflict.c b/REORG.TODO/elf/dl-conflict.c new file mode 100644 index 0000000000..3cbd07435e --- /dev/null +++ b/REORG.TODO/elf/dl-conflict.c @@ -0,0 +1,74 @@ +/* Resolve conflicts against already prelinked libraries. + Copyright (C) 2001-2017 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, see <http://www.gnu.org/licenses/>. */ + +#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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_RELOC)) + _dl_debug_printf ("\nconflict processing: %s\n", DSO_FILENAME (l->l_name)); + + { + /* 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 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" + + /* Override these, defined in dynamic-link.h. */ +#undef CHECK_STATIC_TLS +#define CHECK_STATIC_TLS(ref_map, sym_map) ((void) 0) +#undef TRY_STATIC_TLS +#define TRY_STATIC_TLS(ref_map, sym_map) (0) + + GL(dl_num_cache_relocations) += conflictend - conflict; + + for (; conflict < conflictend; ++conflict) + elf_machine_rela (l, conflict, NULL, NULL, (void *) conflict->r_offset, + 0); + } +#endif +} diff --git a/REORG.TODO/elf/dl-debug.c b/REORG.TODO/elf/dl-debug.c new file mode 100644 index 0000000000..f3957044f6 --- /dev/null +++ b/REORG.TODO/elf/dl-debug.c @@ -0,0 +1,76 @@ +/* Communicate dynamic linker state to the debugger at runtime. + Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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_map == NULL || 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/REORG.TODO/elf/dl-deps.c b/REORG.TODO/elf/dl-deps.c new file mode 100644 index 0000000000..1b8bac6593 --- /dev/null +++ b/REORG.TODO/elf/dl-deps.c @@ -0,0 +1,688 @@ +/* Load the dependencies of a mapped object. + Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <atomic.h> +#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, + (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 (__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 dynamic string token substitution")); \ + else \ + { \ + /* This is for DT_AUXILIARY. */ \ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) \ + _dl_debug_printf (N_("\ +cannot load auxiliary `%s' because of empty dynamic string token " \ + "substitution\n"), __str); \ + continue; \ + } \ + } \ + } \ + \ + __result; }) + +static void +preload (struct list *known, unsigned int *nlist, 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; +} + +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; + + /* No loaded object so far. */ + nlist = 0; + + /* First load MAP itself. */ + preload (known, &nlist, map); + + /* Add the preloaded items after MAP but before any of its dependencies. */ + for (i = 0; i < npreloads; ++i) + preload (known, &nlist, preloads[i]); + + /* Terminate the lists. */ + known[nlist - 1].next = NULL; + + /* Pointer to last unique object. */ + tail = &known[nlist - 1]; + + /* No alloca'd space yet. */ + struct link_map **needed_space = NULL; + size_t needed_space_bytes = 0; + + /* 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) + { + size_t new_size = l->l_ldnum * sizeof (struct link_map *); + + if (new_size > needed_space_bytes) + needed_space + = extend_alloca (needed_space, needed_space_bytes, new_size); + + needed = needed_space; + } + + 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 (__glibc_unlikely (errstring != NULL)) + { + 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; + + /* 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, + DSO_FILENAME (l->l_name)); + + /* We must be prepared that the addressed shared + object is not available. For filter objects the dependency + must be available. */ + bool malloced; + int err = _dl_catch_error (&objname, &errstring, &malloced, + openaux, &args); + + if (__glibc_unlikely (errstring != NULL)) + { + if (d->d_tag == DT_AUXILIARY) + { + /* 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 + { + + 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; + + struct link_map **l_initfini = (struct link_map **) + malloc ((2 * nneeded + 1) * sizeof needed[0]); + if (l_initfini == NULL) + _dl_signal_error (ENOMEM, map->l_name, NULL, + N_("cannot allocate dependency list")); + l_initfini[0] = l; + memcpy (&l_initfini[1], needed, nneeded * sizeof needed[0]); + memcpy (&l_initfini[nneeded + 1], l_initfini, + nneeded * sizeof needed[0]); + atomic_write_barrier (); + l->l_initfini = l_initfini; + l->l_free_initfini = 1; + } + + /* 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); + + struct link_map **old_l_initfini = NULL; + 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); + old_l_initfini = map->l_initfini; + } + + /* Store the search list we built in the object. It will be used for + searches in the scope of this object. */ + struct link_map **l_initfini = + (struct link_map **) malloc ((2 * nlist + 1) + * sizeof (struct link_map *)); + if (l_initfini == NULL) + _dl_signal_error (ENOMEM, map->l_name, NULL, + N_("cannot allocate symbol search list")); + + + map->l_searchlist.r_list = &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 (l_initfini, l); + assert (cnt <= nlist); + for (j = 0; j < cnt; j++) + { + l_initfini[j]->l_reserved = 0; + if (j && __builtin_expect (l_initfini[j]->l_info[DT_SYMBOLIC] + != NULL, 0)) + l->l_symbolic_in_local_scope = true; + } + + 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, l_initfini, + cnt * sizeof (struct link_map *)); + } + } + + /* Maybe we can remove some relocation dependencies now. */ + assert (map->l_searchlist.r_list[0] == map); + struct link_map_reldeps *l_reldeps = NULL; + if (map->l_reldeps != NULL) + { + for (i = 1; i < nlist; ++i) + map->l_searchlist.r_list[i]->l_reserved = 1; + + struct link_map **list = &map->l_reldeps->list[0]; + for (i = 0; i < map->l_reldeps->act; ++i) + if (list[i]->l_reserved) + { + /* Need to allocate new array of relocation dependencies. */ + l_reldeps = malloc (sizeof (*l_reldeps) + + map->l_reldepsmax + * sizeof (struct link_map *)); + if (l_reldeps == NULL) + /* Bad luck, keep the reldeps duplicated between + map->l_reldeps->list and map->l_initfini lists. */ + ; + else + { + unsigned int j = i; + memcpy (&l_reldeps->list[0], &list[0], + i * sizeof (struct link_map *)); + for (i = i + 1; i < map->l_reldeps->act; ++i) + if (!list[i]->l_reserved) + l_reldeps->list[j++] = list[i]; + l_reldeps->act = j; + } + } + + for (i = 1; i < nlist; ++i) + map->l_searchlist.r_list[i]->l_reserved = 0; + } + + /* Sort the initializer list to take dependencies into account. The binary + itself will always be initialize last. */ + memcpy (l_initfini, map->l_searchlist.r_list, + nlist * sizeof (struct link_map *)); + if (__glibc_likely (nlist > 1)) + { + /* We can skip looking for the binary itself which is at the front + of the search list. */ + i = 1; + uint16_t seen[nlist]; + memset (seen, 0, nlist * sizeof (seen[0])); + while (1) + { + /* Keep track of which object we looked at this round. */ + ++seen[i]; + struct link_map *thisp = l_initfini[i]; + + /* Find the last object in the list for which the current one is + a dependency and move the current object behind the object + with the dependency. */ + unsigned int k = nlist - 1; + while (k > i) + { + struct link_map **runp = l_initfini[k]->l_initfini; + if (runp != NULL) + /* Look through the dependencies of the object. */ + while (*runp != NULL) + if (__glibc_unlikely (*runp++ == thisp)) + { + /* Move the current object to the back past the last + object with it as the dependency. */ + memmove (&l_initfini[i], &l_initfini[i + 1], + (k - i) * sizeof (l_initfini[0])); + l_initfini[k] = thisp; + + if (seen[i + 1] > nlist - i) + { + ++i; + goto next_clear; + } + + uint16_t this_seen = seen[i]; + memmove (&seen[i], &seen[i + 1], + (k - i) * sizeof (seen[0])); + seen[k] = this_seen; + + goto next; + } + + --k; + } + + if (++i == nlist) + break; + next_clear: + memset (&seen[i], 0, (nlist - i) * sizeof (seen[0])); + + next:; + } + } + + /* Terminate the list of dependencies. */ + l_initfini[nlist] = NULL; + atomic_write_barrier (); + map->l_initfini = l_initfini; + map->l_free_initfini = 1; + if (l_reldeps != NULL) + { + atomic_write_barrier (); + void *old_l_reldeps = map->l_reldeps; + map->l_reldeps = l_reldeps; + _dl_scope_free (old_l_reldeps); + } + if (old_l_initfini != NULL) + _dl_scope_free (old_l_initfini); + + if (errno_reason) + _dl_signal_error (errno_reason == -1 ? 0 : errno_reason, objname, + NULL, errstring); +} diff --git a/REORG.TODO/elf/dl-dst.h b/REORG.TODO/elf/dl-dst.h new file mode 100644 index 0000000000..a96513d4dc --- /dev/null +++ b/REORG.TODO/elf/dl-dst.h @@ -0,0 +1,74 @@ +/* Handling of dynamic sring tokens. + Copyright (C) 1999-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include "trusted-dirs.h" + +/* 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 (__glibc_unlikely (__sf != NULL)) \ + __cnt = _dl_dst_count (__sf, is_path); \ + \ + __cnt; }) + + +#ifdef SHARED +# define IS_RTLD(l) (l) == &GL(dl_rtld_map) +#else +# define IS_RTLD(l) 0 +#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 dst_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 (MAX (strlen (ORIGIN), strlen (_dl_platform)), \ + strlen (DL_DST_LIB)) \ + minus 4 (which is the length of "$LIB"). \ + \ + First get the origin string if it is not available yet. \ + This can only happen for the map of the executable or, when \ + auditing, in ld.so. */ \ + if ((l)->l_origin == NULL) \ + { \ + assert ((l)->l_name[0] == '\0' || IS_RTLD (l)); \ + (l)->l_origin = _dl_get_origin (); \ + dst_len = ((l)->l_origin && (l)->l_origin != (char *) -1 \ + ? strlen ((l)->l_origin) : 0); \ + } \ + else \ + dst_len = (l)->l_origin == (char *) -1 \ + ? 0 : strlen ((l)->l_origin); \ + dst_len = MAX (MAX (dst_len, GLRO(dl_platformlen)), \ + strlen (DL_DST_LIB)); \ + if (dst_len > 4) \ + __len += __cnt * (dst_len - 4); \ + } \ + \ + __len; }) diff --git a/REORG.TODO/elf/dl-environ.c b/REORG.TODO/elf/dl-environ.c new file mode 100644 index 0000000000..cbffec8808 --- /dev/null +++ b/REORG.TODO/elf/dl-environ.c @@ -0,0 +1,85 @@ +/* Environment handling for dynamic loader. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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/REORG.TODO/elf/dl-error-minimal.c b/REORG.TODO/elf/dl-error-minimal.c new file mode 100644 index 0000000000..384c9b0edc --- /dev/null +++ b/REORG.TODO/elf/dl-error-minimal.c @@ -0,0 +1,23 @@ +/* Error handling for runtime dynamic linker, minimal version. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This version does lives in ld.so, does not use thread-local data + and supports _dl_signal_cerror and _dl_receive_error. */ + +#define DL_ERROR_BOOTSTRAP 1 +#include "dl-error-skeleton.c" diff --git a/REORG.TODO/elf/dl-error-skeleton.c b/REORG.TODO/elf/dl-error-skeleton.c new file mode 100644 index 0000000000..8e5888d4bd --- /dev/null +++ b/REORG.TODO/elf/dl-error-skeleton.c @@ -0,0 +1,230 @@ +/* Template for error handling for runtime dynamic linker. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* The following macro needs to be defined before including this + skeleton file: + + DL_ERROR_BOOTSTRAP + + If 1, do not use TLS and implement _dl_signal_cerror and + _dl_receive_error. If 0, TLS is used, and the variants with + error callbacks are not provided. */ + + +#include <libintl.h> +#include <setjmp.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ldsodefs.h> +#include <stdio.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. */ + volatile int *errcode; /* Return value of _dl_signal_error. */ + 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. */ +#if !DL_ERROR_BOOTSTRAP +static __thread struct catch *catch_hook attribute_tls_model_ie; +#else +/* The version of this code in ld.so cannot use thread-local variables + and is used during bootstrap only. */ +static struct catch *catch_hook; +#endif + +/* 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"; + +#if DL_ERROR_BOOTSTRAP +/* 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; +#endif /* DL_ERROR_BOOTSTRAP */ + +void +internal_function +_dl_signal_error (int errcode, const char *objname, const char *occation, + const char *errstring) +{ + struct catch *lcatch = catch_hook; + + if (! errstring) + errstring = N_("DYNAMIC LINKER BUG!!!"); + + 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; + + char *errstring_copy = malloc (len_objname + len_errstring); + if (errstring_copy != NULL) + { + /* Make a copy of the object file name and the error string. */ + *lcatch->objname = memcpy (__mempcpy (errstring_copy, + errstring, len_errstring), + objname, len_objname); + *lcatch->errstring = errstring_copy; + + /* If the main executable is relocated it means the libc's malloc + is used. */ + bool malloced = true; +#ifdef SHARED + malloced = (GL(dl_ns)[LM_ID_BASE]._ns_loaded != NULL + && (GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_relocated != 0)); +#endif + *lcatch->malloced = malloced; + } + else + { + /* This is better than nothing. */ + *lcatch->objname = ""; + *lcatch->errstring = _dl_out_of_memory; + *lcatch->malloced = false; + } + + *lcatch->errcode = errcode; + + /* We do not restore the signal mask because none was saved. */ + __longjmp (lcatch->env[0].__jmpbuf, 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, + occation ?: N_("error while loading shared libraries"), + objname, *objname ? ": " : "", + errstring, errcode ? ": " : "", + (errcode + ? __strerror_r (errcode, buffer, sizeof buffer) + : "")); + } +} +libc_hidden_def (_dl_signal_error) + + +#if DL_ERROR_BOOTSTRAP +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); +} +#endif /* DL_ERROR_BOOTSTRAP */ + + +int +internal_function +_dl_catch_error (const char **objname, const char **errstring, + bool *mallocedp, void (*operate) (void *), void *args) +{ + /* We need not handle `receiver' since setting a `catch' is handled + before it. */ + + /* Only this needs to be marked volatile, because it is the only local + variable that gets changed between the setjmp invocation and the + longjmp call. All others are just set here (before setjmp) and read + in _dl_signal_error (before longjmp). */ + volatile int errcode; + + struct catch c; + /* Don't use an initializer since we don't need to clear C.env. */ + c.objname = objname; + c.errstring = errstring; + c.malloced = mallocedp; + c.errcode = &errcode; + + struct catch *const old = catch_hook; + catch_hook = &c; + + /* Do not save the signal mask. */ + if (__builtin_expect (__sigsetjmp (c.env, 0), 0) == 0) + { + (*operate) (args); + catch_hook = old; + *objname = NULL; + *errstring = NULL; + *mallocedp = false; + return 0; + } + + /* We get here only if we longjmp'd out of OPERATE. _dl_signal_error has + already stored values into *OBJNAME, *ERRSTRING, and *MALLOCEDP. */ + catch_hook = old; + return errcode; +} +libc_hidden_def (_dl_catch_error) + +#if DL_ERROR_BOOTSTRAP +void +internal_function +_dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args) +{ + struct catch *old_catch = catch_hook; + receiver_fct old_receiver = receiver; + + /* Set the new values. */ + catch_hook = NULL; + receiver = fct; + + (*operate) (args); + + catch_hook = old_catch; + receiver = old_receiver; +} +#endif /* DL_ERROR_BOOTSTRAP */ diff --git a/REORG.TODO/elf/dl-error.c b/REORG.TODO/elf/dl-error.c new file mode 100644 index 0000000000..bfcbd5358b --- /dev/null +++ b/REORG.TODO/elf/dl-error.c @@ -0,0 +1,27 @@ +/* Error handling for runtime dynamic linker, full version. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This implementation lives in libc.so because it uses thread-local + data, which is not available in ld.so. It interposes the version + in dl-error-minimal.c after ld.so bootstrap. + + The signal/catch mechanism is used by the audit framework, which + means that even in ld.so, not all errors are fatal. */ + +#define DL_ERROR_BOOTSTRAP 0 +#include "dl-error-skeleton.c" diff --git a/REORG.TODO/elf/dl-execstack.c b/REORG.TODO/elf/dl-execstack.c new file mode 100644 index 0000000000..875338bea5 --- /dev/null +++ b/REORG.TODO/elf/dl-execstack.c @@ -0,0 +1,31 @@ +/* Stack executability handling for GNU dynamic linker. Stub version. + Copyright (C) 2003-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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/REORG.TODO/elf/dl-fini.c b/REORG.TODO/elf/dl-fini.c new file mode 100644 index 0000000000..93b337bea1 --- /dev/null +++ b/REORG.TODO/elf/dl-fini.c @@ -0,0 +1,281 @@ +/* Call the termination functions of loaded shared objects. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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 **maps, size_t nmaps, char *used, Lmid_t ns) +{ + /* A list of one element need not be sorted. */ + if (nmaps == 1) + return; + + /* We can skip looking for the binary itself which is at the front + of the search list for the main namespace. */ + unsigned int i = ns == LM_ID_BASE; + uint16_t seen[nmaps]; + memset (seen, 0, nmaps * sizeof (seen[0])); + while (1) + { + /* Keep track of which object we looked at this round. */ + ++seen[i]; + struct link_map *thisp = maps[i]; + + /* Do not handle ld.so in secondary namespaces and object which + are not removed. */ + if (thisp != thisp->l_real || thisp->l_idx == -1) + goto skip; + + /* Find the last object in the list for which the current one is + a dependency and move the current object behind the object + with the dependency. */ + unsigned int k = nmaps - 1; + while (k > i) + { + struct link_map **runp = maps[k]->l_initfini; + if (runp != NULL) + /* Look through the dependencies of the object. */ + while (*runp != NULL) + if (__glibc_unlikely (*runp++ == thisp)) + { + move: + /* Move the current object to the back past the last + object with it as the dependency. */ + memmove (&maps[i], &maps[i + 1], + (k - i) * sizeof (maps[0])); + maps[k] = thisp; + + if (used != NULL) + { + char here_used = used[i]; + memmove (&used[i], &used[i + 1], + (k - i) * sizeof (used[0])); + used[k] = here_used; + } + + if (seen[i + 1] > nmaps - i) + { + ++i; + goto next_clear; + } + + uint16_t this_seen = seen[i]; + memmove (&seen[i], &seen[i + 1], (k - i) * sizeof (seen[0])); + seen[k] = this_seen; + + goto next; + } + + if (__glibc_unlikely (maps[k]->l_reldeps != NULL)) + { + unsigned int m = maps[k]->l_reldeps->act; + struct link_map **relmaps = &maps[k]->l_reldeps->list[0]; + + /* Look through the relocation dependencies of the object. */ + while (m-- > 0) + if (__glibc_unlikely (relmaps[m] == thisp)) + { + /* If a cycle exists with a link time dependency, + preserve the latter. */ + struct link_map **runp = thisp->l_initfini; + if (runp != NULL) + while (*runp != NULL) + if (__glibc_unlikely (*runp++ == maps[k])) + goto ignore; + goto move; + } + ignore:; + } + + --k; + } + + skip: + if (++i == nmaps) + break; + next_clear: + memset (&seen[i], 0, (nmaps - i) * sizeof (seen[0])); + + next:; + } +} + + +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. */ + + /* 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 = GL(dl_nns) - 1; ns >= 0; --ns) + { + /* Protect against concurrent loads and unloads. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + unsigned int nloaded = GL(dl_ns)[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 + ) + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + else + { + /* Now we can allocate an array to hold all the pointers and + copy the pointers in. */ + struct link_map *maps[nloaded]; + + 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); + unsigned int nmaps = i; + + /* Now we have to do the sorting. */ + _dl_sort_fini (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. */ + __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) + { + struct link_map *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", + DSO_FILENAME (l->l_name), + 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) + DL_CALL_DT_FINI + (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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS)) + _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/REORG.TODO/elf/dl-fptr.c b/REORG.TODO/elf/dl-fptr.c new file mode 100644 index 0000000000..bf8ae43b51 --- /dev/null +++ b/REORG.TODO/elf/dl-fptr.c @@ -0,0 +1,322 @@ +/* Manage function descriptors. Generic version. + Copyright (C) 1999-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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 <dl-unmap-segments.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) \ + (catomic_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 (__glibc_unlikely (ftab == NULL)) + 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; + + _dl_unmap_segments (map); + + 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/REORG.TODO/elf/dl-hwcaps.c b/REORG.TODO/elf/dl-hwcaps.c new file mode 100644 index 0000000000..ac50fd2c38 --- /dev/null +++ b/REORG.TODO/elf/dl-hwcaps.c @@ -0,0 +1,297 @@ +/* Hardware capability support for run-time dynamic loader. + Copyright (C) 2012-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <elf.h> +#include <errno.h> +#include <libintl.h> +#include <unistd.h> +#include <ldsodefs.h> + +#include <dl-procinfo.h> +#include <dl-hwcaps.h> + +#ifdef _DL_FIRST_PLATFORM +# define _DL_FIRST_EXTRA (_DL_FIRST_PLATFORM + _DL_PLATFORMS_COUNT) +#else +# define _DL_FIRST_EXTRA _DL_HWCAP_COUNT +#endif + +/* 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) +{ + uint64_t hwcap_mask = GET_HWCAP_MASK(); + /* Determine how many important bits are set. */ + uint64_t masked = GLRO(dl_hwcap) & hwcap_mask; + size_t cnt = platform != NULL; + size_t n, m; + size_t total; + 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; + +#ifdef NEED_DL_SYSINFO_DSO + /* 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); + /* The standard ELF note layout is exactly as the anonymous struct. + The next element is a variable length vendor name of length + VENDORLEN (with a real length rounded to ElfW(Word)), followed + by the data of length DATALEN (with a real length rounded to + ElfW(Word)). */ + 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))) + /* The layout of the type 2, vendor "GNU" note is as follows: + .long <Number of capabilities enabled by this note> + .long <Capabilities mask> (as mask >> _DL_FIRST_EXTRA). + .byte <The bit number for the next capability> + .asciz <The name of the capability>. */ + if (note->type == NT_GNU_HWCAP + && 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; /* Pseudo-string "<b>name" */ + dsocapslen = note->datalen - sizeof *p * 2; + break; + } + note = ((const void *) (note + 1) + + ROUND (note->vendorlen) + ROUND (note->datalen)); +#undef ROUND + } + if (dsocaps != NULL) + break; + } + } +#endif + + /* For TLS enabled builds always add 'tls'. */ + ++cnt; + + /* Create temporary data structure to generate result table. */ + struct r_strlenpair temp[cnt]; + m = 0; +#ifdef NEED_DL_SYSINFO_DSO + if (dsocaps != NULL) + { + /* dsocaps points to the .asciz string, and -1 points to the mask + .long just before the string. */ + const ElfW(Word) mask = ((const ElfW(Word) *) dsocaps)[-1]; + GLRO(dl_hwcap) |= (uint64_t) mask << _DL_FIRST_EXTRA; + /* Note that we add the dsocaps to the set already chosen by the + LD_HWCAP_MASK environment variable (or default HWCAP_IMPORTANT). + So there is no way to request ignoring an OS-supplied dsocap + string and bit like you can ignore an OS-supplied HWCAP bit. */ + hwcap_mask |= (uint64_t) mask << _DL_FIRST_EXTRA; +#if HAVE_TUNABLES + TUNABLE_SET (glibc, tune, hwcap_mask, uint64_t, hwcap_mask); +#else + GLRO(dl_hwcap_mask) = hwcap_mask; +#endif + 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 (__glibc_likely (mask & ((ElfW(Word)) 1 << bit))) + { + 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; + } + + temp[m].str = "tls"; + temp[m].len = 3; + ++m; + + assert (m == cnt); + + /* Determine the total size of all strings together. */ + if (cnt == 1) + total = temp[0].len + 1; + else + { + total = temp[0].len + temp[cnt - 1].len + 2; + if (cnt > 2) + { + total <<= 1; + for (n = 1; n + 1 < cnt; ++n) + total += temp[n].len + 1; + if (cnt > 3 + && (cnt >= sizeof (size_t) * 8 + || total + (sizeof (*result) << 3) + >= (1UL << (sizeof (size_t) * 8 - cnt + 3)))) + _dl_signal_error (ENOMEM, NULL, NULL, + N_("cannot create capability list")); + + total <<= cnt - 3; + } + } + + /* The result structure: we use a very compressed way to store the + various combinations of capability names. */ + *sz = 1 << cnt; + result = (struct r_strlenpair *) malloc (*sz * sizeof (*result) + total); + if (result == NULL) + _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 + (indices 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/REORG.TODO/elf/dl-hwcaps.h b/REORG.TODO/elf/dl-hwcaps.h new file mode 100644 index 0000000000..2c4fa3db02 --- /dev/null +++ b/REORG.TODO/elf/dl-hwcaps.h @@ -0,0 +1,30 @@ +/* Hardware capability support for run-time dynamic loader. + Copyright (C) 2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <elf/dl-tunables.h> + +#if HAVE_TUNABLES +# define GET_HWCAP_MASK() TUNABLE_GET (glibc, tune, hwcap_mask, uint64_t, NULL) +#else +# ifdef SHARED +# define GET_HWCAP_MASK() GLRO(dl_hwcap_mask) +# else +/* HWCAP_MASK is ignored in static binaries when built without tunables. */ +# define GET_HWCAP_MASK() (0) +# endif +#endif diff --git a/REORG.TODO/elf/dl-init.c b/REORG.TODO/elf/dl-init.c new file mode 100644 index 0000000000..5c5f3de365 --- /dev/null +++ b/REORG.TODO/elf/dl-init.c @@ -0,0 +1,126 @@ +/* Run initializers for newly loaded objects. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <stddef.h> +#include <ldsodefs.h> + + +/* Type of the initializer. */ +typedef void (*init_t) (int, char **, char **); + + +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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS)) + _dl_debug_printf ("\ncalling init: %s\n\n", + DSO_FILENAME (l->l_name)); + + /* 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) + DL_CALL_DT_INIT(l, l->l_addr + l->l_info[DT_INIT]->d_un.d_ptr, 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 (__glibc_unlikely (GL(dl_initfirst) != NULL)) + { + 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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS)) + _dl_debug_printf ("\ncalling preinit: %s\n\n", + DSO_FILENAME (main_map->l_name)); + + 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. */ + _dl_starting_up = 0; +#endif +} diff --git a/REORG.TODO/elf/dl-iteratephdr.c b/REORG.TODO/elf/dl-iteratephdr.c new file mode 100644 index 0000000000..ddd5bde831 --- /dev/null +++ b/REORG.TODO/elf/dl-iteratephdr.c @@ -0,0 +1,89 @@ +/* Get loaded objects program headers. + Copyright (C) 2001-2017 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, see <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <ldsodefs.h> +#include <stddef.h> +#include <libc-lock.h> + +static void +cancel_handler (void *arg __attribute__((unused))) +{ + __rtld_lock_unlock_recursive (GL(dl_load_write_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 nobody modifies the list of loaded objects. */ + __rtld_lock_lock_recursive (GL(dl_load_write_lock)); + __libc_cleanup_push (cancel_handler, NULL); + + /* We have to determine the namespace of the caller since this determines + which namespace is reported. */ + size_t nloaded = GL(dl_ns)[0]._ns_nloaded; + Lmid_t ns = 0; +#ifdef SHARED + const void *caller = RETURN_ADDRESS (0); + for (Lmid_t cnt = GL(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 + && (l->l_contiguous + || _dl_addr_inside_object (l, (ElfW(Addr)) caller))) + ns = cnt; + } +#endif + + for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) + { + info.dlpi_addr = l->l_real->l_addr; + info.dlpi_name = l->l_real->l_name; + info.dlpi_phdr = l->l_real->l_phdr; + info.dlpi_phnum = l->l_real->l_phnum; + info.dlpi_adds = GL(dl_load_adds); + info.dlpi_subs = GL(dl_load_adds) - nloaded; + info.dlpi_tls_data = NULL; + info.dlpi_tls_modid = l->l_real->l_tls_modid; + if (info.dlpi_tls_modid != 0) + info.dlpi_tls_data = GLRO(dl_tls_get_addr_soft) (l->l_real); + 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_write_lock)); + + return ret; +} +hidden_def (__dl_iterate_phdr) + +weak_alias (__dl_iterate_phdr, dl_iterate_phdr); diff --git a/REORG.TODO/elf/dl-libc.c b/REORG.TODO/elf/dl-libc.c new file mode 100644 index 0000000000..9fdc8b1130 --- /dev/null +++ b/REORG.TODO/elf/dl-libc.c @@ -0,0 +1,330 @@ +/* Handle loading and unloading shared objects for internal libc purposes. + Copyright (C) 1999-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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; + + int result = (_dl_catch_error (&objname, &last_errstring, &malloced, + operate, args) + ?: 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; + /* This is the caller of the dlopen() function. */ + const void *caller_dlopen; + + /* 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, args->caller_dlopen, + __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; + args.caller_dlopen = RETURN_ADDRESS (0); + +#ifdef SHARED + if (__glibc_unlikely (_dl_open_hook != NULL)) + 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 (__glibc_unlikely (_dl_open_hook != NULL)) + 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 (__glibc_unlikely (_dl_open_hook != NULL)) + return _dl_open_hook->dlclose (map); +#endif + return dlerror_run (do_dlclose, map); +} +libc_hidden_def (__libc_dlclose) + + +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; +} + + +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); + } + + for (Lmid_t ns = 0; ns < GL(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; + + /* Remove all additional names added to the objects. */ + while (lnp != NULL) + { + struct libname_list *old = lnp; + lnp = lnp->next; + if (! old->dont_free) + free (old); + } + + /* Free the initfini dependency list. */ + if (l->l_free_initfini) + free (l->l_initfini); + l->l_initfini = NULL; + } + + 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); + } + } + + /* 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); + + void *scope_free_list = GL(dl_scope_free_list); + GL(dl_scope_free_list) = NULL; + free (scope_free_list); +} diff --git a/REORG.TODO/elf/dl-load.c b/REORG.TODO/elf/dl-load.c new file mode 100644 index 0000000000..c1b6d4ba0f --- /dev/null +++ b/REORG.TODO/elf/dl-load.c @@ -0,0 +1,2307 @@ +/* Map in a shared object's segments from the file. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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 <stap-probe.h> +#include <libc-pointer-arith.h> + +#include <dl-dst.h> +#include <dl-load.h> +#include <dl-map-segments.h> +#include <dl-unmap-segments.h> +#include <dl-machine-reject-phdr.h> +#include <dl-sysdep-open.h> + + +#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) + + +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])) + + +static bool +is_trusted_path (const char *path, size_t len) +{ + const char *trun = system_dirs; + + for (size_t idx = 0; idx < nsystem_dirs_len; ++idx) + { + if (len == system_dirs_len[idx] && memcmp (trun, path, len) == 0) + /* Found it. */ + return true; + + trun += system_dirs_len[idx] + 1; + } + + return false; +} + + +static bool +is_trusted_path_normalize (const char *path, size_t len) +{ + if (len == 0) + return false; + + if (*path == ':') + { + ++path; + --len; + } + + char *npath = (char *) alloca (len + 2); + char *wnp = npath; + while (*path != '\0') + { + if (path[0] == '/') + { + if (path[1] == '.') + { + if (path[2] == '.' && (path[3] == '/' || path[3] == '\0')) + { + while (wnp > npath && *--wnp != '/') + ; + path += 3; + continue; + } + else if (path[2] == '/' || path[2] == '\0') + { + path += 2; + continue; + } + } + + if (wnp > npath && wnp[-1] == '/') + { + ++path; + continue; + } + } + + *wnp++ = *path++; + } + + if (wnp == npath || wnp[-1] != '/') + *wnp++ = '/'; + + const char *trun = system_dirs; + + for (size_t idx = 0; idx < nsystem_dirs_len; ++idx) + { + if (wnp - npath >= system_dirs_len[idx] + && memcmp (trun, npath, system_dirs_len[idx]) == 0) + /* Found it. */ + return true; + + trun += system_dirs_len[idx] + 1; + } + + return false; +} + + +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 (__glibc_unlikely (secure) + && ((name[len] != '\0' && name[len] != '/' + && (!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, + __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; + + /* Now fill the result path. While copying over the string we keep + track of the start of the last path element. When we come across + a DST we copy over the value or (if the value is not available) + leave the entire path element out. */ + char *wp = result; + char *last_elem = result; + bool check_for_trusted = false; + + do + { + if (__glibc_unlikely (*name == '$')) + { + const char *repl = NULL; + size_t len; + + ++name; + if ((len = is_dst (start, name, "ORIGIN", is_path, + __libc_enable_secure)) != 0) + { + repl = l->l_origin; + check_for_trusted = (__libc_enable_secure + && l->l_type == lt_executable); + } + 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; + /* Also skip following colon if this is the first rpath + element, but keep an empty element at the end. */ + if (wp == result && is_path && *name == ':' && name[1] != '\0') + ++name; + } + else + /* No DST we recognize. */ + *wp++ = '$'; + } + else + { + *wp++ = *name++; + if (is_path && *name == ':') + { + /* In SUID/SGID programs, after $ORIGIN expansion the + normalized path must be rooted in one of the trusted + directories. */ + if (__glibc_unlikely (check_for_trusted) + && !is_trusted_path_normalize (last_elem, wp - last_elem)) + wp = last_elem; + else + last_elem = wp; + + check_for_trusted = false; + } + } + } + while (*name != '\0'); + + /* In SUID/SGID programs, after $ORIGIN expansion the normalized + path must be rooted in one of the trusted directories. */ + if (__glibc_unlikely (check_for_trusted) + && !is_trusted_path_normalize (last_elem, wp - last_elem)) + wp = last_elem; + + *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, int is_path) +{ + /* 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 no + 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, is_path); + + /* If we do not have to replace anything simply copy the string. */ + if (__glibc_likely (cnt == 0)) + return __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, is_path); +} + + +/* 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, + struct link_map *l) +{ + char *cp; + size_t nelems = 0; + char *to_free; + + while ((cp = __strsep (&rpath, sep)) != NULL) + { + struct r_search_path_elem *dirp; + + to_free = cp = expand_dynamic_string_token (l, cp, 1); + + 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 (__glibc_unlikely (check_trusted) && !is_trusted_path (cp, len)) + { + free (to_free); + 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 (__glibc_likely (where != NULL)) + 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; + } + free (to_free); + } + + /* Terminate the array. */ + result[nelems] = NULL; + + return result; +} + + +static bool +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 (__glibc_unlikely (GLRO(dl_inhibit_rpath) != NULL) + && !__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. */ + sps->dirs = (void *) -1; + return false; + } + + while (*inhp != '\0') + if (*inhp++ == ':') + break; + } + while (*inhp != '\0'); + } + + /* Make a writable copy. */ + copy = __strdup (rpath); + if (copy == NULL) + { + errstring = N_("cannot create RUNPATH/RPATH copy"); + goto signal_error; + } + + /* Ignore empty rpaths. */ + if (*copy == 0) + { + free (copy); + sps->dirs = (struct r_search_path_elem **) -1; + return false; + } + + /* 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) + { + free (copy); + errstring = N_("cannot create cache for search path"); + signal_error: + _dl_signal_error (ENOMEM, NULL, NULL, errstring); + } + + fillin_rpath (copy, result, ":", 0, what, where, l); + + /* 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; + return true; +} + +/* 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. */ + return decompose_rpath (sp, (const char *) (D_PTR (l, l_info[DT_STRTAB]) + + l->l_info[tag]->d_un.d_val), + l, what); +} + + +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; + struct link_map __attribute__ ((unused)) *l = NULL; + /* 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"); + /* During rtld init the memory is allocated by the stub malloc, + prevent any attempt to free it by the normal malloc. */ + l->l_runpath_dirs.malloced = 0; + + /* 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"); + /* During rtld init the memory is allocated by the stub + malloc, prevent any attempt to free it by the normal + malloc. */ + 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; + +#ifdef SHARED + /* Expand DSTs. */ + size_t cnt = DL_DST_COUNT (llp, 1); + if (__glibc_likely (cnt == 0)) + llp_tmp = strdupa (llp); + else + { + /* Determine the length of the substituted string. */ + size_t total = DL_DST_REQUIRED (l, llp, strlen (llp), cnt); + + /* Allocate the necessary memory. */ + llp_tmp = (char *) alloca (total + 1); + llp_tmp = _dl_dst_substitute (l, llp, llp_tmp, 1); + } +#else + llp_tmp = strdupa (llp); +#endif + + /* Decompose the LD_LIBRARY_PATH contents. First determine how many + elements it has. */ + 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, ":;", + __libc_enable_secure, "LD_LIBRARY_PATH", + NULL, l); + + 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; +} + + +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, Lmid_t nsid) +{ + /* The file might already be closed. */ + if (fd != -1) + (void) __close (fd); + if (l != NULL && l->l_origin != (char *) -1l) + free ((char *) l->l_origin); + free (l); + free (realname); + + if (r != NULL) + { + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + LIBC_PROBE (map_failed, 2, nsid, r); + } + + _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, const char *origname, 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; + /* 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. */ + struct r_file_id id; + if (__glibc_unlikely (!_dl_get_file_id (fd, &id))) + { + errstring = N_("cannot stat shared object"); + call_lose_errno: + errval = errno; + call_lose: + lose (errval, fd, name, realname, l, errstring, + make_consistent ? r : NULL, nsid); + } + + /* Look again to see if the real name matched another already loaded. */ + for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next) + if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id)) + { + /* 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 (__glibc_unlikely (nsid != LM_ID_BASE) + && (_dl_file_id_match_p (&id, &GL(dl_rtld_map).l_file_id) + || _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); + + /* Add the map for the mirrored object to the object list. */ + _dl_add_to_namespace_list (l, nsid); + + return l; + } +#endif + + if (mode & RTLD_NOLOAD) + { + /* We are not supposed to load the object unless it is already + loaded. So return now. */ + free (realname); + __close (fd); + return NULL; + } + + /* Print debugging message. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _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) + { + free (realname); + __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 ((mode & __RTLD_AUDIT) == 0 + && __glibc_unlikely (GLRO(dl_naudit) > 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 (); + LIBC_PROBE (map_start, 2, nsid, r); + 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 (__glibc_unlikely (l == NULL)) + { +#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; + } + } + + /* On most platforms presume that PT_GNU_STACK is absent and the stack is + * executable. Other platforms default to a nonexecutable stack and don't + * need PT_GNU_STACK to do so. */ + uint_fast16_t stack_flags = DEFAULT_STACK_PERMS; + + { + /* Scan the program header table, collecting its load commands. */ + struct loadcmd loadcmds[l->l_phnum]; + 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 (__glibc_unlikely ((ph->p_align & (GLRO(dl_pagesize) - 1)) != 0)) + { + errstring = N_("ELF load command alignment not page-aligned"); + goto call_lose; + } + if (__glibc_unlikely (((ph->p_vaddr - ph->p_offset) + & (ph->p_align - 1)) != 0)) + { + errstring + = N_("ELF load command address/offset not properly aligned"); + goto call_lose; + } + + struct loadcmd *c = &loadcmds[nloadcmds++]; + c->mapstart = ALIGN_DOWN (ph->p_vaddr, GLRO(dl_pagesize)); + c->mapend = ALIGN_UP (ph->p_vaddr + ph->p_filesz, GLRO(dl_pagesize)); + c->dataend = ph->p_vaddr + ph->p_filesz; + c->allocend = ph->p_vaddr + ph->p_memsz; + c->mapoff = ALIGN_DOWN (ph->p_offset, GLRO(dl_pagesize)); + + /* 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: + 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 (__glibc_likely (l->l_type == lt_library) + /* If GL(dl_tls_dtv_slotinfo_list) == NULL, then rtld.c did + not set up TLS data structures, so don't use them now. */ + || __glibc_likely (GL(dl_tls_dtv_slotinfo_list) != NULL)) + { + /* Assign the next available module ID. */ + l->l_tls_modid = _dl_next_tls_modid (); + break; + } + +#ifdef SHARED + /* We are loading the executable itself when the dynamic + linker was executed directly. The setup will happen + later. Otherwise, the TLS data structures are already + initialized, and we assigned a TLS modid above. */ + assert (l->l_prev == NULL || (mode & __RTLD_AUDIT) != 0); +#else + assert (false && "TLS not initialized in static application"); +#endif + 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 (__glibc_unlikely (nloadcmds == 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; + } + + if (__glibc_unlikely (type != ET_DYN) + && __glibc_unlikely ((mode & __RTLD_OPENEXEC) == 0)) + { + /* This object is loaded at a fixed address. This must never + happen for objects loaded with dlopen. */ + errstring = N_("cannot dynamically load executable"); + goto call_lose; + } + + /* Length of the sections to be loaded. */ + maplength = loadcmds[nloadcmds - 1].allocend - loadcmds[0].mapstart; + + /* Now process the load commands and map segments into memory. + This is responsible for filling in: + l_map_start, l_map_end, l_addr, l_contiguous, l_text_end, l_phdr + */ + errstring = _dl_map_segments (l, fd, header, type, loadcmds, nloadcmds, + maplength, has_holes, loader); + if (__glibc_unlikely (errstring != NULL)) + goto call_lose; + } + + if (l->l_ld == 0) + { + if (__glibc_unlikely (type == ET_DYN)) + { + 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 (__glibc_unlikely (l->l_flags_1 & DF_1_NOOPEN) + && (mode & __RTLD_DLOPEN)) + { + /* We are not supposed to load this object. Free all resources. */ + _dl_unmap_segments (l); + + 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 (__glibc_unlikely ((stack_flags &~ GL(dl_stack_flags)) & PF_X)) + { + if (__glibc_unlikely (__check_caller (RETURN_ADDRESS (0), allow_ldso) != 0)) + { + errstring = N_("invalid caller"); + goto call_lose; + } + + /* The stack is presently not executable, but this module + requires that it be executable. We must change the + protection of the variable which contains the flags used in + the mprotect calls. */ +#ifdef 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 (__glibc_likely (p + s <= relro_end)) + { + /* The variable lies in the region protected by RELRO. */ + if (__mprotect ((void *) p, s, PROT_READ|PROT_WRITE) < 0) + { + errstring = N_("cannot change memory protections"); + goto call_lose_errno; + } + __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; + } + } + + /* 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; + + /* We are done mapping in the file. We no longer need the descriptor. */ + if (__glibc_unlikely (__close (fd) != 0)) + { + errstring = N_("cannot close file descriptor"); + goto call_lose_errno; + } + /* Signal that we closed the file. */ + fd = -1; + + /* If this is ET_EXEC, we should have loaded it as lt_executable. */ + assert (type != ET_EXEC || l->l_type == lt_executable); + + l->l_entry += l->l_addr; + + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _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 + && __glibc_unlikely (l->l_info[DT_SYMBOLIC] != NULL) + && &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[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_file_id = id; + +#ifdef SHARED + /* When auditing is used the recorded names might not include the + name by which the DSO is actually known. Add that as well. */ + if (__glibc_unlikely (origname != NULL)) + add_name_to_object (l, origname); +#else + /* Audit modules only exist when linking is dynamic so ORIGNAME + cannot be non-NULL. */ + assert (origname == NULL); +#endif + + /* When we profile the SONAME might be needed for something else but + loading. Add it right away. */ + if (__glibc_unlikely (GLRO(dl_profile) != NULL) + && 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 DL_AFTER_LOAD + DL_AFTER_LOAD (l); +#endif + + /* Now that the object is fully initialized add it to the object list. */ + _dl_add_to_namespace_list (l, nsid); + +#ifdef SHARED + /* Auditing checkpoint: we have a new object. */ + if (__glibc_unlikely (GLRO(dl_naudit) > 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, + DSO_FILENAME (name)); + 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. + + If FD is not -1, then the file is already open and FD refers to it. + In that case, FD is consumed for both successful and error returns. */ +static int +open_verify (const char *name, int fd, + struct filebuf *fbp, struct link_map *loader, + int whatcode, int mode, 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(osabi,ver) (ver == 0) +#elif defined MORE_ELF_HEADER_DATA + MORE_ELF_HEADER_DATA; +#endif + static const unsigned char expected[EI_NIDENT] = + { + [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 (__glibc_unlikely (GLRO(dl_naudit) > 0) && whatcode != 0 + && loader->l_auditing == 0) + { + const char *original_name = name; + 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; + } + + if (fd != -1 && name != original_name && strcmp (name, original_name)) + { + /* An audit library changed what we're supposed to open, + so FD no longer matches it. */ + __close (fd); + fd = -1; + } + } +#endif + + if (fd == -1) + /* Open the file. We always open files read-only. */ + fd = __open (name, O_RDONLY | O_CLOEXEC); + + if (fd != -1) + { + ElfW(Ehdr) *ehdr; + ElfW(Phdr) *phdr, *ph; + ElfW(Word) *abi_note; + unsigned int osversion; + size_t maplength; + + /* We successfully opened the file. Now verify it is a file + we can use. */ + __set_errno (0); + fbp->len = 0; + assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr))); + /* Read in the header. */ + do + { + ssize_t retlen = __libc_read (fd, fbp->buf + fbp->len, + sizeof (fbp->buf) - fbp->len); + if (retlen <= 0) + break; + fbp->len += retlen; + } + while (__glibc_unlikely (fbp->len < sizeof (ElfW(Ehdr)))); + + /* This is where the ELF header is loaded. */ + ehdr = (ElfW(Ehdr) *) fbp->buf; + + /* Now run the tests. */ + if (__glibc_unlikely (fbp->len < (ssize_t) sizeof (ElfW(Ehdr)))) + { + 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, 0); + } + + /* See whether the ELF header is what we expect. */ + if (__glibc_unlikely (! VALID_ELF_HEADER (ehdr->e_ident, expected, + EI_ABIVERSION) + || !VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI], + ehdr->e_ident[EI_ABIVERSION]) + || memcmp (&ehdr->e_ident[EI_PAD], + &expected[EI_PAD], + EI_NIDENT - EI_PAD) != 0)) + { + /* Something is wrong. */ + const Elf32_Word *magp = (const void *) ehdr->e_ident; + if (*magp != +#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_OSABI], + ehdr->e_ident[EI_ABIVERSION])) + errstring = N_("ELF file ABI version invalid"); + else if (memcmp (&ehdr->e_ident[EI_PAD], &expected[EI_PAD], + EI_NIDENT - EI_PAD) != 0) + errstring = N_("nonzero padding in e_ident"); + else + /* Otherwise we don't know what went wrong. */ + errstring = N_("internal error"); + + goto call_lose; + } + + if (__glibc_unlikely (ehdr->e_version != EV_CURRENT)) + { + errstring = N_("ELF file version does not match current one"); + goto call_lose; + } + if (! __glibc_likely (elf_machine_matches_host (ehdr))) + goto close_and_out; + else if (__glibc_unlikely (ehdr->e_type != ET_DYN + && ehdr->e_type != ET_EXEC)) + { + errstring = N_("only ET_DYN and ET_EXEC can be loaded"); + goto call_lose; + } + else if (__glibc_unlikely (ehdr->e_type == ET_EXEC + && (mode & __RTLD_OPENEXEC) == 0)) + { + /* BZ #16634. It is an error to dlopen ET_EXEC (unless + __RTLD_OPENEXEC is explicitly set). We return error here + so that code in _dl_map_object_from_fd does not try to set + l_tls_modid for this module. */ + + errstring = N_("cannot dynamically load executable"); + goto call_lose; + } + else if (__glibc_unlikely (ehdr->e_phentsize != 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; + } + } + + if (__glibc_unlikely (elf_machine_reject_phdr_p + (phdr, ehdr->e_phnum, fbp->buf, fbp->len, + loader, fd))) + goto close_and_out; + + /* 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) + { + ElfW(Addr) size = ph->p_filesz; + + if (ph->p_offset + size <= (size_t) fbp->len) + abi_note = (void *) (fbp->buf + ph->p_offset); + else + { + abi_note = alloca (size); + __lseek (fd, ph->p_offset, SEEK_SET); + if (__libc_read (fd, (void *) abi_note, size) != size) + goto read_error; + } + + while (memcmp (abi_note, &expected_note, sizeof (expected_note))) + { +#define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word))) + ElfW(Addr) note_size = 3 * sizeof (ElfW(Word)) + + ROUND (abi_note[0]) + + ROUND (abi_note[1]); + + if (size - 32 < note_size) + { + size = 0; + break; + } + size -= note_size; + abi_note = (void *) abi_note + note_size; + } + + if (size == 0) + 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 mode, + 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 (__glibc_unlikely (dirs == NULL)) + /* 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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS) + && 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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) + _dl_debug_printf (" trying file=%s\n", buf); + + fd = open_verify (buf, -1, fbp, loader, whatcode, mode, + 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 && __glibc_unlikely (mode & __RTLD_SECURE) + && __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 (__glibc_unlikely (! any)) + { + /* 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); + + /* rtld_search_dirs and env_path_list are attribute_relro, therefore + avoid writing into it. */ + if (sps != &rtld_search_dirs && sps != &env_path_list) + 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 type, int trace_mode, int mode, Lmid_t nsid) +{ + int fd; + const char *origname = NULL; + char *realname; + char *name_copy; + struct link_map *l; + struct filebuf fb; + + assert (nsid >= 0); + assert (nsid < GL(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 (__glibc_unlikely ((l->l_faked | l->l_removed) != 0)) + continue; + if (!_dl_name_match_p (name, l)) + { + const char *soname; + + if (__glibc_likely (l->l_soname_added) + || 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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES) + && loader != NULL) + _dl_debug_printf ((mode & __RTLD_CALLMAP) == 0 + ? "\nfile=%s [%lu]; needed by %s [%lu]\n" + : "\nfile=%s [%lu]; dynamically loaded by %s [%lu]\n", + name, nsid, DSO_FILENAME (loader->l_name), loader->l_ns); + +#ifdef SHARED + /* Give the auditing libraries a chance to change the name before we + try anything. */ + if (__glibc_unlikely (GLRO(dl_naudit) > 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) + { + const char *before = name; + 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; + } + if (before != name && strcmp (before, name) != 0) + { + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("audit changed filename %s -> %s\n", + before, name); + + if (origname == NULL) + origname = before; + } + } + + 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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) + _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, mode, + &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, mode, + &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, mode, &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, mode, + &loader->l_runpath_dirs, &realname, &fb, loader, + LA_SER_RUNPATH, &found_other_class); + + if (fd == -1) + { + realname = _dl_sysdep_open_object (name, namelen, &fd); + if (realname != NULL) + { + fd = open_verify (realname, fd, + &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded, + LA_SER_CONFIG, mode, &found_other_class, + false); + if (fd == -1) + free (realname); + } + } + +#ifdef USE_LDCONFIG + if (fd == -1 + && (__glibc_likely ((mode & __RTLD_SECURE) == 0) + || ! __libc_enable_secure) + && __glibc_likely (GLRO(dl_inhibit_cache) == 0)) + { + /* Check the list of libraries in the file /etc/ld.so.cache, + for compatibility with Linux's ldconfig program. */ + char *cached = _dl_load_cache_lookup (name); + + if (cached != NULL) + { + // XXX Correct to unconditionally default to namespace 0? + l = (loader + ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded +# ifdef SHARED + ?: &GL(dl_rtld_map) +# endif + ); + + /* If the loader has the DF_1_NODEFLIB flag set we must not + use a cache entry from any of these directories. */ + if (__glibc_unlikely (l->l_flags_1 & DF_1_NODEFLIB)) + { + 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. */ + free (cached); + cached = NULL; + break; + } + + dirp += system_dirs_len[cnt] + 1; + ++cnt; + } + while (cnt < nsystem_dirs_len); + } + + if (cached != NULL) + { + fd = open_verify (cached, -1, + &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded, + LA_SER_CONFIG, mode, &found_other_class, + false); + if (__glibc_likely (fd != -1)) + realname = cached; + else + free (cached); + } + } + } +#endif + + /* Finally, try the default path. */ + if (fd == -1 + && ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL + || __glibc_likely (!(l->l_flags_1 & DF_1_NODEFLIB))) + && rtld_search_dirs.dirs != (void *) -1) + fd = open_path (name, namelen, mode, &rtld_search_dirs, + &realname, &fb, l, LA_SER_DEFAULT, &found_other_class); + + /* Add another newline when we are tracing the library loading. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) + _dl_debug_printf ("\n"); + } + else + { + /* The path may contain dynamic string tokens. */ + realname = (loader + ? expand_dynamic_string_token (loader, name, 0) + : __strdup (name)); + if (realname == NULL) + fd = -1; + else + { + fd = open_verify (realname, -1, &fb, + loader ?: GL(dl_ns)[nsid]._ns_loaded, 0, mode, + &found_other_class, true); + if (__glibc_unlikely (fd == -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 (__glibc_unlikely (fd == -1)) + { + if (trace_mode + && __glibc_likely ((GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) == 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; + + /* Allocate a new object map. */ + if ((name_copy = __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; + + /* Enter the object in the object list. */ + _dl_add_to_namespace_list (l, nsid); + + 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, origname, fd, &fb, realname, loader, + type, mode, &stack_end, nsid); +} + +struct add_path_state +{ + bool counting; + unsigned int idx; + Dl_serinfo *si; + char *allocptr; +}; + +static void +add_path (struct add_path_state *p, const struct r_search_path_struct *sps, + unsigned int flags) +{ + if (sps->dirs != (void *) -1) + { + struct r_search_path_elem **dirs = sps->dirs; + do + { + const struct r_search_path_elem *const r = *dirs++; + if (p->counting) + { + p->si->dls_cnt++; + p->si->dls_size += MAX (2, r->dirnamelen); + } + else + { + Dl_serpath *const sp = &p->si->dls_serpath[p->idx++]; + sp->dls_name = p->allocptr; + if (r->dirnamelen < 2) + *p->allocptr++ = r->dirnamelen ? '/' : '.'; + else + p->allocptr = __mempcpy (p->allocptr, + r->dirname, r->dirnamelen - 1); + *p->allocptr++ = '\0'; + sp->dls_flags = flags; + } + } + while (*dirs != NULL); + } +} + +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; + } + + struct add_path_state p = + { + .counting = counting, + .idx = 0, + .si = si, + .allocptr = (char *) &si->dls_serpath[si->dls_cnt] + }; + +# define add_path(p, sps, flags) add_path(p, sps, 0) /* XXX */ + + /* 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 (&p, &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 (&p, &l->l_rpath_dirs, XXX_RPATH); + } + } + + /* Try the LD_LIBRARY_PATH environment variable. */ + add_path (&p, &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 (&p, &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 (&p, &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/REORG.TODO/elf/dl-load.h b/REORG.TODO/elf/dl-load.h new file mode 100644 index 0000000000..7cd6214d4e --- /dev/null +++ b/REORG.TODO/elf/dl-load.h @@ -0,0 +1,135 @@ +/* Map in a shared object's segments from the file. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _DL_LOAD_H +#define _DL_LOAD_H 1 + +#include <link.h> +#include <sys/mman.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 + + +/* 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 + + +/* This structure describes one PT_LOAD command. + Its details have been expanded out and converted. */ +struct loadcmd +{ + ElfW(Addr) mapstart, mapend, dataend, allocend; + ElfW(Off) mapoff; + int prot; /* PROT_* bits. */ +}; + + +/* This is a subroutine of _dl_map_segments. It should be called for each + load command, some time after L->l_addr has been set correctly. It is + responsible for setting up the l_text_end and l_phdr fields. */ +static void __always_inline +_dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header, + const struct loadcmd *c) +{ + if (c->prot & PROT_EXEC) + l->l_text_end = l->l_addr + c->mapend; + + if (l->l_phdr == 0 + && 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 *) (uintptr_t) (c->mapstart + header->e_phoff + - c->mapoff); +} + + +/* This is a subroutine of _dl_map_object_from_fd. It is responsible + for filling in several fields in *L: l_map_start, l_map_end, l_addr, + l_contiguous, l_text_end, l_phdr. On successful return, all the + segments are mapped (or copied, or whatever) from the file into their + final places in the address space, with the correct page permissions, + and any bss-like regions already zeroed. It returns a null pointer + on success, or an error message string (to be translated) on error + (having also set errno). + + The file <dl-map-segments.h> defines this function. The canonical + implementation in elf/dl-map-segments.h might be replaced by a sysdeps + version. */ +static const char *_dl_map_segments (struct link_map *l, int fd, + const ElfW(Ehdr) *header, int type, + const struct loadcmd loadcmds[], + size_t nloadcmds, + const size_t maplength, + bool has_holes, + struct link_map *loader); + +/* All the error message strings _dl_map_segments might return are + listed here so that different implementations in different sysdeps + dl-map-segments.h files all use consistent strings that are + guaranteed to have translations. */ +#define DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT \ + N_("failed to map segment from shared object") +#define DL_MAP_SEGMENTS_ERROR_MPROTECT \ + N_("cannot change memory protections") +#define DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL \ + N_("cannot map zero-fill pages") + + +#endif /* dl-load.h */ diff --git a/REORG.TODO/elf/dl-lookup.c b/REORG.TODO/elf/dl-lookup.c new file mode 100644 index 0000000000..3d2369dbf2 --- /dev/null +++ b/REORG.TODO/elf/dl-lookup.c @@ -0,0 +1,1129 @@ +/* Look up a symbol in the loaded objects. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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 <sysdep-cancel.h> +#include <libc-lock.h> +#include <tls.h> +#include <atomic.h> + +#include <assert.h> + +/* Return nonzero if check_match should consider SYM to fail to match a + symbol reference for some machine-specific reason. */ +#ifndef ELF_MACHINE_SYM_NO_MATCH +# define ELF_MACHINE_SYM_NO_MATCH(sym) 0 +#endif + +#define VERSTAG(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (tag)) + + +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 + +/* Utility function for do_lookup_x. The caller is called with undef_name, + ref, version, flags and type_class, and those are passed as the first + five arguments. The caller then computes sym, symidx, strtab, and map + and passes them as the next four arguments. Lastly the caller passes in + versioned_sym and num_versions which are modified by check_match during + the checking process. */ +static const ElfW(Sym) * +check_match (const char *const undef_name, + const ElfW(Sym) *const ref, + const struct r_found_version *const version, + const int flags, + const int type_class, + const ElfW(Sym) *const sym, + const Elf_Symndx symidx, + const char *const strtab, + const struct link_map *const map, + const ElfW(Sym) **const versioned_sym, + int *const num_versions) +{ + unsigned int stt = ELFW(ST_TYPE) (sym->st_info); + assert (ELF_RTYPE_CLASS_PLT == 1); + if (__glibc_unlikely ((sym->st_value == 0 /* No value. */ + && stt != STT_TLS) + || ELF_MACHINE_SYM_NO_MATCH (sym) + || (type_class & (sym->st_shndx == SHN_UNDEF)))) + return NULL; + + /* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC, + STT_COMMON, STT_TLS, and STT_GNU_IFUNC since these are no + code/data definitions. */ +#define ALLOWED_STT \ + ((1 << STT_NOTYPE) | (1 << STT_OBJECT) | (1 << STT_FUNC) \ + | (1 << STT_COMMON) | (1 << STT_TLS) | (1 << STT_GNU_IFUNC)) + if (__glibc_unlikely (((1 << stt) & ALLOWED_STT) == 0)) + 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 (__glibc_unlikely (verstab == NULL)) + { + /* 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 all: 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; +} + +/* Utility function for do_lookup_unique. Add a symbol to TABLE. */ +static void +enter_unique_sym (struct unique_sym *table, size_t size, + unsigned int hash, const char *name, + const ElfW(Sym) *sym, const struct link_map *map) +{ + size_t idx = hash % size; + size_t hash2 = 1 + hash % (size - 2); + while (table[idx].name != NULL) + { + idx += hash2; + if (idx >= size) + idx -= size; + } + + table[idx].hashval = hash; + table[idx].name = name; + table[idx].sym = sym; + table[idx].map = map; +} + +/* Utility function for do_lookup_x. Lookup an STB_GNU_UNIQUE symbol + in the unique symbol table, creating a new entry if necessary. + Return the matching symbol in RESULT. */ +static void +do_lookup_unique (const char *undef_name, uint_fast32_t new_hash, + const struct link_map *map, struct sym_val *result, + int type_class, const ElfW(Sym) *sym, const char *strtab, + const ElfW(Sym) *ref, const struct link_map *undef_map) +{ + /* We have to determine whether we already found a symbol with this + name before. If not then we have to add it to the search table. + If we already found a definition we have to use it. */ + + struct unique_sym_table *tab + = &GL(dl_ns)[map->l_ns]._ns_unique_sym_table; + + __rtld_lock_lock_recursive (tab->lock); + + struct unique_sym *entries = tab->entries; + size_t size = tab->size; + if (entries != NULL) + { + size_t idx = new_hash % size; + size_t hash2 = 1 + new_hash % (size - 2); + while (1) + { + if (entries[idx].hashval == new_hash + && strcmp (entries[idx].name, undef_name) == 0) + { + if ((type_class & ELF_RTYPE_CLASS_COPY) != 0) + { + /* We possibly have to initialize the central + copy from the copy addressed through the + relocation. */ + result->s = sym; + result->m = (struct link_map *) map; + } + else + { + result->s = entries[idx].sym; + result->m = (struct link_map *) entries[idx].map; + } + __rtld_lock_unlock_recursive (tab->lock); + return; + } + + if (entries[idx].name == NULL) + break; + + idx += hash2; + if (idx >= size) + idx -= size; + } + + if (size * 3 <= tab->n_elements * 4) + { + /* Expand the table. */ +#ifdef RTLD_CHECK_FOREIGN_CALL + /* This must not happen during runtime relocations. */ + assert (!RTLD_CHECK_FOREIGN_CALL); +#endif + size_t newsize = _dl_higher_prime_number (size + 1); + struct unique_sym *newentries + = calloc (sizeof (struct unique_sym), newsize); + if (newentries == NULL) + { + nomem: + __rtld_lock_unlock_recursive (tab->lock); + _dl_fatal_printf ("out of memory\n"); + } + + for (idx = 0; idx < size; ++idx) + if (entries[idx].name != NULL) + enter_unique_sym (newentries, newsize, entries[idx].hashval, + entries[idx].name, entries[idx].sym, + entries[idx].map); + + tab->free (entries); + tab->size = newsize; + size = newsize; + entries = tab->entries = newentries; + tab->free = free; + } + } + else + { +#ifdef RTLD_CHECK_FOREIGN_CALL + /* This must not happen during runtime relocations. */ + assert (!RTLD_CHECK_FOREIGN_CALL); +#endif + +#ifdef SHARED + /* If tab->entries is NULL, but tab->size is not, it means + this is the second, conflict finding, lookup for + LD_TRACE_PRELINKING in _dl_debug_bindings. Don't + allocate anything and don't enter anything into the + hash table. */ + if (__glibc_unlikely (tab->size)) + { + assert (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK); + goto success; + } +#endif + +#define INITIAL_NUNIQUE_SYM_TABLE 31 + size = INITIAL_NUNIQUE_SYM_TABLE; + entries = calloc (sizeof (struct unique_sym), size); + if (entries == NULL) + goto nomem; + + tab->entries = entries; + tab->size = size; + tab->free = free; + } + + if ((type_class & ELF_RTYPE_CLASS_COPY) != 0) + enter_unique_sym (entries, size, new_hash, strtab + sym->st_name, ref, + undef_map); + else + { + enter_unique_sym (entries, size, + new_hash, strtab + sym->st_name, sym, map); + + if (map->l_type == lt_loaded) + /* Make sure we don't unload this object by + setting the appropriate flag. */ + ((struct link_map *) map)->l_flags_1 |= DF_1_NODELETE; + } + ++tab->n_elements; + +#ifdef SHARED + success: +#endif + __rtld_lock_unlock_recursive (tab->lock); + + result->s = sym; + result->m = (struct link_map *) map; +} + +/* 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 *undef_map) +{ + size_t n = scope->r_nlist; + /* Make sure we read the value before proceeding. Otherwise we + might use r_list pointing to the initial scope and r_nlist being + the value after a resize. That is the only path in dl-open.c not + protected by GSCOPE. A read barrier here might be to expensive. */ + __asm volatile ("" : "+r" (n), "+m" (scope->r_list)); + struct link_map **list = scope->r_list; + + do + { + const 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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS)) + _dl_debug_printf ("symbol=%s; lookup in file=%s [%lu]\n", + undef_name, DSO_FILENAME (map->l_name), + map->l_ns); + + /* If the hash table is empty there is nothing to do here. */ + if (map->l_nbuckets == 0) + continue; + + Elf_Symndx symidx; + int num_versions = 0; + const ElfW(Sym) *versioned_sym = NULL; + + /* 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]); + + const ElfW(Sym) *sym; + const ElfW(Addr) *bitmask = map->l_gnu_bitmask; + if (__glibc_likely (bitmask != NULL)) + { + 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 (__glibc_unlikely ((bitmask_word >> hashbit1) + & (bitmask_word >> hashbit2) & 1)) + { + 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 ^ new_hash) >> 1) == 0) + { + symidx = hasharr - map->l_gnu_chain_zero; + sym = check_match (undef_name, ref, version, flags, + type_class, &symtab[symidx], symidx, + strtab, map, &versioned_sym, + &num_versions); + if (sym != NULL) + goto found_it; + } + while ((*hasharr++ & 1u) == 0); + } + } + /* No symbol found. */ + symidx = SHN_UNDEF; + } + else + { + if (*old_hash == 0xffffffff) + *old_hash = _dl_elf_hash (undef_name); + + /* 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 (undef_name, ref, version, flags, + type_class, &symtab[symidx], symidx, + strtab, map, &versioned_sym, + &num_versions); + 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: + /* When UNDEF_MAP is NULL, which indicates we are called from + do_lookup_x on relocation against protected data, we skip + the data definion in the executable from copy reloc. */ + if (ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA + && undef_map == NULL + && map->l_type == lt_executable + && type_class == ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA) + { + const ElfW(Sym) *s; + unsigned int i; + +#if ! ELF_MACHINE_NO_RELA + if (map->l_info[DT_RELA] != NULL + && map->l_info[DT_RELASZ] != NULL + && map->l_info[DT_RELASZ]->d_un.d_val != 0) + { + const ElfW(Rela) *rela + = (const ElfW(Rela) *) D_PTR (map, l_info[DT_RELA]); + unsigned int rela_count + = map->l_info[DT_RELASZ]->d_un.d_val / sizeof (*rela); + + for (i = 0; i < rela_count; i++, rela++) + if (elf_machine_type_class (ELFW(R_TYPE) (rela->r_info)) + == ELF_RTYPE_CLASS_COPY) + { + s = &symtab[ELFW(R_SYM) (rela->r_info)]; + if (!strcmp (strtab + s->st_name, undef_name)) + goto skip; + } + } +#endif +#if ! ELF_MACHINE_NO_REL + if (map->l_info[DT_REL] != NULL + && map->l_info[DT_RELSZ] != NULL + && map->l_info[DT_RELSZ]->d_un.d_val != 0) + { + const ElfW(Rel) *rel + = (const ElfW(Rel) *) D_PTR (map, l_info[DT_REL]); + unsigned int rel_count + = map->l_info[DT_RELSZ]->d_un.d_val / sizeof (*rel); + + for (i = 0; i < rel_count; i++, rel++) + if (elf_machine_type_class (ELFW(R_TYPE) (rel->r_info)) + == ELF_RTYPE_CLASS_COPY) + { + s = &symtab[ELFW(R_SYM) (rel->r_info)]; + if (!strcmp (strtab + s->st_name, undef_name)) + goto skip; + } + } +#endif + } + + /* Hidden and internal symbols are local, ignore them. */ + if (__glibc_unlikely (dl_symbol_visibility_binds_local_p (sym))) + goto skip; + + switch (ELFW(ST_BIND) (sym->st_info)) + { + case STB_WEAK: + /* Weak definition. Use this value if we don't find another. */ + if (__glibc_unlikely (GLRO(dl_dynamic_weak))) + { + 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; + + case STB_GNU_UNIQUE:; + do_lookup_unique (undef_name, new_hash, map, result, type_class, + sym, strtab, ref, undef_map); + return 1; + + default: + /* Local symbols are ignored. */ + break; + } + } + +skip: + /* 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 + && __glibc_unlikely (_dl_name_match_p (version->filename, map))) + return -1; + } + while (++i < n); + + /* We have not found anything until now. */ + return 0; +} + + +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, int flags) +{ + struct link_map *runp; + unsigned int i; + int result = 0; + + /* Avoid self-references and references to objects which cannot be + unloaded anyway. */ + if (undef_map == map) + return 0; + + /* Avoid references to objects which cannot be unloaded anyway. */ + assert (map->l_type == lt_loaded); + if ((map->l_flags_1 & DF_1_NODELETE) != 0) + return 0; + + struct link_map_reldeps *l_reldeps + = atomic_forced_read (undef_map->l_reldeps); + + /* Make sure l_reldeps is read before l_initfini. */ + atomic_read_barrier (); + + /* Determine whether UNDEF_MAP already has a reference to MAP. First + look in the normal dependencies. */ + struct link_map **l_initfini = atomic_forced_read (undef_map->l_initfini); + if (l_initfini != NULL) + { + for (i = 0; l_initfini[i] != NULL; ++i) + if (l_initfini[i] == map) + return 0; + } + + /* No normal dependency. See whether we already had to add it + to the special list of dynamic dependencies. */ + unsigned int l_reldepsact = 0; + if (l_reldeps != NULL) + { + struct link_map **list = &l_reldeps->list[0]; + l_reldepsact = l_reldeps->act; + for (i = 0; i < l_reldepsact; ++i) + if (list[i] == map) + return 0; + } + + /* Save serial number of the target MAP. */ + unsigned long long serial = map->l_serial; + + /* Make sure nobody can unload the object while we are at it. */ + if (__glibc_unlikely (flags & DL_LOOKUP_GSCOPE_LOCK)) + { + /* We can't just call __rtld_lock_lock_recursive (GL(dl_load_lock)) + here, that can result in ABBA deadlock. */ + THREAD_GSCOPE_RESET_FLAG (); + __rtld_lock_lock_recursive (GL(dl_load_lock)); + /* While MAP value won't change, after THREAD_GSCOPE_RESET_FLAG () + it can e.g. point to unallocated memory. So avoid the optimizer + treating the above read from MAP->l_serial as ensurance it + can safely dereference it. */ + map = atomic_forced_read (map); + + /* From this point on it is unsafe to dereference MAP, until it + has been found in one of the lists. */ + + /* Redo the l_initfini check in case undef_map's l_initfini + changed in the mean time. */ + if (undef_map->l_initfini != l_initfini + && undef_map->l_initfini != NULL) + { + l_initfini = undef_map->l_initfini; + for (i = 0; l_initfini[i] != NULL; ++i) + if (l_initfini[i] == map) + goto out_check; + } + + /* Redo the l_reldeps check if undef_map's l_reldeps changed in + the mean time. */ + if (undef_map->l_reldeps != NULL) + { + if (undef_map->l_reldeps != l_reldeps) + { + struct link_map **list = &undef_map->l_reldeps->list[0]; + l_reldepsact = undef_map->l_reldeps->act; + for (i = 0; i < l_reldepsact; ++i) + if (list[i] == map) + goto out_check; + } + else if (undef_map->l_reldeps->act > l_reldepsact) + { + struct link_map **list + = &undef_map->l_reldeps->list[0]; + i = l_reldepsact; + l_reldepsact = undef_map->l_reldeps->act; + for (; i < l_reldepsact; ++i) + if (list[i] == map) + goto out_check; + } + } + } + else + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + /* 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. */ + + /* MAP could have been dlclosed, freed and then some other dlopened + library could have the same link_map pointer. */ + if (map->l_serial != serial) + goto out_check; + + /* Redo the NODELETE check, as when dl_load_lock wasn't held + yet this could have changed. */ + if ((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; + } + + /* Add the reference now. */ + if (__glibc_unlikely (l_reldepsact >= undef_map->l_reldepsmax)) + { + /* Allocate more memory for the dependency list. Since this + can never happen during the startup phase we can use + `realloc'. */ + struct link_map_reldeps *newp; + unsigned int max + = undef_map->l_reldepsmax ? undef_map->l_reldepsmax * 2 : 10; + +#ifdef RTLD_PREPARE_FOREIGN_CALL + RTLD_PREPARE_FOREIGN_CALL; +#endif + + newp = malloc (sizeof (*newp) + max * sizeof (struct link_map *)); + if (newp == NULL) + { + /* If we didn't manage to allocate memory for the list this is + no fatal problem. We simply make sure the referenced object + cannot be unloaded. This is semantically the correct + behavior. */ + map->l_flags_1 |= DF_1_NODELETE; + goto out; + } + else + { + if (l_reldepsact) + memcpy (&newp->list[0], &undef_map->l_reldeps->list[0], + l_reldepsact * sizeof (struct link_map *)); + newp->list[l_reldepsact] = map; + newp->act = l_reldepsact + 1; + atomic_write_barrier (); + void *old = undef_map->l_reldeps; + undef_map->l_reldeps = newp; + undef_map->l_reldepsmax = max; + if (old) + _dl_scope_free (old); + } + } + else + { + undef_map->l_reldeps->list[l_reldepsact] = map; + atomic_write_barrier (); + undef_map->l_reldeps->act = l_reldepsact + 1; + } + + /* Display information if we are debugging. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("\ +\nfile=%s [%lu]; needed by %s [%lu] (relocation dependency)\n\n", + DSO_FILENAME (map->l_name), + map->l_ns, + DSO_FILENAME (undef_map->l_name), + 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)); + + if (__glibc_unlikely (flags & DL_LOOKUP_GSCOPE_LOCK)) + THREAD_GSCOPE_SET_FLAG (); + + return result; + + out_check: + if (map->l_serial != serial) + result = -1; + goto out; +} + +static void +internal_function +_dl_debug_bindings (const char *undef_name, struct link_map *undef_map, + const ElfW(Sym) **ref, struct sym_val *value, + const struct r_found_version *version, int type_class, + int protected); + + +/* Search loaded objects' symbol tables for a definition of the symbol + UNDEF_NAME, perhaps with a requested version for the symbol. + + We must never have calls to the audit functions inside this function + or in any function which gets called. If this would happen the audit + code might create a thread which can throw off all the scope locking. */ +lookup_t +internal_function +_dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, + 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 or DL_LOOKUP_GSCOPE_LOCK + is allowed if we look up a versioned symbol. */ + assert (version == NULL + || (flags & ~(DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK)) + == 0); + + size_t i = 0; + if (__glibc_unlikely (skip_map != NULL)) + /* Search the relevant loaded objects for a definition. */ + while ((*scope)->r_list[i] != skip_map) + ++i; + + /* Search the relevant loaded objects for a definition. */ + for (size_t start = i; *scope != NULL; start = 0, ++scope) + { + int res = do_lookup_x (undef_name, new_hash, &old_hash, *ref, + ¤t_value, *scope, start, version, flags, + skip_map, type_class, undef_map); + if (res > 0) + break; + + if (__glibc_unlikely (res < 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 : ""; + + /* XXX We cannot translate the message. */ + _dl_signal_cerror (0, DSO_FILENAME (reference_name), + 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 (__glibc_unlikely (current_value.s == NULL)) + { + if ((*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK) + && !(GLRO(dl_debug_mask) & DL_DEBUG_UNUSED)) + { + /* 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, DSO_FILENAME (reference_name), + N_("symbol lookup error"), + make_string ("undefined symbol: ", undef_name, + versionstr, versionname)); + } + *ref = NULL; + return 0; + } + + int protected = (*ref + && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED); + if (__glibc_unlikely (protected != 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_EXTERN_PROTECTED_DATA + && ELFW(ST_TYPE) ((*ref)->st_info) == STT_OBJECT + && type_class == ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA) + ? ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA + : ELF_RTYPE_CLASS_PLT, NULL) != 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 (__glibc_unlikely (current_value.m->l_type == lt_loaded) + /* 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, flags) < 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, + (flags & DL_LOOKUP_GSCOPE_LOCK) + ? undef_map->l_scope : symbol_scope, + version, type_class, flags, skip_map); + + /* The object is used. */ + if (__glibc_unlikely (current_value.m->l_used == 0)) + current_value.m->l_used = 1; + + if (__glibc_unlikely (GLRO(dl_debug_mask) + & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK))) + _dl_debug_bindings (undef_name, undef_map, ref, + ¤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; + + if (__glibc_likely (map->l_info[DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + + DT_THISPROCNUM + DT_VERSIONTAGNUM + + DT_EXTRANUM + DT_VALNUM] != NULL)) + { + 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++; + /* Skip 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 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'", + DSO_FILENAME (reference_name), + undef_map->l_ns, + DSO_FILENAME (value->m->l_name), + 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) + { +/* ELF_RTYPE_CLASS_XXX must match RTYPE_CLASS_XXX used by prelink with + LD_TRACE_PRELINKING. */ +#define RTYPE_CLASS_VALID 8 +#define RTYPE_CLASS_PLT (8|1) +#define RTYPE_CLASS_COPY (8|2) +#define RTYPE_CLASS_TLS (8|4) +#if ELF_RTYPE_CLASS_PLT != 0 && ELF_RTYPE_CLASS_PLT != 1 +# error ELF_RTYPE_CLASS_PLT must be 0 or 1! +#endif +#if ELF_RTYPE_CLASS_COPY != 0 && ELF_RTYPE_CLASS_COPY != 2 +# error ELF_RTYPE_CLASS_COPY must be 0 or 2! +#endif + 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; + struct unique_sym *saved_entries + = GL(dl_ns)[LM_ID_BASE]._ns_unique_sym_table.entries; + + GL(dl_ns)[LM_ID_BASE]._ns_unique_sym_table.entries = NULL; + do_lookup_x (undef_name, new_hash, &old_hash, *ref, &val, + undef_map->l_local_scope[0], 0, version, 0, NULL, + type_class, undef_map); + if (val.s != value->s || val.m != value->m) + conflict = 1; + else if (__glibc_unlikely (undef_map->l_symbolic_in_local_scope) + && val.s + && __glibc_unlikely (ELFW(ST_BIND) (val.s->st_info) + == STB_GNU_UNIQUE)) + { + /* If it is STB_GNU_UNIQUE and undef_map's l_local_scope + contains any DT_SYMBOLIC libraries, unfortunately there + can be conflicts even if the above is equal. As symbol + resolution goes from the last library to the first and + if a STB_GNU_UNIQUE symbol is found in some late DT_SYMBOLIC + library, it would be the one that is looked up. */ + struct sym_val val2 = { NULL, NULL }; + size_t n; + struct r_scope_elem *scope = undef_map->l_local_scope[0]; + + for (n = 0; n < scope->r_nlist; n++) + if (scope->r_list[n] == val.m) + break; + + for (n++; n < scope->r_nlist; n++) + if (scope->r_list[n]->l_info[DT_SYMBOLIC] != NULL + && do_lookup_x (undef_name, new_hash, &old_hash, *ref, + &val2, + &scope->r_list[n]->l_symbolic_searchlist, + 0, version, 0, NULL, type_class, + undef_map) > 0) + { + conflict = 1; + val = val2; + break; + } + } + GL(dl_ns)[LM_ID_BASE]._ns_unique_sym_table.entries = saved_entries; + } + + if (value->s) + { + /* Keep only ELF_RTYPE_CLASS_PLT and ELF_RTYPE_CLASS_COPY + bits since since prelink only uses them. */ + type_class &= ELF_RTYPE_CLASS_PLT | ELF_RTYPE_CLASS_COPY; + if (__glibc_unlikely (ELFW(ST_TYPE) (value->s->st_info) + == STT_TLS)) + /* Clear the RTYPE_CLASS_VALID bit in RTYPE_CLASS_TLS. */ + type_class = RTYPE_CLASS_TLS & ~RTYPE_CLASS_VALID; + else if (__glibc_unlikely (ELFW(ST_TYPE) (value->s->st_info) + == STT_GNU_IFUNC)) + /* Set the RTYPE_CLASS_VALID bit. */ + type_class |= RTYPE_CLASS_VALID; + } + + 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/REORG.TODO/elf/dl-machine-reject-phdr.h b/REORG.TODO/elf/dl-machine-reject-phdr.h new file mode 100644 index 0000000000..57253deb52 --- /dev/null +++ b/REORG.TODO/elf/dl-machine-reject-phdr.h @@ -0,0 +1,34 @@ +/* Machine-dependent program header inspection for the ELF loader. + Copyright (C) 2014-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _DL_MACHINE_REJECT_PHDR_H +#define _DL_MACHINE_REJECT_PHDR_H 1 + +#include <stdbool.h> + +/* Return true iff ELF program headers are incompatible with the running + host. */ +static inline bool +elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, uint_fast16_t phnum, + const char *buf, size_t len, struct link_map *map, + int fd) +{ + return false; +} + +#endif /* dl-machine-reject-phdr.h */ diff --git a/REORG.TODO/elf/dl-map-segments.h b/REORG.TODO/elf/dl-map-segments.h new file mode 100644 index 0000000000..d36f9bd2f6 --- /dev/null +++ b/REORG.TODO/elf/dl-map-segments.h @@ -0,0 +1,157 @@ +/* Map in a shared object's segments. Generic version. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <dl-load.h> + +/* This implementation assumes (as does the corresponding implementation + of _dl_unmap_segments, in dl-unmap-segments.h) that shared objects + are always laid out with all segments contiguous (or with gaps + between them small enough that it's preferable to reserve all whole + pages inside the gaps with PROT_NONE mappings rather than permitting + other use of those parts of the address space). */ + +static __always_inline const char * +_dl_map_segments (struct link_map *l, int fd, + const ElfW(Ehdr) *header, int type, + const struct loadcmd loadcmds[], size_t nloadcmds, + const size_t maplength, bool has_holes, + struct link_map *loader) +{ + const struct loadcmd *c = loadcmds; + + if (__glibc_likely (type == 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 + = (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 (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED)) + return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; + + 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. */ + if (__glibc_unlikely + (__mprotect ((caddr_t) (l->l_addr + c->mapend), + loadcmds[nloadcmds - 1].mapstart - c->mapend, + PROT_NONE) < 0)) + return DL_MAP_SEGMENTS_ERROR_MPROTECT; + } + + l->l_contiguous = 1; + + goto postmap; + } + + /* Remember which part of the address space this object uses. */ + l->l_map_start = c->mapstart + l->l_addr; + l->l_map_end = l->l_map_start + maplength; + l->l_contiguous = !has_holes; + + while (c < &loadcmds[nloadcmds]) + { + 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)) + return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; + + postmap: + _dl_postprocess_loadcmd (l, header, c); + + 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 (__glibc_unlikely ((c->prot & PROT_WRITE) == 0)) + { + /* Dag nab it. */ + if (__mprotect ((caddr_t) (zero + & ~(GLRO(dl_pagesize) - 1)), + GLRO(dl_pagesize), c->prot|PROT_WRITE) < 0) + return DL_MAP_SEGMENTS_ERROR_MPROTECT; + } + memset ((void *) zero, '\0', zeropage - zero); + if (__glibc_unlikely ((c->prot & PROT_WRITE) == 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, + -1, 0); + if (__glibc_unlikely (mapat == MAP_FAILED)) + return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL; + } + } + + ++c; + } + + /* Notify ELF_PREFERRED_ADDRESS that we have to load this one + fixed. */ + ELF_FIXED_ADDRESS (loader, c->mapstart); + + return NULL; +} diff --git a/REORG.TODO/elf/dl-minimal.c b/REORG.TODO/elf/dl-minimal.c new file mode 100644 index 0000000000..1a35baff2e --- /dev/null +++ b/REORG.TODO/elf/dl-minimal.c @@ -0,0 +1,380 @@ +/* Minimal replacements for basic facilities used in the dynamic linker. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <limits.h> +#include <stdio.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 <_itoa.h> +#include <malloc/malloc-internal.h> + +#include <assert.h> + +/* Minimal malloc allocator for used during initial link. After the + initial link, a full malloc implementation is interposed, either + the one in libc, or a different one supplied by the user through + interposition. */ + +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 +malloc (size_t n) +{ + 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) + MALLOC_ALIGNMENT - 1) + & ~(MALLOC_ALIGNMENT - 1)); + + if (alloc_ptr + n >= alloc_end || n >= -(uintptr_t) alloc_ptr) + { + /* Insufficient space left; allocate another page plus one extra + page to reduce number of mmap calls. */ + caddr_t page; + size_t nup = (n + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1); + if (__glibc_unlikely (nup == 0 && n != 0)) + return NULL; + nup += GLRO(dl_pagesize); + page = __mmap (0, nup, PROT_READ|PROT_WRITE, + MAP_ANON|MAP_PRIVATE, -1, 0); + if (page == MAP_FAILED) + return NULL; + if (page != alloc_end) + alloc_ptr = page; + alloc_end = page + nup; + } + + alloc_last_block = (void *) alloc_ptr; + alloc_ptr += n; + return alloc_last_block; +} + +/* 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.) */ + size_t bytes = nmemb * size; + +#define HALF_SIZE_T (((size_t) 1) << (8 * sizeof (size_t) / 2)) + if (__builtin_expect ((nmemb | size) >= HALF_SIZE_T, 0) + && size != 0 && bytes / size != nmemb) + return NULL; + + return malloc (bytes); +} + +/* This will rarely be called. */ +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) +{ + if (ptr == NULL) + return malloc (n); + assert (ptr == alloc_last_block); + size_t old_size = alloc_ptr - alloc_last_block; + alloc_ptr = alloc_last_block; + void *new = malloc (n); + return new != ptr ? memcpy (new, ptr, old_size) : 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; +} + +void +__libc_fatal (const char *message) +{ + _dl_fatal_printf ("%s", message); +} +rtld_hidden_def (__libc_fatal) + +void +__attribute__ ((noreturn)) +__chk_fail (void) +{ + _exit (127); +} +rtld_hidden_def (__chk_fail) + +#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; + unsigned max_digit; + + 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; + max_digit = 9; + if (*nptr == '0') + { + if (nptr[1] == 'x' || nptr[1] == 'X') + { + base = 16; + nptr += 2; + } + else + { + base = 8; + max_digit = 7; + } + } + + while (1) + { + unsigned long int digval; + if (*nptr >= '0' && *nptr <= '0' + max_digit) + digval = *nptr - '0'; + else if (base == 16) + { + if (*nptr >= 'a' && *nptr <= 'f') + digval = *nptr - 'a' + 10; + else if (*nptr >= 'A' && *nptr <= 'F') + digval = *nptr - 'A' + 10; + else + break; + } + else + break; + + if (result > ULONG_MAX / base + || (result == ULONG_MAX / base && digval > ULONG_MAX % base)) + { + 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; +} + + +#undef _itoa +/* 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 (unsigned long long int value, char *buflim, unsigned int base, + int upper_case) +{ + assert (! upper_case); + + do + *--buflim = _itoa_lower_digits[value % base]; + while ((value /= base) != 0); + + return buflim; +} + +/* The '_itoa_lower_digits' variable in libc.so is able to handle bases + up to 36. We don't need this here. */ +const char _itoa_lower_digits[16] = "0123456789abcdef"; +rtld_hidden_data_def (_itoa_lower_digits) + +/* 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) diff --git a/REORG.TODO/elf/dl-misc.c b/REORG.TODO/elf/dl-misc.c new file mode 100644 index 0000000000..c5d3e0e7c5 --- /dev/null +++ b/REORG.TODO/elf/dl-misc.c @@ -0,0 +1,362 @@ +/* Miscellaneous support functions for dynamic linker + Copyright (C) 1997-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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 <stdint.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sysdep.h> +#include <_itoa.h> +#include <dl-writev.h> + + +/* 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 | O_CLOEXEC); + 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. */ + _dl_writev (fd, iov, niov); +} + + +/* 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; +} + + +unsigned long int +internal_function +_dl_higher_prime_number (unsigned long int n) +{ + /* These are primes that are near, but slightly smaller than, a + power of two. */ + static const uint32_t primes[] = { + UINT32_C (7), + UINT32_C (13), + UINT32_C (31), + UINT32_C (61), + UINT32_C (127), + UINT32_C (251), + UINT32_C (509), + UINT32_C (1021), + UINT32_C (2039), + UINT32_C (4093), + UINT32_C (8191), + UINT32_C (16381), + UINT32_C (32749), + UINT32_C (65521), + UINT32_C (131071), + UINT32_C (262139), + UINT32_C (524287), + UINT32_C (1048573), + UINT32_C (2097143), + UINT32_C (4194301), + UINT32_C (8388593), + UINT32_C (16777213), + UINT32_C (33554393), + UINT32_C (67108859), + UINT32_C (134217689), + UINT32_C (268435399), + UINT32_C (536870909), + UINT32_C (1073741789), + UINT32_C (2147483647), + /* 4294967291L */ + UINT32_C (2147483647) + UINT32_C (2147483644) + }; + + const uint32_t *low = &primes[0]; + const uint32_t *high = &primes[sizeof (primes) / sizeof (primes[0])]; + + while (low != high) + { + const uint32_t *mid = low + (high - low) / 2; + if (n > *mid) + low = mid + 1; + else + high = mid; + } + +#if 0 + /* If we've run out of primes, abort. */ + if (n > *low) + { + fprintf (stderr, "Cannot find prime bigger than %lu\n", n); + abort (); + } +#endif + + return *low; +} diff --git a/REORG.TODO/elf/dl-object.c b/REORG.TODO/elf/dl-object.c new file mode 100644 index 0000000000..4c43235148 --- /dev/null +++ b/REORG.TODO/elf/dl-object.c @@ -0,0 +1,229 @@ +/* Storage management for the chain of loaded shared objects. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <ldsodefs.h> + +#include <assert.h> + + +/* Add the new link_map NEW to the end of the namespace list. */ +void +internal_function +_dl_add_to_namespace_list (struct link_map *new, Lmid_t nsid) +{ + /* We modify the list of loaded objects. */ + __rtld_lock_lock_recursive (GL(dl_load_write_lock)); + + if (GL(dl_ns)[nsid]._ns_loaded != NULL) + { + struct link_map *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; + } + else + GL(dl_ns)[nsid]._ns_loaded = new; + ++GL(dl_ns)[nsid]._ns_nloaded; + new->l_serial = GL(dl_load_adds); + ++GL(dl_load_adds); + + __rtld_lock_unlock_recursive (GL(dl_load_write_lock)); +} + + +/* 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) +{ + 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 (struct link_map *) + + sizeof (*newname) + libname_len, 1); + if (new == NULL) + return NULL; + + new->l_real = new; + new->l_symbolic_searchlist.r_list = (struct link_map **) ((char *) (new + 1) + + audit_space); + + new->l_libname = newname + = (struct libname_list *) (new->l_symbolic_searchlist.r_list + 1); + newname->name = (char *) memcpy (newname + 1, libname, libname_len); + /* newname->next = NULL; We use calloc therefore not necessary. */ + newname->dont_free = 1; + + /* When we create the executable link map, or a VDSO link map, we start + with "" for the l_name. In these cases "" points to ld.so rodata + and won't get dumped during core file generation. Therefore to assist + gdb and to create more self-contained core files we adjust l_name to + point at the newly allocated copy (which will get dumped) instead of + the ld.so rodata copy. */ + new->l_name = *realname ? realname : (char *) newname->name + libname_len - 1; + new->l_type = type; + /* If we set the bit now since we know it is never used we avoid + dirtying the cache line later. */ + if ((GLRO(dl_debug_mask) & DL_DEBUG_UNUSED) == 0) + new->l_used = 1; + new->l_loader = loader; +#if 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 '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. */ + int idx = 0; + + if (GL(dl_ns)[nsid]._ns_loaded != NULL) + /* Add the global scope. */ + new->l_scope[idx++] = &GL(dl_ns)[nsid]._ns_loaded->l_searchlist; + + /* 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/REORG.TODO/elf/dl-open.c b/REORG.TODO/elf/dl-open.c new file mode 100644 index 0000000000..cec54db413 --- /dev/null +++ b/REORG.TODO/elf/dl-open.c @@ -0,0 +1,737 @@ +/* Load a shared object at runtime, relocate it, and run its initializer. + Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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 <libc-lock.h> +#include <ldsodefs.h> +#include <caller.h> +#include <sysdep-cancel.h> +#include <tls.h> +#include <stap-probe.h> +#include <atomic.h> + +#include <dl-dst.h> + + +extern int __libc_multiple_libcs; /* Defined in init-first.c. */ + +/* We must be careful 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 of _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. */ + + struct link_namespaces *ns = &GL(dl_ns)[new->l_ns]; + if (ns->_ns_global_scope_alloc == 0) + { + /* This is the first dynamic object given global scope. */ + ns->_ns_global_scope_alloc + = ns->_ns_main_searchlist->r_nlist + to_add + 8; + new_global = (struct link_map **) + malloc (ns->_ns_global_scope_alloc * sizeof (struct link_map *)); + if (new_global == NULL) + { + 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. */ + ns->_ns_main_searchlist->r_list + = memcpy (new_global, ns->_ns_main_searchlist->r_list, + (ns->_ns_main_searchlist->r_nlist + * sizeof (struct link_map *))); + } + else if (ns->_ns_main_searchlist->r_nlist + to_add + > ns->_ns_global_scope_alloc) + { + /* We have to extend the existing array of link maps in the + main map. */ + struct link_map **old_global + = GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list; + size_t new_nalloc = ((ns->_ns_global_scope_alloc + to_add) * 2); + + new_global = (struct link_map **) + malloc (new_nalloc * sizeof (struct link_map *)); + if (new_global == NULL) + goto nomem; + + memcpy (new_global, old_global, + ns->_ns_global_scope_alloc * sizeof (struct link_map *)); + + ns->_ns_global_scope_alloc = new_nalloc; + ns->_ns_main_searchlist->r_list = new_global; + + if (!RTLD_SINGLE_THREAD_P) + THREAD_GSCOPE_WAIT (); + + free (old_global); + } + + /* Now add the new entries. */ + unsigned int new_nlist = ns->_ns_main_searchlist->r_nlist; + for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt) + { + struct link_map *map = new->l_searchlist.r_list[cnt]; + + if (map->l_global == 0) + { + map->l_global = 1; + ns->_ns_main_searchlist->r_list[new_nlist++] = map; + + /* We modify the global scope. Report this. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES)) + _dl_debug_printf ("\nadd %s [%lu] to global scope\n", + map->l_name, map->l_ns); + } + } + atomic_write_barrier (); + ns->_ns_main_searchlist->r_nlist = new_nlist; + + return 0; +} + +/* Search link maps in all namespaces for the DSO that contains the object at + address ADDR. Returns the pointer to the link map of the matching DSO, or + NULL if a match is not found. */ +struct link_map * +internal_function +_dl_find_dso_for_object (const ElfW(Addr) addr) +{ + struct link_map *l; + + /* Find the highest-addressed object that ADDR is not below. */ + for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns) + for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) + if (addr >= l->l_map_start && addr < l->l_map_end + && (l->l_contiguous + || _dl_addr_inside_object (l, (ElfW(Addr)) addr))) + { + assert (ns == l->l_ns); + return l; + } + return NULL; +} +rtld_hidden_def (_dl_find_dso_for_object); + +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 *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; + + struct link_map *l = _dl_find_dso_for_object ((ElfW(Addr)) caller_dlopen); + + if (l) + call_map = l; + + if (args->nsid == __LM_ID_CALLER) + args->nsid = call_map->l_ns; + } + + /* One might be tempted to assert that we are RT_CONSISTENT at this point, but that + may not be true if this is a recursive call to dlopen. */ + _dl_debug_initialize (0, args->nsid); + + /* Load the named object. */ + struct link_map *new; + args->map = new = _dl_map_object (call_map, file, 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; + } + + /* Mark the object as not deletable if the RTLD_NODELETE flags was passed. + Do this early so that we don't skip marking the object if it was + already loaded. */ + if (__glibc_unlikely (mode & RTLD_NODELETE)) + new->l_flags_1 |= DF_1_NODELETE; + + if (__glibc_unlikely (mode & __RTLD_SPROF)) + /* 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 (__glibc_unlikely (new->l_searchlist.r_list != NULL)) + { + /* Let the user know about the opencount. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _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 (unsigned int 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 SHARED + /* Auditing checkpoint: we have added all objects. */ + if (__glibc_unlikely (GLRO(dl_naudit) > 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 (); + LIBC_PROBE (map_complete, 3, args->nsid, r, new); + + /* Print scope information. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES)) + _dl_show_scope (new, 0); + + /* Only do lazy relocation if `LD_BIND_NOW' is not set. */ + int reloc_mode = mode & __RTLD_AUDIT; + if (GLRO(dl_lazy)) + reloc_mode |= mode & RTLD_LAZY; + + /* Sort the objects by dependency for the relocation process. This + allows IFUNC relocations to work and it also means copy + relocation of dependencies are if necessary overwritten. */ + size_t nmaps = 0; + struct link_map *l = new; + do + { + if (! l->l_real->l_relocated) + ++nmaps; + l = l->l_next; + } + while (l != NULL); + struct link_map *maps[nmaps]; + nmaps = 0; + l = new; + do + { + if (! l->l_real->l_relocated) + maps[nmaps++] = l; + l = l->l_next; + } + while (l != NULL); + if (nmaps > 1) + { + uint16_t seen[nmaps]; + memset (seen, '\0', sizeof (seen)); + size_t i = 0; + while (1) + { + ++seen[i]; + struct link_map *thisp = maps[i]; + + /* Find the last object in the list for which the current one is + a dependency and move the current object behind the object + with the dependency. */ + size_t k = nmaps - 1; + while (k > i) + { + struct link_map **runp = maps[k]->l_initfini; + if (runp != NULL) + /* Look through the dependencies of the object. */ + while (*runp != NULL) + if (__glibc_unlikely (*runp++ == thisp)) + { + /* Move the current object to the back past the last + object with it as the dependency. */ + memmove (&maps[i], &maps[i + 1], + (k - i) * sizeof (maps[0])); + maps[k] = thisp; + + if (seen[i + 1] > nmaps - i) + { + ++i; + goto next_clear; + } + + uint16_t this_seen = seen[i]; + memmove (&seen[i], &seen[i + 1], + (k - i) * sizeof (seen[0])); + seen[k] = this_seen; + + goto next; + } + + --k; + } + + if (++i == nmaps) + break; + next_clear: + memset (&seen[i], 0, (nmaps - i) * sizeof (seen[0])); + next:; + } + } + + int relocation_in_progress = 0; + + for (size_t i = nmaps; i-- > 0; ) + { + l = maps[i]; + + if (! relocation_in_progress) + { + /* Notify the debugger that relocations are about to happen. */ + LIBC_PROBE (reloc_start, 2, args->nsid, r); + relocation_in_progress = 1; + } + +#ifdef SHARED + if (__glibc_unlikely (GLRO(dl_profile) != NULL)) + { + /* 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 it was NULL but is not NULL afterwards we must + start the profiling. */ + struct link_map *old_profile_map = GL(dl_profile_map); + + _dl_relocate_object (l, l->l_scope, reloc_mode | RTLD_LAZY, 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, reloc_mode, 0); + } + + /* If the file is not loaded now as a dependency, add the search + list of the newly loaded object to the scope. */ + bool any_tls = false; + unsigned int first_static_tls = new->l_searchlist.r_nlist; + for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) + { + struct link_map *imap = new->l_searchlist.r_list[i]; + int from_scope = 0; + + /* If the initializer has been called already, the object has + not been loaded here and now. */ + if (imap->l_init_called && imap->l_type == lt_loaded) + { + struct r_scope_elem **runp = imap->l_scope; + size_t cnt = 0; + + while (*runp != NULL) + { + if (*runp == &new->l_searchlist) + break; + ++cnt; + ++runp; + } + + if (*runp != NULL) + /* Avoid duplicates. */ + continue; + + if (__glibc_unlikely (cnt + 1 >= imap->l_scope_max)) + { + /* The 'r_scope' array is too small. Allocate a new one + dynamically. */ + size_t new_size; + struct r_scope_elem **newp; + +#define SCOPE_ELEMS(imap) \ + (sizeof (imap->l_scope_mem) / sizeof (imap->l_scope_mem[0])) + + if (imap->l_scope != imap->l_scope_mem + && imap->l_scope_max < SCOPE_ELEMS (imap)) + { + new_size = SCOPE_ELEMS (imap); + newp = imap->l_scope_mem; + } + else + { + new_size = imap->l_scope_max * 2; + newp = (struct r_scope_elem **) + malloc (new_size * sizeof (struct r_scope_elem *)); + if (newp == NULL) + _dl_signal_error (ENOMEM, "dlopen", NULL, + N_("cannot create scope list")); + } + + memcpy (newp, imap->l_scope, cnt * sizeof (imap->l_scope[0])); + struct r_scope_elem **old = imap->l_scope; + + imap->l_scope = newp; + + if (old != imap->l_scope_mem) + _dl_scope_free (old); + + imap->l_scope_max = new_size; + } + + /* First terminate the extended list. Otherwise a thread + might use the new last element and then use the garbage + at offset IDX+1. */ + imap->l_scope[cnt + 1] = NULL; + atomic_write_barrier (); + imap->l_scope[cnt] = &new->l_searchlist; + + /* Print only new scope information. */ + from_scope = cnt; + } + /* 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 + && first_static_tls == new->l_searchlist.r_nlist) + first_static_tls = i; + + /* We have to bump the generation counter. */ + any_tls = true; + } + + /* Print scope information. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES)) + _dl_show_scope (imap, from_scope); + } + + /* 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.")); + + /* We need a second pass for static tls data, because _dl_update_slotinfo + must not be run while calls to _dl_add_to_slotinfo are still pending. */ + for (unsigned int i = first_static_tls; i < new->l_searchlist.r_nlist; ++i) + { + struct link_map *imap = new->l_searchlist.r_list[i]; + + if (imap->l_need_tls_init + && ! imap->l_init_called + && imap->l_tls_blocksize > 0) + { + /* For static TLS we have to allocate the memory here and + now, but we can delay updating the DTV. */ + 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); + } + } + + /* Notify the debugger all new objects have been relocated. */ + if (relocation_in_progress) + LIBC_PROBE (reloc_complete, 3, args->nsid, r, new); + +#ifndef SHARED + DL_STATIC_INIT (new); +#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; + +#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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _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 (__glibc_unlikely (nsid == LM_ID_NEWLM)) + { + /* Find a new namespace. */ + for (nsid = 1; DL_NNS > 1 && nsid < GL(dl_nns); ++nsid) + if (GL(dl_ns)[nsid]._ns_loaded == NULL) + break; + + if (__glibc_unlikely (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()")); + } + else if (nsid == GL(dl_nns)) + { + __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock); + ++GL(dl_nns); + } + + _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 (__glibc_unlikely (nsid != LM_ID_BASE && nsid != __LM_ID_CALLER) + && (__glibc_unlikely (nsid < 0 || nsid >= GL(dl_nns)) + /* This prevents the [NSID] index expressions from being + evaluated, so the compiler won't think that we are + accessing an invalid index here in the !SHARED case where + DL_NNS is 1 and so any NSID != 0 is invalid. */ + || DL_NNS == 1 + || 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); + +#if defined USE_LDCONFIG && !defined MAP_COPY + /* We must unmap the cache file. */ + _dl_unload_cache (); +#endif + + /* See if an error occurred during loading. */ + if (__glibc_unlikely (errstring != NULL)) + { + /* Remove the object from memory. It may be in an inconsistent + state if relocation failed, for example. */ + if (args.map) + { + /* 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; + + _dl_close_worker (args.map, true); + } + + assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT); + + /* Release the lock. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + /* Make a local copy of the error string so that we can release the + memory allocated for it. */ + size_t len_errstring = strlen (errstring) + 1; + char *local_errstring; + 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); + + /* Reraise the error. */ + _dl_signal_error (errcode, objname, NULL, local_errstring); + } + + assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT); + + /* Release the lock. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + return args.map; +} + + +void +_dl_show_scope (struct link_map *l, int from) +{ + _dl_debug_printf ("object=%s [%lu]\n", + DSO_FILENAME (l->l_name), l->l_ns); + if (l->l_scope != NULL) + for (int scope_cnt = from; l->l_scope[scope_cnt] != NULL; ++scope_cnt) + { + _dl_debug_printf (" scope %u:", scope_cnt); + + for (unsigned int cnt = 0; cnt < l->l_scope[scope_cnt]->r_nlist; ++cnt) + if (*l->l_scope[scope_cnt]->r_list[cnt]->l_name) + _dl_debug_printf_c (" %s", + l->l_scope[scope_cnt]->r_list[cnt]->l_name); + else + _dl_debug_printf_c (" %s", RTLD_PROGNAME); + + _dl_debug_printf_c ("\n"); + } + else + _dl_debug_printf (" no scope\n"); + _dl_debug_printf ("\n"); +} diff --git a/REORG.TODO/elf/dl-origin.c b/REORG.TODO/elf/dl-origin.c new file mode 100644 index 0000000000..1e44272a8e --- /dev/null +++ b/REORG.TODO/elf/dl-origin.c @@ -0,0 +1,50 @@ +/* Find path of executable. + Copyright (C) 1998-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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/REORG.TODO/elf/dl-profile.c b/REORG.TODO/elf/dl-profile.c new file mode 100644 index 0000000000..a4f11089a1 --- /dev/null +++ b/REORG.TODO/elf/dl-profile.c @@ -0,0 +1,596 @@ +/* Profiling of shared libraries. + Copyright (C) 1997-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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 <stdint.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; + /* The count field is atomically incremented in _dl_mcount, which + requires it to be properly aligned for its type, and for this + alignment to be visible to the compiler. The amount of data + before an array of this structure is calculated as + expected_size in _dl_start_profile. Everything in that + calculation is a multiple of 4 bytes (in the case of + kcountsize, because it is derived from a subtraction of + page-aligned values, and the corresponding calculation in + __monstartup also ensures it is at least a multiple of the size + of u_long), so all copies of this field do in fact have the + appropriate alignment. */ + uint32_t count __attribute__ ((aligned (__alignof__ (uint32_t)))); + } __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; + 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. */ + struct real_gmon_hdr + { + char cookie[4]; + int32_t version; + char spare[3 * 4]; + } gmon_hdr; + if (sizeof (gmon_hdr) != sizeof (struct gmon_hdr) + || (offsetof (struct real_gmon_hdr, cookie) + != offsetof (struct gmon_hdr, cookie)) + || (offsetof (struct real_gmon_hdr, version) + != offsetof (struct gmon_hdr, version))) + abort (); + + memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie)); + gmon_hdr.version = GMON_SHOBJ_VERSION; + memset (gmon_hdr.spare, '\0', sizeof (gmon_hdr.spare)); + + /* Create the hist_hdr we expect or write. */ + struct real_gmon_hist_hdr + { + char *low_pc; + char *high_pc; + int32_t hist_size; + int32_t prof_rate; + char dimen[15]; + char dimen_abbrev; + } hist_hdr; + if (sizeof (hist_hdr) != sizeof (struct gmon_hist_hdr) + || (offsetof (struct real_gmon_hist_hdr, low_pc) + != offsetof (struct gmon_hist_hdr, low_pc)) + || (offsetof (struct real_gmon_hist_hdr, high_pc) + != offsetof (struct gmon_hist_hdr, high_pc)) + || (offsetof (struct real_gmon_hist_hdr, hist_size) + != offsetof (struct gmon_hist_hdr, hist_size)) + || (offsetof (struct real_gmon_hist_hdr, prof_rate) + != offsetof (struct gmon_hist_hdr, prof_rate)) + || (offsetof (struct real_gmon_hist_hdr, dimen) + != offsetof (struct gmon_hist_hdr, dimen)) + || (offsetof (struct real_gmon_hist_hdr, dimen_abbrev) + != offsetof (struct gmon_hist_hdr, dimen_abbrev))) + abort (); + + hist_hdr.low_pc = (char *) mapstart; + hist_hdr.high_pc = (char *) mapend; + hist_hdr.hist_size = kcountsize / sizeof (HISTCOUNTER); + 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"); + + fd = __open (filename, O_RDWR | O_CREAT | O_NOFOLLOW, 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 = catomic_exchange_and_add (&fromidx, 1) + 1; + froms[newfromidx].here = &data[narcs]; + froms[newfromidx].link = tos[to_index]; + tos[to_index] = newfromidx; + catomic_increment (&narcs); + } + + /* If we still have no entry stop searching and insert. */ + if (*topcindex == 0) + { + uint_fast32_t newarc = catomic_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 = catomic_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; + catomic_increment (&narcs); + + break; + } + + fromp = &froms[*topcindex]; + } + else + /* Found in. */ + break; + } + + /* Increment the counter. */ + catomic_increment (&fromp->here->count); + + done: + ; +} +rtld_hidden_def (_dl_mcount) diff --git a/REORG.TODO/elf/dl-profstub.c b/REORG.TODO/elf/dl-profstub.c new file mode 100644 index 0000000000..0915e29093 --- /dev/null +++ b/REORG.TODO/elf/dl-profstub.c @@ -0,0 +1,41 @@ +/* Helper definitions for profiling of shared libraries. + Copyright (C) 1998-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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/REORG.TODO/elf/dl-reloc.c b/REORG.TODO/elf/dl-reloc.c new file mode 100644 index 0000000000..b3c3a9bbf9 --- /dev/null +++ b/REORG.TODO/elf/dl-reloc.c @@ -0,0 +1,363 @@ +/* Relocate a shared object and resolve its references to other loaded objects. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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 <_itoa.h> +#include <libc-pointer-arith.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 + + +/* 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. */ +int +internal_function +_dl_try_allocate_static_tls (struct link_map *map) +{ + /* If we've already used the variable with dynamic access, or if the + alignment requirements are too high, fail. */ + if (map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET + || map->l_tls_align > GL(dl_tls_static_align)) + { + fail: + return -1; + } + +#if TLS_TCB_AT_TP + size_t freebytes = GL(dl_tls_static_size) - GL(dl_tls_static_used); + if (freebytes < TLS_TCB_SIZE) + goto fail; + freebytes -= TLS_TCB_SIZE; + + size_t blsize = map->l_tls_blocksize + map->l_tls_firstbyte_offset; + if (freebytes < blsize) + goto fail; + + size_t 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 + /* dl_tls_static_used includes the TCB at the beginning. */ + size_t offset = (ALIGN_UP(GL(dl_tls_static_used) + - map->l_tls_firstbyte_offset, + map->l_tls_align) + + map->l_tls_firstbyte_offset); + size_t used = offset + map->l_tls_blocksize; + + if (used > GL(dl_tls_static_size)) + goto fail; + + map->l_tls_offset = offset; + map->l_tls_firstbyte_offset = GL(dl_tls_static_used); + 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; + + return 0; +} + +void +internal_function __attribute_noinline__ +_dl_allocate_static_tls (struct link_map *map) +{ + if (map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET + || _dl_try_allocate_static_tls (map)) + { + _dl_signal_error (0, map->l_name, NULL, N_("\ +cannot allocate memory in static TLS block")); + } +} + +/* 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 + + /* 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); +} + + +void +_dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], + int reloc_mode, 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; + int lazy = reloc_mode & RTLD_LAZY; + int skip_ifunc = reloc_mode & __RTLD_NOIFUNC; + +#ifdef SHARED + /* If we are auditing, install the same handlers we need for profiling. */ + if ((reloc_mode & __RTLD_AUDIT) == 0) + 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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_RELOC)) + _dl_debug_printf ("\nrelocation processing: %s%s\n", + DSO_FILENAME (l->l_name), 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 (__glibc_unlikely (l->l_info[DT_TEXTREL] != NULL)) + { + /* 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 = ALIGN_UP (ph->p_vaddr + ph->p_memsz, GLRO(dl_pagesize)) + - ALIGN_DOWN (ph->p_vaddr, GLRO(dl_pagesize)); + newp->start = PTR_ALIGN_DOWN (ph->p_vaddr, GLRO(dl_pagesize)) + + (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 \ + && __glibc_likely (!dl_symbol_visibility_binds_local_p (*ref))) \ + ? ((__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; \ + if ((version) != NULL && (version)->hash != 0) \ + v = (version); \ + _lr = _dl_lookup_symbol_x (strtab + (*ref)->st_name, l, (ref), \ + scope, v, _tc, \ + DL_LOOKUP_ADD_DEPENDENCY, NULL); \ + l->l_lookup_cache.ret = (*ref); \ + l->l_lookup_cache.value = _lr; })) \ + : l) + +#include "dynamic-link.h" + + ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling, skip_ifunc); + +#ifndef PROF + if (__glibc_unlikely (consider_profiling) + && l->l_info[DT_PLTRELSZ] != NULL) + { + /* 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. */ + size_t sizeofrel = l->l_info[DT_PLTREL]->d_un.d_val == DT_RELA + ? sizeof (ElfW(Rela)) + : sizeof (ElfW(Rel)); + size_t relcount = l->l_info[DT_PLTRELSZ]->d_un.d_val / sizeofrel; + l->l_reloc_result = calloc (sizeof (l->l_reloc_result[0]), relcount); + + if (l->l_reloc_result == NULL) + { + errstring = N_("\ +%s: out of memory to store relocation results for %s\n"); + _dl_fatal_printf (errstring, RTLD_PROGNAME, l->l_name); + } + } +#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; + } + +#ifdef CLEAR_CACHE + CLEAR_CACHE (textrels->start, textrels->start + textrels->len); +#endif + + 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 = ALIGN_DOWN((l->l_addr + + l->l_relro_addr), + GLRO(dl_pagesize)); + ElfW(Addr) end = ALIGN_DOWN((l->l_addr + + l->l_relro_addr + + l->l_relro_size), + GLRO(dl_pagesize)); + 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) +{ +#define DIGIT(b) _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/REORG.TODO/elf/dl-runtime.c b/REORG.TODO/elf/dl-runtime.c new file mode 100644 index 0000000000..7d1d240403 --- /dev/null +++ b/REORG.TODO/elf/dl-runtime.c @@ -0,0 +1,480 @@ +/* On-demand PLT fixup for shared objects. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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 <sysdep-cancel.h> +#include "dynamic-link.h" +#include <tls.h> +#include <dl-irel.h> + + +#if (!ELF_MACHINE_NO_RELA && !defined ELF_MACHINE_PLT_REL) \ + || ELF_MACHINE_NO_REL +# define PLTREL ElfW(Rela) +#else +# define PLTREL ElfW(Rel) +#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 + +#ifndef reloc_offset +# define reloc_offset reloc_arg +# define reloc_index reloc_arg / sizeof (PLTREL) +#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. */ + +DL_FIXUP_VALUE_TYPE +attribute_hidden __attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE +_dl_fixup ( +# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS + ELF_MACHINE_RUNTIME_FIXUP_ARGS, +# endif + struct link_map *l, ElfW(Word) reloc_arg) +{ + 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; + } + + /* We need to keep the scope around so do some locking. This is + not necessary for objects which cannot be unloaded or when + we are not using any threads (yet). */ + int flags = DL_LOOKUP_ADD_DEPENDENCY; + if (!RTLD_SINGLE_THREAD_P) + { + THREAD_GSCOPE_SET_FLAG (); + flags |= DL_LOOKUP_GSCOPE_LOCK; + } + +#ifdef RTLD_ENABLE_FOREIGN_CALL + RTLD_ENABLE_FOREIGN_CALL; +#endif + + result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, + version, ELF_RTYPE_CLASS_PLT, flags, NULL); + + /* We are done with the global scope. */ + if (!RTLD_SINGLE_THREAD_P) + THREAD_GSCOPE_RESET_FLAG (); + +#ifdef RTLD_FINALIZE_FOREIGN_CALL + RTLD_FINALIZE_FOREIGN_CALL; +#endif + + /* 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); + + if (sym != NULL + && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0)) + value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value)); + + /* Finally, fix up the plt itself. */ + if (__glibc_unlikely (GLRO(dl_bind_not))) + return value; + + return elf_machine_fixup_plt (l, result, reloc, rel_addr, value); +} + +#ifndef PROF +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_arg, + ElfW(Addr) retaddr, void *regs, long int *framesizep) +{ + void (*mcount_fct) (ElfW(Addr), ElfW(Addr)) = _dl_mcount; + + if (l->l_reloc_result == NULL) + { + /* BZ #14843: ELF_DYNAMIC_RELOCATE is called before l_reloc_result + is allocated. We will get here if ELF_DYNAMIC_RELOCATE calls a + resolver function to resolve an IRELATIVE relocation and that + resolver calls a function that is not yet resolved (lazy). For + example, the resolver in x86-64 libm.so calls __get_cpu_features + defined in libc.so. Skip audit and resolve the external function + in this case. */ + *framesizep = -1; + return _dl_fixup ( +# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS +# ifndef ELF_MACHINE_RUNTIME_FIXUP_PARAMS +# error Please define ELF_MACHINE_RUNTIME_FIXUP_PARAMS. +# endif + ELF_MACHINE_RUNTIME_FIXUP_PARAMS, +# endif + l, reloc_arg); + } + + /* 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_index]; + 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; + } + + /* We need to keep the scope around so do some locking. This is + not necessary for objects which cannot be unloaded or when + we are not using any threads (yet). */ + int flags = DL_LOOKUP_ADD_DEPENDENCY; + if (!RTLD_SINGLE_THREAD_P) + { + THREAD_GSCOPE_SET_FLAG (); + flags |= DL_LOOKUP_GSCOPE_LOCK; + } + + result = _dl_lookup_symbol_x (strtab + refsym->st_name, l, + &defsym, l->l_scope, version, + ELF_RTYPE_CLASS_PLT, flags, NULL); + + /* We are done with the global scope. */ + if (!RTLD_SINGLE_THREAD_P) + THREAD_GSCOPE_RESET_FLAG (); + + /* Currently result contains the base load address (or link map) + of the object that defines sym. Now add in the symbol + offset. */ + value = DL_FIXUP_MAKE_VALUE (result, + defsym != NULL + ? LOOKUP_VALUE_ADDRESS (result) + + defsym->st_value : 0); + + if (defsym != NULL + && __builtin_expect (ELFW(ST_TYPE) (defsym->st_info) + == STT_GNU_IFUNC, 0)) + value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value)); + } + 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); + + if (__builtin_expect (ELFW(ST_TYPE) (refsym->st_info) + == STT_GNU_IFUNC, 0)) + value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (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 flags = 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) + { + 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) + { + flags |= 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 = flags; + 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 (__glibc_likely (! GLRO(dl_bind_not))) + *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 flags = 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) + { + 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) + { + flags |= 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 */ + + +#include <stdio.h> +void +ARCH_FIXUP_ATTRIBUTE +_dl_call_pltexit (struct link_map *l, ElfW(Word) reloc_arg, + 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_index]; + 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 (reloc_result->addr); + + /* 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/REORG.TODO/elf/dl-sbrk.c b/REORG.TODO/elf/dl-sbrk.c new file mode 100644 index 0000000000..4713a92694 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/dl-scope.c b/REORG.TODO/elf/dl-scope.c new file mode 100644 index 0000000000..bb924afa8f --- /dev/null +++ b/REORG.TODO/elf/dl-scope.c @@ -0,0 +1,57 @@ +/* Memory handling for the scope data structures. + Copyright (C) 2009-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <stdlib.h> +#include <ldsodefs.h> +#include <sysdep-cancel.h> + + +int +_dl_scope_free (void *old) +{ + struct dl_scope_free_list *fsl; +#define DL_SCOPE_FREE_LIST_SIZE (sizeof (fsl->list) / sizeof (fsl->list[0])) + + if (RTLD_SINGLE_THREAD_P) + free (old); + else if ((fsl = GL(dl_scope_free_list)) == NULL) + { + GL(dl_scope_free_list) = fsl = malloc (sizeof (*fsl)); + if (fsl == NULL) + { + THREAD_GSCOPE_WAIT (); + free (old); + return 1; + } + else + { + fsl->list[0] = old; + fsl->count = 1; + } + } + else if (fsl->count < DL_SCOPE_FREE_LIST_SIZE) + fsl->list[fsl->count++] = old; + else + { + THREAD_GSCOPE_WAIT (); + while (fsl->count > 0) + free (fsl->list[--fsl->count]); + return 1; + } + return 0; +} diff --git a/REORG.TODO/elf/dl-support.c b/REORG.TODO/elf/dl-support.c new file mode 100644 index 0000000000..c22be854f4 --- /dev/null +++ b/REORG.TODO/elf/dl-support.c @@ -0,0 +1,389 @@ +/* Support for dynamic linking code in static libc. + Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* 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 <sys/param.h> +#include <stdint.h> +#include <ldsodefs.h> +#include <dl-machine.h> +#include <libc-lock.h> +#include <dl-cache.h> +#include <dl-librecon.h> +#include <dl-procinfo.h> +#include <unsecvars.h> +#include <hp-timing.h> +#include <stackinfo.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; + +/* A dummy link map for the executable, used by dlopen to access the global + scope. We don't export any symbols ourselves, so this can be minimal. */ +static struct link_map _dl_main_map = + { + .l_name = (char *) "", + .l_real = &_dl_main_map, + .l_ns = LM_ID_BASE, + .l_libname = &(struct libname_list) { .name = "", .dont_free = 1 }, + .l_searchlist = + { + .r_list = &(struct link_map *) { &_dl_main_map }, + .r_nlist = 1, + }, + .l_symbolic_searchlist = { .r_list = &(struct link_map *) { NULL } }, + .l_type = lt_executable, + .l_scope_mem = { &_dl_main_map.l_searchlist }, + .l_scope_max = (sizeof (_dl_main_map.l_scope_mem) + / sizeof (_dl_main_map.l_scope_mem[0])), + .l_scope = _dl_main_map.l_scope_mem, + .l_local_scope = { &_dl_main_map.l_searchlist }, + .l_used = 1, + .l_tls_offset = NO_TLS_OFFSET, + .l_serial = 1, + }; + +/* Namespace information. */ +struct link_namespaces _dl_ns[DL_NNS] = + { + [LM_ID_BASE] = + { + ._ns_loaded = &_dl_main_map, + ._ns_nloaded = 1, + ._ns_main_searchlist = &_dl_main_map.l_searchlist, + } + }; +size_t _dl_nns = 1; + +/* Incremented whenever something may have been added to dl_loaded. */ +unsigned long long _dl_load_adds = 1; + +/* Fake scope of the main application. */ +struct r_scope_elem _dl_initial_searchlist = + { + .r_list = &(struct link_map *) { &_dl_main_map }, + .r_nlist = 1, + }; + +#ifndef HAVE_INLINED_SYSCALLS +/* Nonzero during startup. */ +int _dl_starting_up = 1; +#endif + +/* Random data provided by the kernel. */ +void *_dl_random; + +/* Get architecture specific initializer. */ +#include <dl-procinfo.c> + +/* Initial value of the CPU clock. */ +#ifndef HP_TIMING_NONAVAIL +hp_timing_t _dl_cpuclock_offset; +#endif + +void (*_dl_init_static_tls) (struct link_map *) = &_dl_nothread_init_static_tls; + +size_t _dl_pagesize = EXEC_PAGESIZE; + +int _dl_inhibit_cache; + +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(auxv_t) *_dl_auxv; +const ElfW(Phdr) *_dl_phdr; +size_t _dl_phnum; +uint64_t _dl_hwcap __attribute__ ((nocommon)); +uint64_t _dl_hwcap2 __attribute__ ((nocommon)); + +/* The value of the FPU control word the kernel will preset in hardware. */ +fpu_control_t _dl_fpu_control = _FPU_DEFAULT; + +#if !HAVE_TUNABLES +/* This is not initialized to HWCAP_IMPORTANT, matching the definition + of _dl_important_hwcaps, below, where no hwcap strings are ever + used. This mask is still used to mediate the lookups in the cache + file. Since there is no way to set this nonzero (we don't grok the + LD_HWCAP_MASK environment variable here), there is no real point in + setting _dl_hwcap nonzero below, but we do anyway. */ +uint64_t _dl_hwcap_mask __attribute__ ((nocommon)); +#endif + +/* Prevailing state of the stack. Generally this includes PF_X, indicating it's + * executable but this isn't true for all platforms. */ +ElfW(Word) _dl_stack_flags = DEFAULT_STACK_PERMS; + +/* 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; + + +/* Function in libpthread to wait for termination of lookups. */ +void (*_dl_wait_lookup_done) (void); + +struct dl_scope_free_list *_dl_scope_free_list; + +#ifdef NEED_DL_SYSINFO +/* Needed for improved syscall handling on at least x86/Linux. */ +uintptr_t _dl_sysinfo = DL_SYSINFO_DEFAULT; +#endif +#ifdef NEED_DL_SYSINFO_DSO +/* Address of the ELF headers in the vsyscall page. */ +const ElfW(Ehdr) *_dl_sysinfo_dso; + +struct link_map *_dl_sysinfo_map; + +# include "get-dynamic-info.h" +#endif +#include "setup-vdso.h" + +/* 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) +/* This lock is used to keep __dl_iterate_phdr from inspecting the + list of loaded objects while an object is added to or removed from + that list. */ +__rtld_lock_define_initialized_recursive (, _dl_load_write_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; + + _dl_auxv = av; + for (; av->a_type != AT_NULL; ++av) + switch (av->a_type) + { + case AT_PAGESZ: + if (av->a_un.a_val != 0) + 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) = (const void *) av->a_un.a_val; + break; + case AT_PHNUM: + GL(dl_phnum) = 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_HWCAP2: + GLRO(dl_hwcap2) = (unsigned long int) 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: + GL(dl_sysinfo) = av->a_un.a_val; + break; +#endif +#ifdef 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; + case AT_RANDOM: + _dl_random = (void *) av->a_un.a_val; + break; +# ifdef DL_PLATFORM_AUXV + DL_PLATFORM_AUXV +# endif + } + if (seen == 0xf) + { + __libc_enable_secure = uid != 0 || gid != 0; + __libc_enable_secure_decided = 1; + } +} +#endif + + +void +internal_function +_dl_non_dynamic_init (void) +{ + _dl_main_map.l_origin = _dl_get_origin (); + _dl_main_map.l_phdr = GL(dl_phdr); + _dl_main_map.l_phnum = GL(dl_phnum); + + if (HP_SMALL_TIMING_AVAIL) + HP_TIMING_NOW (_dl_cpuclock_offset); + + _dl_verbose = *(getenv ("LD_WARN") ?: "") == '\0' ? 0 : 1; + + /* Set up the data structures for the system-supplied DSO early, + so they can influence _dl_init_paths. */ + setup_vdso (NULL, NULL); + + /* Initialize the data structures for the search paths for shared + objects. */ + _dl_init_paths (getenv ("LD_LIBRARY_PATH")); + + /* Remember the last search directory added at startup. */ + _dl_init_all_dirs = GL(dl_all_dirs); + + _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 !HAVE_TUNABLES + if (__access ("/etc/suid-debug", F_OK) != 0) + __unsetenv ("MALLOC_CHECK_"); +#endif + } + +#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; + } +} + +#ifdef DL_SYSINFO_IMPLEMENTATION +DL_SYSINFO_IMPLEMENTATION +#endif diff --git a/REORG.TODO/elf/dl-sym.c b/REORG.TODO/elf/dl-sym.c new file mode 100644 index 0000000000..7cd6e97643 --- /dev/null +++ b/REORG.TODO/elf/dl-sym.c @@ -0,0 +1,274 @@ +/* Look up a symbol in a shared object loaded by `dlopen'. + Copyright (C) 1999-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <stddef.h> +#include <setjmp.h> +#include <stdlib.h> +#include <libintl.h> + +#include <dlfcn.h> +#include <ldsodefs.h> +#include <dl-hash.h> +#include <sysdep-cancel.h> +#include <dl-tls.h> +#include <dl-irel.h> + + +#ifdef 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 + + +struct call_dl_lookup_args +{ + /* Arguments to do_dlsym. */ + struct link_map *map; + const char *name; + struct r_found_version *vers; + int flags; + + /* Return values of do_dlsym. */ + lookup_t loadbase; + const ElfW(Sym) **refp; +}; + +static void +call_dl_lookup (void *ptr) +{ + struct call_dl_lookup_args *args = (struct call_dl_lookup_args *) ptr; + args->map = GLRO(dl_lookup_symbol_x) (args->name, args->map, args->refp, + args->map->l_scope, args->vers, 0, + args->flags, NULL); +} + + +static void * +internal_function +do_sym (void *handle, const char *name, void *who, + struct r_found_version *vers, int flags) +{ + const ElfW(Sym) *ref = NULL; + lookup_t result; + ElfW(Addr) caller = (ElfW(Addr)) who; + + struct link_map *l = _dl_find_dso_for_object (caller); + /* If the address is not recognized the call comes from the main + program (we hope). */ + struct link_map *match = l ? l : GL(dl_ns)[LM_ID_BASE]._ns_loaded; + + if (handle == RTLD_DEFAULT) + { + /* Search the global scope. We have the simple case where + we look up in the scope of an object which was part of + the initial binary. And then the more complex part + where the object is dynamically loaded and the scope + array can change. */ + if (RTLD_SINGLE_THREAD_P) + result = GLRO(dl_lookup_symbol_x) (name, match, &ref, + match->l_scope, vers, 0, + flags | DL_LOOKUP_ADD_DEPENDENCY, + NULL); + else + { + struct call_dl_lookup_args args; + args.name = name; + args.map = match; + args.vers = vers; + args.flags + = flags | DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK; + args.refp = &ref; + + THREAD_GSCOPE_SET_FLAG (); + + const char *objname; + const char *errstring = NULL; + bool malloced; + int err = _dl_catch_error (&objname, &errstring, &malloced, + call_dl_lookup, &args); + + THREAD_GSCOPE_RESET_FLAG (); + + if (__glibc_unlikely (errstring != NULL)) + { + /* The lookup was unsuccessful. Rethrow the error. */ + char *errstring_dup = strdupa (errstring); + char *objname_dup = strdupa (objname); + if (malloced) + free ((char *) errstring); + + _dl_signal_error (err, objname_dup, NULL, errstring_dup); + /* NOTREACHED */ + } + + result = args.map; + } + } + else if (handle == RTLD_NEXT) + { + if (__glibc_unlikely (match == GL(dl_ns)[LM_ID_BASE]._ns_loaded)) + { + if (match == NULL + || caller < match->l_map_start + || caller >= match->l_map_end) + _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; + +#ifdef 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); + + /* Resolve indirect function address. */ + if (__glibc_unlikely (ELFW(ST_TYPE) (ref->st_info) == STT_GNU_IFUNC)) + { + DL_FIXUP_VALUE_TYPE fixup + = DL_FIXUP_MAKE_VALUE (result, (ElfW(Addr)) value); + fixup = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (fixup)); + value = (void *) DL_FIXUP_VALUE_CODE_ADDR (fixup); + } + +#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 (__glibc_unlikely (GLRO(dl_naudit) > 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/REORG.TODO/elf/dl-symaddr.c b/REORG.TODO/elf/dl-symaddr.c new file mode 100644 index 0000000000..5caed4ba21 --- /dev/null +++ b/REORG.TODO/elf/dl-symaddr.c @@ -0,0 +1,33 @@ +/* Get the symbol address. Generic version. + Copyright (C) 1999-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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; +} +rtld_hidden_def (_dl_symbol_address) diff --git a/REORG.TODO/elf/dl-sysdep-open.h b/REORG.TODO/elf/dl-sysdep-open.h new file mode 100644 index 0000000000..91851848b3 --- /dev/null +++ b/REORG.TODO/elf/dl-sysdep-open.h @@ -0,0 +1,45 @@ +/* System-specific call to open a shared object by name. Stub version. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _DL_SYSDEP_OPEN_H +#define _DL_SYSDEP_OPEN_H 1 + +#include <assert.h> +#include <stddef.h> + +/* NAME is a name without slashes, as it appears in a DT_NEEDED entry + or a dlopen call's argument or suchlike. NAMELEN is (strlen (NAME) + 1). + + Find NAME in an OS-dependent fashion, and return its "real" name. + Optionally fill in *FD with a file descriptor open on that file (or + else leave its initial value of -1). The return value is a new + malloc'd string, which will be free'd by the caller. If NAME is + resolved to an actual file that can be opened, then the return + value should name that file (and if *FD was not set, then a normal + __open call on that string will be made). If *FD was set by some + other means than a normal open and there is no "real" name to use, + then __strdup (NAME) is fine (modulo error checking). */ + +static inline char * +_dl_sysdep_open_object (const char *name, size_t namelen, int *fd) +{ + assert (*fd == -1); + return NULL; +} + +#endif /* dl-sysdep-open.h */ diff --git a/REORG.TODO/elf/dl-sysdep.c b/REORG.TODO/elf/dl-sysdep.c new file mode 100644 index 0000000000..4053ff3c07 --- /dev/null +++ b/REORG.TODO/elf/dl-sysdep.c @@ -0,0 +1,360 @@ +/* Operating system support for run-time dynamic linker. Generic Unix version. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* We conditionalize the whole of this file rather than simply eliding it + from the static build, because other sysdeps/ versions of this file + might define things needed by a static build. */ + +#ifdef SHARED + +#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 <_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> + +#include <dl-tunables.h> + +extern char **_environ attribute_hidden; +extern char _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; +rtld_hidden_data_def (__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) +void *_dl_random attribute_relro = NULL; + +#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, ElfW(auxv_t) *auxv)) +{ + 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, _dl_argv, _environ, + GLRO(dl_auxv)); + + user_entry = (ElfW(Addr)) ENTRY_POINT; + GLRO(dl_platform) = NULL; /* Default to nothing known about the platform. */ + + for (av = GLRO(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 + __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_HWCAP2: + GLRO(dl_hwcap2) = (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 +#ifdef NEED_DL_SYSINFO_DSO + case AT_SYSINFO_EHDR: + GLRO(dl_sysinfo_dso) = (void *) av->a_un.a_val; + break; +#endif + case AT_RANDOM: + _dl_random = (void *) av->a_un.a_val; + break; +#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. */ + __libc_enable_secure = uid | gid; + } +#endif + +#ifndef HAVE_AUX_PAGESIZE + if (GLRO(dl_pagesize) == 0) + GLRO(dl_pagesize) = __getpagesize (); +#endif + +#ifdef NEED_DL_SYSINFO + if (new_sysinfo != 0) + { +# ifdef NEED_DL_SYSINFO_DSO + /* Only set the sysinfo value if we also have the vsyscall DSO. */ + if (GLRO(dl_sysinfo_dso) != 0) +# endif + GLRO(dl_sysinfo) = new_sysinfo; + } +#endif + + __tunables_init (_environ); + +#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 - (char *) 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 (__libc_enable_secure, 0)) + __libc_check_standard_fds (); + + (*dl_main) (phdr, phnum, &user_entry, GLRO(dl_auxv)); + 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 = GLRO(dl_auxv); av->a_type != AT_NULL; ++av) + { + static const struct + { + const char label[17]; + enum { unknown = 0, dec, hex, str, ignore } form : 8; + } auxvars[] = + { + [AT_EXECFD - 2] = { "EXECFD: ", dec }, + [AT_EXECFN - 2] = { "EXECFN: ", str }, + [AT_PHDR - 2] = { "PHDR: 0x", hex }, + [AT_PHENT - 2] = { "PHENT: ", dec }, + [AT_PHNUM - 2] = { "PHNUM: ", dec }, + [AT_PAGESZ - 2] = { "PAGESZ: ", dec }, + [AT_BASE - 2] = { "BASE: 0x", hex }, + [AT_FLAGS - 2] = { "FLAGS: 0x", hex }, + [AT_ENTRY - 2] = { "ENTRY: 0x", hex }, + [AT_NOTELF - 2] = { "NOTELF: ", hex }, + [AT_UID - 2] = { "UID: ", dec }, + [AT_EUID - 2] = { "EUID: ", dec }, + [AT_GID - 2] = { "GID: ", dec }, + [AT_EGID - 2] = { "EGID: ", dec }, + [AT_PLATFORM - 2] = { "PLATFORM: ", str }, + [AT_HWCAP - 2] = { "HWCAP: ", hex }, + [AT_CLKTCK - 2] = { "CLKTCK: ", dec }, + [AT_FPUCW - 2] = { "FPUCW: ", hex }, + [AT_DCACHEBSIZE - 2] = { "DCACHEBSIZE: 0x", hex }, + [AT_ICACHEBSIZE - 2] = { "ICACHEBSIZE: 0x", hex }, + [AT_UCACHEBSIZE - 2] = { "UCACHEBSIZE: 0x", hex }, + [AT_IGNOREPPC - 2] = { "IGNOREPPC", ignore }, + [AT_SECURE - 2] = { "SECURE: ", dec }, + [AT_BASE_PLATFORM - 2] = { "BASE_PLATFORM:", str }, + [AT_SYSINFO - 2] = { "SYSINFO: 0x", hex }, + [AT_SYSINFO_EHDR - 2] = { "SYSINFO_EHDR: 0x", hex }, + [AT_RANDOM - 2] = { "RANDOM: 0x", hex }, + [AT_HWCAP2 - 2] = { "HWCAP2: 0x", hex }, + }; + unsigned int idx = (unsigned int) (av->a_type - 2); + + if ((unsigned int) av->a_type < 2u + || (idx < sizeof (auxvars) / sizeof (auxvars[0]) + && auxvars[idx].form == ignore)) + continue; + + assert (AT_NULL == 0); + assert (AT_IGNORE == 1); + + if (av->a_type == AT_HWCAP || av->a_type == AT_HWCAP2) + { + /* These are handled in a special way per platform. */ + if (_dl_procinfo (av->a_type, 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 ("AT_%s%s\n", auxvars[idx].label, val); + + continue; + } + + /* Unknown value: print a generic line. */ + char buf2[17]; + buf2[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); + } +} + +#endif diff --git a/REORG.TODO/elf/dl-tls.c b/REORG.TODO/elf/dl-tls.c new file mode 100644 index 0000000000..5aba33b3fa --- /dev/null +++ b/REORG.TODO/elf/dl-tls.c @@ -0,0 +1,953 @@ +/* Thread-local storage handling in the ELF dynamic linker. Generic version. + Copyright (C) 2002-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <errno.h> +#include <libintl.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/param.h> +#include <atomic.h> + +#include <tls.h> +#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 + + +/* Out-of-memory handler. */ +static void +__attribute__ ((__noreturn__)) +oom (void) +{ + _dl_fatal_printf ("cannot allocate memory for thread-local data: ABORT\n"); +} + + +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; +} + + +size_t +internal_function +_dl_count_modids (void) +{ + /* It is rare that we have gaps; see elf/dl-open.c (_dl_open) where + we fail to load a module and unload it leaving a gap. If we don't + have gaps then the number of modids is the current maximum so + return that. */ + if (__glibc_likely (!GL(dl_tls_dtv_gaps))) + return GL(dl_tls_max_dtv_idx); + + /* We have gaps and are forced to count the non-NULL entries. */ + size_t n = 0; + struct dtv_slotinfo_list *runp = GL(dl_tls_dtv_slotinfo_list); + while (runp != NULL) + { + for (size_t i = 0; i < runp->len; ++i) + if (runp->slotinfo[i].map != NULL) + ++n; + + runp = runp->next; + } + + return n; +} + + +#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; +} +#endif /* SHARED */ + +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); +} + +/* Derive the location of the pointer to the start of the original + allocation (before alignment) from the pointer to the TCB. */ +static inline void ** +tcb_to_pointer_to_free_location (void *tcb) +{ +#if TLS_TCB_AT_TP + /* The TCB follows the TLS blocks, and the pointer to the front + follows the TCB. */ + void **original_pointer_location = tcb + TLS_TCB_SIZE; +#elif TLS_DTV_AT_TP + /* The TCB comes first, preceded by the pre-TCB, and the pointer is + before that. */ + void **original_pointer_location = tcb - TLS_PRE_TCB_SIZE - sizeof (void *); +#endif + return original_pointer_location; +} + +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; +#endif + + /* Perform the allocation. Reserve space for the required alignment + and the pointer to the original allocation. */ + size_t alignment = GL(dl_tls_static_align); + void *allocated = malloc (size + alignment + sizeof (void *)); + if (__glibc_unlikely (allocated == NULL)) + return NULL; + + /* Perform alignment and allocate the DTV. */ +#if TLS_TCB_AT_TP + /* The TCB follows the TLS blocks, which determine the alignment. + (TCB alignment requirements have been taken into account when + calculating GL(dl_tls_static_align).) */ + void *aligned = (void *) roundup ((uintptr_t) allocated, alignment); + result = aligned + 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 + /* Pre-TCB and TCB come before the TLS blocks. The layout computed + in _dl_determine_tlsoffset assumes that the TCB is aligned to the + TLS block alignment, and not just the TLS blocks after it. This + can leave an unused alignment gap between the TCB and the TLS + blocks. */ + result = (void *) roundup + (sizeof (void *) + TLS_PRE_TCB_SIZE + (uintptr_t) allocated, + alignment); + + /* 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 (result - TLS_PRE_TCB_SIZE, '\0', TLS_PRE_TCB_SIZE + TLS_TCB_SIZE); +#endif + + /* Record the value of the original pointer for later + deallocation. */ + *tcb_to_pointer_to_free_location (result) = allocated; + + result = allocate_dtv (result); + if (result == NULL) + free (allocated); + return result; +} + + +#ifndef SHARED +extern dtv_t _dl_static_dtv[]; +# define _dl_initial_dtv (&_dl_static_dtv[1]) +#endif + +static dtv_t * +_dl_resize_dtv (dtv_t *dtv) +{ + /* Resize the dtv. */ + dtv_t *newp; + /* Load GL(dl_tls_max_dtv_idx) atomically since it may be written to by + other threads concurrently. */ + size_t newsize + = atomic_load_acquire (&GL(dl_tls_max_dtv_idx)) + DTV_SURPLUS; + size_t oldsize = dtv[-1].counter; + + if (dtv == GL(dl_initial_dtv)) + { + /* This is the initial dtv that was either statically allocated in + __libc_setup_tls or 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], (2 + 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)); + + /* Return the generation counter. */ + return &newp[1]; +} + + +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; + + /* Check if the current dtv is big enough. */ + if (dtv[-1].counter < GL(dl_tls_max_dtv_idx)) + { + /* Resize the dtv. */ + dtv = _dl_resize_dtv (dtv); + + /* Install this new dtv in the thread data structures. */ + INSTALL_DTV (result, &dtv[-1]); + } + + /* 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. */ + assert (listp->slotinfo[cnt].gen <= GL(dl_tls_generation)); + maxgen = MAX (maxgen, listp->slotinfo[cnt].gen); + + dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED; + dtv[map->l_tls_modid].pointer.to_free = NULL; + + if (map->l_tls_offset == NO_TLS_OFFSET + || map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET) + continue; + + assert (map->l_tls_modid == total + 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 + + /* Set up the DTV entry. The simplified __tls_get_addr that + some platforms use in static programs requires it. */ + dtv[map->l_tls_modid].pointer.val = dest; + + /* Copy the initialization image and clear the BSS part. */ + 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) + free (dtv[1 + cnt].pointer.to_free); + + /* The array starts with dtv[-1]. */ + if (dtv != GL(dl_initial_dtv)) + free (dtv - 1); + + if (dealloc_tcb) + free (*tcb_to_pointer_to_free_location (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 +# define GET_ADDR_PARAM 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 + +/* Allocate one DTV entry. */ +static struct dtv_pointer +allocate_dtv_entry (size_t alignment, size_t size) +{ + if (powerof2 (alignment) && alignment <= _Alignof (max_align_t)) + { + /* The alignment is supported by malloc. */ + void *ptr = malloc (size); + return (struct dtv_pointer) { ptr, ptr }; + } + + /* Emulate memalign to by manually aligning a pointer returned by + malloc. First compute the size with an overflow check. */ + size_t alloc_size = size + alignment; + if (alloc_size < size) + return (struct dtv_pointer) {}; + + /* Perform the allocation. This is the pointer we need to free + later. */ + void *start = malloc (alloc_size); + if (start == NULL) + return (struct dtv_pointer) {}; + + /* Find the aligned position within the larger allocation. */ + void *aligned = (void *) roundup ((uintptr_t) start, alignment); + + return (struct dtv_pointer) { .val = aligned, .to_free = start }; +} + +static struct dtv_pointer +allocate_and_init (struct link_map *map) +{ + struct dtv_pointer result = allocate_dtv_entry + (map->l_tls_align, map->l_tls_blocksize); + if (result.val == NULL) + oom (); + + /* Initialize the memory. */ + memset (__mempcpy (result.val, map->l_tls_initimage, + map->l_tls_initimage_size), + '\0', map->l_tls_blocksize - map->l_tls_initimage_size); + + return result; +} + + +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 (dtv[-1].counter >= total + cnt) + { + /* If this modid was used at some point the memory + might still be allocated. */ + free (dtv[total + cnt].pointer.to_free); + dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED; + dtv[total + cnt].pointer.to_free = NULL; + } + + 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) + { + /* Resize the dtv. */ + dtv = _dl_resize_dtv (dtv); + + assert (modid <= dtv[-1].counter); + + /* 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. */ + free (dtv[modid].pointer.to_free); + dtv[modid].pointer.val = TLS_DTV_UNALLOCATED; + dtv[modid].pointer.to_free = NULL; + + 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; +} + + +static void * +__attribute_noinline__ +tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map) +{ + /* 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; + } + + /* Make sure that, if a dlopen running in parallel forces the + variable into static storage, we'll wait until the address in the + static TLS block is set up, and use that. If we're undecided + yet, make sure we make the decision holding the lock as well. */ + if (__glibc_unlikely (the_map->l_tls_offset + != FORCED_DYNAMIC_TLS_OFFSET)) + { + __rtld_lock_lock_recursive (GL(dl_load_lock)); + if (__glibc_likely (the_map->l_tls_offset == NO_TLS_OFFSET)) + { + the_map->l_tls_offset = FORCED_DYNAMIC_TLS_OFFSET; + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + } + else if (__glibc_likely (the_map->l_tls_offset + != FORCED_DYNAMIC_TLS_OFFSET)) + { +#if TLS_TCB_AT_TP + void *p = (char *) THREAD_SELF - the_map->l_tls_offset; +#elif TLS_DTV_AT_TP + void *p = (char *) THREAD_SELF + the_map->l_tls_offset + TLS_PRE_TCB_SIZE; +#else +# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" +#endif + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + dtv[GET_ADDR_MODULE].pointer.to_free = NULL; + dtv[GET_ADDR_MODULE].pointer.val = p; + + return (char *) p + GET_ADDR_OFFSET; + } + else + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + } + struct dtv_pointer result = allocate_and_init (the_map); + dtv[GET_ADDR_MODULE].pointer = result; + assert (result.to_free != NULL); + + return (char *) result.val + GET_ADDR_OFFSET; +} + + +static struct link_map * +__attribute_noinline__ +update_get_addr (GET_ADDR_ARGS) +{ + struct link_map *the_map = _dl_update_slotinfo (GET_ADDR_MODULE); + dtv_t *dtv = THREAD_DTV (); + + void *p = dtv[GET_ADDR_MODULE].pointer.val; + + if (__glibc_unlikely (p == TLS_DTV_UNALLOCATED)) + return tls_get_addr_tail (GET_ADDR_PARAM, dtv, the_map); + + return (void *) p + GET_ADDR_OFFSET; +} + +/* For all machines that have a non-macro version of __tls_get_addr, we + want to use rtld_hidden_proto/rtld_hidden_def in order to call the + internal alias for __tls_get_addr from ld.so. This avoids a PLT entry + in ld.so for __tls_get_addr. */ + +#ifndef __tls_get_addr +extern void * __tls_get_addr (GET_ADDR_ARGS); +rtld_hidden_proto (__tls_get_addr) +rtld_hidden_def (__tls_get_addr) +#endif + +/* 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 (); + + if (__glibc_unlikely (dtv[0].counter != GL(dl_tls_generation))) + return update_get_addr (GET_ADDR_PARAM); + + void *p = dtv[GET_ADDR_MODULE].pointer.val; + + if (__glibc_unlikely (p == TLS_DTV_UNALLOCATED)) + return tls_get_addr_tail (GET_ADDR_PARAM, dtv, NULL); + + 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 * +_dl_tls_get_addr_soft (struct link_map *l) +{ + if (__glibc_unlikely (l->l_tls_modid == 0)) + /* This module has no TLS segment. */ + return NULL; + + dtv_t *dtv = THREAD_DTV (); + if (__glibc_unlikely (dtv[0].counter != GL(dl_tls_generation))) + { + /* 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 (__glibc_unlikely (data == TLS_DTV_UNALLOCATED)) + /* 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; +} diff --git a/REORG.TODO/elf/dl-trampoline.c b/REORG.TODO/elf/dl-trampoline.c new file mode 100644 index 0000000000..3ca89f3879 --- /dev/null +++ b/REORG.TODO/elf/dl-trampoline.c @@ -0,0 +1 @@ +#error "Architecture specific PLT trampolines must be defined." diff --git a/REORG.TODO/elf/dl-tunable-types.h b/REORG.TODO/elf/dl-tunable-types.h new file mode 100644 index 0000000000..1d516df08f --- /dev/null +++ b/REORG.TODO/elf/dl-tunable-types.h @@ -0,0 +1,62 @@ +/* Tunable type information. + + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _TUNABLE_TYPES_H_ +# define _TUNABLE_TYPES_H_ +#include <stddef.h> + +typedef enum +{ + TUNABLE_TYPE_INT_32, + TUNABLE_TYPE_UINT_64, + TUNABLE_TYPE_SIZE_T, + TUNABLE_TYPE_STRING +} tunable_type_code_t; + +typedef struct +{ + tunable_type_code_t type_code; + int64_t min; + int64_t max; +} tunable_type_t; + +typedef union +{ + int64_t numval; + const char *strval; +} tunable_val_t; + +typedef void (*tunable_callback_t) (tunable_val_t *); + +/* Security level for tunables. This decides what to do with individual + tunables for AT_SECURE binaries. */ +typedef enum +{ + /* Erase the tunable for AT_SECURE binaries so that child processes don't + read it. */ + TUNABLE_SECLEVEL_SXID_ERASE = 0, + /* Ignore the tunable for AT_SECURE binaries, but don't erase it, so that + child processes can read it. */ + TUNABLE_SECLEVEL_SXID_IGNORE = 1, + /* Read the tunable. */ + TUNABLE_SECLEVEL_NONE = 2, +} tunable_seclevel_t; + + +#endif diff --git a/REORG.TODO/elf/dl-tunables.c b/REORG.TODO/elf/dl-tunables.c new file mode 100644 index 0000000000..76e8c5cae1 --- /dev/null +++ b/REORG.TODO/elf/dl-tunables.c @@ -0,0 +1,490 @@ +/* The tunable framework. See the README.tunables to know how to use the + tunable in a glibc module. + + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <stdint.h> +#include <stdbool.h> +#include <unistd.h> +#include <stdlib.h> +#include <sysdep.h> +#include <fcntl.h> +#include <ldsodefs.h> + +#define TUNABLES_INTERNAL 1 +#include "dl-tunables.h" + +#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring +# define GLIBC_TUNABLES "GLIBC_TUNABLES" +#endif + +/* Compare environment or tunable names, bounded by the name hardcoded in + glibc. */ +static bool +is_name (const char *orig, const char *envname) +{ + for (;*orig != '\0' && *envname != '\0'; envname++, orig++) + if (*orig != *envname) + break; + + /* The ENVNAME is immediately followed by a value. */ + if (*orig == '\0' && *envname == '=') + return true; + else + return false; +} + +#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring +static char * +tunables_strdup (const char *in) +{ + size_t i = 0; + + while (in[i++] != '\0'); + char *out = __sbrk (i); + + /* FIXME: In reality if the allocation fails, __sbrk will crash attempting to + set the thread-local errno since the TCB has not yet been set up. This + needs to be fixed with an __sbrk implementation that does not set + errno. */ + if (out == (void *)-1) + return NULL; + + i--; + + while (i-- > 0) + out[i] = in[i]; + + return out; +} +#endif + +static char ** +get_next_env (char **envp, char **name, size_t *namelen, char **val, + char ***prev_envp) +{ + while (envp != NULL && *envp != NULL) + { + char **prev = envp; + char *envline = *envp++; + int len = 0; + + while (envline[len] != '\0' && envline[len] != '=') + len++; + + /* Just the name and no value, go to the next one. */ + if (envline[len] == '\0') + continue; + + *name = envline; + *namelen = len; + *val = &envline[len + 1]; + *prev_envp = prev; + + return envp; + } + + return NULL; +} + +/* A stripped down strtoul-like implementation for very early use. It does not + set errno if the result is outside bounds because it gets called before + errno may have been set up. */ +static uint64_t +tunables_strtoul (const char *nptr) +{ + uint64_t result = 0; + long int sign = 1; + unsigned max_digit; + + while (*nptr == ' ' || *nptr == '\t') + ++nptr; + + if (*nptr == '-') + { + sign = -1; + ++nptr; + } + else if (*nptr == '+') + ++nptr; + + if (*nptr < '0' || *nptr > '9') + return 0UL; + + int base = 10; + max_digit = 9; + if (*nptr == '0') + { + if (nptr[1] == 'x' || nptr[1] == 'X') + { + base = 16; + nptr += 2; + } + else + { + base = 8; + max_digit = 7; + } + } + + while (1) + { + int digval; + if (*nptr >= '0' && *nptr <= '0' + max_digit) + digval = *nptr - '0'; + else if (base == 16) + { + if (*nptr >= 'a' && *nptr <= 'f') + digval = *nptr - 'a' + 10; + else if (*nptr >= 'A' && *nptr <= 'F') + digval = *nptr - 'A' + 10; + else + break; + } + else + break; + + if (result >= (UINT64_MAX - digval) / base) + return UINT64_MAX; + result *= base; + result += digval; + ++nptr; + } + + return result * sign; +} + +#define TUNABLE_SET_VAL_IF_VALID_RANGE(__cur, __val, __type, __default_min, \ + __default_max) \ +({ \ + __type min = (__cur)->type.min; \ + __type max = (__cur)->type.max; \ + \ + if (min == max) \ + { \ + min = __default_min; \ + max = __default_max; \ + } \ + \ + if ((__type) (__val) >= min && (__type) (val) <= max) \ + { \ + (__cur)->val.numval = val; \ + (__cur)->initialized = true; \ + } \ +}) + +static void +do_tunable_update_val (tunable_t *cur, const void *valp) +{ + uint64_t val; + + if (cur->type.type_code != TUNABLE_TYPE_STRING) + val = *((int64_t *) valp); + + switch (cur->type.type_code) + { + case TUNABLE_TYPE_INT_32: + { + TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, int64_t, INT32_MIN, INT32_MAX); + break; + } + case TUNABLE_TYPE_UINT_64: + { + TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t, 0, UINT64_MAX); + break; + } + case TUNABLE_TYPE_SIZE_T: + { + TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t, 0, SIZE_MAX); + break; + } + case TUNABLE_TYPE_STRING: + { + cur->val.strval = valp; + break; + } + default: + __builtin_unreachable (); + } +} + +/* Validate range of the input value and initialize the tunable CUR if it looks + good. */ +static void +tunable_initialize (tunable_t *cur, const char *strval) +{ + uint64_t val; + const void *valp; + + if (cur->type.type_code != TUNABLE_TYPE_STRING) + { + val = tunables_strtoul (strval); + valp = &val; + } + else + { + cur->initialized = true; + valp = strval; + } + do_tunable_update_val (cur, valp); +} + +void +__tunable_set_val (tunable_id_t id, void *valp) +{ + tunable_t *cur = &tunable_list[id]; + + do_tunable_update_val (cur, valp); +} + +#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring +/* Parse the tunable string TUNESTR and adjust it to drop any tunables that may + be unsafe for AT_SECURE processes so that it can be used as the new + environment variable value for GLIBC_TUNABLES. VALSTRING is the original + environment variable string which we use to make NULL terminated values so + that we don't have to allocate memory again for it. */ +static void +parse_tunables (char *tunestr, char *valstring) +{ + if (tunestr == NULL || *tunestr == '\0') + return; + + char *p = tunestr; + + while (true) + { + char *name = p; + size_t len = 0; + + /* First, find where the name ends. */ + while (p[len] != '=' && p[len] != ':' && p[len] != '\0') + len++; + + /* If we reach the end of the string before getting a valid name-value + pair, bail out. */ + if (p[len] == '\0') + return; + + /* We did not find a valid name-value pair before encountering the + colon. */ + if (p[len]== ':') + { + p += len + 1; + continue; + } + + p += len + 1; + + /* Take the value from the valstring since we need to NULL terminate it. */ + char *value = &valstring[p - tunestr]; + len = 0; + + while (p[len] != ':' && p[len] != '\0') + len++; + + /* Add the tunable if it exists. */ + for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++) + { + tunable_t *cur = &tunable_list[i]; + + if (is_name (cur->name, name)) + { + /* If we are in a secure context (AT_SECURE) then ignore the tunable + unless it is explicitly marked as secure. Tunable values take + precendence over their envvar aliases. */ + if (__libc_enable_secure) + { + if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE) + { + if (p[len] == '\0') + { + /* Last tunable in the valstring. Null-terminate and + return. */ + *name = '\0'; + return; + } + else + { + /* Remove the current tunable from the string. We do + this by overwriting the string starting from NAME + (which is where the current tunable begins) with + the remainder of the string. We then have P point + to NAME so that we continue in the correct + position in the valstring. */ + char *q = &p[len + 1]; + p = name; + while (*q != '\0') + *name++ = *q++; + name[0] = '\0'; + len = 0; + } + } + + if (cur->security_level != TUNABLE_SECLEVEL_NONE) + break; + } + + value[len] = '\0'; + tunable_initialize (cur, value); + break; + } + } + + if (p[len] == '\0') + return; + else + p += len + 1; + } +} +#endif + +/* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when + the system administrator has created the /etc/suid-debug file. This is a + special case where we want to conditionally enable/disable a tunable even + for setuid binaries. We use the special version of access() to avoid + setting ERRNO, which is a TLS variable since TLS has not yet been set + up. */ +static inline void +__always_inline +maybe_enable_malloc_check (void) +{ + tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check); + if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0) + tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE; +} + +/* Initialize the tunables list from the environment. For now we only use the + ENV_ALIAS to find values. Later we will also use the tunable names to find + values. */ +void +__tunables_init (char **envp) +{ + char *envname = NULL; + char *envval = NULL; + size_t len = 0; + char **prev_envp = envp; + + maybe_enable_malloc_check (); + + while ((envp = get_next_env (envp, &envname, &len, &envval, + &prev_envp)) != NULL) + { +#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring + if (is_name (GLIBC_TUNABLES, envname)) + { + char *new_env = tunables_strdup (envname); + if (new_env != NULL) + parse_tunables (new_env + len + 1, envval); + /* Put in the updated envval. */ + *prev_envp = new_env; + continue; + } +#endif + + for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++) + { + tunable_t *cur = &tunable_list[i]; + + /* Skip over tunables that have either been set already or should be + skipped. */ + if (cur->initialized || cur->env_alias == NULL) + continue; + + const char *name = cur->env_alias; + + /* We have a match. Initialize and move on to the next line. */ + if (is_name (name, envname)) + { + /* For AT_SECURE binaries, we need to check the security settings of + the tunable and decide whether we read the value and also whether + we erase the value so that child processes don't inherit them in + the environment. */ + if (__libc_enable_secure) + { + if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE) + { + /* Erase the environment variable. */ + char **ep = prev_envp; + + while (*ep != NULL) + { + if (is_name (name, *ep)) + { + char **dp = ep; + + do + dp[0] = dp[1]; + while (*dp++); + } + else + ++ep; + } + /* Reset the iterator so that we read the environment again + from the point we erased. */ + envp = prev_envp; + } + + if (cur->security_level != TUNABLE_SECLEVEL_NONE) + continue; + } + + tunable_initialize (cur, envval); + break; + } + } + } +} + +/* Set the tunable value. This is called by the module that the tunable exists + in. */ +void +__tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback) +{ + tunable_t *cur = &tunable_list[id]; + + switch (cur->type.type_code) + { + case TUNABLE_TYPE_UINT_64: + { + *((uint64_t *) valp) = (uint64_t) cur->val.numval; + break; + } + case TUNABLE_TYPE_INT_32: + { + *((int32_t *) valp) = (int32_t) cur->val.numval; + break; + } + case TUNABLE_TYPE_SIZE_T: + { + *((size_t *) valp) = (size_t) cur->val.numval; + break; + } + case TUNABLE_TYPE_STRING: + { + *((const char **)valp) = cur->val.strval; + break; + } + default: + __builtin_unreachable (); + } + + if (cur->initialized && callback != NULL) + callback (&cur->val); +} + +rtld_hidden_def (__tunable_get_val) diff --git a/REORG.TODO/elf/dl-tunables.h b/REORG.TODO/elf/dl-tunables.h new file mode 100644 index 0000000000..6c49dcbf47 --- /dev/null +++ b/REORG.TODO/elf/dl-tunables.h @@ -0,0 +1,115 @@ +/* The tunable framework. See the README to know how to use the tunable in + a glibc module. + + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _TUNABLES_H_ +#define _TUNABLES_H_ + +#if !HAVE_TUNABLES +static inline void +__always_inline +__tunables_init (char **unused __attribute__ ((unused))) +{ + /* This is optimized out if tunables are not enabled. */ +} +#else + +# include <stddef.h> +# include "dl-tunable-types.h" + +/* A tunable. */ +struct _tunable +{ + const char *name; /* Internal name of the tunable. */ + tunable_type_t type; /* Data type of the tunable. */ + tunable_val_t val; /* The value. */ + bool initialized; /* Flag to indicate that the tunable is + initialized. */ + tunable_seclevel_t security_level; /* Specify the security level for the + tunable with respect to AT_SECURE + programs. See description of + tunable_seclevel_t to see a + description of the values. + + Note that even if the tunable is + read, it may not get used by the + target module if the value is + considered unsafe. */ + /* Compatibility elements. */ + const char *env_alias; /* The compatibility environment + variable name. */ +}; + +typedef struct _tunable tunable_t; + +/* Full name for a tunable is top_ns.tunable_ns.id. */ +# define TUNABLE_NAME_S(top,ns,id) #top "." #ns "." #id + +# define TUNABLE_ENUM_NAME(__top,__ns,__id) TUNABLE_ENUM_NAME1 (__top,__ns,__id) +# define TUNABLE_ENUM_NAME1(__top,__ns,__id) __top ## _ ## __ns ## _ ## __id + +# include "dl-tunable-list.h" + +extern void __tunables_init (char **); +extern void __tunable_get_val (tunable_id_t, void *, tunable_callback_t); +extern void __tunable_set_val (tunable_id_t, void *); +rtld_hidden_proto (__tunables_init) +rtld_hidden_proto (__tunable_get_val) + +/* Define TUNABLE_GET and TUNABLE_SET in short form if TOP_NAMESPACE and + TUNABLE_NAMESPACE are defined. This is useful shorthand to get and set + tunables within a module. */ +#if defined TOP_NAMESPACE && defined TUNABLE_NAMESPACE +# define TUNABLE_GET(__id, __type, __cb) \ + TUNABLE_GET_FULL (TOP_NAMESPACE, TUNABLE_NAMESPACE, __id, __type, __cb) +# define TUNABLE_SET(__id, __type, __val) \ + TUNABLE_SET_FULL (TOP_NAMESPACE, TUNABLE_NAMESPACE, __id, __type, __val) +#else +# define TUNABLE_GET(__top, __ns, __id, __type, __cb) \ + TUNABLE_GET_FULL (__top, __ns, __id, __type, __cb) +# define TUNABLE_SET(__top, __ns, __id, __type, __val) \ + TUNABLE_SET_FULL (__top, __ns, __id, __type, __val) +#endif + +/* Get and return a tunable value. If the tunable was set externally and __CB + is defined then call __CB before returning the value. */ +# define TUNABLE_GET_FULL(__top, __ns, __id, __type, __cb) \ +({ \ + tunable_id_t id = TUNABLE_ENUM_NAME (__top, __ns, __id); \ + __type ret; \ + __tunable_get_val (id, &ret, __cb); \ + ret; \ +}) + +/* Set a tunable value. */ +# define TUNABLE_SET_FULL(__top, __ns, __id, __type, __val) \ +({ \ + __tunable_set_val (TUNABLE_ENUM_NAME (__top, __ns, __id), \ + & (__type) {__val}); \ +}) + +/* Namespace sanity for callback functions. Use this macro to keep the + namespace of the modules clean. */ +# define TUNABLE_CALLBACK(__name) _dl_tunable_ ## __name + +# define TUNABLES_FRONTEND_valstring 1 +/* The default value for TUNABLES_FRONTEND. */ +# define TUNABLES_FRONTEND_yes TUNABLES_FRONTEND_valstring +#endif +#endif diff --git a/REORG.TODO/elf/dl-tunables.list b/REORG.TODO/elf/dl-tunables.list new file mode 100644 index 0000000000..41ce9afa28 --- /dev/null +++ b/REORG.TODO/elf/dl-tunables.list @@ -0,0 +1,87 @@ +# Copyright (C) 2016-2017 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# <http://www.gnu.org/licenses/>. + +# Allowed attributes for tunables: +# +# type: Defaults to STRING +# minval: Optional minimum acceptable value +# maxval: Optional maximum acceptable value +# env_alias: An alias environment variable +# security_level: Specify security level of the tunable. Valid values are: +# +# SXID_ERASE: (default) Don't read for AT_SECURE binaries and +# removed so that child processes can't read it. +# SXID_IGNORE: Don't read for AT_SECURE binaries, but retained for +# non-AT_SECURE subprocesses. +# NONE: Read all the time. + +glibc { + malloc { + check { + type: INT_32 + minval: 0 + maxval: 3 + env_alias: MALLOC_CHECK_ + } + top_pad { + type: SIZE_T + env_alias: MALLOC_TOP_PAD_ + security_level: SXID_IGNORE + } + perturb { + type: INT_32 + minval: 0 + maxval: 0xff + env_alias: MALLOC_PERTURB_ + security_level: SXID_IGNORE + } + mmap_threshold { + type: SIZE_T + env_alias: MALLOC_MMAP_THRESHOLD_ + security_level: SXID_IGNORE + } + trim_threshold { + type: SIZE_T + env_alias: MALLOC_TRIM_THRESHOLD_ + security_level: SXID_IGNORE + } + mmap_max { + type: INT_32 + env_alias: MALLOC_MMAP_MAX_ + security_level: SXID_IGNORE + } + arena_max { + type: SIZE_T + env_alias: MALLOC_ARENA_MAX + minval: 1 + security_level: SXID_IGNORE + } + arena_test { + type: SIZE_T + env_alias: MALLOC_ARENA_TEST + minval: 1 + security_level: SXID_IGNORE + } + } + tune { + hwcap_mask { + type: UINT_64 + env_alias: LD_HWCAP_MASK + default: HWCAP_IMPORTANT + } + } +} diff --git a/REORG.TODO/elf/dl-unmap-segments.h b/REORG.TODO/elf/dl-unmap-segments.h new file mode 100644 index 0000000000..f37e183943 --- /dev/null +++ b/REORG.TODO/elf/dl-unmap-segments.h @@ -0,0 +1,35 @@ +/* Unmap a shared object's segments. Generic version. + Copyright (C) 2014-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _DL_UNMAP_SEGMENTS_H +#define _DL_UNMAP_SEGMENTS_H 1 + +#include <link.h> +#include <sys/mman.h> + +/* _dl_map_segments ensures that any whole pages in gaps between segments + are filled in with PROT_NONE mappings. So we can just unmap the whole + range in one fell swoop. */ + +static __always_inline void +_dl_unmap_segments (struct link_map *l) +{ + __munmap ((void *) l->l_map_start, l->l_map_end - l->l_map_start); +} + +#endif /* dl-unmap-segments.h */ diff --git a/REORG.TODO/elf/dl-version.c b/REORG.TODO/elf/dl-version.c new file mode 100644 index 0000000000..c00078e5e4 --- /dev/null +++ b/REORG.TODO/elf/dl-version.c @@ -0,0 +1,389 @@ +/* Handle symbol and library versioning. + Copyright (C) 1997-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#include <elf.h> +#include <errno.h> +#include <libintl.h> +#include <stdlib.h> +#include <string.h> +#include <ldsodefs.h> +#include <_itoa.h> + +#include <assert.h> + + +#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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_VERSIONS)) + _dl_debug_printf ("\ +checking for version `%s' in file %s [%lu] required by file %s [%lu]\n", + string, DSO_FILENAME (map->l_name), + map->l_ns, name, ns); + + if (__glibc_unlikely (map->l_info[VERSYMIDX (DT_VERDEF)] == NULL)) + { + /* 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 (__glibc_likely (weak)) + { + 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, DSO_FILENAME (map->l_name), + N_("version lookup error"), 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, DSO_FILENAME (map->l_name), + 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 (DSO_FILENAME (map->l_name), + 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 (__glibc_unlikely (map->l_versions == NULL)) + { + 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; + /* In trace mode, dependencies may be missing. */ + if (__glibc_likely (ndx < map->l_nversions)) + { + 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/REORG.TODO/elf/dl-writev.h b/REORG.TODO/elf/dl-writev.h new file mode 100644 index 0000000000..4db083bef6 --- /dev/null +++ b/REORG.TODO/elf/dl-writev.h @@ -0,0 +1,56 @@ +/* Message-writing for the dynamic linker. Generic version. + Copyright (C) 2013-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <sys/uio.h> +#include <ldsodefs.h> +#include <libc-lock.h> + +/* This is used from only one place: dl-misc.c:_dl_debug_vdprintf. + Hence it's in a header with the expectation it will be inlined. + + This is writev, but with a constraint added and others loosened: + + 1. Under RTLD_PRIVATE_ERRNO, it must not clobber the private errno + when another thread holds the dl_load_lock. + 2. It is not obliged to detect and report errors at all. + 3. It's not really obliged to deliver a single atomic write + (though it may be preferable). */ + +static inline void +_dl_writev (int fd, const struct iovec *iov, size_t niov) +{ + /* Note that if __writev is an implementation that calls malloc, + this will cause linking problems building the dynamic linker. */ + +#if 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 (__glibc_unlikely (_dl_starting_up)) + __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 +} diff --git a/REORG.TODO/elf/do-rel.h b/REORG.TODO/elf/do-rel.h new file mode 100644 index 0000000000..70071e5c44 --- /dev/null +++ b/REORG.TODO/elf/do-rel.h @@ -0,0 +1,191 @@ +/* Do relocations for ELF dynamic linking. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* 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 Rel Rela +# define elf_machine_rel elf_machine_rela +# define elf_machine_rel_relative elf_machine_rela_relative +#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 + +/* 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, + __typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative, + int lazy, int skip_ifunc) +{ + const ElfW(Rel) *r = (const void *) reladdr; + const ElfW(Rel) *end = (const void *) (reladdr + relsize); + ElfW(Addr) l_addr = map->l_addr; +# if defined ELF_MACHINE_IRELATIVE && !defined RTLD_BOOTSTRAP + const ElfW(Rel) *r2 = NULL; + const ElfW(Rel) *end2 = NULL; +# endif + +#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) +# ifdef ELF_MACHINE_IRELATIVE + if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE) + { + if (r2 == NULL) + r2 = r; + end2 = r; + } + else +# endif + elf_machine_lazy_rel (map, l_addr, r, skip_ifunc); + +# ifdef ELF_MACHINE_IRELATIVE + if (r2 != NULL) + for (; r2 <= end2; ++r2) + if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE) + elf_machine_lazy_rel (map, l_addr, r2, skip_ifunc); +# endif + } + else +#endif + { + const ElfW(Sym) *const symtab = + (const void *) D_PTR (map, l_info[DT_SYMTAB]); + const ElfW(Rel) *relative = r; + r += nrelative; + +#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) + { +#if defined ELF_MACHINE_IRELATIVE && !defined RTLD_BOOTSTRAP + if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE) + { + if (r2 == NULL) + r2 = r; + end2 = r; + continue; + } +#endif + + 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), skip_ifunc); + } + +#if defined ELF_MACHINE_IRELATIVE && !defined RTLD_BOOTSTRAP + if (r2 != NULL) + for (; r2 <= end2; ++r2) + if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE) + { + ElfW(Half) ndx + = version[ELFW(R_SYM) (r2->r_info)] & 0x7fff; + elf_machine_rel (map, r2, + &symtab[ELFW(R_SYM) (r2->r_info)], + &map->l_versions[ndx], + (void *) (l_addr + r2->r_offset), + skip_ifunc); + } +#endif + } +#ifndef RTLD_BOOTSTRAP + else + { + for (; r < end; ++r) +# ifdef ELF_MACHINE_IRELATIVE + if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE) + { + if (r2 == NULL) + r2 = r; + end2 = r; + } + else +# endif + elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], NULL, + (void *) (l_addr + r->r_offset), skip_ifunc); + +# ifdef ELF_MACHINE_IRELATIVE + if (r2 != NULL) + for (; r2 <= end2; ++r2) + if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE) + elf_machine_rel (map, r2, &symtab[ELFW(R_SYM) (r2->r_info)], + NULL, (void *) (l_addr + r2->r_offset), + skip_ifunc); +# endif + } +#endif + } +} + +#undef elf_dynamic_do_Rel +#undef Rel +#undef elf_machine_rel +#undef elf_machine_rel_relative +#undef DO_ELF_MACHINE_REL_RELATIVE +#undef DO_RELA diff --git a/REORG.TODO/elf/dynamic-link.h b/REORG.TODO/elf/dynamic-link.h new file mode 100644 index 0000000000..60f2d91151 --- /dev/null +++ b/REORG.TODO/elf/dynamic-link.h @@ -0,0 +1,203 @@ +/* Inline functions for dynamic linking. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* 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 HAVE_STATIC_TLS(map, sym_map) \ + (__builtin_expect ((sym_map)->l_tls_offset != NO_TLS_OFFSET \ + && ((sym_map)->l_tls_offset \ + != FORCED_DYNAMIC_TLS_OFFSET), 1)) + +#define CHECK_STATIC_TLS(map, sym_map) \ + do { \ + if (!HAVE_STATIC_TLS (map, sym_map)) \ + _dl_allocate_static_tls (sym_map); \ + } while (0) + +#define TRY_STATIC_TLS(map, sym_map) \ + (__builtin_expect ((sym_map)->l_tls_offset \ + != FORCED_DYNAMIC_TLS_OFFSET, 1) \ + && (__builtin_expect ((sym_map)->l_tls_offset != NO_TLS_OFFSET, 1) \ + || _dl_try_allocate_static_tls (sym_map) == 0)) + +int internal_function attribute_hidden + _dl_try_allocate_static_tls (struct link_map *map); + +#include <elf.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, int skip_ifunc); +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, int skip_ifunc); +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, + int skip_ifunc); +# else +auto inline void __attribute__((always_inline)) +elf_machine_lazy_rel (struct link_map *map, + ElfW(Addr) l_addr, const ElfW(Rela) *reloc, + int skip_ifunc); +# endif +#endif + +#include <dl-machine.h> + +#include "get-dynamic-info.h" + +#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! + + We are guarenteed that we have one of three situations. Either DT_JMPREL + comes immediately after DT_REL*, or there is overlap and DT_JMPREL + consumes precisely the very end of the DT_REL*, or DT_JMPREL and DT_REL* + are completely separate and there is a gap between them. */ + +# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, do_lazy, skip_ifunc, test_rel) \ + do { \ + struct { ElfW(Addr) start, size; \ + __typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative; int lazy; } \ + ranges[2] = { { 0, 0, 0, 0 }, { 0, 0, 0, 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[VERSYMIDX (DT_##RELOC##COUNT)] != NULL) \ + ranges[0].nrelative \ + = map->l_info[VERSYMIDX (DT_##RELOC##COUNT)]->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]); \ + ElfW(Addr) size = (map)->l_info[DT_PLTRELSZ]->d_un.d_val; \ + \ + if (ranges[0].start + ranges[0].size == (start + size)) \ + ranges[0].size -= size; \ + if (ELF_DURING_STARTUP \ + || (!(do_lazy) \ + && (ranges[0].start + ranges[0].size) == start)) \ + { \ + /* Combine processing the sections. */ \ + ranges[0].size += size; \ + } \ + else \ + { \ + ranges[1].start = start; \ + ranges[1].size = size; \ + ranges[1].lazy = (do_lazy); \ + } \ + } \ + \ + if (ELF_DURING_STARTUP) \ + elf_dynamic_do_##reloc ((map), ranges[0].start, ranges[0].size, \ + ranges[0].nrelative, 0, skip_ifunc); \ + 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].nrelative, \ + ranges[ranges_index].lazy, \ + skip_ifunc); \ + } \ + } while (0) + +# 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, skip_ifunc) \ + _ELF_DYNAMIC_DO_RELOC (REL, Rel, map, lazy, skip_ifunc, _ELF_CHECK_REL) +# else +# define ELF_DYNAMIC_DO_REL(map, lazy, skip_ifunc) /* Nothing to do. */ +# endif + +# if ! ELF_MACHINE_NO_RELA +# define DO_RELA +# include "do-rel.h" +# define ELF_DYNAMIC_DO_RELA(map, lazy, skip_ifunc) \ + _ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, lazy, skip_ifunc, _ELF_CHECK_REL) +# else +# define ELF_DYNAMIC_DO_RELA(map, lazy, skip_ifunc) /* 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, skip_ifunc) \ + do { \ + int edr_lazy = elf_machine_runtime_setup ((map), (lazy), \ + (consider_profile)); \ + ELF_DYNAMIC_DO_REL ((map), edr_lazy, skip_ifunc); \ + ELF_DYNAMIC_DO_RELA ((map), edr_lazy, skip_ifunc); \ + } while (0) + +#endif diff --git a/REORG.TODO/elf/elf.h b/REORG.TODO/elf/elf.h new file mode 100644 index 0000000000..fff893d433 --- /dev/null +++ b/REORG.TODO/elf/elf.h @@ -0,0 +1,3761 @@ +/* This file defines standard ELF types, structures, and macros. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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_GNU 3 /* Object uses GNU ELF extensions. */ +#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ +#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_AEABI 64 /* ARM EABI */ +#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_IAMCU 6 /* Intel MCU */ +#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 */ + /* reserved 11-14 */ +#define EM_PARISC 15 /* HPPA */ + /* reserved 16 */ +#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_SPU 23 /* IBM SPU/SPC */ + /* reserved 24-35 */ +#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_PDP10 64 /* Digital PDP-10 */ +#define EM_PDP11 65 /* Digital PDP-11 */ +#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 emb.proc */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit emb.proc */ +#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 proc */ +#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_COMPACT 93 /* ARC International ARCompact */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_VIDEOCORE 95 /* Alphamosaic VideoCore */ +#define EM_TMM_GPP 96 /* Thompson Multimedia General Purpose Proc */ +#define EM_NS32K 97 /* National Semi. 32000 */ +#define EM_TPC 98 /* Tenor Network TPC */ +#define EM_SNP1K 99 /* Trebia SNP 1000 */ +#define EM_ST200 100 /* STMicroelectronics ST200 */ +#define EM_IP2K 101 /* Ubicom IP2xxx */ +#define EM_MAX 102 /* MAX processor */ +#define EM_CR 103 /* National Semi. CompactRISC */ +#define EM_F2MC16 104 /* Fujitsu F2MC16 */ +#define EM_MSP430 105 /* Texas Instruments msp430 */ +#define EM_BLACKFIN 106 /* Analog Devices Blackfin DSP */ +#define EM_SE_C33 107 /* Seiko Epson S1C33 family */ +#define EM_SEP 108 /* Sharp embedded microprocessor */ +#define EM_ARCA 109 /* Arca RISC */ +#define EM_UNICORE 110 /* PKU-Unity & MPRC Peking Uni. mc series */ +#define EM_EXCESS 111 /* eXcess configurable cpu */ +#define EM_DXP 112 /* Icera Semi. Deep Execution Processor */ +#define EM_ALTERA_NIOS2 113 /* Altera Nios II */ +#define EM_CRX 114 /* National Semi. CompactRISC CRX */ +#define EM_XGATE 115 /* Motorola XGATE */ +#define EM_C166 116 /* Infineon C16x/XC16x */ +#define EM_M16C 117 /* Renesas M16C */ +#define EM_DSPIC30F 118 /* Microchip Technology dsPIC30F */ +#define EM_CE 119 /* Freescale Communication Engine RISC */ +#define EM_M32C 120 /* Renesas M32C */ + /* reserved 121-130 */ +#define EM_TSK3000 131 /* Altium TSK3000 */ +#define EM_RS08 132 /* Freescale RS08 */ +#define EM_SHARC 133 /* Analog Devices SHARC family */ +#define EM_ECOG2 134 /* Cyan Technology eCOG2 */ +#define EM_SCORE7 135 /* Sunplus S+core7 RISC */ +#define EM_DSP24 136 /* New Japan Radio (NJR) 24-bit DSP */ +#define EM_VIDEOCORE3 137 /* Broadcom VideoCore III */ +#define EM_LATTICEMICO32 138 /* RISC for Lattice FPGA */ +#define EM_SE_C17 139 /* Seiko Epson C17 */ +#define EM_TI_C6000 140 /* Texas Instruments TMS320C6000 DSP */ +#define EM_TI_C2000 141 /* Texas Instruments TMS320C2000 DSP */ +#define EM_TI_C5500 142 /* Texas Instruments TMS320C55x DSP */ +#define EM_TI_ARP32 143 /* Texas Instruments App. Specific RISC */ +#define EM_TI_PRU 144 /* Texas Instruments Prog. Realtime Unit */ + /* reserved 145-159 */ +#define EM_MMDSP_PLUS 160 /* STMicroelectronics 64bit VLIW DSP */ +#define EM_CYPRESS_M8C 161 /* Cypress M8C */ +#define EM_R32C 162 /* Renesas R32C */ +#define EM_TRIMEDIA 163 /* NXP Semi. TriMedia */ +#define EM_QDSP6 164 /* QUALCOMM DSP6 */ +#define EM_8051 165 /* Intel 8051 and variants */ +#define EM_STXP7X 166 /* STMicroelectronics STxP7x */ +#define EM_NDS32 167 /* Andes Tech. compact code emb. RISC */ +#define EM_ECOG1X 168 /* Cyan Technology eCOG1X */ +#define EM_MAXQ30 169 /* Dallas Semi. MAXQ30 mc */ +#define EM_XIMO16 170 /* New Japan Radio (NJR) 16-bit DSP */ +#define EM_MANIK 171 /* M2000 Reconfigurable RISC */ +#define EM_CRAYNV2 172 /* Cray NV2 vector architecture */ +#define EM_RX 173 /* Renesas RX */ +#define EM_METAG 174 /* Imagination Tech. META */ +#define EM_MCST_ELBRUS 175 /* MCST Elbrus */ +#define EM_ECOG16 176 /* Cyan Technology eCOG16 */ +#define EM_CR16 177 /* National Semi. CompactRISC CR16 */ +#define EM_ETPU 178 /* Freescale Extended Time Processing Unit */ +#define EM_SLE9X 179 /* Infineon Tech. SLE9X */ +#define EM_L10M 180 /* Intel L10M */ +#define EM_K10M 181 /* Intel K10M */ + /* reserved 182 */ +#define EM_AARCH64 183 /* ARM AARCH64 */ + /* reserved 184 */ +#define EM_AVR32 185 /* Amtel 32-bit microprocessor */ +#define EM_STM8 186 /* STMicroelectronics STM8 */ +#define EM_TILE64 187 /* Tileta TILE64 */ +#define EM_TILEPRO 188 /* Tilera TILEPro */ +#define EM_MICROBLAZE 189 /* Xilinx MicroBlaze */ +#define EM_CUDA 190 /* NVIDIA CUDA */ +#define EM_TILEGX 191 /* Tilera TILE-Gx */ +#define EM_CLOUDSHIELD 192 /* CloudShield */ +#define EM_COREA_1ST 193 /* KIPO-KAIST Core-A 1st gen. */ +#define EM_COREA_2ND 194 /* KIPO-KAIST Core-A 2nd gen. */ +#define EM_ARC_COMPACT2 195 /* Synopsys ARCompact V2 */ +#define EM_OPEN8 196 /* Open8 RISC */ +#define EM_RL78 197 /* Renesas RL78 */ +#define EM_VIDEOCORE5 198 /* Broadcom VideoCore V */ +#define EM_78KOR 199 /* Renesas 78KOR */ +#define EM_56800EX 200 /* Freescale 56800EX DSC */ +#define EM_BA1 201 /* Beyond BA1 */ +#define EM_BA2 202 /* Beyond BA2 */ +#define EM_XCORE 203 /* XMOS xCORE */ +#define EM_MCHP_PIC 204 /* Microchip 8-bit PIC(r) */ + /* reserved 205-209 */ +#define EM_KM32 210 /* KM211 KM32 */ +#define EM_KMX32 211 /* KM211 KMX32 */ +#define EM_EMX16 212 /* KM211 KMX16 */ +#define EM_EMX8 213 /* KM211 KMX8 */ +#define EM_KVARC 214 /* KM211 KVARC */ +#define EM_CDP 215 /* Paneve CDP */ +#define EM_COGE 216 /* Cognitive Smart Memory Processor */ +#define EM_COOL 217 /* Bluechip CoolEngine */ +#define EM_NORC 218 /* Nanoradio Optimized RISC */ +#define EM_CSR_KALIMBA 219 /* CSR Kalimba */ +#define EM_Z80 220 /* Zilog Z80 */ +#define EM_VISIUM 221 /* Controls and Data Services VISIUMcore */ +#define EM_FT32 222 /* FTDI Chip FT32 */ +#define EM_MOXIE 223 /* Moxie processor */ +#define EM_AMDGPU 224 /* AMD GPU */ + /* reserved 225-242 */ +#define EM_RISCV 243 /* RISC-V */ + +#define EM_BPF 247 /* Linux BPF -- in-kernel virtual machine */ + +#define EM_NUM 248 + +/* Old spellings/synonyms. */ + +#define EM_ARC_A5 EM_ARC_COMPACT + +/* 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_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ +#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_COMPRESSED (1 << 11) /* Section with compressed 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 (1U << 31) /* Section is excluded unless + referenced or allocated (Solaris).*/ + +/* Section compression header. Used when SHF_COMPRESSED is set. */ + +typedef struct +{ + Elf32_Word ch_type; /* Compression format. */ + Elf32_Word ch_size; /* Uncompressed data size. */ + Elf32_Word ch_addralign; /* Uncompressed data alignment. */ +} Elf32_Chdr; + +typedef struct +{ + Elf64_Word ch_type; /* Compression format. */ + Elf64_Word ch_reserved; + Elf64_Xword ch_size; /* Uncompressed data size. */ + Elf64_Xword ch_addralign; /* Uncompressed data alignment. */ +} Elf64_Chdr; + +/* Legal values for ch_type (compression algorithm). */ +#define ELFCOMPRESS_ZLIB 1 /* ZLIB/DEFLATE algorithm. */ +#define ELFCOMPRESS_LOOS 0x60000000 /* Start of OS-specific. */ +#define ELFCOMPRESS_HIOS 0x6fffffff /* End of OS-specific. */ +#define ELFCOMPRESS_LOPROC 0x70000000 /* Start of processor-specific. */ +#define ELFCOMPRESS_HIPROC 0x7fffffff /* End of processor-specific. */ + +/* 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_GNU_UNIQUE 10 /* Unique symbol. */ +#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_GNU_IFUNC 10 /* Symbol is indirect code object */ +#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; + +/* Special value for e_phnum. This indicates that the real number of + program headers is too large to fit into e_phnum. Instead the real + value is in the field sh_info of section 0. */ + +#define PN_XNUM 0xffff + +/* 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 */ +#define NT_SIGINFO 0x53494749 /* Contains copy of siginfo_t, + size might increase */ +#define NT_FILE 0x46494c45 /* Contains information about mapped + files */ +#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ +#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ +#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ +#define NT_S390_TIMER 0x301 /* s390 timer register */ +#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ +#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ +#define NT_S390_CTRS 0x304 /* s390 control registers */ +#define NT_S390_PREFIX 0x305 /* s390 prefix register */ +#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ +#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ +#define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */ +#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ +#define NT_ARM_TLS 0x401 /* ARM TLS register */ +#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ +#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ +#define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ + +/* 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. */ +#define DF_1_NODIRECT 0x00020000 /* Object has no-direct binding. */ +#define DF_1_IGNMULDEF 0x00040000 +#define DF_1_NOKSYMS 0x00080000 +#define DF_1_NOHDR 0x00100000 +#define DF_1_EDITED 0x00200000 /* Object is modified after built. */ +#define DF_1_NORELOC 0x00400000 +#define DF_1_SYMINTPOSE 0x00800000 /* Object has individual interposers. */ +#define DF_1_GLOBAUDIT 0x01000000 /* Global auditing required. */ +#define DF_1_SINGLETON 0x02000000 /* Singleton symbols are used. */ + +/* 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? */ + +#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ + +#define AT_RANDOM 25 /* Address of 16 random bytes. */ + +#define AT_HWCAP2 26 /* More machine-dependent hints about + processor capabilities. */ + +#define AT_EXECFN 31 /* Filename of executable. */ + +/* 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 + +/* Shapes of the caches, with more room to describe them. + *GEOMETRY are comprised of cache line size in bytes in the bottom 16 bits + and the cache associativity in the next 16 bits. */ +#define AT_L1I_CACHESIZE 40 +#define AT_L1I_CACHEGEOMETRY 41 +#define AT_L1D_CACHESIZE 42 +#define AT_L1D_CACHEGEOMETRY 43 +#define AT_L2_CACHESIZE 44 +#define AT_L2_CACHEGEOMETRY 45 +#define AT_L3_CACHESIZE 46 +#define AT_L3_CACHEGEOMETRY 47 + +/* 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 NT_GNU_ABI_TAG 1 +#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ + +/* Known OSes. These values can appear in word 0 of an + NT_GNU_ABI_TAG 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 + +/* Synthetic hwcap information. The descriptor begins with two words: + word 0: number of entries + word 1: bitmask of enabled entries + Then follow variable-length entries, one byte followed by a + '\0'-terminated hwcap name string. The byte gives the bit + number to test if enabled, (1U << bit) & bitmask. */ +#define NT_GNU_HWCAP 2 + +/* Build ID bits as generated by ld --build-id. + The descriptor consists of any nonzero number of bytes. */ +#define NT_GNU_BUILD_ID 3 + +/* Version note generated by GNU gold containing a version string. */ +#define NT_GNU_GOLD_VERSION 4 + + +/* 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 */ +#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ +#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ +#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ +#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ +#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ +#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ +#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ +#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ +#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ +#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ +#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ +#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ +#define R_68K_TLS_LE32 37 /* 32 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE16 38 /* 16 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE8 39 /* 8 bit offset relative to + static TLS block */ +#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ +#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ +#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ +/* Keep this the last entry. */ +#define R_68K_NUM 43 + +/* 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 */ +#define R_386_SIZE32 38 /* 32-bit symbol size */ +#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ +#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS + descriptor for + relaxation. */ +#define R_386_TLS_DESC 41 /* TLS descriptor containing + pointer to code and to + argument, returning the TLS + offset for the symbol. */ +#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ +#define R_386_GOT32X 43 /* Load from 32 bit GOT entry, + relaxable. */ +/* Keep this the last entry. */ +#define R_386_NUM 44 + +/* 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_GLOB_JMP 42 /* was part of v9 ABI but was removed */ +#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 +#define R_SPARC_GOTDATA_HIX22 80 +#define R_SPARC_GOTDATA_LOX10 81 +#define R_SPARC_GOTDATA_OP_HIX22 82 +#define R_SPARC_GOTDATA_OP_LOX10 83 +#define R_SPARC_GOTDATA_OP 84 +#define R_SPARC_H34 85 +#define R_SPARC_SIZE32 86 +#define R_SPARC_SIZE64 87 +#define R_SPARC_WDISP10 88 +#define R_SPARC_JMP_IREL 248 +#define R_SPARC_IRELATIVE 249 +#define R_SPARC_GNU_VTINHERIT 250 +#define R_SPARC_GNU_VTENTRY 251 +#define R_SPARC_REV32 252 +/* Keep this the last entry. */ +#define R_SPARC_NUM 253 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* 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_FP64 512 /* Uses FP64 (12 callee-saved). */ +#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */ +#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 0x50000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */ +#define EF_MIPS_ARCH_32R2 0x70000000 /* MIPS32r2 code. */ +#define EF_MIPS_ARCH_64R2 0x80000000 /* MIPS64r2 code. */ + +/* The following are unofficial names and should not be used. */ + +#define E_MIPS_ARCH_1 EF_MIPS_ARCH_1 +#define E_MIPS_ARCH_2 EF_MIPS_ARCH_2 +#define E_MIPS_ARCH_3 EF_MIPS_ARCH_3 +#define E_MIPS_ARCH_4 EF_MIPS_ARCH_4 +#define E_MIPS_ARCH_5 EF_MIPS_ARCH_5 +#define E_MIPS_ARCH_32 EF_MIPS_ARCH_32 +#define E_MIPS_ARCH_64 EF_MIPS_ARCH_64 + +/* 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 info. */ +#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 in 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_PLT 0x8 +#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 */ +#define R_MIPS_GLOB_DAT 51 +#define R_MIPS_COPY 126 +#define R_MIPS_JUMP_SLOT 127 +/* Keep this the last entry. */ +#define R_MIPS_NUM 128 + +/* 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 +#define PT_MIPS_ABIFLAGS 0x70000003 /* FP mode requirement. */ + +/* 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. */ +/* The address of .got.plt in an executable using the new non-PIC ABI. */ +#define DT_MIPS_PLTGOT 0x70000032 +/* The base of the PLT in an executable using the new non-PIC ABI if that + PLT is writable. For a non-writable PLT, this is omitted or has a zero + value. */ +#define DT_MIPS_RWPLT 0x70000034 +/* An alternative description of the classic MIPS RLD_MAP that is usable + in a PIE as it stores a relative offset from the address of the tag + rather than an absolute address. */ +#define DT_MIPS_RLD_MAP_REL 0x70000035 +#define DT_MIPS_NUM 0x36 + +/* 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; + +typedef struct +{ + /* Version of flags structure. */ + Elf32_Half version; + /* The level of the ISA: 1-5, 32, 64. */ + unsigned char isa_level; + /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ + unsigned char isa_rev; + /* The size of general purpose registers. */ + unsigned char gpr_size; + /* The size of co-processor 1 registers. */ + unsigned char cpr1_size; + /* The size of co-processor 2 registers. */ + unsigned char cpr2_size; + /* The floating-point ABI. */ + unsigned char fp_abi; + /* Processor-specific extension. */ + Elf32_Word isa_ext; + /* Mask of ASEs used. */ + Elf32_Word ases; + /* Mask of general flags. */ + Elf32_Word flags1; + Elf32_Word flags2; +} Elf_MIPS_ABIFlags_v0; + +/* Values for the register size bytes of an abi flags structure. */ + +#define MIPS_AFL_REG_NONE 0x00 /* No registers. */ +#define MIPS_AFL_REG_32 0x01 /* 32-bit registers. */ +#define MIPS_AFL_REG_64 0x02 /* 64-bit registers. */ +#define MIPS_AFL_REG_128 0x03 /* 128-bit registers. */ + +/* Masks for the ases word of an ABI flags structure. */ + +#define MIPS_AFL_ASE_DSP 0x00000001 /* DSP ASE. */ +#define MIPS_AFL_ASE_DSPR2 0x00000002 /* DSP R2 ASE. */ +#define MIPS_AFL_ASE_EVA 0x00000004 /* Enhanced VA Scheme. */ +#define MIPS_AFL_ASE_MCU 0x00000008 /* MCU (MicroController) ASE. */ +#define MIPS_AFL_ASE_MDMX 0x00000010 /* MDMX ASE. */ +#define MIPS_AFL_ASE_MIPS3D 0x00000020 /* MIPS-3D ASE. */ +#define MIPS_AFL_ASE_MT 0x00000040 /* MT ASE. */ +#define MIPS_AFL_ASE_SMARTMIPS 0x00000080 /* SmartMIPS ASE. */ +#define MIPS_AFL_ASE_VIRT 0x00000100 /* VZ ASE. */ +#define MIPS_AFL_ASE_MSA 0x00000200 /* MSA ASE. */ +#define MIPS_AFL_ASE_MIPS16 0x00000400 /* MIPS16 ASE. */ +#define MIPS_AFL_ASE_MICROMIPS 0x00000800 /* MICROMIPS ASE. */ +#define MIPS_AFL_ASE_XPA 0x00001000 /* XPA ASE. */ +#define MIPS_AFL_ASE_MASK 0x00001fff /* All ASEs. */ + +/* Values for the isa_ext word of an ABI flags structure. */ + +#define MIPS_AFL_EXT_XLR 1 /* RMI Xlr instruction. */ +#define MIPS_AFL_EXT_OCTEON2 2 /* Cavium Networks Octeon2. */ +#define MIPS_AFL_EXT_OCTEONP 3 /* Cavium Networks OcteonP. */ +#define MIPS_AFL_EXT_LOONGSON_3A 4 /* Loongson 3A. */ +#define MIPS_AFL_EXT_OCTEON 5 /* Cavium Networks Octeon. */ +#define MIPS_AFL_EXT_5900 6 /* MIPS R5900 instruction. */ +#define MIPS_AFL_EXT_4650 7 /* MIPS R4650 instruction. */ +#define MIPS_AFL_EXT_4010 8 /* LSI R4010 instruction. */ +#define MIPS_AFL_EXT_4100 9 /* NEC VR4100 instruction. */ +#define MIPS_AFL_EXT_3900 10 /* Toshiba R3900 instruction. */ +#define MIPS_AFL_EXT_10000 11 /* MIPS R10000 instruction. */ +#define MIPS_AFL_EXT_SB1 12 /* Broadcom SB-1 instruction. */ +#define MIPS_AFL_EXT_4111 13 /* NEC VR4111/VR4181 instruction. */ +#define MIPS_AFL_EXT_4120 14 /* NEC VR4120 instruction. */ +#define MIPS_AFL_EXT_5400 15 /* NEC VR5400 instruction. */ +#define MIPS_AFL_EXT_5500 16 /* NEC VR5500 instruction. */ +#define MIPS_AFL_EXT_LOONGSON_2E 17 /* ST Microelectronics Loongson 2E. */ +#define MIPS_AFL_EXT_LOONGSON_2F 18 /* ST Microelectronics Loongson 2F. */ + +/* Masks for the flags1 word of an ABI flags structure. */ +#define MIPS_AFL_FLAGS1_ODDSPREG 1 /* Uses odd single-precision registers. */ + +/* Object attribute values. */ +enum +{ + /* Not tagged or not using any ABIs affected by the differences. */ + Val_GNU_MIPS_ABI_FP_ANY = 0, + /* Using hard-float -mdouble-float. */ + Val_GNU_MIPS_ABI_FP_DOUBLE = 1, + /* Using hard-float -msingle-float. */ + Val_GNU_MIPS_ABI_FP_SINGLE = 2, + /* Using soft-float. */ + Val_GNU_MIPS_ABI_FP_SOFT = 3, + /* Using -mips32r2 -mfp64. */ + Val_GNU_MIPS_ABI_FP_OLD_64 = 4, + /* Using -mfpxx. */ + Val_GNU_MIPS_ABI_FP_XX = 5, + /* Using -mips32r2 -mfp64. */ + Val_GNU_MIPS_ABI_FP_64 = 6, + /* Using -mips32r2 -mfp64 -mno-odd-spreg. */ + Val_GNU_MIPS_ABI_FP_64A = 7, + /* Maximum allocated FP ABI value. */ + Val_GNU_MIPS_ABI_FP_MAX = 7 +}; + +/* 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_PLABEL21L 66 /* Left 21 bits of fdesc address. */ +#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc 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_GNU_VTENTRY 232 +#define R_PARISC_GNU_VTINHERIT 233 +#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ +#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ +#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ +#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ +#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ +#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ +#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ +#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ +#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ +#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ +#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L +#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R +#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L +#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R +#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 +#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 +#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 */ +#define R_PPC_TLSGD 95 /* none (sym+add)@tlsgd */ +#define R_PPC_TLSLD 96 /* none (sym+add)@tlsld */ + +/* 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 extension to support local ifunc. */ +#define R_PPC_IRELATIVE 248 + +/* GNU relocs used in PIC code sequences. */ +#define R_PPC_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@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_OPT (DT_LOPROC + 1) +#define DT_PPC_NUM 2 + +/* PowerPC specific values for the DT_PPC_OPT Dyn entry. */ +#define PPC_OPT_TLS 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 */ +#define R_PPC64_TLSGD 107 /* none (sym+add)@tlsgd */ +#define R_PPC64_TLSLD 108 /* none (sym+add)@tlsld */ +#define R_PPC64_TOCSAVE 109 /* none */ + +/* Added when HA and HI relocs were changed to report overflows. */ +#define R_PPC64_ADDR16_HIGH 110 +#define R_PPC64_ADDR16_HIGHA 111 +#define R_PPC64_TPREL16_HIGH 112 +#define R_PPC64_TPREL16_HIGHA 113 +#define R_PPC64_DTPREL16_HIGH 114 +#define R_PPC64_DTPREL16_HIGHA 115 + +/* GNU extension to support local ifunc. */ +#define R_PPC64_JMP_IREL 247 +#define R_PPC64_IRELATIVE 248 +#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* e_flags bits specifying ABI. + 1 for original function descriptor using ABI, + 2 for revised ABI without function descriptors, + 0 for unspecified or not using any features affected by the differences. */ +#define EF_PPC64_ABI 3 + +/* 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_OPT (DT_LOPROC + 3) +#define DT_PPC64_NUM 4 + +/* PowerPC64 specific values for the DT_PPC64_OPT Dyn entry. */ +#define PPC64_OPT_TLS 1 +#define PPC64_OPT_MULTI_TOC 2 + +/* PowerPC64 specific values for the Elf64_Sym st_other field. */ +#define STO_PPC64_LOCAL_BIT 5 +#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT) +#define PPC64_LOCAL_ENTRY_OFFSET(other) \ + (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2) + + +/* 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 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_MAVERICK_FLOAT 0x800 + +#define EF_ARM_ABI_FLOAT_SOFT 0x200 /* NB conflicts with EF_ARM_SOFT_FLOAT */ +#define EF_ARM_ABI_FLOAT_HARD 0x400 /* NB conflicts with EF_ARM_VFP_FLOAT */ + + +/* 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 + +/* Constants defined in AAELF. */ +#define EF_ARM_BE8 0x00800000 +#define EF_ARM_LE8 0x00400000 + +#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 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + +/* Additional symbol types for Thumb. */ +#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ +#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ + +/* 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. */ +#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ +#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ +#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ +#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ + + +/* AArch64 relocs. */ + +#define R_AARCH64_NONE 0 /* No relocation. */ + +/* ILP32 AArch64 relocs. */ +#define R_AARCH64_P32_ABS32 1 /* Direct 32 bit. */ +#define R_AARCH64_P32_COPY 180 /* Copy symbol at runtime. */ +#define R_AARCH64_P32_GLOB_DAT 181 /* Create GOT entry. */ +#define R_AARCH64_P32_JUMP_SLOT 182 /* Create PLT entry. */ +#define R_AARCH64_P32_RELATIVE 183 /* Adjust by program base. */ +#define R_AARCH64_P32_TLS_DTPMOD 184 /* Module number, 32 bit. */ +#define R_AARCH64_P32_TLS_DTPREL 185 /* Module-relative offset, 32 bit. */ +#define R_AARCH64_P32_TLS_TPREL 186 /* TP-relative offset, 32 bit. */ +#define R_AARCH64_P32_TLSDESC 187 /* TLS Descriptor. */ +#define R_AARCH64_P32_IRELATIVE 188 /* STT_GNU_IFUNC relocation. */ + +/* LP64 AArch64 relocs. */ +#define R_AARCH64_ABS64 257 /* Direct 64 bit. */ +#define R_AARCH64_ABS32 258 /* Direct 32 bit. */ +#define R_AARCH64_ABS16 259 /* Direct 16-bit. */ +#define R_AARCH64_PREL64 260 /* PC-relative 64-bit. */ +#define R_AARCH64_PREL32 261 /* PC-relative 32-bit. */ +#define R_AARCH64_PREL16 262 /* PC-relative 16-bit. */ +#define R_AARCH64_MOVW_UABS_G0 263 /* Dir. MOVZ imm. from bits 15:0. */ +#define R_AARCH64_MOVW_UABS_G0_NC 264 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G1 265 /* Dir. MOVZ imm. from bits 31:16. */ +#define R_AARCH64_MOVW_UABS_G1_NC 266 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G2 267 /* Dir. MOVZ imm. from bits 47:32. */ +#define R_AARCH64_MOVW_UABS_G2_NC 268 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G3 269 /* Dir. MOV{K,Z} imm. from 63:48. */ +#define R_AARCH64_MOVW_SABS_G0 270 /* Dir. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_SABS_G1 271 /* Dir. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_SABS_G2 272 /* Dir. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_LD_PREL_LO19 273 /* PC-rel. LD imm. from bits 20:2. */ +#define R_AARCH64_ADR_PREL_LO21 274 /* PC-rel. ADR imm. from bits 20:0. */ +#define R_AARCH64_ADR_PREL_PG_HI21 275 /* Page-rel. ADRP imm. from 32:12. */ +#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check. */ +#define R_AARCH64_ADD_ABS_LO12_NC 277 /* Dir. ADD imm. from bits 11:0. */ +#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* Likewise for LD/ST; no check. */ +#define R_AARCH64_TSTBR14 279 /* PC-rel. TBZ/TBNZ imm. from 15:2. */ +#define R_AARCH64_CONDBR19 280 /* PC-rel. cond. br. imm. from 20:2. */ +#define R_AARCH64_JUMP26 282 /* PC-rel. B imm. from bits 27:2. */ +#define R_AARCH64_CALL26 283 /* Likewise for CALL. */ +#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1. */ +#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2. */ +#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3. */ +#define R_AARCH64_MOVW_PREL_G0 287 /* PC-rel. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_PREL_G0_NC 288 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G1 289 /* PC-rel. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_PREL_G1_NC 290 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G2 291 /* PC-rel. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_MOVW_PREL_G2_NC 292 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G3 293 /* PC-rel. MOV{N,Z} imm. from 63:48. */ +#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4. */ +#define R_AARCH64_MOVW_GOTOFF_G0 300 /* GOT-rel. off. MOV{N,Z} imm. 15:0. */ +#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G1 302 /* GOT-rel. o. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G2 304 /* GOT-rel. o. MOV{N,Z} imm. 47:32. */ +#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G3 306 /* GOT-rel. o. MOV{N,Z} imm. 63:48. */ +#define R_AARCH64_GOTREL64 307 /* GOT-relative 64-bit. */ +#define R_AARCH64_GOTREL32 308 /* GOT-relative 32-bit. */ +#define R_AARCH64_GOT_LD_PREL19 309 /* PC-rel. GOT off. load imm. 20:2. */ +#define R_AARCH64_LD64_GOTOFF_LO15 310 /* GOT-rel. off. LD/ST imm. 14:3. */ +#define R_AARCH64_ADR_GOT_PAGE 311 /* P-page-rel. GOT off. ADRP 32:12. */ +#define R_AARCH64_LD64_GOT_LO12_NC 312 /* Dir. GOT off. LD/ST imm. 11:3. */ +#define R_AARCH64_LD64_GOTPAGE_LO15 313 /* GOT-page-rel. GOT off. LD/ST 14:3 */ +#define R_AARCH64_TLSGD_ADR_PREL21 512 /* PC-relative ADR imm. 20:0. */ +#define R_AARCH64_TLSGD_ADR_PAGE21 513 /* page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSGD_ADD_LO12_NC 514 /* direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSGD_MOVW_G1 515 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSGD_MOVW_G0_NC 516 /* GOT-rel. MOVK imm. 15:0. */ +#define R_AARCH64_TLSLD_ADR_PREL21 517 /* Like 512; local dynamic model. */ +#define R_AARCH64_TLSLD_ADR_PAGE21 518 /* Like 513; local dynamic model. */ +#define R_AARCH64_TLSLD_ADD_LO12_NC 519 /* Like 514; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G1 520 /* Like 515; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G0_NC 521 /* Like 516; local dynamic model. */ +#define R_AARCH64_TLSLD_LD_PREL19 522 /* TLS PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0. */ +#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12. */ +#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3. */ +#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check. */ +#define R_AARCH64_TLSDESC_LD_PREL19 560 /* PC-rel. load immediate 20:2. */ +#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0. */ +#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSDESC_LD64_LO12 563 /* Direct LD off. from 11:3. */ +#define R_AARCH64_TLSDESC_ADD_LO12 564 /* Direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSDESC_OFF_G1 565 /* GOT-rel. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_TLSDESC_OFF_G0_NC 566 /* GOT-rel. MOVK imm. 15:0; no ck. */ +#define R_AARCH64_TLSDESC_LDR 567 /* Relax LDR. */ +#define R_AARCH64_TLSDESC_ADD 568 /* Relax ADD. */ +#define R_AARCH64_TLSDESC_CALL 569 /* Relax BLR. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check. */ +#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */ +#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */ +#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */ +#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */ +#define R_AARCH64_TLS_DTPMOD 1028 /* Module number, 64 bit. */ +#define R_AARCH64_TLS_DTPREL 1029 /* Module-relative offset, 64 bit. */ +#define R_AARCH64_TLS_TPREL 1030 /* TP-relative offset, 64 bit. */ +#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */ +#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */ + +/* ARM relocs. */ + +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* Deprecated 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 /* Direct & 0x7C (LDR, STR). */ +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 /* PC relative 24 bit (Thumb32 BL). */ +#define R_ARM_THM_PC8 11 /* PC relative & 0x3FC + (Thumb16 LDR, ADD, ADR). */ +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 /* Obsolete static relocation. */ +#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ +#define R_ARM_THM_SWI8 14 /* Reserved. */ +#define R_ARM_XPC25 15 /* Reserved. */ +#define R_ARM_THM_XPC22 16 /* Reserved. */ +#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 /* Deprecated, 32 bit PLT address. */ +#define R_ARM_CALL 28 /* PC relative 24 bit (BL, BLX). */ +#define R_ARM_JUMP24 29 /* PC relative 24 bit + (B, BL<cond>). */ +#define R_ARM_THM_JUMP24 30 /* PC relative 24 bit (Thumb32 B.W). */ +#define R_ARM_BASE_ABS 31 /* Adjust by program base. */ +#define R_ARM_ALU_PCREL_7_0 32 /* Obsolete. */ +#define R_ARM_ALU_PCREL_15_8 33 /* Obsolete. */ +#define R_ARM_ALU_PCREL_23_15 34 /* Obsolete. */ +#define R_ARM_LDR_SBREL_11_0 35 /* Deprecated, prog. base relative. */ +#define R_ARM_ALU_SBREL_19_12 36 /* Deprecated, prog. base relative. */ +#define R_ARM_ALU_SBREL_27_20 37 /* Deprecated, prog. base relative. */ +#define R_ARM_TARGET1 38 +#define R_ARM_SBREL31 39 /* Program base relative. */ +#define R_ARM_V4BX 40 +#define R_ARM_TARGET2 41 +#define R_ARM_PREL31 42 /* 32 bit PC relative. */ +#define R_ARM_MOVW_ABS_NC 43 /* Direct 16-bit (MOVW). */ +#define R_ARM_MOVT_ABS 44 /* Direct high 16-bit (MOVT). */ +#define R_ARM_MOVW_PREL_NC 45 /* PC relative 16-bit (MOVW). */ +#define R_ARM_MOVT_PREL 46 /* PC relative (MOVT). */ +#define R_ARM_THM_MOVW_ABS_NC 47 /* Direct 16 bit (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_ABS 48 /* Direct high 16 bit + (Thumb32 MOVT). */ +#define R_ARM_THM_MOVW_PREL_NC 49 /* PC relative 16 bit + (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_PREL 50 /* PC relative high 16 bit + (Thumb32 MOVT). */ +#define R_ARM_THM_JUMP19 51 /* PC relative 20 bit + (Thumb32 B<cond>.W). */ +#define R_ARM_THM_JUMP6 52 /* PC relative X & 0x7E + (Thumb16 CBZ, CBNZ). */ +#define R_ARM_THM_ALU_PREL_11_0 53 /* PC relative 12 bit + (Thumb32 ADR.W). */ +#define R_ARM_THM_PC12 54 /* PC relative 12 bit + (Thumb32 LDR{D,SB,H,SH}). */ +#define R_ARM_ABS32_NOI 55 /* Direct 32-bit. */ +#define R_ARM_REL32_NOI 56 /* PC relative 32-bit. */ +#define R_ARM_ALU_PC_G0_NC 57 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G0 58 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G1_NC 59 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G1 60 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G2 61 /* PC relative (ADD, SUB). */ +#define R_ARM_LDR_PC_G1 62 /* PC relative (LDR,STR,LDRB,STRB). */ +#define R_ARM_LDR_PC_G2 63 /* PC relative (LDR,STR,LDRB,STRB). */ +#define R_ARM_LDRS_PC_G0 64 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDRS_PC_G1 65 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDRS_PC_G2 66 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDC_PC_G0 67 /* PC relative (LDC, STC). */ +#define R_ARM_LDC_PC_G1 68 /* PC relative (LDC, STC). */ +#define R_ARM_LDC_PC_G2 69 /* PC relative (LDC, STC). */ +#define R_ARM_ALU_SB_G0_NC 70 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G0 71 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G1_NC 72 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G1 73 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G2 74 /* Program base relative (ADD,SUB). */ +#define R_ARM_LDR_SB_G0 75 /* Program base relative (LDR, + STR, LDRB, STRB). */ +#define R_ARM_LDR_SB_G1 76 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDR_SB_G2 77 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G0 78 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G1 79 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G2 80 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDC_SB_G0 81 /* Program base relative (LDC,STC). */ +#define R_ARM_LDC_SB_G1 82 /* Program base relative (LDC,STC). */ +#define R_ARM_LDC_SB_G2 83 /* Program base relative (LDC,STC). */ +#define R_ARM_MOVW_BREL_NC 84 /* Program base relative 16 + bit (MOVW). */ +#define R_ARM_MOVT_BREL 85 /* Program base relative high + 16 bit (MOVT). */ +#define R_ARM_MOVW_BREL 86 /* Program base relative 16 + bit (MOVW). */ +#define R_ARM_THM_MOVW_BREL_NC 87 /* Program base relative 16 + bit (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_BREL 88 /* Program base relative high + 16 bit (Thumb32 MOVT). */ +#define R_ARM_THM_MOVW_BREL 89 /* Program base relative 16 + bit (Thumb32 MOVW). */ +#define R_ARM_TLS_GOTDESC 90 +#define R_ARM_TLS_CALL 91 +#define R_ARM_TLS_DESCSEQ 92 /* TLS relaxation. */ +#define R_ARM_THM_TLS_CALL 93 +#define R_ARM_PLT32_ABS 94 +#define R_ARM_GOT_ABS 95 /* GOT entry. */ +#define R_ARM_GOT_PREL 96 /* PC relative GOT entry. */ +#define R_ARM_GOT_BREL12 97 /* GOT entry relative to GOT + origin (LDR). */ +#define R_ARM_GOTOFF12 98 /* 12 bit, GOT entry relative + to GOT origin (LDR, STR). */ +#define R_ARM_GOTRELAX 99 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* PC relative & 0xFFE (Thumb16 B). */ +#define R_ARM_THM_PC9 103 /* PC relative & 0x1FE + (Thumb16 B/B<cond>). */ +#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_TLS_LDO12 109 /* 12 bit relative to TLS + block (LDR, STR). */ +#define R_ARM_TLS_LE12 110 /* 12 bit relative to static + TLS block (LDR, STR). */ +#define R_ARM_TLS_IE12GP 111 /* 12 bit GOT entry relative + to GOT origin (LDR). */ +#define R_ARM_ME_TOO 128 /* Obsolete. */ +#define R_ARM_THM_TLS_DESCSEQ 129 +#define R_ARM_THM_TLS_DESCSEQ16 129 +#define R_ARM_THM_TLS_DESCSEQ32 130 +#define R_ARM_THM_GOT_BREL12 131 /* GOT entry relative to GOT + origin, 12 bit (Thumb32 LDR). */ +#define R_ARM_IRELATIVE 160 +#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 */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_SH_MACH_MASK 0x1f +#define EF_SH_UNKNOWN 0x0 +#define EF_SH1 0x1 +#define EF_SH2 0x2 +#define EF_SH3 0x3 +#define EF_SH_DSP 0x4 +#define EF_SH3_DSP 0x5 +#define EF_SH4AL_DSP 0x6 +#define EF_SH3E 0x8 +#define EF_SH4 0x9 +#define EF_SH2E 0xb +#define EF_SH4A 0xc +#define EF_SH2A 0xd +#define EF_SH4_NOFPU 0x10 +#define EF_SH4A_NOFPU 0x11 +#define EF_SH4_NOMMU_NOFPU 0x12 +#define EF_SH2A_NOFPU 0x13 +#define EF_SH3_NOMMU 0x14 +#define EF_SH2A_SH4_NOFPU 0x15 +#define EF_SH2A_SH3_NOFPU 0x16 +#define EF_SH2A_SH4 0x17 +#define EF_SH2A_SH3E 0x18 + +/* 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 + +/* S/390 specific definitions. */ + +/* Valid values for the e_flags field. */ + +#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ + +/* 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. */ +#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */ +/* Keep this the last entry. */ +#define R_390_NUM 62 + + +/* 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_PC64 24 /* PC relative 64 bit */ +#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ +#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative + offset to GOT */ +#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ +#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset + to GOT entry */ +#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ +#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ +#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset + to PLT entry */ +#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ +#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ +#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ +#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS + descriptor. */ +#define R_X86_64_TLSDESC 36 /* TLS descriptor. */ +#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ +#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */ + /* 39 Reserved was R_X86_64_PC32_BND */ + /* 40 Reserved was R_X86_64_PLT32_BND */ +#define R_X86_64_GOTPCRELX 41 /* Load from 32 bit signed pc relative + offset to GOT entry without REX + prefix, relaxable. */ +#define R_X86_64_REX_GOTPCRELX 42 /* Load from 32 bit signed pc relative + offset to GOT entry with REX prefix, + relaxable. */ +#define R_X86_64_NUM 43 + + +/* 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_TLS_GD 24 /* 32-bit offset for global dynamic. */ +#define R_MN10300_TLS_LD 25 /* 32-bit offset for local dynamic. */ +#define R_MN10300_TLS_LDO 26 /* Module-relative offset. */ +#define R_MN10300_TLS_GOTIE 27 /* GOT offset for static TLS block + offset. */ +#define R_MN10300_TLS_IE 28 /* GOT address for static TLS block + offset. */ +#define R_MN10300_TLS_LE 29 /* Offset relative to static TLS + block. */ +#define R_MN10300_TLS_DTPMOD 30 /* ID of module containing symbol. */ +#define R_MN10300_TLS_DTPOFF 31 /* Offset in module TLS block. */ +#define R_MN10300_TLS_TPOFF 32 /* Offset in static TLS block. */ +#define R_MN10300_SYM_DIFF 33 /* Adjustment for next reloc as needed + by linker relaxation. */ +#define R_MN10300_ALIGN 34 /* Alignment requirement for linker + relaxation. */ +#define R_MN10300_NUM 35 + + +/* 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. */ + +/* MicroBlaze relocations */ +#define R_MICROBLAZE_NONE 0 /* No reloc. */ +#define R_MICROBLAZE_32 1 /* Direct 32 bit. */ +#define R_MICROBLAZE_32_PCREL 2 /* PC relative 32 bit. */ +#define R_MICROBLAZE_64_PCREL 3 /* PC relative 64 bit. */ +#define R_MICROBLAZE_32_PCREL_LO 4 /* Low 16 bits of PCREL32. */ +#define R_MICROBLAZE_64 5 /* Direct 64 bit. */ +#define R_MICROBLAZE_32_LO 6 /* Low 16 bit. */ +#define R_MICROBLAZE_SRO32 7 /* Read-only small data area. */ +#define R_MICROBLAZE_SRW32 8 /* Read-write small data area. */ +#define R_MICROBLAZE_64_NONE 9 /* No reloc. */ +#define R_MICROBLAZE_32_SYM_OP_SYM 10 /* Symbol Op Symbol relocation. */ +#define R_MICROBLAZE_GNU_VTINHERIT 11 /* GNU C++ vtable hierarchy. */ +#define R_MICROBLAZE_GNU_VTENTRY 12 /* GNU C++ vtable member usage. */ +#define R_MICROBLAZE_GOTPC_64 13 /* PC-relative GOT offset. */ +#define R_MICROBLAZE_GOT_64 14 /* GOT entry offset. */ +#define R_MICROBLAZE_PLT_64 15 /* PLT offset (PC-relative). */ +#define R_MICROBLAZE_REL 16 /* Adjust by program base. */ +#define R_MICROBLAZE_JUMP_SLOT 17 /* Create PLT entry. */ +#define R_MICROBLAZE_GLOB_DAT 18 /* Create GOT entry. */ +#define R_MICROBLAZE_GOTOFF_64 19 /* 64 bit offset to GOT. */ +#define R_MICROBLAZE_GOTOFF_32 20 /* 32 bit offset to GOT. */ +#define R_MICROBLAZE_COPY 21 /* Runtime copy. */ +#define R_MICROBLAZE_TLS 22 /* TLS Reloc. */ +#define R_MICROBLAZE_TLSGD 23 /* TLS General Dynamic. */ +#define R_MICROBLAZE_TLSLD 24 /* TLS Local Dynamic. */ +#define R_MICROBLAZE_TLSDTPMOD32 25 /* TLS Module ID. */ +#define R_MICROBLAZE_TLSDTPREL32 26 /* TLS Offset Within TLS Block. */ +#define R_MICROBLAZE_TLSDTPREL64 27 /* TLS Offset Within TLS Block. */ +#define R_MICROBLAZE_TLSGOTTPREL32 28 /* TLS Offset From Thread Pointer. */ +#define R_MICROBLAZE_TLSTPREL32 29 /* TLS Offset From Thread Pointer. */ + +/* Legal values for d_tag (dynamic entry type). */ +#define DT_NIOS2_GP 0x70000002 /* Address of _gp. */ + +/* Nios II relocations. */ +#define R_NIOS2_NONE 0 /* No reloc. */ +#define R_NIOS2_S16 1 /* Direct signed 16 bit. */ +#define R_NIOS2_U16 2 /* Direct unsigned 16 bit. */ +#define R_NIOS2_PCREL16 3 /* PC relative 16 bit. */ +#define R_NIOS2_CALL26 4 /* Direct call. */ +#define R_NIOS2_IMM5 5 /* 5 bit constant expression. */ +#define R_NIOS2_CACHE_OPX 6 /* 5 bit expression, shift 22. */ +#define R_NIOS2_IMM6 7 /* 6 bit constant expression. */ +#define R_NIOS2_IMM8 8 /* 8 bit constant expression. */ +#define R_NIOS2_HI16 9 /* High 16 bit. */ +#define R_NIOS2_LO16 10 /* Low 16 bit. */ +#define R_NIOS2_HIADJ16 11 /* High 16 bit, adjusted. */ +#define R_NIOS2_BFD_RELOC_32 12 /* 32 bit symbol value + addend. */ +#define R_NIOS2_BFD_RELOC_16 13 /* 16 bit symbol value + addend. */ +#define R_NIOS2_BFD_RELOC_8 14 /* 8 bit symbol value + addend. */ +#define R_NIOS2_GPREL 15 /* 16 bit GP pointer offset. */ +#define R_NIOS2_GNU_VTINHERIT 16 /* GNU C++ vtable hierarchy. */ +#define R_NIOS2_GNU_VTENTRY 17 /* GNU C++ vtable member usage. */ +#define R_NIOS2_UJMP 18 /* Unconditional branch. */ +#define R_NIOS2_CJMP 19 /* Conditional branch. */ +#define R_NIOS2_CALLR 20 /* Indirect call through register. */ +#define R_NIOS2_ALIGN 21 /* Alignment requirement for + linker relaxation. */ +#define R_NIOS2_GOT16 22 /* 16 bit GOT entry. */ +#define R_NIOS2_CALL16 23 /* 16 bit GOT entry for function. */ +#define R_NIOS2_GOTOFF_LO 24 /* %lo of offset to GOT pointer. */ +#define R_NIOS2_GOTOFF_HA 25 /* %hiadj of offset to GOT pointer. */ +#define R_NIOS2_PCREL_LO 26 /* %lo of PC relative offset. */ +#define R_NIOS2_PCREL_HA 27 /* %hiadj of PC relative offset. */ +#define R_NIOS2_TLS_GD16 28 /* 16 bit GOT offset for TLS GD. */ +#define R_NIOS2_TLS_LDM16 29 /* 16 bit GOT offset for TLS LDM. */ +#define R_NIOS2_TLS_LDO16 30 /* 16 bit module relative offset. */ +#define R_NIOS2_TLS_IE16 31 /* 16 bit GOT offset for TLS IE. */ +#define R_NIOS2_TLS_LE16 32 /* 16 bit LE TP-relative offset. */ +#define R_NIOS2_TLS_DTPMOD 33 /* Module number. */ +#define R_NIOS2_TLS_DTPREL 34 /* Module-relative offset. */ +#define R_NIOS2_TLS_TPREL 35 /* TP-relative offset. */ +#define R_NIOS2_COPY 36 /* Copy symbol at runtime. */ +#define R_NIOS2_GLOB_DAT 37 /* Create GOT entry. */ +#define R_NIOS2_JUMP_SLOT 38 /* Create PLT entry. */ +#define R_NIOS2_RELATIVE 39 /* Adjust by program base. */ +#define R_NIOS2_GOTOFF 40 /* 16 bit offset to GOT pointer. */ +#define R_NIOS2_CALL26_NOAT 41 /* Direct call in .noat section. */ +#define R_NIOS2_GOT_LO 42 /* %lo() of GOT entry. */ +#define R_NIOS2_GOT_HA 43 /* %hiadj() of GOT entry. */ +#define R_NIOS2_CALL_LO 44 /* %lo() of function GOT entry. */ +#define R_NIOS2_CALL_HA 45 /* %hiadj() of function GOT entry. */ + +/* TILEPro relocations. */ +#define R_TILEPRO_NONE 0 /* No reloc */ +#define R_TILEPRO_32 1 /* Direct 32 bit */ +#define R_TILEPRO_16 2 /* Direct 16 bit */ +#define R_TILEPRO_8 3 /* Direct 8 bit */ +#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */ +#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */ +#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */ +#define R_TILEPRO_LO16 7 /* Low 16 bit */ +#define R_TILEPRO_HI16 8 /* High 16 bit */ +#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */ +#define R_TILEPRO_COPY 10 /* Copy relocation */ +#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */ +#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */ +#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */ +#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */ +#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */ +#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */ +#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */ +#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */ +#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */ +#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */ +#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */ +#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */ +#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */ +#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */ +#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */ +#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */ +#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */ +#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */ +#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */ +/* Relocs 56-59 are currently not defined. */ +#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */ +#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */ +#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */ +#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */ +#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */ +#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */ + +#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEPRO_NUM 130 + + +/* TILE-Gx relocations. */ +#define R_TILEGX_NONE 0 /* No reloc */ +#define R_TILEGX_64 1 /* Direct 64 bit */ +#define R_TILEGX_32 2 /* Direct 32 bit */ +#define R_TILEGX_16 3 /* Direct 16 bit */ +#define R_TILEGX_8 4 /* Direct 8 bit */ +#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */ +#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */ +#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */ +#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */ +#define R_TILEGX_HW0 9 /* hword 0 16-bit */ +#define R_TILEGX_HW1 10 /* hword 1 16-bit */ +#define R_TILEGX_HW2 11 /* hword 2 16-bit */ +#define R_TILEGX_HW3 12 /* hword 3 16-bit */ +#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */ +#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */ +#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */ +#define R_TILEGX_COPY 16 /* Copy relocation */ +#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */ +#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */ +#define R_TILEGX_RELATIVE 19 /* Adjust by program base */ +#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */ +#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */ +#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */ +#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */ +#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */ +#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */ +#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */ +#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */ +#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */ +#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */ +#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */ +#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */ +#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */ +#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */ +#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */ +#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */ +#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */ +#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */ +#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */ +#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */ +#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */ +#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */ +#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */ +#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW0_PLT_PCREL 66 /* X0 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PLT_PCREL 67 /* X1 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PLT_PCREL 68 /* X0 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PLT_PCREL 69 /* X1 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PLT_PCREL 70 /* X0 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PLT_PCREL 71 /* X1 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X0_HW3_PLT_PCREL 76 /* X0 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PLT_PCREL 77 /* X1 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */ +/* Relocs 90-91 are currently not defined. */ +#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL 94 /* X0 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL 95 /* X1 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL 96 /* X0 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL 97 /* X1 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL 98 /* X0 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL 99 /* X1 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */ +/* Relocs 104-105 are currently not defined. */ +#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */ +#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */ +#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */ +#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */ +#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */ + +#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEGX_NUM 130 + +/* BPF specific declarations. */ + +#define R_BPF_NONE 0 /* No reloc */ +#define R_BPF_MAP_FD 1 /* Map fd to pointer */ + +/* Imagination Meta specific relocations. */ + +#define R_METAG_HIADDR16 0 +#define R_METAG_LOADDR16 1 +#define R_METAG_ADDR32 2 /* 32bit absolute address */ +#define R_METAG_NONE 3 /* No reloc */ +#define R_METAG_RELBRANCH 4 +#define R_METAG_GETSETOFF 5 + +/* Backward compatability */ +#define R_METAG_REG32OP1 6 +#define R_METAG_REG32OP2 7 +#define R_METAG_REG32OP3 8 +#define R_METAG_REG16OP1 9 +#define R_METAG_REG16OP2 10 +#define R_METAG_REG16OP3 11 +#define R_METAG_REG32OP4 12 + +#define R_METAG_HIOG 13 +#define R_METAG_LOOG 14 + +#define R_METAG_REL8 15 +#define R_METAG_REL16 16 + +/* GNU */ +#define R_METAG_GNU_VTINHERIT 30 +#define R_METAG_GNU_VTENTRY 31 + +/* PIC relocations */ +#define R_METAG_HI16_GOTOFF 32 +#define R_METAG_LO16_GOTOFF 33 +#define R_METAG_GETSET_GOTOFF 34 +#define R_METAG_GETSET_GOT 35 +#define R_METAG_HI16_GOTPC 36 +#define R_METAG_LO16_GOTPC 37 +#define R_METAG_HI16_PLT 38 +#define R_METAG_LO16_PLT 39 +#define R_METAG_RELBRANCH_PLT 40 +#define R_METAG_GOTOFF 41 +#define R_METAG_PLT 42 +#define R_METAG_COPY 43 +#define R_METAG_JMP_SLOT 44 +#define R_METAG_RELATIVE 45 +#define R_METAG_GLOB_DAT 46 + +/* TLS relocations */ +#define R_METAG_TLS_GD 47 +#define R_METAG_TLS_LDM 48 +#define R_METAG_TLS_LDO_HI16 49 +#define R_METAG_TLS_LDO_LO16 50 +#define R_METAG_TLS_LDO 51 +#define R_METAG_TLS_IE 52 +#define R_METAG_TLS_IENONPIC 53 +#define R_METAG_TLS_IENONPIC_HI16 54 +#define R_METAG_TLS_IENONPIC_LO16 55 +#define R_METAG_TLS_TPOFF 56 +#define R_METAG_TLS_DTPMOD 57 +#define R_METAG_TLS_DTPOFF 58 +#define R_METAG_TLS_LE 59 +#define R_METAG_TLS_LE_HI16 60 +#define R_METAG_TLS_LE_LO16 61 + +__END_DECLS + +#endif /* elf.h */ diff --git a/REORG.TODO/elf/enbl-secure.c b/REORG.TODO/elf/enbl-secure.c new file mode 100644 index 0000000000..c0723774d9 --- /dev/null +++ b/REORG.TODO/elf/enbl-secure.c @@ -0,0 +1,36 @@ +/* Define and initialize the `__libc_enable_secure' flag. Generic version. + Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* 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/REORG.TODO/elf/failobj.c b/REORG.TODO/elf/failobj.c new file mode 100644 index 0000000000..500606382e --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/filter.c b/REORG.TODO/elf/filter.c new file mode 100644 index 0000000000..46aa15ba16 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/filtmod1.c b/REORG.TODO/elf/filtmod1.c new file mode 100644 index 0000000000..1d9b19481d --- /dev/null +++ b/REORG.TODO/elf/filtmod1.c @@ -0,0 +1,7 @@ +extern const char *foo (void); + +const char * +foo (void) +{ + return __FILE__; +} diff --git a/REORG.TODO/elf/filtmod2.c b/REORG.TODO/elf/filtmod2.c new file mode 100644 index 0000000000..1d9b19481d --- /dev/null +++ b/REORG.TODO/elf/filtmod2.c @@ -0,0 +1,7 @@ +extern const char *foo (void); + +const char * +foo (void) +{ + return __FILE__; +} diff --git a/REORG.TODO/elf/firstobj.c b/REORG.TODO/elf/firstobj.c new file mode 100644 index 0000000000..2e6033eab6 --- /dev/null +++ b/REORG.TODO/elf/firstobj.c @@ -0,0 +1,10 @@ +#include <errno.h> + +extern int foo (void); + +int +foo (void) +{ + errno = 0; + return 0; +} diff --git a/REORG.TODO/elf/gen-trusted-dirs.awk b/REORG.TODO/elf/gen-trusted-dirs.awk new file mode 100644 index 0000000000..59f10a4856 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/genrtldtbl.awk b/REORG.TODO/elf/genrtldtbl.awk new file mode 100644 index 0000000000..0e2a374901 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/get-dynamic-info.h b/REORG.TODO/elf/get-dynamic-info.h new file mode 100644 index 0000000000..7525c3a5b2 --- /dev/null +++ b/REORG.TODO/elf/get-dynamic-info.h @@ -0,0 +1,184 @@ +/* Read the dynamic section at DYN and fill in INFO with indices DT_*. + Copyright (C) 2012-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This file is included multiple times and therefore lacks a header + file inclusion guard. */ + +#include <assert.h> +#include <libc-diag.h> + +#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; +#if __ELF_NATIVE_CLASS == 32 + typedef Elf32_Word d_tag_utype; +#elif __ELF_NATIVE_CLASS == 64 + typedef Elf64_Xword d_tag_utype; +#endif + +#ifndef RTLD_BOOTSTRAP + if (dyn == NULL) + return; +#endif + + info = l->l_info; + + while (dyn->d_tag != DT_NULL) + { + if ((d_tag_utype) dyn->d_tag < DT_NUM) + info[dyn->d_tag] = dyn; + else if (dyn->d_tag >= DT_LOPROC && + dyn->d_tag < DT_LOPROC + DT_THISPROCNUM) + { + /* This does not violate the array bounds of l->l_info, but + gcc 4.6 on sparc somehow does not see this. */ + DIAG_PUSH_NEEDS_COMMENT; + DIAG_IGNORE_NEEDS_COMMENT (4.6, + "-Warray-bounds"); + info[dyn->d_tag - DT_LOPROC + DT_NUM] = dyn; + DIAG_POP_NEEDS_COMMENT; + } + else if ((d_tag_utype) DT_VERSIONTAGIDX (dyn->d_tag) < DT_VERSIONTAGNUM) + info[VERSYMIDX (dyn->d_tag)] = dyn; + else if ((d_tag_utype) DT_EXTRATAGIDX (dyn->d_tag) < DT_EXTRANUM) + info[DT_EXTRATAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM + + DT_VERSIONTAGNUM] = dyn; + else if ((d_tag_utype) DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM) + info[DT_VALTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM + + DT_VERSIONTAGNUM + DT_EXTRANUM] = dyn; + else if ((d_tag_utype) 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) == 0); + assert (info[DT_FLAGS] == NULL + || (info[DT_FLAGS]->d_un.d_val & ~DF_BIND_NOW) == 0); + /* 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; + + /* Only DT_1_SUPPORTED_MASK bits are supported, and we would like + to assert this, but we can't. Users have been setting + unsupported DF_1_* flags for a long time and glibc has ignored + them. Therefore to avoid breaking existing applications the + best we can do is add a warning during debugging with the + intent of notifying the user of the problem. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0) + && l->l_flags_1 & ~DT_1_SUPPORTED_MASK) + _dl_debug_printf ("\nWARNING: Unsupported flag value(s) of 0x%x in DT_FLAGS_1.\n", + l->l_flags_1 & ~DT_1_SUPPORTED_MASK); + + 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 +} diff --git a/REORG.TODO/elf/global.c b/REORG.TODO/elf/global.c new file mode 100644 index 0000000000..c675858b64 --- /dev/null +++ b/REORG.TODO/elf/global.c @@ -0,0 +1,7 @@ +extern int test (void); + +int +main (void) +{ + return test (); +} diff --git a/REORG.TODO/elf/globalmod1.c b/REORG.TODO/elf/globalmod1.c new file mode 100644 index 0000000000..3f80822269 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/ifuncdep1.c b/REORG.TODO/elf/ifuncdep1.c new file mode 100644 index 0000000000..77d663dcec --- /dev/null +++ b/REORG.TODO/elf/ifuncdep1.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols without -fPIC. */ + +#include "ifuncmod1.c" diff --git a/REORG.TODO/elf/ifuncdep1pic.c b/REORG.TODO/elf/ifuncdep1pic.c new file mode 100644 index 0000000000..b6381e4868 --- /dev/null +++ b/REORG.TODO/elf/ifuncdep1pic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC. */ + +#include "ifuncmod1.c" diff --git a/REORG.TODO/elf/ifuncdep2.c b/REORG.TODO/elf/ifuncdep2.c new file mode 100644 index 0000000000..d87d61d5be --- /dev/null +++ b/REORG.TODO/elf/ifuncdep2.c @@ -0,0 +1,59 @@ +/* Test 3 STT_GNU_IFUNC symbols. */ + +#include "ifunc-sel.h" + +int global = -1; +/* Can't use __attribute__((visibility("protected"))) until the GCC bug: + + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65248 + + is fixed. */ +asm (".protected global"); + +static int +one (void) +{ + return 1; +} + +static int +minus_one (void) +{ + return -1; +} + +static int +zero (void) +{ + return 0; +} + +void * foo1_ifunc (void) __asm__ ("foo1"); +__asm__(".type foo1, %gnu_indirect_function"); + +void * +inhibit_stack_protector +foo1_ifunc (void) +{ + return ifunc_sel (one, minus_one, zero); +} + +void * foo2_ifunc (void) __asm__ ("foo2"); +__asm__(".type foo2, %gnu_indirect_function"); + +void * +inhibit_stack_protector +foo2_ifunc (void) +{ + return ifunc_sel (minus_one, one, zero); +} + +void * foo3_ifunc (void) __asm__ ("foo3"); +__asm__(".type foo3, %gnu_indirect_function"); + +void * +inhibit_stack_protector +foo3_ifunc (void) +{ + return ifunc_sel (one, zero, minus_one); +} diff --git a/REORG.TODO/elf/ifuncdep2pic.c b/REORG.TODO/elf/ifuncdep2pic.c new file mode 100644 index 0000000000..a84253dbc4 --- /dev/null +++ b/REORG.TODO/elf/ifuncdep2pic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC. */ + +#include "ifuncdep2.c" diff --git a/REORG.TODO/elf/ifuncdep5.c b/REORG.TODO/elf/ifuncdep5.c new file mode 100644 index 0000000000..f26234336e --- /dev/null +++ b/REORG.TODO/elf/ifuncdep5.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols without -fPIC. */ + +#include "ifuncmod5.c" diff --git a/REORG.TODO/elf/ifuncdep5pic.c b/REORG.TODO/elf/ifuncdep5pic.c new file mode 100644 index 0000000000..3edb3a07c6 --- /dev/null +++ b/REORG.TODO/elf/ifuncdep5pic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC. */ + +#include "ifuncmod5.c" diff --git a/REORG.TODO/elf/ifuncmain1.c b/REORG.TODO/elf/ifuncmain1.c new file mode 100644 index 0000000000..747fc02648 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain1.c @@ -0,0 +1,64 @@ +/* Test STT_GNU_IFUNC symbols: + + 1. Direct function call. + 2. Function pointer. + 3. Visibility without override. + */ + +#include <stdlib.h> + +int ret_foo; +int ret_foo_hidden; +int ret_foo_protected; + +extern int foo (void); +extern int foo_protected (void); + +#ifndef FOO_P +typedef int (*foo_p) (void); +#endif + +foo_p foo_ptr = foo; +foo_p foo_procted_ptr = foo_protected; + +extern foo_p get_foo_p (void); +extern foo_p get_foo_hidden_p (void); +extern foo_p get_foo_protected_p (void); + +int +main (void) +{ + foo_p p; + + if (foo_ptr != foo) + abort (); + if (foo () != -1) + abort (); + if ((*foo_ptr) () != -1) + abort (); + + if (foo_procted_ptr != foo_protected) + abort (); + if (foo_protected () != 0) + abort (); + if ((*foo_procted_ptr) () != 0) + abort (); + + p = get_foo_p (); + if (p != foo) + abort (); + if (ret_foo != -1 || (*p) () != ret_foo) + abort (); + + p = get_foo_hidden_p (); + if (ret_foo_hidden != 1 || (*p) () != ret_foo_hidden) + abort (); + + p = get_foo_protected_p (); + if (p != foo_protected) + abort (); + if (ret_foo_protected != 0 || (*p) () != ret_foo_protected) + abort (); + + return 0; +} diff --git a/REORG.TODO/elf/ifuncmain1pic.c b/REORG.TODO/elf/ifuncmain1pic.c new file mode 100644 index 0000000000..db19dc9678 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain1pic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC. */ + +#include "ifuncmain1.c" diff --git a/REORG.TODO/elf/ifuncmain1picstatic.c b/REORG.TODO/elf/ifuncmain1picstatic.c new file mode 100644 index 0000000000..c937933029 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain1picstatic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC and -static. */ + +#include "ifuncmain1.c" diff --git a/REORG.TODO/elf/ifuncmain1pie.c b/REORG.TODO/elf/ifuncmain1pie.c new file mode 100644 index 0000000000..c16ef6dd09 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain1pie.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with PIE. */ + +#include "ifuncmain1.c" diff --git a/REORG.TODO/elf/ifuncmain1static.c b/REORG.TODO/elf/ifuncmain1static.c new file mode 100644 index 0000000000..fdd1e09024 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain1static.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -static. */ + +#include "ifuncmain1.c" diff --git a/REORG.TODO/elf/ifuncmain1staticpic.c b/REORG.TODO/elf/ifuncmain1staticpic.c new file mode 100644 index 0000000000..39e0cbb4b8 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain1staticpic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC and no DSO. */ + +#include "ifuncmain1.c" diff --git a/REORG.TODO/elf/ifuncmain1staticpie.c b/REORG.TODO/elf/ifuncmain1staticpie.c new file mode 100644 index 0000000000..4891114260 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain1staticpie.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with PIE and no DSO. */ + +#include "ifuncmain1.c" diff --git a/REORG.TODO/elf/ifuncmain1vis.c b/REORG.TODO/elf/ifuncmain1vis.c new file mode 100644 index 0000000000..d35e2f81fc --- /dev/null +++ b/REORG.TODO/elf/ifuncmain1vis.c @@ -0,0 +1,87 @@ +/* Test STT_GNU_IFUNC symbols: + + 1. Direct function call. + 2. Function pointer. + 3. Visibility with override. + */ + +#include <stdlib.h> + +int ret_foo; +int ret_foo_hidden; +int ret_foo_protected; + +extern int foo (void); +extern int foo_protected (void); + +#ifndef FOO_P +typedef int (*foo_p) (void); +#endif + +foo_p foo_ptr = foo; +foo_p foo_procted_ptr = foo_protected; + +extern foo_p get_foo_p (void); +extern foo_p get_foo_hidden_p (void); +extern foo_p get_foo_protected_p (void); + +int +__attribute__ ((noinline)) +foo (void) +{ + return -30; +} + +int +__attribute__ ((noinline)) +foo_hidden (void) +{ + return -20; +} + +int +__attribute__ ((noinline)) +foo_protected (void) +{ + return -40; +} + +int +main (void) +{ + foo_p p; + + if (foo_ptr != foo) + abort (); + if ((*foo_ptr) () != -30) + abort (); + + if (foo_procted_ptr != foo_protected) + abort (); + if ((*foo_procted_ptr) () != -40) + abort (); + + p = get_foo_p (); + if (p != foo) + abort (); + if (foo () != -30) + abort (); + if (ret_foo != -30 || (*p) () != ret_foo) + abort (); + + p = get_foo_hidden_p (); + if (foo_hidden () != -20) + abort (); + if (ret_foo_hidden != 1 || (*p) () != ret_foo_hidden) + abort (); + + p = get_foo_protected_p (); + if (p == foo_protected) + abort (); + if (foo_protected () != -40) + abort (); + if (ret_foo_protected != 0 || (*p) () != ret_foo_protected) + abort (); + + return 0; +} diff --git a/REORG.TODO/elf/ifuncmain1vispic.c b/REORG.TODO/elf/ifuncmain1vispic.c new file mode 100644 index 0000000000..f8c104d560 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain1vispic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC. */ + +#include "ifuncmain1vis.c" diff --git a/REORG.TODO/elf/ifuncmain1vispie.c b/REORG.TODO/elf/ifuncmain1vispie.c new file mode 100644 index 0000000000..ad06d2ba1c --- /dev/null +++ b/REORG.TODO/elf/ifuncmain1vispie.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with PIE. */ + +#include "ifuncmain1vis.c" diff --git a/REORG.TODO/elf/ifuncmain2.c b/REORG.TODO/elf/ifuncmain2.c new file mode 100644 index 0000000000..db3ba56a02 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain2.c @@ -0,0 +1,14 @@ +/* Test calling one STT_GNU_IFUNC function with 3 different + STT_GNU_IFUNC definitions. */ + +#include <stdlib.h> + +extern int foo1 (void); + +int +main (void) +{ + if (foo1 () != -1) + abort (); + return 0; +} diff --git a/REORG.TODO/elf/ifuncmain2pic.c b/REORG.TODO/elf/ifuncmain2pic.c new file mode 100644 index 0000000000..0006012a96 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain2pic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC. */ + +#include "ifuncmain2.c" diff --git a/REORG.TODO/elf/ifuncmain2picstatic.c b/REORG.TODO/elf/ifuncmain2picstatic.c new file mode 100644 index 0000000000..3e89db536d --- /dev/null +++ b/REORG.TODO/elf/ifuncmain2picstatic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC and -static. */ + +#include "ifuncmain2.c" diff --git a/REORG.TODO/elf/ifuncmain2static.c b/REORG.TODO/elf/ifuncmain2static.c new file mode 100644 index 0000000000..6932ae8066 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain2static.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -static. */ + +#include "ifuncmain2.c" diff --git a/REORG.TODO/elf/ifuncmain3.c b/REORG.TODO/elf/ifuncmain3.c new file mode 100644 index 0000000000..1574dd5cbe --- /dev/null +++ b/REORG.TODO/elf/ifuncmain3.c @@ -0,0 +1,129 @@ +/* Test STT_GNU_IFUNC symbols with dlopen: + + 1. Direct function call. + 2. Function pointer. + 3. Visibility with override. + */ + +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> + +typedef int (*foo_p) (void); + +int +__attribute__ ((noinline)) +foo (void) +{ + return -30; +} + +int +__attribute__ ((noinline)) +foo_hidden (void) +{ + return -20; +} + +int +__attribute__ ((noinline)) +foo_protected (void) +{ + return -40; +} + +int +main (void) +{ + foo_p p; + foo_p (*f) (void); + int *ret; + + void *h = dlopen ("ifuncmod3.so", RTLD_LAZY); + if (h == NULL) + { + printf ("cannot load: %s\n", dlerror ()); + return 1; + } + + p = dlsym (h, "foo"); + if (p == NULL) + { + printf ("symbol not found: %s\n", dlerror ()); + return 1; + } + if ((*p) () != -1) + abort (); + + f = dlsym (h, "get_foo_p"); + if (f == NULL) + { + printf ("symbol not found: %s\n", dlerror ()); + return 1; + } + + ret = dlsym (h, "ret_foo"); + if (ret == NULL) + { + printf ("symbol not found: %s\n", dlerror ()); + return 1; + } + + p = (*f) (); + if (p != foo) + abort (); + if (foo () != -30) + abort (); + if (*ret != -30 || (*p) () != *ret) + abort (); + + f = dlsym (h, "get_foo_hidden_p"); + if (f == NULL) + { + printf ("symbol not found: %s\n", dlerror ()); + return 1; + } + + ret = dlsym (h, "ret_foo_hidden"); + if (ret == NULL) + { + printf ("symbol not found: %s\n", dlerror ()); + return 1; + } + + p = (*f) (); + if (foo_hidden () != -20) + abort (); + if (*ret != 1 || (*p) () != *ret) + abort (); + + f = dlsym (h, "get_foo_protected_p"); + if (f == NULL) + { + printf ("symbol not found: %s\n", dlerror ()); + return 1; + } + + ret = dlsym (h, "ret_foo_protected"); + if (ret == NULL) + { + printf ("symbol not found: %s\n", dlerror ()); + return 1; + } + + p = (*f) (); + if (p == foo_protected) + abort (); + if (foo_protected () != -40) + abort (); + if (*ret != 0 || (*p) () != *ret) + abort (); + + if (dlclose (h) != 0) + { + printf ("cannot close: %s\n", dlerror ()); + return 1; + } + + return 0; +} diff --git a/REORG.TODO/elf/ifuncmain4.c b/REORG.TODO/elf/ifuncmain4.c new file mode 100644 index 0000000000..e55fee2eb3 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain4.c @@ -0,0 +1,4 @@ +/* Test STT_GNU_IFUNC symbols in a single source file. */ + +#include "ifuncmod1.c" +#include "ifuncmain1.c" diff --git a/REORG.TODO/elf/ifuncmain4picstatic.c b/REORG.TODO/elf/ifuncmain4picstatic.c new file mode 100644 index 0000000000..977d7f97fc --- /dev/null +++ b/REORG.TODO/elf/ifuncmain4picstatic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC and -static. */ + +#include "ifuncmain4.c" diff --git a/REORG.TODO/elf/ifuncmain4static.c b/REORG.TODO/elf/ifuncmain4static.c new file mode 100644 index 0000000000..c399977013 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain4static.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -static. */ + +#include "ifuncmain4.c" diff --git a/REORG.TODO/elf/ifuncmain5.c b/REORG.TODO/elf/ifuncmain5.c new file mode 100644 index 0000000000..f398085cb4 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain5.c @@ -0,0 +1,38 @@ +/* Test STT_GNU_IFUNC symbols with dynamic function pointer only. */ + +#include <stdlib.h> + +extern int foo (void); +extern int foo_protected (void); + +typedef int (*foo_p) (void); + +foo_p +__attribute__ ((noinline)) +get_foo (void) +{ + return foo; +} + +foo_p +__attribute__ ((noinline)) +get_foo_protected (void) +{ + return foo_protected; +} + +int +main (void) +{ + foo_p p; + + p = get_foo (); + if ((*p) () != -1) + abort (); + + p = get_foo_protected (); + if ((*p) () != 0) + abort (); + + return 0; +} diff --git a/REORG.TODO/elf/ifuncmain5pic.c b/REORG.TODO/elf/ifuncmain5pic.c new file mode 100644 index 0000000000..e9144fbb20 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain5pic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC. */ + +#include "ifuncmain5.c" diff --git a/REORG.TODO/elf/ifuncmain5picstatic.c b/REORG.TODO/elf/ifuncmain5picstatic.c new file mode 100644 index 0000000000..a0afe905d7 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain5picstatic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC and -static. */ + +#include "ifuncmain5.c" diff --git a/REORG.TODO/elf/ifuncmain5pie.c b/REORG.TODO/elf/ifuncmain5pie.c new file mode 100644 index 0000000000..669f31eeda --- /dev/null +++ b/REORG.TODO/elf/ifuncmain5pie.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with PIE. */ + +#include "ifuncmain5.c" diff --git a/REORG.TODO/elf/ifuncmain5static.c b/REORG.TODO/elf/ifuncmain5static.c new file mode 100644 index 0000000000..72504404a5 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain5static.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -static. */ + +#include "ifuncmain5.c" diff --git a/REORG.TODO/elf/ifuncmain5staticpic.c b/REORG.TODO/elf/ifuncmain5staticpic.c new file mode 100644 index 0000000000..9e8bac254f --- /dev/null +++ b/REORG.TODO/elf/ifuncmain5staticpic.c @@ -0,0 +1,3 @@ +/* Test STT_GNU_IFUNC symbols with -fPIC and no DSO. */ + +#include "ifuncmain5.c" diff --git a/REORG.TODO/elf/ifuncmain6pie.c b/REORG.TODO/elf/ifuncmain6pie.c new file mode 100644 index 0000000000..04faeb86ef --- /dev/null +++ b/REORG.TODO/elf/ifuncmain6pie.c @@ -0,0 +1,65 @@ +/* Test STT_GNU_IFUNC symbols in PIE: + + 1. Direct function call. + 2. Function pointer. + 3. Reference from a shared library. + */ + +#include <stdlib.h> +#include "ifunc-sel.h" + +typedef int (*foo_p) (void); +extern foo_p foo_ptr; + +static int +one (void) +{ + return -30; +} + +void * foo_ifunc (void) __asm__ ("foo"); +__asm__(".type foo, %gnu_indirect_function"); + +void * +inhibit_stack_protector +foo_ifunc (void) +{ + return ifunc_one (one); +} + +extern int foo (void); +extern foo_p get_foo (void); +extern foo_p get_foo_p (void); + +foo_p my_foo_ptr = foo; + +int +main (void) +{ + foo_p p; + + p = get_foo (); + if (p != foo) + abort (); + if ((*p) () != -30) + abort (); + + p = get_foo_p (); + if (p != foo) + abort (); + if ((*p) () != -30) + abort (); + + if (foo_ptr != foo) + abort (); + if (my_foo_ptr != foo) + abort (); + if ((*foo_ptr) () != -30) + abort (); + if ((*my_foo_ptr) () != -30) + abort (); + if (foo () != -30) + abort (); + + return 0; +} diff --git a/REORG.TODO/elf/ifuncmain7.c b/REORG.TODO/elf/ifuncmain7.c new file mode 100644 index 0000000000..1e8f7ea38e --- /dev/null +++ b/REORG.TODO/elf/ifuncmain7.c @@ -0,0 +1,72 @@ +/* Test local STT_GNU_IFUNC symbols: + + 1. Direct function call. + 2. Function pointer. + */ + +#include <stdlib.h> +#include "ifunc-sel.h" + +extern int foo (void); + +static int +one (void) +{ + return -30; +} + +static void * foo_ifunc (void) __asm__ ("foo"); +__asm__(".type foo, %gnu_indirect_function"); + +static void * +__attribute__ ((used)) +inhibit_stack_protector +foo_ifunc (void) +{ + return ifunc_one (one); +} + +typedef int (*foo_p) (void); + +foo_p foo_ptr = foo; + +foo_p +__attribute__ ((noinline)) +get_foo_p (void) +{ + return foo_ptr; +} + +foo_p +__attribute__ ((noinline)) +get_foo (void) +{ + return foo; +} + +int +main (void) +{ + foo_p p; + + p = get_foo (); + if (p != foo) + abort (); + if ((*p) () != -30) + abort (); + + p = get_foo_p (); + if (p != foo) + abort (); + if ((*p) () != -30) + abort (); + + if (foo_ptr != foo) + abort (); + if ((*foo_ptr) () != -30) + abort (); + if (foo () != -30) + abort (); + + return 0; +} diff --git a/REORG.TODO/elf/ifuncmain7pic.c b/REORG.TODO/elf/ifuncmain7pic.c new file mode 100644 index 0000000000..fc37bf4469 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain7pic.c @@ -0,0 +1,7 @@ +/* Test local STT_GNU_IFUNC symbols with -fPIC: + + 1. Direct function call. + 2. Function pointer. + */ + +#include "ifuncmain7.c" diff --git a/REORG.TODO/elf/ifuncmain7picstatic.c b/REORG.TODO/elf/ifuncmain7picstatic.c new file mode 100644 index 0000000000..baf8934b95 --- /dev/null +++ b/REORG.TODO/elf/ifuncmain7picstatic.c @@ -0,0 +1,7 @@ +/* Test local STT_GNU_IFUNC symbols with -fPIC and -static: + + 1. Direct function call. + 2. Function pointer. + */ + +#include "ifuncmain7.c" diff --git a/REORG.TODO/elf/ifuncmain7pie.c b/REORG.TODO/elf/ifuncmain7pie.c new file mode 100644 index 0000000000..254d453f1e --- /dev/null +++ b/REORG.TODO/elf/ifuncmain7pie.c @@ -0,0 +1,7 @@ +/* Test local STT_GNU_IFUNC symbols with PIE: + + 1. Direct function call. + 2. Function pointer. + */ + +#include "ifuncmain7.c" diff --git a/REORG.TODO/elf/ifuncmain7static.c b/REORG.TODO/elf/ifuncmain7static.c new file mode 100644 index 0000000000..e470d570ef --- /dev/null +++ b/REORG.TODO/elf/ifuncmain7static.c @@ -0,0 +1,7 @@ +/* Test local STT_GNU_IFUNC symbols with -static: + + 1. Direct function call. + 2. Function pointer. + */ + +#include "ifuncmain7.c" diff --git a/REORG.TODO/elf/ifuncmod1.c b/REORG.TODO/elf/ifuncmod1.c new file mode 100644 index 0000000000..f0bf5fb45f --- /dev/null +++ b/REORG.TODO/elf/ifuncmod1.c @@ -0,0 +1,100 @@ +/* Test STT_GNU_IFUNC symbols: + + 1. Direct function call. + 2. Function pointer. + 3. Visibility. + */ +#include "ifunc-sel.h" + +int global = -1; +/* Can't use __attribute__((visibility("protected"))) until the GCC bug: + + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65248 + + is fixed. */ +asm (".protected global"); + +static int +one (void) +{ + return 1; +} + +static int +minus_one (void) +{ + return -1; +} + +static int +zero (void) +{ + return 0; +} + +void * foo_ifunc (void) __asm__ ("foo"); +__asm__(".type foo, %gnu_indirect_function"); + +void * +inhibit_stack_protector +foo_ifunc (void) +{ + return ifunc_sel (one, minus_one, zero); +} + +void * foo_hidden_ifunc (void) __asm__ ("foo_hidden"); +__asm__(".type foo_hidden, %gnu_indirect_function"); + +void * +inhibit_stack_protector +foo_hidden_ifunc (void) +{ + return ifunc_sel (minus_one, one, zero); +} + +void * foo_protected_ifunc (void) __asm__ ("foo_protected"); +__asm__(".type foo_protected, %gnu_indirect_function"); + +void * +inhibit_stack_protector +foo_protected_ifunc (void) +{ + return ifunc_sel (one, zero, minus_one); +} + +/* Test hidden indirect function. */ +__asm__(".hidden foo_hidden"); + +/* Test protected indirect function. */ +__asm__(".protected foo_protected"); + +extern int foo (void); +extern int foo_hidden (void); +extern int foo_protected (void); +extern int ret_foo; +extern int ret_foo_hidden; +extern int ret_foo_protected; + +#define FOO_P +typedef int (*foo_p) (void); + +foo_p +get_foo_p (void) +{ + ret_foo = foo (); + return foo; +} + +foo_p +get_foo_hidden_p (void) +{ + ret_foo_hidden = foo_hidden (); + return foo_hidden; +} + +foo_p +get_foo_protected_p (void) +{ + ret_foo_protected = foo_protected (); + return foo_protected; +} diff --git a/REORG.TODO/elf/ifuncmod3.c b/REORG.TODO/elf/ifuncmod3.c new file mode 100644 index 0000000000..ca2d962600 --- /dev/null +++ b/REORG.TODO/elf/ifuncmod3.c @@ -0,0 +1,7 @@ +/* Test STT_GNU_IFUNC symbols with dlopen. */ + +#include "ifuncmod1.c" + +int ret_foo; +int ret_foo_hidden; +int ret_foo_protected; diff --git a/REORG.TODO/elf/ifuncmod5.c b/REORG.TODO/elf/ifuncmod5.c new file mode 100644 index 0000000000..5a957800e8 --- /dev/null +++ b/REORG.TODO/elf/ifuncmod5.c @@ -0,0 +1,64 @@ +/* Test STT_GNU_IFUNC symbols without direct function call. */ +#include "ifunc-sel.h" + +int global = -1; +/* Can't use __attribute__((visibility("protected"))) until the GCC bug: + + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65248 + + is fixed. */ +asm (".protected global"); + +static int +one (void) +{ + return 1; +} + +static int +minus_one (void) +{ + return -1; +} + +static int +zero (void) +{ + return 0; +} + +void * foo_ifunc (void) __asm__ ("foo"); +__asm__(".type foo, %gnu_indirect_function"); + +void * +inhibit_stack_protector +foo_ifunc (void) +{ + return ifunc_sel (one, minus_one, zero); +} + +void * foo_hidden_ifunc (void) __asm__ ("foo_hidden"); +__asm__(".type foo_hidden, %gnu_indirect_function"); + +void * +inhibit_stack_protector +foo_hidden_ifunc (void) +{ + return ifunc_sel (minus_one, one, zero); +} + +void * foo_protected_ifunc (void) __asm__ ("foo_protected"); +__asm__(".type foo_protected, %gnu_indirect_function"); + +void * +inhibit_stack_protector +foo_protected_ifunc (void) +{ + return ifunc_sel (one, zero, minus_one); +} + +/* Test hidden indirect function. */ +__asm__(".hidden foo_hidden"); + +/* Test protected indirect function. */ +__asm__(".protected foo_protected"); diff --git a/REORG.TODO/elf/ifuncmod6.c b/REORG.TODO/elf/ifuncmod6.c new file mode 100644 index 0000000000..2e16c1d06d --- /dev/null +++ b/REORG.TODO/elf/ifuncmod6.c @@ -0,0 +1,19 @@ +/* Test STT_GNU_IFUNC symbol reference in a shared library. */ + +extern int foo (void); + +typedef int (*foo_p) (void); + +foo_p foo_ptr = foo; + +foo_p +get_foo_p (void) +{ + return foo_ptr; +} + +foo_p +get_foo (void) +{ + return foo; +} diff --git a/REORG.TODO/elf/initfirst.c b/REORG.TODO/elf/initfirst.c new file mode 100644 index 0000000000..5ca83d21bc --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/interp.c b/REORG.TODO/elf/interp.c new file mode 100644 index 0000000000..b6e8f04444 --- /dev/null +++ b/REORG.TODO/elf/interp.c @@ -0,0 +1,22 @@ +/* interp - add information about dynamic loader to shared library objects. + Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <runtime-linker.h> + +const char __invoke_dynamic_linker__[] __attribute__ ((section (".interp"))) + = RUNTIME_LINKER; diff --git a/REORG.TODO/elf/lateglobal.c b/REORG.TODO/elf/lateglobal.c new file mode 100644 index 0000000000..4a1a7cd085 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/ldconfig.c b/REORG.TODO/elf/ldconfig.c new file mode 100644 index 0000000000..99caf9e9bb --- /dev/null +++ b/REORG.TODO/elf/ldconfig.c @@ -0,0 +1,1418 @@ +/* Copyright (C) 1999-2017 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 as published + by the Free Software Foundation; version 2 of the License, or + (at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. */ + +#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 <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdint.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; + +/* Enable symbolic link processing. If set, create or update symbolic + links, and remove stale symbolic 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; + +/* Should we ignore an old auxiliary cache file? */ +static int opt_ignore_aux_cache; + +/* 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; + +/* Function to print some extra text in the help message. */ +static char *more_help (int key, const char *text, void *input); + +/* 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 update symbolic 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}, + { "ignore-aux-cache", 'i', NULL, 0, N_("Ignore auxiliary cache file"), 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, more_help, 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); + + /* Is this a normal hwcap for the machine like "fpu?" */ + if (hwcap_idx != -1 && ((1 << hwcap_idx) & hwcap_mask)) + return 1; + + /* Is this a platform pseudo-hwcap like "i686?" */ + hwcap_idx = _dl_string_platform (name); + if (hwcap_idx != -1) + return 1; + + /* Is this one of the extra pseudo-hwcaps that we map beyond + _DL_FIRST_EXTRA like "tls", or "nosegneg?" */ + 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; + /* Ignore auxiliary cache since we use non-standard cache. */ + opt_ignore_aux_cache = 1; + break; + case 'f': + config_file = arg; + break; + case 'i': + opt_ignore_aux_cache = 1; + 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 bug-reporting information in the help message. */ +static char * +more_help (int key, const char *text, void *input) +{ + char *tp = NULL; + switch (key) + { + case ARGP_KEY_HELP_EXTRA: + /* We print some extra information. */ + if (asprintf (&tp, gettext ("\ +For bug reporting instructions, please see:\n\ +%s.\n"), REPORT_BUGS_TO) < 0) + return NULL; + return tp; + default: + break; + } + return (char *) text; +} + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state) +{ + fprintf (stream, "ldconfig %s%s\n", PKGVERSION, 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\ +"), "2017"); + 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. */ + i = strlen (entry->path); + + while (i > 0 && isspace (entry->path[i - 1])) + entry->path[--i] = '\0'; + + while (i > 0 && entry->path[i - 1] == '/') + entry->path[--i] = '\0'; + + if (i == 0) + return; + + 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, _("Cannot 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, &stat_buf)) + { + error (0, 0, _("No link created since soname could not be found for %s"), + library); + free (path); + return; + } + if (soname == NULL) + soname = implicit_soname (libname, flag); + 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) +{ + uint64_t hwcap = path_hwcap (entry->path); + if (opt_verbose) + { + if (hwcap != 0) + printf ("%s: (hwcap: %#.16" PRIx64 ")\n", entry->path, hwcap); + else + printf ("%s:\n", entry->path); + } + + char *dir_name; + char *real_file_name; + size_t real_file_name_len; + size_t file_name_len = PATH_MAX; + char *file_name = alloca (file_name_len); + 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; + } + + DIR *dir; + 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; + } + + struct dirent64 *direntry; + struct dlib_entry *dlibs = NULL; + 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; + + size_t len = strlen (direntry->d_name); + /* Skip temporary files created by the prelink program. Files with + names like these are never really DSOs we want to look at. */ + if (len >= sizeof (".#prelink#") - 1) + { + if (strcmp (direntry->d_name + len - sizeof (".#prelink#") + 1, + ".#prelink#") == 0) + continue; + if (len >= sizeof (".#prelink#.XXXXXX") - 1 + && memcmp (direntry->d_name + len - sizeof (".#prelink#.XXXXXX") + + 1, ".#prelink#.", sizeof (".#prelink#.") - 1) == 0) + continue; + } + len += strlen (entry->path) + 2; + if (len > file_name_len) + { + file_name_len = len; + 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) + 2; + if (len > real_file_name_len) + { + real_file_name_len = len; + real_file_name = alloca (real_file_name_len); + } + sprintf (real_file_name, "%s/%s", dir_name, direntry->d_name); + } + + struct stat64 lstat_buf; +#ifdef _DIRENT_HAVE_D_TYPE + /* We optimize and try to do the lstat call only if needed. */ + if (direntry->d_type != DT_UNKNOWN) + lstat_buf.st_mode = DTTOIF (direntry->d_type); + else +#endif + if (__glibc_unlikely (lstat64 (real_file_name, &lstat_buf))) + { + error (0, errno, _("Cannot lstat %s"), file_name); + continue; + } + + struct stat64 stat_buf; + int is_dir; + int is_link = S_ISLNK (lstat_buf.st_mode); + if (is_link) + { + /* In case of symlink, we check if the symlink refers to + a directory. */ + char *target_name = real_file_name; + if (opt_chroot) + { + target_name = chroot_canon (opt_chroot, file_name); + if (target_name == NULL) + { + if (strstr (file_name, ".so") == NULL) + error (0, 0, _("Input file %s not found.\n"), file_name); + continue; + } + } + if (__glibc_unlikely (stat64 (target_name, &stat_buf))) + { + if (opt_verbose) + error (0, errno, _("Cannot stat %s"), file_name); + + /* Remove stale symlinks. */ + if (opt_link && strstr (direntry->d_name, ".so.")) + unlink (real_file_name); + continue; + } + is_dir = S_ISDIR (stat_buf.st_mode); + + /* lstat_buf is later stored, update contents. */ + lstat_buf.st_dev = stat_buf.st_dev; + lstat_buf.st_ino = stat_buf.st_ino; + lstat_buf.st_size = stat_buf.st_size; + lstat_buf.st_ctime = stat_buf.st_ctime; + } + 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; +#ifdef _DIRENT_HAVE_D_TYPE + /* We have filled in lstat only #ifndef + _DIRENT_HAVE_D_TYPE. Fill it in if needed. */ + if (!is_link + && 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; + + char *real_name; + 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; + +#ifdef _DIRENT_HAVE_D_TYPE + /* Call lstat64 if not done yet. */ + if (!is_link + && direntry->d_type != DT_UNKNOWN + && __builtin_expect (lstat64 (real_file_name, &lstat_buf), 0)) + { + error (0, errno, _("Cannot lstat %s"), file_name); + continue; + } +#endif + + /* First search whether the auxiliary cache contains this + library already and it's not changed. */ + char *soname; + unsigned int osversion; + if (!search_aux_cache (&lstat_buf, &flag, &osversion, &soname)) + { + if (process_file (real_name, file_name, direntry->d_name, &flag, + &osversion, &soname, is_link, &lstat_buf)) + { + if (real_name != real_file_name) + free (real_name); + continue; + } + else if (opt_build_cache) + add_to_aux_cache (&lstat_buf, flag, osversion, soname); + } + + if (soname == NULL) + soname = implicit_soname (direntry->d_name, flag); + + /* A link may just point to itself. */ + if (is_link) + { + /* If the path the link points to isn't its soname or it is not + the .so symlink for ld(1), we treat it as a normal file. + + You should always do this: + + libfoo.so -> SONAME -> Arbitrary package-chosen name. + + e.g. libfoo.so -> libfoo.so.1 -> libfooimp.so.9.99. + Given a SONAME of libfoo.so.1. + + You should *never* do this: + + libfoo.so -> libfooimp.so.9.99 + + If you do, and your SONAME is libfoo.so.1, then libfoo.so + fails to point at the SONAME. In that case ldconfig may consider + libfoo.so as another implementation of SONAME and will create + symlinks against it causing problems when you try to upgrade + or downgrade. The problems will arise because ldconfig will, + depending on directory ordering, creat symlinks against libfoo.so + e.g. libfoo.so.1.2 -> libfoo.so, but when libfoo.so is removed + (typically by the removal of a development pacakge not required + for the runtime) it will break the libfoo.so.1.2 symlink and the + application will fail to start. */ + 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. */ + struct dlib_entry *dlib_ptr; + 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->name = xstrdup (direntry->d_name); + dlib_ptr->is_link = is_link; + dlib_ptr->osversion = osversion; + } + /* 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->soname = soname; + dlib_ptr->flag = flag; + dlib_ptr->is_link = is_link; + dlib_ptr->osversion = osversion; + /* 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. */ + struct dlib_entry *dlib_ptr; + 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, _("\ +Warning: ignoring configuration file that cannot be opened: %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); + if (canon == NULL) + return; + result = glob64 (canon, 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; + } + + 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) +{ + /* Set locale via LC_ALL. */ + setlocale (LC_ALL, ""); + + /* Set the text message domain. */ + textdomain (_libc_intl_domainname); + + /* Parse and process arguments. */ + int remaining; + 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]); + } + + /* The last entry in hwcap_extra is reserved for the "tls" pseudo-hwcap which + indicates support for TLS. This pseudo-hwcap is only used by old versions + under which TLS support was optional. The entry is no longer needed, but + must remain for compatibility. */ + hwcap_extra[63 - _DL_FIRST_EXTRA] = "tls"; + + 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); + } + + const char *aux_cache_file = _PATH_LDCONFIG_AUX_CACHE; + if (opt_chroot) + aux_cache_file = chroot_canon (opt_chroot, aux_cache_file); + + if (! opt_ignore_aux_cache && aux_cache_file) + load_aux_cache (aux_cache_file); + else + init_aux_cache (); + + search_dirs (); + + if (opt_build_cache) + { + save_cache (cache_file); + if (aux_cache_file) + save_aux_cache (aux_cache_file); + } + + return 0; +} diff --git a/REORG.TODO/elf/ldd.bash.in b/REORG.TODO/elf/ldd.bash.in new file mode 100644 index 0000000000..7dd1fccf24 --- /dev/null +++ b/REORG.TODO/elf/ldd.bash.in @@ -0,0 +1,203 @@ +#! @BASH@ +# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# <http://www.gnu.org/licenses/>. + + +# 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 @PKGVERSION@@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. +" "2017" + 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 +" + printf $"For bug reporting instructions, please see:\\n%s.\\n" \ + "@REPORT_BUGS_TO@" + 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 command substitution is needed to make ldd work in SELinux +# environments where the RTLD might not have permission to write to the +# terminal. The extra "x" character prevents the shell from trimming trailing +# newlines from command substitution results. This function is defined as a +# subshell compound list (using "(...)") to prevent parameter assignments from +# affecting the calling shell execution environment. +try_trace() ( + output=$(eval $add_env '"$@"' 2>&1; rc=$?; printf 'x'; exit $rc) + rc=$? + printf '%s' "${output%x}" + return $rc +) + +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/REORG.TODO/elf/link.h b/REORG.TODO/elf/link.h new file mode 100644 index 0000000000..0e223ce9f0 --- /dev/null +++ b/REORG.TODO/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-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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; /* Difference between the address in the ELF + file and the addresses in memory. */ + 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. */ + __extension__ unsigned long long int dlpi_adds; + /* Incremented when an object may have been removed. */ + __extension__ 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/REORG.TODO/elf/loadfail.c b/REORG.TODO/elf/loadfail.c new file mode 100644 index 0000000000..7531aa958e --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/loadtest.c b/REORG.TODO/elf/loadtest.c new file mode 100644 index 0000000000..727469b496 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/ltglobmod1.c b/REORG.TODO/elf/ltglobmod1.c new file mode 100644 index 0000000000..300fa9a89f --- /dev/null +++ b/REORG.TODO/elf/ltglobmod1.c @@ -0,0 +1,7 @@ +extern int bar (void); + +int +bar (void) +{ + return 42; +} diff --git a/REORG.TODO/elf/ltglobmod2.c b/REORG.TODO/elf/ltglobmod2.c new file mode 100644 index 0000000000..33f14cc980 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/multiload.c b/REORG.TODO/elf/multiload.c new file mode 100644 index 0000000000..e85cc96589 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/neededobj1.c b/REORG.TODO/elf/neededobj1.c new file mode 100644 index 0000000000..eb55adab39 --- /dev/null +++ b/REORG.TODO/elf/neededobj1.c @@ -0,0 +1,6 @@ +extern void c_function (void); + +void +c_function (void) +{ +} diff --git a/REORG.TODO/elf/neededobj2.c b/REORG.TODO/elf/neededobj2.c new file mode 100644 index 0000000000..5ad8a51d62 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/neededobj3.c b/REORG.TODO/elf/neededobj3.c new file mode 100644 index 0000000000..da25329aa7 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/neededobj4.c b/REORG.TODO/elf/neededobj4.c new file mode 100644 index 0000000000..3ea8540047 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/neededobj5.c b/REORG.TODO/elf/neededobj5.c new file mode 100644 index 0000000000..2d629b0880 --- /dev/null +++ b/REORG.TODO/elf/neededobj5.c @@ -0,0 +1,5 @@ +extern void a1_function (void); + +void a1_function (void) +{ +} diff --git a/REORG.TODO/elf/neededobj6.c b/REORG.TODO/elf/neededobj6.c new file mode 100644 index 0000000000..639b6f195e --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/neededtest.c b/REORG.TODO/elf/neededtest.c new file mode 100644 index 0000000000..3cea499314 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/neededtest2.c b/REORG.TODO/elf/neededtest2.c new file mode 100644 index 0000000000..17c75f2ba3 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/neededtest3.c b/REORG.TODO/elf/neededtest3.c new file mode 100644 index 0000000000..41970cf2c7 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/neededtest4.c b/REORG.TODO/elf/neededtest4.c new file mode 100644 index 0000000000..0ae0b7ff47 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/next.c b/REORG.TODO/elf/next.c new file mode 100644 index 0000000000..a0d532b8c3 --- /dev/null +++ b/REORG.TODO/elf/next.c @@ -0,0 +1,43 @@ +#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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/nextmod1.c b/REORG.TODO/elf/nextmod1.c new file mode 100644 index 0000000000..56de3536a0 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/nextmod2.c b/REORG.TODO/elf/nextmod2.c new file mode 100644 index 0000000000..b2c435f341 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/nodel2mod1.c b/REORG.TODO/elf/nodel2mod1.c new file mode 100644 index 0000000000..acddc4cf8b --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/nodel2mod2.c b/REORG.TODO/elf/nodel2mod2.c new file mode 100644 index 0000000000..d0020240a8 --- /dev/null +++ b/REORG.TODO/elf/nodel2mod2.c @@ -0,0 +1,7 @@ +void +__attribute__((constructor)) +xxx (void) +{ + extern void baz (void); + baz (); +} diff --git a/REORG.TODO/elf/nodel2mod3.c b/REORG.TODO/elf/nodel2mod3.c new file mode 100644 index 0000000000..6d1a0d47b7 --- /dev/null +++ b/REORG.TODO/elf/nodel2mod3.c @@ -0,0 +1 @@ +int x; diff --git a/REORG.TODO/elf/nodelete.c b/REORG.TODO/elf/nodelete.c new file mode 100644 index 0000000000..c8d71152f2 --- /dev/null +++ b/REORG.TODO/elf/nodelete.c @@ -0,0 +1,210 @@ +#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); +} + + +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 <support/test-driver.c> diff --git a/REORG.TODO/elf/nodelete2.c b/REORG.TODO/elf/nodelete2.c new file mode 100644 index 0000000000..b3d7e31a08 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/nodelmod1.c b/REORG.TODO/elf/nodelmod1.c new file mode 100644 index 0000000000..fc24a7b172 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/nodelmod2.c b/REORG.TODO/elf/nodelmod2.c new file mode 100644 index 0000000000..6bd7108a13 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/nodelmod3.c b/REORG.TODO/elf/nodelmod3.c new file mode 100644 index 0000000000..817c94db6e --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/nodelmod4.c b/REORG.TODO/elf/nodelmod4.c new file mode 100644 index 0000000000..2cca43a6e8 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/nodlopen.c b/REORG.TODO/elf/nodlopen.c new file mode 100644 index 0000000000..642bdb3011 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/nodlopen2.c b/REORG.TODO/elf/nodlopen2.c new file mode 100644 index 0000000000..a223f36834 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/nodlopenmod.c b/REORG.TODO/elf/nodlopenmod.c new file mode 100644 index 0000000000..4bcf8c9786 --- /dev/null +++ b/REORG.TODO/elf/nodlopenmod.c @@ -0,0 +1 @@ +int a = 42; diff --git a/REORG.TODO/elf/nodlopenmod2.c b/REORG.TODO/elf/nodlopenmod2.c new file mode 100644 index 0000000000..e72ae53e95 --- /dev/null +++ b/REORG.TODO/elf/nodlopenmod2.c @@ -0,0 +1,9 @@ +extern int a; + +extern int foo (void); + +int +foo (void) +{ + return a; +} diff --git a/REORG.TODO/elf/noload.c b/REORG.TODO/elf/noload.c new file mode 100644 index 0000000000..bcc85efc27 --- /dev/null +++ b/REORG.TODO/elf/noload.c @@ -0,0 +1,81 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <mcheck.h> + +int +main (void) +{ + int result = 0; + void *p; + + mtrace (); + + /* First try to load an object which is a dependency. This should + succeed. */ + p = dlopen ("testobj1.so", RTLD_LAZY | RTLD_NOLOAD); + if (p == NULL) + { + printf ("cannot open \"testobj1.so\": %s\n", dlerror ()); + result = 1; + } + else + { + puts ("loading \"testobj1.so\" succeeded, OK"); + dlclose (p); + } + + /* 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. */ + 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"); + + void *q = dlopen ("testobj5.so", RTLD_LAZY | RTLD_NOLOAD); + if (q == NULL) + { + printf ("cannot open \"testobj5.so\": %s\n", dlerror ()); + result = 1; + } + else + { + puts ("loading \"testobj5.so\" with RTLD_NOLOAD succeeded, OK"); + dlclose (q); + } + + 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/REORG.TODO/elf/order.c b/REORG.TODO/elf/order.c new file mode 100644 index 0000000000..ca617cbc09 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/order2.c b/REORG.TODO/elf/order2.c new file mode 100644 index 0000000000..bcf266d5b1 --- /dev/null +++ b/REORG.TODO/elf/order2.c @@ -0,0 +1,45 @@ +#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; +} + +#include <support/test-driver.c> + +static void +__attribute__ ((destructor)) +fini (void) +{ + if (call_puts) + puts ("5"); +} diff --git a/REORG.TODO/elf/order2mod1.c b/REORG.TODO/elf/order2mod1.c new file mode 100644 index 0000000000..b695db29b7 --- /dev/null +++ b/REORG.TODO/elf/order2mod1.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +static void +__attribute__ ((destructor)) +fini (void) +{ + putchar ('1'); +} diff --git a/REORG.TODO/elf/order2mod2.c b/REORG.TODO/elf/order2mod2.c new file mode 100644 index 0000000000..026cd2acc4 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/order2mod3.c b/REORG.TODO/elf/order2mod3.c new file mode 100644 index 0000000000..7913a79925 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/order2mod4.c b/REORG.TODO/elf/order2mod4.c new file mode 100644 index 0000000000..4f2026f041 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/origtest.c b/REORG.TODO/elf/origtest.c new file mode 100644 index 0000000000..1cacabcc39 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/pathoptobj.c b/REORG.TODO/elf/pathoptobj.c new file mode 100644 index 0000000000..a452c2d7d4 --- /dev/null +++ b/REORG.TODO/elf/pathoptobj.c @@ -0,0 +1,8 @@ +extern int in_renamed (int); + + +int +in_renamed (int a) +{ + return a - 10; +} diff --git a/REORG.TODO/elf/pldd-xx.c b/REORG.TODO/elf/pldd-xx.c new file mode 100644 index 0000000000..5ce0bce4c9 --- /dev/null +++ b/REORG.TODO/elf/pldd-xx.c @@ -0,0 +1,251 @@ +/* Copyright (C) 2011-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@gmail.com>, 2011. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#define E(name) E_(name, CLASS) +#define E_(name, cl) E__(name, cl) +#define E__(name, cl) name##cl +#define EW(type) EW_(Elf, CLASS, type) +#define EW_(e, w, t) EW__(e, w, _##t) +#define EW__(e, w, t) e##w##t + +#define pldd_assert(name, exp) \ + typedef int __assert_##name[((exp) != 0) - 1] + + +struct E(link_map) +{ + EW(Addr) l_addr; + EW(Addr) l_name; + EW(Addr) l_ld; + EW(Addr) l_next; + EW(Addr) l_prev; + EW(Addr) l_real; + Lmid_t l_ns; + EW(Addr) l_libname; +}; +#if CLASS == __ELF_NATIVE_CLASS +pldd_assert (l_addr, (offsetof (struct link_map, l_addr) + == offsetof (struct E(link_map), l_addr))); +pldd_assert (l_name, (offsetof (struct link_map, l_name) + == offsetof (struct E(link_map), l_name))); +pldd_assert (l_next, (offsetof (struct link_map, l_next) + == offsetof (struct E(link_map), l_next))); +#endif + + +struct E(libname_list) +{ + EW(Addr) name; + EW(Addr) next; +}; +#if CLASS == __ELF_NATIVE_CLASS +pldd_assert (name, (offsetof (struct libname_list, name) + == offsetof (struct E(libname_list), name))); +pldd_assert (next, (offsetof (struct libname_list, next) + == offsetof (struct E(libname_list), next))); +#endif + +struct E(r_debug) +{ + int r_version; +#if CLASS == 64 + int pad; +#endif + EW(Addr) r_map; +}; +#if CLASS == __ELF_NATIVE_CLASS +pldd_assert (r_version, (offsetof (struct r_debug, r_version) + == offsetof (struct E(r_debug), r_version))); +pldd_assert (r_map, (offsetof (struct r_debug, r_map) + == offsetof (struct E(r_debug), r_map))); +#endif + + +static int + +E(find_maps) (pid_t pid, void *auxv, size_t auxv_size) +{ + EW(Addr) phdr = 0; + unsigned int phnum = 0; + unsigned int phent = 0; + + EW(auxv_t) *auxvXX = (EW(auxv_t) *) auxv; + for (int i = 0; i < auxv_size / sizeof (EW(auxv_t)); ++i) + switch (auxvXX[i].a_type) + { + case AT_PHDR: + phdr = auxvXX[i].a_un.a_val; + break; + case AT_PHNUM: + phnum = auxvXX[i].a_un.a_val; + break; + case AT_PHENT: + phent = auxvXX[i].a_un.a_val; + break; + default: + break; + } + + if (phdr == 0 || phnum == 0 || phent == 0) + error (EXIT_FAILURE, 0, gettext ("cannot find program header of process")); + + EW(Phdr) *p = alloca (phnum * phent); + if (pread64 (memfd, p, phnum * phent, phdr) != phnum * phent) + { + error (0, 0, gettext ("cannot read program header")); + return EXIT_FAILURE; + } + + /* Determine the load offset. We need this for interpreting the + other program header entries so we do this in a separate loop. + Fortunately it is the first time unless someone does something + stupid when linking the application. */ + EW(Addr) offset = 0; + for (unsigned int i = 0; i < phnum; ++i) + if (p[i].p_type == PT_PHDR) + { + offset = phdr - p[i].p_vaddr; + break; + } + + EW(Addr) list = 0; + char *interp = NULL; + for (unsigned int i = 0; i < phnum; ++i) + if (p[i].p_type == PT_DYNAMIC) + { + EW(Dyn) *dyn = xmalloc (p[i].p_filesz); + if (pread64 (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr) + != p[i].p_filesz) + { + error (0, 0, gettext ("cannot read dynamic section")); + return EXIT_FAILURE; + } + + /* Search for the DT_DEBUG entry. */ + for (unsigned int j = 0; j < p[i].p_filesz / sizeof (EW(Dyn)); ++j) + if (dyn[j].d_tag == DT_DEBUG && dyn[j].d_un.d_ptr != 0) + { + struct E(r_debug) r; + if (pread64 (memfd, &r, sizeof (r), dyn[j].d_un.d_ptr) + != sizeof (r)) + { + error (0, 0, gettext ("cannot read r_debug")); + return EXIT_FAILURE; + } + + if (r.r_map != 0) + { + list = r.r_map; + break; + } + } + + free (dyn); + break; + } + else if (p[i].p_type == PT_INTERP) + { + interp = alloca (p[i].p_filesz); + if (pread64 (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr) + != p[i].p_filesz) + { + error (0, 0, gettext ("cannot read program interpreter")); + return EXIT_FAILURE; + } + } + + if (list == 0) + { + if (interp == NULL) + { + // XXX check whether the executable itself is the loader + return EXIT_FAILURE; + } + + // XXX perhaps try finding ld.so and _r_debug in it + + return EXIT_FAILURE; + } + + /* Print the PID and program name first. */ + printf ("%lu:\t%s\n", (unsigned long int) pid, exe); + + /* Iterate over the list of objects and print the information. */ + struct scratch_buffer tmpbuf; + scratch_buffer_init (&tmpbuf); + int status = 0; + do + { + struct E(link_map) m; + if (pread64 (memfd, &m, sizeof (m), list) != sizeof (m)) + { + error (0, 0, gettext ("cannot read link map")); + status = EXIT_FAILURE; + goto out; + } + + EW(Addr) name_offset = m.l_name; + again: + while (1) + { + ssize_t n = pread64 (memfd, tmpbuf.data, tmpbuf.length, name_offset); + if (n == -1) + { + error (0, 0, gettext ("cannot read object name")); + status = EXIT_FAILURE; + goto out; + } + + if (memchr (tmpbuf.data, '\0', n) != NULL) + break; + + if (!scratch_buffer_grow (&tmpbuf)) + { + error (0, 0, gettext ("cannot allocate buffer for object name")); + status = EXIT_FAILURE; + goto out; + } + } + + if (((char *)tmpbuf.data)[0] == '\0' && name_offset == m.l_name + && m.l_libname != 0) + { + /* Try the l_libname element. */ + struct E(libname_list) ln; + if (pread64 (memfd, &ln, sizeof (ln), m.l_libname) == sizeof (ln)) + { + name_offset = ln.name; + goto again; + } + } + + /* Skip over the executable. */ + if (((char *)tmpbuf.data)[0] != '\0') + printf ("%s\n", (char *)tmpbuf.data); + + list = m.l_next; + } + while (list != 0); + + out: + scratch_buffer_free (&tmpbuf); + return status; +} + + +#undef CLASS diff --git a/REORG.TODO/elf/pldd.c b/REORG.TODO/elf/pldd.c new file mode 100644 index 0000000000..ddfa88d63b --- /dev/null +++ b/REORG.TODO/elf/pldd.c @@ -0,0 +1,344 @@ +/* List dynamic shared objects linked into given process. + Copyright (C) 2011-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@gmail.com>, 2011. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <alloca.h> +#include <argp.h> +#include <assert.h> +#include <dirent.h> +#include <elf.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <libintl.h> +#include <link.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ptrace.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <scratch_buffer.h> + +#include <ldsodefs.h> +#include <version.h> + +/* Global variables. */ +extern char *program_invocation_short_name; +#define PACKAGE _libc_intl_domainname + +/* External functions. */ +#include <programs/xmalloc.h> + +/* 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; + +/* Function to print some extra text in the help message. */ +static char *more_help (int key, const char *text, void *input); + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { NULL, 0, NULL, 0, NULL } +}; + +/* Short description of program. */ +static const char doc[] = N_("\ +List dynamic shared objects loaded into process."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("PID"); + +/* 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, NULL, more_help, NULL +}; + +// File descriptor of /proc/*/mem file. +static int memfd; + +/* Name of the executable */ +static char *exe; + +/* Local functions. */ +static int get_process_info (int dfd, long int pid); +static void wait_for_ptrace_stop (long int pid); + + +int +main (int argc, char *argv[]) +{ + /* Parse and process arguments. */ + int remaining; + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + if (remaining != argc - 1) + { + fprintf (stderr, + gettext ("Exactly one parameter with process ID required.\n")); + argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name); + return 1; + } + + assert (sizeof (pid_t) == sizeof (int) + || sizeof (pid_t) == sizeof (long int)); + char *endp; + errno = 0; + long int pid = strtol (argv[remaining], &endp, 10); + if (pid < 0 || (pid == ULONG_MAX && errno == ERANGE) || *endp != '\0' + || (sizeof (pid_t) < sizeof (pid) && pid > INT_MAX)) + error (EXIT_FAILURE, 0, gettext ("invalid process ID '%s'"), + argv[remaining]); + + /* Determine the program name. */ + char buf[7 + 3 * sizeof (pid)]; + snprintf (buf, sizeof (buf), "/proc/%lu", pid); + int dfd = open (buf, O_RDONLY | O_DIRECTORY); + if (dfd == -1) + error (EXIT_FAILURE, errno, gettext ("cannot open %s"), buf); + + struct scratch_buffer exebuf; + scratch_buffer_init (&exebuf); + ssize_t nexe; + while ((nexe = readlinkat (dfd, "exe", + exebuf.data, exebuf.length)) == exebuf.length) + { + if (!scratch_buffer_grow (&exebuf)) + { + nexe = -1; + break; + } + } + if (nexe == -1) + exe = (char *) "<program name undetermined>"; + else + { + exe = exebuf.data; + exe[nexe] = '\0'; + } + + /* Stop all threads since otherwise the list of loaded modules might + change while we are reading it. */ + struct thread_list + { + pid_t tid; + struct thread_list *next; + } *thread_list = NULL; + + int taskfd = openat (dfd, "task", O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (taskfd == 1) + error (EXIT_FAILURE, errno, gettext ("cannot open %s/task"), buf); + DIR *dir = fdopendir (taskfd); + if (dir == NULL) + error (EXIT_FAILURE, errno, gettext ("cannot prepare reading %s/task"), + buf); + + struct dirent64 *d; + while ((d = readdir64 (dir)) != NULL) + { + if (! isdigit (d->d_name[0])) + continue; + + errno = 0; + long int tid = strtol (d->d_name, &endp, 10); + if (tid < 0 || (tid == ULONG_MAX && errno == ERANGE) || *endp != '\0' + || (sizeof (pid_t) < sizeof (pid) && tid > INT_MAX)) + error (EXIT_FAILURE, 0, gettext ("invalid thread ID '%s'"), + d->d_name); + + if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0) + { + /* There might be a race between reading the directory and + threads terminating. Ignore errors attaching to unknown + threads unless this is the main thread. */ + if (errno == ESRCH && tid != pid) + continue; + + error (EXIT_FAILURE, errno, gettext ("cannot attach to process %lu"), + tid); + } + + wait_for_ptrace_stop (tid); + + struct thread_list *newp = alloca (sizeof (*newp)); + newp->tid = tid; + newp->next = thread_list; + thread_list = newp; + } + + closedir (dir); + + int status = get_process_info (dfd, pid); + + assert (thread_list != NULL); + do + { + ptrace (PTRACE_DETACH, thread_list->tid, NULL, NULL); + thread_list = thread_list->next; + } + while (thread_list != NULL); + + close (dfd); + + return status; +} + + +/* Wait for PID to enter ptrace-stop state after being attached. */ +static void +wait_for_ptrace_stop (long int pid) +{ + int status; + + /* While waiting for SIGSTOP being delivered to the tracee we have to + reinject any other pending signal. Ignore all other errors. */ + while (waitpid (pid, &status, __WALL) == pid && WIFSTOPPED (status)) + { + /* The STOP signal should not be delivered to the tracee. */ + if (WSTOPSIG (status) == SIGSTOP) + return; + if (ptrace (PTRACE_CONT, pid, NULL, + (void *) (uintptr_t) WSTOPSIG (status))) + /* The only possible error is that the process died. */ + return; + } +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* Print bug-reporting information in the help message. */ +static char * +more_help (int key, const char *text, void *input) +{ + char *tp = NULL; + switch (key) + { + case ARGP_KEY_HELP_EXTRA: + /* We print some extra information. */ + if (asprintf (&tp, gettext ("\ +For bug reporting instructions, please see:\n\ +%s.\n"), REPORT_BUGS_TO) < 0) + return NULL; + return tp; + default: + break; + } + return (char *) text; +} + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state) +{ + fprintf (stream, "pldd %s%s\n", PKGVERSION, 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\ +"), "2017"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +#define CLASS 32 +#include "pldd-xx.c" +#define CLASS 64 +#include "pldd-xx.c" + + +static int +get_process_info (int dfd, long int pid) +{ + memfd = openat (dfd, "mem", O_RDONLY); + if (memfd == -1) + goto no_info; + + int fd = openat (dfd, "exe", O_RDONLY); + if (fd == -1) + { + no_info: + error (0, errno, gettext ("cannot get information about process %lu"), + pid); + return EXIT_FAILURE; + } + + char e_ident[EI_NIDENT]; + if (read (fd, e_ident, EI_NIDENT) != EI_NIDENT) + goto no_info; + + close (fd); + + if (memcmp (e_ident, ELFMAG, SELFMAG) != 0) + { + error (0, 0, gettext ("process %lu is no ELF program"), pid); + return EXIT_FAILURE; + } + + fd = openat (dfd, "auxv", O_RDONLY); + if (fd == -1) + goto no_info; + + size_t auxv_size = 0; + void *auxv = NULL; + while (1) + { + auxv_size += 512; + auxv = xrealloc (auxv, auxv_size); + + ssize_t n = pread (fd, auxv, auxv_size, 0); + if (n < 0) + goto no_info; + if (n < auxv_size) + { + auxv_size = n; + break; + } + } + + close (fd); + + int retval; + if (e_ident[EI_CLASS] == ELFCLASS32) + retval = find_maps32 (pid, auxv, auxv_size); + else + retval = find_maps64 (pid, auxv, auxv_size); + + free (auxv); + close (memfd); + + return retval; +} diff --git a/REORG.TODO/elf/preloadtest.c b/REORG.TODO/elf/preloadtest.c new file mode 100644 index 0000000000..7ea10b9b5b --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/readelflib.c b/REORG.TODO/elf/readelflib.c new file mode 100644 index 0000000000..9ad56dcc34 --- /dev/null +++ b/REORG.TODO/elf/readelflib.c @@ -0,0 +1,234 @@ +/* Copyright (C) 1999-2017 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, see + <http://www.gnu.org/licenses/>. */ + +/* 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); + ElfW(Addr) size = segment->p_filesz; + + while (abi_note [0] != 4 || abi_note [1] != 16 + || abi_note [2] != 1 + || memcmp (abi_note + 3, "GNU", 4) != 0) + { +#define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word))) + ElfW(Addr) note_size = 3 * sizeof (ElfW(Word)) + + ROUND (abi_note[0]) + + ROUND (abi_note[1]); + + if (size - 32 < note_size || note_size == 0) + { + size = 0; + break; + } + size -= note_size; + abi_note = (void *) abi_note + note_size; + } + + if (size == 0) + break; + + *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; + } + } + + return 0; +} diff --git a/REORG.TODO/elf/readlib.c b/REORG.TODO/elf/readlib.c new file mode 100644 index 0000000000..d278a189b2 --- /dev/null +++ b/REORG.TODO/elf/readlib.c @@ -0,0 +1,212 @@ +/* Copyright (C) 1999-2017 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 as published + by the Free Software Foundation; version 2 of the License, or + (at your option) any later version. + + 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, see <http://www.gnu.org/licenses/>. */ + +/* 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 +}; + + +/* Check if string corresponds to a GDB Python file. */ +static bool +is_gdb_python_file (const char *name) +{ + size_t len = strlen (name); + return len > 7 && strcmp (name + len - 7, "-gdb.py") == 0; +} + +/* 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, struct stat64 *stat_buf) +{ + 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 + && !is_gdb_python_file (file_name)) + 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); + + *stat_buf = statbuf; + return ret; +} + +/* Returns made up soname if lib doesn't have explicit DT_SONAME. */ + +char * +implicit_soname (const char *lib, int flag) +{ + char *soname = xstrdup (lib); + + if ((flag & FLAG_TYPE_MASK) != FLAG_LIBC4) + return soname; + + /* Aout files don't have a soname, just return the name + including the major number. */ + char *major = strstr (soname, ".so."); + if (major) + { + char *dot = strstr (major + 4, "."); + if (dot) + *dot = '\0'; + } + return soname; +} + +/* Get architecture specific version of process_elf_file. */ +#include <readelflib.c> diff --git a/REORG.TODO/elf/reldep.c b/REORG.TODO/elf/reldep.c new file mode 100644 index 0000000000..adabc0d5d5 --- /dev/null +++ b/REORG.TODO/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 + an 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/REORG.TODO/elf/reldep2.c b/REORG.TODO/elf/reldep2.c new file mode 100644 index 0000000000..ba5ab222f9 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep3.c b/REORG.TODO/elf/reldep3.c new file mode 100644 index 0000000000..05013d3509 --- /dev/null +++ b/REORG.TODO/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 + 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 != 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/REORG.TODO/elf/reldep4.c b/REORG.TODO/elf/reldep4.c new file mode 100644 index 0000000000..ba12e7d9c4 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep4mod1.c b/REORG.TODO/elf/reldep4mod1.c new file mode 100644 index 0000000000..934a68096e --- /dev/null +++ b/REORG.TODO/elf/reldep4mod1.c @@ -0,0 +1,7 @@ +int foo (void); + +int foo (void) +{ + return 0; +} + diff --git a/REORG.TODO/elf/reldep4mod2.c b/REORG.TODO/elf/reldep4mod2.c new file mode 100644 index 0000000000..26ce6bf13a --- /dev/null +++ b/REORG.TODO/elf/reldep4mod2.c @@ -0,0 +1,8 @@ +extern int foo (void); +extern int bar (void); + +int +bar (void) +{ + return foo (); +} diff --git a/REORG.TODO/elf/reldep4mod3.c b/REORG.TODO/elf/reldep4mod3.c new file mode 100644 index 0000000000..934a68096e --- /dev/null +++ b/REORG.TODO/elf/reldep4mod3.c @@ -0,0 +1,7 @@ +int foo (void); + +int foo (void) +{ + return 0; +} + diff --git a/REORG.TODO/elf/reldep4mod4.c b/REORG.TODO/elf/reldep4mod4.c new file mode 100644 index 0000000000..26ce6bf13a --- /dev/null +++ b/REORG.TODO/elf/reldep4mod4.c @@ -0,0 +1,8 @@ +extern int foo (void); +extern int bar (void); + +int +bar (void) +{ + return foo (); +} diff --git a/REORG.TODO/elf/reldep5.c b/REORG.TODO/elf/reldep5.c new file mode 100644 index 0000000000..881d519ff6 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep6.c b/REORG.TODO/elf/reldep6.c new file mode 100644 index 0000000000..1eeec6c862 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep6mod0.c b/REORG.TODO/elf/reldep6mod0.c new file mode 100644 index 0000000000..58f3745fb4 --- /dev/null +++ b/REORG.TODO/elf/reldep6mod0.c @@ -0,0 +1,8 @@ +int bar (void); +extern void free (void *); + +int bar (void) +{ + free (0); + return 40; +} diff --git a/REORG.TODO/elf/reldep6mod1.c b/REORG.TODO/elf/reldep6mod1.c new file mode 100644 index 0000000000..037a73a198 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep6mod2.c b/REORG.TODO/elf/reldep6mod2.c new file mode 100644 index 0000000000..c2ef3f9bc0 --- /dev/null +++ b/REORG.TODO/elf/reldep6mod2.c @@ -0,0 +1,3 @@ +extern int foo (void); + +void *foop = (void *) foo; diff --git a/REORG.TODO/elf/reldep6mod3.c b/REORG.TODO/elf/reldep6mod3.c new file mode 100644 index 0000000000..881828ef6e --- /dev/null +++ b/REORG.TODO/elf/reldep6mod3.c @@ -0,0 +1,3 @@ +extern void *foop; + +void **foopp = &foop; diff --git a/REORG.TODO/elf/reldep6mod4.c b/REORG.TODO/elf/reldep6mod4.c new file mode 100644 index 0000000000..8fa89de64b --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep7.c b/REORG.TODO/elf/reldep7.c new file mode 100644 index 0000000000..5275a0e8f1 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep7mod1.c b/REORG.TODO/elf/reldep7mod1.c new file mode 100644 index 0000000000..de1bb3a6cd --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep7mod2.c b/REORG.TODO/elf/reldep7mod2.c new file mode 100644 index 0000000000..3fa3368792 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep8.c b/REORG.TODO/elf/reldep8.c new file mode 100644 index 0000000000..90009a5609 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep8mod1.c b/REORG.TODO/elf/reldep8mod1.c new file mode 100644 index 0000000000..acddc4cf8b --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep8mod2.c b/REORG.TODO/elf/reldep8mod2.c new file mode 100644 index 0000000000..d0020240a8 --- /dev/null +++ b/REORG.TODO/elf/reldep8mod2.c @@ -0,0 +1,7 @@ +void +__attribute__((constructor)) +xxx (void) +{ + extern void baz (void); + baz (); +} diff --git a/REORG.TODO/elf/reldep8mod3.c b/REORG.TODO/elf/reldep8mod3.c new file mode 100644 index 0000000000..6d1a0d47b7 --- /dev/null +++ b/REORG.TODO/elf/reldep8mod3.c @@ -0,0 +1 @@ +int x; diff --git a/REORG.TODO/elf/reldep9.c b/REORG.TODO/elf/reldep9.c new file mode 100644 index 0000000000..51c7a8bb9e --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep9mod1.c b/REORG.TODO/elf/reldep9mod1.c new file mode 100644 index 0000000000..249a2bae1c --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldep9mod2.c b/REORG.TODO/elf/reldep9mod2.c new file mode 100644 index 0000000000..090966e3e3 --- /dev/null +++ b/REORG.TODO/elf/reldep9mod2.c @@ -0,0 +1,3 @@ +void baz (void) +{ +} diff --git a/REORG.TODO/elf/reldep9mod3.c b/REORG.TODO/elf/reldep9mod3.c new file mode 100644 index 0000000000..6d1a0d47b7 --- /dev/null +++ b/REORG.TODO/elf/reldep9mod3.c @@ -0,0 +1 @@ +int x; diff --git a/REORG.TODO/elf/reldepmod1.c b/REORG.TODO/elf/reldepmod1.c new file mode 100644 index 0000000000..b8ef6401e1 --- /dev/null +++ b/REORG.TODO/elf/reldepmod1.c @@ -0,0 +1,10 @@ +extern int foo (void); + +int some_var; + + +int +foo (void) +{ + return some_var; +} diff --git a/REORG.TODO/elf/reldepmod2.c b/REORG.TODO/elf/reldepmod2.c new file mode 100644 index 0000000000..b7edebae80 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldepmod3.c b/REORG.TODO/elf/reldepmod3.c new file mode 100644 index 0000000000..66a996cd90 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldepmod4.c b/REORG.TODO/elf/reldepmod4.c new file mode 100644 index 0000000000..dcb503bba7 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/reldepmod5.c b/REORG.TODO/elf/reldepmod5.c new file mode 100644 index 0000000000..62df697162 --- /dev/null +++ b/REORG.TODO/elf/reldepmod5.c @@ -0,0 +1,7 @@ +extern int foo (void); + +int +foo (void) +{ + return 42; +} diff --git a/REORG.TODO/elf/reldepmod6.c b/REORG.TODO/elf/reldepmod6.c new file mode 100644 index 0000000000..cd2aeb400d --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/resolvfail.c b/REORG.TODO/elf/resolvfail.c new file mode 100644 index 0000000000..ebd635d153 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/restest1.c b/REORG.TODO/elf/restest1.c new file mode 100644 index 0000000000..eb5aeca59e --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/restest2.c b/REORG.TODO/elf/restest2.c new file mode 100644 index 0000000000..f959f030a0 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/rtld-Rules b/REORG.TODO/elf/rtld-Rules new file mode 100644 index 0000000000..2c7b99828c --- /dev/null +++ b/REORG.TODO/elf/rtld-Rules @@ -0,0 +1,149 @@ +# Subroutine makefile for compiling libc modules linked into dynamic linker. + +# Copyright (C) 2002-2017 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# <http://www.gnu.org/licenses/>. + +# 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 $^ + 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. + +rtld-compile-command.S = $(compile-command.S) $(rtld-CPPFLAGS) +rtld-compile-command.s = $(compile-command.s) $(rtld-CPPFLAGS) +rtld-compile-command.c = $(compile-command.c) $(rtld-CPPFLAGS) $(rtld-CFLAGS) + +# 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: rtld-%.S $(before-compile) + $(rtld-compile-command.S) +$(objpfx)rtld-%.os: rtld-%.s $(before-compile) + $(rtld-compile-command.s) +$(objpfx)rtld-%.os: rtld-%.c $(before-compile) + $(rtld-compile-command.c) +$(objpfx)rtld-%.os: %.S $(before-compile) + $(rtld-compile-command.S) +$(objpfx)rtld-%.os: %.s $(before-compile) + $(rtld-compile-command.s) +$(objpfx)rtld-%.os: %.c $(before-compile) + $(rtld-compile-command.c) + +# The rules for generated source files. +$(objpfx)rtld-%.os: $(objpfx)rtld-%.S $(before-compile) + $(rtld-compile-command.S) +$(objpfx)rtld-%.os: $(objpfx)rtld-%.s $(before-compile) + $(rtld-compile-command.s) +$(objpfx)rtld-%.os: $(objpfx)rtld-%.c $(before-compile) + $(rtld-compile-command.c) +$(objpfx)rtld-%.os: $(objpfx)%.S $(before-compile) + $(rtld-compile-command.S) +$(objpfx)rtld-%.os: $(objpfx)%.s $(before-compile) + $(rtld-compile-command.s) +$(objpfx)rtld-%.os: $(objpfx)%.c $(before-compile) + $(rtld-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 + +# This here is the whole point of all the shenanigans. +# Set libof-* for each routine. +cpp-srcs-left := $(rtld-modules:%.os=%) +lib := rtld +include $(patsubst %,$(..)libof-iterator.mk,$(cpp-srcs-left)) + +rtld-CFLAGS += $(no-stack-protector) + +endif diff --git a/REORG.TODO/elf/rtld-debugger-interface.txt b/REORG.TODO/elf/rtld-debugger-interface.txt new file mode 100644 index 0000000000..61bc99e4b0 --- /dev/null +++ b/REORG.TODO/elf/rtld-debugger-interface.txt @@ -0,0 +1,122 @@ +Standard debugger interface +=========================== + +The run-time linker exposes a rendezvous structure to allow debuggers +to interface with it. This structure, r_debug, is defined in link.h. +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. + +The r_debug structure contains (amongst others) the following fields: + + struct link_map *r_map: + A linked list of loaded objects. + + enum { RT_CONSISTENT, RT_ADD, RT_DELETE } r_state: + The current state of the r_map list. RT_CONSISTENT means that r_map + is not currently being modified and may safely be inspected. RT_ADD + means that an object is being added to r_map, and that the list is + not guaranteed to be consistent. Likewise RT_DELETE means that an + object is being removed from the list. + + ElfW(Addr) r_brk: + The address of a function internal to the run-time linker which is + called whenever r_state is changed. The debugger should set a + breakpoint at this address if it wants to notice mapping changes. + +This protocol is widely supported, but somewhat limited in that it +has no provision to provide access to multiple namespaces, and that +the notifications (via r_brk) only refer to changes to r_map--the +debugger is notified that a new object has been added, for instance, +but there is no way for the debugger to discover whether any of the +objects in the link-map have been relocated or not. + + +Probe-based debugger interface +============================== + +Systemtap is a dynamic tracing/instrumenting tool available on Linux. +Probes that are not fired at run time have close to zero overhead. +glibc contains a number of probes that debuggers can set breakpoints +on in order to notice certain events. + +All rtld probes have the following arguments: + + arg1: Lmid_t lmid: + The link-map ID of the link-map list that the object was loaded + into. This will be LM_ID_BASE for the application's main link-map + list, or some other value for different namespaces. + + arg2: struct r_debug *r_debug: + A pointer to the r_debug structure containing the link-map list + that the object was loaded into. This will be the value stored in + DT_DEBUG for the application's main link-map list, or some other + value for different namespaces. + +map_complete and reloc_complete may have the following additional +argument: + + arg3: struct link_map *new: + A pointer which, if not NULL, points to the entry in the specified + r_debug structure's link-map list corresponding to the first new + object to have been mapped or relocated, with new->l_next pointing + to the link-map of the next new object to have been mapped or + relocated, and so on. Note that because `new' is an entry in a + larger list, new->l_prev (if not NULL) will point to what was the + last link-map in the link-map list prior to the new objects being + mapped or relocated. + +The following probes are available: + + init_start: + This is called once, when the linker is about to fill in the main + r_debug structure at application startup. init_start always has + lmid set to LM_ID_BASE and r_debug set to the value stored in + DT_DEBUG. r_debug is not guaranteed to be consistent until + init_complete is fired. + + init_complete: + This is called once, when the linker has filled in the main + r_debug structure at application startup. init_complete always + has lmid set to LM_ID_BASE and r_debug set to the value stored + in DT_DEBUG. The r_debug structure is consistent and may be + inspected, and all objects in the link-map are guaranteed to + have been relocated. + + map_start: + The linker is about to map new objects into the specified + namespace. The namespace's r_debug structure is not guaranteed + to be consistent until a corresponding map_complete is fired. + + map_complete: + The linker has finished mapping new objects into the specified + namespace. The namespace's r_debug structure is consistent and + may be inspected, although objects in the namespace's link-map + are not guaranteed to have been relocated. + + map_failed: + The linker failed while attempting to map new objects into + the specified namespace. The namespace's r_debug structure + is consistent and may be inspected. + + reloc_start: + The linker is about to relocate all unrelocated objects in the + specified namespace. The namespace's r_debug structure is not + guaranteed to be consistent until a corresponding reloc_complete + is fired. + + reloc_complete: + The linker has relocated all objects in the specified namespace. + The namespace's r_debug structure is consistent and may be + inspected, and all objects in the namespace's link-map are + guaranteed to have been relocated. + + unmap_start: + The linker is about to remove objects from the specified + namespace. The namespace's r_debug structure is not guaranteed to + be consistent until a corresponding unmap_complete is fired. + + unmap_complete: + The linker has finished removing objects into the specified + namespace. The namespace's r_debug structure is consistent and + may be inspected. diff --git a/REORG.TODO/elf/rtld.c b/REORG.TODO/elf/rtld.c new file mode 100644 index 0000000000..3746653afb --- /dev/null +++ b/REORG.TODO/elf/rtld.c @@ -0,0 +1,2652 @@ +/* Run time dynamic linker. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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> +#include <sys/param.h> +#include <sys/stat.h> +#include <ldsodefs.h> +#include <_itoa.h> +#include <entry.h> +#include <fpu_control.h> +#include <hp-timing.h> +#include <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 <stap-probe.h> +#include <stackinfo.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 +rtld_hidden_data_def (_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; +rtld_hidden_def (_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 = + { + /* Generally the default presumption without further information is an + * executable stack but this is not true for all platforms. */ + ._dl_stack_flags = DEFAULT_STACK_PERMS, +#ifdef _LIBC_REENTRANT + ._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER, + ._dl_load_write_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER, +#endif + ._dl_nns = 1, + ._dl_ns = + { +#ifdef _LIBC_REENTRANT + [LM_ID_BASE] = { ._ns_unique_sym_table + = { .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, +#if !HAVE_TUNABLES + ._dl_hwcap_mask = HWCAP_IMPORTANT, +#endif + ._dl_lazy = 1, + ._dl_fpu_control = _FPU_DEFAULT, + ._dl_pagesize = EXEC_PAGESIZE, + ._dl_inhibit_cache = 0, + + /* Function pointers. */ + ._dl_debug_printf = _dl_debug_printf, + ._dl_mcount = _dl_mcount, + ._dl_lookup_symbol_x = _dl_lookup_symbol_x, + ._dl_check_caller = _dl_check_caller, + ._dl_open = _dl_open, + ._dl_close = _dl_close, + ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft, +#ifdef HAVE_DL_DISCOVER_OSVERSION + ._dl_discover_osversion = _dl_discover_osversion +#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_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, ElfW(auxv_t) *auxv); + +/* These two variables cannot be moved into .data.rel.ro. */ +static struct libname_list _dl_rtld_libname; +static struct libname_list _dl_rtld_libname2; + +/* 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. */ +#ifdef PI_STATIC_AND_HIDDEN +# 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 + +/* 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_SMALL_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 + } + + /* 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. */ +#ifndef DONT_USE_BOOTSTRAP_MAP +# if NO_TLS_OFFSET != 0 + GL(dl_rtld_map).l_tls_offset = NO_TLS_OFFSET; +# endif +#endif + + HP_TIMING_NOW (GL(dl_cpuclock_offset)); + + /* 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_SMALL_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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS)) + { +#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. + Since ld.so must not have any undefined symbols the result + is trivial: always the map of ld.so itself. */ +#define RTLD_BOOTSTRAP +#define RESOLVE_MAP(sym, version, flags) (&bootstrap_map) +#include "dynamic-link.h" + + if (HP_TIMING_INLINE && HP_SMALL_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 NO_TLS_OFFSET != 0 + bootstrap_map.l_tls_offset = NO_TLS_OFFSET; +#endif + +#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, 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 reloc_mode; +}; + +struct map_args +{ + /* Argument to map_doit. */ + const char *str; + struct link_map *loader; + 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->reloc_mode, 0); +} + +static void +map_doit (void *a) +{ + struct map_args *args = (struct map_args *) a; + int type = (args->mode == __RTLD_OPENEXEC) ? lt_executable : lt_library; + args->map = _dl_map_object (args->loader, args->str, type, 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 + | __RTLD_SECURE), + dl_main, LM_ID_NEWLM, _dl_argc, _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; +} + +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. */ + const char *lossage = TLS_INIT_TP (tcbp); + if (__glibc_unlikely (lossage != NULL)) + _dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage); + tls_init_tp_called = true; + + return tcbp; +} + +static unsigned int +do_preload (const 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.mode = __RTLD_SECURE; + + unsigned int old_nloaded = GL(dl_ns)[LM_ID_BASE]._ns_nloaded; + + (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit, &args); + if (__glibc_unlikely (err_str != NULL)) + { + _dl_error_printf ("\ +ERROR: ld.so: object '%s' from %s cannot be preloaded (%s): ignored.\n", + fname, where, err_str); + /* 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 + + +static void +security_init (void) +{ + /* Set up the stack checker's canary. */ + uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random); +#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. */ + uintptr_t pointer_chk_guard + = _dl_setup_pointer_guard (_dl_random, stack_chk_guard); +#ifdef THREAD_SET_POINTER_GUARD + THREAD_SET_POINTER_GUARD (pointer_chk_guard); +#endif + __pointer_chk_guard_local = pointer_chk_guard; + + /* We do not need the _dl_random value anymore. The less + information we leave behind, the better, so clear the + variable. */ + _dl_random = NULL; +} + +#include "setup-vdso.h" + +/* 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, + ElfW(auxv_t) *auxv) +{ + 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 + void *tcbp = NULL; + + GL(dl_init_static_tls) = &_dl_nothread_init_static_tls; + +#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. */ + _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 (_dl_argv[1], "--list")) + { + mode = list; + GLRO(dl_lazy) = -1; /* This means do no dependency analysis. */ + + ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } + else if (! strcmp (_dl_argv[1], "--verify")) + { + mode = verify; + + ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } + else if (! strcmp (_dl_argv[1], "--inhibit-cache")) + { + GLRO(dl_inhibit_cache) = 1; + ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } + else if (! strcmp (_dl_argv[1], "--library-path") + && _dl_argc > 2) + { + library_path = _dl_argv[2]; + + _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } + else if (! strcmp (_dl_argv[1], "--inhibit-rpath") + && _dl_argc > 2) + { + GLRO(dl_inhibit_rpath) = _dl_argv[2]; + + _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } + else if (! strcmp (_dl_argv[1], "--audit") && _dl_argc > 2) + { + process_dl_audit (_dl_argv[2]); + + _dl_skip_args += 2; + _dl_argc -= 2; + _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\ + --inhibit-cache Do not use " LD_SO_CACHE "\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\ + --audit LIST use objects named in LIST as auditors\n"); + + ++_dl_skip_args; + --_dl_argc; + ++_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.mode = __RTLD_OPENEXEC; + (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit, + &args); + if (__glibc_unlikely (err_str != NULL)) + /* 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, lt_executable, 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; + + if (__builtin_expect (mode, normal) == normal + && GL(dl_rtld_map).l_info[DT_SONAME] != NULL + && main_map->l_info[DT_SONAME] != NULL + && strcmp ((const char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB]) + + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_val, + (const char *) D_PTR (main_map, l_info[DT_STRTAB]) + + main_map->l_info[DT_SONAME]->d_un.d_val) == 0) + _dl_fatal_printf ("loader cannot load itself\n"); + + 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; + +#ifdef HAVE_AUX_VECTOR + /* Adjust the on-stack auxiliary vector so that it looks like the + binary was executed directly. */ + for (ElfW(auxv_t) *av = auxv; av->a_type != AT_NULL; av++) + switch (av->a_type) + { + case AT_PHDR: + av->a_un.a_val = (uintptr_t) phdr; + break; + case AT_PHNUM: + av->a_un.a_val = phnum; + break; + case AT_ENTRY: + av->a_un.a_val = *user_entry; + break; + case AT_EXECFN: + av->a_un.a_val = (uintptr_t) _dl_argv[0]; + break; + } +#endif + } + 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); + main_map->l_phdr = phdr; + main_map->l_phnum = phnum; + main_map->l_entry = *user_entry; + + /* Even though the link map is not yet fully initialized we can add + it to the map list since there are no possible users running yet. */ + _dl_add_to_namespace_list (main_map, LM_ID_BASE); + assert (main_map == GL(dl_ns)[LM_ID_BASE]._ns_loaded); + + /* 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 & ~(GLRO(dl_pagesize) - 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: + 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; + } + 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; + } + + /* 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; + 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; + /* Set up the data structures for the system-supplied DSO early, + so they can influence _dl_init_paths. */ + setup_vdso (main_map, &first_preload); + +#ifdef DL_SYSDEP_OSCHECK + DL_SYSDEP_OSCHECK (_dl_fatal_printf); +#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. */ + const ElfW(Ehdr) *rtld_ehdr; + + /* Starting from binutils-2.23, the linker will define the magic symbol + __ehdr_start to point to our own ELF header if it is visible in a + segment that also includes the phdrs. If that's not available, we use + the old method that assumes the beginning of the file is part of the + lowest-addressed PT_LOAD segment. */ +#ifdef HAVE_EHDR_START + extern const ElfW(Ehdr) __ehdr_start __attribute__ ((visibility ("hidden"))); + rtld_ehdr = &__ehdr_start; +#else + rtld_ehdr = (void *) GL(dl_rtld_map).l_map_start; +#endif + assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr); + assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr))); + + const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + 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; + } + + /* 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 (); + + /* If we have auditing DSOs to load, do it now. */ + if (__glibc_unlikely (audit_list != NULL)) + { + /* Iterate over all entries in the list. The order is important. */ + struct audit_ifaces *last_audit = NULL; + struct audit_list *al = audit_list->next; + + /* Since we start using the auditing DSOs right away we need to + initialize the data structures now. */ + tcbp = init_tls (); + + /* Initialize security features. We need to do it this early + since otherwise the constructors of the audit libraries will + use different values (especially the pointer guard) and will + fail later on. */ + security_init (); + + do + { + 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. */ + 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 (__glibc_unlikely (err_str != NULL)) + { + 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); + + GL(dl_tls_max_dtv_idx) = tls_idx; + 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 (__glibc_unlikely (GLRO(dl_naudit) > 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; + } + } + } + } + + /* Keep track of the currently loaded modules to count how many + non-audit modules which use TLS are loaded. */ + size_t count_modids = _dl_count_modids (); + + /* 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 (); + LIBC_PROBE (init_start, 2, LM_ID_BASE, r); + + /* Auditing checkpoint: we are ready to signal that the initial map + is being constructed. */ + if (__glibc_unlikely (GLRO(dl_naudit) > 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 (__glibc_unlikely (preloadlist != NULL)) + { + /* 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 (! __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 (__glibc_unlikely (__access (preload_file, R_OK) == 0)) + { + /* Read the contents of the file. */ + file = _dl_sysdep_read_whole_file (preload_file, &file_size, + PROT_READ | PROT_WRITE); + if (__glibc_unlikely (file != MAP_FAILED)) + { + /* 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 (__glibc_unlikely (*first_preload != NULL)) + { + /* 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; + + /* 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 (__glibc_likely (i < main_map->l_searchlist.r_nlist)) + { + /* 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); +#ifdef 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); + } + + /* 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 (); + + if (__glibc_likely (audit_list == NULL)) + /* Initialize security features. But only if we have not done it + earlier. */ + security_init (); + + 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)", + DSO_FILENAME (l->l_libname->name), + DSO_FILENAME (l->l_name), + (int) sizeof l->l_map_start * 2, + (size_t) l->l_map_start, + (int) sizeof l->l_addr * 2, + (size_t) l->l_addr); + + 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 + _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, + .reloc_mode = ((GLRO(dl_lazy) + ? RTLD_LAZY : 0) + | __RTLD_NOIFUNC) }; + _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; +#ifdef NEED_DL_SYSINFO_DSO + /* Skip the VDSO since it's not part of the list + of objects we brought in via DT_NEEDED entries. */ + if (l == GLRO(dl_sysinfo_map)) + l = l->l_next; +#endif + 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 (_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", + _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; + unsigned int i; + + args.reloc_mode = ((GLRO(dl_lazy) ? RTLD_LAZY : 0) + | __RTLD_NOIFUNC); + + i = main_map->l_searchlist.r_nlist; + while (i-- > 0) + { + struct link_map *l = main_map->l_initfini[i]; + if (l != &GL(dl_rtld_map) && ! l->l_faked) + { + args.l = l; + _dl_receive_error (print_unresolved, relocate_doit, + &args); + } + } + + 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, __RTLD_NOIFUNC, 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", DSO_FILENAME (map->l_name)); + + 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) + && ! __builtin_expect (GLRO(dl_dynamic_weak), 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 (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) + _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; + + /* 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; + + /* Remember the last search directory added at startup, now that + malloc will no longer be the one from dl-minimal.c. */ + GLRO(dl_init_all_dirs) = GL(dl_all_dirs); + + /* Print scope information. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES)) + { + _dl_debug_printf ("\nInitial object scopes\n"); + + for (struct link_map *l = main_map; l != NULL; l = l->l_next) + _dl_show_scope (l, 0); + } + + 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); + + /* Add object to slot information data if necessasy. */ + if (l->l_tls_blocksize != 0 && tls_init_tp_called) + _dl_add_to_slotinfo (l); + } + } + 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; + + HP_TIMING_NOW (start); + unsigned i = main_map->l_searchlist.r_nlist; + while (i-- > 0) + { + struct link_map *l = main_map->l_initfini[i]; + + /* 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; + } + /* Also allocated with the fake malloc(). */ + l->l_free_initfini = 0; + + if (l != &GL(dl_rtld_map)) + _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0, + consider_profiling); + + /* Add object to slot information data if necessasy. */ + if (l->l_tls_blocksize != 0 && tls_init_tp_called) + _dl_add_to_slotinfo (l); + } + HP_TIMING_NOW (stop); + + HP_TIMING_DIFF (relocate_time, start, stop); + + /* 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 (__glibc_unlikely (GL(dl_profile_map) != NULL)) + /* We must prepare the profiling. */ + _dl_start_profile (); + } + + if ((!was_tls_init_tp_called && GL(dl_tls_max_dtv_idx) > 0) + || count_modids != _dl_count_modids ()) + ++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 (! tls_init_tp_called) + { + const char *lossage = TLS_INIT_TP (tcbp); + if (__glibc_unlikely (lossage != NULL)) + _dl_fatal_printf ("cannot set up thread-local storage: %s\n", + lossage); + } + + /* Make sure no new search directories have been added. */ + assert (GLRO(dl_init_all_dirs) == GL(dl_all_dirs)); + + 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); + } + + /* 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 (); + +#ifdef SHARED + /* Auditing checkpoint: we have added all objects. */ + if (__glibc_unlikely (GLRO(dl_naudit) > 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 (); + LIBC_PROBE (init_complete, 2, LM_ID_BASE, r); + +#if defined USE_LDCONFIG && !defined 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; + _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, + 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 ("scopes"), "display scope information", + DL_DEBUG_SCOPES }, + { 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 + | DL_DEBUG_SCOPES }, + { 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_UNUSED) + { + /* In order to get an accurate picture of whether a particular + DT_NEEDED entry is actually used we have to process both + the PLT and non-PLT relocation entries. */ + GLRO(dl_lazy) = 0; + } + + 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 (! __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"[__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 (!__libc_enable_secure + && memcmp (envline, "SHOW_AUXV", 9) == 0) + _dl_show_auxv (); + break; + +#if !HAVE_TUNABLES + case 10: + /* Mask for the important hardware capabilities. */ + if (!__libc_enable_secure + && memcmp (envline, "HWCAP_MASK", 10) == 0) + GLRO(dl_hwcap_mask) = __strtoul_internal (&envline[11], NULL, + 0, 0); + break; +#endif + + case 11: + /* Path where the binary is found. */ + if (!__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 (!__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 (!__libc_enable_secure + && memcmp (envline, "USE_LOAD_BIAS", 13) == 0) + { + GLRO(dl_use_load_bias) = envline[14] == '1' ? -1 : 0; + break; + } + break; + + case 14: + /* Where to place the profiling data file. */ + if (!__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 (__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) + { +#if !HAVE_TUNABLES + unsetenv ("MALLOC_CHECK_"); +#endif + 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) + { + const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW; + 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_SMALL_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 < GL(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_SMALL_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/REORG.TODO/elf/setup-vdso.h b/REORG.TODO/elf/setup-vdso.h new file mode 100644 index 0000000000..2db597eba3 --- /dev/null +++ b/REORG.TODO/elf/setup-vdso.h @@ -0,0 +1,121 @@ +/* Set up the data structures for the system-supplied DSO. + Copyright (C) 2012-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +static inline void __attribute__ ((always_inline)) +setup_vdso (struct link_map *main_map __attribute__ ((unused)), + struct link_map ***first_preload __attribute__ ((unused))) +{ +#ifdef NEED_DL_SYSINFO_DSO + if (GLRO(dl_sysinfo_dso) == NULL) + return; + + /* 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 (__glibc_likely (l != NULL)) + { + 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; + + /* The vDSO is always used. */ + l->l_used = 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) + 1; + char *copy = malloc (len); + if (copy == NULL) + _dl_fatal_printf ("out of memory\n"); + l->l_libname->name = l->l_name = memcpy (copy, dsoname, len); + } + + /* Add the vDSO to the object list. */ + _dl_add_to_namespace_list (l, LM_ID_BASE); + +# if IS_IN (rtld) + /* 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; +# else + GL(dl_nns) = 1; +# endif + + /* 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 +} diff --git a/REORG.TODO/elf/sln.c b/REORG.TODO/elf/sln.c new file mode 100644 index 0000000000..af2d0de164 --- /dev/null +++ b/REORG.TODO/elf/sln.c @@ -0,0 +1,202 @@ +/* `sln' program to create symbolic links between files. + Copyright (C) 1998-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <error.h> +#include <errno.h> +#include <libintl.h> +#include <locale.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> + +#include "../version.h" + +#define PACKAGE _libc_intl_domainname + +static int makesymlink (const char *src, const char *dest); +static int makesymlinks (const char *file); +static void usage (void); + +int +main (int argc, char **argv) +{ + /* Set locale via LC_ALL. */ + setlocale (LC_ALL, ""); + + /* Set the text message domain. */ + textdomain (PACKAGE); + + switch (argc) + { + case 2: + if (strcmp (argv[1], "--version") == 0) { + printf ("sln %s%s\n", PKGVERSION, VERSION); + return 0; + } else if (strcmp (argv[1], "--help") == 0) { + usage (); + return 0; + } + return makesymlinks (argv [1]); + break; + + case 3: + return makesymlink (argv [1], argv [2]); + break; + + default: + usage (); + return 1; + break; + } +} + +static void +usage (void) +{ + printf (_("Usage: sln src dest|file\n\n")); + printf (_("For bug reporting instructions, please see:\n\ +%s.\n"), REPORT_BUGS_TO); +} + +static int +makesymlinks (const char *file) +{ + 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 (const char *src, const char *dest) +{ + struct stat64 stats; + const char *error; + + /* Destination must not be a directory. */ + if (lstat64 (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; + } + + if (symlink (src, dest) == 0) + { + /* 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/REORG.TODO/elf/sofini.c b/REORG.TODO/elf/sofini.c new file mode 100644 index 0000000000..13e74b7903 --- /dev/null +++ b/REORG.TODO/elf/sofini.c @@ -0,0 +1,19 @@ +/* Finalizer module for ELF shared C library. This provides terminating + null pointer words in the `.ctors' and `.dtors' sections. */ + +#ifndef NO_CTORS_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 }; +#endif + +/* 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/REORG.TODO/elf/soinit.c b/REORG.TODO/elf/soinit.c new file mode 100644 index 0000000000..fe9935732b --- /dev/null +++ b/REORG.TODO/elf/soinit.c @@ -0,0 +1,43 @@ +/* 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. */ + +#ifndef NO_CTORS_DTORS_SECTIONS +# include <stdlib.h> + +static void (*const __CTOR_LIST__[1]) (void) + __attribute__ ((used, section (".ctors"))) + = { (void (*) (void)) -1 }; +static void (*const __DTOR_LIST__[1]) (void) + __attribute__ ((used, section (".dtors"))) + = { (void (*) (void)) -1 }; + +static inline void +run_hooks (void (*const list[]) (void)) +{ + while (*++list) + (**list) (); +} + +/* 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; +#endif diff --git a/REORG.TODO/elf/sotruss-lib.c b/REORG.TODO/elf/sotruss-lib.c new file mode 100644 index 0000000000..da2fedd52a --- /dev/null +++ b/REORG.TODO/elf/sotruss-lib.c @@ -0,0 +1,386 @@ +/* Trace calls through PLTs and show caller, callee, and parameters. + Copyright (C) 2011-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@gmail.com>, 2011. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <error.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/uio.h> + +#include <ldsodefs.h> + + +extern const char *__progname; +extern const char *__progname_full; + + +/* List of objects to trace calls from. */ +static const char *fromlist; +/* List of objects to trace calls to. */ +static const char *tolist; + +/* If non-zero, also trace returns of the calls. */ +static int do_exit; +/* If non-zero print PID for each line. */ +static int print_pid; + +/* The output stream to use. */ +static FILE *out_file; + + +static int +match_pid (pid_t pid, const char *which) +{ + if (which == NULL || which[0] == '\0') + { + print_pid = 1; + return 1; + } + + char *endp; + unsigned long n = strtoul (which, &endp, 0); + return *endp == '\0' && n == pid; +} + + +static void +init (void) +{ + fromlist = getenv ("SOTRUSS_FROMLIST"); + if (fromlist != NULL && fromlist[0] == '\0') + fromlist = NULL; + tolist = getenv ("SOTRUSS_TOLIST"); + if (tolist != NULL && tolist[0] == '\0') + tolist = NULL; + do_exit = (getenv ("SOTRUSS_EXIT") ?: "")[0] != '\0'; + + /* Determine whether this process is supposed to be traced and if + yes, whether we should print into a file. */ + const char *which_process = getenv ("SOTRUSS_WHICH"); + pid_t pid = getpid (); + int out_fd = -1; + if (match_pid (pid, which_process)) + { + const char *out_filename = getenv ("SOTRUSS_OUTNAME"); + + if (out_filename != NULL && out_filename[0] != 0) + { + size_t out_filename_len = strlen (out_filename) + 13; + char fullname[out_filename_len]; + char *endp = stpcpy (fullname, out_filename); + if (which_process == NULL || which_process[0] == '\0') + snprintf (endp, 13, ".%ld", (long int) pid); + + out_fd = open (fullname, O_RDWR | O_CREAT | O_TRUNC, 0666); + if (out_fd != -1) + print_pid = 0; + } + } + + /* If we do not write into a file write to stderr. Duplicate the + descriptor so that we can keep printing in case the program + closes stderr. Try first to allocate a descriptor with a value + usually not used as to minimize interference with the + program. */ + if (out_fd == -1) + { + out_fd = fcntl (STDERR_FILENO, F_DUPFD, 1000); + if (out_fd == -1) + out_fd = dup (STDERR_FILENO); + } + + if (out_fd != -1) + { + /* Convert file descriptor into a stream. */ + out_file = fdopen (out_fd, "w"); + if (out_file != NULL) + setlinebuf (out_file); + } +} + + +/* Audit interface verification. We also initialize everything if + everything checks out OK. */ +unsigned int +la_version (unsigned int v) +{ + if (v != LAV_CURRENT) + error (1, 0, "cannot handle interface version %u", v); + + init (); + + return v; +} + + +/* Check whether a file name is on the colon-separated list of file + names. */ +static unsigned int +match_file (const char *list, const char *name, size_t name_len, + unsigned int mask) +{ + if (list[0] == '\0') + return 0; + + const char *cp = list; + while (1) + { + if (strncmp (cp, name, name_len) == 0 + && (cp[name_len] == ':' || cp[name_len] == '\0')) + return mask; + + cp = strchr (cp, ':'); + if (cp == NULL) + return 0; + ++cp; + } +} + + +unsigned int +la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie) +{ + if (out_file == NULL) + return 0; + + const char *full_name = map->l_name ?: ""; + if (full_name[0] == '\0') + full_name = __progname_full; + size_t full_name_len = strlen (full_name); + const char *base_name = basename (full_name); + if (base_name[0] == '\0') + base_name = __progname; + size_t base_name_len = strlen (base_name); + + int result = 0; + const char *print_name = NULL; + for (struct libname_list *l = map->l_libname; l != NULL; l = l->next) + { + if (print_name == NULL || (print_name[0] == '/' && l->name[0] != '/')) + print_name = l->name; + + if (fromlist != NULL) + result |= match_file (fromlist, l->name, strlen (l->name), + LA_FLG_BINDFROM); + + if (tolist != NULL) + result |= match_file (tolist, l->name, strlen (l->name),LA_FLG_BINDTO); + } + + if (print_name == NULL) + print_name = base_name; + if (print_name[0] == '\0') + print_name = __progname; + + /* We cannot easily get to the object name in the PLT handling + functions. Use the cookie to get the string pointer passed back + to us. */ + *cookie = (uintptr_t) print_name; + + /* The object name has to be on the list of objects to trace calls + from or that list must be empty. In the latter case we trace + only calls from the main binary. */ + if (fromlist == NULL) + result |= map->l_name[0] == '\0' ? LA_FLG_BINDFROM : 0; + else + result |= (match_file (fromlist, full_name, full_name_len, + LA_FLG_BINDFROM) + | match_file (fromlist, base_name, base_name_len, + LA_FLG_BINDFROM)); + + /* The object name has to be on the list of objects to trace calls + to or that list must be empty. In the latter case we trace + calls toall objects. */ + if (tolist == NULL) + result |= LA_FLG_BINDTO; + else + result |= (match_file (tolist, full_name, full_name_len, LA_FLG_BINDTO) + | match_file (tolist, base_name, base_name_len, LA_FLG_BINDTO)); + + return result; +} + + +#if __ELF_NATIVE_CLASS == 32 +# define la_symbind la_symbind32 +typedef Elf32_Sym Elf_Sym; +#else +# define la_symbind la_symbind64 +typedef Elf64_Sym Elf_Sym; +#endif + +uintptr_t +la_symbind (Elf_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, const char *symname) +{ + if (!do_exit) + *flags = LA_SYMB_NOPLTEXIT; + + return sym->st_value; +} + + +static void +print_enter (uintptr_t *refcook, uintptr_t *defcook, const char *symname, + unsigned long int reg1, unsigned long int reg2, + unsigned long int reg3, unsigned int flags) +{ + char buf[3 * sizeof (pid_t) + 3]; + buf[0] = '\0'; + if (print_pid) + snprintf (buf, sizeof (buf), "%5ld: ", (long int) getpid ()); + + fprintf (out_file, "%s%15s -> %-15s:%s%s(0x%lx, 0x%lx, 0x%lx)\n", + buf, (char *) *refcook, (char *) *defcook, + (flags & LA_SYMB_NOPLTEXIT) ? "*" : " ", symname, reg1, reg2, reg3); +} + + +#ifdef __i386__ +Elf32_Addr +la_i86_gnu_pltenter (Elf32_Sym *sym __attribute__ ((unused)), + unsigned int ndx __attribute__ ((unused)), + uintptr_t *refcook, uintptr_t *defcook, + La_i86_regs *regs, unsigned int *flags, + const char *symname, long int *framesizep) +{ + unsigned long int *sp = (unsigned long int *) regs->lr_esp; + + print_enter (refcook, defcook, symname, sp[1], sp[2], sp[3], *flags); + + /* No need to copy anything, we will not need the parameters in any case. */ + *framesizep = 0; + + return sym->st_value; +} +#elif defined __x86_64__ +Elf64_Addr +la_x86_64_gnu_pltenter (Elf64_Sym *sym __attribute__ ((unused)), + unsigned int ndx __attribute__ ((unused)), + uintptr_t *refcook, uintptr_t *defcook, + La_x86_64_regs *regs, unsigned int *flags, + const char *symname, long int *framesizep) +{ + print_enter (refcook, defcook, symname, + regs->lr_rdi, regs->lr_rsi, regs->lr_rdx, *flags); + + /* No need to copy anything, we will not need the parameters in any case. */ + *framesizep = 0; + + return sym->st_value; +} +#elif defined __sparc__ && !defined __arch64__ +Elf32_Addr +la_sparc32_gnu_pltenter (Elf32_Sym *sym __attribute__ ((unused)), + unsigned int ndx __attribute__ ((unused)), + uintptr_t *refcook, uintptr_t *defcook, + La_sparc32_regs *regs, unsigned int *flags, + const char *symname, long int *framesizep) +{ + print_enter (refcook, defcook, symname, + regs->lr_reg[0], regs->lr_reg[1], regs->lr_reg[2], + *flags); + + /* No need to copy anything, we will not need the parameters in any case. */ + *framesizep = 0; + + return sym->st_value; +} +#elif defined __sparc__ && defined __arch64__ +Elf64_Addr +la_sparc64_gnu_pltenter (Elf64_Sym *sym __attribute__ ((unused)), + unsigned int ndx __attribute__ ((unused)), + uintptr_t *refcook, uintptr_t *defcook, + La_sparc64_regs *regs, unsigned int *flags, + const char *symname, long int *framesizep) +{ + print_enter (refcook, defcook, symname, + regs->lr_reg[0], regs->lr_reg[1], regs->lr_reg[2], + *flags); + + /* No need to copy anything, we will not need the parameters in any case. */ + *framesizep = 0; + + return sym->st_value; +} +#elif !defined HAVE_ARCH_PLTENTER +# warning "pltenter for architecture not supported" +#endif + + +static void +print_exit (uintptr_t *refcook, uintptr_t *defcook, const char *symname, + unsigned long int reg) +{ + char buf[3 * sizeof (pid_t) + 3]; + buf[0] = '\0'; + if (print_pid) + snprintf (buf, sizeof (buf), "%5ld: ", (long int) getpid ()); + + fprintf (out_file, "%s%15s -> %-15s:%s%s - 0x%lx\n", + buf, (char *) *refcook, (char *) *defcook, " ", symname, reg); +} + + +#ifdef __i386__ +unsigned int +la_i86_gnu_pltexit (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, const struct La_i86_regs *inregs, + struct La_i86_retval *outregs, const char *symname) +{ + print_exit (refcook, defcook, symname, outregs->lrv_eax); + + return 0; +} +#elif defined __x86_64__ +unsigned int +la_x86_64_gnu_pltexit (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, const struct La_x86_64_regs *inregs, + struct La_x86_64_retval *outregs, const char *symname) +{ + print_exit (refcook, defcook, symname, outregs->lrv_rax); + + return 0; +} +#elif defined __sparc__ && !defined __arch64__ +unsigned int +la_sparc32_gnu_pltexit (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, const struct La_sparc32_regs *inregs, + struct La_sparc32_retval *outregs, const char *symname) +{ + print_exit (refcook, defcook, symname, outregs->lrv_reg[0]); + + return 0; +} +#elif defined __sparc__ && defined __arch64__ +unsigned int +la_sparc64_gnu_pltexit (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, const struct La_sparc64_regs *inregs, + struct La_sparc64_retval *outregs, const char *symname) +{ + print_exit (refcook, defcook, symname, outregs->lrv_reg[0]); + + return 0; +} +#elif !defined HAVE_ARCH_PLTEXIT +# warning "pltexit for architecture not supported" +#endif diff --git a/REORG.TODO/elf/sotruss.sh b/REORG.TODO/elf/sotruss.sh new file mode 100755 index 0000000000..c83e837e0e --- /dev/null +++ b/REORG.TODO/elf/sotruss.sh @@ -0,0 +1,152 @@ +#! @BASH@ +# Copyright (C) 2011-2017 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# <http://www.gnu.org/licenses/>. + +# We should be able to find the translation right at the beginning. +TEXTDOMAIN=libc +TEXTDOMAINDIR=@TEXTDOMAINDIR@ + +unset SOTRUSS_FROMLIST +unset SOTRUSS_TOLIST +unset SOTRUSS_OUTNAME +unset SOTRUSS_EXIT +unset SOTRUSS_NOINDENT +SOTRUSS_WHICH=$$ +lib='@PREFIX@/$LIB/audit/sotruss-lib.so' + +do_help() { + echo $"Usage: sotruss [OPTION...] [--] EXECUTABLE [EXECUTABLE-OPTION...] + -F, --from FROMLIST Trace calls from objects on FROMLIST + -T, --to TOLIST Trace calls to objects on TOLIST + + -e, --exit Also show exits from the function calls + -f, --follow Trace child processes + -o, --output FILENAME Write output to FILENAME (or FILENAME.$PID in case + -f is also used) instead of standard error + + -?, --help Give this help list + --usage Give a short usage message + --version Print program version" + + echo + printf $"Mandatory arguments to long options are also mandatory for any corresponding\nshort options.\n" + echo + + printf $"For bug reporting instructions, please see:\\n%s.\\n" \ + "@REPORT_BUGS_TO@" + exit 0 +} + +do_missing_arg() { + printf >&2 $"%s: option requires an argument -- '%s'\n" sotruss "$1" + printf >&2 $"Try \`%s --help' or \`%s --usage' for more information.\n" sotruss sotruss + exit 1 +} + +do_ambiguous() { + printf >&2 $"%s: option is ambiguous; possibilities:" + while test $# -gt 0; do + printf >&2 " '%s'" $1 + shift + done + printf >&2 "\n" + printf >&2 $"Try \`%s --help' or \`%s --usage' for more information.\n" sotruss sotruss + exit 1 +} + +while test $# -gt 0; do + case "$1" in + --v | --ve | --ver | --vers | --versi | --versio | --version) + echo "sotruss @PKGVERSION@@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. +" "2017" + printf $"Written by %s.\n" "Ulrich Drepper" + exit 0 + ;; + -\? | --h | --he | --hel | --help) + do_help + ;; + --u | --us | --usa | --usag | --usage) + printf $"Usage: %s [-ef] [-F FROMLIST] [-o FILENAME] [-T TOLIST] [--exit] + [--follow] [--from FROMLIST] [--output FILENAME] [--to TOLIST] + [--help] [--usage] [--version] [--] + EXECUTABLE [EXECUTABLE-OPTION...]\n" sotruss + exit 0 + ;; + -F | --fr | --fro | --from) + if test $# -eq 1; then + do_missing_arg "$1" + fi + shift + SOTRUSS_FROMLIST="$1" + ;; + -T | --t | --to) + if test $# -eq 1; then + do_missing_arg "$1" + fi + shift + SOTRUSS_TOLIST="$1" + ;; + -o | --o | --ou | --out | --outp | --outpu | --output) + if test $# -eq 1; then + do_missing_arg "$1" + fi + shift + SOTRUSS_OUTNAME="$1" + ;; + -f | --fo | --fol | --foll | --follo | --follow) + unset SOTRUSS_WHICH + ;; + -l | --l | --li | --lib) + if test $# -eq 1; then + do_missing_arg "$1" + fi + shift + lib="$1" + ;; + -e | --e | --ex | --exi | --exit) + SOTRUSS_EXIT=1 + ;; + --f) + do_ambiguous '--from' '--follow' + ;; + --) + shift + break + ;; + -*) + printf >&2 $"%s: unrecognized option '%c%s'\n" sotruss '-' ${1#-} + printf >&2 $"Try \`%s --help' or \`%s --usage' for more information.\n" sotruss sotruss + exit 1 + ;; + *) + break + ;; + esac + shift +done + +export SOTRUSS_FROMLIST +export SOTRUSS_TOLIST +export SOTRUSS_OUTNAME +export SOTRUSS_WHICH +export SOTRUSS_EXIT +export LD_AUDIT="$lib" + +exec "$@" diff --git a/REORG.TODO/elf/sprof.c b/REORG.TODO/elf/sprof.c new file mode 100644 index 0000000000..85c4975360 --- /dev/null +++ b/REORG.TODO/elf/sprof.c @@ -0,0 +1,1436 @@ +/* Read and display shared object profiling data. + Copyright (C) 1997-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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 <stdint.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."); +//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); + +/* Function to print some extra text in the help message. */ +static char *more_help (int key, const char *text, void *input); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, more_help +}; + + +/* 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 real_gmon_hist_hdr +{ + char *low_pc; + char *high_pc; + int32_t hist_size; + int32_t prof_rate; + char dimen[15]; + char dimen_abbrev; +}; + + +struct profdata +{ + void *addr; + off_t size; + + char *hist; + struct real_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; +} + + +static char * +more_help (int key, const char *text, void *input) +{ + char *tp = NULL; + switch (key) + { + case ARGP_KEY_HELP_EXTRA: + /* We print some extra information. */ + if (asprintf (&tp, gettext ("\ +For bug reporting instructions, please see:\n\ +%s.\n"), REPORT_BUGS_TO) < 0) + return NULL; + return tp; + default: + break; + } + return (char *) text; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state) +{ + fprintf (stream, "sprof %s%s\n", PKGVERSION, 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\ +"), + "2017"); + 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; + 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 descriptor")); + 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 (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); + ssize_t n = readlink (origprocname, origlink, PATH_MAX - 1); + if (n == -1) + goto no_debuginfo; + origlink[n] = '\0'; + + /* 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 stat64 st; + void *addr; + 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 (fstat64 (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 real_gmon_hist_hdr *) ((char *) result->hist + + sizeof (uint32_t)); + result->kcount = (uint16_t *) ((char *) result->hist + sizeof (uint32_t) + + sizeof (struct real_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. */ + struct real_gmon_hdr + { + char cookie[4]; + int32_t version; + char spare[3 * 4]; + } gmon_hdr; + if (sizeof (gmon_hdr) != sizeof (struct gmon_hdr) + || (offsetof (struct real_gmon_hdr, cookie) + != offsetof (struct gmon_hdr, cookie)) + || (offsetof (struct real_gmon_hdr, version) + != offsetof (struct gmon_hdr, version))) + abort (); + + memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie)); + gmon_hdr.version = GMON_SHOBJ_VERSION; + memset (gmon_hdr.spare, '\0', sizeof (gmon_hdr.spare)); + + /* Create the hist_hdr we expect or write. */ + struct real_gmon_hist_hdr hist_hdr; + if (sizeof (hist_hdr) != sizeof (struct gmon_hist_hdr) + || (offsetof (struct real_gmon_hist_hdr, low_pc) + != offsetof (struct gmon_hist_hdr, low_pc)) + || (offsetof (struct real_gmon_hist_hdr, high_pc) + != offsetof (struct gmon_hist_hdr, high_pc)) + || (offsetof (struct real_gmon_hist_hdr, hist_size) + != offsetof (struct gmon_hist_hdr, hist_size)) + || (offsetof (struct real_gmon_hist_hdr, prof_rate) + != offsetof (struct gmon_hist_hdr, prof_rate)) + || (offsetof (struct real_gmon_hist_hdr, dimen) + != offsetof (struct gmon_hist_hdr, dimen)) + || (offsetof (struct real_gmon_hist_hdr, dimen_abbrev) + != offsetof (struct gmon_hist_hdr, dimen_abbrev))) + abort (); + + hist_hdr.low_pc = (char *) shobj->lowpc - shobj->map->l_addr; + hist_hdr.high_pc = (char *) shobj->highpc - shobj->map->l_addr; + if (do_test) + printf ("low_pc = %p\nhigh_pc = %p\n", hist_hdr.low_pc, hist_hdr.high_pc); + hist_hdr.hist_size = shobj->kcountsize / sizeof (HISTCOUNTER); + 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 / 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 about 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/REORG.TODO/elf/static-stubs.c b/REORG.TODO/elf/static-stubs.c new file mode 100644 index 0000000000..f81d5b792b --- /dev/null +++ b/REORG.TODO/elf/static-stubs.c @@ -0,0 +1,46 @@ +/* Stub implementations of functions to link into statically linked + programs without needing libgcc_eh. + Copyright (C) 2012-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* Avoid backtrace (and so _Unwind_Backtrace) dependencies from + sysdeps/unix/sysv/linux/libc_fatal.c. */ +#include <sysdeps/posix/libc_fatal.c> + +#include <stdlib.h> +#include <unwind.h> + +/* These programs do not use thread cancellation, so _Unwind_Resume + and the personality routine are never actually called. */ + +void +_Unwind_Resume (struct _Unwind_Exception *exc __attribute__ ((unused))) +{ + abort (); +} + +_Unwind_Reason_Code +__gcc_personality_v0 (int version __attribute__ ((unused)), + _Unwind_Action actions __attribute__ ((unused)), + _Unwind_Exception_Class exception_class + __attribute__ ((unused)), + struct _Unwind_Exception *ue_header + __attribute__ ((unused)), + struct _Unwind_Context *context __attribute__ ((unused))) +{ + abort (); +} diff --git a/REORG.TODO/elf/testobj.h b/REORG.TODO/elf/testobj.h new file mode 100644 index 0000000000..1707ae340e --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/testobj1.c b/REORG.TODO/elf/testobj1.c new file mode 100644 index 0000000000..5ab20efd62 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/testobj1_1.c b/REORG.TODO/elf/testobj1_1.c new file mode 100644 index 0000000000..2541a5ad1b --- /dev/null +++ b/REORG.TODO/elf/testobj1_1.c @@ -0,0 +1,7 @@ +#include "testobj.h" + +int +obj1func1 (int a) +{ + return 42 + obj1func2 (a); +} diff --git a/REORG.TODO/elf/testobj2.c b/REORG.TODO/elf/testobj2.c new file mode 100644 index 0000000000..7e4b610982 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/testobj3.c b/REORG.TODO/elf/testobj3.c new file mode 100644 index 0000000000..c025ff631a --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/testobj4.c b/REORG.TODO/elf/testobj4.c new file mode 100644 index 0000000000..2729ba32be --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/testobj5.c b/REORG.TODO/elf/testobj5.c new file mode 100644 index 0000000000..9675cad88d --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/testobj6.c b/REORG.TODO/elf/testobj6.c new file mode 100644 index 0000000000..fcba01631d --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tls-macros.h b/REORG.TODO/elf/tls-macros.h new file mode 100644 index 0000000000..e25e33b0f0 --- /dev/null +++ b/REORG.TODO/elf/tls-macros.h @@ -0,0 +1,25 @@ +/* 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. */ +#if !defined TLS_LE || !defined TLS_IE \ + || !defined TLS_LD || !defined TLS_GD +# error "No support for this architecture so far." +#endif diff --git a/REORG.TODO/elf/tlsdeschtab.h b/REORG.TODO/elf/tlsdeschtab.h new file mode 100644 index 0000000000..3091d8b420 --- /dev/null +++ b/REORG.TODO/elf/tlsdeschtab.h @@ -0,0 +1,164 @@ +/* Hash table for TLS descriptors. + Copyright (C) 2005-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Alexandre Oliva <aoliva@redhat.com> + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef TLSDESCHTAB_H +# define TLSDESCHTAB_H 1 + +#include <atomic.h> + +# ifdef SHARED + +# include <inline-hashtab.h> + +inline static int +hash_tlsdesc (void *p) +{ + struct tlsdesc_dynamic_arg *td = p; + + /* We know all entries are for the same module, so ti_offset is the + only distinguishing entry. */ + return td->tlsinfo.ti_offset; +} + +inline static int +eq_tlsdesc (void *p, void *q) +{ + struct tlsdesc_dynamic_arg *tdp = p, *tdq = q; + + return tdp->tlsinfo.ti_offset == tdq->tlsinfo.ti_offset; +} + +inline static size_t +map_generation (struct link_map *map) +{ + size_t idx = map->l_tls_modid; + struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list); + + /* Find the place in the dtv slotinfo list. */ + do + { + /* Does it fit in the array of this list element? */ + if (idx < listp->len) + { + /* We should never get here for a module in static TLS, so + we can assume that, if the generation count is zero, we + still haven't determined the generation count for this + module. */ + if (listp->slotinfo[idx].map == map && listp->slotinfo[idx].gen) + return listp->slotinfo[idx].gen; + else + break; + } + idx -= listp->len; + listp = listp->next; + } + while (listp != NULL); + + /* If we get to this point, the module still hasn't been assigned an + entry in the dtv slotinfo data structures, and it will when we're + done with relocations. At that point, the module will get a + generation number that is one past the current generation, so + return exactly that. */ + return GL(dl_tls_generation) + 1; +} + +void * +internal_function +_dl_make_tlsdesc_dynamic (struct link_map *map, size_t ti_offset) +{ + struct hashtab *ht; + void **entry; + struct tlsdesc_dynamic_arg *td, test; + + /* FIXME: We could use a per-map lock here, but is it worth it? */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + ht = map->l_mach.tlsdesc_table; + if (! ht) + { + ht = htab_create (); + if (! ht) + { + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + return 0; + } + map->l_mach.tlsdesc_table = ht; + } + + test.tlsinfo.ti_module = map->l_tls_modid; + test.tlsinfo.ti_offset = ti_offset; + entry = htab_find_slot (ht, &test, 1, hash_tlsdesc, eq_tlsdesc); + if (! entry) + { + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + return 0; + } + + if (*entry) + { + td = *entry; + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + return td; + } + + *entry = td = malloc (sizeof (struct tlsdesc_dynamic_arg)); + /* This may be higher than the map's generation, but it doesn't + matter much. Worst case, we'll have one extra DTV update per + thread. */ + td->gen_count = map_generation (map); + td->tlsinfo = test.tlsinfo; + + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + return td; +} + +# endif /* SHARED */ + +/* The idea of the following two functions is to stop multiple threads + from attempting to resolve the same TLS descriptor without busy + waiting. Ideally, we should be able to release the lock right + after changing td->entry, and then using say a condition variable + or a futex wake to wake up any waiting threads, but let's try to + avoid introducing such dependencies. */ + +static int +_dl_tlsdesc_resolve_early_return_p (struct tlsdesc volatile *td, void *caller) +{ + if (caller != atomic_load_relaxed (&td->entry)) + return 1; + + __rtld_lock_lock_recursive (GL(dl_load_lock)); + if (caller != atomic_load_relaxed (&td->entry)) + { + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + return 1; + } + + atomic_store_relaxed (&td->entry, _dl_tlsdesc_resolve_hold); + + return 0; +} + +static void +_dl_tlsdesc_wake_up_held_fixups (void) +{ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); +} + +#endif diff --git a/REORG.TODO/elf/tst-_dl_addr_inside_object.c b/REORG.TODO/elf/tst-_dl_addr_inside_object.c new file mode 100644 index 0000000000..1604b8df63 --- /dev/null +++ b/REORG.TODO/elf/tst-_dl_addr_inside_object.c @@ -0,0 +1,222 @@ +/* Unit test for _dl_addr_inside_object. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <link.h> +#include <elf.h> +#include <libc-symbols.h> + +extern int internal_function _dl_addr_inside_object (struct link_map *l, + const ElfW(Addr) addr); + +static int +do_test (void) +{ + int ret, err = 0; + ElfW(Addr) addr; + struct link_map map; + ElfW(Phdr) header; + map.l_phdr = &header; + map.l_phnum = 1; + map.l_addr = 0x0; + /* Segment spans 0x2000 -> 0x4000. */ + header.p_vaddr = 0x2000; + header.p_memsz = 0x2000; + header.p_type = PT_LOAD; + /* Address is above the segment e.g. > 0x4000. */ + addr = 0x5000; + ret = _dl_addr_inside_object (&map, addr); + switch (ret) + { + case 0: + printf ("PASS: Above: Address is detected as outside the segment.\n"); + break; + case 1: + printf ("FAIL: Above: Address is detected as inside the segment.\n"); + err++; + break; + default: + printf ("FAIL: Above: Invalid return value.\n"); + exit (1); + } + /* Address is inside the segment e.g. 0x2000 < addr < 0x4000. */ + addr = 0x3000; + ret = _dl_addr_inside_object (&map, addr); + switch (ret) + { + case 0: + printf ("FAIL: Inside: Address is detected as outside the segment.\n"); + err++; + break; + case 1: + printf ("PASS: Inside: Address is detected as inside the segment.\n"); + break; + default: + printf ("FAIL: Inside: Invalid return value.\n"); + exit (1); + } + /* Address is below the segment e.g. < 0x2000. */ + addr = 0x1000; + ret = _dl_addr_inside_object (&map, addr); + switch (ret) + { + case 0: + printf ("PASS: Below: Address is detected as outside the segment.\n"); + break; + case 1: + printf ("FAIL: Below: Address is detected as inside the segment.\n"); + err++; + break; + default: + printf ("FAIL: Below: Invalid return value.\n"); + exit (1); + } + /* Address is in the segment and addr == p_vaddr. */ + addr = 0x2000; + ret = _dl_addr_inside_object (&map, addr); + switch (ret) + { + case 0: + printf ("FAIL: At p_vaddr: Address is detected as outside the segment.\n"); + err++; + break; + case 1: + printf ("PASS: At p_vaddr: Address is detected as inside the segment.\n"); + break; + default: + printf ("FAIL: At p_vaddr: Invalid return value.\n"); + exit (1); + } + /* Address is in the segment and addr == p_vaddr + p_memsz - 1. */ + addr = 0x2000 + 0x2000 - 0x1; + ret = _dl_addr_inside_object (&map, addr); + switch (ret) + { + case 0: + printf ("FAIL: At p_memsz-1: Address is detected as outside the segment.\n"); + err++; + break; + case 1: + printf ("PASS: At p_memsz-1: Address is detected as inside the segment.\n"); + break; + default: + printf ("FAIL: At p_memsz-1: Invalid return value.\n"); + exit (1); + } + /* Address is outside the segment and addr == p_vaddr + p_memsz. */ + addr = 0x2000 + 0x2000; + ret = _dl_addr_inside_object (&map, addr); + switch (ret) + { + case 0: + printf ("PASS: At p_memsz: Address is detected as outside the segment.\n"); + break; + case 1: + printf ("FAIL: At p_memsz: Address is detected as inside the segment.\n"); + err++; + break; + default: + printf ("FAIL: At p_memsz: Invalid return value.\n"); + exit (1); + } + /* Address is outside the segment and p_vaddr at maximum address. */ + addr = 0x0 - 0x2; + header.p_vaddr = 0x0 - 0x1; + header.p_memsz = 0x1; + ret = _dl_addr_inside_object (&map, addr); + switch (ret) + { + case 0: + printf ("PASS: At max: Address is detected as outside the segment.\n"); + break; + case 1: + printf ("FAIL: At max: Address is detected as inside the segment.\n"); + err++; + break; + default: + printf ("FAIL: At max: Invalid return value.\n"); + exit (1); + } + /* Address is outside the segment and p_vaddr at minimum address. */ + addr = 0x1; + header.p_vaddr = 0x0; + header.p_memsz = 0x1; + ret = _dl_addr_inside_object (&map, addr); + switch (ret) + { + case 0: + printf ("PASS: At min: Address is detected as outside the segment.\n"); + break; + case 1: + printf ("FAIL: At min: Address is detected as inside the segment.\n"); + err++; + break; + default: + printf ("FAIL: At min: Invalid return value.\n"); + exit (1); + } + /* Address is always inside the segment with p_memsz at max. */ + addr = 0x0; + header.p_vaddr = 0x0; + header.p_memsz = 0x0 - 0x1; + ret = _dl_addr_inside_object (&map, addr); + switch (ret) + { + case 0: + printf ("FAIL: At maxmem: Address is detected as outside the segment.\n"); + err++; + break; + case 1: + printf ("PASS: At maxmem: Address is detected as inside the segment.\n"); + break; + default: + printf ("FAIL: At maxmem: Invalid return value.\n"); + exit (1); + } + /* Attempt to wrap addr into the segment. + Pick a load address in the middle of the address space. + Place the test address at 0x0 so it wraps to the middle again. */ + map.l_addr = 0x0 - 0x1; + map.l_addr = map.l_addr / 2; + addr = 0; + /* Setup a segment covering 1/2 the address space. */ + header.p_vaddr = 0x0; + header.p_memsz = 0x0 - 0x1 - map.l_addr; + /* No matter where you place addr everything is shifted modulo l_addr + and even with this underflow you're always 1 byte away from being + in the range. */ + ret = _dl_addr_inside_object (&map, addr); + switch (ret) + { + case 0: + printf ("PASS: Underflow: Address is detected as outside the segment.\n"); + break; + case 1: + printf ("FAIL: Underflow: Address is detected as inside the segment.\n"); + err++; + break; + default: + printf ("FAIL: Underflow: Invalid return value.\n"); + exit (1); + } + + return err; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-addr1.c b/REORG.TODO/elf/tst-addr1.c new file mode 100644 index 0000000000..68ff74aabd --- /dev/null +++ b/REORG.TODO/elf/tst-addr1.c @@ -0,0 +1,25 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> + +static int +do_test (void) +{ + Dl_info i; + if (dladdr (&printf, &i) == 0) + { + puts ("not found"); + return 1; + } + printf ("found symbol %s in %s\n", i.dli_sname, i.dli_fname); + return i.dli_sname == NULL + || (strcmp (i.dli_sname, "printf") != 0 + /* On architectures which create PIC code by default + &printf may resolve to an address in libc.so + rather than in the binary. printf and _IO_printf + are aliased and which one comes first in the + hash table is up to the linker. */ + && strcmp (i.dli_sname, "_IO_printf") != 0); +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-align.c b/REORG.TODO/elf/tst-align.c new file mode 100644 index 0000000000..01b0b4ffb3 --- /dev/null +++ b/REORG.TODO/elf/tst-align.c @@ -0,0 +1,52 @@ +/* Copyright (C) 2003-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-align2.c b/REORG.TODO/elf/tst-align2.c new file mode 100644 index 0000000000..78b66be20a --- /dev/null +++ b/REORG.TODO/elf/tst-align2.c @@ -0,0 +1,155 @@ +/* Copyright (C) 2005-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-alignmod.c b/REORG.TODO/elf/tst-alignmod.c new file mode 100644 index 0000000000..b5e47be0bd --- /dev/null +++ b/REORG.TODO/elf/tst-alignmod.c @@ -0,0 +1,52 @@ +/* Copyright (C) 2003-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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/REORG.TODO/elf/tst-alignmod2.c b/REORG.TODO/elf/tst-alignmod2.c new file mode 100644 index 0000000000..f338ab5c27 --- /dev/null +++ b/REORG.TODO/elf/tst-alignmod2.c @@ -0,0 +1,59 @@ +/* Copyright (C) 2003-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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/REORG.TODO/elf/tst-array1-static.c b/REORG.TODO/elf/tst-array1-static.c new file mode 100644 index 0000000000..21539a4212 --- /dev/null +++ b/REORG.TODO/elf/tst-array1-static.c @@ -0,0 +1 @@ +#include "tst-array1.c" diff --git a/REORG.TODO/elf/tst-array1.c b/REORG.TODO/elf/tst-array1.c new file mode 100644 index 0000000000..e998932b36 --- /dev/null +++ b/REORG.TODO/elf/tst-array1.c @@ -0,0 +1,103 @@ +#include <unistd.h> + +/* Give init non-default priority so that it runs before init_array. */ +static void init (void) __attribute__ ((constructor (1000))); + +static void +init (void) +{ + write (STDOUT_FILENO, "init\n", 5); +} + +/* Give fini the same priority as init. */ +static void fini (void) __attribute__ ((destructor (1000))); + +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 (*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 (*fini_array []) (void) + __attribute__ ((section (".fini_array"), aligned (sizeof (void *)))) = +{ + &fini_0, + &fini_1, + &fini_2 +}; + +int +main (void) +{ + return 0; +} diff --git a/REORG.TODO/elf/tst-array1.exp b/REORG.TODO/elf/tst-array1.exp new file mode 100644 index 0000000000..cfcec9de0f --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-array2.c b/REORG.TODO/elf/tst-array2.c new file mode 100644 index 0000000000..21539a4212 --- /dev/null +++ b/REORG.TODO/elf/tst-array2.c @@ -0,0 +1 @@ +#include "tst-array1.c" diff --git a/REORG.TODO/elf/tst-array2.exp b/REORG.TODO/elf/tst-array2.exp new file mode 100644 index 0000000000..ed203525b5 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-array2dep.c b/REORG.TODO/elf/tst-array2dep.c new file mode 100644 index 0000000000..2f920cdc8d --- /dev/null +++ b/REORG.TODO/elf/tst-array2dep.c @@ -0,0 +1,71 @@ +#include <unistd.h> + +/* Give init non-default priority so that it runs before init_array. */ +static void init (void) __attribute__ ((constructor (1000))); + +static void +init (void) +{ + write (STDOUT_FILENO, "DSO init\n", 9); +} + +/* Give fini the same priority as init. */ +static void fini (void) __attribute__ ((destructor (1000))); + +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 (*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 (*fini_array []) (void) + __attribute__ ((section (".fini_array"), aligned (sizeof (void *)))) = +{ + &fini_0, + &fini_1, + &fini_2 +}; diff --git a/REORG.TODO/elf/tst-array3.c b/REORG.TODO/elf/tst-array3.c new file mode 100644 index 0000000000..21539a4212 --- /dev/null +++ b/REORG.TODO/elf/tst-array3.c @@ -0,0 +1 @@ +#include "tst-array1.c" diff --git a/REORG.TODO/elf/tst-array4.c b/REORG.TODO/elf/tst-array4.c new file mode 100644 index 0000000000..ac3d4eb716 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-array4.exp b/REORG.TODO/elf/tst-array4.exp new file mode 100644 index 0000000000..560444d2e8 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-array5-static.c b/REORG.TODO/elf/tst-array5-static.c new file mode 100644 index 0000000000..4ef2aba3f3 --- /dev/null +++ b/REORG.TODO/elf/tst-array5-static.c @@ -0,0 +1 @@ +#include "tst-array5.c" diff --git a/REORG.TODO/elf/tst-array5-static.exp b/REORG.TODO/elf/tst-array5-static.exp new file mode 100644 index 0000000000..b1dc9e467b --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-array5.c b/REORG.TODO/elf/tst-array5.c new file mode 100644 index 0000000000..03a5668326 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-array5.exp b/REORG.TODO/elf/tst-array5.exp new file mode 100644 index 0000000000..28b4909833 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-array5dep.c b/REORG.TODO/elf/tst-array5dep.c new file mode 100644 index 0000000000..570d282af4 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-audit1.c b/REORG.TODO/elf/tst-audit1.c new file mode 100644 index 0000000000..63656b4ee9 --- /dev/null +++ b/REORG.TODO/elf/tst-audit1.c @@ -0,0 +1 @@ +#include "../io/pwd.c" diff --git a/REORG.TODO/elf/tst-audit11.c b/REORG.TODO/elf/tst-audit11.c new file mode 100644 index 0000000000..ff91a6bd6d --- /dev/null +++ b/REORG.TODO/elf/tst-audit11.c @@ -0,0 +1,35 @@ +/* Test version symbol binding can find a DSO replaced by la_objsearch. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <dlfcn.h> +#include <stdio.h> + +int +do_test (void) +{ + puts ("Start"); + if (dlopen ("$ORIGIN/tst-audit11mod1.so", RTLD_LAZY) == NULL) + { + printf ("module not loaded: %s\n", dlerror ()); + return 1; + } + puts ("OK"); + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-audit11mod1.c b/REORG.TODO/elf/tst-audit11mod1.c new file mode 100644 index 0000000000..0c0f5c6838 --- /dev/null +++ b/REORG.TODO/elf/tst-audit11mod1.c @@ -0,0 +1,24 @@ +/* DSO directly opened by tst-audit11. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +extern int f2 (void); +int +f1 (void) +{ + return f2 (); +} diff --git a/REORG.TODO/elf/tst-audit11mod2.c b/REORG.TODO/elf/tst-audit11mod2.c new file mode 100644 index 0000000000..d5eb029744 --- /dev/null +++ b/REORG.TODO/elf/tst-audit11mod2.c @@ -0,0 +1,23 @@ +/* DSO indirectly opened by tst-audit11, with symbol versioning. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +int +f2 (void) +{ + return 42; +} diff --git a/REORG.TODO/elf/tst-audit11mod2.map b/REORG.TODO/elf/tst-audit11mod2.map new file mode 100644 index 0000000000..278787872c --- /dev/null +++ b/REORG.TODO/elf/tst-audit11mod2.map @@ -0,0 +1,22 @@ +/* Symbol versioning for the DSO indirectly opened by tst-audit11. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +V1 { + global: f2; + local: *; +}; diff --git a/REORG.TODO/elf/tst-audit12.c b/REORG.TODO/elf/tst-audit12.c new file mode 100644 index 0000000000..62ac5f28a4 --- /dev/null +++ b/REORG.TODO/elf/tst-audit12.c @@ -0,0 +1,48 @@ +/* Test that symbol is bound to a DSO replaced by la_objsearch. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <dlfcn.h> +#include <stdio.h> + +int +do_test (void) +{ + puts ("Start"); + void *h = dlopen ("$ORIGIN/tst-audit12mod1.so", RTLD_LAZY); + if (h == NULL) + { + printf ("module not loaded: %s\n", dlerror ()); + return 1; + } + int (*fp) (void) = (int (*) (void)) dlsym (h, "f1"); + if (fp == NULL) + { + printf ("function f1 not found: %s\n", dlerror ()); + return 1; + } + int res = fp (); + if (res != 43) + { + puts ("incorrect function f2 called"); + return 1; + } + printf ("%d is OK\n", res); + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-audit12mod1.c b/REORG.TODO/elf/tst-audit12mod1.c new file mode 100644 index 0000000000..a48795b661 --- /dev/null +++ b/REORG.TODO/elf/tst-audit12mod1.c @@ -0,0 +1,24 @@ +/* DSO directly opened by tst-audit12. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +extern int f2 (void); +int +f1 (void) +{ + return f2 (); +} diff --git a/REORG.TODO/elf/tst-audit12mod2.c b/REORG.TODO/elf/tst-audit12mod2.c new file mode 100644 index 0000000000..593d02dfb6 --- /dev/null +++ b/REORG.TODO/elf/tst-audit12mod2.c @@ -0,0 +1,23 @@ +/* Replaced DSO referenced by tst-audit12mod1.so, for tst-audit12. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +int +f2 (void) +{ + return 42; +} diff --git a/REORG.TODO/elf/tst-audit12mod2.map b/REORG.TODO/elf/tst-audit12mod2.map new file mode 100644 index 0000000000..11e22bbdee --- /dev/null +++ b/REORG.TODO/elf/tst-audit12mod2.map @@ -0,0 +1,22 @@ +/* Symbol versioning for tst-audit12mod2.so used by tst-audit12. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +V1 { + global: f2; + local: *; +}; diff --git a/REORG.TODO/elf/tst-audit12mod3.c b/REORG.TODO/elf/tst-audit12mod3.c new file mode 100644 index 0000000000..1e01bb8eea --- /dev/null +++ b/REORG.TODO/elf/tst-audit12mod3.c @@ -0,0 +1,23 @@ +/* Replacement DSO loaded by the audit module, for tst-audit12. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +int +f2 (void) +{ + return 43; +} diff --git a/REORG.TODO/elf/tst-audit2.c b/REORG.TODO/elf/tst-audit2.c new file mode 100644 index 0000000000..0e66f5c328 --- /dev/null +++ b/REORG.TODO/elf/tst-audit2.c @@ -0,0 +1,60 @@ +/* Test case for early TLS initialization in dynamic linker. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dlfcn.h> + +#define MAGIC1 0xabcdef72 +#define MAGIC2 0xd8675309 +static __thread unsigned int magic[] = { MAGIC1, MAGIC2 }; +static __thread int calloc_called; + +#undef calloc + +/* This calloc definition will be called by the dynamic linker itself. + We test that interposed calloc is called by the dynamic loader, and + that TLS is fully initialized by then. */ + +void * +calloc (size_t n, size_t m) +{ + if (!calloc_called) + { + /* Allow our calloc to be called more than once. */ + calloc_called = 1; + 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; + } + + n *= m; + void *ptr = malloc (n); + if (ptr != NULL) + memset (ptr, '\0', n); + return ptr; +} + +static int +do_test (void) +{ + /* Make sure that our calloc is called from the dynamic linker at least + once. */ + void *h = dlopen("$ORIGIN/tst-auditmod9b.so", RTLD_LAZY); + if (h != NULL) + dlclose (h); + if (magic[1] != MAGIC1 || magic[0] != MAGIC2) + { + printf ("{%x, %x} != {%x, %x}\n", magic[0], magic[1], MAGIC2, MAGIC1); + return 1; + } + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-audit8.c b/REORG.TODO/elf/tst-audit8.c new file mode 100644 index 0000000000..63656b4ee9 --- /dev/null +++ b/REORG.TODO/elf/tst-audit8.c @@ -0,0 +1 @@ +#include "../io/pwd.c" diff --git a/REORG.TODO/elf/tst-audit9.c b/REORG.TODO/elf/tst-audit9.c new file mode 100644 index 0000000000..b9de1bf5a2 --- /dev/null +++ b/REORG.TODO/elf/tst-audit9.c @@ -0,0 +1,11 @@ +#include <dlfcn.h> + +static int +do_test (void) +{ + void *h = dlopen("$ORIGIN/tst-auditmod9b.so", RTLD_LAZY); + int (*fp)(void) = dlsym(h, "f"); + return fp() - 1; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-auditmod1.c b/REORG.TODO/elf/tst-auditmod1.c new file mode 100644 index 0000000000..573e37abd6 --- /dev/null +++ b/REORG.TODO/elf/tst-auditmod1.c @@ -0,0 +1,135 @@ +#include <dlfcn.h> +#include <link.h> +#include <stddef.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; +} + +#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" +#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, + (ptrdiff_t) outregs->int_retval); + + return 0; +} diff --git a/REORG.TODO/elf/tst-auditmod11.c b/REORG.TODO/elf/tst-auditmod11.c new file mode 100644 index 0000000000..18feb5efc4 --- /dev/null +++ b/REORG.TODO/elf/tst-auditmod11.c @@ -0,0 +1,39 @@ +/* Audit module for tst-audit11. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <link.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +unsigned int +la_version (unsigned int version) +{ + return version; +} + +char * +la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag) +{ + if (strcmp (name, "tst-audit11mod2.so") == 0) + { + return (char *) "$ORIGIN/tst-audit11mod2.so"; + } + return (char *) name; +} diff --git a/REORG.TODO/elf/tst-auditmod12.c b/REORG.TODO/elf/tst-auditmod12.c new file mode 100644 index 0000000000..039b7cd62b --- /dev/null +++ b/REORG.TODO/elf/tst-auditmod12.c @@ -0,0 +1,43 @@ +/* Audit module for tst-audit12. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <link.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +unsigned int +la_version (unsigned int version) +{ + return version; +} + +char * +la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag) +{ + const char target[] = "tst-audit12mod2.so"; + + size_t namelen = strlen (name); + if (namelen >= sizeof (target) - 1 + && strcmp (name + namelen - (sizeof (target) - 1), target) == 0) + { + return (char *) "$ORIGIN/tst-audit12mod3.so"; + } + return (char *) name; +} diff --git a/REORG.TODO/elf/tst-auditmod9a.c b/REORG.TODO/elf/tst-auditmod9a.c new file mode 100644 index 0000000000..7213ade123 --- /dev/null +++ b/REORG.TODO/elf/tst-auditmod9a.c @@ -0,0 +1,15 @@ +#include <stdint.h> + +__thread int var; + +unsigned int +la_version (unsigned int v) +{ + return v; +} + +void +la_activity (uintptr_t *cookie, unsigned int flag) +{ + ++var; +} diff --git a/REORG.TODO/elf/tst-auditmod9b.c b/REORG.TODO/elf/tst-auditmod9b.c new file mode 100644 index 0000000000..8eeeb49986 --- /dev/null +++ b/REORG.TODO/elf/tst-auditmod9b.c @@ -0,0 +1,6 @@ +__thread int a; + +int f(void) +{ + return ++a; +} diff --git a/REORG.TODO/elf/tst-auxv.c b/REORG.TODO/elf/tst-auxv.c new file mode 100644 index 0000000000..bc571c5fa7 --- /dev/null +++ b/REORG.TODO/elf/tst-auxv.c @@ -0,0 +1,70 @@ +/* Copyright (C) 2013-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <elf.h> +#include <errno.h> +#include <link.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <misc/sys/auxv.h> + +static int +do_test (int argc, char *argv[]) +{ + errno = 0; + const char *execfn = (const char *) getauxval (AT_NULL); + + if (errno != ENOENT) + { + printf ("errno is %d rather than %d (ENOENT) on failure\n", errno, + ENOENT); + return 1; + } + + if (execfn != NULL) + { + printf ("getauxval return value is nonzero on failure\n"); + return 1; + } + + errno = 0; + execfn = (const char *) getauxval (AT_EXECFN); + + if (execfn == NULL) + { + printf ("No AT_EXECFN found, AT_EXECFN test skipped\n"); + return 0; + } + + if (errno != 0) + { + printf ("errno erroneously set to %d on success\n", errno); + return 1; + } + + if (strcmp (argv[0], execfn) != 0) + { + printf ("Mismatch: argv[0]: %s vs. AT_EXECFN: %s\n", argv[0], execfn); + return 1; + } + + return 0; +} + +#define TEST_FUNCTION_ARGV do_test +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-deep1.c b/REORG.TODO/elf/tst-deep1.c new file mode 100644 index 0000000000..97dce7ea4d --- /dev/null +++ b/REORG.TODO/elf/tst-deep1.c @@ -0,0 +1,35 @@ +#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 (); +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-deep1mod1.c b/REORG.TODO/elf/tst-deep1mod1.c new file mode 100644 index 0000000000..cc922e6ea5 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-deep1mod2.c b/REORG.TODO/elf/tst-deep1mod2.c new file mode 100644 index 0000000000..b99caf0328 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-deep1mod3.c b/REORG.TODO/elf/tst-deep1mod3.c new file mode 100644 index 0000000000..eee7d5c97b --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-dl-iter-static.c b/REORG.TODO/elf/tst-dl-iter-static.c new file mode 100644 index 0000000000..9a2758c8ef --- /dev/null +++ b/REORG.TODO/elf/tst-dl-iter-static.c @@ -0,0 +1,46 @@ +/* BZ #16046 dl_iterate_phdr static executable test. + Copyright (C) 2014-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <link.h> + +/* Check that the link map of the static executable itself is iterated + over exactly once. */ + +static int +callback (struct dl_phdr_info *info, size_t size, void *data) +{ + int *count = data; + + if (info->dlpi_name[0] == '\0') + (*count)++; + + return 0; +} + +static int +do_test (void) +{ + int count = 0; + int status; + + status = dl_iterate_phdr (callback, &count); + + return status || count != 1; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-dlmodcount.c b/REORG.TODO/elf/tst-dlmodcount.c new file mode 100644 index 0000000000..34c5b25d7f --- /dev/null +++ b/REORG.TODO/elf/tst-dlmodcount.c @@ -0,0 +1,108 @@ +/* Copyright (C) 2004-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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); +} + +static int +do_test (void) +{ + 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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-dlmopen1.c b/REORG.TODO/elf/tst-dlmopen1.c new file mode 100644 index 0000000000..24145cfca6 --- /dev/null +++ b/REORG.TODO/elf/tst-dlmopen1.c @@ -0,0 +1,80 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <gnu/lib-names.h> + +#define TEST_SO "$ORIGIN/tst-dlmopen1mod.so" + +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, TEST_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", TEST_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", + TEST_SO, __func__, dlerror ()); + return 1; + } + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-dlmopen1mod.c b/REORG.TODO/elf/tst-dlmopen1mod.c new file mode 100644 index 0000000000..142488098a --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-dlmopen2.c b/REORG.TODO/elf/tst-dlmopen2.c new file mode 100644 index 0000000000..8489ffba08 --- /dev/null +++ b/REORG.TODO/elf/tst-dlmopen2.c @@ -0,0 +1,69 @@ +#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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-dlmopen3.c b/REORG.TODO/elf/tst-dlmopen3.c new file mode 100644 index 0000000000..8167507784 --- /dev/null +++ b/REORG.TODO/elf/tst-dlmopen3.c @@ -0,0 +1,21 @@ +#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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-dlopen-aout.c b/REORG.TODO/elf/tst-dlopen-aout.c new file mode 100644 index 0000000000..cccc508966 --- /dev/null +++ b/REORG.TODO/elf/tst-dlopen-aout.c @@ -0,0 +1,67 @@ +/* Test case for BZ #16634. + + Verify that incorrectly dlopen()ing an executable without + __RTLD_OPENEXEC does not cause assertion in ld.so. + + Copyright (C) 2014-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. + + Note: this test currently only fails when glibc is configured with + --enable-hardcoded-path-in-tests. */ + +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <pthread.h> + +__thread int x; + +void * +fn (void *p) +{ + return p; +} + +static int +do_test (int argc, char *argv[]) +{ + int j; + + for (j = 0; j < 100; ++j) + { + pthread_t thr; + void *p; + int rc; + + p = dlopen (argv[0], RTLD_LAZY); + if (p != NULL) + { + fprintf (stderr, "dlopen unexpectedly succeeded\n"); + return 1; + } + rc = pthread_create (&thr, NULL, fn, NULL); + assert (rc == 0); + + rc = pthread_join (thr, NULL); + assert (rc == 0); + } + + return 0; +} + +#define TEST_FUNCTION_ARGV do_test +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-dlopenrpath.c b/REORG.TODO/elf/tst-dlopenrpath.c new file mode 100644 index 0000000000..77346d36f9 --- /dev/null +++ b/REORG.TODO/elf/tst-dlopenrpath.c @@ -0,0 +1,70 @@ +/* Copyright (C) 2004-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-dlopenrpathmod.c b/REORG.TODO/elf/tst-dlopenrpathmod.c new file mode 100644 index 0000000000..6d244401bf --- /dev/null +++ b/REORG.TODO/elf/tst-dlopenrpathmod.c @@ -0,0 +1,35 @@ +/* Copyright (C) 2004-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#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/REORG.TODO/elf/tst-dlsym-error.c b/REORG.TODO/elf/tst-dlsym-error.c new file mode 100644 index 0000000000..fac8f10ccf --- /dev/null +++ b/REORG.TODO/elf/tst-dlsym-error.c @@ -0,0 +1,113 @@ +/* Test error reporting for dlsym, dlvsym failures. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <dlfcn.h> +#include <gnu/lib-names.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* Used to disambiguate symbol names. */ +static int counter; + +static void +test_one (void *handle, const char *name, void *(func) (void *, const char *), + const char *suffix) +{ + ++counter; + char symbol[32]; + snprintf (symbol, sizeof (symbol), "no_such_symbol_%d", counter); + char *expected_message; + if (asprintf (&expected_message, ": undefined symbol: %s%s", + symbol, suffix) < 0) + { + printf ("error: asprintf: %m\n"); + abort (); + } + + void *addr = func (handle, symbol); + if (addr != NULL) + { + printf ("error: %s: found symbol \"no_such_symbol\"\n", name); + abort (); + } + const char *message = dlerror (); + if (message == NULL) + { + printf ("error: %s: missing error message\n", name); + abort (); + } + const char *message_without_path = strchrnul (message, ':'); + if (strcmp (message_without_path, expected_message) != 0) + { + printf ("error: %s: unexpected error message: %s\n", name, message); + abort (); + } + free (expected_message); + + message = dlerror (); + if (message != NULL) + { + printf ("error: %s: unexpected error message: %s\n", name, message); + abort (); + } +} + +static void +test_handles (const char *name, void *(func) (void *, const char *), + const char *suffix) +{ + test_one (RTLD_DEFAULT, name, func, suffix); + test_one (RTLD_NEXT, name, func, suffix); + + void *handle = dlopen (LIBC_SO, RTLD_LAZY); + if (handle == NULL) + { + printf ("error: cannot dlopen %s: %s\n", LIBC_SO, dlerror ()); + abort (); + } + test_one (handle, name, func, suffix); + dlclose (handle); +} + +static void * +dlvsym_no_such_version (void *handle, const char *name) +{ + return dlvsym (handle, name, "NO_SUCH_VERSION"); +} + +static void * +dlvsym_glibc_private (void *handle, const char *name) +{ + return dlvsym (handle, name, "GLIBC_PRIVATE"); +} + +static int +do_test (void) +{ + test_handles ("dlsym", dlsym, ""); + test_handles ("dlvsym", dlvsym_no_such_version, + ", version NO_SUCH_VERSION"); + test_handles ("dlvsym", dlvsym_glibc_private, + ", version GLIBC_PRIVATE"); + + return 0; +} + + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-env-setuid-tunables.c b/REORG.TODO/elf/tst-env-setuid-tunables.c new file mode 100644 index 0000000000..afcb146e6d --- /dev/null +++ b/REORG.TODO/elf/tst-env-setuid-tunables.c @@ -0,0 +1,75 @@ +/* Copyright (C) 2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* Verify that tunables correctly filter out unsafe tunables like + glibc.malloc.check and glibc.malloc.mmap_threshold but also retain + glibc.malloc.mmap_threshold in an unprivileged child. */ + +/* This is compiled as part of the testsuite but needs to see + HAVE_TUNABLES. */ +#define _LIBC 1 +#include "config.h" +#undef _LIBC + +#define test_parent test_parent_tunables +#define test_child test_child_tunables + +static int test_child_tunables (void); +static int test_parent_tunables (void); + +#include "tst-env-setuid.c" + +#define CHILD_VALSTRING_VALUE "glibc.malloc.mmap_threshold=4096" +#define PARENT_VALSTRING_VALUE \ + "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096" + +static int +test_child_tunables (void) +{ + const char *val = getenv ("GLIBC_TUNABLES"); + +#if HAVE_TUNABLES + if (val != NULL && strcmp (val, CHILD_VALSTRING_VALUE) == 0) + return 0; + + if (val != NULL) + printf ("Unexpected GLIBC_TUNABLES VALUE %s\n", val); + + return 1; +#else + if (val != NULL) + { + printf ("GLIBC_TUNABLES not cleared\n"); + return 1; + } + return 0; +#endif +} + +static int +test_parent_tunables (void) +{ + const char *val = getenv ("GLIBC_TUNABLES"); + + if (val != NULL && strcmp (val, PARENT_VALSTRING_VALUE) == 0) + return 0; + + if (val != NULL) + printf ("Unexpected GLIBC_TUNABLES VALUE %s\n", val); + + return 1; +} diff --git a/REORG.TODO/elf/tst-env-setuid.c b/REORG.TODO/elf/tst-env-setuid.c new file mode 100644 index 0000000000..eec408eb5d --- /dev/null +++ b/REORG.TODO/elf/tst-env-setuid.c @@ -0,0 +1,296 @@ +/* Copyright (C) 2012-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* Verify that tunables correctly filter out unsafe environment variables like + MALLOC_CHECK_ and MALLOC_MMAP_THRESHOLD_ but also retain + MALLOC_MMAP_THRESHOLD_ in an unprivileged child. */ + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <support/support.h> +#include <support/test-driver.h> + +static char SETGID_CHILD[] = "setgid-child"; +#define CHILD_STATUS 42 + +/* Return a GID which is not our current GID, but is present in the + supplementary group list. */ +static gid_t +choose_gid (void) +{ + const int count = 64; + gid_t groups[count]; + int ret = getgroups (count, groups); + if (ret < 0) + { + printf ("getgroups: %m\n"); + exit (1); + } + gid_t current = getgid (); + for (int i = 0; i < ret; ++i) + { + if (groups[i] != current) + return groups[i]; + } + return 0; +} + +/* Spawn and execute a program and verify that it returns the CHILD_STATUS. */ +static pid_t +do_execve (char **args) +{ + pid_t kid = vfork (); + + if (kid < 0) + { + printf ("vfork: %m\n"); + return -1; + } + + if (kid == 0) + { + /* Child process. */ + execve (args[0], args, environ); + _exit (-errno); + } + + if (kid < 0) + return 1; + + int status; + + if (waitpid (kid, &status, 0) < 0) + { + printf ("waitpid: %m\n"); + return 1; + } + + if (WEXITSTATUS (status) == EXIT_UNSUPPORTED) + return EXIT_UNSUPPORTED; + + if (!WIFEXITED (status) || WEXITSTATUS (status) != CHILD_STATUS) + { + printf ("Unexpected exit status %d from child process\n", + WEXITSTATUS (status)); + return 1; + } + return 0; +} + +/* Copies the executable into a restricted directory, so that we can + safely make it SGID with the TARGET group ID. Then runs the + executable. */ +static int +run_executable_sgid (gid_t target) +{ + char *dirname = xasprintf ("%s/tst-tunables-setuid.%jd", + test_dir, (intmax_t) getpid ()); + char *execname = xasprintf ("%s/bin", dirname); + int infd = -1; + int outfd = -1; + int ret = 0; + if (mkdir (dirname, 0700) < 0) + { + printf ("mkdir: %m\n"); + goto err; + } + infd = open ("/proc/self/exe", O_RDONLY); + if (infd < 0) + { + printf ("open (/proc/self/exe): %m\n"); + goto err; + } + outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700); + if (outfd < 0) + { + printf ("open (%s): %m\n", execname); + goto err; + } + char buf[4096]; + for (;;) + { + ssize_t rdcount = read (infd, buf, sizeof (buf)); + if (rdcount < 0) + { + printf ("read: %m\n"); + goto err; + } + if (rdcount == 0) + break; + char *p = buf; + char *end = buf + rdcount; + while (p != end) + { + ssize_t wrcount = write (outfd, buf, end - p); + if (wrcount == 0) + errno = ENOSPC; + if (wrcount <= 0) + { + printf ("write: %m\n"); + goto err; + } + p += wrcount; + } + } + if (fchown (outfd, getuid (), target) < 0) + { + printf ("fchown (%s): %m\n", execname); + goto err; + } + if (fchmod (outfd, 02750) < 0) + { + printf ("fchmod (%s): %m\n", execname); + goto err; + } + if (close (outfd) < 0) + { + printf ("close (outfd): %m\n"); + goto err; + } + if (close (infd) < 0) + { + printf ("close (infd): %m\n"); + goto err; + } + + char *args[] = {execname, SETGID_CHILD, NULL}; + + ret = do_execve (args); + +err: + if (outfd >= 0) + close (outfd); + if (infd >= 0) + close (infd); + if (execname) + { + unlink (execname); + free (execname); + } + if (dirname) + { + rmdir (dirname); + free (dirname); + } + return ret; +} + +#ifndef test_child +static int +test_child (void) +{ + if (getenv ("MALLOC_CHECK_") != NULL) + { + printf ("MALLOC_CHECK_ is still set\n"); + return 1; + } + + if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL) + { + printf ("MALLOC_MMAP_THRESHOLD_ lost\n"); + return 1; + } + + if (getenv ("LD_HWCAP_MASK") != NULL) + { + printf ("LD_HWCAP_MASK still set\n"); + return 1; + } + + return 0; +} +#endif + +#ifndef test_parent +static int +test_parent (void) +{ + if (getenv ("MALLOC_CHECK_") == NULL) + { + printf ("MALLOC_CHECK_ lost\n"); + return 1; + } + + if (getenv ("MALLOC_MMAP_THRESHOLD_") == NULL) + { + printf ("MALLOC_MMAP_THRESHOLD_ lost\n"); + return 1; + } + + if (getenv ("LD_HWCAP_MASK") == NULL) + { + printf ("LD_HWCAP_MASK lost\n"); + return 1; + } + + return 0; +} +#endif + +static int +do_test (int argc, char **argv) +{ + /* Setgid child process. */ + if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0) + { + if (getgid () == getegid ()) + { + /* This can happen if the file system is mounted nosuid. */ + fprintf (stderr, "SGID failed: GID and EGID match (%jd)\n", + (intmax_t) getgid ()); + exit (EXIT_UNSUPPORTED); + } + + int ret = test_child (); + + if (ret != 0) + exit (1); + + exit (CHILD_STATUS); + } + else + { + if (test_parent () != 0) + exit (1); + + /* Try running a setgid program. */ + gid_t target = choose_gid (); + if (target == 0) + { + fprintf (stderr, + "Could not find a suitable GID for user %jd, skipping test\n", + (intmax_t) getuid ()); + exit (0); + } + + return run_executable_sgid (target); + } + + /* Something went wrong and our argv was corrupted. */ + _exit (1); +} + +#define TEST_FUNCTION_ARGV do_test +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-execstack-mod.c b/REORG.TODO/elf/tst-execstack-mod.c new file mode 100644 index 0000000000..038e6550b5 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/tst-execstack-needed.c b/REORG.TODO/elf/tst-execstack-needed.c new file mode 100644 index 0000000000..8b794a3d47 --- /dev/null +++ b/REORG.TODO/elf/tst-execstack-needed.c @@ -0,0 +1,34 @@ +/* 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); +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-execstack-prog.c b/REORG.TODO/elf/tst-execstack-prog.c new file mode 100644 index 0000000000..8663153372 --- /dev/null +++ b/REORG.TODO/elf/tst-execstack-prog.c @@ -0,0 +1,33 @@ +/* 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); +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-execstack.c b/REORG.TODO/elf/tst-execstack.c new file mode 100644 index 0000000000..114f341d76 --- /dev/null +++ b/REORG.TODO/elf/tst-execstack.c @@ -0,0 +1,236 @@ +/* Test program for making nonexecutable stacks executable + on load of a DSO that requires executable stacks. */ + +#include <dlfcn.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <error.h> +#include <stackinfo.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 bool allow_execstack = true; + + +static int +do_test (void) +{ + /* Check whether SELinux is enabled and disallows executable stacks. */ + FILE *fp = fopen ("/selinux/enforce", "r"); + if (fp != NULL) + { + char *line = NULL; + size_t linelen = 0; + + bool enabled = false; + ssize_t n = getline (&line, &linelen, fp); + if (n > 0 && line[0] != '0') + enabled = true; + + fclose (fp); + + if (enabled) + { + fp = fopen ("/selinux/booleans/allow_execstack", "r"); + if (fp != NULL) + { + n = getline (&line, &linelen, fp); + if (n > 0 && line[0] == '0') + allow_execstack = false; + } + + fclose (fp); + } + } + + printf ("executable stacks %sallowed\n", allow_execstack ? "" : "not "); + + 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 (); + +#if USE_PTHREADS + void *old_stack_addr, *new_stack_addr; + size_t stack_size; + pthread_t me = pthread_self (); + pthread_attr_t attr; + int ret = 0; + + ret = pthread_getattr_np (me, &attr); + if (ret) + { + printf ("before execstack: pthread_getattr_np returned error: %s\n", + strerror (ret)); + return 1; + } + + ret = pthread_attr_getstack (&attr, &old_stack_addr, &stack_size); + if (ret) + { + printf ("before execstack: pthread_attr_getstack returned error: %s\n", + strerror (ret)); + return 1; + } +# if _STACK_GROWS_DOWN + old_stack_addr += stack_size; +# else + old_stack_addr -= stack_size; +# endif +#endif + + /* 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 allow_execstack; + } + + 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 (); + +#if USE_PTHREADS + ret = pthread_getattr_np (me, &attr); + if (ret) + { + printf ("after execstack: pthread_getattr_np returned error: %s\n", + strerror (ret)); + return 1; + } + + ret = pthread_attr_getstack (&attr, &new_stack_addr, &stack_size); + if (ret) + { + printf ("after execstack: pthread_attr_getstack returned error: %s\n", + strerror (ret)); + return 1; + } + +# if _STACK_GROWS_DOWN + new_stack_addr += stack_size; +# else + new_stack_addr -= stack_size; +# endif + + /* It is possible that the dlopen'd module may have been mmapped just below + the stack. The stack size is taken as MIN(stack rlimit size, end of last + vma) in pthread_getattr_np. If rlimit is set high enough, it is possible + that the size may have changed. A subsequent call to + pthread_attr_getstack returns the size and (bottom - size) as the + stacksize and stackaddr respectively. If the size changes due to the + above, then both stacksize and stackaddr can change, but the stack bottom + should remain the same, which is computed as stackaddr + stacksize. */ + if (old_stack_addr != new_stack_addr) + { + printf ("Stack end changed, old: %p, new: %p\n", + old_stack_addr, new_stack_addr); + return 1; + } + printf ("Stack address remains the same: %p\n", old_stack_addr); +#endif + + /* 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 ((void *) (long int) (! allow_execstack)); +#endif + + return ! allow_execstack; +} + +static void +deeper (void (*f) (void)) +{ + char stack[1100 * 1024]; + memfrob (stack, sizeof stack); + (*f) (); + memfrob (stack, sizeof stack); +} + + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-global1.c b/REORG.TODO/elf/tst-global1.c new file mode 100644 index 0000000000..5dae74eec0 --- /dev/null +++ b/REORG.TODO/elf/tst-global1.c @@ -0,0 +1,38 @@ +#include <dlfcn.h> +#include <stdio.h> + +static int +do_test (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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-gnu2-tls1.c b/REORG.TODO/elf/tst-gnu2-tls1.c new file mode 100644 index 0000000000..b33b60a301 --- /dev/null +++ b/REORG.TODO/elf/tst-gnu2-tls1.c @@ -0,0 +1,51 @@ +/* Test local and global dynamic models for GNU2 TLS. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> + +extern int * get_gd (void); +extern void set_gd (int); +extern int test_gd (int); +extern int * get_ld (void); +extern void set_ld (int); +extern int test_ld (int); + +__thread int gd = 1; + +static int +do_test (void) +{ + int *p; + + p = get_gd (); + set_gd (3); + if (*p != 3 || !test_gd (3)) + abort (); + + p = get_ld (); + set_ld (4); + if (*p != 4 || !test_ld (4)) + abort (); + + printf ("PASS\n"); + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-gnu2-tls1mod.c b/REORG.TODO/elf/tst-gnu2-tls1mod.c new file mode 100644 index 0000000000..fa76ab1222 --- /dev/null +++ b/REORG.TODO/elf/tst-gnu2-tls1mod.c @@ -0,0 +1,56 @@ +/* DSO used by tst-gnu2-tls1. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +static __thread int ld; + +int * +get_ld (void) +{ + return &ld; +} + +void +set_ld (int i) +{ + ld = i; +} + +int +test_ld (int i) +{ + return ld == i; +} +extern __thread int gd; + +int * +get_gd (void) +{ + return &gd; +} + +void +set_gd (int i) +{ + gd = i; +} + +int +test_gd (int i) +{ + return gd == i; +} diff --git a/REORG.TODO/elf/tst-initorder.c b/REORG.TODO/elf/tst-initorder.c new file mode 100644 index 0000000000..9638382104 --- /dev/null +++ b/REORG.TODO/elf/tst-initorder.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int +main( int argc, char *argv[] ) +{ + printf( "main\n" ); +} diff --git a/REORG.TODO/elf/tst-initorder.exp b/REORG.TODO/elf/tst-initorder.exp new file mode 100644 index 0000000000..8718f65765 --- /dev/null +++ b/REORG.TODO/elf/tst-initorder.exp @@ -0,0 +1,13 @@ +start_a1 +start_a2 +start_b1 +start_b2 +start_a3 +start_a4 +main +finish_a4 +finish_a3 +finish_b2 +finish_b1 +finish_a2 +finish_a1 diff --git a/REORG.TODO/elf/tst-initorder2.c b/REORG.TODO/elf/tst-initorder2.c new file mode 100644 index 0000000000..050f9568b8 --- /dev/null +++ b/REORG.TODO/elf/tst-initorder2.c @@ -0,0 +1,20 @@ +#include <stdio.h> + +#ifndef NAME +int +main (void) +{ + puts ("main"); +} +#else +static void __attribute__ ((constructor)) +init (void) +{ + puts ("init: " NAME); +} +static void __attribute__ ((destructor)) +fini (void) +{ + puts ("fini: " NAME); +} +#endif diff --git a/REORG.TODO/elf/tst-initorder2.exp b/REORG.TODO/elf/tst-initorder2.exp new file mode 100644 index 0000000000..5169489b85 --- /dev/null +++ b/REORG.TODO/elf/tst-initorder2.exp @@ -0,0 +1,9 @@ +init: d +init: c +init: b +init: a +main +fini: a +fini: b +fini: c +fini: d diff --git a/REORG.TODO/elf/tst-initordera1.c b/REORG.TODO/elf/tst-initordera1.c new file mode 100644 index 0000000000..f161257142 --- /dev/null +++ b/REORG.TODO/elf/tst-initordera1.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_a1( void ) __attribute__((constructor)); +extern void finish_a1( void ) __attribute__((destructor)); + +void +start_a1( void ) +{ + printf( "start_a1\n" ); +} + +void +finish_a1( void ) +{ + printf( "finish_a1\n" ); +} diff --git a/REORG.TODO/elf/tst-initordera2.c b/REORG.TODO/elf/tst-initordera2.c new file mode 100644 index 0000000000..a5a9b42ff6 --- /dev/null +++ b/REORG.TODO/elf/tst-initordera2.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_a2( void ) __attribute__((constructor)); +extern void finish_a2( void ) __attribute__((destructor)); + +void +start_a2( void ) +{ + printf( "start_a2\n" ); +} + +void +finish_a2( void ) +{ + printf( "finish_a2\n" ); +} diff --git a/REORG.TODO/elf/tst-initordera3.c b/REORG.TODO/elf/tst-initordera3.c new file mode 100644 index 0000000000..1c7f496e9a --- /dev/null +++ b/REORG.TODO/elf/tst-initordera3.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_a3( void ) __attribute__((constructor)); +extern void finish_a3( void ) __attribute__((destructor)); + +void +start_a3( void ) +{ + printf( "start_a3\n" ); +} + +void +finish_a3( void ) +{ + printf( "finish_a3\n" ); +} diff --git a/REORG.TODO/elf/tst-initordera4.c b/REORG.TODO/elf/tst-initordera4.c new file mode 100644 index 0000000000..70b9f5e392 --- /dev/null +++ b/REORG.TODO/elf/tst-initordera4.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_a4( void ) __attribute__((constructor)); +extern void finish_a4( void ) __attribute__((destructor)); + +void +start_a4( void ) +{ + printf( "start_a4\n" ); +} + +void +finish_a4( void ) +{ + printf( "finish_a4\n" ); +} diff --git a/REORG.TODO/elf/tst-initorderb1.c b/REORG.TODO/elf/tst-initorderb1.c new file mode 100644 index 0000000000..993ea3fe30 --- /dev/null +++ b/REORG.TODO/elf/tst-initorderb1.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_b1( void ) __attribute__((constructor)); +extern void finish_b1( void ) __attribute__((destructor)); + +void +start_b1( void ) +{ + printf( "start_b1\n" ); +} + +void +finish_b1( void ) +{ + printf( "finish_b1\n" ); +} diff --git a/REORG.TODO/elf/tst-initorderb2.c b/REORG.TODO/elf/tst-initorderb2.c new file mode 100644 index 0000000000..3334dda0a9 --- /dev/null +++ b/REORG.TODO/elf/tst-initorderb2.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_b2( void ) __attribute__((constructor)); +extern void finish_b2( void ) __attribute__((destructor)); + +void +start_b2( void ) +{ + printf( "start_b2\n" ); +} + +void +finish_b2( void ) +{ + printf( "finish_b2\n" ); +} diff --git a/REORG.TODO/elf/tst-latepthread.c b/REORG.TODO/elf/tst-latepthread.c new file mode 100644 index 0000000000..ca2f82243d --- /dev/null +++ b/REORG.TODO/elf/tst-latepthread.c @@ -0,0 +1,104 @@ +/* Test that loading libpthread does not break ld.so exceptions (bug 16628). + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <dlfcn.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> + +static int +do_test (void) +{ + void *handle = dlopen ("tst-latepthreadmod.so", RTLD_LOCAL | RTLD_LAZY); + if (handle == NULL) + { + printf ("error: dlopen failed: %s\n", dlerror ()); + return 1; + } + void *ptr = dlsym (handle, "trigger_dynlink_failure"); + if (ptr == NULL) + { + printf ("error: dlsym failed: %s\n", dlerror ()); + return 1; + } + int (*func) (void) = ptr; + + /* Run the actual test in a subprocess, to capture the error. */ + int fds[2]; + if (pipe (fds) < 0) + { + printf ("error: pipe: %m\n"); + return 1; + } + pid_t pid = fork (); + if (pid < 0) + { + printf ("error: fork: %m\n"); + return 1; + } + else if (pid == 0) + { + if (dup2 (fds[1], STDERR_FILENO) < 0) + _exit (2); + /* Trigger an abort. */ + func (); + _exit (3); + } + /* NB: This assumes that the abort message is so short that the pipe + does not block. */ + int status; + if (waitpid (pid, &status, 0) < 0) + { + printf ("error: waitpid: %m\n"); + return 1; + } + + /* Check the printed error message. */ + if (close (fds[1]) < 0) + { + printf ("error: close: %m\n"); + return 1; + } + char buf[512]; + /* Leave room for the NUL terminator. */ + ssize_t ret = read (fds[0], buf, sizeof (buf) - 1); + if (ret < 0) + { + printf ("error: read: %m\n"); + return 1; + } + if (ret > 0 && buf[ret - 1] == '\n') + --ret; + buf[ret] = '\0'; + printf ("info: exit status: %d, message: %s\n", status, buf); + if (strstr (buf, "undefined symbol: this_function_is_not_defined") == NULL) + { + printf ("error: message does not contain expected string\n"); + return 1; + } + if (!WIFEXITED (status) || WEXITSTATUS (status) != 127) + { + printf ("error: unexpected process exit status\n"); + return 1; + } + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-latepthreadmod.c b/REORG.TODO/elf/tst-latepthreadmod.c new file mode 100644 index 0000000000..35a82d388b --- /dev/null +++ b/REORG.TODO/elf/tst-latepthreadmod.c @@ -0,0 +1,33 @@ +/* DSO which links against libpthread and triggers a lazy binding. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This file is compiled into a DSO which loads libpthread, but fails + the dynamic linker afterwards. */ + +#include <pthread.h> + +/* Link in libpthread. */ +void *pthread_create_ptr = &pthread_create; + +int this_function_is_not_defined (void); + +int +trigger_dynlink_failure (void) +{ + return this_function_is_not_defined (); +} diff --git a/REORG.TODO/elf/tst-ldconfig-X.sh b/REORG.TODO/elf/tst-ldconfig-X.sh new file mode 100644 index 0000000000..e97ca89946 --- /dev/null +++ b/REORG.TODO/elf/tst-ldconfig-X.sh @@ -0,0 +1,62 @@ +#!/bin/sh +# Test that ldconfig -X does not remove stale symbolic links. +# Copyright (C) 2000-2017 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# <http://www.gnu.org/licenses/>. + +set -ex + +common_objpfx=$1 +test_wrapper_env=$2 +run_program_env=$3 + +testroot="${common_objpfx}elf/bug19610-test-directory" +cleanup () { + rm -rf "$testroot" +} +trap cleanup 0 + +rm -rf "$testroot" +mkdir -p $testroot/lib $testroot/etc + +# Relative symbolic link target. +ln -s libdoesnotexist.so.1.1 $testroot/lib/libdoesnotexist.so.1 + +# Absolute symbolic link target. +ln -s $testroot/opt/sw/lib/libdoesnotexist2.so.1.1 $testroot/lib/ + +errors=0 +check_files () { + for name in libdoesnotexist.so.1 libdoesnotexist2.so.1.1 ; do + path="$testroot/lib/$name" + if test ! -h $path ; then + echo "error: missing file: $path" + errors=1 + fi + done +} + +check_files + +${test_wrapper_env} \ +${run_program_env} \ +${common_objpfx}elf/ldconfig -X -f /dev/null \ + -C $testroot/etc/ld.so.cache \ + $testroot/lib + +check_files + +exit $errors diff --git a/REORG.TODO/elf/tst-leaks1-static.c b/REORG.TODO/elf/tst-leaks1-static.c new file mode 100644 index 0000000000..b956d66905 --- /dev/null +++ b/REORG.TODO/elf/tst-leaks1-static.c @@ -0,0 +1 @@ +#include "tst-leaks1.c" diff --git a/REORG.TODO/elf/tst-leaks1.c b/REORG.TODO/elf/tst-leaks1.c new file mode 100644 index 0000000000..d67e8269c4 --- /dev/null +++ b/REORG.TODO/elf/tst-leaks1.c @@ -0,0 +1,27 @@ +#include <stdio.h> +#include <dlfcn.h> +#include <mcheck.h> +#include <stdlib.h> + +static int +do_test (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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-linkall-static.c b/REORG.TODO/elf/tst-linkall-static.c new file mode 100644 index 0000000000..8f40657244 --- /dev/null +++ b/REORG.TODO/elf/tst-linkall-static.c @@ -0,0 +1,52 @@ +/* Test static linking against multiple libraries, to find symbol conflicts. + Copyright (C) 2016-2017 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; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <math.h> +#include <pthread.h> +#include <crypt.h> +#include <resolv.h> +#include <dlfcn.h> +#include <utmp.h> +#include <aio.h> +#include <netdb.h> + +/* These references force linking the executable against central + functions in the static libraries, pulling significant parts of + each library into the link. */ +void *references[] = + { + &pow, /* libm */ + &pthread_create, /* libpthread */ +#if USE_CRYPT + &crypt, /* libcrypt */ +#endif + &res_send, /* libresolv */ + &dlopen, /* libdl */ + &login, /* libutil */ + &aio_init, /* librt */ + &getaddrinfo_a, /* libanl */ + }; + +static int +do_test (void) +{ + /* This is a link-time test. There is nothing to run here. */ + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-nodelete-dlclose-dso.c b/REORG.TODO/elf/tst-nodelete-dlclose-dso.c new file mode 100644 index 0000000000..4042c78b8a --- /dev/null +++ b/REORG.TODO/elf/tst-nodelete-dlclose-dso.c @@ -0,0 +1,90 @@ +/* Bug 11941: Improper assert map->l_init_called in dlclose. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This is the primary DSO that is loaded by the appliation. This DSO + then loads a plugin with RTLD_NODELETE. This plugin depends on this + DSO. This dependency chain means that at application shutdown the + plugin will be destructed first. Thus by the time this DSO is + destructed we will be calling dlclose on an object that has already + been destructed. It is allowed to call dlclose in this way and + should not assert. */ +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> + +/* Plugin to load. */ +static void *plugin_lib = NULL; +/* Plugin function. */ +static void (*plugin_func) (void); +#define LIB_PLUGIN "tst-nodelete-dlclose-plugin.so" + +/* This function is never called but the plugin references it. + We do this to avoid any future --as-needed from removing the + plugin's DT_NEEDED on this DSO (required for the test). */ +void +primary_reference (void) +{ + printf ("INFO: Called primary_reference function.\n"); +} + +void +primary (void) +{ + char *error; + + plugin_lib = dlopen (LIB_PLUGIN, RTLD_NOW | RTLD_LOCAL | RTLD_NODELETE); + if (plugin_lib == NULL) + { + printf ("ERROR: Unable to load plugin library.\n"); + exit (EXIT_FAILURE); + } + dlerror (); + + plugin_func = (void (*) (void)) dlsym (plugin_lib, "plugin_func"); + error = dlerror (); + if (error != NULL) + { + printf ("ERROR: Unable to find symbol with error \"%s\".", + error); + exit (EXIT_FAILURE); + } + + return; +} + +__attribute__ ((destructor)) +static void +primary_dtor (void) +{ + int ret; + + printf ("INFO: Calling primary destructor.\n"); + + /* The destructor runs in the test driver also, which + hasn't called primary, in that case do nothing. */ + if (plugin_lib == NULL) + return; + + ret = dlclose (plugin_lib); + if (ret != 0) + { + printf ("ERROR: Calling dlclose failed with \"%s\"\n", + dlerror ()); + exit (EXIT_FAILURE); + } +} diff --git a/REORG.TODO/elf/tst-nodelete-dlclose-plugin.c b/REORG.TODO/elf/tst-nodelete-dlclose-plugin.c new file mode 100644 index 0000000000..00c84d0bc0 --- /dev/null +++ b/REORG.TODO/elf/tst-nodelete-dlclose-plugin.c @@ -0,0 +1,40 @@ +/* Bug 11941: Improper assert map->l_init_called in dlclose. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This DSO simulates a plugin with a dependency on the + primary DSO loaded by the appliation. */ +#include <stdio.h> + +extern void primary_reference (void); + +void +plugin_func (void) +{ + printf ("INFO: Calling plugin function.\n"); + /* Need a reference to the DSO to ensure that a potential --as-needed + doesn't remove the DT_NEEDED entry which we rely upon to ensure + destruction ordering. */ + primary_reference (); +} + +__attribute__ ((destructor)) +static void +plugin_dtor (void) +{ + printf ("INFO: Calling plugin destructor.\n"); +} diff --git a/REORG.TODO/elf/tst-nodelete-dlclose.c b/REORG.TODO/elf/tst-nodelete-dlclose.c new file mode 100644 index 0000000000..178673e9d0 --- /dev/null +++ b/REORG.TODO/elf/tst-nodelete-dlclose.c @@ -0,0 +1,35 @@ +/* Bug 11941: Improper assert map->l_init_called in dlclose. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This simulates an application using the primary DSO which loads the + plugin DSO. */ +#include <stdio.h> +#include <stdlib.h> + +extern void primary (void); + +static int +do_test (void) +{ + printf ("INFO: Starting application.\n"); + primary (); + printf ("INFO: Exiting application.\n"); + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-nodelete-opened-lib.c b/REORG.TODO/elf/tst-nodelete-opened-lib.c new file mode 100644 index 0000000000..3e1dcdfc1b --- /dev/null +++ b/REORG.TODO/elf/tst-nodelete-opened-lib.c @@ -0,0 +1,19 @@ +/* Verify that objects opened with RTLD_NODELETE are not unloaded - the DSO. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +int foo_var = 42; diff --git a/REORG.TODO/elf/tst-nodelete-opened.c b/REORG.TODO/elf/tst-nodelete-opened.c new file mode 100644 index 0000000000..d71efa4603 --- /dev/null +++ b/REORG.TODO/elf/tst-nodelete-opened.c @@ -0,0 +1,68 @@ +/* Verify that an already opened DSO opened agained with RTLD_NODELETE actually + sets the NODELETE flag. + + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <dlfcn.h> +#include <stdio.h> + +int +do_test (void) +{ + void *h1 = dlopen ("$ORIGIN/tst-nodelete-opened-lib.so", RTLD_LAZY); + if (h1 == NULL) + { + printf ("h1: failed to open DSO: %s\n", dlerror ()); + return 1; + } + + void *h2 = dlopen ("$ORIGIN/tst-nodelete-opened-lib.so", + RTLD_LAZY | RTLD_NODELETE); + if (h2 == NULL) + { + printf ("h2: failed to open DSO: %s\n", dlerror ()); + return 1; + } + + int *foo = dlsym (h2, "foo_var"); + if (foo == NULL) + { + printf ("failed to load symbol foo_var: %s\n", dlerror ()); + return 1; + } + + if (dlclose (h1) != 0) + { + printf ("h1: dlclose failed: %s\n", dlerror ()); + return 1; + } + + if (dlclose (h2) != 0) + { + printf ("h2: dlclose failed: %s\n", dlerror ()); + return 1; + } + + /* This FOO dereference will crash with a segfault if the DSO was + unloaded. */ + printf ("foo == %d\n", *foo); + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-nodelete-rtldmod.cc b/REORG.TODO/elf/tst-nodelete-rtldmod.cc new file mode 100644 index 0000000000..740e1d8181 --- /dev/null +++ b/REORG.TODO/elf/tst-nodelete-rtldmod.cc @@ -0,0 +1,6 @@ +extern int not_exist (void); + +int foo (void) +{ + return not_exist (); +} diff --git a/REORG.TODO/elf/tst-nodelete-uniquemod.cc b/REORG.TODO/elf/tst-nodelete-uniquemod.cc new file mode 100644 index 0000000000..632b303d58 --- /dev/null +++ b/REORG.TODO/elf/tst-nodelete-uniquemod.cc @@ -0,0 +1,14 @@ +extern int not_exist (void); + +inline int make_unique (void) +{ + /* Static variables in inline functions and classes + generate STB_GNU_UNIQUE symbols. */ + static int unique; + return ++unique; +} + +int foo (void) +{ + return make_unique () + not_exist (); +} diff --git a/REORG.TODO/elf/tst-nodelete-zmod.cc b/REORG.TODO/elf/tst-nodelete-zmod.cc new file mode 100644 index 0000000000..740e1d8181 --- /dev/null +++ b/REORG.TODO/elf/tst-nodelete-zmod.cc @@ -0,0 +1,6 @@ +extern int not_exist (void); + +int foo (void) +{ + return not_exist (); +} diff --git a/REORG.TODO/elf/tst-nodelete.cc b/REORG.TODO/elf/tst-nodelete.cc new file mode 100644 index 0000000000..5752e7df26 --- /dev/null +++ b/REORG.TODO/elf/tst-nodelete.cc @@ -0,0 +1,50 @@ +#include "../dlfcn/dlfcn.h" +#include <stdio.h> +#include <stdlib.h> + +static int +do_test (void) +{ + int result = 0; + + /* This is a test for correct handling of dlopen failures for library that + is loaded with RTLD_NODELETE flag. The first dlopen should fail because + of undefined symbols in shared library. The second dlopen then verifies + that library was properly unloaded. */ + if (dlopen ("tst-nodelete-rtldmod.so", RTLD_NOW | RTLD_NODELETE) != NULL + || dlopen ("tst-nodelete-rtldmod.so", RTLD_LAZY | RTLD_NOLOAD) != NULL) + { + printf ("RTLD_NODELETE test failed\n"); + result = 1; + } + + /* This is a test for correct handling of dlopen failures for library that + is linked with '-z nodelete' option and hence has DF_1_NODELETE flag. + The first dlopen should fail because of undefined symbols in shared + library. The second dlopen then verifies that library was properly + unloaded. */ + if (dlopen ("tst-nodelete-zmod.so", RTLD_NOW) != NULL + || dlopen ("tst-nodelete-zmod.so", RTLD_LAZY | RTLD_NOLOAD) != NULL) + { + printf ("-z nodelete test failed\n"); + result = 1; + } + + /* This is a test for correct handling of dlopen failures for library + with unique symbols. The first dlopen should fail because of undefined + symbols in shared library. The second dlopen then verifies that library + was properly unloaded. */ + if (dlopen ("tst-nodelete-uniquemod.so", RTLD_NOW) != NULL + || dlopen ("tst-nodelete-uniquemod.so", RTLD_LAZY | RTLD_NOLOAD) != NULL) + { + printf ("Unique symbols test failed\n"); + result = 1; + } + + if (result == 0) + printf ("SUCCESS\n"); + + return result; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-nodelete2.c b/REORG.TODO/elf/tst-nodelete2.c new file mode 100644 index 0000000000..010c4ae237 --- /dev/null +++ b/REORG.TODO/elf/tst-nodelete2.c @@ -0,0 +1,36 @@ +#include "../dlfcn/dlfcn.h" +#include <stdio.h> +#include <stdlib.h> +#include <gnu/lib-names.h> + +static int +do_test (void) +{ + int result = 0; + + printf ("\nOpening pthread library.\n"); + void *pthread = dlopen (LIBPTHREAD_SO, RTLD_LAZY); + + /* This is a test for correct DF_1_NODELETE clearing when dlopen failure + happens. We should clear DF_1_NODELETE for failed library only, because + doing this for others (e.g. libpthread) might cause them to be unloaded, + that may lead to some global references (e.g. __rtld_lock_unlock) to be + broken. The dlopen should fail because of undefined symbols in shared + library, that cause DF_1_NODELETE to be cleared. For libpthread, this + flag should be set, because if not, SIGSEGV will happen in dlclose. */ + if (dlopen ("tst-nodelete2mod.so", RTLD_NOW) != NULL) + { + printf ("Unique symbols test failed\n"); + result = 1; + } + + if (pthread) + dlclose (pthread); + + if (result == 0) + printf ("SUCCESS\n"); + + return result; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-nodelete2mod.c b/REORG.TODO/elf/tst-nodelete2mod.c new file mode 100644 index 0000000000..e88c756f5e --- /dev/null +++ b/REORG.TODO/elf/tst-nodelete2mod.c @@ -0,0 +1,7 @@ +/* Undefined symbol. */ +extern int not_exist (void); + +int foo (void) +{ + return not_exist (); +} diff --git a/REORG.TODO/elf/tst-noload.c b/REORG.TODO/elf/tst-noload.c new file mode 100644 index 0000000000..3fb2895e2c --- /dev/null +++ b/REORG.TODO/elf/tst-noload.c @@ -0,0 +1,72 @@ +/* Verify that RTLD_NOLOAD works as expected. + + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <dlfcn.h> +#include <stdio.h> +#include <gnu/lib-names.h> + +static int +do_test (void) +{ + /* Test that no object is loaded with RTLD_NOLOAD. */ + void *h1 = dlopen (LIBM_SO, RTLD_LAZY | RTLD_NOLOAD); + if (h1 != NULL) + { + printf ("h1: DSO has been loaded while it should have not\n"); + return 1; + } + + /* This used to segfault in some glibc versions. */ + void *h2 = dlopen (LIBM_SO, RTLD_LAZY | RTLD_NOLOAD | RTLD_NODELETE); + if (h2 != NULL) + { + printf ("h2: DSO has been loaded while it should have not\n"); + return 1; + } + + /* Test that loading an already loaded object returns the same. */ + void *h3 = dlopen (LIBM_SO, RTLD_LAZY); + if (h3 == NULL) + { + printf ("h3: failed to open DSO: %s\n", dlerror ()); + return 1; + } + void *h4 = dlopen (LIBM_SO, RTLD_LAZY | RTLD_NOLOAD); + if (h4 == NULL) + { + printf ("h4: failed to open DSO: %s\n", dlerror ()); + return 1; + } + if (h4 != h3) + { + printf ("h4: should return the same object\n"); + return 1; + } + + /* Cleanup */ + if (dlclose (h3) != 0) + { + printf ("h3: dlclose failed: %s\n", dlerror ()); + return 1; + } + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-null-argv-lib.c b/REORG.TODO/elf/tst-null-argv-lib.c new file mode 100644 index 0000000000..12af03ba95 --- /dev/null +++ b/REORG.TODO/elf/tst-null-argv-lib.c @@ -0,0 +1,24 @@ +/* Verify that program does not crash when LD_DEBUG is set and the program name + is not available. This is the library. + Copyright (C) 2013-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +void +foo (void) +{ + return; +} diff --git a/REORG.TODO/elf/tst-null-argv.c b/REORG.TODO/elf/tst-null-argv.c new file mode 100644 index 0000000000..21b87327c1 --- /dev/null +++ b/REORG.TODO/elf/tst-null-argv.c @@ -0,0 +1,36 @@ +/* Verify that program does not crash when LD_DEBUG is set and the program name + is not available. + Copyright (C) 2013-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +extern void foo (void); + +int +do_test (int argc, char **argv) +{ + argv[0] = argv[1]; + argc--; + + /* This should result in a symbol lookup, causing a volley of debug output + when LD_DEBUG=symbols. */ + foo (); + + return 0; +} + +#define TEST_FUNCTION_ARGV do_test +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-order-a1.c b/REORG.TODO/elf/tst-order-a1.c new file mode 100644 index 0000000000..f161257142 --- /dev/null +++ b/REORG.TODO/elf/tst-order-a1.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_a1( void ) __attribute__((constructor)); +extern void finish_a1( void ) __attribute__((destructor)); + +void +start_a1( void ) +{ + printf( "start_a1\n" ); +} + +void +finish_a1( void ) +{ + printf( "finish_a1\n" ); +} diff --git a/REORG.TODO/elf/tst-order-a2.c b/REORG.TODO/elf/tst-order-a2.c new file mode 100644 index 0000000000..a5a9b42ff6 --- /dev/null +++ b/REORG.TODO/elf/tst-order-a2.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_a2( void ) __attribute__((constructor)); +extern void finish_a2( void ) __attribute__((destructor)); + +void +start_a2( void ) +{ + printf( "start_a2\n" ); +} + +void +finish_a2( void ) +{ + printf( "finish_a2\n" ); +} diff --git a/REORG.TODO/elf/tst-order-a3.c b/REORG.TODO/elf/tst-order-a3.c new file mode 100644 index 0000000000..1c7f496e9a --- /dev/null +++ b/REORG.TODO/elf/tst-order-a3.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_a3( void ) __attribute__((constructor)); +extern void finish_a3( void ) __attribute__((destructor)); + +void +start_a3( void ) +{ + printf( "start_a3\n" ); +} + +void +finish_a3( void ) +{ + printf( "finish_a3\n" ); +} diff --git a/REORG.TODO/elf/tst-order-a4.c b/REORG.TODO/elf/tst-order-a4.c new file mode 100644 index 0000000000..70b9f5e392 --- /dev/null +++ b/REORG.TODO/elf/tst-order-a4.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_a4( void ) __attribute__((constructor)); +extern void finish_a4( void ) __attribute__((destructor)); + +void +start_a4( void ) +{ + printf( "start_a4\n" ); +} + +void +finish_a4( void ) +{ + printf( "finish_a4\n" ); +} diff --git a/REORG.TODO/elf/tst-order-b1.c b/REORG.TODO/elf/tst-order-b1.c new file mode 100644 index 0000000000..993ea3fe30 --- /dev/null +++ b/REORG.TODO/elf/tst-order-b1.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_b1( void ) __attribute__((constructor)); +extern void finish_b1( void ) __attribute__((destructor)); + +void +start_b1( void ) +{ + printf( "start_b1\n" ); +} + +void +finish_b1( void ) +{ + printf( "finish_b1\n" ); +} diff --git a/REORG.TODO/elf/tst-order-b2.c b/REORG.TODO/elf/tst-order-b2.c new file mode 100644 index 0000000000..3334dda0a9 --- /dev/null +++ b/REORG.TODO/elf/tst-order-b2.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern void start_b2( void ) __attribute__((constructor)); +extern void finish_b2( void ) __attribute__((destructor)); + +void +start_b2( void ) +{ + printf( "start_b2\n" ); +} + +void +finish_b2( void ) +{ + printf( "finish_b2\n" ); +} diff --git a/REORG.TODO/elf/tst-order-main.c b/REORG.TODO/elf/tst-order-main.c new file mode 100644 index 0000000000..2a90130db6 --- /dev/null +++ b/REORG.TODO/elf/tst-order-main.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +static int +do_test (void) +{ + printf( "main\n" ); + exit(EXIT_SUCCESS); +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-pathopt.c b/REORG.TODO/elf/tst-pathopt.c new file mode 100644 index 0000000000..e2c96fbc72 --- /dev/null +++ b/REORG.TODO/elf/tst-pathopt.c @@ -0,0 +1,41 @@ +#include <dlfcn.h> +#include <mcheck.h> +#include <stdio.h> +#include <stdlib.h> + + +static int +do_test (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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-pathopt.sh b/REORG.TODO/elf/tst-pathopt.sh new file mode 100755 index 0000000000..4183a697dc --- /dev/null +++ b/REORG.TODO/elf/tst-pathopt.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# Test lookup path optimization. +# Copyright (C) 2000-2017 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# <http://www.gnu.org/licenses/>. + +set -e + +common_objpfx=$1 +test_wrapper_env=$2 +run_program_env=$3 + +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 + +${test_wrapper_env} \ +${run_program_env} \ +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/REORG.TODO/elf/tst-pie1.c b/REORG.TODO/elf/tst-pie1.c new file mode 100644 index 0000000000..75d941f21f --- /dev/null +++ b/REORG.TODO/elf/tst-pie1.c @@ -0,0 +1,5 @@ +int +foo (void) +{ + return 34; +} diff --git a/REORG.TODO/elf/tst-pie2.c b/REORG.TODO/elf/tst-pie2.c new file mode 100644 index 0000000000..32943bbc1a --- /dev/null +++ b/REORG.TODO/elf/tst-pie2.c @@ -0,0 +1,40 @@ +/* Test case for BZ #16381 + + Copyright (C) 2014-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + + +#include <assert.h> + +static int g; + +void init_g (void) __attribute__((constructor)); + +void +init_g (void) +{ + assert (g == 0); + g += 1; +} + +static int +do_test (void) +{ + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-piemod1.c b/REORG.TODO/elf/tst-piemod1.c new file mode 100644 index 0000000000..72d7e0a187 --- /dev/null +++ b/REORG.TODO/elf/tst-piemod1.c @@ -0,0 +1,22 @@ +#include <stdio.h> + +int +foo (void) +{ + return 21; +} + +static int +do_test (void) +{ + int val = foo (); + if (val != 34) + { + printf ("foo () returned %d\n", val); + return 1; + } + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-prelink.c b/REORG.TODO/elf/tst-prelink.c new file mode 100644 index 0000000000..7435c321d3 --- /dev/null +++ b/REORG.TODO/elf/tst-prelink.c @@ -0,0 +1,29 @@ +/* Test the output from the environment variable, LD_TRACE_PRELINKING, + for prelink. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> + +static int +do_test (void) +{ + fprintf (stdout, "hello\n"); + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-prelink.exp b/REORG.TODO/elf/tst-prelink.exp new file mode 100644 index 0000000000..b35b4c9705 --- /dev/null +++ b/REORG.TODO/elf/tst-prelink.exp @@ -0,0 +1 @@ +/0 stdout diff --git a/REORG.TODO/elf/tst-protected1a.c b/REORG.TODO/elf/tst-protected1a.c new file mode 100644 index 0000000000..4267b951c4 --- /dev/null +++ b/REORG.TODO/elf/tst-protected1a.c @@ -0,0 +1,234 @@ +/* Test the protected visibility when main is linked with moda and modb + in that order: + 1. Protected symbols, protected1, protected2 and protected3, defined + in moda, are used in moda. + 2. Protected symbol, protected3, defined in modb, are used in modb. + 3. Symbol, protected1, defined in moda, is also used in main and modb. + 4. Symbol, protected2, defined in main, is used in main. + 5. Symbol, protected3, defined in moda, is also used in main. + + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This file must be compiled as PIE to avoid copy relocation when + accessing protected symbols defined in shared libaries since copy + relocation doesn't work with protected symbols and linker in + binutils 2.26 enforces this rule. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "tst-protected1mod.h" + +/* Prototype for our test function. */ +extern int do_test (void); + +int protected2 = -1; + +/* This defines the `main' function and some more. */ +#include <support/test-driver.c> + +int +do_test (void) +{ + int res = 0; + + /* Check if we get the same address for the protected data symbol. */ + if (&protected1 != protected1a_p ()) + { + puts ("`protected1' in main and moda doesn't have same address"); + res = 1; + } + if (&protected1 != protected1b_p ()) + { + puts ("`protected1' in main and modb doesn't have same address"); + res = 1; + } + + /* Check if we get the right value for the protected data symbol. */ + if (protected1 != 3) + { + puts ("`protected1' in main and moda doesn't have same value"); + res = 1; + } + + /* Check if we get the right value for data defined in executable. */ + if (protected2 != -1) + { + puts ("`protected2' in main has the wrong value"); + res = 1; + } + + /* Check `protected1' in moda. */ + if (!check_protected1 ()) + { + puts ("`protected1' in moda has the wrong value"); + res = 1; + } + + /* Check `protected2' in moda. */ + if (!check_protected2 ()) + { + puts ("`protected2' in moda has the wrong value"); + res = 1; + } + + /* Check if we get the same address for the protected data symbol. */ + if (&protected3 != protected3a_p ()) + { + puts ("`protected3' in main and moda doesn't have same address"); + res = 1; + } + if (&protected3 == protected3b_p ()) + { + puts ("`protected3' in main and modb has same address"); + res = 1; + } + + /* Check if we get the right value for the protected data symbol. */ + if (protected3 != 5) + { + puts ("`protected3' in main and moda doesn't have same value"); + res = 1; + } + + /* Check `protected3' in moda. */ + if (!check_protected3a ()) + { + puts ("`protected3' in moda has the wrong value"); + res = 1; + } + + /* Check `protected3' in modb. */ + if (!check_protected3b ()) + { + puts ("`protected3' in modb has the wrong value"); + res = 1; + } + + /* Set `protected2' in moda to 30. */ + set_protected2 (300); + + /* Check `protected2' in moda. */ + if (!check_protected2 ()) + { + puts ("`protected2' in moda has the wrong value"); + res = 1; + } + + /* Set `protected1' in moda to 30. */ + set_protected1a (30); + + /* Check `protected1' in moda. */ + if (!check_protected1 ()) + { + puts ("`protected1' in moda has the wrong value"); + res = 1; + } + + /* Check if we get the updated value for the protected data symbol. */ + if (protected1 != 30) + { + puts ("`protected1' in main doesn't have the updated value"); + res = 1; + } + + protected2 = -300; + + /* Check `protected2' in moda. */ + if (!check_protected2 ()) + { + puts ("`protected2' in moda has the wrong value"); + res = 1; + } + + /* Check if data defined in executable is changed. */ + if (protected2 != -300) + { + puts ("`protected2' in main is changed"); + res = 1; + } + + /* Set `protected1' in modb to 40. */ + set_protected1b (40); + set_expected_protected1 (40); + + /* Check `protected1' in moda. */ + if (!check_protected1 ()) + { + puts ("`protected1' in moda has the wrong value"); + res = 1; + } + + /* Check if we get the updated value for the protected data symbol. */ + if (protected1 != 40) + { + puts ("`protected1' in main doesn't have the updated value"); + res = 1; + } + + /* Set `protected3' in moda to 80. */ + set_protected3a (80); + + /* Check `protected3' in moda. */ + if (!check_protected3a ()) + { + puts ("`protected3' in moda has the wrong value"); + res = 1; + } + + /* Check if we get the updated value for the protected data symbol. */ + if (protected3 != 80) + { + puts ("`protected3' in main doesn't have the updated value"); + res = 1; + } + + /* Check `protected3' in modb. */ + if (!check_protected3b ()) + { + puts ("`protected3' in modb has the wrong value"); + res = 1; + } + + /* Set `protected3' in modb to 100. */ + set_protected3b (100); + + /* Check `protected3' in moda. */ + if (!check_protected3a ()) + { + puts ("`protected3' in moda has the wrong value"); + res = 1; + } + + /* Check if we get the updated value for the protected data symbol. */ + if (protected3 != 80) + { + puts ("`protected3' in main doesn't have the updated value"); + res = 1; + } + + /* Check `protected3' in modb. */ + if (!check_protected3b ()) + { + puts ("`protected3' in modb has the wrong value"); + res = 1; + } + + return res; +} diff --git a/REORG.TODO/elf/tst-protected1b.c b/REORG.TODO/elf/tst-protected1b.c new file mode 100644 index 0000000000..9fd695bffa --- /dev/null +++ b/REORG.TODO/elf/tst-protected1b.c @@ -0,0 +1,240 @@ +/* Test the protected visibility when main is linked with modb and moda + in that order: + 1. Protected symbols, protected1, protected2 and protected3, defined + in moda, are used in moda. + 2. Protected symbol, protected3, defined in modb, are used in modb + 3. Symbol, protected1, defined in modb, is used in main and modb. + 4. Symbol, protected2, defined in main, is used in main. + 5. Symbol, protected3, defined in modb, is also used in main. + + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This file must be compiled as PIE to avoid copy relocation when + accessing protected symbols defined in shared libaries since copy + relocation doesn't work with protected symbols and linker in + binutils 2.26 enforces this rule. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "tst-protected1mod.h" + +/* Prototype for our test function. */ +extern int do_test (void); + +int protected2 = -1; + +/* This defines the `main' function and some more. */ +#include <support/test-driver.c> + +int +do_test (void) +{ + int res = 0; + + /* Check if we get the same address for the protected data symbol. */ + if (&protected1 == protected1a_p ()) + { + puts ("`protected1' in main and moda has same address"); + res = 1; + } + if (&protected1 != protected1b_p ()) + { + puts ("`protected1' in main and modb doesn't have same address"); + res = 1; + } + + /* Check if we get the right value for the protected data symbol. */ + if (protected1 != -3) + { + puts ("`protected1' in main and modb doesn't have same value"); + res = 1; + } + + /* Check if we get the right value for data defined in executable. */ + if (protected2 != -1) + { + puts ("`protected2' in main has the wrong value"); + res = 1; + } + + /* Check `protected1' in moda. */ + if (!check_protected1 ()) + { + puts ("`protected1' in moda has the wrong value"); + res = 1; + } + + /* Check `protected2' in moda. */ + if (!check_protected2 ()) + { + puts ("`protected2' in moda has the wrong value"); + res = 1; + } + + /* Check if we get the same address for the protected data symbol. */ + if (&protected3 == protected3a_p ()) + { + puts ("`protected3' in main and moda has same address"); + res = 1; + } + if (&protected3 != protected3b_p ()) + { + puts ("`protected3' in main and modb doesn't have same address"); + res = 1; + } + + /* Check if we get the right value for the protected data symbol. */ + if (protected3 != -5) + { + puts ("`protected3' in main and modb doesn't have same value"); + res = 1; + } + + /* Check `protected3' in moda. */ + if (!check_protected3a ()) + { + puts ("`protected3' in moda has the wrong value"); + res = 1; + } + + /* Check `protected3' in modb. */ + if (!check_protected3b ()) + { + puts ("`protected3' in modb has the wrong value"); + res = 1; + } + + /* Set `protected2' in moda to 30. */ + set_protected2 (300); + + /* Check `protected2' in moda. */ + if (!check_protected2 ()) + { + puts ("`protected2' in moda has the wrong value"); + res = 1; + } + + /* Check if we get the right value for data defined in executable. */ + if (protected2 != -1) + { + puts ("`protected2' in main has the wrong value"); + res = 1; + } + + /* Set `protected1' in moda to 30. */ + set_protected1a (30); + + /* Check `protected1' in moda. */ + if (!check_protected1 ()) + { + puts ("`protected1' in moda has the wrong value"); + res = 1; + } + + /* Check if we get the same value for the protected data symbol. */ + if (protected1 != -3) + { + puts ("`protected1' in main has the wrong value"); + res = 1; + } + + protected2 = -300; + + /* Check `protected2' in moda. */ + if (!check_protected2 ()) + { + puts ("`protected2' in moda has the wrong value"); + res = 1; + } + + /* Check if data defined in executable is changed. */ + if (protected2 != -300) + { + puts ("`protected2' in main is changed"); + res = 1; + } + + /* Set `protected1' in modb to 40. */ + set_protected1b (40); + + /* Check `protected1' in moda. */ + if (!check_protected1 ()) + { + puts ("`protected1' in moda has the wrong value"); + res = 1; + } + + /* Check if we get the updated value for the protected data symbol. */ + if (protected1 != 40) + { + puts ("`protected1' in main doesn't have the updated value"); + res = 1; + } + + /* Set `protected3' in moda to 80. */ + set_protected3a (80); + + /* Check `protected3' in moda. */ + if (!check_protected3a ()) + { + puts ("`protected3' in moda has the wrong value"); + res = 1; + } + + /* Check if we get the updated value for the protected data symbol. */ + if (protected3 != -5) + { + puts ("`protected3' in main doesn't have the updated value"); + res = 1; + } + + /* Check `protected3' in modb. */ + if (!check_protected3b ()) + { + puts ("`protected3' in modb has the wrong value"); + res = 1; + } + + /* Set `protected3' in modb to 100. */ + set_protected3b (100); + + /* Check `protected3' in moda. */ + if (!check_protected3a ()) + { + puts ("`protected3' in moda has the wrong value"); + res = 1; + } + + /* Check if we get the updated value for the protected data symbol. */ + if (protected3 != 100) + { + puts ("`protected3' in main doesn't have the updated value"); + res = 1; + } + + /* Check `protected3' in modb. */ + if (!check_protected3b ()) + { + puts ("`protected3' in modb has the wrong value"); + res = 1; + } + + return res; +} diff --git a/REORG.TODO/elf/tst-protected1mod.h b/REORG.TODO/elf/tst-protected1mod.h new file mode 100644 index 0000000000..a47d65f6b1 --- /dev/null +++ b/REORG.TODO/elf/tst-protected1mod.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* Prototypes for the functions in the DSOs. */ +extern int protected1; +extern int protected2; +extern int protected3; + +extern void set_protected1a (int); +extern void set_protected1b (int); +extern int *protected1a_p (void); +extern int *protected1b_p (void); + +extern void set_expected_protected1 (int); +extern int check_protected1 (void); + +extern void set_protected2 (int); +extern int check_protected2 (void); + +extern void set_expected_protected3a (int); +extern void set_protected3a (int); +extern int check_protected3a (void); +extern int *protected3a_p (void); +extern void set_expected_protected3b (int); +extern void set_protected3b (int); +extern int check_protected3b (void); +extern int *protected3b_p (void); diff --git a/REORG.TODO/elf/tst-protected1moda.c b/REORG.TODO/elf/tst-protected1moda.c new file mode 100644 index 0000000000..aca4fc82a8 --- /dev/null +++ b/REORG.TODO/elf/tst-protected1moda.c @@ -0,0 +1,92 @@ +/* Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include "tst-protected1mod.h" + +int protected1 = 3; +static int expected_protected1 = 3; +int protected2 = 4; +static int expected_protected2 = 4; +int protected3 = 5; +static int expected_protected3 = 5; + +asm (".protected protected1"); +asm (".protected protected2"); +asm (".protected protected3"); + +void +set_protected1a (int i) +{ + protected1 = i; + set_expected_protected1 (i); +} + +void +set_expected_protected1 (int i) +{ + expected_protected1 = i; +} + +int * +protected1a_p (void) +{ + return &protected1; +} + +int +check_protected1 (void) +{ + return protected1 == expected_protected1; +} + +void +set_protected2 (int i) +{ + protected2 = i; + expected_protected2 = i; +} + +int +check_protected2 (void) +{ + return protected2 == expected_protected2; +} + +void +set_expected_protected3a (int i) +{ + expected_protected3 = i; +} + +void +set_protected3a (int i) +{ + protected3 = i; + set_expected_protected3a (i); +} + +int +check_protected3a (void) +{ + return protected3 == expected_protected3; +} + +int * +protected3a_p (void) +{ + return &protected3; +} diff --git a/REORG.TODO/elf/tst-protected1modb.c b/REORG.TODO/elf/tst-protected1modb.c new file mode 100644 index 0000000000..5036b6c3e7 --- /dev/null +++ b/REORG.TODO/elf/tst-protected1modb.c @@ -0,0 +1,62 @@ +/* Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <stdlib.h> +#include "tst-protected1mod.h" + +int protected1 = -3; +int protected3 = -5; +static int expected_protected3 = -5; + +asm (".protected protected3"); + +void +set_protected1b (int i) +{ + protected1 = i; +} + +int * +protected1b_p (void) +{ + return &protected1; +} + +void +set_expected_protected3b (int i) +{ + expected_protected3 = i; +} + +void +set_protected3b (int i) +{ + protected3 = i; + set_expected_protected3b (i); +} + +int +check_protected3b (void) +{ + return protected3 == expected_protected3; +} + +int * +protected3b_p (void) +{ + return &protected3; +} diff --git a/REORG.TODO/elf/tst-ptrguard1-static.c b/REORG.TODO/elf/tst-ptrguard1-static.c new file mode 100644 index 0000000000..7aff3b7b5d --- /dev/null +++ b/REORG.TODO/elf/tst-ptrguard1-static.c @@ -0,0 +1 @@ +#include "tst-ptrguard1.c" diff --git a/REORG.TODO/elf/tst-ptrguard1.c b/REORG.TODO/elf/tst-ptrguard1.c new file mode 100644 index 0000000000..8ea65bb0bb --- /dev/null +++ b/REORG.TODO/elf/tst-ptrguard1.c @@ -0,0 +1,217 @@ +/* Copyright (C) 2013-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> +#include <stackguard-macros.h> +#include <tls.h> +#include <unistd.h> + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +/* Requires _GNU_SOURCE */ +#include <getopt.h> + +#ifndef POINTER_CHK_GUARD +extern uintptr_t __pointer_chk_guard; +# define POINTER_CHK_GUARD __pointer_chk_guard +#endif + +static const char *command; +static bool child; +static uintptr_t ptr_chk_guard_copy; +static bool ptr_chk_guard_copy_set; +static int fds[2]; + +static void __attribute__ ((constructor)) +con (void) +{ + ptr_chk_guard_copy = POINTER_CHK_GUARD; + ptr_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 (!ptr_chk_guard_copy_set) + { + puts ("constructor has not been run"); + return 1; + } + + if (ptr_chk_guard_copy != POINTER_CHK_GUARD) + { + puts ("POINTER_CHK_GUARD changed between constructor and do_test"); + return 1; + } + + if (child) + { + write (2, &ptr_chk_guard_copy, sizeof (ptr_chk_guard_copy)); + return 0; + } + + if (command == NULL) + { + puts ("missing --command or --child argument"); + return 1; + } + +#define N 16 + uintptr_t child_ptr_chk_guards[N + 1]; + child_ptr_chk_guards[N] = ptr_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 (ptr_chk_guard_copy != POINTER_CHK_GUARD) + { + puts ("POINTER_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_ptr_chk_guards[i], + sizeof (uintptr_t))) != sizeof (uintptr_t)) + { + puts ("could not read ptr_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_ptr_chk_guards, N + 1, sizeof (uintptr_t), uintptr_t_cmp); + + /* The default pointer guard is the same as the default stack guard. + They are only set to default if dl_random is NULL. */ + 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 pointer guard canaries are either randomized, + or equal to the default pointer guard value. + Even with randomized pointer 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_ptr_chk_guards[i] != child_ptr_chk_guards[i+1]) + ndifferences++; + else if (child_ptr_chk_guards[i] == default_guard) + ndefaults++; + } + + printf ("differences %d defaults %d\n", ndifferences, ndefaults); + + if (ndifferences < N / 2 && ndefaults < N / 2) + { + puts ("pointer guard values are not randomized enough"); + puts ("nor equal to the default 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 }, + +static void __attribute((used)) +cmdline_process_function (int c) +{ + switch (c) + { + case OPT_COMMAND: + command = optarg; + break; + case OPT_CHILD: + child = true; + break; + } +} + +#define CMDLINE_PROCESS cmdline_process_function + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-relsort1.c b/REORG.TODO/elf/tst-relsort1.c new file mode 100644 index 0000000000..775c968e1f --- /dev/null +++ b/REORG.TODO/elf/tst-relsort1.c @@ -0,0 +1,18 @@ +#include <dlfcn.h> +#include <stdio.h> + + +static int +do_test (void) +{ + const char lib[] = "$ORIGIN/tst-relsort1mod1.so"; + void *h = dlopen (lib, RTLD_NOW); + if (h == NULL) + { + puts (dlerror ()); + return 1; + } + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-relsort1mod1.c b/REORG.TODO/elf/tst-relsort1mod1.c new file mode 100644 index 0000000000..9e4a94321d --- /dev/null +++ b/REORG.TODO/elf/tst-relsort1mod1.c @@ -0,0 +1,7 @@ +extern int foo (double); + +int +bar (void) +{ + return foo (1.2); +} diff --git a/REORG.TODO/elf/tst-relsort1mod2.c b/REORG.TODO/elf/tst-relsort1mod2.c new file mode 100644 index 0000000000..a2c3e551e4 --- /dev/null +++ b/REORG.TODO/elf/tst-relsort1mod2.c @@ -0,0 +1,7 @@ +#include <math.h> + +int +foo (double d) +{ + return floor (d) != 0.0; +} diff --git a/REORG.TODO/elf/tst-rtld-load-self.sh b/REORG.TODO/elf/tst-rtld-load-self.sh new file mode 100755 index 0000000000..e05f6d145f --- /dev/null +++ b/REORG.TODO/elf/tst-rtld-load-self.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# Test how rtld loads itself. +# Copyright (C) 2012-2017 Free Software Foundation, Inc. +# This file is part of the GNU C Library. +# + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# <http://www.gnu.org/licenses/>. + +set -e + +rtld=$1 +test_wrapper=$2 +test_wrapper_env=$3 +result=0 + +echo '# normal mode' +${test_wrapper} $rtld $rtld 2>&1 && rc=0 || rc=$? +echo "# exit status $rc" +test $rc -le 127 || result=1 + +echo '# list mode' +${test_wrapper} $rtld --list $rtld 2>&1 && rc=0 || rc=$? +echo "# exit status $rc" +test $rc -eq 0 || result=1 + +echo '# verify mode' +${test_wrapper} $rtld --verify $rtld 2>&1 && rc=0 || rc=$? +echo "# exit status $rc" +test $rc -eq 2 || result=1 + +echo '# trace mode' +${test_wrapper_env} LD_TRACE_LOADED_OBJECTS=1 \ + $rtld $rtld 2>&1 && rc=0 || rc=$? +echo "# exit status $rc" +test $rc -eq 0 || result=1 + +exit $result diff --git a/REORG.TODO/elf/tst-stackguard1-static.c b/REORG.TODO/elf/tst-stackguard1-static.c new file mode 100644 index 0000000000..db1e21554d --- /dev/null +++ b/REORG.TODO/elf/tst-stackguard1-static.c @@ -0,0 +1 @@ +#include "tst-stackguard1.c" diff --git a/REORG.TODO/elf/tst-stackguard1.c b/REORG.TODO/elf/tst-stackguard1.c new file mode 100644 index 0000000000..78e33c7083 --- /dev/null +++ b/REORG.TODO/elf/tst-stackguard1.c @@ -0,0 +1,205 @@ +/* Copyright (C) 2005-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <getopt.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> +#include <stackguard-macros.h> +#include <tls.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 }, + +static void __attribute__((used)) +cmdline_process_function (int c) +{ + switch (c) + { + case OPT_COMMAND: + command = optarg; + break; + case OPT_CHILD: + child = true; + break; + } +} +#define CMDLINE_PROCESS cmdline_process_function + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-thrlock.c b/REORG.TODO/elf/tst-thrlock.c new file mode 100644 index 0000000000..1beffc3861 --- /dev/null +++ b/REORG.TODO/elf/tst-thrlock.c @@ -0,0 +1,58 @@ +#include <dlfcn.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <gnu/lib-names.h> + +static void * +tf (void *arg) +{ + void *h = dlopen (LIBM_SO, RTLD_LAZY); + if (h == NULL) + { + printf ("dlopen failed: %s\n", dlerror ()); + exit (1); + } + if (dlsym (h, "sin") == NULL) + { + printf ("dlsym failed: %s\n", dlerror ()); + exit (1); + } + if (dlclose (h) != 0) + { + printf ("dlclose failed: %s\n", dlerror ()); + exit (1); + } + return NULL; +} + + +static int +do_test (void) +{ +#define N 10 + pthread_t th[N]; + for (int i = 0; i < N; ++i) + { + int e = pthread_create (&th[i], NULL, tf, NULL); + if (e != 0) + { + printf ("pthread_create failed with %d (%s)\n", e, strerror (e)); + return 1; + } + } + for (int i = 0; i < N; ++i) + { + void *res; + int e = pthread_join (th[i], &res); + if (e != 0 || res != NULL) + { + puts ("thread failed"); + return 1; + } + } + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls-dlinfo.c b/REORG.TODO/elf/tst-tls-dlinfo.c new file mode 100644 index 0000000000..7d2b42e2ab --- /dev/null +++ b/REORG.TODO/elf/tst-tls-dlinfo.c @@ -0,0 +1,85 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + + +static int +do_test (void) +{ + 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; +} + + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls-manydynamic.c b/REORG.TODO/elf/tst-tls-manydynamic.c new file mode 100644 index 0000000000..b072d0be68 --- /dev/null +++ b/REORG.TODO/elf/tst-tls-manydynamic.c @@ -0,0 +1,151 @@ +/* Test with many dynamic TLS variables. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This test intends to exercise dynamic TLS variable allocation. It + achieves this by combining dlopen (to avoid static TLS allocation + after static TLS resizing), many DSOs with a large variable (to + exceed the static TLS reserve), and an already-running thread (to + force full dynamic TLS initialization). */ + +#include "tst-tls-manydynamic.h" + +#include <errno.h> +#include <dlfcn.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static int do_test (void); +#include <support/xthread.h> +#include <support/test-driver.c> + +void *handles[COUNT]; +set_value_func set_value_funcs[COUNT]; +get_value_func get_value_funcs[COUNT]; + +static void +init_functions (void) +{ + for (int i = 0; i < COUNT; ++i) + { + /* Open the module. */ + { + char soname[100]; + snprintf (soname, sizeof (soname), "tst-tls-manydynamic%02dmod.so", i); + handles[i] = dlopen (soname, RTLD_LAZY); + if (handles[i] == NULL) + { + printf ("error: dlopen failed: %s\n", dlerror ()); + exit (1); + } + } + + /* Obtain the setter function. */ + { + char fname[100]; + snprintf (fname, sizeof (fname), "set_value_%02d", i); + void *func = dlsym (handles[i], fname); + if (func == NULL) + { + printf ("error: dlsym: %s\n", dlerror ()); + exit (1); + } + set_value_funcs[i] = func; + } + + /* Obtain the getter function. */ + { + char fname[100]; + snprintf (fname, sizeof (fname), "get_value_%02d", i); + void *func = dlsym (handles[i], fname); + if (func == NULL) + { + printf ("error: dlsym: %s\n", dlerror ()); + exit (1); + } + get_value_funcs[i] = func; + } + } +} + +static pthread_barrier_t barrier; + +/* Running thread which forces real TLS initialization. */ +static void * +blocked_thread_func (void *closure) +{ + xpthread_barrier_wait (&barrier); + + /* TLS test runs here in the main thread. */ + + xpthread_barrier_wait (&barrier); + return NULL; +} + +static int +do_test (void) +{ + { + int ret = pthread_barrier_init (&barrier, NULL, 2); + if (ret != 0) + { + errno = ret; + printf ("error: pthread_barrier_init: %m\n"); + exit (1); + } + } + + pthread_t blocked_thread = xpthread_create (NULL, blocked_thread_func, NULL); + xpthread_barrier_wait (&barrier); + + init_functions (); + + struct value values[COUNT]; + /* Initialze the TLS variables. */ + for (int i = 0; i < COUNT; ++i) + { + for (int j = 0; j < PER_VALUE_COUNT; ++j) + values[i].num[j] = rand (); + set_value_funcs[i] (&values[i]); + } + + /* Read back their values to check that they do not overlap. */ + for (int i = 0; i < COUNT; ++i) + { + struct value actual; + get_value_funcs[i] (&actual); + + for (int j = 0; j < PER_VALUE_COUNT; ++j) + if (actual.num[j] != values[i].num[j]) + { + printf ("error: mismatch at variable %d/%d: %d != %d\n", + i, j, actual.num[j], values[i].num[j]); + exit (1); + } + } + + xpthread_barrier_wait (&barrier); + xpthread_join (blocked_thread); + + /* Close the modules. */ + for (int i = 0; i < COUNT; ++i) + dlclose (handles[i]); + + return 0; +} diff --git a/REORG.TODO/elf/tst-tls-manydynamic.h b/REORG.TODO/elf/tst-tls-manydynamic.h new file mode 100644 index 0000000000..13fac4e7e8 --- /dev/null +++ b/REORG.TODO/elf/tst-tls-manydynamic.h @@ -0,0 +1,44 @@ +/* Interfaces for test with many dynamic TLS variables. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef TST_TLS_MANYDYNAMIC_H +#define TST_TLS_MANYDYNAMIC_H + +enum + { + /* This many TLS variables (and modules) are defined. */ + COUNT = 100, + + /* Number of elements in the TLS variable. */ + PER_VALUE_COUNT = 1, + }; + +/* The TLS variables are of this type. We use a larger type to ensure + that we can reach the static TLS limit with COUNT variables. */ +struct value +{ + int num[PER_VALUE_COUNT]; +}; + +/* Set the TLS variable defined in the module. */ +typedef void (*set_value_func) (const struct value *); + +/* Read the TLS variable defined in the module. */ +typedef void (*get_value_func) (struct value *); + +#endif /* TST_TLS_MANYDYNAMICMOD_H */ diff --git a/REORG.TODO/elf/tst-tls-manydynamicmod.c b/REORG.TODO/elf/tst-tls-manydynamicmod.c new file mode 100644 index 0000000000..edfaeef7f3 --- /dev/null +++ b/REORG.TODO/elf/tst-tls-manydynamicmod.c @@ -0,0 +1,36 @@ +/* Module for test with many dynamic TLS variables. + Copyright (C) 2016-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This file is parameterized by macros NAME, SETTER, GETTER, which + are set form the Makefile. */ + +#include "tst-tls-manydynamic.h" + +__thread struct value NAME; + +void +SETTER (const struct value *value) +{ + NAME = *value; +} + +void +GETTER (struct value *value) +{ + *value = NAME; +} diff --git a/REORG.TODO/elf/tst-tls1-static.c b/REORG.TODO/elf/tst-tls1-static.c new file mode 100644 index 0000000000..a01008073b --- /dev/null +++ b/REORG.TODO/elf/tst-tls1-static.c @@ -0,0 +1 @@ +#include "tst-tls1.c" diff --git a/REORG.TODO/elf/tst-tls1.c b/REORG.TODO/elf/tst-tls1.c new file mode 100644 index 0000000000..c31da56ce9 --- /dev/null +++ b/REORG.TODO/elf/tst-tls1.c @@ -0,0 +1,82 @@ +/* glibc test for TLS in ld.so. */ +#include <stdio.h> + +#include "tls-macros.h" + + +/* Two common 'int' variables in TLS. */ +COMMON_INT_DEF(foo); +COMMON_INT_DEF(bar); + + +static int +do_test (void) +{ + 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; +} + + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls10.c b/REORG.TODO/elf/tst-tls10.c new file mode 100644 index 0000000000..d9611aac6d --- /dev/null +++ b/REORG.TODO/elf/tst-tls10.c @@ -0,0 +1,39 @@ +#include "tst-tls10.h" + +__thread int dummy __attribute__((visibility ("hidden"))) = 12; +__thread struct A local = { 1, 2, 3 }; + +#define CHECK(N, S) \ + p = f##N##a (); \ + if (p->a != S || p->b != S + 1 || p->c != S + 2) \ + abort () + +static int +do_test (void) +{ + 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); + + exit (0); +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls10.h b/REORG.TODO/elf/tst-tls10.h new file mode 100644 index 0000000000..7c8c6a6391 --- /dev/null +++ b/REORG.TODO/elf/tst-tls10.h @@ -0,0 +1,32 @@ +#include <stdlib.h> + +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); diff --git a/REORG.TODO/elf/tst-tls11.c b/REORG.TODO/elf/tst-tls11.c new file mode 100644 index 0000000000..a5c3dd70b0 --- /dev/null +++ b/REORG.TODO/elf/tst-tls11.c @@ -0,0 +1,28 @@ +#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 () + +static int +do_test (void) +{ + 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); + + exit (0); +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls12.c b/REORG.TODO/elf/tst-tls12.c new file mode 100644 index 0000000000..ccd5f8b43e --- /dev/null +++ b/REORG.TODO/elf/tst-tls12.c @@ -0,0 +1,19 @@ +#include "tst-tls10.h" + +#define CHECK(N, S) \ + p = &a##N; \ + if (p->a != S || p->b != S + 1 || p->c != S + 2) \ + abort () + +static int +do_test (void) +{ + struct A *p; + check1 (); + CHECK (1, 4); + CHECK (2, 7); + + exit (0); +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls13.c b/REORG.TODO/elf/tst-tls13.c new file mode 100644 index 0000000000..b1d303310f --- /dev/null +++ b/REORG.TODO/elf/tst-tls13.c @@ -0,0 +1,28 @@ +/* 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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls14.c b/REORG.TODO/elf/tst-tls14.c new file mode 100644 index 0000000000..a6a79ef24f --- /dev/null +++ b/REORG.TODO/elf/tst-tls14.c @@ -0,0 +1,54 @@ +/* Check alignment of TLS variable. */ +#include <dlfcn.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls15.c b/REORG.TODO/elf/tst-tls15.c new file mode 100644 index 0000000000..db2a4f4b77 --- /dev/null +++ b/REORG.TODO/elf/tst-tls15.c @@ -0,0 +1,32 @@ +#include <dlfcn.h> +#include <stdlib.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 (); +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls16.c b/REORG.TODO/elf/tst-tls16.c new file mode 100644 index 0000000000..f2830b8a4f --- /dev/null +++ b/REORG.TODO/elf/tst-tls16.c @@ -0,0 +1,52 @@ +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> + +static int +do_test (void) +{ + void *h = dlopen ("tst-tlsmod16a.so", RTLD_LAZY | RTLD_GLOBAL); + if (h == NULL) + { + puts ("unexpectedly failed to open tst-tlsmod16a.so"); + exit (1); + } + + void *p = dlsym (h, "tlsvar"); + + /* This dlopen should indeed fail, because tlsvar was assigned to + dynamic TLS, and the new module requests it to be in static TLS. + However, there's a possibility that dlopen succeeds if the + variable is, for whatever reason, assigned to static TLS, or if + the module fails to require static TLS, or even if TLS is not + supported. */ + h = dlopen ("tst-tlsmod16b.so", RTLD_NOW | RTLD_GLOBAL); + if (h == NULL) + { + return 0; + } + + puts ("unexpectedly succeeded to open tst-tlsmod16b.so"); + + + void *(*fp) (void) = (void *(*) (void)) dlsym (h, "in_dso"); + if (fp == NULL) + { + puts ("cannot find in_dso"); + exit (1); + } + + /* If the dlopen passes, at least make sure the address returned by + dlsym is the same as that returned by the initial-exec access. + If the variable was assigned to dynamic TLS during dlsym, this + portion will fail. */ + if (fp () != p) + { + puts ("returned values do not match"); + exit (1); + } + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls17.c b/REORG.TODO/elf/tst-tls17.c new file mode 100644 index 0000000000..c2a972d3c4 --- /dev/null +++ b/REORG.TODO/elf/tst-tls17.c @@ -0,0 +1,28 @@ +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> + +static int +do_test (void) +{ + void *h = dlopen ("tst-tlsmod17b.so", RTLD_LAZY); + if (h == NULL) + { + puts ("unexpectedly failed to open tst-tlsmod17b.so"); + exit (1); + } + + int (*fp) (void) = (int (*) (void)) dlsym (h, "tlsmod17b"); + if (fp == NULL) + { + puts ("cannot find tlsmod17b"); + exit (1); + } + + if (fp ()) + exit (1); + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls18.c b/REORG.TODO/elf/tst-tls18.c new file mode 100644 index 0000000000..b705b61d60 --- /dev/null +++ b/REORG.TODO/elf/tst-tls18.c @@ -0,0 +1,37 @@ +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> + +static int +do_test (void) +{ + char modname[sizeof "tst-tlsmod18aXX.so"]; + void *h[20]; + for (int i = 0; i < 20; i++) + { + snprintf (modname, sizeof modname, "tst-tlsmod18a%d.so", i); + h[i] = dlopen (modname, RTLD_LAZY); + if (h[i] == NULL) + { + printf ("unexpectedly failed to open %s", modname); + exit (1); + } + } + + for (int i = 0; i < 20; i++) + { + int (*fp) (void) = (int (*) (void)) dlsym (h[i], "test"); + if (fp == NULL) + { + printf ("cannot find test in tst-tlsmod18a%d.so", i); + exit (1); + } + + if (fp ()) + exit (1); + } + + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls19.c b/REORG.TODO/elf/tst-tls19.c new file mode 100644 index 0000000000..dd8ea42c3f --- /dev/null +++ b/REORG.TODO/elf/tst-tls19.c @@ -0,0 +1,26 @@ +// BZ 12453 +#include <stdio.h> +#include <dlfcn.h> + + +static int +do_test (void) +{ + void* dl = dlopen ("tst-tls19mod1.so", RTLD_LAZY | RTLD_GLOBAL); + if (dl == NULL) + { + printf ("Error loading tst-tls19mod1.so: %s\n", dlerror ()); + return 1; + } + + int (*fn) (void) = dlsym (dl, "foo"); + if (fn == NULL) + { + printf("Error obtaining symbol foo\n"); + return 1; + } + + return fn (); +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls19mod1.c b/REORG.TODO/elf/tst-tls19mod1.c new file mode 100644 index 0000000000..2790097ae5 --- /dev/null +++ b/REORG.TODO/elf/tst-tls19mod1.c @@ -0,0 +1,15 @@ +#include <stdio.h> + +extern int bar (void); +extern int baz (void); + +int +foo (void) +{ + int v1 = bar (); + int v2 = baz (); + + printf ("bar=%d, baz=%d\n", v1, v2); + + return v1 != 666 || v2 != 42; +} diff --git a/REORG.TODO/elf/tst-tls19mod2.c b/REORG.TODO/elf/tst-tls19mod2.c new file mode 100644 index 0000000000..cae702f67c --- /dev/null +++ b/REORG.TODO/elf/tst-tls19mod2.c @@ -0,0 +1,13 @@ +static int __thread tbar __attribute__ ((tls_model ("initial-exec"))) = 666; + +void +setter (int a) +{ + tbar = a; +} + +int +bar (void) +{ + return tbar; +} diff --git a/REORG.TODO/elf/tst-tls19mod3.c b/REORG.TODO/elf/tst-tls19mod3.c new file mode 100644 index 0000000000..e7b28016b3 --- /dev/null +++ b/REORG.TODO/elf/tst-tls19mod3.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +static int __thread tbaz __attribute__ ((tls_model ("local-dynamic"))) = 42; + +void +setter2 (int a) +{ + tbaz = a; +} + +int +baz (void) +{ + printf ("&tbaz=%p\n", &tbaz); + return tbaz; +} diff --git a/REORG.TODO/elf/tst-tls2-static.c b/REORG.TODO/elf/tst-tls2-static.c new file mode 100644 index 0000000000..55ffa57448 --- /dev/null +++ b/REORG.TODO/elf/tst-tls2-static.c @@ -0,0 +1 @@ +#include "tst-tls2.c" diff --git a/REORG.TODO/elf/tst-tls2.c b/REORG.TODO/elf/tst-tls2.c new file mode 100644 index 0000000000..963b8d6c88 --- /dev/null +++ b/REORG.TODO/elf/tst-tls2.c @@ -0,0 +1,82 @@ +/* glibc test for TLS in ld.so. */ +#include <stdio.h> + +#include "tls-macros.h" + + +/* Two 'int' variables in TLS. */ +VAR_INT_DEF(foo); +VAR_INT_DEF(bar); + + +static int +do_test (void) +{ + 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; +} + + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls3.c b/REORG.TODO/elf/tst-tls3.c new file mode 100644 index 0000000000..7e0abb4c58 --- /dev/null +++ b/REORG.TODO/elf/tst-tls3.c @@ -0,0 +1,67 @@ +/* glibc test for TLS in ld.so. */ +#include <stdio.h> + +#include "tls-macros.h" + + +/* One define int variable, two externs. */ +COMMON_INT_DECL(foo); +VAR_INT_DECL(bar); +VAR_INT_DEF(baz); + + +extern int in_dso (void); + + +static int +do_test (void) +{ + 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; +} + + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls4.c b/REORG.TODO/elf/tst-tls4.c new file mode 100644 index 0000000000..6841f81386 --- /dev/null +++ b/REORG.TODO/elf/tst-tls4.c @@ -0,0 +1,49 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + + +static int +do_test (void) +{ + 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; +} + + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls5.c b/REORG.TODO/elf/tst-tls5.c new file mode 100644 index 0000000000..5f006fd645 --- /dev/null +++ b/REORG.TODO/elf/tst-tls5.c @@ -0,0 +1,65 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + + +static int +do_test (void) +{ + 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; +} + + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls6.c b/REORG.TODO/elf/tst-tls6.c new file mode 100644 index 0000000000..df81c1f6b4 --- /dev/null +++ b/REORG.TODO/elf/tst-tls6.c @@ -0,0 +1,84 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <link.h> + + +static int +do_test (void) +{ + 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; +} + + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls7.c b/REORG.TODO/elf/tst-tls7.c new file mode 100644 index 0000000000..fa46709600 --- /dev/null +++ b/REORG.TODO/elf/tst-tls7.c @@ -0,0 +1,55 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <link.h> + + +static int +do_test (void) +{ + 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; +} + + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls8.c b/REORG.TODO/elf/tst-tls8.c new file mode 100644 index 0000000000..c779572617 --- /dev/null +++ b/REORG.TODO/elf/tst-tls8.c @@ -0,0 +1,167 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <link.h> + + +static int +do_test (void) +{ + 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; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tls9-static.c b/REORG.TODO/elf/tst-tls9-static.c new file mode 100644 index 0000000000..51812ccc7d --- /dev/null +++ b/REORG.TODO/elf/tst-tls9-static.c @@ -0,0 +1 @@ +#include "tst-tls9.c" diff --git a/REORG.TODO/elf/tst-tls9.c b/REORG.TODO/elf/tst-tls9.c new file mode 100644 index 0000000000..ee21b47c70 --- /dev/null +++ b/REORG.TODO/elf/tst-tls9.c @@ -0,0 +1,36 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <link.h> + +static int +do_test (void) +{ + 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; +} + + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tlsalign-extern-static.c b/REORG.TODO/elf/tst-tlsalign-extern-static.c new file mode 100644 index 0000000000..e84900eee4 --- /dev/null +++ b/REORG.TODO/elf/tst-tlsalign-extern-static.c @@ -0,0 +1 @@ +#include "tst-tlsalign-extern.c" diff --git a/REORG.TODO/elf/tst-tlsalign-extern.c b/REORG.TODO/elf/tst-tlsalign-extern.c new file mode 100644 index 0000000000..11384d085e --- /dev/null +++ b/REORG.TODO/elf/tst-tlsalign-extern.c @@ -0,0 +1,73 @@ +/* Test for large alignment in TLS blocks (extern case), BZ#18383. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +/* This is the same as tst-tlsalign-static.c, except that it uses + TLS variables that are defined in a separate translation unit + (ts-tlsalign-vars.c). It turned out that the cause of BZ#18383 + on ARM was actually an ARM assembler bug triggered by the ways of + using .tdata/.tbss sections and relocs referring to them that GCC + chooses when the variables are defined in the same translation + unit that contains the references. */ + +extern __thread int tdata1; +extern __thread int tdata2; +extern __thread int tdata3; +extern __thread int tbss1; +extern __thread int tbss2; +extern __thread int tbss3; + +static int +test_one (const char *which, unsigned int alignment, int *var, int value) +{ + uintptr_t addr = (uintptr_t) var; + unsigned int misalign = addr & (alignment - 1); + + printf ("%s TLS address %p %% %u = %u\n", + which, (void *) var, alignment, misalign); + + int got = *var; + if (got != value) + { + printf ("%s value %d should be %d\n", which, got, value); + return 1; + } + + return misalign != 0; +} + +static int +do_test (void) +{ + int fail = 0; + + fail |= test_one ("tdata1", 4, &tdata1, 1); + fail |= test_one ("tdata2", 0x10, &tdata2, 2); + fail |= test_one ("tdata3", 0x1000, &tdata3, 4); + + fail |= test_one ("tbss1", 4, &tbss1, 0); + fail |= test_one ("tbss2", 0x10, &tbss2, 0); + fail |= test_one ("tbss3", 0x1000, &tbss3, 0); + + return fail ? EXIT_FAILURE : EXIT_SUCCESS; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tlsalign-lib.c b/REORG.TODO/elf/tst-tlsalign-lib.c new file mode 100644 index 0000000000..4371e581a6 --- /dev/null +++ b/REORG.TODO/elf/tst-tlsalign-lib.c @@ -0,0 +1,6 @@ +__thread int mod_tdata1 = 1; +__thread int mod_tdata2 __attribute__ ((aligned (0x10))) = 2; +__thread int mod_tdata3 __attribute__ ((aligned (0x1000))) = 4; +__thread int mod_tbss1; +__thread int mod_tbss2 __attribute__ ((aligned (0x10))); +__thread int mod_tbss3 __attribute__ ((aligned (0x1000))); diff --git a/REORG.TODO/elf/tst-tlsalign-static.c b/REORG.TODO/elf/tst-tlsalign-static.c new file mode 100644 index 0000000000..1671abf28e --- /dev/null +++ b/REORG.TODO/elf/tst-tlsalign-static.c @@ -0,0 +1,2 @@ +#define NO_LIB +#include "tst-tlsalign.c" diff --git a/REORG.TODO/elf/tst-tlsalign-vars.c b/REORG.TODO/elf/tst-tlsalign-vars.c new file mode 100644 index 0000000000..01b3501d3b --- /dev/null +++ b/REORG.TODO/elf/tst-tlsalign-vars.c @@ -0,0 +1,28 @@ +/* This is for tst-tlsalign-extern.c, which see. It's essential for the + purpose of the test that these definitions be in a separate translation + unit from the code using the variables. */ + +__thread int tdata1 = 1; +__thread int tdata2 __attribute__ ((aligned (0x10))) = 2; +__thread int tdata3 __attribute__ ((aligned (0x1000))) = 4; +__thread int tbss1; +__thread int tbss2 __attribute__ ((aligned (0x10))); +__thread int tbss3 __attribute__ ((aligned (0x1000))); + +/* This function is never called. But its presence in this translation + unit makes GCC emit the variables above in the order defined (perhaps + because it's the order in which they're used here?) rather than + reordering them into descending order of alignment requirement--and so + keeps it more similar to the tst-tlsalign-static.c case--just in case + that affects the bug (though there is no evidence that it does). */ + +void +unused (void) +{ + tdata1 = -1; + tdata2 = -2; + tdata3 = -3; + tbss1 = -4; + tbss2 = -5; + tbss3 = -6; +} diff --git a/REORG.TODO/elf/tst-tlsalign.c b/REORG.TODO/elf/tst-tlsalign.c new file mode 100644 index 0000000000..b129ebda0e --- /dev/null +++ b/REORG.TODO/elf/tst-tlsalign.c @@ -0,0 +1,84 @@ +/* Test for large alignment in TLS blocks, BZ#18383. + Copyright (C) 2015-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +static __thread int tdata1 = 1; +static __thread int tdata2 __attribute__ ((aligned (0x10))) = 2; +static __thread int tdata3 __attribute__ ((aligned (0x1000))) = 4; +static __thread int tbss1; +static __thread int tbss2 __attribute__ ((aligned (0x10))); +static __thread int tbss3 __attribute__ ((aligned (0x1000))); + +#ifndef NO_LIB +extern __thread int mod_tdata1; +extern __thread int mod_tdata2; +extern __thread int mod_tdata3; +extern __thread int mod_tbss1; +extern __thread int mod_tbss2; +extern __thread int mod_tbss3; +#endif + +static int +test_one (const char *which, unsigned int alignment, int *var, int value) +{ + uintptr_t addr = (uintptr_t) var; + unsigned int misalign = addr & (alignment - 1); + + printf ("%s TLS address %p %% %u = %u\n", + which, (void *) var, alignment, misalign); + + int got = *var; + if (got != value) + { + printf ("%s value %d should be %d\n", which, got, value); + return 1; + } + + return misalign != 0; +} + +static int +do_test (void) +{ + int fail = 0; + + fail |= test_one ("tdata1", 4, &tdata1, 1); + fail |= test_one ("tdata2", 0x10, &tdata2, 2); + fail |= test_one ("tdata3", 0x1000, &tdata3, 4); + + fail |= test_one ("tbss1", 4, &tbss1, 0); + fail |= test_one ("tbss2", 0x10, &tbss2, 0); + fail |= test_one ("tbss3", 0x1000, &tbss3, 0); + +#ifndef NO_LIB + fail |= test_one ("mod_tdata1", 4, &mod_tdata1, 1); + fail |= test_one ("mod_tdata2", 0x10, &mod_tdata2, 2); + fail |= test_one ("mod_tdata3", 0x1000, &mod_tdata3, 4); + + fail |= test_one ("mod_tbss1", 4, &mod_tbss1, 0); + fail |= test_one ("mod_tbss2", 0x10, &mod_tbss2, 0); + fail |= test_one ("mod_tbss3", 0x1000, &mod_tbss3, 0); +#endif + + return fail ? EXIT_FAILURE : EXIT_SUCCESS; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-tlsmod1.c b/REORG.TODO/elf/tst-tlsmod1.c new file mode 100644 index 0000000000..8d9156791b --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod1.c @@ -0,0 +1,62 @@ +#include <stdio.h> + +#include "tls-macros.h" + + +/* One define int variable, two externs. */ +COMMON_INT_DEF(foo); +VAR_INT_DEF(bar); +VAR_INT_DECL(baz); + +extern int in_dso (void); + +int +in_dso (void) +{ + int result = 0; + 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; + } + + return result; +} diff --git a/REORG.TODO/elf/tst-tlsmod10.c b/REORG.TODO/elf/tst-tlsmod10.c new file mode 100644 index 0000000000..32e54f3c04 --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod10.c @@ -0,0 +1 @@ +#include "tst-tlsmod8.c" diff --git a/REORG.TODO/elf/tst-tlsmod11.c b/REORG.TODO/elf/tst-tlsmod11.c new file mode 100644 index 0000000000..cffbd68edc --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod11.c @@ -0,0 +1,4 @@ +#include "tst-tls10.h" + +__thread struct A a1 = { 4, 5, 6 }; +__thread struct A a2 = { 7, 8, 9 }; diff --git a/REORG.TODO/elf/tst-tlsmod12.c b/REORG.TODO/elf/tst-tlsmod12.c new file mode 100644 index 0000000000..d0be51891a --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod12.c @@ -0,0 +1,12 @@ +#include "tst-tls10.h" + +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 (); +} diff --git a/REORG.TODO/elf/tst-tlsmod13.c b/REORG.TODO/elf/tst-tlsmod13.c new file mode 100644 index 0000000000..7712d8b8c8 --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod13.c @@ -0,0 +1,7 @@ +__thread int a[2] __attribute__ ((tls_model ("initial-exec"))); + +int +foo (void) +{ + return a[0]; +} diff --git a/REORG.TODO/elf/tst-tlsmod13a.c b/REORG.TODO/elf/tst-tlsmod13a.c new file mode 100644 index 0000000000..ca4eaccbff --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod13a.c @@ -0,0 +1,9 @@ +__thread int b[2] __attribute__ ((tls_model ("initial-exec"))); + +extern int foo (void); + +int +bar (void) +{ + return foo () + b[0]; +} diff --git a/REORG.TODO/elf/tst-tlsmod14a.c b/REORG.TODO/elf/tst-tlsmod14a.c new file mode 100644 index 0000000000..824c06d1f9 --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod14a.c @@ -0,0 +1,35 @@ +#include <stdint.h> +#include <stdio.h> + +#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; +} diff --git a/REORG.TODO/elf/tst-tlsmod14b.c b/REORG.TODO/elf/tst-tlsmod14b.c new file mode 100644 index 0000000000..24d9ceaf7e --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod14b.c @@ -0,0 +1,2 @@ +#define FCT in_dso2 +#include "tst-tlsmod14a.c" diff --git a/REORG.TODO/elf/tst-tlsmod15a.c b/REORG.TODO/elf/tst-tlsmod15a.c new file mode 100644 index 0000000000..66c707129a --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod15a.c @@ -0,0 +1,6 @@ +extern int nonexistent_dummy_var; +int * +foo (void) +{ + return &nonexistent_dummy_var; +} diff --git a/REORG.TODO/elf/tst-tlsmod15b.c b/REORG.TODO/elf/tst-tlsmod15b.c new file mode 100644 index 0000000000..b37283686a --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod15b.c @@ -0,0 +1,9 @@ +#include "tst-tls10.h" + +__thread int mod15b_var __attribute__((tls_model("initial-exec"))); + +int +in_dso (void) +{ + return mod15b_var; +} diff --git a/REORG.TODO/elf/tst-tlsmod16a.c b/REORG.TODO/elf/tst-tlsmod16a.c new file mode 100644 index 0000000000..4ec6a6c37d --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod16a.c @@ -0,0 +1 @@ +int __thread tlsvar; diff --git a/REORG.TODO/elf/tst-tlsmod16b.c b/REORG.TODO/elf/tst-tlsmod16b.c new file mode 100644 index 0000000000..1ecba26dbe --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod16b.c @@ -0,0 +1,7 @@ +extern __thread int tlsvar __attribute__((tls_model("initial-exec"))); + +void * +in_dso (void) +{ + return &tlsvar; +} diff --git a/REORG.TODO/elf/tst-tlsmod17a.c b/REORG.TODO/elf/tst-tlsmod17a.c new file mode 100644 index 0000000000..24c84a1590 --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod17a.c @@ -0,0 +1,23 @@ +#include <stdio.h> + +#ifndef N +#define N 0 +#endif +#define CONCAT1(s, n) s##n +#define CONCAT(s, n) CONCAT1(s, n) + +__thread int CONCAT (v, N) = 4; + +int +CONCAT (tlsmod17a, N) (void) +{ + int *p = &CONCAT (v, N); + /* GCC assumes &var is never NULL, add optimization barrier. */ + asm volatile ("" : "+r" (p)); + if (p == NULL || *p != 4) + { + printf ("fail %d %p\n", N, p); + return 1; + } + return 0; +} diff --git a/REORG.TODO/elf/tst-tlsmod17b.c b/REORG.TODO/elf/tst-tlsmod17b.c new file mode 100644 index 0000000000..6178828737 --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod17b.c @@ -0,0 +1,15 @@ +#define P(N) extern int tlsmod17a##N (void); +#define PS P(0) P(1) P(2) P(3) P(4) P(5) P(6) P(7) P(8) P(9) \ + P(10) P(12) P(13) P(14) P(15) P(16) P(17) P(18) P(19) +PS +#undef P + +int +tlsmod17b (void) +{ + int res = 0; +#define P(N) res |= tlsmod17a##N (); + PS +#undef P + return res; +} diff --git a/REORG.TODO/elf/tst-tlsmod18a.c b/REORG.TODO/elf/tst-tlsmod18a.c new file mode 100644 index 0000000000..1d728daa05 --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod18a.c @@ -0,0 +1,21 @@ +#include <stdio.h> + +#ifndef N +# define N 0 +#endif + +static __thread int var = 4; + +int +test (void) +{ + int *p = &var; + /* GCC assumes &var is never NULL, add optimization barrier. */ + asm volatile ("" : "+r" (p)); + if (p == NULL || *p != 4) + { + printf ("fail %d %p\n", N, p); + return 1; + } + return 0; +} diff --git a/REORG.TODO/elf/tst-tlsmod2.c b/REORG.TODO/elf/tst-tlsmod2.c new file mode 100644 index 0000000000..40eb1407f8 --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod2.c @@ -0,0 +1,34 @@ +#include <stdio.h> + +#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; +} diff --git a/REORG.TODO/elf/tst-tlsmod3.c b/REORG.TODO/elf/tst-tlsmod3.c new file mode 100644 index 0000000000..6d186c47ee --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod3.c @@ -0,0 +1,37 @@ +#include <stdio.h> + +#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; +} diff --git a/REORG.TODO/elf/tst-tlsmod4.c b/REORG.TODO/elf/tst-tlsmod4.c new file mode 100644 index 0000000000..86889aac7e --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod4.c @@ -0,0 +1,34 @@ +#include <stdio.h> + +#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; +} diff --git a/REORG.TODO/elf/tst-tlsmod5.c b/REORG.TODO/elf/tst-tlsmod5.c new file mode 100644 index 0000000000..a97c7e5e0c --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod5.c @@ -0,0 +1,3 @@ +#include "tls-macros.h" + +COMMON_INT_DEF(foo); diff --git a/REORG.TODO/elf/tst-tlsmod6.c b/REORG.TODO/elf/tst-tlsmod6.c new file mode 100644 index 0000000000..e968596dd4 --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod6.c @@ -0,0 +1,3 @@ +#include "tls-macros.h" + +COMMON_INT_DEF(bar); diff --git a/REORG.TODO/elf/tst-tlsmod7.c b/REORG.TODO/elf/tst-tlsmod7.c new file mode 100644 index 0000000000..3df7907bcf --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod7.c @@ -0,0 +1,101 @@ +#include "tst-tls10.h" + +__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; +} diff --git a/REORG.TODO/elf/tst-tlsmod8.c b/REORG.TODO/elf/tst-tlsmod8.c new file mode 100644 index 0000000000..89772ac42f --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod8.c @@ -0,0 +1,70 @@ +#include "tst-tls10.h" + +__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; +} diff --git a/REORG.TODO/elf/tst-tlsmod9.c b/REORG.TODO/elf/tst-tlsmod9.c new file mode 100644 index 0000000000..6b11ed58b8 --- /dev/null +++ b/REORG.TODO/elf/tst-tlsmod9.c @@ -0,0 +1,99 @@ +#include "tst-tls10.h" + +__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; +} diff --git a/REORG.TODO/elf/tst-unique1.c b/REORG.TODO/elf/tst-unique1.c new file mode 100644 index 0000000000..b5e53e49a0 --- /dev/null +++ b/REORG.TODO/elf/tst-unique1.c @@ -0,0 +1,73 @@ +#include <config.h> +#include <dlfcn.h> +#include <stdio.h> +#include <sys/mman.h> + +static int +do_test (void) +{ + void *h1 = dlopen ("tst-unique1mod1.so", RTLD_LAZY); + if (h1 == NULL) + { + puts ("cannot load tst-unique1mod1"); + return 1; + } + int *(*f1) (void) = dlsym (h1, "f"); + if (f1 == NULL) + { + puts ("cannot locate f in tst-unique1mod1"); + return 1; + } + void *h2 = dlopen ("tst-unique1mod2.so", RTLD_LAZY); + if (h2 == NULL) + { + puts ("cannot load tst-unique1mod2"); + return 1; + } + int (*f2) (int *) = dlsym (h2, "f"); + if (f2 == NULL) + { + puts ("cannot locate f in tst-unique1mod2"); + return 1; + } + if (f2 (f1 ())) + { + puts ("f from tst-unique1mod2 failed"); + return 1; + } + dlclose (h2); + dlclose (h1); + mmap (NULL, 1024 * 1024 * 16, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + h2 = dlopen ("tst-unique1mod2.so", RTLD_LAZY); + if (h2 == NULL) + { + puts ("cannot load tst-unique1mod2"); + return 1; + } + f2 = dlsym (h2, "f"); + if (f2 == NULL) + { + puts ("cannot locate f in tst-unique1mod2"); + return 1; + } + h1 = dlopen ("tst-unique1mod1.so", RTLD_LAZY); + if (h1 == NULL) + { + puts ("cannot load tst-unique1mod1"); + return 1; + } + f1 = dlsym (h1, "f"); + if (f1 == NULL) + { + puts ("cannot locate f in tst-unique1mod1"); + return 1; + } + if (f2 (f1 ())) + { + puts ("f from tst-unique1mod2 failed"); + return 1; + } + return 0; +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-unique1mod1.c b/REORG.TODO/elf/tst-unique1mod1.c new file mode 100644 index 0000000000..84b1f908d6 --- /dev/null +++ b/REORG.TODO/elf/tst-unique1mod1.c @@ -0,0 +1,16 @@ +#include <config.h> + +asm (".data;" + ".globl var\n" + ".type var, %gnu_unique_object\n" + ".size var, 4\n" + "var:.zero 4\n" + ".previous"); +extern int var; + +int * +f (void) +{ + var = 1; + return &var; +} diff --git a/REORG.TODO/elf/tst-unique1mod2.c b/REORG.TODO/elf/tst-unique1mod2.c new file mode 100644 index 0000000000..126ca1ac6c --- /dev/null +++ b/REORG.TODO/elf/tst-unique1mod2.c @@ -0,0 +1,15 @@ +#include <config.h> + +asm (".data;" + ".globl var\n" + ".type var, %gnu_unique_object\n" + ".size var, 4\n" + "var:.zero 4\n" + ".previous"); +extern int var; + +int +f (int *p) +{ + return &var != p || *p != 1; +} diff --git a/REORG.TODO/elf/tst-unique2.c b/REORG.TODO/elf/tst-unique2.c new file mode 100644 index 0000000000..e0173b7bcc --- /dev/null +++ b/REORG.TODO/elf/tst-unique2.c @@ -0,0 +1,27 @@ +#include <config.h> +#include <dlfcn.h> +#include <stdio.h> + +extern int var; + +static int +do_test (void) +{ + var = 1; + + void *h = dlopen ("tst-unique2mod2.so", RTLD_LAZY); + if (h == NULL) + { + puts ("cannot load tst-unique2mod2"); + return 1; + } + int (*f) (int *) = dlsym (h, "f"); + if (f == NULL) + { + puts ("cannot locate f in tst-unique2mod2"); + return 1; + } + return f (&var); +} + +#include <support/test-driver.c> diff --git a/REORG.TODO/elf/tst-unique2mod1.c b/REORG.TODO/elf/tst-unique2mod1.c new file mode 100644 index 0000000000..7cdb0eb0a0 --- /dev/null +++ b/REORG.TODO/elf/tst-unique2mod1.c @@ -0,0 +1,8 @@ +#include <config.h> + +asm (".data;" + ".globl var\n" + ".type var, %gnu_unique_object\n" + ".size var, 4\n" + "var:.zero 4\n" + ".previous"); diff --git a/REORG.TODO/elf/tst-unique2mod2.c b/REORG.TODO/elf/tst-unique2mod2.c new file mode 100644 index 0000000000..126ca1ac6c --- /dev/null +++ b/REORG.TODO/elf/tst-unique2mod2.c @@ -0,0 +1,15 @@ +#include <config.h> + +asm (".data;" + ".globl var\n" + ".type var, %gnu_unique_object\n" + ".size var, 4\n" + "var:.zero 4\n" + ".previous"); +extern int var; + +int +f (int *p) +{ + return &var != p || *p != 1; +} diff --git a/REORG.TODO/elf/tst-unique3.cc b/REORG.TODO/elf/tst-unique3.cc new file mode 100644 index 0000000000..efdd6d78c2 --- /dev/null +++ b/REORG.TODO/elf/tst-unique3.cc @@ -0,0 +1,24 @@ +#include "tst-unique3.h" + +#include <cstdio> +#include "../dlfcn/dlfcn.h" + +int t = S<char>::i; + +int +main (void) +{ + std::printf ("%d %d\n", S<char>::i, t); + int result = S<char>::i++ != 1 || t != 1; + result |= in_lib (); + void *d = dlopen ("$ORIGIN/tst-unique3lib2.so", RTLD_LAZY); + int (*fp) (); + if (d == NULL || (fp = (int(*)()) dlsym (d, "in_lib2")) == NULL) + { + std::printf ("failed to get symbol in_lib2\n"); + return 1; + } + result |= fp (); + dlclose (d); + return result; +} diff --git a/REORG.TODO/elf/tst-unique3.h b/REORG.TODO/elf/tst-unique3.h new file mode 100644 index 0000000000..716d23641c --- /dev/null +++ b/REORG.TODO/elf/tst-unique3.h @@ -0,0 +1,8 @@ +// BZ 12510 +template<typename T> +struct S +{ + static int i; +}; + +extern int in_lib (void); diff --git a/REORG.TODO/elf/tst-unique3lib.cc b/REORG.TODO/elf/tst-unique3lib.cc new file mode 100644 index 0000000000..fa8e85a36c --- /dev/null +++ b/REORG.TODO/elf/tst-unique3lib.cc @@ -0,0 +1,11 @@ +#include <cstdio> +#include "tst-unique3.h" +template<typename T> int S<T>::i = 1; +static int i = S<char>::i; + +int +in_lib (void) +{ + std::printf ("in_lib: %d %d\n", S<char>::i, i); + return S<char>::i++ != 2 || i != 1; +} diff --git a/REORG.TODO/elf/tst-unique3lib2.cc b/REORG.TODO/elf/tst-unique3lib2.cc new file mode 100644 index 0000000000..17d817e12e --- /dev/null +++ b/REORG.TODO/elf/tst-unique3lib2.cc @@ -0,0 +1,12 @@ +#include <cstdio> +#include "tst-unique3.h" + +template<typename T> int S<T>::i; + +extern "C" +int +in_lib2 () +{ + std::printf ("in_lib2: %d\n", S<char>::i); + return S<char>::i != 3; +} diff --git a/REORG.TODO/elf/tst-unique4.cc b/REORG.TODO/elf/tst-unique4.cc new file mode 100644 index 0000000000..575c70d3a1 --- /dev/null +++ b/REORG.TODO/elf/tst-unique4.cc @@ -0,0 +1,28 @@ +// BZ 12511 +#include "tst-unique4.h" + +#include <cstdio> + +static int a[24] = + { + S<1>::i, S<2>::i, S<3>::i, S<4>::i, S<5>::i, S<6>::i, S<7>::i, S<8>::i, + S<9>::i, S<10>::i, S<11>::i, S<12>::i, S<13>::i, S<14>::i, S<15>::i, + S<16>::i, S<17>::i, S<18>::i, S<19>::i, S<20>::i, S<21>::i, S<22>::i, + S<23>::i, S<24>::i + }; + +int +main (void) +{ + int result = 0; + for (int i = 0; i < 24; ++i) + { + printf("%d ", a[i]); + result |= a[i] != i + 1; + } + + printf("\n%d\n", S<1>::j); + result |= S<1>::j != -1; + + return result; +} diff --git a/REORG.TODO/elf/tst-unique4.h b/REORG.TODO/elf/tst-unique4.h new file mode 100644 index 0000000000..2d377f5d51 --- /dev/null +++ b/REORG.TODO/elf/tst-unique4.h @@ -0,0 +1,7 @@ +// BZ 12511 +template<int N> +struct S +{ + static int i; + static const int j; +}; diff --git a/REORG.TODO/elf/tst-unique4lib.cc b/REORG.TODO/elf/tst-unique4lib.cc new file mode 100644 index 0000000000..17a7cdf567 --- /dev/null +++ b/REORG.TODO/elf/tst-unique4lib.cc @@ -0,0 +1,17 @@ +// BZ 12511 +#include "tst-unique4.h" + +template<int N> +int S<N>::i = N; +template<int N> +const int S<N>::j __attribute__ ((used)) = -1; + +static int a[24] __attribute__ ((used)) = + { + S<1>::i, S<2>::i, S<3>::i, S<4>::i, S<5>::i, S<6>::i, S<7>::i, S<8>::i, + S<9>::i, S<10>::i, S<11>::i, S<12>::i, S<13>::i, S<14>::i, S<15>::i, + S<16>::i, S<17>::i, S<18>::i, S<19>::i, S<20>::i, S<21>::i, S<22>::i, + S<23>::i, S<24>::i + }; + +static int b __attribute__ ((used)) = S<1>::j; diff --git a/REORG.TODO/elf/unload.c b/REORG.TODO/elf/unload.c new file mode 100644 index 0000000000..4566f226f8 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload2.c b/REORG.TODO/elf/unload2.c new file mode 100644 index 0000000000..eef2bfd426 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload2dep.c b/REORG.TODO/elf/unload2dep.c new file mode 100644 index 0000000000..0d319515d5 --- /dev/null +++ b/REORG.TODO/elf/unload2dep.c @@ -0,0 +1,6 @@ +extern void foo (void); + +void +foo (void) +{ +} diff --git a/REORG.TODO/elf/unload2mod.c b/REORG.TODO/elf/unload2mod.c new file mode 100644 index 0000000000..9c2ea586bc --- /dev/null +++ b/REORG.TODO/elf/unload2mod.c @@ -0,0 +1,8 @@ +extern void foo (void); +extern void bar (void); + +void +bar (void) +{ + foo (); +} diff --git a/REORG.TODO/elf/unload3.c b/REORG.TODO/elf/unload3.c new file mode 100644 index 0000000000..6f1af707e6 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload3mod1.c b/REORG.TODO/elf/unload3mod1.c new file mode 100644 index 0000000000..e886b11c18 --- /dev/null +++ b/REORG.TODO/elf/unload3mod1.c @@ -0,0 +1 @@ +int dummy1; diff --git a/REORG.TODO/elf/unload3mod2.c b/REORG.TODO/elf/unload3mod2.c new file mode 100644 index 0000000000..03252a523b --- /dev/null +++ b/REORG.TODO/elf/unload3mod2.c @@ -0,0 +1 @@ +int dummy2; diff --git a/REORG.TODO/elf/unload3mod3.c b/REORG.TODO/elf/unload3mod3.c new file mode 100644 index 0000000000..046022c55d --- /dev/null +++ b/REORG.TODO/elf/unload3mod3.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int +foo (int x) +{ + puts ("foo"); + return x * 2; +} diff --git a/REORG.TODO/elf/unload3mod4.c b/REORG.TODO/elf/unload3mod4.c new file mode 100644 index 0000000000..52f808e79b --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload4.c b/REORG.TODO/elf/unload4.c new file mode 100644 index 0000000000..6e171a22e0 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload4mod1.c b/REORG.TODO/elf/unload4mod1.c new file mode 100644 index 0000000000..38c5b0168d --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload4mod2.c b/REORG.TODO/elf/unload4mod2.c new file mode 100644 index 0000000000..497ef5d93b --- /dev/null +++ b/REORG.TODO/elf/unload4mod2.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int +baz (int x) +{ + puts ("in baz"); + return x * 4; +} diff --git a/REORG.TODO/elf/unload4mod3.c b/REORG.TODO/elf/unload4mod3.c new file mode 100644 index 0000000000..4b280bc05b --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload4mod4.c b/REORG.TODO/elf/unload4mod4.c new file mode 100644 index 0000000000..ba5a144d38 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload5.c b/REORG.TODO/elf/unload5.c new file mode 100644 index 0000000000..0555052ce8 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload6.c b/REORG.TODO/elf/unload6.c new file mode 100644 index 0000000000..1efc7eb841 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload6mod1.c b/REORG.TODO/elf/unload6mod1.c new file mode 100644 index 0000000000..24f2e5a19a --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload6mod2.c b/REORG.TODO/elf/unload6mod2.c new file mode 100644 index 0000000000..980efa4b0e --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload6mod3.c b/REORG.TODO/elf/unload6mod3.c new file mode 100644 index 0000000000..7b29e1d626 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/unload7.c b/REORG.TODO/elf/unload7.c new file mode 100644 index 0000000000..198f7db286 --- /dev/null +++ b/REORG.TODO/elf/unload7.c @@ -0,0 +1,39 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +main (void) +{ + void *h = dlopen ("$ORIGIN/unload7mod1.so", RTLD_LAZY); + if (h == NULL) + { + puts ("dlopen unload7mod1.so failed"); + return 1; + } + + int (*fn) (void); + fn = dlsym (h, "foo"); + if (fn == NULL) + { + puts ("dlsym failed"); + return 1; + } + + int ret = 0; + if (fn () == 0) + ++ret; + + void *h2 = dlopen ("$ORIGIN/unload7mod2.so", RTLD_LAZY); + if (h2 == NULL) + { + puts ("dlopen unload7mod2.so failed"); + return 1; + } + dlclose (h2); + + if (fn () == 0) + ++ret; + + dlclose (h); + return ret; +} diff --git a/REORG.TODO/elf/unload7mod1.c b/REORG.TODO/elf/unload7mod1.c new file mode 100644 index 0000000000..7435adce2c --- /dev/null +++ b/REORG.TODO/elf/unload7mod1.c @@ -0,0 +1,11 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +foo (int i) +{ + if (dlsym (RTLD_DEFAULT, "unload7_nonexistent_symbol") == NULL) + return 1; + puts ("dlsym returned non-NULL"); + return 0; +} diff --git a/REORG.TODO/elf/unload7mod2.c b/REORG.TODO/elf/unload7mod2.c new file mode 100644 index 0000000000..6d1a0d47b7 --- /dev/null +++ b/REORG.TODO/elf/unload7mod2.c @@ -0,0 +1 @@ +int x; diff --git a/REORG.TODO/elf/unload8.c b/REORG.TODO/elf/unload8.c new file mode 100644 index 0000000000..f984a38098 --- /dev/null +++ b/REORG.TODO/elf/unload8.c @@ -0,0 +1,33 @@ +#include <dlfcn.h> +#include <stdio.h> + +int +main (void) +{ + void *h = dlopen ("$ORIGIN/unload8mod1.so", RTLD_LAZY); + if (h == NULL) + { + puts ("dlopen unload8mod1.so failed"); + return 1; + } + + void *h2 = dlopen ("$ORIGIN/unload8mod1x.so", RTLD_LAZY); + if (h2 == NULL) + { + puts ("dlopen unload8mod1x.so failed"); + return 1; + } + dlclose (h2); + + int (*mod1) (void) = dlsym (h, "mod1"); + if (mod1 == NULL) + { + puts ("dlsym failed"); + return 1; + } + + mod1 (); + dlclose (h); + + return 0; +} diff --git a/REORG.TODO/elf/unload8mod1.c b/REORG.TODO/elf/unload8mod1.c new file mode 100644 index 0000000000..fe7e81c1c3 --- /dev/null +++ b/REORG.TODO/elf/unload8mod1.c @@ -0,0 +1,7 @@ +extern void mod2 (void); + +void +mod1 (void) +{ + mod2 (); +} diff --git a/REORG.TODO/elf/unload8mod1x.c b/REORG.TODO/elf/unload8mod1x.c new file mode 100644 index 0000000000..835b634914 --- /dev/null +++ b/REORG.TODO/elf/unload8mod1x.c @@ -0,0 +1 @@ +int mod1x; diff --git a/REORG.TODO/elf/unload8mod2.c b/REORG.TODO/elf/unload8mod2.c new file mode 100644 index 0000000000..2fd8b6768a --- /dev/null +++ b/REORG.TODO/elf/unload8mod2.c @@ -0,0 +1,7 @@ +extern void mod3 (void); + +void +mod2 (void) +{ + mod3 (); +} diff --git a/REORG.TODO/elf/unload8mod3.c b/REORG.TODO/elf/unload8mod3.c new file mode 100644 index 0000000000..d49e22b24c --- /dev/null +++ b/REORG.TODO/elf/unload8mod3.c @@ -0,0 +1,27 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +void +mod3_fini2 (void) +{ +} + +void +mod3_fini (void) +{ + mod3_fini2 (); +} + +void +mod3 (void) +{ + void *h = dlopen ("$ORIGIN/unload8mod2.so", RTLD_LAZY); + if (h == NULL) + { + puts ("dlopen unload8mod2.so failed"); + exit (1); + } + + atexit (mod3_fini); +} diff --git a/REORG.TODO/elf/unloadmod.c b/REORG.TODO/elf/unloadmod.c new file mode 100644 index 0000000000..3aa5403edf --- /dev/null +++ b/REORG.TODO/elf/unloadmod.c @@ -0,0 +1,4 @@ +struct testdat +{ + void *next; +} testdat; diff --git a/REORG.TODO/elf/vismain.c b/REORG.TODO/elf/vismain.c new file mode 100644 index 0000000000..43f1d8f095 --- /dev/null +++ b/REORG.TODO/elf/vismain.c @@ -0,0 +1,260 @@ +/* Copyright (C) 2000-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This file must be compiled as PIE to avoid copy relocation when + accessing protected symbols defined in shared libaries since copy + relocation doesn't work with protected symbols and linker in + binutils 2.26 enforces this rule. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "vismod.h" + +/* Prototype for our test function. */ +extern int do_test (void); + + +/* This defines the `main' function and some more. */ +#include <support/test-driver.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/REORG.TODO/elf/vismod.h b/REORG.TODO/elf/vismod.h new file mode 100644 index 0000000000..ef05ffd5e9 --- /dev/null +++ b/REORG.TODO/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/REORG.TODO/elf/vismod1.c b/REORG.TODO/elf/vismod1.c new file mode 100644 index 0000000000..3e56404ce7 --- /dev/null +++ b/REORG.TODO/elf/vismod1.c @@ -0,0 +1,103 @@ +/* Copyright (C) 2000-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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/REORG.TODO/elf/vismod2.c b/REORG.TODO/elf/vismod2.c new file mode 100644 index 0000000000..89be69728a --- /dev/null +++ b/REORG.TODO/elf/vismod2.c @@ -0,0 +1,123 @@ +/* Copyright (C) 2000-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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/REORG.TODO/elf/vismod3.c b/REORG.TODO/elf/vismod3.c new file mode 100644 index 0000000000..1074d1bcda --- /dev/null +++ b/REORG.TODO/elf/vismod3.c @@ -0,0 +1,46 @@ +/* Copyright (C) 2000-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#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; +} |