summaryrefslogtreecommitdiff
path: root/libc/elf
diff options
context:
space:
mode:
authorgcc <gcc@7b3dc134-2b1b-0410-93df-9e9f96275f8d>2006-08-17 01:18:26 +0000
committergcc <gcc@7b3dc134-2b1b-0410-93df-9e9f96275f8d>2006-08-17 01:18:26 +0000
commit15f34685e7a9b5caf761af2ebf6afa20438d440b (patch)
treedc04ce3cdf040f198743c15b64557824de174680 /libc/elf
parent1e848e0e775a36f6359161f5deb890942ef42ff3 (diff)
downloadeglibc2-15f34685e7a9b5caf761af2ebf6afa20438d440b.tar.gz
Import glibc-mainline for 2006-08-16
git-svn-id: svn://svn.eglibc.org/fsf/trunk@4 7b3dc134-2b1b-0410-93df-9e9f96275f8d
Diffstat (limited to 'libc/elf')
-rw-r--r--libc/elf/.cvsignore6
-rw-r--r--libc/elf/Makefile908
-rw-r--r--libc/elf/Versions67
-rw-r--r--libc/elf/cache.c523
-rw-r--r--libc/elf/check-localplt.c299
-rw-r--r--libc/elf/check-textrel.c199
-rw-r--r--libc/elf/chroot_canon.c183
-rw-r--r--libc/elf/circleload1.c166
-rw-r--r--libc/elf/circlemod1.c7
-rw-r--r--libc/elf/circlemod1a.c1
-rw-r--r--libc/elf/circlemod2.c9
-rw-r--r--libc/elf/circlemod2a.c7
-rw-r--r--libc/elf/circlemod3.c14
-rw-r--r--libc/elf/circlemod3a.c1
-rw-r--r--libc/elf/constload1.c32
-rw-r--r--libc/elf/constload2.c50
-rw-r--r--libc/elf/constload3.c8
-rw-r--r--libc/elf/dblload.c53
-rw-r--r--libc/elf/dblloadmod1.c8
-rw-r--r--libc/elf/dblloadmod2.c15
-rw-r--r--libc/elf/dblloadmod3.c8
-rw-r--r--libc/elf/dblunload.c53
-rw-r--r--libc/elf/dep1.c25
-rw-r--r--libc/elf/dep2.c25
-rw-r--r--libc/elf/dep3.c23
-rw-r--r--libc/elf/dep4.c24
-rw-r--r--libc/elf/dl-addr.c162
-rw-r--r--libc/elf/dl-brk.c5
-rw-r--r--libc/elf/dl-cache.c311
-rw-r--r--libc/elf/dl-caller.c87
-rw-r--r--libc/elf/dl-close.c690
-rw-r--r--libc/elf/dl-conflict.c70
-rw-r--r--libc/elf/dl-debug.c78
-rw-r--r--libc/elf/dl-deps.c643
-rw-r--r--libc/elf/dl-dst.h85
-rw-r--r--libc/elf/dl-environ.c86
-rw-r--r--libc/elf/dl-error.c214
-rw-r--r--libc/elf/dl-execstack.c32
-rw-r--r--libc/elf/dl-fini.c287
-rw-r--r--libc/elf/dl-fptr.c323
-rw-r--r--libc/elf/dl-init.c141
-rw-r--r--libc/elf/dl-iteratephdr.c125
-rw-r--r--libc/elf/dl-libc.c254
-rw-r--r--libc/elf/dl-load.c2310
-rw-r--r--libc/elf/dl-lookup.c484
-rw-r--r--libc/elf/dl-minimal.c362
-rw-r--r--libc/elf/dl-misc.c325
-rw-r--r--libc/elf/dl-object.c205
-rw-r--r--libc/elf/dl-open.c646
-rw-r--r--libc/elf/dl-origin.c51
-rw-r--r--libc/elf/dl-profile.c555
-rw-r--r--libc/elf/dl-profstub.c42
-rw-r--r--libc/elf/dl-reloc.c371
-rw-r--r--libc/elf/dl-runtime.c415
-rw-r--r--libc/elf/dl-sbrk.c5
-rw-r--r--libc/elf/dl-support.c313
-rw-r--r--libc/elf/dl-sym.c210
-rw-r--r--libc/elf/dl-symaddr.c33
-rw-r--r--libc/elf/dl-sysdep.c590
-rw-r--r--libc/elf/dl-tls.c843
-rw-r--r--libc/elf/dl-trampoline.c1
-rw-r--r--libc/elf/dl-tsd.c58
-rw-r--r--libc/elf/dl-version.c392
-rw-r--r--libc/elf/do-lookup.h261
-rw-r--r--libc/elf/do-rel.h139
-rw-r--r--libc/elf/dynamic-link.h343
-rw-r--r--libc/elf/elf.h2608
-rw-r--r--libc/elf/enbl-secure.c37
-rw-r--r--libc/elf/failobj.c10
-rw-r--r--libc/elf/filter.c19
-rw-r--r--libc/elf/filtmod1.c7
-rw-r--r--libc/elf/filtmod2.c7
-rw-r--r--libc/elf/firstobj.c10
-rw-r--r--libc/elf/gen-trusted-dirs.awk37
-rw-r--r--libc/elf/genrtldtbl.awk29
-rw-r--r--libc/elf/global.c7
-rw-r--r--libc/elf/globalmod1.c17
-rw-r--r--libc/elf/initfirst.c22
-rw-r--r--libc/elf/interp.c21
-rw-r--r--libc/elf/lateglobal.c47
-rw-r--r--libc/elf/ldconfig.c1297
-rw-r--r--libc/elf/ldd.bash.in204
-rw-r--r--libc/elf/link.h194
-rw-r--r--libc/elf/loadfail.c42
-rw-r--r--libc/elf/loadtest.c202
-rw-r--r--libc/elf/ltglobmod1.c7
-rw-r--r--libc/elf/ltglobmod2.c33
-rw-r--r--libc/elf/multiload.c105
-rw-r--r--libc/elf/neededobj1.c6
-rw-r--r--libc/elf/neededobj2.c8
-rw-r--r--libc/elf/neededobj3.c10
-rw-r--r--libc/elf/neededobj4.c12
-rw-r--r--libc/elf/neededobj5.c5
-rw-r--r--libc/elf/neededobj6.c7
-rw-r--r--libc/elf/neededtest.c125
-rw-r--r--libc/elf/neededtest2.c118
-rw-r--r--libc/elf/neededtest3.c129
-rw-r--r--libc/elf/neededtest4.c158
-rw-r--r--libc/elf/next.c44
-rw-r--r--libc/elf/nextmod1.c30
-rw-r--r--libc/elf/nextmod2.c10
-rw-r--r--libc/elf/nodel2mod1.c19
-rw-r--r--libc/elf/nodel2mod2.c7
-rw-r--r--libc/elf/nodel2mod3.c1
-rw-r--r--libc/elf/nodelete.c211
-rw-r--r--libc/elf/nodelete2.c16
-rw-r--r--libc/elf/nodelmod1.c9
-rw-r--r--libc/elf/nodelmod2.c9
-rw-r--r--libc/elf/nodelmod3.c8
-rw-r--r--libc/elf/nodelmod4.c9
-rw-r--r--libc/elf/nodlopen.c15
-rw-r--r--libc/elf/nodlopen2.c15
-rw-r--r--libc/elf/nodlopenmod.c1
-rw-r--r--libc/elf/nodlopenmod2.c9
-rw-r--r--libc/elf/noload.c71
-rw-r--r--libc/elf/order.c25
-rw-r--r--libc/elf/order2.c46
-rw-r--r--libc/elf/order2mod1.c8
-rw-r--r--libc/elf/order2mod2.c18
-rw-r--r--libc/elf/order2mod3.c14
-rw-r--r--libc/elf/order2mod4.c16
-rw-r--r--libc/elf/origtest.c39
-rw-r--r--libc/elf/pathoptobj.c8
-rw-r--r--libc/elf/preloadtest.c19
-rw-r--r--libc/elf/readelflib.c220
-rw-r--r--libc/elf/readlib.c181
-rw-r--r--libc/elf/reldep.c111
-rw-r--r--libc/elf/reldep2.c101
-rw-r--r--libc/elf/reldep3.c101
-rw-r--r--libc/elf/reldep4.c40
-rw-r--r--libc/elf/reldep4mod1.c7
-rw-r--r--libc/elf/reldep4mod2.c8
-rw-r--r--libc/elf/reldep4mod3.c7
-rw-r--r--libc/elf/reldep4mod4.c8
-rw-r--r--libc/elf/reldep5.c70
-rw-r--r--libc/elf/reldep6.c109
-rw-r--r--libc/elf/reldep6mod0.c8
-rw-r--r--libc/elf/reldep6mod1.c14
-rw-r--r--libc/elf/reldep6mod2.c3
-rw-r--r--libc/elf/reldep6mod3.c3
-rw-r--r--libc/elf/reldep6mod4.c12
-rw-r--r--libc/elf/reldep7.c58
-rw-r--r--libc/elf/reldep7mod1.c12
-rw-r--r--libc/elf/reldep7mod2.c12
-rw-r--r--libc/elf/reldep8.c16
-rw-r--r--libc/elf/reldep8mod1.c19
-rw-r--r--libc/elf/reldep8mod2.c7
-rw-r--r--libc/elf/reldep8mod3.c1
-rw-r--r--libc/elf/reldep9.c16
-rw-r--r--libc/elf/reldep9mod1.c23
-rw-r--r--libc/elf/reldep9mod2.c3
-rw-r--r--libc/elf/reldep9mod3.c1
-rw-r--r--libc/elf/reldepmod1.c10
-rw-r--r--libc/elf/reldepmod2.c8
-rw-r--r--libc/elf/reldepmod3.c20
-rw-r--r--libc/elf/reldepmod4.c37
-rw-r--r--libc/elf/reldepmod5.c7
-rw-r--r--libc/elf/reldepmod6.c8
-rw-r--r--libc/elf/resolvfail.c31
-rw-r--r--libc/elf/restest1.c57
-rw-r--r--libc/elf/restest2.c33
-rw-r--r--libc/elf/rtld-Rules133
-rw-r--r--libc/elf/rtld.c2826
-rw-r--r--libc/elf/sln.c189
-rw-r--r--libc/elf/sofini.c17
-rw-r--r--libc/elf/soinit.c46
-rw-r--r--libc/elf/sprof.c1383
-rw-r--r--libc/elf/stackguard-macros.h33
-rw-r--r--libc/elf/testobj.h28
-rw-r--r--libc/elf/testobj1.c25
-rw-r--r--libc/elf/testobj1_1.c7
-rw-r--r--libc/elf/testobj2.c32
-rw-r--r--libc/elf/testobj3.c26
-rw-r--r--libc/elf/testobj4.c25
-rw-r--r--libc/elf/testobj5.c26
-rw-r--r--libc/elf/testobj6.c19
-rw-r--r--libc/elf/tls-macros.h851
-rw-r--r--libc/elf/tst-align.c54
-rw-r--r--libc/elf/tst-align2.c157
-rw-r--r--libc/elf/tst-alignmod.c53
-rw-r--r--libc/elf/tst-alignmod2.c60
-rw-r--r--libc/elf/tst-array1-static.c1
-rw-r--r--libc/elf/tst-array1.c101
-rw-r--r--libc/elf/tst-array1.exp11
-rw-r--r--libc/elf/tst-array2.c1
-rw-r--r--libc/elf/tst-array2.exp19
-rw-r--r--libc/elf/tst-array2dep.c69
-rw-r--r--libc/elf/tst-array3.c1
-rw-r--r--libc/elf/tst-array4.c18
-rw-r--r--libc/elf/tst-array4.exp19
-rw-r--r--libc/elf/tst-array5-static.c1
-rw-r--r--libc/elf/tst-array5-static.exp2
-rw-r--r--libc/elf/tst-array5.c50
-rw-r--r--libc/elf/tst-array5.exp3
-rw-r--r--libc/elf/tst-array5dep.c23
-rw-r--r--libc/elf/tst-audit1.c1
-rw-r--r--libc/elf/tst-audit2.c50
-rw-r--r--libc/elf/tst-auditmod1.c200
-rw-r--r--libc/elf/tst-deep1.c36
-rw-r--r--libc/elf/tst-deep1mod1.c14
-rw-r--r--libc/elf/tst-deep1mod2.c16
-rw-r--r--libc/elf/tst-deep1mod3.c17
-rw-r--r--libc/elf/tst-dlmodcount.c107
-rw-r--r--libc/elf/tst-dlmopen1.c80
-rw-r--r--libc/elf/tst-dlmopen1mod.c59
-rw-r--r--libc/elf/tst-dlmopen2.c70
-rw-r--r--libc/elf/tst-dlmopen3.c22
-rw-r--r--libc/elf/tst-dlopenrpath.c72
-rw-r--r--libc/elf/tst-dlopenrpathmod.c36
-rw-r--r--libc/elf/tst-execstack-mod.c30
-rw-r--r--libc/elf/tst-execstack-needed.c36
-rw-r--r--libc/elf/tst-execstack-prog.c35
-rw-r--r--libc/elf/tst-execstack.c133
-rw-r--r--libc/elf/tst-global1.c36
-rw-r--r--libc/elf/tst-leaks1.c25
-rw-r--r--libc/elf/tst-pathopt.c39
-rwxr-xr-xlibc/elf/tst-pathopt.sh37
-rw-r--r--libc/elf/tst-pie1.c5
-rw-r--r--libc/elf/tst-piemod1.c20
-rw-r--r--libc/elf/tst-stackguard1-static.c1
-rw-r--r--libc/elf/tst-stackguard1.c196
-rw-r--r--libc/elf/tst-tls-dlinfo.c92
-rw-r--r--libc/elf/tst-tls1-static.c1
-rw-r--r--libc/elf/tst-tls1.c91
-rw-r--r--libc/elf/tst-tls10.c40
-rw-r--r--libc/elf/tst-tls10.h38
-rw-r--r--libc/elf/tst-tls11.c27
-rw-r--r--libc/elf/tst-tls12.c18
-rw-r--r--libc/elf/tst-tls13.c30
-rw-r--r--libc/elf/tst-tls14.c66
-rw-r--r--libc/elf/tst-tls15.c32
-rw-r--r--libc/elf/tst-tls2-static.c1
-rw-r--r--libc/elf/tst-tls2.c91
-rw-r--r--libc/elf/tst-tls3.c76
-rw-r--r--libc/elf/tst-tls4.c56
-rw-r--r--libc/elf/tst-tls5.c72
-rw-r--r--libc/elf/tst-tls6.c90
-rw-r--r--libc/elf/tst-tls7.c61
-rw-r--r--libc/elf/tst-tls8.c174
-rw-r--r--libc/elf/tst-tls9-static.c1
-rw-r--r--libc/elf/tst-tls9.c42
-rw-r--r--libc/elf/tst-tlsmod1.c68
-rw-r--r--libc/elf/tst-tlsmod10.c1
-rw-r--r--libc/elf/tst-tlsmod11.c6
-rw-r--r--libc/elf/tst-tlsmod12.c14
-rw-r--r--libc/elf/tst-tlsmod13.c14
-rw-r--r--libc/elf/tst-tlsmod13a.c16
-rw-r--r--libc/elf/tst-tlsmod14a.c41
-rw-r--r--libc/elf/tst-tlsmod14b.c2
-rw-r--r--libc/elf/tst-tlsmod15a.c6
-rw-r--r--libc/elf/tst-tlsmod15b.c17
-rw-r--r--libc/elf/tst-tlsmod2.c38
-rw-r--r--libc/elf/tst-tlsmod3.c41
-rw-r--r--libc/elf/tst-tlsmod4.c38
-rw-r--r--libc/elf/tst-tlsmod5.c7
-rw-r--r--libc/elf/tst-tlsmod6.c7
-rw-r--r--libc/elf/tst-tlsmod7.c103
-rw-r--r--libc/elf/tst-tlsmod8.c72
-rw-r--r--libc/elf/tst-tlsmod9.c101
-rw-r--r--libc/elf/unload.c91
-rw-r--r--libc/elf/unload2.c59
-rw-r--r--libc/elf/unload2dep.c6
-rw-r--r--libc/elf/unload2mod.c8
-rw-r--r--libc/elf/unload3.c41
-rw-r--r--libc/elf/unload3mod1.c1
-rw-r--r--libc/elf/unload3mod2.c1
-rw-r--r--libc/elf/unload3mod3.c8
-rw-r--r--libc/elf/unload3mod4.c13
-rw-r--r--libc/elf/unload4.c48
-rw-r--r--libc/elf/unload4mod1.c10
-rw-r--r--libc/elf/unload4mod2.c8
-rw-r--r--libc/elf/unload4mod3.c16
-rw-r--r--libc/elf/unload4mod4.c16
-rw-r--r--libc/elf/unload5.c42
-rw-r--r--libc/elf/unload6.c30
-rw-r--r--libc/elf/unload6mod1.c16
-rw-r--r--libc/elf/unload6mod2.c23
-rw-r--r--libc/elf/unload6mod3.c23
-rw-r--r--libc/elf/unloadmod.c4
-rw-r--r--libc/elf/vismain.c257
-rw-r--r--libc/elf/vismod.h27
-rw-r--r--libc/elf/vismod1.c104
-rw-r--r--libc/elf/vismod2.c124
-rw-r--r--libc/elf/vismod3.c47
284 files changed, 33256 insertions, 0 deletions
diff --git a/libc/elf/.cvsignore b/libc/elf/.cvsignore
new file mode 100644
index 000000000..3fc9f4cdf
--- /dev/null
+++ b/libc/elf/.cvsignore
@@ -0,0 +1,6 @@
+*.d *.o *.so *.po *.go stamp.* *.stamp *.ustamp *.udeps
+*.gz *.Z *.tar *.tgz
+=*
+TODO COPYING* AUTHORS copyr-* copying.*
+glibc-*
+distinfo
diff --git a/libc/elf/Makefile b/libc/elf/Makefile
new file mode 100644
index 000000000..3b4ef26d4
--- /dev/null
+++ b/libc/elf/Makefile
@@ -0,0 +1,908 @@
+# Copyright (C) 1995-2004, 2005, 2006 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, write to the Free
+# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+# 02111-1307 USA.
+
+# Makefile for elf subdirectory of GNU C Library.
+
+subdir := elf
+
+headers = elf.h bits/elfclass.h link.h bits/link.h
+routines = $(dl-routines) dl-support dl-iteratephdr \
+ dl-addr enbl-secure dl-profstub \
+ dl-origin dl-libc dl-sym dl-tsd
+
+# The core dynamic linking functions are in libc for the static and
+# profiled libraries.
+dl-routines = $(addprefix dl-,load cache lookup object reloc deps \
+ runtime error init fini debug misc \
+ version profile conflict tls origin \
+ execstack caller open close trampoline)
+all-dl-routines = $(dl-routines) $(sysdep-dl-routines)
+# But they are absent from the shared libc, because that code is in ld.so.
+elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin
+shared-only-routines += dl-caller
+
+# ld.so uses those routines, plus some special stuff for being the program
+# interpreter and operating independent of libc.
+rtld-routines := rtld $(dl-routines) dl-sysdep dl-environ dl-minimal
+all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
+
+distribute := rtld-Rules \
+ $(rtld-routines:=.c) dynamic-link.h do-rel.h dl-machine.h \
+ dl-cache.h dl-hash.h soinit.c sofini.c ldd.bash.in \
+ genrtldtbl.awk atomicity.h dl-procinfo.h ldsodefs.h \
+ dl-librecon.h interp.c sln.c dl-dst.h hp-timing.h \
+ do-lookup.h dl-lookupcfg.h sprof.c gen-trusted-dirs.awk \
+ testobj1.c testobj2.c testobj3.c testobj4.c testobj5.c \
+ testobj6.c testobj1_1.c failobj.c unloadmod.c \
+ ldconfig.h ldconfig.c cache.c readlib.c readelflib.c \
+ chroot_canon.c gccframe.h \
+ dep1.c dep2.c dep3.c dep4.c dl-dtprocnum.h unsecvars.h \
+ vismain.c vismod1.c vismod2.c vismod3.c \
+ constload2.c constload3.c filtmod1.c filtmod2.c \
+ nodlopenmod.c nodelete.c nodelmod1.c nodelmod2.c \
+ nodelmod3.c nodelmod4.c nodlopen.c dl-osinfo.h \
+ reldepmod1.c reldepmod2.c reldepmod3.c reldepmod4.c \
+ reldepmod5.c reldepmod6.c \
+ reldep4mod1.c reldep4mod2.c reldep4mod3.c reldep4mod4.c \
+ nextmod1.c nextmod2.c pathoptobj.c tst-pathopt.sh \
+ neededobj1.c neededobj2.c neededobj3.c neededobj4.c \
+ neededobj5.c neededobj6.c firstobj.c \
+ unload2mod.c unload2dep.c ltglobmod1.c ltglobmod2.c \
+ testobj.h vismod.h globalmod1.c \
+ dblloadmod1.c dblloadmod2.c dblloadmod3.c \
+ reldep6mod4.c reldep6mod3.c reldep6mod2.c reldep6mod1.c \
+ reldep6mod0.c reldep7mod1.c reldep7mod2.c \
+ unwind-dw2.c unwind-dw2-fde.c unwind.h unwind-pe.h \
+ unwind-dw2-fde.h dwarf2.h dl-procinfo.c tls.h dl-tls.h \
+ tst-tlsmod1.c tst-tlsmod2.c tst-tlsmod3.c tst-tlsmod4.c \
+ tst-tlsmod5.c tst-tlsmod6.c tst-tlsmod7.c tst-tlsmod8.c \
+ tst-tlsmod9.c tst-tlsmod10.c tst-tlsmod11.c \
+ tst-tlsmod12.c tst-tls10.h tst-alignmod.c tst-alignmod2.c \
+ circlemod1.c circlemod1a.c circlemod2.c circlemod2a.c \
+ circlemod3.c circlemod3a.c nodlopenmod2.c \
+ tls-macros.h \
+ reldep8mod1.c reldep8mod2.c reldep8mod3.c \
+ nodel2mod1.c nodel2mod2.c nodel2mod3.c \
+ reldep9.c reldep9mod1.c reldep9mod2.c reldep9mod3.c \
+ tst-array1.exp tst-array2.exp tst-array4.exp \
+ tst-array2dep.c tst-piemod1.c \
+ tst-execstack-mod.c tst-dlmodcount.c \
+ check-textrel.c dl-sysdep.h test-dlopenrpathmod.c \
+ tst-deep1mod1.c tst-deep1mod2.c tst-deep1mod3.c \
+ unload3mod1.c unload3mod2.c unload3mod3.c unload3mod4.c \
+ unload4mod1.c unload4mod2.c unload4mod3.c unload4mod4.c \
+ unload6mod1.c unload6mod2.c unload6mod3.c \
+ tst-auditmod1.c tst-audit.sh \
+ order2mod1.c order2mod2.c order2mod3.c order2mod4.c \
+ tst-stackguard1.c tst-stackguard1-static.c \
+ tst-array5.c tst-array5-static.c tst-array5dep.c \
+ tst-array5.exp tst-leaks1.c
+
+CFLAGS-dl-runtime.c = -fexceptions -fasynchronous-unwind-tables
+CFLAGS-dl-lookup.c = -fexceptions -fasynchronous-unwind-tables
+CFLAGS-dl-iterate-phdr.c = $(uses-callbacks)
+
+include ../Makeconfig
+
+ifeq ($(unwind-find-fde),yes)
+routines += unwind-dw2-fde-glibc
+shared-only-routines += unwind-dw2-fde-glibc
+endif
+
+before-compile = $(objpfx)trusted-dirs.h
+generated := trusted-dirs.h trusted-dirs.st for-renamed/renamed.so
+generated-dirs := for-renamed
+
+ifeq ($(versioning),yes)
+ld-map = $(common-objpfx)ld.map
+endif
+
+ifeq (yes,$(build-shared))
+extra-objs = $(all-rtld-routines:%=%.os) soinit.os sofini.os interp.os
+generated += librtld.os dl-allobjs.os ld.so ldd
+install-others = $(inst_slibdir)/$(rtld-installed-name)
+install-bin-script = ldd
+endif
+
+others = sprof sln
+install-bin = sprof
+others-static = sln
+install-rootsbin = sln
+
+ifeq (yes,$(use-ldconfig))
+ifeq (yes,$(build-shared))
+others-static += ldconfig
+others += ldconfig
+install-rootsbin += ldconfig
+
+ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon
+extra-objs += $(ldconfig-modules:=.o)
+
+# To find xmalloc.c and xstrdup.c
+vpath %.c ../locale/programs
+
+endif
+endif
+
+tests = tst-tls1 tst-tls2 tst-tls9 tst-leaks1
+ifeq (yes,$(have-initfini-array))
+tests += tst-array1 tst-array2 tst-array3 tst-array4 tst-array5
+endif
+ifeq (yes,$(build-static))
+tests-static = tst-tls1-static tst-tls2-static tst-stackguard1-static
+ifeq (yesyesyes,$(build-static)$(build-shared)$(elf))
+tests-static += tst-tls9-static
+tst-tls9-static-ENV = \
+ LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn
+endif
+ifeq (yes,$(have-initfini-array))
+tests-static += tst-array1-static tst-array5-static
+endif
+tests += $(tests-static)
+endif
+ifeq (yes,$(build-shared))
+tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
+ constload1 order $(tests-vis-$(have-protected)) noload filter unload \
+ reldep reldep2 reldep3 reldep4 $(tests-nodelete-$(have-z-nodelete)) \
+ $(tests-nodlopen-$(have-z-nodlopen)) neededtest neededtest2 \
+ neededtest3 neededtest4 unload2 lateglobal initfirst global \
+ restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \
+ circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8 \
+ tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-tls15 \
+ tst-tls-dlinfo \
+ tst-align tst-align2 $(tests-execstack-$(have-z-execstack)) \
+ tst-dlmodcount tst-dlopenrpath tst-deep1 \
+ tst-dlmopen1 tst-dlmopen2 tst-dlmopen3 \
+ unload3 unload4 unload5 unload6 tst-global1 order2 \
+ tst-audit1 tst-audit2 \
+ tst-stackguard1
+# reldep9
+test-srcs = tst-pathopt
+tests-vis-yes = vismain
+tests-nodelete-yes = nodelete nodelete2
+tests-nodlopen-yes = nodlopen nodlopen2
+tests-execstack-yes = tst-execstack tst-execstack-needed tst-execstack-prog
+endif
+ifeq (yesyes,$(have-fpie)$(build-shared))
+tests: $(objpfx)tst-pie1.out
+endif
+tests: $(objpfx)tst-leaks1-mem
+modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
+ testobj1_1 failobj constload2 constload3 unloadmod \
+ dep1 dep2 dep3 dep4 $(modules-vis-$(have-protected)) \
+ $(modules-nodelete-$(have-z-nodelete)) \
+ $(modules-nodlopen-$(have-z-nodlopen)) filtmod1 filtmod2 \
+ reldepmod1 reldepmod2 reldepmod3 reldepmod4 nextmod1 nextmod2 \
+ reldep4mod1 reldep4mod2 reldep4mod3 reldep4mod4 \
+ neededobj1 neededobj2 neededobj3 neededobj4 \
+ neededobj5 neededobj6 firstobj globalmod1 \
+ unload2mod unload2dep ltglobmod1 ltglobmod2 pathoptobj \
+ dblloadmod1 dblloadmod2 dblloadmod3 reldepmod5 reldepmod6 \
+ reldep6mod0 reldep6mod1 reldep6mod2 reldep6mod3 reldep6mod4 \
+ reldep7mod1 reldep7mod2 \
+ tst-tlsmod1 tst-tlsmod2 tst-tlsmod3 tst-tlsmod4 \
+ tst-tlsmod5 tst-tlsmod6 tst-tlsmod7 tst-tlsmod8 \
+ tst-tlsmod9 tst-tlsmod10 tst-tlsmod11 tst-tlsmod12 \
+ tst-tlsmod13 tst-tlsmod13a tst-tlsmod14a tst-tlsmod14b \
+ tst-tlsmod15a tst-tlsmod15b \
+ circlemod1 circlemod1a circlemod2 circlemod2a \
+ circlemod3 circlemod3a \
+ reldep8mod1 reldep8mod2 reldep8mod3 \
+ reldep9mod1 reldep9mod2 reldep9mod3 \
+ tst-alignmod tst-alignmod2 \
+ $(modules-execstack-$(have-z-execstack)) \
+ tst-dlopenrpathmod tst-deep1mod1 tst-deep1mod2 tst-deep1mod3 \
+ tst-dlmopen1mod tst-auditmod1 \
+ unload3mod1 unload3mod2 unload3mod3 unload3mod4 \
+ unload4mod1 unload4mod2 unload4mod3 unload4mod4 \
+ unload6mod1 unload6mod2 unload6mod3 \
+ order2mod1 order2mod2 order2mod3 order2mod4
+ifeq (yes,$(have-initfini-array))
+modules-names += tst-array2dep tst-array5dep
+endif
+ifeq (yesyes,$(have-fpie)$(build-shared))
+modules-names += tst-piemod1
+endif
+modules-vis-yes = vismod1 vismod2 vismod3
+modules-nodelete-yes = nodelmod1 nodelmod2 nodelmod3 nodelmod4 \
+ nodel2mod1 nodel2mod2 nodel2mod3
+modules-nodlopen-yes = nodlopenmod nodlopenmod2
+modules-execstack-yes = tst-execstack-mod
+extra-objs += $(addsuffix .os,$(strip $(modules-names)))
+# We need this variable to be sure the test modules get the right CPPFLAGS.
+test-extras += $(modules-names)
+
+# filtmod1.so has a special rule
+modules-names-nobuild := filtmod1
+
+
+include ../Rules
+
+check-abi: check-abi-ld
+update-abi: update-abi-ld
+
+ifeq (yes,$(build-shared))
+# Make sure these things are built in the `make lib' pass so they can be used
+# to run programs during the `make others' pass.
+lib-noranlib: $(objpfx)$(rtld-installed-name) \
+ $(addprefix $(objpfx),$(extra-objs))
+endif
+
+# Command to link into a larger single relocatable object.
+reloc-link = $(LINK.o) -nostdlib -nostartfiles -r
+
+$(objpfx)dl-allobjs.os: $(all-rtld-routines:%=$(objpfx)%.os)
+ $(reloc-link) -o $@ $^
+
+# Link together the dynamic linker into a single relocatable object.
+# First we do a link against libc_pic.a just to get a link map,
+# and discard the object produced by that link. From the link map
+# we can glean all the libc modules that need to go into the dynamic
+# linker. Then we do a recursive make that goes into all the subdirs
+# those modules come from and builds special rtld-foo.os versions that
+# are compiled with special flags, and puts these modules into rtld-libc.a
+# for us. Then we do the real link using rtld-libc.a instead of libc_pic.a.
+
+$(objpfx)librtld.map: $(objpfx)dl-allobjs.os $(common-objpfx)libc_pic.a
+ @-rm -f $@T
+ $(reloc-link) -o $@.o '-Wl,-(' $^ -lgcc '-Wl,-)' -Wl,-Map,$@T
+ rm -f $@.o
+ mv -f $@T $@
+
+$(objpfx)librtld.mk: $(objpfx)librtld.map Makefile
+ LC_ALL=C \
+ sed -n 's@^$(common-objpfx)\([^(]*\)(\([^)]*\.os\)) *.*$$@\1 \2@p' \
+ $< | \
+ while read lib file; do \
+ case $$lib in \
+ libc_pic.a) \
+ LC_ALL=C fgrep -l /$$file \
+ $(common-objpfx)stamp.os $(common-objpfx)*/stamp.os | \
+ LC_ALL=C \
+ sed 's@^$(common-objpfx)\([^/]*\)/stamp\.os$$@rtld-\1'" +=$$file@"\
+ ;; \
+ */*.a) \
+ echo rtld-$${lib%%/*} += $$file ;; \
+ *) echo "Wasn't expecting $$lib($$file)" >&2; exit 1 ;; \
+ esac; \
+ done > $@T
+ echo rtld-subdirs = `LC_ALL=C sed 's/^rtld-\([^ ]*\).*$$/\1/' $@T \
+ | LC_ALL=C sort -u` >> $@T
+ mv -f $@T $@
+
+$(objpfx)rtld-libc.a: $(objpfx)librtld.mk FORCE
+ $(MAKE) -f $< -f rtld-Rules
+
+$(objpfx)librtld.os: $(objpfx)dl-allobjs.os $(objpfx)rtld-libc.a
+ $(LINK.o) -nostdlib -nostartfiles -r -o $@ '-Wl,-(' $^ -lgcc '-Wl,-)' \
+ -Wl,-Map,$@.map
+
+generated += librtld.map librtld.mk rtld-libc.a librtld.os.map
+
+z-now-yes = -Wl,-z,now
+
+$(objpfx)ld.so: $(objpfx)librtld.os $(ld-map)
+ @rm -f $@.lds
+ $(LINK.o) -nostdlib -nostartfiles -shared $(z-now-$(bind-now)) \
+ $(LDFLAGS-rtld) -Wl,-z,defs -Wl,--verbose 2>&1 | \
+ LC_ALL=C \
+ sed -e '/^=========/,/^=========/!d;/^=========/d' \
+ -e 's/\. = 0 + SIZEOF_HEADERS;/& _begin = . - SIZEOF_HEADERS;/' \
+ > $@.lds
+ $(LINK.o) -nostdlib -nostartfiles -shared -o $@ \
+ $(LDFLAGS-rtld) -Wl,-z,defs $(z-now-$(bind-now)) \
+ $(filter-out $(map-file),$^) $(load-map-file) \
+ -Wl,-soname=$(rtld-installed-name) -T $@.lds
+ rm -f $@.lds
+
+# interp.c exists just to get this string into the libraries.
+CFLAGS-interp.c = -D'RUNTIME_LINKER="$(slibdir)/$(rtld-installed-name)"' \
+ -DNOT_IN_libc=1
+$(objpfx)interp.os: $(common-objpfx)config.make
+
+ifneq (ld.so,$(rtld-installed-name))
+# Make sure ld.so.1 exists in the build directory so we can link
+# against it.
+$(objpfx)$(rtld-installed-name): $(objpfx)ld.so
+ rm -f $@
+ ln -s $(<F) $@
+generated += $(rtld-installed-name)
+endif
+
+# Build a file mentioning all trustworthy directories to look for shared
+# libraries when using LD_LIBRARY_PATH in a setuid program. The user can
+# add directories to the list by defining $(user-defined-trusted-dirs)
+# before starting make.
+$(objpfx)trusted-dirs.h: $(objpfx)trusted-dirs.st; @:
+$(objpfx)trusted-dirs.st: Makefile $(..)Makeconfig
+ $(make-target-directory)
+ echo "$(subst :, ,$(default-rpath) $(user-defined-trusted-dirs))" \
+ | $(AWK) -f gen-trusted-dirs.awk > ${@:st=T};
+ echo '#define DL_DST_LIB "$(notdir $(slibdir))"' >> ${@:st=T}
+ $(move-if-change) ${@:st=T} ${@:st=h}
+ touch $@
+CPPFLAGS-dl-load.c = -I$(objpfx). -I$(csu-objpfx).
+
+ifeq (yes,$(build-shared))
+$(inst_slibdir)/$(rtld-version-installed-name): $(objpfx)ld.so $(+force)
+ $(make-target-directory)
+ $(do-install-program)
+
+$(inst_slibdir)/$(rtld-installed-name): \
+ $(inst_slibdir)/$(rtld-version-installed-name) \
+ $(inst_slibdir)/libc-$(version).so
+ $(make-shlib-link)
+
+# Special target called by parent to install just the dynamic linker.
+.PHONY: ldso_install
+ldso_install: $(inst_slibdir)/$(rtld-installed-name)
+endif
+
+
+common-ldd-rewrite = -e 's%@RTLD@%$(slibdir)/$(rtld-installed-name)%g' \
+ -e 's%@VERSION@%$(version)%g'
+sh-ldd-rewrite = $(common-ldd-rewrite) -e 's%@BASH@%/bin/sh%g;s/\$$"/"/g'
+bash-ldd-rewrite = $(common-ldd-rewrite) -e 's%@BASH@%$(BASH)%g' \
+ -e 's%@TEXTDOMAINDIR@%$(msgcatdir)%g'
+
+ifneq ($(have-bash2),yes)
+ldd-shell = sh
+else
+ldd-shell = bash
+endif
+
+ifeq ($(ldd-rewrite-script),no)
+define gen-ldd
+LC_ALL=C sed $($(ldd-shell)-ldd-rewrite) < $< > $@.new
+endef
+else
+define gen-ldd
+LC_ALL=C sed $($(ldd-shell)-ldd-rewrite) < $< | LC_ALL=C sed -f $(ldd-rewrite-script) > $@.new
+endef
+endif
+
+$(objpfx)ldd: ldd.bash.in $(common-objpfx)soversions.mk \
+ $(common-objpfx)config.make
+ $(gen-ldd)
+ chmod 555 $@.new
+ mv -f $@.new $@
+
+$(objpfx)sprof: $(libdl)
+
+$(objpfx)ldconfig: $(ldconfig-modules:%=$(objpfx)%.o)
+SYSCONF-FLAGS := -D'SYSCONFDIR="$(sysconfdir)"'
+CFLAGS-ldconfig.c = $(SYSCONF-FLAGS) -D'LIBDIR="$(libdir)"' \
+ -D'SLIBDIR="$(slibdir)"' -DIS_IN_ldconfig=1
+CFLAGS-dl-cache.c = $(SYSCONF-FLAGS)
+CFLAGS-cache.c = $(SYSCONF-FLAGS)
+
+CPPFLAGS-.os += $(if $(filter $(@F),$(patsubst %,%.os,$(all-rtld-routines))),-DNOT_IN_libc=1 -DIS_IN_rtld=1)
+
+test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(strip $(modules-names))))
+generated += $(addsuffix .so,$(strip $(modules-names)))
+
+ifeq (yes,$(build-shared))
+ifeq ($(cross-compiling),no)
+tests: $(objpfx)tst-pathopt.out
+endif
+endif
+
+$(objpfx)testobj1.so: $(libdl)
+$(objpfx)testobj1_1.so: $(objpfx)testobj1.so $(libdl)
+$(objpfx)testobj2.so: $(objpfx)testobj1.so $(libdl)
+$(objpfx)testobj3.so: $(libdl)
+$(objpfx)testobj4.so: $(libdl)
+$(objpfx)testobj5.so: $(libdl)
+$(objpfx)testobj6.so: $(objpfx)testobj1.so $(objpfx)testobj2.so $(libdl)
+$(objpfx)failobj.so: $(objpfx)testobj6.so
+$(objpfx)dep1.so: $(objpfx)dep2.so $(objpfx)dep4.so
+$(objpfx)dep2.so: $(objpfx)dep3.so $(objpfx)dep4.so
+$(objpfx)dep4.so: $(objpfx)dep3.so
+$(objpfx)nodelmod3.so: $(objpfx)nodelmod4.so
+$(objpfx)nextmod1.so: $(libdl)
+$(objpfx)neededobj1.so: $(libdl)
+$(objpfx)neededobj2.so: $(objpfx)neededobj1.so $(libdl)
+$(objpfx)neededobj3.so: $(objpfx)neededobj1.so $(objpfx)neededobj2.so $(libdl)
+$(objpfx)neededobj4.so: $(objpfx)neededobj1.so $(objpfx)neededobj2.so \
+ $(objpfx)neededobj3.so $(libdl)
+$(objpfx)neededobj6.so: $(objpfx)neededobj5.so
+$(objpfx)unload2mod.so: $(objpfx)unload2dep.so
+$(objpfx)ltglobmod2.so: $(libdl)
+$(objpfx)firstobj.so: $(shared-thread-library)
+$(objpfx)globalmod1.so: $(libdl)
+$(objpfx)reldep4mod1.so: $(objpfx)reldep4mod3.so
+$(objpfx)reldep4mod2.so: $(objpfx)reldep4mod4.so
+$(objpfx)dblloadmod1.so: $(objpfx)dblloadmod3.so
+$(objpfx)dblloadmod2.so: $(objpfx)dblloadmod3.so
+$(objpfx)reldepmod5.so: $(objpfx)reldepmod2.so
+$(objpfx)reldepmod6.so: $(objpfx)reldepmod2.so
+$(objpfx)reldep6mod1.so: $(objpfx)reldep6mod0.so
+$(objpfx)reldep6mod2.so: $(objpfx)reldep6mod1.so
+$(objpfx)reldep6mod3.so: $(objpfx)reldep6mod2.so
+$(objpfx)reldep6mod4.so: $(objpfx)reldep6mod1.so
+$(objpfx)tst-tlsmod3.so: $(objpfx)tst-tlsmod2.so
+$(objpfx)tst-tlsmod8.so: $(objpfx)tst-tlsmod7.so
+$(objpfx)tst-tlsmod10.so: $(objpfx)tst-tlsmod9.so
+$(objpfx)tst-tlsmod12.so: $(objpfx)tst-tlsmod11.so
+$(objpfx)tst-tlsmod13a.so: $(objpfx)tst-tlsmod13.so
+# For tst-tls9-static, make sure the modules it dlopens have libc.so in DT_NEEDED
+$(objpfx)tst-tlsmod5.so: $(common-objpfx)libc.so
+$(objpfx)tst-tlsmod6.so: $(common-objpfx)libc.so
+$(objpfx)reldep8mod3.so: $(objpfx)reldep8mod1.so $(objpfx)reldep8mod2.so
+$(objpfx)nodel2mod3.so: $(objpfx)nodel2mod1.so $(objpfx)nodel2mod2.so
+$(objpfx)reldep9mod2.so: $(objpfx)reldep9mod1.so
+$(objpfx)reldep9mod3.so: $(objpfx)reldep9mod1.so $(objpfx)reldep9mod2.so
+$(objpfx)unload3mod1.so: $(objpfx)unload3mod3.so
+$(objpfx)unload3mod2.so: $(objpfx)unload3mod3.so
+$(objpfx)unload3mod3.so: $(objpfx)unload3mod4.so
+$(objpfx)unload4mod1.so: $(objpfx)unload4mod2.so $(objpfx)unload4mod3.so
+$(objpfx)unload4mod2.so: $(objpfx)unload4mod4.so $(objpfx)unload4mod3.so
+$(objpfx)unload6mod1.so: $(libdl)
+$(objpfx)unload6mod2.so: $(libdl)
+$(objpfx)unload6mod3.so: $(libdl)
+
+LDFLAGS-tst-tlsmod5.so = -nostdlib
+LDFLAGS-tst-tlsmod6.so = -nostdlib
+
+testobj1.so-no-z-defs = yes
+testobj3.so-no-z-defs = yes
+testobj4.so-no-z-defs = yes
+testobj5.so-no-z-defs = yes
+testobj6.so-no-z-defs = yes
+failobj.so-no-z-defs = yes
+constload2.so-no-z-defs = yes
+constload3.so-no-z-defs = yes
+nodelmod1.so-no-z-defs = yes
+nodelmod2.so-no-z-defs = yes
+nodelmod4.so-no-z-defs = yes
+nodel2mod2.so-no-z-defs = yes
+reldepmod2.so-no-z-defs = yes
+reldepmod3.so-no-z-defs = yes
+reldepmod4.so-no-z-defs = yes
+reldep4mod4.so-no-z-defs = yes
+reldep4mod2.so-no-z-defs = yes
+ltglobmod2.so-no-z-defs = yes
+dblloadmod3.so-no-z-defs = yes
+tst-tlsmod1.so-no-z-defs = yes
+tst-tlsmod2.so-no-z-defs = yes
+tst-tlsmod3.so-no-z-defs = yes
+tst-tlsmod4.so-no-z-defs = yes
+tst-tlsmod7.so-no-z-defs = yes
+tst-tlsmod8.so-no-z-defs = yes
+tst-tlsmod9.so-no-z-defs = yes
+tst-tlsmod10.so-no-z-defs = yes
+tst-tlsmod12.so-no-z-defs = yes
+tst-tlsmod14a.so-no-z-defs = yes
+tst-tlsmod14b.so-no-z-defs = yes
+tst-tlsmod15a.so-no-z-defs = yes
+circlemod2.so-no-z-defs = yes
+circlemod3.so-no-z-defs = yes
+circlemod3a.so-no-z-defs = yes
+reldep8mod2.so-no-z-defs = yes
+reldep9mod1.so-no-z-defs = yes
+unload3mod4.so-no-z-defs = yes
+unload4mod1.so-no-z-defs = yes
+
+ifeq ($(build-shared),yes)
+# Build all the modules even when not actually running test programs.
+tests: $(test-modules)
+endif
+
+$(objpfx)loadtest: $(libdl)
+LDFLAGS-loadtest = -rdynamic
+
+$(objpfx)loadtest.out: $(test-modules)
+
+$(objpfx)neededtest: $(libdl)
+$(objpfx)neededtest.out: $(objpfx)neededobj1.so $(objpfx)neededobj2.so \
+ $(objpfx)neededobj3.so
+
+$(objpfx)neededtest2: $(libdl)
+$(objpfx)neededtest2.out: $(objpfx)neededobj1.so $(objpfx)neededobj2.so \
+ $(objpfx)neededobj3.so
+
+$(objpfx)neededtest3: $(libdl)
+$(objpfx)neededtest3.out: $(objpfx)neededobj1.so $(objpfx)neededobj2.so \
+ $(objpfx)neededobj3.so $(objpfx)neededobj4.so
+
+neededtest4-ENV = LC_ALL=C LANGUAGE=C
+$(objpfx)neededtest4: $(libdl) $(objpfx)neededobj1.so
+$(objpfx)neededtest4.out: $(objpfx)neededobj5.so $(objpfx)neededobj6.so
+
+$(objpfx)restest1: $(objpfx)testobj1.so $(objpfx)testobj1_1.so $(libdl)
+LDFLAGS-restest1 = -rdynamic
+
+$(objpfx)restest2: $(libdl)
+LDFLAGS-restest2 = -rdynamic
+
+$(objpfx)restest1.out: $(test-modules)
+
+preloadtest-preloads = testobj1 testobj2 testobj3 testobj4 testobj5
+$(objpfx)preloadtest: $(objpfx)testobj6.so
+LDFLAGS-preloadtest = -rdynamic
+$(objpfx)preloadtest.out: $(preloadtest-preloads:%=$(objpfx)%.so)
+preloadtest-ENV = \
+ LD_PRELOAD=$(subst $(empty) ,:,$(strip $(preloadtest-preloads:=.so)))
+
+$(objpfx)loadfail: $(libdl)
+LDFLAGS-loadfail = -rdynamic
+
+$(objpfx)loadfail.out: $(objpfx)failobj.so
+
+$(objpfx)multiload: $(libdl)
+LDFLAGS-multiload = -rdynamic
+CFLAGS-multiload.c = -DOBJDIR=\"$(elf-objpfx)\"
+
+$(objpfx)multiload.out: $(objpfx)testobj1.so
+
+$(objpfx)origtest: $(libdl)
+LDFLAGS-origtest = -rdynamic
+$(objpfx)origtest.out: $(objpfx)testobj1.so
+
+ifeq ($(have-thread-library),yes)
+$(objpfx)resolvfail: $(libdl) $(shared-thread-library)
+else
+$(objpfx)resolvfail: $(libdl)
+endif
+
+$(objpfx)constload1: $(libdl)
+$(objpfx)constload1.out: $(objpfx)constload2.so $(objpfx)constload3.so
+
+$(objpfx)circleload1: $(libdl)
+$(objpfx)circleload1.out: $(objpfx)circlemod1.so \
+ $(objpfx)circlemod1a.so
+
+$(objpfx)circlemod1.so: $(objpfx)circlemod2.so
+$(objpfx)circlemod2.so: $(objpfx)circlemod3.so
+$(objpfx)circlemod1a.so: $(objpfx)circlemod2a.so
+$(objpfx)circlemod2a.so: $(objpfx)circlemod3a.so
+
+$(objpfx)order: $(addprefix $(objpfx),dep4.so dep3.so dep2.so dep1.so)
+
+$(objpfx)order.out: $(objpfx)order
+ $(elf-objpfx)$(rtld-installed-name) \
+ --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \
+ $(objpfx)order > $@
+ (echo "0123456789" | cmp $@ -) > /dev/null
+
+$(objpfx)vismain: $(addprefix $(objpfx),vismod1.so vismod2.so)
+$(objpfx)vismain.out: $(addprefix $(objpfx),vismod3.so)
+vismain-ENV = LD_PRELOAD=$(addprefix $(objpfx),vismod3.so)
+
+$(objpfx)noload: $(objpfx)testobj1.so
+LDFLAGS-noload = -rdynamic
+$(objpfx)noload.out: $(objpfx)testobj5.so
+
+LDFLAGS-nodelete = -rdynamic
+LDFLAGS-nodelmod1.so = -Wl,--enable-new-dtags,-z,nodelete
+LDFLAGS-nodelmod4.so = -Wl,--enable-new-dtags,-z,nodelete
+$(objpfx)nodelete: $(libdl)
+$(objpfx)nodelete.out: $(objpfx)nodelmod1.so $(objpfx)nodelmod2.so \
+ $(objpfx)nodelmod3.so
+
+LDFLAGS-nodlopenmod.so = -Wl,--enable-new-dtags,-z,nodlopen
+$(objpfx)nodlopen: $(libdl)
+$(objpfx)nodlopen.out: $(objpfx)nodlopenmod.so
+
+$(objpfx)nodlopenmod2.so: $(objpfx)nodlopenmod.so
+$(objpfx)nodlopen2: $(libdl)
+$(objpfx)nodlopen2.out: $(objpfx)nodlopenmod2.so
+
+$(objpfx)filtmod1.so: $(objpfx)filtmod1.os $(objpfx)filtmod2.so
+ $(LINK.o) -shared -o $@ -B$(csu-objpfx) $(LDFLAGS.so) \
+ -L$(subst :, -L,$(rpath-link)) \
+ -Wl,-rpath-link=$(rpath-link) \
+ $< -Wl,-F,$(objpfx)filtmod2.so
+$(objpfx)filter: $(objpfx)filtmod1.so
+
+$(objpfx)unload: $(libdl)
+$(objpfx)unload.out: $(objpfx)unloadmod.so
+
+$(objpfx)reldep: $(libdl)
+$(objpfx)reldep.out: $(objpfx)reldepmod1.so $(objpfx)reldepmod2.so
+
+$(objpfx)reldep2: $(libdl)
+$(objpfx)reldep2.out: $(objpfx)reldepmod1.so $(objpfx)reldepmod3.so
+
+$(objpfx)reldep3: $(libdl)
+$(objpfx)reldep3.out: $(objpfx)reldepmod1.so $(objpfx)reldepmod4.so
+
+$(objpfx)reldep4: $(libdl)
+$(objpfx)reldep4.out: $(objpfx)reldep4mod1.so $(objpfx)reldep4mod2.so
+
+$(objpfx)next: $(objpfx)nextmod1.so $(objpfx)nextmod2.so $(libdl)
+
+$(objpfx)unload2: $(libdl)
+$(objpfx)unload2.out: $(objpfx)unload2mod.so $(objpfx)unload2dep.so
+
+$(objpfx)lateglobal: $(libdl)
+$(objpfx)lateglobal.out: $(objpfx)ltglobmod1.so $(objpfx)ltglobmod2.so
+
+$(objpfx)tst-pathopt: $(libdl)
+$(objpfx)tst-pathopt.out: tst-pathopt.sh $(objpfx)tst-pathopt \
+ $(objpfx)pathoptobj.so
+ $(SHELL) -e $< $(common-objpfx)
+
+$(objpfx)initfirst: $(libdl)
+$(objpfx)initfirst.out: $(objpfx)firstobj.so
+
+$(objpfx)global: $(objpfx)globalmod1.so
+$(objpfx)global.out: $(objpfx)reldepmod1.so
+
+$(objpfx)dblload: $(libdl)
+$(objpfx)dblload.out: $(objpfx)dblloadmod1.so $(objpfx)dblloadmod2.so
+
+$(objpfx)dblunload: $(libdl)
+$(objpfx)dblunload.out: $(objpfx)dblloadmod1.so $(objpfx)dblloadmod2.so
+
+$(objpfx)reldep5: $(libdl)
+$(objpfx)reldep5.out: $(objpfx)reldepmod5.so $(objpfx)reldepmod6.so
+
+$(objpfx)reldep6: $(libdl)
+$(objpfx)reldep6.out: $(objpfx)reldep6mod3.so $(objpfx)reldep6mod4.so
+
+$(objpfx)reldep7: $(libdl)
+$(objpfx)reldep7.out: $(objpfx)reldep7mod1.so $(objpfx)reldep7mod2.so
+
+$(objpfx)reldep8: $(libdl)
+$(objpfx)reldep8.out: $(objpfx)reldep8mod3.so
+
+LDFLAGS-nodel2mod2.so = -Wl,--enable-new-dtags,-z,nodelete
+$(objpfx)nodelete2: $(libdl)
+$(objpfx)nodelete2.out: $(objpfx)nodel2mod3.so
+
+$(objpfx)reldep9: $(libdl)
+$(objpfx)reldep9.out: $(objpfx)reldep9mod3.so
+
+$(objpfx)tst-tls3: $(objpfx)tst-tlsmod1.so
+
+$(objpfx)tst-tls4: $(libdl)
+$(objpfx)tst-tls4.out: $(objpfx)tst-tlsmod2.so
+
+$(objpfx)tst-tls5: $(libdl)
+$(objpfx)tst-tls5.out: $(objpfx)tst-tlsmod2.so
+
+$(objpfx)tst-tls6: $(libdl)
+$(objpfx)tst-tls6.out: $(objpfx)tst-tlsmod2.so
+
+$(objpfx)tst-tls7: $(libdl)
+$(objpfx)tst-tls7.out: $(objpfx)tst-tlsmod3.so
+
+$(objpfx)tst-tls8: $(libdl)
+$(objpfx)tst-tls8.out: $(objpfx)tst-tlsmod3.so $(objpfx)tst-tlsmod4.so
+
+$(objpfx)tst-tls9: $(libdl)
+$(objpfx)tst-tls9.out: $(objpfx)tst-tlsmod5.so $(objpfx)tst-tlsmod6.so
+
+$(objpfx)tst-tls10: $(objpfx)tst-tlsmod8.so
+
+$(objpfx)tst-tls11: $(objpfx)tst-tlsmod10.so
+
+$(objpfx)tst-tls12: $(objpfx)tst-tlsmod12.so
+
+$(objpfx)tst-tls13: $(libdl)
+$(objpfx)tst-tls13.out: $(objpfx)tst-tlsmod13a.so
+
+$(objpfx)tst-tls14: $(objpfx)tst-tlsmod14a.so $(libdl)
+$(objpfx)tst-tls14.out: $(objpfx)tst-tlsmod14b.so
+
+$(objpfx)tst-tls15: $(libdl)
+$(objpfx)tst-tls15.out: $(objpfx)tst-tlsmod15a.so $(objpfx)tst-tlsmod15b.so
+
+$(objpfx)tst-tls-dlinfo: $(libdl)
+$(objpfx)tst-tls-dlinfo.out: $(objpfx)tst-tlsmod2.so
+
+
+
+CFLAGS-tst-align.c = $(stack-align-test-flags)
+CFLAGS-tst-align2.c = $(stack-align-test-flags)
+CFLAGS-tst-alignmod.c = $(stack-align-test-flags)
+CFLAGS-tst-alignmod2.c = $(stack-align-test-flags)
+$(objpfx)tst-align: $(libdl)
+$(objpfx)tst-align.out: $(objpfx)tst-alignmod.so
+$(objpfx)tst-align2: $(objpfx)tst-alignmod2.so
+
+$(objpfx)unload3: $(libdl)
+$(objpfx)unload3.out: $(objpfx)unload3mod1.so $(objpfx)unload3mod2.so \
+ $(objpfx)unload3mod3.so $(objpfx)unload3mod4.so
+
+$(objpfx)unload4: $(libdl)
+$(objpfx)unload4.out: $(objpfx)unload4mod1.so $(objpfx)unload4mod3.so
+
+$(objpfx)unload5: $(libdl)
+$(objpfx)unload5.out: $(objpfx)unload3mod1.so $(objpfx)unload3mod2.so \
+ $(objpfx)unload3mod3.so $(objpfx)unload3mod4.so
+
+$(objpfx)unload6: $(libdl)
+$(objpfx)unload6.out: $(objpfx)unload6mod1.so $(objpfx)unload6mod2.so \
+ $(objpfx)unload6mod3.so
+
+ifdef libdl
+$(objpfx)tst-tls9-static: $(common-objpfx)dlfcn/libdl.a
+$(objpfx)tst-tls9-static.out: $(objpfx)tst-tlsmod5.so $(objpfx)tst-tlsmod6.so
+endif
+
+ifeq ($(have-z-execstack),yes)
+$(objpfx)tst-execstack: $(libdl)
+$(objpfx)tst-execstack.out: $(objpfx)tst-execstack-mod.so
+LDFLAGS-tst-execstack = -Wl,-z,noexecstack
+LDFLAGS-tst-execstack-mod = -Wl,-z,execstack
+
+$(objpfx)tst-execstack-needed: $(objpfx)tst-execstack-mod.so
+LDFLAGS-tst-execstack-needed = -Wl,-z,noexecstack
+
+LDFLAGS-tst-execstack-prog = -Wl,-z,execstack
+endif
+
+$(objpfx)tst-array1.out: $(objpfx)tst-array1
+ $(elf-objpfx)$(rtld-installed-name) \
+ --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \
+ $(objpfx)tst-array1 > $@
+ cmp $@ tst-array1.exp > /dev/null
+
+$(objpfx)tst-array1-static.out: $(objpfx)tst-array1-static
+ $(objpfx)tst-array1-static > $@
+ cmp $@ tst-array1.exp > /dev/null
+
+$(objpfx)tst-array2: $(objpfx)tst-array2dep.so
+$(objpfx)tst-array2.out: $(objpfx)tst-array2
+ $(elf-objpfx)$(rtld-installed-name) \
+ --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \
+ $(objpfx)tst-array2 > $@
+ cmp $@ tst-array2.exp > /dev/null
+
+$(objpfx)tst-array3.out: $(objpfx)tst-array3
+ $(elf-objpfx)$(rtld-installed-name) \
+ --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \
+ $(objpfx)tst-array3 > $@
+ cmp $@ tst-array1.exp > /dev/null
+
+$(objpfx)tst-array4: $(libdl)
+$(objpfx)tst-array4.out: $(objpfx)tst-array4 $(objpfx)tst-array2dep.so
+ $(elf-objpfx)$(rtld-installed-name) \
+ --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \
+ $< > $@
+ cmp $@ tst-array4.exp > /dev/null
+
+$(objpfx)tst-array5: $(objpfx)tst-array5dep.so
+$(objpfx)tst-array5.out: $(objpfx)tst-array5
+ $(elf-objpfx)$(rtld-installed-name) \
+ --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \
+ $(objpfx)tst-array5 > $@
+ cmp $@ tst-array5.exp > /dev/null
+
+$(objpfx)tst-array5-static.out: $(objpfx)tst-array5-static
+ $(objpfx)tst-array5-static > $@
+ cmp $@ tst-array5-static.exp > /dev/null
+
+ifeq (yesyes,$(have-fpie)$(build-shared))
+CFLAGS-tst-pie1.c += -fpie
+
+$(objpfx)tst-pie1.out: $(objpfx)tst-pie1
+ $(elf-objpfx)$(rtld-installed-name) \
+ --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \
+ $< > $@
+
+$(objpfx)tst-pie1: $(objpfx)tst-pie1.o $(objpfx)tst-piemod1.so
+ $(LINK.o) -pie -Wl,-O1 \
+ $(sysdep-LDFLAGS) $(config-LDFLAGS) \
+ $(extra-B-$(@F:lib%.so=%).so) -B$(csu-objpfx) \
+ $(extra-B-$(@F:lib%.so=%).so) $(load-map-file) \
+ $(LDFLAGS) $(LDFLAGS-$(@F)) \
+ -L$(subst :, -L,$(rpath-link)) -Wl,-rpath-link=$(rpath-link) \
+ -o $@ $(objpfx)tst-pie1.o $(objpfx)tst-piemod1.so \
+ $(common-objpfx)libc_nonshared.a
+
+generated += tst-pie1 tst-pie1.out tst-pie1.o
+endif
+
+check-textrel-CFLAGS = -O -Wall -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
+$(objpfx)check-textrel: check-textrel.c
+ $(native-compile)
+
+check-localplt-CFLAGS = -O -Wall -D_GNU_SOURCE -std=gnu99
+$(objpfx)check-localplt: check-localplt.c
+ $(native-compile)
+
+ifeq (yes,$(build-shared))
+tests: $(objpfx)check-textrel.out
+
+$(objpfx)check-textrel.out: $(objpfx)check-textrel
+ $(dir $<)$(notdir $<) $(common-objpfx)libc.so \
+ $(sort $(wildcard $(common-objpfx)*/lib*.so \
+ $(common-objpfx)iconvdata/*.so)) > $@
+generated += check-textrel check-textrel.out
+
+$(objpfx)tst-dlmodcount: $(libdl)
+$(objpfx)tst-dlmodcount.out: $(test-modules)
+
+check-data := $(firstword $(wildcard \
+ $(foreach M,$(config-machine) $(base-machine),\
+ ../scripts/data/localplt-$M-$(config-os).data)))
+ifneq (,$(check-data))
+tests: $(objpfx)check-localplt.out
+
+ifeq ($(have-thread-library),yes)
+thread-dso := $(filter-out %_nonshared.a, $(shared-thread-library))
+endif
+
+$(objpfx)check-localplt.out: $(objpfx)check-localplt $(common-objpfx)libc.so \
+ $(common-objpfx)math/libm.so $(thread-dso) \
+ $(common-objpfx)rt/librt.so \
+ $(common-objpfx)dlfcn/libdl.so \
+ $(check-data)
+ $(objpfx)check-localplt $(common-objpfx)libc.so \
+ $(common-objpfx)math/libm.so $(thread-dso) \
+ $(common-objpfx)rt/librt.so \
+ $(common-objpfx)dlfcn/libdl.so | \
+ LC_ALL=C sort | \
+ diff -u $(check-data) - > $@
+endif
+endif
+
+$(objpfx)tst-dlopenrpathmod.so: $(libdl)
+$(objpfx)tst-dlopenrpath: $(objpfx)tst-dlopenrpathmod.so $(libdl)
+CFLAGS-tst-dlopenrpath.c += -DPFX=\"$(objpfx)\"
+LDFLAGS-tst-dlopenrpathmod.so += -Wl,-rpath,\$$ORIGIN/test-subdir
+$(objpfx)tst-dlopenrpath.out: $(objpfx)firstobj.so
+
+$(objpfx)tst-deep1mod2.so: $(objpfx)tst-deep1mod3.so
+$(objpfx)tst-deep1: $(libdl) $(objpfx)tst-deep1mod1.so
+$(objpfx)tst-deep1.out: $(objpfx)tst-deep1mod2.so
+LDFLAGS-tst-deep1 += -rdynamic
+tst-deep1mod3.so-no-z-defs = yes
+
+$(objpfx)tst-dlmopen1mod.so: $(libdl)
+$(objpfx)tst-dlmopen1: $(libdl)
+$(objpfx)tst-dlmopen1.out: $(objpfx)tst-dlmopen1mod.so
+
+$(objpfx)tst-dlmopen2: $(libdl)
+$(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so
+
+$(objpfx)tst-dlmopen3: $(libdl)
+$(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so
+
+$(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so
+tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
+
+$(objpfx)tst-audit2.out: $(objpfx)tst-auditmod1.so
+tst-audit2-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
+
+$(objpfx)tst-global1: $(libdl)
+$(objpfx)tst-global1.out: $(objpfx)testobj6.so $(objpfx)testobj2.so
+
+$(objpfx)order2: $(libdl)
+$(objpfx)order2.out: $(objpfx)order2 $(objpfx)order2mod1.so \
+ $(objpfx)order2mod2.so
+ $(elf-objpfx)$(rtld-installed-name) \
+ --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \
+ $(objpfx)order2 > $@
+ (echo "12345" | cmp $@ -) > /dev/null
+$(objpfx)order2mod1.so: $(objpfx)order2mod4.so
+$(objpfx)order2mod4.so: $(objpfx)order2mod3.so
+$(objpfx)order2mod2.so: $(objpfx)order2mod3.so
+order2mod2.so-no-z-defs = yes
+
+tst-stackguard1-ARGS = --command "$(built-program-cmd) --child"
+tst-stackguard1-static-ARGS = --command "$(objpfx)tst-stackguard1-static --child"
+
+$(objpfx)tst-leaks1: $(libdl)
+$(objpfx)tst-leaks1-mem: $(objpfx)tst-leaks1.out
+ $(common-objpfx)malloc/mtrace $(objpfx)tst-leaks1.mtrace > $@
+
+tst-leaks1-ENV = MALLOC_TRACE=$(objpfx)tst-leaks1.mtrace
diff --git a/libc/elf/Versions b/libc/elf/Versions
new file mode 100644
index 000000000..967ebdb3a
--- /dev/null
+++ b/libc/elf/Versions
@@ -0,0 +1,67 @@
+libc {
+ GLIBC_2.0 {
+%ifdef EXPORT_UNWIND_FIND_FDE
+ __register_frame_info; __deregister_frame_info;
+%endif
+ }
+ GLIBC_2.1 {
+ # functions used in other libraries
+ _dl_mcount_wrapper; _dl_mcount_wrapper_check;
+ }
+ GLIBC_2.2.4 {
+ dl_iterate_phdr;
+ }
+%ifdef EXPORT_UNWIND_FIND_FDE
+ GCC_3.0 {
+ __register_frame_info_bases; __deregister_frame_info_bases;
+ __register_frame_info_table_bases; _Unwind_Find_FDE;
+ }
+%endif
+ GLIBC_PRIVATE {
+ # functions used in other libraries
+ _dl_addr;
+ _dl_sym; _dl_vsym;
+ _dl_open_hook;
+ __libc_dlopen_mode; __libc_dlsym; __libc_dlclose;
+ }
+}
+
+ld {
+ GLIBC_2.0 {
+ # Function from libc.so which must be shared with libc.
+ calloc; free; malloc; realloc; __libc_memalign;
+
+ _r_debug;
+ }
+ GLIBC_2.1 {
+ # functions used in other libraries
+ _dl_mcount;
+ # historically used by Garbage Collectors
+ __libc_stack_end;
+ }
+ GLIBC_2.3 {
+ # runtime interface to TLS
+ __tls_get_addr;
+ }
+ GLIBC_2.4 {
+ # stack canary
+ __stack_chk_guard;
+ }
+ GLIBC_PRIVATE {
+ # Those are in the dynamic linker, but used by libc.so.
+ __libc_enable_secure;
+ _dl_argv;
+ _dl_out_of_memory;
+ _dl_starting_up;
+ _rtld_global; _rtld_global_ro;
+ _dl_allocate_tls; _dl_deallocate_tls;
+ _dl_get_tls_static_info; _dl_allocate_tls_init;
+ _dl_tls_setup; _dl_rtld_di_serinfo;
+ _dl_tls_get_addr_soft;
+ _dl_make_stack_executable;
+ # Only here for gdb while a better method is developed.
+ _dl_debug_state;
+ # Pointer protection.
+ __pointer_chk_guard;
+ }
+}
diff --git a/libc/elf/cache.c b/libc/elf/cache.c
new file mode 100644
index 000000000..6730fb36e
--- /dev/null
+++ b/libc/elf/cache.c
@@ -0,0 +1,523 @@
+/* Copyright (C) 1999-2003,2005,2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Andreas Jaeger <aj@suse.de>, 1999.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <errno.h>
+#include <error.h>
+#include <dirent.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <ldconfig.h>
+#include <dl-cache.h>
+
+struct cache_entry
+{
+ char *lib; /* Library name. */
+ char *path; /* Path to find library. */
+ int flags; /* Flags to indicate kind of library. */
+ unsigned int osversion; /* Required OS version. */
+ uint64_t hwcap; /* Important hardware capabilities. */
+ int bits_hwcap; /* Number of bits set in hwcap. */
+ struct cache_entry *next; /* Next entry in list. */
+};
+
+/* List of all cache entries. */
+static struct cache_entry *entries;
+
+static const char *flag_descr[] =
+{ "libc4", "ELF", "libc5", "libc6"};
+
+/* Print a single entry. */
+static void
+print_entry (const char *lib, int flag, unsigned int osversion,
+ uint64_t hwcap, const char *key)
+{
+ printf ("\t%s (", lib);
+ switch (flag & FLAG_TYPE_MASK)
+ {
+ case FLAG_LIBC4:
+ case FLAG_ELF:
+ case FLAG_ELF_LIBC5:
+ case FLAG_ELF_LIBC6:
+ fputs (flag_descr[flag & FLAG_TYPE_MASK], stdout);
+ break;
+ default:
+ fputs (_("unknown"), stdout);
+ break;
+ }
+ switch (flag & FLAG_REQUIRED_MASK)
+ {
+ case FLAG_SPARC_LIB64:
+ fputs (",64bit", stdout);
+ break;
+ case FLAG_IA64_LIB64:
+ fputs (",IA-64", stdout);
+ break;
+ case FLAG_X8664_LIB64:
+ fputs (",x86-64", stdout);
+ break;
+ case FLAG_S390_LIB64:
+ fputs(",64bit", stdout);
+ break;
+ case FLAG_POWERPC_LIB64:
+ fputs(",64bit", stdout);
+ break;
+ case FLAG_MIPS64_LIBN32:
+ fputs(",N32", stdout);
+ break;
+ case FLAG_MIPS64_LIBN64:
+ fputs(",64bit", stdout);
+ case 0:
+ break;
+ default:
+ printf (",%d", flag & FLAG_REQUIRED_MASK);
+ break;
+ }
+ if (hwcap != 0)
+ printf (", hwcap: %#.16" PRIx64, hwcap);
+ if (osversion != 0)
+ {
+ static const char *const abi_tag_os[] =
+ {
+ [0] = "Linux",
+ [1] = "Hurd",
+ [2] = "Solaris",
+ [3] = "FreeBSD",
+ [4] = "kNetBSD",
+ [5] = "Syllable",
+ [6] = N_("Unknown OS")
+ };
+#define MAXTAG (sizeof abi_tag_os / sizeof abi_tag_os[0] - 1)
+ unsigned int os = osversion >> 24;
+
+ printf (_(", OS ABI: %s %d.%d.%d"),
+ _(abi_tag_os[os > MAXTAG ? MAXTAG : os]),
+ (osversion >> 16) & 0xff,
+ (osversion >> 8) & 0xff,
+ osversion & 0xff);
+ }
+ printf (") => %s\n", key);
+}
+
+
+/* Print the whole cache file, if a file contains the new cache format
+ hidden in the old one, print the contents of the new format. */
+void
+print_cache (const char *cache_name)
+{
+ size_t cache_size;
+ struct stat64 st;
+ int fd;
+ unsigned int i;
+ struct cache_file *cache;
+ struct cache_file_new *cache_new = NULL;
+ const char *cache_data;
+ int format = 0;
+
+ fd = open (cache_name, O_RDONLY);
+ if (fd < 0)
+ error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), cache_name);
+
+ if (fstat64 (fd, &st) < 0
+ /* No need to map the file if it is empty. */
+ || st.st_size == 0)
+ {
+ close (fd);
+ return;
+ }
+
+ cache = mmap (0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (cache == MAP_FAILED)
+ error (EXIT_FAILURE, errno, _("mmap of cache file failed.\n"));
+ cache_size = st.st_size;
+
+ if (cache_size < sizeof (struct cache_file))
+ error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
+
+ if (memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
+ {
+ /* This can only be the new format without the old one. */
+ cache_new = (struct cache_file_new *) cache;
+
+ if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)
+ || memcmp (cache_new->version, CACHE_VERSION,
+ sizeof CACHE_VERSION - 1))
+ error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
+ format = 1;
+ /* This is where the strings start. */
+ cache_data = (const char *) cache_new;
+ }
+ else
+ {
+ size_t offset = ALIGN_CACHE (sizeof (struct cache_file)
+ + (cache->nlibs
+ * sizeof (struct file_entry)));
+ /* This is where the strings start. */
+ cache_data = (const char *) &cache->libs[cache->nlibs];
+
+ /* Check for a new cache embedded in the old format. */
+ if (cache_size >
+ (offset + sizeof (struct cache_file_new)))
+ {
+
+ cache_new = (struct cache_file_new *) ((void *)cache + offset);
+
+ if (memcmp (cache_new->magic, CACHEMAGIC_NEW,
+ sizeof CACHEMAGIC_NEW - 1) == 0
+ && memcmp (cache_new->version, CACHE_VERSION,
+ sizeof CACHE_VERSION - 1) == 0)
+ {
+ cache_data = (const char *) cache_new;
+ format = 1;
+ }
+ }
+ }
+
+ if (format == 0)
+ {
+ printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name);
+
+ /* Print everything. */
+ for (i = 0; i < cache->nlibs; i++)
+ print_entry (cache_data + cache->libs[i].key,
+ cache->libs[i].flags, 0, 0,
+ cache_data + cache->libs[i].value);
+ }
+ else if (format == 1)
+ {
+ printf (_("%d libs found in cache `%s'\n"),
+ cache_new->nlibs, cache_name);
+
+ /* Print everything. */
+ for (i = 0; i < cache_new->nlibs; i++)
+ print_entry (cache_data + cache_new->libs[i].key,
+ cache_new->libs[i].flags,
+ cache_new->libs[i].osversion,
+ cache_new->libs[i].hwcap,
+ cache_data + cache_new->libs[i].value);
+ }
+ /* Cleanup. */
+ munmap (cache, cache_size);
+ close (fd);
+}
+
+/* Initialize cache data structures. */
+void
+init_cache (void)
+{
+ entries = NULL;
+}
+
+
+
+static
+int compare (const struct cache_entry *e1, const struct cache_entry *e2)
+{
+ int res;
+
+ /* We need to swap entries here to get the correct sort order. */
+ res = _dl_cache_libcmp (e2->lib, e1->lib);
+ if (res == 0)
+ {
+ if (e1->flags < e2->flags)
+ return 1;
+ else if (e1->flags > e2->flags)
+ return -1;
+ /* Sort by most specific hwcap. */
+ else if (e2->bits_hwcap > e1->bits_hwcap)
+ return 1;
+ else if (e2->bits_hwcap < e1->bits_hwcap)
+ return -1;
+ else if (e2->hwcap > e1->hwcap)
+ return 1;
+ else if (e2->hwcap < e1->hwcap)
+ return -1;
+ if (e2->osversion > e1->osversion)
+ return 1;
+ if (e2->osversion < e1->osversion)
+ return -1;
+ }
+ return res;
+}
+
+/* Save the contents of the cache. */
+void
+save_cache (const char *cache_name)
+{
+ struct cache_entry *entry;
+ int fd, idx_old, idx_new;
+ size_t total_strlen, len;
+ char *strings, *str, *temp_name;
+ struct cache_file *file_entries = NULL;
+ struct cache_file_new *file_entries_new = NULL;
+ size_t file_entries_size = 0;
+ size_t file_entries_new_size = 0;
+ unsigned int str_offset;
+ /* Number of cache entries. */
+ int cache_entry_count = 0;
+ /* Number of normal cache entries. */
+ int cache_entry_old_count = 0;
+ /* Pad for alignment of cache_file_new. */
+ size_t pad;
+
+ /* The cache entries are sorted already, save them in this order. */
+
+ /* Count the length of all strings. */
+ /* The old format doesn't contain hwcap entries and doesn't contain
+ libraries in subdirectories with hwcaps entries. Count therefore
+ also all entries with hwcap == 0. */
+ total_strlen = 0;
+ for (entry = entries; entry != NULL; entry = entry->next)
+ {
+ /* Account the final NULs. */
+ total_strlen += strlen (entry->lib) + strlen (entry->path) + 2;
+ ++cache_entry_count;
+ if (entry->hwcap == 0)
+ ++cache_entry_old_count;
+ }
+
+ /* Create the on disk cache structure. */
+ /* First an array for all strings. */
+ strings = (char *)xmalloc (total_strlen);
+
+ if (opt_format != 2)
+ {
+ /* struct cache_file_new is 64-bit aligned on some arches while
+ only 32-bit aligned on other arches. Duplicate last old
+ cache entry so that new cache in ld.so.cache can be used by
+ both. */
+ if (opt_format != 0)
+ cache_entry_old_count = (cache_entry_old_count + 1) & ~1;
+
+ /* And the list of all entries in the old format. */
+ file_entries_size = sizeof (struct cache_file)
+ + cache_entry_old_count * sizeof (struct file_entry);
+ file_entries = (struct cache_file *) xmalloc (file_entries_size);
+
+ /* Fill in the header. */
+ memset (file_entries, 0, sizeof (struct cache_file));
+ memcpy (file_entries->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1);
+
+ file_entries->nlibs = cache_entry_old_count;
+ }
+
+ if (opt_format != 0)
+ {
+ /* And the list of all entries in the new format. */
+ file_entries_new_size = sizeof (struct cache_file_new)
+ + cache_entry_count * sizeof (struct file_entry_new);
+ file_entries_new =
+ (struct cache_file_new *) xmalloc (file_entries_new_size);
+
+ /* Fill in the header. */
+ memset (file_entries_new, 0, sizeof (struct cache_file_new));
+ memcpy (file_entries_new->magic, CACHEMAGIC_NEW,
+ sizeof CACHEMAGIC_NEW - 1);
+ memcpy (file_entries_new->version, CACHE_VERSION,
+ sizeof CACHE_VERSION - 1);
+
+ file_entries_new->nlibs = cache_entry_count;
+ file_entries_new->len_strings = total_strlen;
+ }
+
+ pad = ALIGN_CACHE (file_entries_size) - file_entries_size;
+
+ /* If we have both formats, we hide the new format in the strings
+ table, we have to adjust all string indices for this so that
+ old libc5/glibc 2 dynamic linkers just ignore them. */
+ if (opt_format != 0)
+ str_offset = file_entries_new_size;
+ else
+ str_offset = 0;
+
+ str = strings;
+ for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL;
+ entry = entry->next, ++idx_new)
+ {
+ /* First the library. */
+ if (opt_format != 2 && entry->hwcap == 0)
+ {
+ file_entries->libs[idx_old].flags = entry->flags;
+ /* XXX: Actually we can optimize here and remove duplicates. */
+ file_entries->libs[idx_old].key = str_offset + pad;
+ }
+ if (opt_format != 0)
+ {
+ /* We could subtract file_entries_new_size from str_offset -
+ not doing so makes the code easier, the string table
+ always begins at the beginning of the the new cache
+ struct. */
+ file_entries_new->libs[idx_new].flags = entry->flags;
+ file_entries_new->libs[idx_new].osversion = entry->osversion;
+ file_entries_new->libs[idx_new].hwcap = entry->hwcap;
+ file_entries_new->libs[idx_new].key = str_offset;
+ }
+ len = strlen (entry->lib);
+ str = stpcpy (str, entry->lib);
+ /* Account the final NUL. */
+ ++str;
+ str_offset += len + 1;
+ /* Then the path. */
+ if (opt_format != 2 && entry->hwcap == 0)
+ file_entries->libs[idx_old].value = str_offset + pad;
+ if (opt_format != 0)
+ file_entries_new->libs[idx_new].value = str_offset;
+ len = strlen (entry->path);
+ str = stpcpy (str, entry->path);
+ /* Account the final NUL. */
+ ++str;
+ str_offset += len + 1;
+ /* Ignore entries with hwcap for old format. */
+ if (entry->hwcap == 0)
+ ++idx_old;
+ }
+
+ /* Duplicate last old cache entry if needed. */
+ if (opt_format != 2
+ && idx_old < cache_entry_old_count)
+ file_entries->libs[idx_old] = file_entries->libs[idx_old - 1];
+
+ /* Write out the cache. */
+
+ /* Write cache first to a temporary file and rename it later. */
+ temp_name = xmalloc (strlen (cache_name) + 2);
+ sprintf (temp_name, "%s~", cache_name);
+ /* First remove an old copy if it exists. */
+ if (unlink (temp_name) && errno != ENOENT)
+ error (EXIT_FAILURE, errno, _("Can't remove old temporary cache file %s"),
+ temp_name);
+
+ /* Create file. */
+ fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW,
+ S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR);
+ if (fd < 0)
+ error (EXIT_FAILURE, errno, _("Can't create temporary cache file %s"),
+ temp_name);
+
+ /* Write contents. */
+ if (opt_format != 2)
+ {
+ if (write (fd, file_entries, file_entries_size)
+ != (ssize_t) file_entries_size)
+ error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
+ }
+ if (opt_format != 0)
+ {
+ /* Align cache. */
+ if (opt_format != 2)
+ {
+ char zero[pad];
+ memset (zero, '\0', pad);
+ if (write (fd, zero, pad) != (ssize_t) pad)
+ error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
+ }
+ if (write (fd, file_entries_new, file_entries_new_size)
+ != (ssize_t) file_entries_new_size)
+ error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
+ }
+
+ if (write (fd, strings, total_strlen) != (ssize_t) total_strlen)
+ error (EXIT_FAILURE, errno, _("Writing of cache data failed."));
+
+ close (fd);
+
+ /* Make sure user can always read cache file */
+ if (chmod (temp_name, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR))
+ error (EXIT_FAILURE, errno,
+ _("Changing access rights of %s to %#o failed"), temp_name,
+ S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR);
+
+ /* Move temporary to its final location. */
+ if (rename (temp_name, cache_name))
+ error (EXIT_FAILURE, errno, _("Renaming of %s to %s failed"), temp_name,
+ cache_name);
+
+ /* Free all allocated memory. */
+ free (file_entries_new);
+ free (file_entries);
+ free (strings);
+
+ while (entries)
+ {
+ entry = entries;
+ free (entry->path);
+ free (entry->lib);
+ entries = entries->next;
+ free (entry);
+ }
+}
+
+
+/* Add one library to the cache. */
+void
+add_to_cache (const char *path, const char *lib, int flags,
+ unsigned int osversion, uint64_t hwcap)
+{
+ struct cache_entry *new_entry, *ptr, *prev;
+ char *full_path;
+ size_t len, i;
+
+ new_entry = (struct cache_entry *) xmalloc (sizeof (struct cache_entry));
+
+ len = strlen (lib) + strlen (path) + 2;
+
+ full_path = (char *) xmalloc (len);
+ snprintf (full_path, len, "%s/%s", path, lib);
+
+ new_entry->lib = xstrdup (lib);
+ new_entry->path = full_path;
+ new_entry->flags = flags;
+ new_entry->osversion = osversion;
+ new_entry->hwcap = hwcap;
+ new_entry->bits_hwcap = 0;
+
+ /* Count the number of bits set in the masked value. */
+ for (i = 0; (~((1ULL << i) - 1) & hwcap) != 0 && i < 8 * sizeof (hwcap); ++i)
+ if ((hwcap & (1ULL << i)) != 0)
+ ++new_entry->bits_hwcap;
+
+
+ /* Keep the list sorted - search for right place to insert. */
+ ptr = entries;
+ prev = entries;
+ while (ptr != NULL)
+ {
+ if (compare (ptr, new_entry) > 0)
+ break;
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ /* Is this the first entry? */
+ if (ptr == entries)
+ {
+ new_entry->next = entries;
+ entries = new_entry;
+ }
+ else
+ {
+ new_entry->next = prev->next;
+ prev->next = new_entry;
+ }
+}
diff --git a/libc/elf/check-localplt.c b/libc/elf/check-localplt.c
new file mode 100644
index 000000000..b4358a8a3
--- /dev/null
+++ b/libc/elf/check-localplt.c
@@ -0,0 +1,299 @@
+/* Show local PLT use in DSOs.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contribute by Ulrich Drepper <drepper@redhat.com>. 2006.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <byteswap.h>
+#include <elf.h>
+#include <endian.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+#ifdef BITS
+
+# define AB(name) _AB (name, BITS)
+# define _AB(name, bits) __AB (name, bits)
+# define __AB(name, bits) name##bits
+# define E(name) _E (name, BITS)
+# define _E(name, bits) __E (name, bits)
+# define __E(name, bits) Elf##bits##_##name
+# define EE(name) _EE (name, BITS)
+# define _EE(name, bits) __EE (name, bits)
+# define __EE(name, bits) ELF##bits##_##name
+# define SWAP(val) \
+ ({ __typeof (val) __res; \
+ if (((ehdr.e_ident[EI_DATA] == ELFDATA2MSB \
+ && BYTE_ORDER == LITTLE_ENDIAN) \
+ || (ehdr.e_ident[EI_DATA] == ELFDATA2LSB \
+ && BYTE_ORDER == BIG_ENDIAN)) \
+ && sizeof (val) != 1) \
+ { \
+ if (sizeof (val) == 2) \
+ __res = bswap_16 (val); \
+ else if (sizeof (val) == 4) \
+ __res = bswap_32 (val); \
+ else \
+ __res = bswap_64 (val); \
+ } \
+ else \
+ __res = (val); \
+ __res; })
+
+
+static int
+AB(handle_file) (const char *fname, int fd)
+{
+ E(Ehdr) ehdr;
+
+ if (pread (fd, &ehdr, sizeof (ehdr), 0) != sizeof (ehdr))
+ {
+ read_error:
+ printf ("%s: read error: %m\n", fname);
+ return 1;
+ }
+
+ const size_t phnum = SWAP (ehdr.e_phnum);
+ const size_t phentsize = SWAP (ehdr.e_phentsize);
+
+ /* Read the program header. */
+ E(Phdr) *phdr = alloca (phentsize * phnum);
+ if (pread (fd, phdr, phentsize * phnum, SWAP (ehdr.e_phoff))
+ != phentsize * phnum)
+ goto read_error;
+
+ /* Search for the PT_DYNAMIC entry. */
+ size_t cnt;
+ E(Phdr) *dynphdr = NULL;
+ for (cnt = 0; cnt < phnum; ++cnt)
+ if (SWAP (phdr[cnt].p_type) == PT_DYNAMIC)
+ {
+ dynphdr = &phdr[cnt];
+ break;
+ }
+
+ if (dynphdr == NULL)
+ {
+ printf ("%s: no DYNAMIC segment found\n", fname);
+ return 1;
+ }
+
+ /* Read the dynamic segment. */
+ size_t pmemsz = SWAP(dynphdr->p_memsz);
+ E(Dyn) *dyn = alloca (pmemsz);
+ if (pread64 (fd, dyn, pmemsz, SWAP(dynphdr->p_offset)) != pmemsz)
+ goto read_error;
+
+ /* Search for an DT_PLTREL, DT_JMPREL, DT_PLTRELSZ, DT_STRTAB,
+ DT_STRSZ, and DT_SYMTAB entries. */
+ size_t pltrel_idx = SIZE_MAX;
+ size_t jmprel_idx = SIZE_MAX;
+ size_t pltrelsz_idx = SIZE_MAX;
+ size_t strtab_idx = SIZE_MAX;
+ size_t strsz_idx = SIZE_MAX;
+ size_t symtab_idx = SIZE_MAX;
+ for (cnt = 0; (cnt + 1) * sizeof (E(Dyn)) - 1 < pmemsz; ++cnt)
+ {
+ unsigned int tag = SWAP (dyn[cnt].d_tag);
+
+ if (tag == DT_NULL)
+ /* We reached the end. */
+ break;
+
+ if (tag == DT_PLTREL)
+ pltrel_idx = cnt;
+ else if (tag == DT_JMPREL)
+ jmprel_idx = cnt;
+ else if (tag == DT_PLTRELSZ)
+ pltrelsz_idx = cnt;
+ else if (tag == DT_STRTAB)
+ strtab_idx = cnt;
+ else if (tag == DT_STRSZ)
+ strsz_idx = cnt;
+ else if (tag == DT_SYMTAB)
+ symtab_idx = cnt;
+ }
+
+ if (pltrel_idx == SIZE_MAX || jmprel_idx == SIZE_MAX
+ || pltrelsz_idx == SIZE_MAX || strtab_idx == SIZE_MAX
+ || strsz_idx == SIZE_MAX || symtab_idx == SIZE_MAX)
+ {
+ puts ("not all PLT information found");
+ return 1;
+ }
+
+ E(Xword) relsz = SWAP (dyn[pltrelsz_idx].d_un.d_val);
+
+ void *relmem = NULL;
+ char *strtab = NULL;
+ E(Xword) symtab_offset = 0;
+
+ /* Find the offset of DT_JMPREL and load the data. */
+ for (cnt = 0; cnt < phnum; ++cnt)
+ if (SWAP (phdr[cnt].p_type) == PT_LOAD)
+ {
+ E(Addr) vaddr = SWAP (phdr[cnt].p_vaddr);
+ E(Xword) memsz = SWAP (phdr[cnt].p_memsz);
+
+ if (vaddr <= SWAP (dyn[jmprel_idx].d_un.d_val)
+ && vaddr + memsz >= SWAP (dyn[jmprel_idx].d_un.d_val) + relsz)
+ {
+ relmem = alloca (SWAP (dyn[pltrelsz_idx].d_un.d_val));
+ if (pread64 (fd, relmem, relsz,
+ SWAP (phdr[cnt].p_offset)
+ + SWAP (dyn[jmprel_idx].d_un.d_val) - vaddr)
+ != relsz)
+ {
+ puts ("cannot read JMPREL");
+ return 1;
+ }
+ }
+
+ if (vaddr <= SWAP (dyn[symtab_idx].d_un.d_val)
+ && vaddr + memsz > SWAP (dyn[symtab_idx].d_un.d_val))
+ symtab_offset = (SWAP (phdr[cnt].p_offset)
+ + SWAP (dyn[symtab_idx].d_un.d_val) - vaddr);
+
+ if (vaddr <= SWAP (dyn[strtab_idx].d_un.d_val)
+ && vaddr + memsz >= (SWAP (dyn[strtab_idx].d_un.d_val)
+ + SWAP(dyn[strsz_idx].d_un.d_val)))
+ {
+ strtab = alloca (SWAP(dyn[strsz_idx].d_un.d_val));
+ if (pread64 (fd, strtab, SWAP(dyn[strsz_idx].d_un.d_val),
+ SWAP (phdr[cnt].p_offset)
+ + SWAP (dyn[strtab_idx].d_un.d_val) - vaddr)
+ != SWAP(dyn[strsz_idx].d_un.d_val))
+ {
+ puts ("cannot read STRTAB");
+ return 1;
+ }
+ }
+ }
+
+ if (relmem == NULL || strtab == NULL || symtab_offset == 0)
+ {
+ puts ("couldn't load PLT data");
+ return 1;
+ }
+
+ if (SWAP (dyn[pltrel_idx].d_un.d_val) == DT_RELA)
+ for (E(Rela) *rela = relmem; (char *) rela - (char *) relmem < relsz;
+ ++rela)
+ {
+ E(Sym) sym;
+
+ if (pread64 (fd, &sym, sizeof (sym),
+ symtab_offset
+ + EE(R_SYM) (SWAP (rela->r_info)) * sizeof (sym))
+ != sizeof (sym))
+ {
+ puts ("cannot read symbol");
+ return 1;
+ }
+
+ if (sym.st_value != 0)
+ /* This symbol is locally defined. */
+ printf ("%s: %s\n", basename (fname), strtab + SWAP (sym.st_name));
+ }
+ else
+ for (E(Rel) *rel = relmem; (char *) rel - (char *) relmem < relsz; ++rel)
+ {
+ E(Sym) sym;
+
+ if (pread64 (fd, &sym, sizeof (sym),
+ symtab_offset
+ + EE(R_SYM) (SWAP (rel->r_info)) * sizeof (sym))
+ != sizeof (sym))
+ {
+ puts ("cannot read symbol");
+ return 1;
+ }
+
+ if (sym.st_value != 0)
+ /* This symbol is locally defined. */
+ printf ("%s: %s\n", basename (fname), strtab + SWAP (sym.st_name));
+ }
+
+ return 0;
+}
+
+# undef BITS
+#else
+
+# define BITS 32
+# include "check-localplt.c"
+
+# define BITS 64
+# include "check-localplt.c"
+
+
+static int
+handle_file (const char *fname)
+{
+ int fd = open (fname, O_RDONLY);
+ if (fd == -1)
+ {
+ printf ("cannot open %s: %m\n", fname);
+ return 1;
+ }
+
+ /* Read was is supposed to be the ELF header. Read the initial
+ bytes to determine whether this is a 32 or 64 bit file. */
+ char ident[EI_NIDENT];
+ if (read (fd, ident, EI_NIDENT) != EI_NIDENT)
+ {
+ printf ("%s: read error: %m\n", fname);
+ close (fd);
+ return 1;
+ }
+
+ if (memcmp (&ident[EI_MAG0], ELFMAG, SELFMAG) != 0)
+ {
+ printf ("%s: not an ELF file\n", fname);
+ close (fd);
+ return 1;
+ }
+
+ int result;
+ if (ident[EI_CLASS] == ELFCLASS64)
+ result = handle_file64 (fname, fd);
+ else
+ result = handle_file32 (fname, fd);
+
+ close (fd);
+
+ return result;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int cnt;
+ int result = 0;
+
+ for (cnt = 1; cnt < argc; ++cnt)
+ result |= handle_file (argv[cnt]);
+
+ return result;
+}
+#endif
diff --git a/libc/elf/check-textrel.c b/libc/elf/check-textrel.c
new file mode 100644
index 000000000..1a9a5ecdf
--- /dev/null
+++ b/libc/elf/check-textrel.c
@@ -0,0 +1,199 @@
+/* Check for text relocations in DSOs.
+ Copyright (C) 2002, 2003, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contribute by Ulrich Drepper <drepper@redhat.com>. 2002.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <byteswap.h>
+#include <elf.h>
+#include <endian.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+#ifdef BITS
+
+# define AB(name) _AB (name, BITS)
+# define _AB(name, bits) __AB (name, bits)
+# define __AB(name, bits) name##bits
+# define E(name) _E (name, BITS)
+# define _E(name, bits) __E (name, bits)
+# define __E(name, bits) Elf##bits##_##name
+# define SWAP(val) \
+ ({ __typeof (val) __res; \
+ if (((ehdr.e_ident[EI_DATA] == ELFDATA2MSB \
+ && BYTE_ORDER == LITTLE_ENDIAN) \
+ || (ehdr.e_ident[EI_DATA] == ELFDATA2LSB \
+ && BYTE_ORDER == BIG_ENDIAN)) \
+ && sizeof (val) != 1) \
+ { \
+ if (sizeof (val) == 2) \
+ __res = bswap_16 (val); \
+ else if (sizeof (val) == 4) \
+ __res = bswap_32 (val); \
+ else \
+ __res = bswap_64 (val); \
+ } \
+ else \
+ __res = (val); \
+ __res; })
+
+
+static int
+AB(handle_file) (const char *fname, int fd)
+{
+ E(Ehdr) ehdr;
+
+ if (pread (fd, &ehdr, sizeof (ehdr), 0) != sizeof (ehdr))
+ {
+ read_error:
+ printf ("%s: read error: %m\n", fname);
+ return 1;
+ }
+
+ const size_t phnum = SWAP (ehdr.e_phnum);
+ const size_t phentsize = SWAP (ehdr.e_phentsize);
+
+ /* Read the program header. */
+ E(Phdr) *phdr = alloca (phentsize * phnum);
+ if (pread (fd, phdr, phentsize * phnum, SWAP (ehdr.e_phoff))
+ != phentsize * phnum)
+ goto read_error;
+
+ /* Search for the PT_DYNAMIC entry. */
+ size_t cnt;
+ E(Phdr) *dynphdr = NULL;
+ for (cnt = 0; cnt < phnum; ++cnt)
+ if (SWAP (phdr[cnt].p_type) == PT_DYNAMIC)
+ dynphdr = &phdr[cnt];
+ else if (SWAP (phdr[cnt].p_type) == PT_LOAD
+ && (SWAP (phdr[cnt].p_flags) & (PF_X | PF_W)) == (PF_X | PF_W))
+ {
+ printf ("%s: segment %zu is executable and writable\n",
+ fname, cnt);
+#if !defined __sparc__ \
+ && !defined __alpha__ \
+ && (!defined __powerpc__ || defined __powerpc64__ || defined HAVE_PPC_SECURE_PLT)
+ /* sparc, sparc64, alpha and powerpc32 (the last one only when using
+ -mbss-plt) are expected to have PF_X | PF_W segment containing .plt
+ section, it is part of their ABI. It is bad security wise, nevertheless
+ this test shouldn't fail because of this. */
+ return 1;
+#endif
+ }
+
+ if (dynphdr == NULL)
+ {
+ printf ("%s: no DYNAMIC segment found\n", fname);
+ return 1;
+ }
+
+ /* Read the dynamic segment. */
+ size_t pmemsz = SWAP(dynphdr->p_memsz);
+ E(Dyn) *dyn = alloca (pmemsz);
+ if (pread (fd, dyn, pmemsz, SWAP(dynphdr->p_offset)) != pmemsz)
+ goto read_error;
+
+ /* Search for an DT_TEXTREL entry of DT_FLAGS with the DF_TEXTREL
+ bit set. */
+ for (cnt = 0; (cnt + 1) * sizeof (E(Dyn)) - 1 < pmemsz; ++cnt)
+ {
+ unsigned int tag = SWAP (dyn[cnt].d_tag);
+
+ if (tag == DT_NULL)
+ /* We reached the end. */
+ break;
+
+ if (tag == DT_TEXTREL
+ || (tag == DT_FLAGS
+ && (SWAP (dyn[cnt].d_un.d_val) & DF_TEXTREL) != 0))
+ {
+ /* Urgh! The DSO has text relocations. */
+ printf ("%s: text relocations used\n", fname);
+ return 1;
+ }
+ }
+
+ printf ("%s: OK\n", fname);
+
+ return 0;
+}
+
+# undef BITS
+#else
+
+# define BITS 32
+# include "check-textrel.c"
+
+# define BITS 64
+# include "check-textrel.c"
+
+
+static int
+handle_file (const char *fname)
+{
+ int fd = open (fname, O_RDONLY);
+ if (fd == -1)
+ {
+ printf ("cannot open %s: %m\n", fname);
+ return 1;
+ }
+
+ /* Read was is supposed to be the ELF header. Read the initial
+ bytes to determine whether this is a 32 or 64 bit file. */
+ char ident[EI_NIDENT];
+ if (read (fd, ident, EI_NIDENT) != EI_NIDENT)
+ {
+ printf ("%s: read error: %m\n", fname);
+ close (fd);
+ return 1;
+ }
+
+ if (memcmp (&ident[EI_MAG0], ELFMAG, SELFMAG) != 0)
+ {
+ printf ("%s: not an ELF file\n", fname);
+ close (fd);
+ return 1;
+ }
+
+ int result;
+ if (ident[EI_CLASS] == ELFCLASS64)
+ result = handle_file64 (fname, fd);
+ else
+ result = handle_file32 (fname, fd);
+
+ close (fd);
+
+ return result;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int cnt;
+ int result = 0;
+
+ for (cnt = 1; cnt < argc; ++cnt)
+ result |= handle_file (argv[cnt]);
+
+ return result;
+}
+#endif
diff --git a/libc/elf/chroot_canon.c b/libc/elf/chroot_canon.c
new file mode 100644
index 000000000..3ef2fdf08
--- /dev/null
+++ b/libc/elf/chroot_canon.c
@@ -0,0 +1,183 @@
+/* Return the canonical absolute name of a given file inside chroot.
+ Copyright (C) 1996,1997,1998,1999,2000,2001,2004,2005
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <ldconfig.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+/* Return the canonical absolute name of file NAME as if chroot(CHROOT) was
+ done first. A canonical name does not contain any `.', `..' components
+ nor any repeated path separators ('/') or symlinks. All path components
+ must exist and NAME must be absolute filename. The result is malloc'd.
+ The returned name includes the CHROOT prefix. */
+
+char *
+chroot_canon (const char *chroot, const char *name)
+{
+ char *rpath;
+ char *dest;
+ char *extra_buf = NULL;
+ char *rpath_root;
+ const char *start;
+ const char *end;
+ const char *rpath_limit;
+ int num_links = 0;
+ size_t chroot_len = strlen (chroot);
+
+ if (chroot_len < 1)
+ {
+ __set_errno (EINVAL);
+ return NULL;
+ }
+
+ rpath = malloc (chroot_len + PATH_MAX);
+ if (rpath == NULL)
+ return NULL;
+
+ rpath_limit = rpath + chroot_len + PATH_MAX;
+
+ rpath_root = (char *) mempcpy (rpath, chroot, chroot_len) - 1;
+ if (*rpath_root != '/')
+ *++rpath_root = '/';
+ dest = rpath_root + 1;
+
+ for (start = end = name; *start; start = end)
+ {
+ struct stat64 st;
+ int n;
+
+ /* Skip sequence of multiple path-separators. */
+ while (*start == '/')
+ ++start;
+
+ /* Find end of path component. */
+ for (end = start; *end && *end != '/'; ++end)
+ /* Nothing. */;
+
+ if (end - start == 0)
+ break;
+ else if (end - start == 1 && start[0] == '.')
+ /* nothing */;
+ else if (end - start == 2 && start[0] == '.' && start[1] == '.')
+ {
+ /* Back up to previous component, ignore if at root already. */
+ if (dest > rpath_root + 1)
+ while ((--dest)[-1] != '/');
+ }
+ else
+ {
+ size_t new_size;
+
+ if (dest[-1] != '/')
+ *dest++ = '/';
+
+ if (dest + (end - start) >= rpath_limit)
+ {
+ ptrdiff_t dest_offset = dest - rpath;
+ char *new_rpath;
+
+ new_size = rpath_limit - rpath;
+ if (end - start + 1 > PATH_MAX)
+ new_size += end - start + 1;
+ else
+ new_size += PATH_MAX;
+ new_rpath = (char *) realloc (rpath, new_size);
+ if (new_rpath == NULL)
+ goto error;
+ rpath = new_rpath;
+ rpath_limit = rpath + new_size;
+
+ dest = rpath + dest_offset;
+ }
+
+ dest = mempcpy (dest, start, end - start);
+ *dest = '\0';
+
+ if (lstat64 (rpath, &st) < 0)
+ {
+ if (*end == '\0')
+ goto done;
+ goto error;
+ }
+
+ if (S_ISLNK (st.st_mode))
+ {
+ char *buf = alloca (PATH_MAX);
+ size_t len;
+
+ if (++num_links > MAXSYMLINKS)
+ {
+ __set_errno (ELOOP);
+ goto error;
+ }
+
+ n = readlink (rpath, buf, PATH_MAX);
+ if (n < 0)
+ {
+ if (*end == '\0')
+ goto done;
+ goto error;
+ }
+ buf[n] = '\0';
+
+ if (!extra_buf)
+ extra_buf = alloca (PATH_MAX);
+
+ len = strlen (end);
+ if ((long int) (n + len) >= PATH_MAX)
+ {
+ __set_errno (ENAMETOOLONG);
+ goto error;
+ }
+
+ /* Careful here, end may be a pointer into extra_buf... */
+ memmove (&extra_buf[n], end, len + 1);
+ name = end = memcpy (extra_buf, buf, n);
+
+ if (buf[0] == '/')
+ dest = rpath_root + 1; /* It's an absolute symlink */
+ else
+ /* Back up to previous component, ignore if at root already: */
+ if (dest > rpath_root + 1)
+ while ((--dest)[-1] != '/');
+ }
+ }
+ }
+ done:
+ if (dest > rpath_root + 1 && dest[-1] == '/')
+ --dest;
+ *dest = '\0';
+
+ return rpath;
+
+ error:
+ free (rpath);
+ return NULL;
+}
diff --git a/libc/elf/circleload1.c b/libc/elf/circleload1.c
new file mode 100644
index 000000000..990ff84a8
--- /dev/null
+++ b/libc/elf/circleload1.c
@@ -0,0 +1,166 @@
+#include <dlfcn.h>
+#include <libintl.h>
+#include <link.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAPS ((struct link_map *) _r_debug.r_map)
+
+static int
+check_loaded_objects (const char **loaded)
+{
+ struct link_map *lm;
+ int n;
+ int *found = NULL;
+ int errors = 0;
+
+ for (n = 0; loaded[n]; n++)
+ /* NOTHING */;
+
+ if (n)
+ {
+ found = (int *) alloca (sizeof (int) * n);
+ memset (found, 0, sizeof (int) * n);
+ }
+
+ printf(" Name\n");
+ printf(" --------------------------------------------------------\n");
+ for (lm = MAPS; lm; lm = lm->l_next)
+ {
+ if (lm->l_name && lm->l_name[0])
+ printf(" %s, count = %d\n", lm->l_name, (int) lm->l_direct_opencount);
+ if (lm->l_type == lt_loaded && lm->l_name)
+ {
+ int match = 0;
+ for (n = 0; loaded[n] != NULL; n++)
+ {
+ if (strcmp (basename (loaded[n]), basename (lm->l_name)) == 0)
+ {
+ found[n] = 1;
+ match = 1;
+ break;
+ }
+ }
+
+ if (match == 0)
+ {
+ ++errors;
+ printf ("ERRORS: %s is not unloaded\n", lm->l_name);
+ }
+ }
+ }
+
+ for (n = 0; loaded[n] != NULL; n++)
+ {
+ if (found[n] == 0)
+ {
+ ++errors;
+ printf ("ERRORS: %s is not loaded\n", loaded[n]);
+ }
+ }
+
+ return errors;
+}
+
+static int
+load_dso (const char **loading, int undef, int flag)
+{
+ void *obj;
+ const char *loaded[] = { NULL, NULL, NULL, NULL };
+ int errors = 0;
+ const char *errstring;
+
+ printf ("\nThis is what is in memory now:\n");
+ errors += check_loaded_objects (loaded);
+
+ printf ("Loading shared object %s: %s\n", loading[0],
+ flag == RTLD_LAZY ? "RTLD_LAZY" : "RTLD_NOW");
+ obj = dlopen (loading[0], flag);
+ if (obj == NULL)
+ {
+ if (flag == RTLD_LAZY)
+ {
+ ++errors;
+ printf ("ERRORS: dlopen shouldn't fail for RTLD_LAZY\n");
+ }
+
+ errstring = dlerror ();
+ if (strstr (errstring, "undefined symbol") == 0
+ || strstr (errstring, "circlemod2_undefined") == 0)
+ {
+ ++errors;
+ printf ("ERRORS: dlopen: `%s': Invalid error string\n",
+ errstring);
+ }
+ else
+ printf ("dlopen: %s\n", errstring);
+ }
+ else
+ {
+ if (undef && flag == RTLD_NOW)
+ {
+ ++errors;
+ printf ("ERRORS: dlopen shouldn't work for RTLD_NOW\n");
+ }
+
+ if (!undef)
+ {
+ int (*func) (void);
+
+ func = dlsym (obj, "circlemod1");
+ if (func == NULL)
+ {
+ ++errors;
+ printf ("ERRORS: cannot get address of \"circlemod1\": %s\n",
+ dlerror ());
+ }
+ else if (func () != 3)
+ {
+ ++errors;
+ printf ("ERRORS: function \"circlemod1\" returned wrong result\n");
+ }
+ }
+
+ loaded[0] = loading[0];
+ loaded[1] = loading[1];
+ loaded[2] = loading[2];
+ }
+ errors += check_loaded_objects (loaded);
+
+ if (obj)
+ {
+ printf ("UnLoading shared object %s\n", loading[0]);
+ dlclose (obj);
+ loaded[0] = NULL;
+ loaded[1] = NULL;
+ loaded[2] = NULL;
+ errors += check_loaded_objects (loaded);
+ }
+
+ return errors;
+}
+
+int
+main (void)
+{
+ int errors = 0;
+ const char *loading[3];
+
+ loading[0] = "circlemod1a.so";
+ loading[1] = "circlemod2a.so";
+ loading[2] = "circlemod3a.so";
+ errors += load_dso (loading, 0, RTLD_LAZY);
+ errors += load_dso (loading, 0, RTLD_NOW);
+
+ loading[0] = "circlemod1.so";
+ loading[1] = "circlemod2.so";
+ loading[2] = "circlemod3.so";
+ errors += load_dso (loading, 1, RTLD_LAZY);
+ errors += load_dso (loading, 1, RTLD_NOW);
+
+ if (errors != 0)
+ printf ("%d errors found\n", errors);
+
+ return errors;
+}
diff --git a/libc/elf/circlemod1.c b/libc/elf/circlemod1.c
new file mode 100644
index 000000000..933ccd3c0
--- /dev/null
+++ b/libc/elf/circlemod1.c
@@ -0,0 +1,7 @@
+extern int circlemod2 (void);
+
+int
+circlemod1 (void)
+{
+ return circlemod2 ();
+}
diff --git a/libc/elf/circlemod1a.c b/libc/elf/circlemod1a.c
new file mode 100644
index 000000000..45f229136
--- /dev/null
+++ b/libc/elf/circlemod1a.c
@@ -0,0 +1 @@
+#include "circlemod1.c"
diff --git a/libc/elf/circlemod2.c b/libc/elf/circlemod2.c
new file mode 100644
index 000000000..ed8c1175f
--- /dev/null
+++ b/libc/elf/circlemod2.c
@@ -0,0 +1,9 @@
+extern void circlemod2_undefined (void);
+extern int circlemod3 (void);
+
+int
+circlemod2 (void)
+{
+ circlemod2_undefined ();
+ return circlemod3 ();
+}
diff --git a/libc/elf/circlemod2a.c b/libc/elf/circlemod2a.c
new file mode 100644
index 000000000..dc6410b28
--- /dev/null
+++ b/libc/elf/circlemod2a.c
@@ -0,0 +1,7 @@
+extern int circlemod3 (void);
+
+int
+circlemod2 (void)
+{
+ return circlemod3 ();
+}
diff --git a/libc/elf/circlemod3.c b/libc/elf/circlemod3.c
new file mode 100644
index 000000000..8d16fe682
--- /dev/null
+++ b/libc/elf/circlemod3.c
@@ -0,0 +1,14 @@
+extern int circlemod1 (void);
+extern int circlemod2 (void);
+
+int
+circlemod3 (void)
+{
+ return 3;
+}
+
+int
+circlemod3a (void)
+{
+ return circlemod1 () + circlemod2 ();
+}
diff --git a/libc/elf/circlemod3a.c b/libc/elf/circlemod3a.c
new file mode 100644
index 000000000..f1b166ef8
--- /dev/null
+++ b/libc/elf/circlemod3a.c
@@ -0,0 +1 @@
+#include "circlemod3.c"
diff --git a/libc/elf/constload1.c b/libc/elf/constload1.c
new file mode 100644
index 000000000..7381beea8
--- /dev/null
+++ b/libc/elf/constload1.c
@@ -0,0 +1,32 @@
+#include <dlfcn.h>
+#include <errno.h>
+#include <error.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ int (*foo) (void);
+ void *h;
+ int ret;
+
+ mtrace ();
+
+ h = dlopen ("constload2.so", RTLD_LAZY | RTLD_GLOBAL);
+ if (h == NULL)
+ error (EXIT_FAILURE, errno, "cannot load module \"constload2.so\"");
+ foo = dlsym (h, "foo");
+ ret = foo ();
+ /* Note that the following dlclose() call cannot unload the objects.
+ Due to the introduced relocation dependency constload2.so depends
+ on constload3.so and the dependencies of constload2.so on constload3.so
+ is not visible to ld.so since it's done using dlopen(). */
+ if (dlclose (h) != 0)
+ {
+ puts ("failed to close");
+ exit (EXIT_FAILURE);
+ }
+ return ret;
+}
diff --git a/libc/elf/constload2.c b/libc/elf/constload2.c
new file mode 100644
index 000000000..bf1bf182f
--- /dev/null
+++ b/libc/elf/constload2.c
@@ -0,0 +1,50 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int bar (void);
+extern int baz (void);
+extern int foo (void);
+extern void __attribute__ ((__constructor__)) init (void);
+
+void *h;
+
+int
+foo (void)
+{
+ return 42 + bar ();
+}
+
+int
+baz (void)
+{
+ return -21;
+}
+
+
+void
+__attribute__ ((__constructor__))
+init (void)
+{
+ h = dlopen ("constload3.so", RTLD_GLOBAL | RTLD_LAZY);
+ if (h == NULL)
+ {
+ puts ("failed to load constload3");
+ exit (1);
+ }
+ else
+ puts ("succeeded loading constload3");
+}
+
+static void
+__attribute__ ((__destructor__))
+fini (void)
+{
+ if (dlclose (h) != 0)
+ {
+ puts ("failed to unload constload3");
+ exit (1);
+ }
+ else
+ puts ("succeeded unloading constload3");
+}
diff --git a/libc/elf/constload3.c b/libc/elf/constload3.c
new file mode 100644
index 000000000..9c37620bb
--- /dev/null
+++ b/libc/elf/constload3.c
@@ -0,0 +1,8 @@
+extern int baz (void);
+extern int bar (void);
+
+int
+bar (void)
+{
+ return -21 + baz ();
+}
diff --git a/libc/elf/dblload.c b/libc/elf/dblload.c
new file mode 100644
index 000000000..52389a60c
--- /dev/null
+++ b/libc/elf/dblload.c
@@ -0,0 +1,53 @@
+#include <dlfcn.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+int
+main (void)
+{
+ void *p1;
+ void *p2;
+ int (*fp) (void);
+ int result;
+
+ mtrace ();
+
+ p1 = dlopen ("dblloadmod1.so", RTLD_LAZY);
+ if (p1 == NULL)
+ {
+ printf ("cannot open dblloadmod1.so: %s\n", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ p2 = dlopen ("dblloadmod2.so", RTLD_LAZY);
+ if (p1 == NULL)
+ {
+ printf ("cannot open dblloadmod2.so: %s\n", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ fp = dlsym (p1, "foo");
+ if (fp == NULL)
+ {
+ printf ("cannot get function \"foo\": %s\n", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ result = fp ();
+
+ if (dlclose (p1) != 0)
+ {
+ printf ("error while closing dblloadmod1.so: %s\n", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ if (dlclose (p2) != 0)
+ {
+ printf ("error while closing dblloadmod2.so: %s\n", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ return result;
+}
diff --git a/libc/elf/dblloadmod1.c b/libc/elf/dblloadmod1.c
new file mode 100644
index 000000000..ecec29ec6
--- /dev/null
+++ b/libc/elf/dblloadmod1.c
@@ -0,0 +1,8 @@
+extern int bar (void);
+extern int foo (void);
+
+int
+foo (void)
+{
+ return 10 + bar ();
+}
diff --git a/libc/elf/dblloadmod2.c b/libc/elf/dblloadmod2.c
new file mode 100644
index 000000000..3e20aa941
--- /dev/null
+++ b/libc/elf/dblloadmod2.c
@@ -0,0 +1,15 @@
+extern int bar (void);
+extern int baz (void);
+extern int xyzzy (void);
+
+int
+baz (void)
+{
+ return -42;
+}
+
+int
+xyzzy (void)
+{
+ return 10 + bar ();
+}
diff --git a/libc/elf/dblloadmod3.c b/libc/elf/dblloadmod3.c
new file mode 100644
index 000000000..80ac3a637
--- /dev/null
+++ b/libc/elf/dblloadmod3.c
@@ -0,0 +1,8 @@
+extern int bar (void);
+extern int baz (void);
+
+int
+bar (void)
+{
+ return 32 + baz ();
+}
diff --git a/libc/elf/dblunload.c b/libc/elf/dblunload.c
new file mode 100644
index 000000000..ab0b2a5e9
--- /dev/null
+++ b/libc/elf/dblunload.c
@@ -0,0 +1,53 @@
+#include <dlfcn.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+int
+main (void)
+{
+ void *p1;
+ void *p2;
+ int (*fp) (void);
+ int result;
+
+ mtrace ();
+
+ p1 = dlopen ("dblloadmod1.so", RTLD_LAZY);
+ if (p1 == NULL)
+ {
+ printf ("cannot load dblloadmod1.so: %s\n", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ p2 = dlopen ("dblloadmod2.so", RTLD_LAZY);
+ if (p2 == NULL)
+ {
+ printf ("cannot load dblloadmod2.so: %s\n", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ if (dlclose (p1) != 0)
+ {
+ printf ("error while closing dblloadmod1.so: %s\n", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ fp = dlsym (p2, "xyzzy");
+ if (fp == NULL)
+ {
+ printf ("cannot get function \"xyzzy\": %s\n", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ result = fp ();
+
+ if (dlclose (p2) != 0)
+ {
+ printf ("error while closing dblloadmod2.so: %s\n", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ return result;
+}
diff --git a/libc/elf/dep1.c b/libc/elf/dep1.c
new file mode 100644
index 000000000..7ef47adb4
--- /dev/null
+++ b/libc/elf/dep1.c
@@ -0,0 +1,25 @@
+#include <unistd.h>
+
+extern int dep1 (void);
+extern int dep2 (void);
+extern int dep4 (void);
+
+static void
+__attribute__ ((constructor))
+init (void)
+{
+ write (1, "3", 1);
+}
+
+static void
+__attribute__ ((destructor))
+fini (void)
+{
+ write (1, "6", 1);
+}
+
+int
+dep1 (void)
+{
+ return dep4 () - dep2 ();
+}
diff --git a/libc/elf/dep2.c b/libc/elf/dep2.c
new file mode 100644
index 000000000..749036a4e
--- /dev/null
+++ b/libc/elf/dep2.c
@@ -0,0 +1,25 @@
+#include <unistd.h>
+
+extern int dep2 (void);
+extern int dep3 (void);
+extern int dep4 (void);
+
+static void
+__attribute__ ((constructor))
+init (void)
+{
+ write (1, "2", 1);
+}
+
+static void
+__attribute__ ((destructor))
+fini (void)
+{
+ write (1, "7", 1);
+}
+
+int
+dep2 (void)
+{
+ return dep3 () - dep4 ();
+}
diff --git a/libc/elf/dep3.c b/libc/elf/dep3.c
new file mode 100644
index 000000000..3df628200
--- /dev/null
+++ b/libc/elf/dep3.c
@@ -0,0 +1,23 @@
+#include <unistd.h>
+
+extern int dep3 (void);
+
+static void
+__attribute__ ((constructor))
+init (void)
+{
+ write (1, "0", 1);
+}
+
+static void
+__attribute__ ((destructor))
+fini (void)
+{
+ write (1, "9\n", 2);
+}
+
+int
+dep3 (void)
+{
+ return 42;
+}
diff --git a/libc/elf/dep4.c b/libc/elf/dep4.c
new file mode 100644
index 000000000..c496d6f53
--- /dev/null
+++ b/libc/elf/dep4.c
@@ -0,0 +1,24 @@
+#include <unistd.h>
+
+extern int dep3 (void);
+extern int dep4 (void);
+
+static void
+__attribute__ ((constructor))
+init (void)
+{
+ write (1, "1", 1);
+}
+
+static void
+__attribute__ ((destructor))
+fini (void)
+{
+ write (1, "8", 1);
+}
+
+int
+dep4 (void)
+{
+ return dep3 ();
+}
diff --git a/libc/elf/dl-addr.c b/libc/elf/dl-addr.c
new file mode 100644
index 000000000..5d3719adb
--- /dev/null
+++ b/libc/elf/dl-addr.c
@@ -0,0 +1,162 @@
+/* Locate the shared object symbol nearest a given address.
+ Copyright (C) 1996-2004, 2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <dlfcn.h>
+#include <stddef.h>
+#include <ldsodefs.h>
+
+
+int
+internal_function
+_dl_addr (const void *address, Dl_info *info,
+ struct link_map **mapp, const ElfW(Sym) **symbolp)
+{
+ const ElfW(Addr) addr = DL_LOOKUP_ADDRESS (address);
+
+ /* Protect against concurrent loads and unloads. */
+ __rtld_lock_lock_recursive (GL(dl_load_lock));
+
+ /* Find the highest-addressed object that ADDRESS is not below. */
+ struct link_map *match = NULL;
+ for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
+ for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l; l = l->l_next)
+ if (addr >= l->l_map_start && addr < l->l_map_end)
+ {
+ /* We know ADDRESS lies within L if in any shared object.
+ Make sure it isn't past the end of L's segments. */
+ size_t n = l->l_phnum;
+ if (n > 0)
+ {
+ do
+ --n;
+ while (l->l_phdr[n].p_type != PT_LOAD);
+ if (addr >= (l->l_addr +
+ l->l_phdr[n].p_vaddr + l->l_phdr[n].p_memsz))
+ /* Off the end of the highest-addressed shared object. */
+ continue;
+ }
+
+ match = l;
+ break;
+ }
+
+ int result = 0;
+ if (match != NULL)
+ {
+ /* Now we know what object the address lies in. */
+ info->dli_fname = match->l_name;
+ info->dli_fbase = (void *) match->l_map_start;
+
+ /* If this is the main program the information is incomplete. */
+ if (__builtin_expect (match->l_name[0], 'a') == '\0'
+ && match->l_type == lt_executable)
+ info->dli_fname = _dl_argv[0];
+
+ const ElfW(Sym) *symtab
+ = (const ElfW(Sym) *) D_PTR (match, l_info[DT_SYMTAB]);
+ const char *strtab = (const char *) D_PTR (match, l_info[DT_STRTAB]);
+
+ ElfW(Word) strtabsize = match->l_info[DT_STRSZ]->d_un.d_val;
+
+ const ElfW(Sym) *matchsym = NULL;
+ if (match->l_info[DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM
+ + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] != NULL)
+ {
+ /* We look at all symbol table entries referenced by the
+ hash table. */
+ for (Elf_Symndx bucket = 0; bucket < match->l_nbuckets; ++bucket)
+ {
+ Elf32_Word symndx = match->l_gnu_buckets[bucket];
+ if (symndx != 0)
+ {
+ const Elf32_Word *hasharr = &match->l_gnu_chain_zero[symndx];
+
+ do
+ {
+ /* The hash table never references local symbols
+ so we can omit that test here. */
+ if (symtab[symndx].st_shndx != SHN_UNDEF
+#ifdef USE_TLS
+ && ELFW(ST_TYPE) (symtab[symndx].st_info) != STT_TLS
+#endif
+ && DL_ADDR_SYM_MATCH (match, &symtab[symndx],
+ matchsym, addr)
+ && symtab[symndx].st_name < strtabsize)
+ matchsym = (ElfW(Sym) *) &symtab[symndx];
+
+ ++symndx;
+ }
+ while ((*hasharr++ & 1u) == 0);
+ }
+ }
+ }
+ else
+ {
+ const ElfW(Sym) *symtabend;
+ if (match->l_info[DT_HASH] != NULL)
+ symtabend = (symtab
+ + ((Elf_Symndx *) D_PTR (match, l_info[DT_HASH]))[1]);
+ else
+ /* There is no direct way to determine the number of symbols in the
+ dynamic symbol table and no hash table is present. The ELF
+ binary is ill-formed but what shall we do? Use the beginning of
+ the string table which generally follows the symbol table. */
+ symtabend = (const ElfW(Sym) *) strtab;
+
+ for (; (void *) symtab < (void *) symtabend; ++symtab)
+ if ((ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL
+ || ELFW(ST_BIND) (symtab->st_info) == STB_WEAK)
+#ifdef USE_TLS
+ && ELFW(ST_TYPE) (symtab->st_info) != STT_TLS
+#endif
+ && symtab->st_shndx != SHN_UNDEF
+ && DL_ADDR_SYM_MATCH (match, symtab, matchsym, addr)
+ && symtab->st_name < strtabsize)
+ matchsym = (ElfW(Sym) *) symtab;
+ }
+
+ if (mapp)
+ *mapp = match;
+ if (symbolp)
+ *symbolp = matchsym;
+
+ if (matchsym)
+ {
+ /* We found a symbol close by. Fill in its name and exact
+ address. */
+ lookup_t matchl = LOOKUP_VALUE (match);
+
+ info->dli_sname = strtab + matchsym->st_name;
+ info->dli_saddr = DL_SYMBOL_ADDRESS (matchl, matchsym);
+ }
+ else
+ {
+ /* No symbol matches. We return only the containing object. */
+ info->dli_sname = NULL;
+ info->dli_saddr = NULL;
+ }
+
+ result = 1;
+ }
+
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
+
+ return result;
+}
+libc_hidden_def (_dl_addr)
diff --git a/libc/elf/dl-brk.c b/libc/elf/dl-brk.c
new file mode 100644
index 000000000..c37cdfec3
--- /dev/null
+++ b/libc/elf/dl-brk.c
@@ -0,0 +1,5 @@
+/* We can use the normal code but we also know the __curbrk is not exported
+ from ld.so. */
+extern void *__curbrk attribute_hidden;
+
+#include <brk.c>
diff --git a/libc/elf/dl-cache.c b/libc/elf/dl-cache.c
new file mode 100644
index 000000000..29886e194
--- /dev/null
+++ b/libc/elf/dl-cache.c
@@ -0,0 +1,311 @@
+/* Support for reading /etc/ld.so.cache files written by Linux ldconfig.
+ Copyright (C) 1996-2002, 2003, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <sys/mman.h>
+#include <dl-cache.h>
+#include <dl-procinfo.h>
+
+#include <stdio-common/_itoa.h>
+
+#ifndef _DL_PLATFORMS_COUNT
+# define _DL_PLATFORMS_COUNT 0
+#endif
+
+/* This is the starting address and the size of the mmap()ed file. */
+static struct cache_file *cache;
+static struct cache_file_new *cache_new;
+static size_t cachesize;
+
+/* 1 if cache_data + PTR points into the cache. */
+#define _dl_cache_verify_ptr(ptr) (ptr < cache_data_size)
+
+#define SEARCH_CACHE(cache) \
+/* We use binary search since the table is sorted in the cache file. \
+ The first matching entry in the table is returned. \
+ It is important to use the same algorithm as used while generating \
+ the cache file. */ \
+do \
+ { \
+ left = 0; \
+ right = cache->nlibs - 1; \
+ \
+ while (left <= right) \
+ { \
+ __typeof__ (cache->libs[0].key) key; \
+ \
+ middle = (left + right) / 2; \
+ \
+ key = cache->libs[middle].key; \
+ \
+ /* Make sure string table indices are not bogus before using \
+ them. */ \
+ if (! _dl_cache_verify_ptr (key)) \
+ { \
+ cmpres = 1; \
+ break; \
+ } \
+ \
+ /* Actually compare the entry with the key. */ \
+ cmpres = _dl_cache_libcmp (name, cache_data + key); \
+ if (__builtin_expect (cmpres == 0, 0)) \
+ { \
+ /* Found it. LEFT now marks the last entry for which we \
+ know the name is correct. */ \
+ left = middle; \
+ \
+ /* There might be entries with this name before the one we \
+ found. So we have to find the beginning. */ \
+ while (middle > 0) \
+ { \
+ __typeof__ (cache->libs[0].key) key; \
+ \
+ key = cache->libs[middle - 1].key; \
+ /* Make sure string table indices are not bogus before \
+ using them. */ \
+ if (! _dl_cache_verify_ptr (key) \
+ /* Actually compare the entry. */ \
+ || _dl_cache_libcmp (name, cache_data + key) != 0) \
+ break; \
+ --middle; \
+ } \
+ \
+ do \
+ { \
+ int flags; \
+ __typeof__ (cache->libs[0]) *lib = &cache->libs[middle]; \
+ \
+ /* Only perform the name test if necessary. */ \
+ if (middle > left \
+ /* We haven't seen this string so far. Test whether the \
+ index is ok and whether the name matches. Otherwise \
+ we are done. */ \
+ && (! _dl_cache_verify_ptr (lib->key) \
+ || (_dl_cache_libcmp (name, cache_data + lib->key) \
+ != 0))) \
+ break; \
+ \
+ flags = lib->flags; \
+ if (_dl_cache_check_flags (flags) \
+ && _dl_cache_verify_ptr (lib->value)) \
+ { \
+ if (best == NULL || flags == GLRO(dl_correct_cache_id)) \
+ { \
+ HWCAP_CHECK; \
+ best = cache_data + lib->value; \
+ \
+ if (flags == GLRO(dl_correct_cache_id)) \
+ /* We've found an exact match for the shared \
+ object and no general `ELF' release. Stop \
+ searching. */ \
+ break; \
+ } \
+ } \
+ } \
+ while (++middle <= right); \
+ break; \
+ } \
+ \
+ if (cmpres < 0) \
+ left = middle + 1; \
+ else \
+ right = middle - 1; \
+ } \
+ } \
+while (0)
+
+
+int
+internal_function
+_dl_cache_libcmp (const char *p1, const char *p2)
+{
+ while (*p1 != '\0')
+ {
+ if (*p1 >= '0' && *p1 <= '9')
+ {
+ if (*p2 >= '0' && *p2 <= '9')
+ {
+ /* Must compare this numerically. */
+ int val1;
+ int val2;
+
+ val1 = *p1++ - '0';
+ val2 = *p2++ - '0';
+ while (*p1 >= '0' && *p1 <= '9')
+ val1 = val1 * 10 + *p1++ - '0';
+ while (*p2 >= '0' && *p2 <= '9')
+ val2 = val2 * 10 + *p2++ - '0';
+ if (val1 != val2)
+ return val1 - val2;
+ }
+ else
+ return 1;
+ }
+ else if (*p2 >= '0' && *p2 <= '9')
+ return -1;
+ else if (*p1 != *p2)
+ return *p1 - *p2;
+ else
+ {
+ ++p1;
+ ++p2;
+ }
+ }
+ return *p1 - *p2;
+}
+
+
+/* Look up NAME in ld.so.cache and return the file name stored there,
+ or null if none is found. */
+
+const char *
+internal_function
+_dl_load_cache_lookup (const char *name)
+{
+ int left, right, middle;
+ int cmpres;
+ const char *cache_data;
+ uint32_t cache_data_size;
+ const char *best;
+
+ /* Print a message if the loading of libs is traced. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
+ _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE);
+
+ if (cache == NULL)
+ {
+ /* Read the contents of the file. */
+ void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,
+ PROT_READ);
+
+ /* We can handle three different cache file formats here:
+ - the old libc5/glibc2.0/2.1 format
+ - the old format with the new format in it
+ - only the new format
+ The following checks if the cache contains any of these formats. */
+ if (file != MAP_FAILED && cachesize > sizeof *cache
+ && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0)
+ {
+ size_t offset;
+ /* Looks ok. */
+ cache = file;
+
+ /* Check for new version. */
+ offset = ALIGN_CACHE (sizeof (struct cache_file)
+ + cache->nlibs * sizeof (struct file_entry));
+
+ cache_new = (struct cache_file_new *) ((void *) cache + offset);
+ if (cachesize < (offset + sizeof (struct cache_file_new))
+ || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW,
+ sizeof CACHEMAGIC_VERSION_NEW - 1) != 0)
+ cache_new = (void *) -1;
+ }
+ else if (file != MAP_FAILED && cachesize > sizeof *cache_new
+ && memcmp (file, CACHEMAGIC_VERSION_NEW,
+ sizeof CACHEMAGIC_VERSION_NEW - 1) == 0)
+ {
+ cache_new = file;
+ cache = file;
+ }
+ else
+ {
+ if (file != MAP_FAILED)
+ __munmap (file, cachesize);
+ cache = (void *) -1;
+ }
+
+ assert (cache != NULL);
+ }
+
+ if (cache == (void *) -1)
+ /* Previously looked for the cache file and didn't find it. */
+ return NULL;
+
+ best = NULL;
+
+ if (cache_new != (void *) -1)
+ {
+ uint64_t platform;
+
+ /* This is where the strings start. */
+ cache_data = (const char *) cache_new;
+
+ /* Now we can compute how large the string table is. */
+ cache_data_size = (const char *) cache + cachesize - cache_data;
+
+ platform = _dl_string_platform (GLRO(dl_platform));
+ if (platform != (uint64_t) -1)
+ platform = 1ULL << platform;
+
+ /* Only accept hwcap if it's for the right platform. */
+#ifdef USE_TLS
+# define _DL_HWCAP_TLS_MASK (1LL << 63)
+#else
+# define _DL_HWCAP_TLS_MASK 0
+#endif
+#define HWCAP_CHECK \
+ if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion)) \
+ continue; \
+ if (_DL_PLATFORMS_COUNT && platform != -1 \
+ && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0 \
+ && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform) \
+ continue; \
+ if (lib->hwcap \
+ & ~(GLRO(dl_hwcap) | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK)) \
+ continue
+ SEARCH_CACHE (cache_new);
+ }
+ else
+ {
+ /* This is where the strings start. */
+ cache_data = (const char *) &cache->libs[cache->nlibs];
+
+ /* Now we can compute how large the string table is. */
+ cache_data_size = (const char *) cache + cachesize - cache_data;
+
+#undef HWCAP_CHECK
+#define HWCAP_CHECK do {} while (0)
+ SEARCH_CACHE (cache);
+ }
+
+ /* Print our result if wanted. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)
+ && best != NULL)
+ _dl_debug_printf (" trying file=%s\n", best);
+
+ return best;
+}
+
+#ifndef MAP_COPY
+/* If the system does not support MAP_COPY we cannot leave the file open
+ all the time since this would create problems when the file is replaced.
+ Therefore we provide this function to close the file and open it again
+ once needed. */
+void
+_dl_unload_cache (void)
+{
+ if (cache != NULL && cache != (struct cache_file *) -1)
+ {
+ __munmap (cache, cachesize);
+ cache = NULL;
+ }
+}
+#endif
diff --git a/libc/elf/dl-caller.c b/libc/elf/dl-caller.c
new file mode 100644
index 000000000..b0c1264d0
--- /dev/null
+++ b/libc/elf/dl-caller.c
@@ -0,0 +1,87 @@
+/* Check whether caller comes from the right place.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <ldsodefs.h>
+#include <stddef.h>
+#include <caller.h>
+#include <gnu/lib-names.h>
+
+
+int
+attribute_hidden
+_dl_check_caller (const void *caller, enum allowmask mask)
+{
+ static const char expected1[] = LIBC_SO;
+ static const char expected2[] = LIBDL_SO;
+#ifdef LIBPTHREAD_SO
+ static const char expected3[] = LIBPTHREAD_SO;
+#endif
+ static const char expected4[] = LD_SO;
+
+ for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
+ for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL;
+ l = l->l_next)
+ if (caller >= (const void *) l->l_map_start
+ && caller < (const void *) l->l_text_end)
+ {
+ /* The address falls into this DSO's address range. Check the
+ name. */
+ if ((mask & allow_libc) && strcmp (expected1, l->l_name) == 0)
+ return 0;
+ if ((mask & allow_libdl) && strcmp (expected2, l->l_name) == 0)
+ return 0;
+#ifdef LIBPTHREAD_SO
+ if ((mask & allow_libpthread) && strcmp (expected3, l->l_name) == 0)
+ return 0;
+#endif
+ if ((mask & allow_ldso) && strcmp (expected4, l->l_name) == 0)
+ return 0;
+
+ struct libname_list *runp = l->l_libname;
+
+ while (runp != NULL)
+ {
+ if ((mask & allow_libc) && strcmp (expected1, runp->name) == 0)
+ return 0;
+ if ((mask & allow_libdl) && strcmp (expected2, runp->name) == 0)
+ return 0;
+#ifdef LIBPTHREAD_SO
+ if ((mask & allow_libpthread)
+ && strcmp (expected3, runp->name) == 0)
+ return 0;
+#endif
+ if ((mask & allow_ldso) && strcmp (expected4, runp->name) == 0)
+ return 0;
+
+ runp = runp->next;
+ }
+
+ break;
+ }
+
+ /* Maybe the dynamic linker is not yet on the list. */
+ if ((mask & allow_ldso) != 0
+ && caller >= (const void *) GL(dl_rtld_map).l_map_start
+ && caller < (const void *) GL(dl_rtld_map).l_text_end)
+ return 0;
+
+ /* No valid caller. */
+ return 1;
+}
diff --git a/libc/elf/dl-close.c b/libc/elf/dl-close.c
new file mode 100644
index 000000000..754dd678f
--- /dev/null
+++ b/libc/elf/dl-close.c
@@ -0,0 +1,690 @@
+/* Close a shared object opened by `_dl_open'.
+ Copyright (C) 1996-2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <libintl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <bits/libc-lock.h>
+#include <ldsodefs.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+
+/* Type of the constructor functions. */
+typedef void (*fini_t) (void);
+
+
+#ifdef USE_TLS
+/* Returns true we an non-empty was found. */
+static bool
+remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp,
+ bool should_be_there)
+{
+ if (idx - disp >= listp->len)
+ {
+ if (listp->next == NULL)
+ {
+ /* The index is not actually valid in the slotinfo list,
+ because this object was closed before it was fully set
+ up due to some error. */
+ assert (! should_be_there);
+ }
+ else
+ {
+ if (remove_slotinfo (idx, listp->next, disp + listp->len,
+ should_be_there))
+ return true;
+
+ /* No non-empty entry. Search from the end of this element's
+ slotinfo array. */
+ idx = disp + listp->len;
+ }
+ }
+ else
+ {
+ struct link_map *old_map = listp->slotinfo[idx - disp].map;
+
+ /* The entry might still be in its unused state if we are closing an
+ object that wasn't fully set up. */
+ if (__builtin_expect (old_map != NULL, 1))
+ {
+ assert (old_map->l_tls_modid == idx);
+
+ /* Mark the entry as unused. */
+ listp->slotinfo[idx - disp].gen = GL(dl_tls_generation) + 1;
+ listp->slotinfo[idx - disp].map = NULL;
+ }
+
+ /* If this is not the last currently used entry no need to look
+ further. */
+ if (idx != GL(dl_tls_max_dtv_idx))
+ return true;
+ }
+
+ while (idx - disp > (disp == 0 ? 1 + GL(dl_tls_static_nelem) : 0))
+ {
+ --idx;
+
+ if (listp->slotinfo[idx - disp].map != NULL)
+ {
+ /* Found a new last used index. */
+ GL(dl_tls_max_dtv_idx) = idx;
+ return true;
+ }
+ }
+
+ /* No non-entry in this list element. */
+ return false;
+}
+#endif
+
+
+void
+_dl_close (void *_map)
+{
+ struct link_map *map = _map;
+ Lmid_t ns = map->l_ns;
+ unsigned int i;
+ /* First see whether we can remove the object at all. */
+ if (__builtin_expect (map->l_flags_1 & DF_1_NODELETE, 0)
+ && map->l_init_called)
+ /* Nope. Do nothing. */
+ return;
+
+ if (__builtin_expect (map->l_direct_opencount, 1) == 0)
+ GLRO(dl_signal_error) (0, map->l_name, NULL, N_("shared object not open"));
+
+ /* Acquire the lock. */
+ __rtld_lock_lock_recursive (GL(dl_load_lock));
+
+ /* One less direct use. */
+ --map->l_direct_opencount;
+
+ /* If _dl_close is called recursively (some destructor call dlclose),
+ just record that the parent _dl_close will need to do garbage collection
+ again and return. */
+ static enum { not_pending, pending, rerun } dl_close_state;
+
+ if (map->l_direct_opencount > 0 || map->l_type != lt_loaded
+ || dl_close_state != not_pending)
+ {
+ if (map->l_direct_opencount == 0 && map->l_type == lt_loaded)
+ dl_close_state = rerun;
+
+ /* There are still references to this object. Do nothing more. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0))
+ _dl_debug_printf ("\nclosing file=%s; direct_opencount=%u\n",
+ map->l_name, map->l_direct_opencount);
+
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
+ return;
+ }
+
+ retry:
+ dl_close_state = pending;
+
+#ifdef USE_TLS
+ bool any_tls = false;
+#endif
+ const unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
+ char used[nloaded];
+ char done[nloaded];
+ struct link_map *maps[nloaded];
+
+ /* Run over the list and assign indexes to the link maps and enter
+ them into the MAPS array. */
+ int idx = 0;
+ for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next)
+ {
+ l->l_idx = idx;
+ maps[idx] = l;
+ ++idx;
+ }
+ assert (idx == nloaded);
+
+ /* Prepare the bitmaps. */
+ memset (used, '\0', sizeof (used));
+ memset (done, '\0', sizeof (done));
+
+ /* Keep track of the lowest index link map we have covered already. */
+ int done_index = -1;
+ while (++done_index < nloaded)
+ {
+ struct link_map *l = maps[done_index];
+
+ if (done[done_index])
+ /* Already handled. */
+ continue;
+
+ /* Check whether this object is still used. */
+ if (l->l_type == lt_loaded
+ && l->l_direct_opencount == 0
+ && (l->l_flags_1 & DF_1_NODELETE) == 0
+ && !used[done_index])
+ continue;
+
+ /* We need this object and we handle it now. */
+ done[done_index] = 1;
+ used[done_index] = 1;
+ /* Signal the object is still needed. */
+ l->l_idx = -1;
+
+ /* Mark all dependencies as used. */
+ if (l->l_initfini != NULL)
+ {
+ struct link_map **lp = &l->l_initfini[1];
+ while (*lp != NULL)
+ {
+ if ((*lp)->l_idx != -1)
+ {
+ assert ((*lp)->l_idx >= 0 && (*lp)->l_idx < nloaded);
+
+ if (!used[(*lp)->l_idx])
+ {
+ used[(*lp)->l_idx] = 1;
+ if ((*lp)->l_idx - 1 < done_index)
+ done_index = (*lp)->l_idx - 1;
+ }
+ }
+
+ ++lp;
+ }
+ }
+ /* And the same for relocation dependencies. */
+ if (l->l_reldeps != NULL)
+ for (unsigned int j = 0; j < l->l_reldepsact; ++j)
+ {
+ struct link_map *jmap = l->l_reldeps[j];
+
+ if (jmap->l_idx != -1)
+ {
+ assert (jmap->l_idx >= 0 && jmap->l_idx < nloaded);
+
+ if (!used[jmap->l_idx])
+ {
+ used[jmap->l_idx] = 1;
+ if (jmap->l_idx - 1 < done_index)
+ done_index = jmap->l_idx - 1;
+ }
+ }
+ }
+ }
+
+ /* Sort the entries. */
+ _dl_sort_fini (GL(dl_ns)[ns]._ns_loaded, maps, nloaded, used, ns);
+
+ /* Call all termination functions at once. */
+#ifdef SHARED
+ bool do_audit = GLRO(dl_naudit) > 0 && !GL(dl_ns)[ns]._ns_loaded->l_auditing;
+#endif
+ bool unload_any = false;
+ unsigned int first_loaded = ~0;
+ for (i = 0; i < nloaded; ++i)
+ {
+ struct link_map *imap = maps[i];
+
+ /* All elements must be in the same namespace. */
+ assert (imap->l_ns == ns);
+
+ if (!used[i])
+ {
+ assert (imap->l_type == lt_loaded
+ && (imap->l_flags_1 & DF_1_NODELETE) == 0);
+
+ /* Call its termination function. Do not do it for
+ half-cooked objects. */
+ if (imap->l_init_called)
+ {
+ /* When debugging print a message first. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS,
+ 0))
+ _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
+ imap->l_name, ns);
+
+ if (imap->l_info[DT_FINI_ARRAY] != NULL)
+ {
+ ElfW(Addr) *array =
+ (ElfW(Addr) *) (imap->l_addr
+ + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
+ unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
+ / sizeof (ElfW(Addr)));
+
+ while (sz-- > 0)
+ ((fini_t) array[sz]) ();
+ }
+
+ /* Next try the old-style destructor. */
+ if (imap->l_info[DT_FINI] != NULL)
+ (*(void (*) (void)) DL_DT_FINI_ADDRESS
+ (imap, ((void *) imap->l_addr
+ + imap->l_info[DT_FINI]->d_un.d_ptr))) ();
+ }
+
+#ifdef SHARED
+ /* Auditing checkpoint: we have a new object. */
+ if (__builtin_expect (do_audit, 0))
+ {
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->objclose != NULL)
+ /* Return value is ignored. */
+ (void) afct->objclose (&imap->l_audit[cnt].cookie);
+
+ afct = afct->next;
+ }
+ }
+#endif
+
+ /* This object must not be used anymore. */
+ imap->l_removed = 1;
+
+ /* We indeed have an object to remove. */
+ unload_any = true;
+
+ /* Remember where the first dynamically loaded object is. */
+ if (i < first_loaded)
+ first_loaded = i;
+ }
+ /* Else used[i]. */
+ else if (imap->l_type == lt_loaded)
+ {
+ if (imap->l_searchlist.r_list == NULL
+ && imap->l_initfini != NULL)
+ {
+ /* The object is still used. But one of the objects we are
+ unloading right now is responsible for loading it. If
+ the current object does not have it's own scope yet we
+ have to create one. This has to be done before running
+ the finalizers.
+
+ To do this count the number of dependencies. */
+ unsigned int cnt;
+ for (cnt = 1; imap->l_initfini[cnt] != NULL; ++cnt)
+ ;
+
+ /* We simply reuse the l_initfini list. */
+ imap->l_searchlist.r_list = &imap->l_initfini[cnt + 1];
+ imap->l_searchlist.r_nlist = cnt;
+
+ for (cnt = 0; imap->l_scope[cnt] != NULL; ++cnt)
+ /* This relies on l_scope[] entries being always set either
+ to its own l_symbolic_searchlist address, or some other map's
+ l_searchlist address. */
+ if (imap->l_scope[cnt] != &imap->l_symbolic_searchlist)
+ {
+ struct link_map *tmap;
+
+ tmap = (struct link_map *) ((char *) imap->l_scope[cnt]
+ - offsetof (struct link_map,
+ l_searchlist));
+ assert (tmap->l_ns == ns);
+ if (tmap->l_idx != -1)
+ {
+ imap->l_scope[cnt] = &imap->l_searchlist;
+ break;
+ }
+ }
+ }
+
+ /* The loader is gone, so mark the object as not having one.
+ Note: l_idx != -1 -> object will be removed. */
+ if (imap->l_loader != NULL && imap->l_loader->l_idx != -1)
+ imap->l_loader = NULL;
+
+ /* Remember where the first dynamically loaded object is. */
+ if (i < first_loaded)
+ first_loaded = i;
+ }
+ }
+
+ /* If there are no objects to unload, do nothing further. */
+ if (!unload_any)
+ goto out;
+
+#ifdef SHARED
+ /* Auditing checkpoint: we will start deleting objects. */
+ if (__builtin_expect (do_audit, 0))
+ {
+ struct link_map *head = GL(dl_ns)[ns]._ns_loaded;
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ /* Do not call the functions for any auditing object. */
+ if (head->l_auditing == 0)
+ {
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->activity != NULL)
+ afct->activity (&head->l_audit[cnt].cookie, LA_ACT_DELETE);
+
+ afct = afct->next;
+ }
+ }
+ }
+#endif
+
+ /* Notify the debugger we are about to remove some loaded objects. */
+ struct r_debug *r = _dl_debug_initialize (0, ns);
+ r->r_state = RT_DELETE;
+ _dl_debug_state ();
+
+#ifdef USE_TLS
+ size_t tls_free_start;
+ size_t tls_free_end;
+ tls_free_start = tls_free_end = NO_TLS_OFFSET;
+#endif
+
+ /* Check each element of the search list to see if all references to
+ it are gone. */
+ for (i = first_loaded; i < nloaded; ++i)
+ {
+ struct link_map *imap = maps[i];
+ if (!used[i])
+ {
+ assert (imap->l_type == lt_loaded);
+
+ /* That was the last reference, and this was a dlopen-loaded
+ object. We can unmap it. */
+ if (__builtin_expect (imap->l_global, 0))
+ {
+ /* This object is in the global scope list. Remove it. */
+ unsigned int cnt = GL(dl_ns)[ns]._ns_main_searchlist->r_nlist;
+
+ do
+ --cnt;
+ while (GL(dl_ns)[ns]._ns_main_searchlist->r_list[cnt] != imap);
+
+ /* The object was already correctly registered. */
+ while (++cnt
+ < GL(dl_ns)[ns]._ns_main_searchlist->r_nlist)
+ GL(dl_ns)[ns]._ns_main_searchlist->r_list[cnt - 1]
+ = GL(dl_ns)[ns]._ns_main_searchlist->r_list[cnt];
+
+ --GL(dl_ns)[ns]._ns_main_searchlist->r_nlist;
+ }
+
+#ifdef USE_TLS
+ /* Remove the object from the dtv slotinfo array if it uses TLS. */
+ if (__builtin_expect (imap->l_tls_blocksize > 0, 0))
+ {
+ any_tls = true;
+
+ if (GL(dl_tls_dtv_slotinfo_list) != NULL
+ && ! remove_slotinfo (imap->l_tls_modid,
+ GL(dl_tls_dtv_slotinfo_list), 0,
+ imap->l_init_called))
+ /* All dynamically loaded modules with TLS are unloaded. */
+ GL(dl_tls_max_dtv_idx) = GL(dl_tls_static_nelem);
+
+ if (imap->l_tls_offset != NO_TLS_OFFSET)
+ {
+ /* Collect a contiguous chunk built from the objects in
+ this search list, going in either direction. When the
+ whole chunk is at the end of the used area then we can
+ reclaim it. */
+# if TLS_TCB_AT_TP
+ if (tls_free_start == NO_TLS_OFFSET
+ || (size_t) imap->l_tls_offset == tls_free_start)
+ {
+ /* Extend the contiguous chunk being reclaimed. */
+ tls_free_start
+ = imap->l_tls_offset - imap->l_tls_blocksize;
+
+ if (tls_free_end == NO_TLS_OFFSET)
+ tls_free_end = imap->l_tls_offset;
+ }
+ else if (imap->l_tls_offset - imap->l_tls_blocksize
+ == tls_free_end)
+ /* Extend the chunk backwards. */
+ tls_free_end = imap->l_tls_offset;
+ else
+ {
+ /* This isn't contiguous with the last chunk freed.
+ One of them will be leaked unless we can free
+ one block right away. */
+ if (tls_free_end == GL(dl_tls_static_used))
+ {
+ GL(dl_tls_static_used) = tls_free_start;
+ tls_free_end = imap->l_tls_offset;
+ tls_free_start
+ = tls_free_end - imap->l_tls_blocksize;
+ }
+ else if ((size_t) imap->l_tls_offset
+ == GL(dl_tls_static_used))
+ GL(dl_tls_static_used)
+ = imap->l_tls_offset - imap->l_tls_blocksize;
+ else if (tls_free_end < (size_t) imap->l_tls_offset)
+ {
+ /* We pick the later block. It has a chance to
+ be freed. */
+ tls_free_end = imap->l_tls_offset;
+ tls_free_start
+ = tls_free_end - imap->l_tls_blocksize;
+ }
+ }
+# elif TLS_DTV_AT_TP
+ if ((size_t) imap->l_tls_offset == tls_free_end)
+ /* Extend the contiguous chunk being reclaimed. */
+ tls_free_end -= imap->l_tls_blocksize;
+ else if (imap->l_tls_offset + imap->l_tls_blocksize
+ == tls_free_start)
+ /* Extend the chunk backwards. */
+ tls_free_start = imap->l_tls_offset;
+ else
+ {
+ /* This isn't contiguous with the last chunk freed.
+ One of them will be leaked. */
+ if (tls_free_end == GL(dl_tls_static_used))
+ GL(dl_tls_static_used) = tls_free_start;
+ tls_free_start = imap->l_tls_offset;
+ tls_free_end = tls_free_start + imap->l_tls_blocksize;
+ }
+# else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+ }
+ }
+#endif
+
+ /* We can unmap all the maps at once. We determined the
+ start address and length when we loaded the object and
+ the `munmap' call does the rest. */
+ DL_UNMAP (imap);
+
+ /* Finally, unlink the data structure and free it. */
+ if (imap->l_prev != NULL)
+ imap->l_prev->l_next = imap->l_next;
+ else
+ {
+#ifdef SHARED
+ assert (ns != LM_ID_BASE);
+#endif
+ GL(dl_ns)[ns]._ns_loaded = imap->l_next;
+ }
+
+ --GL(dl_ns)[ns]._ns_nloaded;
+ if (imap->l_next != NULL)
+ imap->l_next->l_prev = imap->l_prev;
+
+ free (imap->l_versions);
+ if (imap->l_origin != (char *) -1)
+ free ((char *) imap->l_origin);
+
+ free (imap->l_reldeps);
+
+ /* Print debugging message. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0))
+ _dl_debug_printf ("\nfile=%s [%lu]; destroying link map\n",
+ imap->l_name, imap->l_ns);
+
+ /* This name always is allocated. */
+ free (imap->l_name);
+ /* Remove the list with all the names of the shared object. */
+
+ struct libname_list *lnp = imap->l_libname;
+ do
+ {
+ struct libname_list *this = lnp;
+ lnp = lnp->next;
+ if (!this->dont_free)
+ free (this);
+ }
+ while (lnp != NULL);
+
+ /* Remove the searchlists. */
+ free (imap->l_initfini);
+
+ /* Remove the scope array if we allocated it. */
+ if (imap->l_scope != imap->l_scope_mem)
+ free (imap->l_scope);
+
+ if (imap->l_phdr_allocated)
+ free ((void *) imap->l_phdr);
+
+ if (imap->l_rpath_dirs.dirs != (void *) -1)
+ free (imap->l_rpath_dirs.dirs);
+ if (imap->l_runpath_dirs.dirs != (void *) -1)
+ free (imap->l_runpath_dirs.dirs);
+
+ free (imap);
+ }
+ }
+
+#ifdef USE_TLS
+ /* If we removed any object which uses TLS bump the generation counter. */
+ if (any_tls)
+ {
+ if (__builtin_expect (++GL(dl_tls_generation) == 0, 0))
+ _dl_fatal_printf ("TLS generation counter wrapped! Please report as described in <http://www.gnu.org/software/libc/bugs.html>.\n");
+
+ if (tls_free_end == GL(dl_tls_static_used))
+ GL(dl_tls_static_used) = tls_free_start;
+ }
+#endif
+
+#ifdef SHARED
+ /* Auditing checkpoint: we have deleted all objects. */
+ if (__builtin_expect (do_audit, 0))
+ {
+ struct link_map *head = GL(dl_ns)[ns]._ns_loaded;
+ /* Do not call the functions for any auditing object. */
+ if (head->l_auditing == 0)
+ {
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->activity != NULL)
+ afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT);
+
+ afct = afct->next;
+ }
+ }
+ }
+#endif
+
+ /* Notify the debugger those objects are finalized and gone. */
+ r->r_state = RT_CONSISTENT;
+ _dl_debug_state ();
+
+ /* Recheck if we need to retry, release the lock. */
+ out:
+ if (dl_close_state == rerun)
+ goto retry;
+
+ dl_close_state = not_pending;
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
+}
+
+
+#ifdef USE_TLS
+static bool __libc_freeres_fn_section
+free_slotinfo (struct dtv_slotinfo_list **elemp)
+{
+ size_t cnt;
+
+ if (*elemp == NULL)
+ /* Nothing here, all is removed (or there never was anything). */
+ return true;
+
+ if (!free_slotinfo (&(*elemp)->next))
+ /* We cannot free the entry. */
+ return false;
+
+ /* That cleared our next pointer for us. */
+
+ for (cnt = 0; cnt < (*elemp)->len; ++cnt)
+ if ((*elemp)->slotinfo[cnt].map != NULL)
+ /* Still used. */
+ return false;
+
+ /* We can remove the list element. */
+ free (*elemp);
+ *elemp = NULL;
+
+ return true;
+}
+#endif
+
+
+libc_freeres_fn (free_mem)
+{
+ for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
+ if (__builtin_expect (GL(dl_ns)[ns]._ns_global_scope_alloc, 0) != 0
+ && (GL(dl_ns)[ns]._ns_main_searchlist->r_nlist
+ // XXX Check whether we need NS-specific initial_searchlist
+ == GLRO(dl_initial_searchlist).r_nlist))
+ {
+ /* All object dynamically loaded by the program are unloaded. Free
+ the memory allocated for the global scope variable. */
+ struct link_map **old = GL(dl_ns)[ns]._ns_main_searchlist->r_list;
+
+ /* Put the old map in. */
+ GL(dl_ns)[ns]._ns_main_searchlist->r_list
+ // XXX Check whether we need NS-specific initial_searchlist
+ = GLRO(dl_initial_searchlist).r_list;
+ /* Signal that the original map is used. */
+ GL(dl_ns)[ns]._ns_global_scope_alloc = 0;
+
+ /* Now free the old map. */
+ free (old);
+ }
+
+#ifdef USE_TLS
+ if (USE___THREAD || GL(dl_tls_dtv_slotinfo_list) != NULL)
+ {
+ /* Free the memory allocated for the dtv slotinfo array. We can do
+ this only if all modules which used this memory are unloaded. */
+# ifdef SHARED
+ if (GL(dl_initial_dtv) == NULL)
+ /* There was no initial TLS setup, it was set up later when
+ it used the normal malloc. */
+ free_slotinfo (&GL(dl_tls_dtv_slotinfo_list));
+ else
+# endif
+ /* The first element of the list does not have to be deallocated.
+ It was allocated in the dynamic linker (i.e., with a different
+ malloc), and in the static library it's in .bss space. */
+ free_slotinfo (&GL(dl_tls_dtv_slotinfo_list)->next);
+ }
+#endif
+}
diff --git a/libc/elf/dl-conflict.c b/libc/elf/dl-conflict.c
new file mode 100644
index 000000000..9b49e77d2
--- /dev/null
+++ b/libc/elf/dl-conflict.c
@@ -0,0 +1,70 @@
+/* Resolve conflicts against already prelinked libraries.
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Jakub Jelinek <jakub@redhat.com>, 2001.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <errno.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include "dynamic-link.h"
+
+void
+_dl_resolve_conflicts (struct link_map *l, ElfW(Rela) *conflict,
+ ElfW(Rela) *conflictend)
+{
+#if ! ELF_MACHINE_NO_RELA
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_RELOC, 0))
+ _dl_debug_printf ("\nconflict processing: %s\n",
+ l->l_name[0] ? l->l_name : rtld_progname);
+
+ {
+ /* Do the conflict relocation of the object and library GOT and other
+ data. */
+
+ /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code. */
+#define RESOLVE_MAP(ref, version, flags) (*ref = NULL, NULL)
+#define RESOLVE(ref, version, flags) (*ref = NULL, 0)
+#define CHECK_STATIC_TLS(ref_map, sym_map) ((void) 0)
+#define RESOLVE_CONFLICT_FIND_MAP(map, r_offset) \
+ do { \
+ while ((resolve_conflict_map->l_map_end < (ElfW(Addr)) (r_offset)) \
+ || (resolve_conflict_map->l_map_start > (ElfW(Addr)) (r_offset))) \
+ resolve_conflict_map = resolve_conflict_map->l_next; \
+ \
+ (map) = resolve_conflict_map; \
+ } while (0)
+
+ /* Prelinking makes no sense for anything but the main namespace. */
+ assert (l->l_ns == LM_ID_BASE);
+ struct link_map *resolve_conflict_map __attribute__ ((__unused__))
+ = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+
+#include "dynamic-link.h"
+
+ GL(dl_num_cache_relocations) += conflictend - conflict;
+
+ for (; conflict < conflictend; ++conflict)
+ elf_machine_rela (l, conflict, NULL, NULL, (void *) conflict->r_offset);
+ }
+#endif
+}
diff --git a/libc/elf/dl-debug.c b/libc/elf/dl-debug.c
new file mode 100644
index 000000000..d00fe87fb
--- /dev/null
+++ b/libc/elf/dl-debug.c
@@ -0,0 +1,78 @@
+/* Communicate dynamic linker state to the debugger at runtime.
+ Copyright (C) 1996, 1998,2000,2002,2004,2005,2006
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <ldsodefs.h>
+
+
+/* These are the members in the public `struct link_map' type.
+ Sanity check that the internal type and the public type match. */
+#define VERIFY_MEMBER(name) \
+ (offsetof (struct link_map_public, name) == offsetof (struct link_map, name))
+extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
+ && VERIFY_MEMBER (l_name)
+ && VERIFY_MEMBER (l_ld)
+ && VERIFY_MEMBER (l_next)
+ && VERIFY_MEMBER (l_prev))
+ ? 1 : -1];
+
+/* This structure communicates dl state to the debugger. The debugger
+ normally finds it via the DT_DEBUG entry in the dynamic section, but in
+ a statically-linked program there is no dynamic section for the debugger
+ to examine and it looks for this particular symbol name. */
+struct r_debug _r_debug;
+
+
+/* Initialize _r_debug if it has not already been done. The argument is
+ the run-time load address of the dynamic linker, to be put in
+ _r_debug.r_ldbase. Returns the address of _r_debug. */
+
+struct r_debug *
+internal_function
+_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
+{
+ struct r_debug *r;
+
+ if (ns == LM_ID_BASE)
+ r = &_r_debug;
+ else
+ r = &GL(dl_ns)[ns]._ns_debug;
+
+ if (r->r_brk == 0 || ldbase != 0)
+ {
+ /* Tell the debugger where to find the map of loaded objects. */
+ r->r_version = 1 /* R_DEBUG_VERSION XXX */;
+ r->r_ldbase = ldbase ?: _r_debug.r_ldbase;
+ r->r_map = (void *) GL(dl_ns)[ns]._ns_loaded;
+ r->r_brk = (ElfW(Addr)) &_dl_debug_state;
+ }
+
+ return r;
+}
+
+
+/* This function exists solely to have a breakpoint set on it by the
+ debugger. The debugger is supposed to find this function's address by
+ examining the r_brk member of struct r_debug, but GDB 4.15 in fact looks
+ for this particular symbol name in the PT_INTERP file. */
+void
+_dl_debug_state (void)
+{
+}
+rtld_hidden_def (_dl_debug_state)
diff --git a/libc/elf/dl-deps.c b/libc/elf/dl-deps.c
new file mode 100644
index 000000000..c35cc977f
--- /dev/null
+++ b/libc/elf/dl-deps.c
@@ -0,0 +1,643 @@
+/* Load the dependencies of a mapped object.
+ Copyright (C) 1996-2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <ldsodefs.h>
+
+#include <dl-dst.h>
+
+/* Whether an shared object references one or more auxiliary objects
+ is signaled by the AUXTAG entry in l_info. */
+#define AUXTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \
+ + DT_EXTRATAGIDX (DT_AUXILIARY))
+/* Whether an shared object references one or more auxiliary objects
+ is signaled by the AUXTAG entry in l_info. */
+#define FILTERTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \
+ + DT_EXTRATAGIDX (DT_FILTER))
+
+
+/* When loading auxiliary objects we must ignore errors. It's ok if
+ an object is missing. */
+struct openaux_args
+ {
+ /* The arguments to openaux. */
+ struct link_map *map;
+ int trace_mode;
+ int open_mode;
+ const char *strtab;
+ const char *name;
+
+ /* The return value of openaux. */
+ struct link_map *aux;
+ };
+
+static void
+openaux (void *a)
+{
+ struct openaux_args *args = (struct openaux_args *) a;
+
+ args->aux = _dl_map_object (args->map, args->name, 0,
+ (args->map->l_type == lt_executable
+ ? lt_library : args->map->l_type),
+ args->trace_mode, args->open_mode,
+ args->map->l_ns);
+}
+
+static ptrdiff_t
+internal_function
+_dl_build_local_scope (struct link_map **list, struct link_map *map)
+{
+ struct link_map **p = list;
+ struct link_map **q;
+
+ *p++ = map;
+ map->l_reserved = 1;
+ if (map->l_initfini)
+ for (q = map->l_initfini + 1; *q; ++q)
+ if (! (*q)->l_reserved)
+ p += _dl_build_local_scope (p, *q);
+ return p - list;
+}
+
+
+/* We use a very special kind of list to track the path
+ through the list of loaded shared objects. We have to
+ produce a flat list with unique members of all involved objects.
+*/
+struct list
+ {
+ int done; /* Nonzero if this map was processed. */
+ struct link_map *map; /* The data. */
+ struct list *next; /* Elements for normal list. */
+ };
+
+
+/* Macro to expand DST. It is an macro since we use `alloca'. */
+#define expand_dst(l, str, fatal) \
+ ({ \
+ const char *__str = (str); \
+ const char *__result = __str; \
+ size_t __dst_cnt = DL_DST_COUNT (__str, 0); \
+ \
+ if (__dst_cnt != 0) \
+ { \
+ char *__newp; \
+ \
+ /* DST must not appear in SUID/SGID programs. */ \
+ if (INTUSE(__libc_enable_secure)) \
+ _dl_signal_error (0, __str, NULL, N_("\
+DST not allowed in SUID/SGID programs")); \
+ \
+ __newp = (char *) alloca (DL_DST_REQUIRED (l, __str, strlen (__str), \
+ __dst_cnt)); \
+ \
+ __result = _dl_dst_substitute (l, __str, __newp, 0); \
+ \
+ if (*__result == '\0') \
+ { \
+ /* The replacement for the DST is not known. We can't \
+ processed. */ \
+ if (fatal) \
+ _dl_signal_error (0, __str, NULL, N_("\
+empty dynamics string token substitution")); \
+ else \
+ { \
+ /* This is for DT_AUXILIARY. */ \
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))\
+ _dl_debug_printf (N_("\
+cannot load auxiliary `%s' because of empty dynamic string token " \
+ "substitution\n"), __str); \
+ continue; \
+ } \
+ } \
+ } \
+ \
+ __result; })
+
+
+void
+internal_function
+_dl_map_object_deps (struct link_map *map,
+ struct link_map **preloads, unsigned int npreloads,
+ int trace_mode, int open_mode)
+{
+ struct list *known = __alloca (sizeof *known * (1 + npreloads + 1));
+ struct list *runp, *tail;
+ unsigned int nlist, i;
+ /* Object name. */
+ const char *name;
+ int errno_saved;
+ int errno_reason;
+ const char *errstring;
+ const char *objname;
+
+ auto inline void preload (struct link_map *map);
+
+ inline void preload (struct link_map *map)
+ {
+ known[nlist].done = 0;
+ known[nlist].map = map;
+ known[nlist].next = &known[nlist + 1];
+
+ ++nlist;
+ /* We use `l_reserved' as a mark bit to detect objects we have
+ already put in the search list and avoid adding duplicate
+ elements later in the list. */
+ map->l_reserved = 1;
+ }
+
+ /* No loaded object so far. */
+ nlist = 0;
+
+ /* First load MAP itself. */
+ preload (map);
+
+ /* Add the preloaded items after MAP but before any of its dependencies. */
+ for (i = 0; i < npreloads; ++i)
+ preload (preloads[i]);
+
+ /* Terminate the lists. */
+ known[nlist - 1].next = NULL;
+
+ /* Pointer to last unique object. */
+ tail = &known[nlist - 1];
+
+ /* Process each element of the search list, loading each of its
+ auxiliary objects and immediate dependencies. Auxiliary objects
+ will be added in the list before the object itself and
+ dependencies will be appended to the list as we step through it.
+ This produces a flat, ordered list that represents a
+ breadth-first search of the dependency tree.
+
+ The whole process is complicated by the fact that we better
+ should use alloca for the temporary list elements. But using
+ alloca means we cannot use recursive function calls. */
+ errno_saved = errno;
+ errno_reason = 0;
+ errstring = NULL;
+ errno = 0;
+ name = NULL;
+ for (runp = known; runp; )
+ {
+ struct link_map *l = runp->map;
+ struct link_map **needed = NULL;
+ unsigned int nneeded = 0;
+
+ /* Unless otherwise stated, this object is handled. */
+ runp->done = 1;
+
+ /* Allocate a temporary record to contain the references to the
+ dependencies of this object. */
+ if (l->l_searchlist.r_list == NULL && l->l_initfini == NULL
+ && l != map && l->l_ldnum > 0)
+ needed = (struct link_map **) alloca (l->l_ldnum
+ * sizeof (struct link_map *));
+
+ if (l->l_info[DT_NEEDED] || l->l_info[AUXTAG] || l->l_info[FILTERTAG])
+ {
+ const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
+ struct openaux_args args;
+ struct list *orig;
+ const ElfW(Dyn) *d;
+
+ args.strtab = strtab;
+ args.map = l;
+ args.trace_mode = trace_mode;
+ args.open_mode = open_mode;
+ orig = runp;
+
+ for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
+ if (__builtin_expect (d->d_tag, DT_NEEDED) == DT_NEEDED)
+ {
+ /* Map in the needed object. */
+ struct link_map *dep;
+
+ /* Recognize DSTs. */
+ name = expand_dst (l, strtab + d->d_un.d_val, 0);
+ /* Store the tag in the argument structure. */
+ args.name = name;
+
+ bool malloced;
+ int err = _dl_catch_error (&objname, &errstring, &malloced,
+ openaux, &args);
+ if (__builtin_expect (errstring != NULL, 0))
+ {
+ char *new_errstring = strdupa (errstring);
+ objname = strdupa (objname);
+ if (malloced)
+ free ((char *) errstring);
+ errstring = new_errstring;
+
+ if (err)
+ errno_reason = err;
+ else
+ errno_reason = -1;
+ goto out;
+ }
+ else
+ dep = args.aux;
+
+ if (! dep->l_reserved)
+ {
+ /* Allocate new entry. */
+ struct list *newp;
+
+ newp = alloca (sizeof (struct list));
+
+ /* Append DEP to the list. */
+ newp->map = dep;
+ newp->done = 0;
+ newp->next = NULL;
+ tail->next = newp;
+ tail = newp;
+ ++nlist;
+ /* Set the mark bit that says it's already in the list. */
+ dep->l_reserved = 1;
+ }
+
+ /* Remember this dependency. */
+ if (needed != NULL)
+ needed[nneeded++] = dep;
+ }
+ else if (d->d_tag == DT_AUXILIARY || d->d_tag == DT_FILTER)
+ {
+ struct list *newp;
+
+ /* Recognize DSTs. */
+ name = expand_dst (l, strtab + d->d_un.d_val,
+ d->d_tag == DT_AUXILIARY);
+ /* Store the tag in the argument structure. */
+ args.name = name;
+
+ if (d->d_tag == DT_AUXILIARY)
+ {
+ /* Say that we are about to load an auxiliary library. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS,
+ 0))
+ _dl_debug_printf ("load auxiliary object=%s"
+ " requested by file=%s\n",
+ name,
+ l->l_name[0]
+ ? l->l_name : rtld_progname);
+
+ /* We must be prepared that the addressed shared
+ object is not available. */
+ bool malloced;
+ (void) _dl_catch_error (&objname, &errstring, &malloced,
+ openaux, &args);
+ if (__builtin_expect (errstring != NULL, 0))
+ {
+ /* We are not interested in the error message. */
+ assert (errstring != NULL);
+ if (malloced)
+ free ((char *) errstring);
+
+ /* Simply ignore this error and continue the work. */
+ continue;
+ }
+ }
+ else
+ {
+ /* Say that we are about to load an auxiliary library. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS,
+ 0))
+ _dl_debug_printf ("load filtered object=%s"
+ " requested by file=%s\n",
+ name,
+ l->l_name[0]
+ ? l->l_name : rtld_progname);
+
+ /* For filter objects the dependency must be available. */
+ bool malloced;
+ int err = _dl_catch_error (&objname, &errstring, &malloced,
+ openaux, &args);
+ if (__builtin_expect (errstring != NULL, 0))
+ {
+ char *new_errstring = strdupa (errstring);
+ objname = strdupa (objname);
+ if (malloced)
+ free ((char *) errstring);
+ errstring = new_errstring;
+
+ if (err)
+ errno_reason = err;
+ else
+ errno_reason = -1;
+ goto out;
+ }
+ }
+
+ /* The auxiliary object is actually available.
+ Incorporate the map in all the lists. */
+
+ /* Allocate new entry. This always has to be done. */
+ newp = alloca (sizeof (struct list));
+
+ /* We want to insert the new map before the current one,
+ but we have no back links. So we copy the contents of
+ the current entry over. Note that ORIG and NEWP now
+ have switched their meanings. */
+ memcpy (newp, orig, sizeof (*newp));
+
+ /* Initialize new entry. */
+ orig->done = 0;
+ orig->map = args.aux;
+
+ /* Remember this dependency. */
+ if (needed != NULL)
+ needed[nneeded++] = args.aux;
+
+ /* We must handle two situations here: the map is new,
+ so we must add it in all three lists. If the map
+ is already known, we have two further possibilities:
+ - if the object is before the current map in the
+ search list, we do nothing. It is already found
+ early
+ - if the object is after the current one, we must
+ move it just before the current map to make sure
+ the symbols are found early enough
+ */
+ if (args.aux->l_reserved)
+ {
+ /* The object is already somewhere in the list.
+ Locate it first. */
+ struct list *late;
+
+ /* This object is already in the search list we
+ are building. Don't add a duplicate pointer.
+ Just added by _dl_map_object. */
+ for (late = newp; late->next != NULL; late = late->next)
+ if (late->next->map == args.aux)
+ break;
+
+ if (late->next != NULL)
+ {
+ /* The object is somewhere behind the current
+ position in the search path. We have to
+ move it to this earlier position. */
+ orig->next = newp;
+
+ /* Now remove the later entry from the list
+ and adjust the tail pointer. */
+ if (tail == late->next)
+ tail = late;
+ late->next = late->next->next;
+
+ /* We must move the object earlier in the chain. */
+ if (args.aux->l_prev != NULL)
+ args.aux->l_prev->l_next = args.aux->l_next;
+ if (args.aux->l_next != NULL)
+ args.aux->l_next->l_prev = args.aux->l_prev;
+
+ args.aux->l_prev = newp->map->l_prev;
+ newp->map->l_prev = args.aux;
+ if (args.aux->l_prev != NULL)
+ args.aux->l_prev->l_next = args.aux;
+ args.aux->l_next = newp->map;
+ }
+ else
+ {
+ /* The object must be somewhere earlier in the
+ list. Undo to the current list element what
+ we did above. */
+ memcpy (orig, newp, sizeof (*newp));
+ continue;
+ }
+ }
+ else
+ {
+ /* This is easy. We just add the symbol right here. */
+ orig->next = newp;
+ ++nlist;
+ /* Set the mark bit that says it's already in the list. */
+ args.aux->l_reserved = 1;
+
+ /* The only problem is that in the double linked
+ list of all objects we don't have this new
+ object at the correct place. Correct this here. */
+ if (args.aux->l_prev)
+ args.aux->l_prev->l_next = args.aux->l_next;
+ if (args.aux->l_next)
+ args.aux->l_next->l_prev = args.aux->l_prev;
+
+ args.aux->l_prev = newp->map->l_prev;
+ newp->map->l_prev = args.aux;
+ if (args.aux->l_prev != NULL)
+ args.aux->l_prev->l_next = args.aux;
+ args.aux->l_next = newp->map;
+ }
+
+ /* Move the tail pointer if necessary. */
+ if (orig == tail)
+ tail = newp;
+
+ /* Move on the insert point. */
+ orig = newp;
+ }
+ }
+
+ /* Terminate the list of dependencies and store the array address. */
+ if (needed != NULL)
+ {
+ needed[nneeded++] = NULL;
+
+ l->l_initfini = (struct link_map **)
+ malloc ((2 * nneeded + 1) * sizeof needed[0]);
+ if (l->l_initfini == NULL)
+ _dl_signal_error (ENOMEM, map->l_name, NULL,
+ N_("cannot allocate dependency list"));
+ l->l_initfini[0] = l;
+ memcpy (&l->l_initfini[1], needed, nneeded * sizeof needed[0]);
+ memcpy (&l->l_initfini[nneeded + 1], l->l_initfini,
+ nneeded * sizeof needed[0]);
+ }
+
+ /* If we have no auxiliary objects just go on to the next map. */
+ if (runp->done)
+ do
+ runp = runp->next;
+ while (runp != NULL && runp->done);
+ }
+
+ out:
+ if (errno == 0 && errno_saved != 0)
+ __set_errno (errno_saved);
+
+ if (map->l_initfini != NULL && map->l_type == lt_loaded)
+ {
+ /* This object was previously loaded as a dependency and we have
+ a separate l_initfini list. We don't need it anymore. */
+ assert (map->l_searchlist.r_list == NULL);
+ free (map->l_initfini);
+ }
+
+ /* Store the search list we built in the object. It will be used for
+ searches in the scope of this object. */
+ map->l_initfini =
+ (struct link_map **) malloc ((2 * nlist + 1)
+ * sizeof (struct link_map *));
+ if (map->l_initfini == NULL)
+ _dl_signal_error (ENOMEM, map->l_name, NULL,
+ N_("cannot allocate symbol search list"));
+
+
+ map->l_searchlist.r_list = &map->l_initfini[nlist + 1];
+ map->l_searchlist.r_nlist = nlist;
+
+ for (nlist = 0, runp = known; runp; runp = runp->next)
+ {
+ if (__builtin_expect (trace_mode, 0) && runp->map->l_faked)
+ /* This can happen when we trace the loading. */
+ --map->l_searchlist.r_nlist;
+ else
+ map->l_searchlist.r_list[nlist++] = runp->map;
+
+ /* Now clear all the mark bits we set in the objects on the search list
+ to avoid duplicates, so the next call starts fresh. */
+ runp->map->l_reserved = 0;
+ }
+
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK, 0) != 0
+ && map == GL(dl_ns)[LM_ID_BASE]._ns_loaded)
+ {
+ /* If we are to compute conflicts, we have to build local scope
+ for each library, not just the ultimate loader. */
+ for (i = 0; i < nlist; ++i)
+ {
+ struct link_map *l = map->l_searchlist.r_list[i];
+ unsigned int j, cnt;
+
+ /* The local scope has been already computed. */
+ if (l == map
+ || (l->l_local_scope[0]
+ && l->l_local_scope[0]->r_nlist) != 0)
+ continue;
+
+ if (l->l_info[AUXTAG] || l->l_info[FILTERTAG])
+ {
+ /* As current DT_AUXILIARY/DT_FILTER implementation needs to be
+ rewritten, no need to bother with prelinking the old
+ implementation. */
+ _dl_signal_error (EINVAL, l->l_name, NULL, N_("\
+Filters not supported with LD_TRACE_PRELINKING"));
+ }
+
+ cnt = _dl_build_local_scope (map->l_initfini, l);
+ assert (cnt <= nlist);
+ for (j = 0; j < cnt; j++)
+ map->l_initfini[j]->l_reserved = 0;
+
+ l->l_local_scope[0] =
+ (struct r_scope_elem *) malloc (sizeof (struct r_scope_elem)
+ + (cnt
+ * sizeof (struct link_map *)));
+ if (l->l_local_scope[0] == NULL)
+ _dl_signal_error (ENOMEM, map->l_name, NULL,
+ N_("cannot allocate symbol search list"));
+ l->l_local_scope[0]->r_nlist = cnt;
+ l->l_local_scope[0]->r_list =
+ (struct link_map **) (l->l_local_scope[0] + 1);
+ memcpy (l->l_local_scope[0]->r_list, map->l_initfini,
+ cnt * sizeof (struct link_map *));
+ }
+ }
+
+ /* Maybe we can remove some relocation dependencies now. */
+ assert (map->l_searchlist.r_list[0] == map);
+ for (i = 0; i < map->l_reldepsact; ++i)
+ {
+ unsigned int j;
+
+ for (j = 1; j < nlist; ++j)
+ if (map->l_searchlist.r_list[j] == map->l_reldeps[i])
+ {
+ /* A direct or transitive dependency is also on the list
+ of relocation dependencies. Remove the latter. */
+ for (j = i + 1; j < map->l_reldepsact; ++j)
+ map->l_reldeps[j - 1] = map->l_reldeps[j];
+
+ --map->l_reldepsact;
+
+ /* Account for the '++i' performed by the 'for'. */
+ --i;
+ break;
+ }
+ }
+
+ /* Now determine the order in which the initialization has to happen. */
+ memcpy (map->l_initfini, map->l_searchlist.r_list,
+ nlist * sizeof (struct link_map *));
+ /* We can skip looking for the binary itself which is at the front
+ of the search list. Look through the list backward so that circular
+ dependencies are not changing the order. */
+ for (i = 1; i < nlist; ++i)
+ {
+ struct link_map *l = map->l_searchlist.r_list[i];
+ unsigned int j;
+ unsigned int k;
+
+ /* Find the place in the initfini list where the map is currently
+ located. */
+ for (j = 1; map->l_initfini[j] != l; ++j)
+ ;
+
+ /* Find all object for which the current one is a dependency and
+ move the found object (if necessary) in front. */
+ for (k = j + 1; k < nlist; ++k)
+ {
+ struct link_map **runp;
+
+ runp = map->l_initfini[k]->l_initfini;
+ if (runp != NULL)
+ {
+ while (*runp != NULL)
+ if (__builtin_expect (*runp++ == l, 0))
+ {
+ struct link_map *here = map->l_initfini[k];
+
+ /* Move it now. */
+ memmove (&map->l_initfini[j] + 1,
+ &map->l_initfini[j],
+ (k - j) * sizeof (struct link_map *));
+ map->l_initfini[j] = here;
+
+ /* Don't insert further matches before the last
+ entry moved to the front. */
+ ++j;
+
+ break;
+ }
+ }
+ }
+ }
+ /* Terminate the list of dependencies. */
+ map->l_initfini[nlist] = NULL;
+
+ if (errno_reason)
+ _dl_signal_error (errno_reason == -1 ? 0 : errno_reason, objname,
+ NULL, errstring);
+}
diff --git a/libc/elf/dl-dst.h b/libc/elf/dl-dst.h
new file mode 100644
index 000000000..83d16bdb0
--- /dev/null
+++ b/libc/elf/dl-dst.h
@@ -0,0 +1,85 @@
+/* Handling of dynamic sring tokens.
+ Copyright (C) 1999,2001,2002,2003,2004,2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/* Determine the number of DST elements in the name. Only if IS_PATH is
+ nonzero paths are recognized (i.e., multiple, ':' separated filenames). */
+#define DL_DST_COUNT(name, is_path) \
+ ({ \
+ size_t __cnt = 0; \
+ const char *__sf = strchr (name, '$'); \
+ \
+ if (__builtin_expect (__sf != NULL, 0)) \
+ __cnt = _dl_dst_count (__sf, is_path); \
+ \
+ __cnt; })
+#ifndef IS_IN_rtld
+# define _dl_dst_count GLRO(dl_dst_count)
+#endif
+
+
+/* Guess from the number of DSTs the length of the result string. */
+#define DL_DST_REQUIRED(l, name, len, cnt) \
+ ({ \
+ size_t __len = (len); \
+ size_t __cnt = (cnt); \
+ \
+ if (__cnt > 0) \
+ { \
+ size_t origin_len; \
+ /* Now we make a guess how many extra characters on top of the \
+ length of S we need to represent the result. We know that \
+ we have CNT replacements. Each at most can use \
+ MAX (strlen (ORIGIN), strlen (_dl_platform)) \
+ minus 7 (which is the length of "$ORIGIN"). \
+ \
+ First get the origin string if it is not available yet. \
+ This can only happen for the map of the executable. */ \
+ DL_DST_REQ_STATIC \
+ if ((l)->l_origin == NULL) \
+ { \
+ assert ((l)->l_name[0] == '\0'); \
+ (l)->l_origin = _dl_get_origin (); \
+ origin_len = ((l)->l_origin && (l)->l_origin != (char *) -1 \
+ ? strlen ((l)->l_origin) : 0); \
+ } \
+ else \
+ origin_len = (l)->l_origin == (char *) -1 \
+ ? 0 : strlen ((l)->l_origin); \
+ \
+ __len += __cnt * (MAX (origin_len, GLRO(dl_platformlen)) - 7); \
+ } \
+ \
+ __len; })
+
+#ifdef SHARED
+# define DL_DST_REQ_STATIC /* nothing */
+#else
+# define DL_DST_REQ_STATIC \
+ if ((l) == NULL) \
+ { \
+ const char *origin = _dl_get_origin (); \
+ origin_len = (origin && origin != (char *) -1 ? strlen (origin) : 0); \
+ } \
+ else
+#endif
+
+#ifndef IS_IN_rtld
+# define _dl_get_origin GLRO(dl_get_origin)
+# define _dl_dst_substitute GLRO(dl_dst_substitute)
+#endif
diff --git a/libc/elf/dl-environ.c b/libc/elf/dl-environ.c
new file mode 100644
index 000000000..089e89e6e
--- /dev/null
+++ b/libc/elf/dl-environ.c
@@ -0,0 +1,86 @@
+/* Environment handling for dynamic loader.
+ Copyright (C) 1995-1998, 2000, 2001, 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+
+/* Walk through the environment of the process and return all entries
+ starting with `LD_'. */
+char *
+internal_function
+_dl_next_ld_env_entry (char ***position)
+{
+ char **current = *position;
+ char *result = NULL;
+
+ while (*current != NULL)
+ {
+ if (__builtin_expect ((*current)[0] == 'L', 0)
+ && (*current)[1] == 'D' && (*current)[2] == '_')
+ {
+ result = &(*current)[3];
+
+ /* Save current position for next visit. */
+ *position = ++current;
+
+ break;
+ }
+
+ ++current;
+ }
+
+ return result;
+}
+
+
+/* In ld.so __environ is not exported. */
+extern char **__environ attribute_hidden;
+
+int
+unsetenv (const char *name)
+{
+ char **ep;
+
+ ep = __environ;
+ while (*ep != NULL)
+ {
+ size_t cnt = 0;
+
+ while ((*ep)[cnt] == name[cnt] && name[cnt] != '\0')
+ ++cnt;
+
+ if (name[cnt] == '\0' && (*ep)[cnt] == '=')
+ {
+ /* Found it. Remove this pointer by moving later ones to
+ the front. */
+ char **dp = ep;
+
+ do
+ dp[0] = dp[1];
+ while (*dp++);
+ /* Continue the loop in case NAME appears again. */
+ }
+ else
+ ++ep;
+ }
+
+ return 0;
+}
diff --git a/libc/elf/dl-error.c b/libc/elf/dl-error.c
new file mode 100644
index 000000000..79ebaaf01
--- /dev/null
+++ b/libc/elf/dl-error.c
@@ -0,0 +1,214 @@
+/* Error handling for runtime dynamic linker.
+ Copyright (C) 1995-2002,2004,2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <libintl.h>
+#include <setjmp.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+
+/* This structure communicates state between _dl_catch_error and
+ _dl_signal_error. */
+struct catch
+ {
+ const char *objname; /* Object/File name. */
+ const char *errstring; /* Error detail filled in here. */
+ bool malloced; /* Nonzero if the string is malloced
+ by the libc malloc. */
+ jmp_buf env; /* longjmp here on error. */
+ };
+
+/* Multiple threads at once can use the `_dl_catch_error' function. The
+ calls can come from `_dl_map_object_deps', `_dlerror_run', or from
+ any of the libc functionality which loads dynamic objects (NSS, iconv).
+ Therefore we have to be prepared to save the state in thread-local
+ memory. The _dl_error_catch_tsd function pointer is reset by the thread
+ library so that it returns the address of a thread-local variable. */
+
+
+/* This message we return as a last resort. We define the string in a
+ variable since we have to avoid freeing it and so have to enable
+ a pointer comparison. See below and in dlfcn/dlerror.c. */
+static const char _dl_out_of_memory[] = "out of memory";
+
+
+/* This points to a function which is called when an continuable error is
+ received. Unlike the handling of `catch' this function may return.
+ The arguments will be the `errstring' and `objname'.
+
+ Since this functionality is not used in normal programs (only in ld.so)
+ we do not care about multi-threaded programs here. We keep this as a
+ global variable. */
+static receiver_fct receiver;
+
+#ifdef _LIBC_REENTRANT
+# define CATCH_HOOK (*(struct catch **) (*GL(dl_error_catch_tsd)) ())
+#else
+static struct catch *catch_hook;
+# define CATCH_HOOK catch_hook
+#endif
+
+void
+internal_function
+_dl_signal_error (int errcode, const char *objname, const char *occation,
+ const char *errstring)
+{
+ struct catch *lcatch;
+
+ if (! errstring)
+ errstring = N_("DYNAMIC LINKER BUG!!!");
+
+ lcatch = CATCH_HOOK;
+ if (objname == NULL)
+ objname = "";
+ if (lcatch != NULL)
+ {
+ /* We are inside _dl_catch_error. Return to it. We have to
+ duplicate the error string since it might be allocated on the
+ stack. The object name is always a string constant. */
+ size_t len_objname = strlen (objname) + 1;
+ size_t len_errstring = strlen (errstring) + 1;
+
+ lcatch->errstring = (char *) malloc (len_objname + len_errstring);
+ if (lcatch->errstring != NULL)
+ {
+ /* Make a copy of the object file name and the error string. */
+ lcatch->objname = memcpy (__mempcpy ((char *) lcatch->errstring,
+ errstring, len_errstring),
+ objname, len_objname);
+
+ /* If the main executable is relocated it means the libc's malloc
+ is used. */
+#ifdef SHARED
+ lcatch->malloced = (GL(dl_ns)[LM_ID_BASE]._ns_loaded != NULL
+ && (GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_relocated
+ != 0));
+#else
+ lcatch->malloced = true;
+#endif
+ }
+ else
+ {
+ /* This is better than nothing. */
+ lcatch->objname = "";
+ lcatch->errstring = _dl_out_of_memory;
+ lcatch->malloced = false;
+ }
+ /* We do not restore the signal mask because none was saved. */
+ __longjmp (lcatch->env[0].__jmpbuf, errcode ?: -1);
+ }
+ else
+ {
+ /* Lossage while resolving the program's own symbols is always fatal. */
+ char buffer[1024];
+ _dl_fatal_printf ("%s: %s: %s%s%s%s%s\n",
+ rtld_progname ?: "<program name unknown>",
+ occation ?: N_("error while loading shared libraries"),
+ objname, *objname ? ": " : "",
+ errstring, errcode ? ": " : "",
+ (errcode
+ ? __strerror_r (errcode, buffer, sizeof buffer)
+ : ""));
+ }
+}
+
+
+void
+internal_function
+_dl_signal_cerror (int errcode, const char *objname, const char *occation,
+ const char *errstring)
+{
+ if (__builtin_expect (GLRO(dl_debug_mask)
+ & ~(DL_DEBUG_STATISTICS|DL_DEBUG_PRELINK), 0))
+ _dl_debug_printf ("%s: error: %s: %s (%s)\n", objname, occation,
+ errstring, receiver ? "continued" : "fatal");
+
+ if (receiver)
+ {
+ /* We are inside _dl_receive_error. Call the user supplied
+ handler and resume the work. The receiver will still be
+ installed. */
+ (*receiver) (errcode, objname, errstring);
+ }
+ else
+ _dl_signal_error (errcode, objname, occation, errstring);
+}
+
+
+int
+internal_function
+_dl_catch_error (const char **objname, const char **errstring,
+ bool *mallocedp, void (*operate) (void *), void *args)
+{
+ int errcode;
+ struct catch *volatile old;
+ struct catch c;
+ /* We need not handle `receiver' since setting a `catch' is handled
+ before it. */
+
+ /* Some systems (e.g., SPARC) handle constructors to local variables
+ inefficient. So we initialize `c' by hand. */
+ c.errstring = NULL;
+
+ struct catch **const catchp = &CATCH_HOOK;
+ old = *catchp;
+ /* Do not save the signal mask. */
+ errcode = __sigsetjmp (c.env, 0);
+ if (__builtin_expect (errcode, 0) == 0)
+ {
+ *catchp = &c;
+ (*operate) (args);
+ *catchp = old;
+ *objname = NULL;
+ *errstring = NULL;
+ *mallocedp = false;
+ return 0;
+ }
+
+ /* We get here only if we longjmp'd out of OPERATE. */
+ *catchp = old;
+ *objname = c.objname;
+ *errstring = c.errstring;
+ *mallocedp = c.malloced;
+ return errcode == -1 ? 0 : errcode;
+}
+
+
+void
+internal_function
+_dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args)
+{
+ struct catch **const catchp = &CATCH_HOOK;
+ struct catch *old_catch;
+ receiver_fct old_receiver;
+
+ old_catch = *catchp;
+ old_receiver = receiver;
+
+ /* Set the new values. */
+ *catchp = NULL;
+ receiver = fct;
+
+ (*operate) (args);
+
+ *catchp = old_catch;
+ receiver = old_receiver;
+}
diff --git a/libc/elf/dl-execstack.c b/libc/elf/dl-execstack.c
new file mode 100644
index 000000000..6dce21e7a
--- /dev/null
+++ b/libc/elf/dl-execstack.c
@@ -0,0 +1,32 @@
+/* Stack executability handling for GNU dynamic linker. Stub version.
+ Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <ldsodefs.h>
+#include <errno.h>
+
+/* There is no portable way to know the bounds of the initial thread's stack
+ so as to mprotect it. */
+
+int
+internal_function
+_dl_make_stack_executable (void **stack_endp)
+{
+ return ENOSYS;
+}
+rtld_hidden_def (_dl_make_stack_executable)
diff --git a/libc/elf/dl-fini.c b/libc/elf/dl-fini.c
new file mode 100644
index 000000000..3cd7e7bbf
--- /dev/null
+++ b/libc/elf/dl-fini.c
@@ -0,0 +1,287 @@
+/* Call the termination functions of loaded shared objects.
+ Copyright (C) 1995,96,1998-2002,2004, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <alloca.h>
+#include <assert.h>
+#include <string.h>
+#include <ldsodefs.h>
+
+
+/* Type of the constructor functions. */
+typedef void (*fini_t) (void);
+
+
+void
+internal_function
+_dl_sort_fini (struct link_map *l, struct link_map **maps, size_t nmaps,
+ char *used, Lmid_t ns)
+{
+ if (ns == LM_ID_BASE)
+ /* The main executable always comes first. */
+ l = l->l_next;
+
+ for (; l != NULL; l = l->l_next)
+ /* Do not handle ld.so in secondary namespaces and object which
+ are not removed. */
+ if (l == l->l_real && l->l_idx != -1)
+ {
+ /* Find the place in the 'maps' array. */
+ unsigned int j;
+ for (j = ns == LM_ID_BASE ? 1 : 0; maps[j] != l; ++j)
+ assert (j < nmaps);
+
+ /* Find all object for which the current one is a dependency
+ and move the found object (if necessary) in front. */
+ for (unsigned int k = j + 1; k < nmaps; ++k)
+ {
+ struct link_map **runp = maps[k]->l_initfini;
+ if (runp != NULL)
+ {
+ while (*runp != NULL)
+ if (*runp == l)
+ {
+ struct link_map *here = maps[k];
+
+ /* Move it now. */
+ memmove (&maps[j] + 1,
+ &maps[j], (k - j) * sizeof (struct link_map *));
+ maps[j] = here;
+
+ if (used != NULL)
+ {
+ char here_used = used[k];
+
+ memmove (&used[j] + 1,
+ &used[j], (k - j) * sizeof (char));
+ used[j] = here_used;
+ }
+
+ ++j;
+
+ break;
+ }
+ else
+ ++runp;
+ }
+
+ if (__builtin_expect (maps[k]->l_reldeps != NULL, 0))
+ {
+ unsigned int m = maps[k]->l_reldepsact;
+ struct link_map **relmaps = maps[k]->l_reldeps;
+
+ while (m-- > 0)
+ {
+ if (relmaps[m] == l)
+ {
+ struct link_map *here = maps[k];
+
+ /* Move it now. */
+ memmove (&maps[j] + 1,
+ &maps[j],
+ (k - j) * sizeof (struct link_map *));
+ maps[j] = here;
+
+ if (used != NULL)
+ {
+ char here_used = used[k];
+
+ memmove (&used[j] + 1,
+ &used[j], (k - j) * sizeof (char));
+ used[j] = here_used;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void
+internal_function
+_dl_fini (void)
+{
+ /* Lots of fun ahead. We have to call the destructors for all still
+ loaded objects, in all namespaces. The problem is that the ELF
+ specification now demands that dependencies between the modules
+ are taken into account. I.e., the destructor for a module is
+ called before the ones for any of its dependencies.
+
+ To make things more complicated, we cannot simply use the reverse
+ order of the constructors. Since the user might have loaded objects
+ using `dlopen' there are possibly several other modules with its
+ dependencies to be taken into account. Therefore we have to start
+ determining the order of the modules once again from the beginning. */
+ struct link_map **maps = NULL;
+ size_t maps_size = 0;
+
+ /* We run the destructors of the main namespaces last. As for the
+ other namespaces, we pick run the destructors in them in reverse
+ order of the namespace ID. */
+#ifdef SHARED
+ int do_audit = 0;
+ again:
+#endif
+ for (Lmid_t ns = DL_NNS - 1; ns >= 0; --ns)
+ {
+ /* Protect against concurrent loads and unloads. */
+ __rtld_lock_lock_recursive (GL(dl_load_lock));
+
+ unsigned int nmaps = 0;
+ unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
+ /* No need to do anything for empty namespaces or those used for
+ auditing DSOs. */
+ if (nloaded == 0
+#ifdef SHARED
+ || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
+#endif
+ )
+ goto out;
+
+ /* XXX Could it be (in static binaries) that there is no object
+ loaded? */
+ assert (ns != LM_ID_BASE || nloaded > 0);
+
+ /* Now we can allocate an array to hold all the pointers and copy
+ the pointers in. */
+ if (maps_size < nloaded * sizeof (struct link_map *))
+ {
+ if (maps_size == 0)
+ {
+ maps_size = nloaded * sizeof (struct link_map *);
+ maps = (struct link_map **) alloca (maps_size);
+ }
+ else
+ maps = (struct link_map **)
+ extend_alloca (maps, maps_size,
+ nloaded * sizeof (struct link_map *));
+ }
+
+ unsigned int i;
+ struct link_map *l;
+ assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
+ for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
+ /* Do not handle ld.so in secondary namespaces. */
+ if (l == l->l_real)
+ {
+ assert (i < nloaded);
+
+ maps[i] = l;
+ l->l_idx = i;
+ ++i;
+
+ /* Bump l_direct_opencount of all objects so that they are
+ not dlclose()ed from underneath us. */
+ ++l->l_direct_opencount;
+ }
+ assert (ns != LM_ID_BASE || i == nloaded);
+ assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
+ nmaps = i;
+
+ if (nmaps != 0)
+ /* Now we have to do the sorting. */
+ _dl_sort_fini (GL(dl_ns)[ns]._ns_loaded, maps, nmaps, NULL, ns);
+
+ /* We do not rely on the linked list of loaded object anymore from
+ this point on. We have our own list here (maps). The various
+ members of this list cannot vanish since the open count is too
+ high and will be decremented in this loop. So we release the
+ lock so that some code which might be called from a destructor
+ can directly or indirectly access the lock. */
+ out:
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
+
+ /* 'maps' now contains the objects in the right order. Now call the
+ destructors. We have to process this array from the front. */
+ for (i = 0; i < nmaps; ++i)
+ {
+ l = maps[i];
+
+ if (l->l_init_called)
+ {
+ /* Make sure nothing happens if we are called twice. */
+ l->l_init_called = 0;
+
+ /* Is there a destructor function? */
+ if (l->l_info[DT_FINI_ARRAY] != NULL
+ || l->l_info[DT_FINI] != NULL)
+ {
+ /* When debugging print a message first. */
+ if (__builtin_expect (GLRO(dl_debug_mask)
+ & DL_DEBUG_IMPCALLS, 0))
+ _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
+ l->l_name[0] ? l->l_name : rtld_progname,
+ ns);
+
+ /* First see whether an array is given. */
+ if (l->l_info[DT_FINI_ARRAY] != NULL)
+ {
+ ElfW(Addr) *array =
+ (ElfW(Addr) *) (l->l_addr
+ + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
+ unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
+ / sizeof (ElfW(Addr)));
+ while (i-- > 0)
+ ((fini_t) array[i]) ();
+ }
+
+ /* Next try the old-style destructor. */
+ if (l->l_info[DT_FINI] != NULL)
+ ((fini_t) DL_DT_FINI_ADDRESS (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr)) ();
+ }
+
+#ifdef SHARED
+ /* Auditing checkpoint: another object closed. */
+ if (!do_audit && __builtin_expect (GLRO(dl_naudit) > 0, 0))
+ {
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->objclose != NULL)
+ /* Return value is ignored. */
+ (void) afct->objclose (&l->l_audit[cnt].cookie);
+
+ afct = afct->next;
+ }
+ }
+#endif
+ }
+
+ /* Correct the previous increment. */
+ --l->l_direct_opencount;
+ }
+ }
+
+#ifdef SHARED
+ if (! do_audit && GLRO(dl_naudit) > 0)
+ {
+ do_audit = 1;
+ goto again;
+ }
+
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS, 0))
+ _dl_debug_printf ("\nruntime linker statistics:\n"
+ " final number of relocations: %lu\n"
+ "final number of relocations from cache: %lu\n",
+ GL(dl_num_relocations),
+ GL(dl_num_cache_relocations));
+#endif
+}
diff --git a/libc/elf/dl-fptr.c b/libc/elf/dl-fptr.c
new file mode 100644
index 000000000..78beecfdc
--- /dev/null
+++ b/libc/elf/dl-fptr.c
@@ -0,0 +1,323 @@
+/* Manage function descriptors. Generic version.
+ Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <libintl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <link.h>
+#include <ldsodefs.h>
+#include <elf/dynamic-link.h>
+#include <dl-fptr.h>
+#include <atomic.h>
+
+#ifndef ELF_MACHINE_BOOT_FPTR_TABLE_LEN
+/* ELF_MACHINE_BOOT_FPTR_TABLE_LEN should be greater than the number of
+ dynamic symbols in ld.so. */
+# define ELF_MACHINE_BOOT_FPTR_TABLE_LEN 256
+#endif
+
+#ifndef ELF_MACHINE_LOAD_ADDRESS
+# error "ELF_MACHINE_LOAD_ADDRESS is not defined."
+#endif
+
+#ifndef COMPARE_AND_SWAP
+# define COMPARE_AND_SWAP(ptr, old, new) \
+ (atomic_compare_and_exchange_bool_acq (ptr, new, old) == 0)
+#endif
+
+ElfW(Addr) _dl_boot_fptr_table [ELF_MACHINE_BOOT_FPTR_TABLE_LEN];
+
+static struct local
+ {
+ struct fdesc_table *root;
+ struct fdesc *free_list;
+ unsigned int npages; /* # of pages to allocate */
+ /* the next to members MUST be consecutive! */
+ struct fdesc_table boot_table;
+ struct fdesc boot_fdescs[1024];
+ }
+local =
+ {
+ .root = &local.boot_table,
+ .npages = 2,
+ .boot_table =
+ {
+ .len = sizeof (local.boot_fdescs) / sizeof (local.boot_fdescs[0]),
+ .first_unused = 0
+ }
+ };
+
+/* Create a new fdesc table and return a pointer to the first fdesc
+ entry. The fdesc lock must have been acquired already. */
+
+static struct fdesc_table *
+new_fdesc_table (struct local *l, size_t *size)
+{
+ size_t old_npages = l->npages;
+ size_t new_npages = old_npages + old_npages;
+ struct fdesc_table *new_table;
+
+ /* If someone has just created a new table, we return NULL to tell
+ the caller to use the new table. */
+ if (! COMPARE_AND_SWAP (&l->npages, old_npages, new_npages))
+ return (struct fdesc_table *) NULL;
+
+ *size = old_npages * GLRO(dl_pagesize);
+ new_table = __mmap (NULL, *size,
+ PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (new_table == MAP_FAILED)
+ _dl_signal_error (errno, NULL, NULL,
+ N_("cannot map pages for fdesc table"));
+
+ new_table->len
+ = (*size - sizeof (*new_table)) / sizeof (struct fdesc);
+ new_table->first_unused = 1;
+ return new_table;
+}
+
+
+static ElfW(Addr)
+make_fdesc (ElfW(Addr) ip, ElfW(Addr) gp)
+{
+ struct fdesc *fdesc = NULL;
+ struct fdesc_table *root;
+ unsigned int old;
+ struct local *l;
+
+ ELF_MACHINE_LOAD_ADDRESS (l, local);
+
+ retry:
+ root = l->root;
+ while (1)
+ {
+ old = root->first_unused;
+ if (old >= root->len)
+ break;
+ else if (COMPARE_AND_SWAP (&root->first_unused, old, old + 1))
+ {
+ fdesc = &root->fdesc[old];
+ goto install;
+ }
+ }
+
+ if (l->free_list)
+ {
+ /* Get it from free-list. */
+ do
+ {
+ fdesc = l->free_list;
+ if (fdesc == NULL)
+ goto retry;
+ }
+ while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->free_list,
+ (ElfW(Addr)) fdesc, fdesc->ip));
+ }
+ else
+ {
+ /* Create a new fdesc table. */
+ size_t size;
+ struct fdesc_table *new_table = new_fdesc_table (l, &size);
+
+ if (new_table == NULL)
+ goto retry;
+
+ new_table->next = root;
+ if (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->root,
+ (ElfW(Addr)) root,
+ (ElfW(Addr)) new_table))
+ {
+ /* Someone has just installed a new table. Return NULL to
+ tell the caller to use the new table. */
+ __munmap (new_table, size);
+ goto retry;
+ }
+
+ /* Note that the first entry was reserved while allocating the
+ memory for the new page. */
+ fdesc = &new_table->fdesc[0];
+ }
+
+ install:
+ fdesc->ip = ip;
+ fdesc->gp = gp;
+
+ return (ElfW(Addr)) fdesc;
+}
+
+
+static inline ElfW(Addr) * __attribute__ ((always_inline))
+make_fptr_table (struct link_map *map)
+{
+ const ElfW(Sym) *symtab
+ = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
+ const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+ ElfW(Addr) *fptr_table;
+ size_t size;
+ size_t len;
+
+ /* XXX Apparently the only way to find out the size of the dynamic
+ symbol section is to assume that the string table follows right
+ afterwards... */
+ len = ((strtab - (char *) symtab)
+ / map->l_info[DT_SYMENT]->d_un.d_val);
+ size = ((len * sizeof (fptr_table[0]) + GLRO(dl_pagesize) - 1)
+ & -GLRO(dl_pagesize));
+ /* XXX We don't support here in the moment systems without MAP_ANON.
+ There probably are none for IA-64. In case this is proven wrong
+ we will have to open /dev/null here and use the file descriptor
+ instead of the hard-coded -1. */
+ fptr_table = __mmap (NULL, size,
+ PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
+ -1, 0);
+ if (fptr_table == MAP_FAILED)
+ _dl_signal_error (errno, NULL, NULL,
+ N_("cannot map pages for fptr table"));
+
+ if (COMPARE_AND_SWAP ((ElfW(Addr) *) &map->l_mach.fptr_table,
+ (ElfW(Addr)) NULL, (ElfW(Addr)) fptr_table))
+ map->l_mach.fptr_table_len = len;
+ else
+ __munmap (fptr_table, len * sizeof (fptr_table[0]));
+
+ return map->l_mach.fptr_table;
+}
+
+
+ElfW(Addr)
+_dl_make_fptr (struct link_map *map, const ElfW(Sym) *sym,
+ ElfW(Addr) ip)
+{
+ ElfW(Addr) *ftab = map->l_mach.fptr_table;
+ const ElfW(Sym) *symtab;
+ Elf_Symndx symidx;
+ struct local *l;
+
+ if (__builtin_expect (ftab == NULL, 0))
+ ftab = make_fptr_table (map);
+
+ symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
+ symidx = sym - symtab;
+
+ if (symidx >= map->l_mach.fptr_table_len)
+ _dl_signal_error (0, NULL, NULL,
+ N_("internal error: symidx out of range of fptr table"));
+
+ while (ftab[symidx] == 0)
+ {
+ /* GOT has already been relocated in elf_get_dynamic_info -
+ don't try to relocate it again. */
+ ElfW(Addr) fdesc
+ = make_fdesc (ip, map->l_info[DT_PLTGOT]->d_un.d_ptr);
+
+ if (__builtin_expect (COMPARE_AND_SWAP (&ftab[symidx], (ElfW(Addr)) NULL,
+ fdesc), 1))
+ {
+ /* Noone has updated the entry and the new function
+ descriptor has been installed. */
+#if 0
+ const char *strtab
+ = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+
+ ELF_MACHINE_LOAD_ADDRESS (l, local);
+ if (l->root != &l->boot_table
+ || l->boot_table.first_unused > 20)
+ _dl_debug_printf ("created fdesc symbol `%s' at %lx\n",
+ strtab + sym->st_name, ftab[symidx]);
+#endif
+ break;
+ }
+ else
+ {
+ /* We created a duplicated function descriptor. We put it on
+ free-list. */
+ struct fdesc *f = (struct fdesc *) fdesc;
+
+ ELF_MACHINE_LOAD_ADDRESS (l, local);
+
+ do
+ f->ip = (ElfW(Addr)) l->free_list;
+ while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->free_list,
+ f->ip, fdesc));
+ }
+ }
+
+ return ftab[symidx];
+}
+
+
+void
+_dl_unmap (struct link_map *map)
+{
+ ElfW(Addr) *ftab = map->l_mach.fptr_table;
+ struct fdesc *head = NULL, *tail = NULL;
+ size_t i;
+
+ __munmap ((void *) map->l_map_start,
+ map->l_map_end - map->l_map_start);
+
+ if (ftab == NULL)
+ return;
+
+ /* String together the fdesc structures that are being freed. */
+ for (i = 0; i < map->l_mach.fptr_table_len; ++i)
+ {
+ if (ftab[i])
+ {
+ *(struct fdesc **) ftab[i] = head;
+ head = (struct fdesc *) ftab[i];
+ if (tail == NULL)
+ tail = head;
+ }
+ }
+
+ /* Prepend the new list to the free_list: */
+ if (tail)
+ do
+ tail->ip = (ElfW(Addr)) local.free_list;
+ while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &local.free_list,
+ tail->ip, (ElfW(Addr)) head));
+
+ __munmap (ftab, (map->l_mach.fptr_table_len
+ * sizeof (map->l_mach.fptr_table[0])));
+
+ map->l_mach.fptr_table = NULL;
+}
+
+
+ElfW(Addr)
+_dl_lookup_address (const void *address)
+{
+ ElfW(Addr) addr = (ElfW(Addr)) address;
+ struct fdesc_table *t;
+ unsigned long int i;
+
+ for (t = local.root; t != NULL; t = t->next)
+ {
+ i = (struct fdesc *) addr - &t->fdesc[0];
+ if (i < t->first_unused && addr == (ElfW(Addr)) &t->fdesc[i])
+ {
+ addr = t->fdesc[i].ip;
+ break;
+ }
+ }
+
+ return addr;
+}
diff --git a/libc/elf/dl-init.c b/libc/elf/dl-init.c
new file mode 100644
index 000000000..e7b67570f
--- /dev/null
+++ b/libc/elf/dl-init.c
@@ -0,0 +1,141 @@
+/* Return the next shared object initializer function not yet run.
+ Copyright (C) 1995, 1996, 1998-2002, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <stddef.h>
+#include <ldsodefs.h>
+
+
+/* Type of the initializer. */
+typedef void (*init_t) (int, char **, char **);
+
+#ifndef HAVE_INLINED_SYSCALLS
+/* Flag, nonzero during startup phase. */
+extern int _dl_starting_up;
+extern int _dl_starting_up_internal attribute_hidden;
+#endif
+
+
+static void
+call_init (struct link_map *l, int argc, char **argv, char **env)
+{
+ if (l->l_init_called)
+ /* This object is all done. */
+ return;
+
+ /* Avoid handling this constructor again in case we have a circular
+ dependency. */
+ l->l_init_called = 1;
+
+ /* Check for object which constructors we do not run here. */
+ if (__builtin_expect (l->l_name[0], 'a') == '\0'
+ && l->l_type == lt_executable)
+ return;
+
+ /* Are there any constructors? */
+ if (l->l_info[DT_INIT] == NULL
+ && __builtin_expect (l->l_info[DT_INIT_ARRAY] == NULL, 1))
+ return;
+
+ /* Print a debug message if wanted. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
+ _dl_debug_printf ("\ncalling init: %s\n\n",
+ l->l_name[0] ? l->l_name : rtld_progname);
+
+ /* Now run the local constructors. There are two forms of them:
+ - the one named by DT_INIT
+ - the others in the DT_INIT_ARRAY.
+ */
+ if (l->l_info[DT_INIT] != NULL)
+ {
+ init_t init = (init_t) DL_DT_INIT_ADDRESS
+ (l, l->l_addr + l->l_info[DT_INIT]->d_un.d_ptr);
+
+ /* Call the function. */
+ init (argc, argv, env);
+ }
+
+ /* Next see whether there is an array with initialization functions. */
+ ElfW(Dyn) *init_array = l->l_info[DT_INIT_ARRAY];
+ if (init_array != NULL)
+ {
+ unsigned int j;
+ unsigned int jm;
+ ElfW(Addr) *addrs;
+
+ jm = l->l_info[DT_INIT_ARRAYSZ]->d_un.d_val / sizeof (ElfW(Addr));
+
+ addrs = (ElfW(Addr) *) (init_array->d_un.d_ptr + l->l_addr);
+ for (j = 0; j < jm; ++j)
+ ((init_t) addrs[j]) (argc, argv, env);
+ }
+}
+
+
+void
+internal_function
+_dl_init (struct link_map *main_map, int argc, char **argv, char **env)
+{
+ ElfW(Dyn) *preinit_array = main_map->l_info[DT_PREINIT_ARRAY];
+ ElfW(Dyn) *preinit_array_size = main_map->l_info[DT_PREINIT_ARRAYSZ];
+ unsigned int i;
+
+ if (__builtin_expect (GL(dl_initfirst) != NULL, 0))
+ {
+ call_init (GL(dl_initfirst), argc, argv, env);
+ GL(dl_initfirst) = NULL;
+ }
+
+ /* Don't do anything if there is no preinit array. */
+ if (__builtin_expect (preinit_array != NULL, 0)
+ && preinit_array_size != NULL
+ && (i = preinit_array_size->d_un.d_val / sizeof (ElfW(Addr))) > 0)
+ {
+ ElfW(Addr) *addrs;
+ unsigned int cnt;
+
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
+ _dl_debug_printf ("\ncalling preinit: %s\n\n",
+ main_map->l_name[0]
+ ? main_map->l_name : rtld_progname);
+
+ addrs = (ElfW(Addr) *) (preinit_array->d_un.d_ptr + main_map->l_addr);
+ for (cnt = 0; cnt < i; ++cnt)
+ ((init_t) addrs[cnt]) (argc, argv, env);
+ }
+
+ /* Stupid users forced the ELF specification to be changed. It now
+ says that the dynamic loader is responsible for determining the
+ order in which the constructors have to run. The constructors
+ for all dependencies of an object must run before the constructor
+ for the object itself. Circular dependencies are left unspecified.
+
+ This is highly questionable since it puts the burden on the dynamic
+ loader which has to find the dependencies at runtime instead of
+ letting the user do it right. Stupidity rules! */
+
+ i = main_map->l_searchlist.r_nlist;
+ while (i-- > 0)
+ call_init (main_map->l_initfini[i], argc, argv, env);
+
+#ifndef HAVE_INLINED_SYSCALLS
+ /* Finished starting up. */
+ INTUSE(_dl_starting_up) = 0;
+#endif
+}
+INTDEF (_dl_init)
diff --git a/libc/elf/dl-iteratephdr.c b/libc/elf/dl-iteratephdr.c
new file mode 100644
index 000000000..52a114421
--- /dev/null
+++ b/libc/elf/dl-iteratephdr.c
@@ -0,0 +1,125 @@
+/* Get loaded objects program headers.
+ Copyright (C) 2001,2002,2003,2004,2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Jakub Jelinek <jakub@redhat.com>, 2001.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <errno.h>
+#include <ldsodefs.h>
+#include <stddef.h>
+#include <bits/libc-lock.h>
+
+static void
+cancel_handler (void *arg __attribute__((unused)))
+{
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
+}
+
+hidden_proto (__dl_iterate_phdr)
+int
+__dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info,
+ size_t size, void *data), void *data)
+{
+ struct link_map *l;
+ struct dl_phdr_info info;
+ int ret = 0;
+
+ /* Make sure we are alone. */
+ __rtld_lock_lock_recursive (GL(dl_load_lock));
+ __libc_cleanup_push (cancel_handler, 0);
+
+ /* We have to determine the namespace of the caller since this determines
+ which namespace is reported. */
+ const void *caller = RETURN_ADDRESS (0);
+ size_t nloaded = GL(dl_ns)[0]._ns_nloaded;
+ Lmid_t ns = 0;
+ for (Lmid_t cnt = DL_NNS - 1; cnt > 0; --cnt)
+ for (struct link_map *l = GL(dl_ns)[cnt]._ns_loaded; l; l = l->l_next)
+ {
+ /* We have to count the total number of loaded objects. */
+ nloaded += GL(dl_ns)[cnt]._ns_nloaded;
+
+ if (caller >= (const void *) l->l_map_start
+ && caller < (const void *) l->l_map_end)
+ /* There must be exactly one DSO for the range of the virtual
+ memory. Otherwise something is really broken. */
+ ns = cnt;
+ }
+
+ for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next)
+ {
+ info.dlpi_addr = l->l_addr;
+ info.dlpi_name = l->l_name;
+ info.dlpi_phdr = l->l_phdr;
+ info.dlpi_phnum = l->l_phnum;
+ info.dlpi_adds = GL(dl_load_adds);
+ info.dlpi_subs = GL(dl_load_adds) - nloaded;
+ info.dlpi_tls_modid = 0;
+ info.dlpi_tls_data = NULL;
+#ifdef USE_TLS
+ info.dlpi_tls_modid = l->l_tls_modid;
+ if (info.dlpi_tls_modid != 0)
+ info.dlpi_tls_data = _dl_tls_get_addr_soft (l);
+#endif
+ ret = callback (&info, sizeof (struct dl_phdr_info), data);
+ if (ret)
+ break;
+ }
+
+ /* Release the lock. */
+ __libc_cleanup_pop (0);
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
+
+ return ret;
+}
+hidden_def (__dl_iterate_phdr)
+
+#ifdef SHARED
+
+weak_alias (__dl_iterate_phdr, dl_iterate_phdr);
+
+#else
+
+/* dl-support.c defines these and initializes them early on. */
+extern ElfW(Phdr) *_dl_phdr;
+extern size_t _dl_phnum;
+
+int
+dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info,
+ size_t size, void *data), void *data)
+{
+ if (_dl_phnum != 0)
+ {
+ /* This entry describes this statically-linked program itself. */
+ struct dl_phdr_info info;
+ int ret;
+ info.dlpi_addr = 0;
+ info.dlpi_name = "";
+ info.dlpi_phdr = _dl_phdr;
+ info.dlpi_phnum = _dl_phnum;
+ info.dlpi_adds = GL(dl_load_adds);
+ info.dlpi_subs = GL(dl_load_adds) - GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
+ ret = (*callback) (&info, sizeof (struct dl_phdr_info), data);
+ if (ret)
+ return ret;
+ }
+
+ return __dl_iterate_phdr (callback, data);
+}
+
+
+#endif
diff --git a/libc/elf/dl-libc.c b/libc/elf/dl-libc.c
new file mode 100644
index 000000000..1b995eda9
--- /dev/null
+++ b/libc/elf/dl-libc.c
@@ -0,0 +1,254 @@
+/* Handle loading and unloading shared objects for internal libc purposes.
+ Copyright (C) 1999,2000,2001,2002,2004,2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Zack Weinberg <zack@rabi.columbia.edu>, 1999.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <ldsodefs.h>
+
+extern int __libc_argc attribute_hidden;
+extern char **__libc_argv attribute_hidden;
+
+extern char **__environ;
+
+/* The purpose of this file is to provide wrappers around the dynamic
+ linker error mechanism (similar to dlopen() et al in libdl) which
+ are usable from within libc. Generally we want to throw away the
+ string that dlerror() would return and just pass back a null pointer
+ for errors. This also lets the rest of libc not know about the error
+ handling mechanism.
+
+ Much of this code came from gconv_dl.c with slight modifications. */
+
+static int
+internal_function
+dlerror_run (void (*operate) (void *), void *args)
+{
+ const char *objname;
+ const char *last_errstring = NULL;
+ bool malloced;
+
+ (void) GLRO(dl_catch_error) (&objname, &last_errstring, &malloced,
+ operate, args);
+
+ int result = last_errstring != NULL;
+ if (result && malloced)
+ free ((char *) last_errstring);
+
+ return result;
+}
+
+/* These functions are called by dlerror_run... */
+
+struct do_dlopen_args
+{
+ /* Argument to do_dlopen. */
+ const char *name;
+ /* Opening mode. */
+ int mode;
+
+ /* Return from do_dlopen. */
+ struct link_map *map;
+};
+
+struct do_dlsym_args
+{
+ /* Arguments to do_dlsym. */
+ struct link_map *map;
+ const char *name;
+
+ /* Return values of do_dlsym. */
+ lookup_t loadbase;
+ const ElfW(Sym) *ref;
+};
+
+static void
+do_dlopen (void *ptr)
+{
+ struct do_dlopen_args *args = (struct do_dlopen_args *) ptr;
+ /* Open and relocate the shared object. */
+ args->map = GLRO(dl_open) (args->name, args->mode, NULL, __LM_ID_CALLER,
+ __libc_argc, __libc_argv, __environ);
+}
+
+static void
+do_dlsym (void *ptr)
+{
+ struct do_dlsym_args *args = (struct do_dlsym_args *) ptr;
+ args->ref = NULL;
+ args->loadbase = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref,
+ args->map->l_local_scope, NULL, 0,
+ DL_LOOKUP_RETURN_NEWEST, NULL);
+}
+
+static void
+do_dlclose (void *ptr)
+{
+ GLRO(dl_close) ((struct link_map *) ptr);
+}
+
+/* This code is to support __libc_dlopen from __libc_dlopen'ed shared
+ libraries. We need to ensure the statically linked __libc_dlopen
+ etc. functions are used instead of the dynamically loaded. */
+struct dl_open_hook
+{
+ void *(*dlopen_mode) (const char *name, int mode);
+ void *(*dlsym) (void *map, const char *name);
+ int (*dlclose) (void *map);
+};
+
+#ifdef SHARED
+extern struct dl_open_hook *_dl_open_hook;
+libc_hidden_proto (_dl_open_hook);
+struct dl_open_hook *_dl_open_hook __attribute__ ((nocommon));
+libc_hidden_data_def (_dl_open_hook);
+#else
+static void
+do_dlsym_private (void *ptr)
+{
+ lookup_t l;
+ struct r_found_version vers;
+ vers.name = "GLIBC_PRIVATE";
+ vers.hidden = 1;
+ /* vers.hash = _dl_elf_hash (vers.name); */
+ vers.hash = 0x0963cf85;
+ vers.filename = NULL;
+
+ struct do_dlsym_args *args = (struct do_dlsym_args *) ptr;
+ args->ref = NULL;
+ l = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref,
+ args->map->l_scope, &vers, 0, 0, NULL);
+ args->loadbase = l;
+}
+
+static struct dl_open_hook _dl_open_hook =
+ {
+ .dlopen_mode = __libc_dlopen_mode,
+ .dlsym = __libc_dlsym,
+ .dlclose = __libc_dlclose
+ };
+#endif
+
+/* ... and these functions call dlerror_run. */
+
+void *
+__libc_dlopen_mode (const char *name, int mode)
+{
+ struct do_dlopen_args args;
+ args.name = name;
+ args.mode = mode;
+
+#ifdef SHARED
+ if (__builtin_expect (_dl_open_hook != NULL, 0))
+ return _dl_open_hook->dlopen_mode (name, mode);
+ return (dlerror_run (do_dlopen, &args) ? NULL : (void *) args.map);
+#else
+ if (dlerror_run (do_dlopen, &args))
+ return NULL;
+
+ __libc_register_dl_open_hook (args.map);
+ __libc_register_dlfcn_hook (args.map);
+ return (void *) args.map;
+#endif
+}
+libc_hidden_def (__libc_dlopen_mode)
+
+#ifndef SHARED
+void *
+__libc_dlsym_private (struct link_map *map, const char *name)
+{
+ struct do_dlsym_args sargs;
+ sargs.map = map;
+ sargs.name = name;
+
+ if (! dlerror_run (do_dlsym_private, &sargs))
+ return DL_SYMBOL_ADDRESS (sargs.loadbase, sargs.ref);
+ return NULL;
+}
+
+void
+__libc_register_dl_open_hook (struct link_map *map)
+{
+ struct dl_open_hook **hook;
+
+ hook = (struct dl_open_hook **) __libc_dlsym_private (map, "_dl_open_hook");
+ if (hook != NULL)
+ *hook = &_dl_open_hook;
+}
+#endif
+
+void *
+__libc_dlsym (void *map, const char *name)
+{
+ struct do_dlsym_args args;
+ args.map = map;
+ args.name = name;
+
+#ifdef SHARED
+ if (__builtin_expect (_dl_open_hook != NULL, 0))
+ return _dl_open_hook->dlsym (map, name);
+#endif
+ return (dlerror_run (do_dlsym, &args) ? NULL
+ : (void *) (DL_SYMBOL_ADDRESS (args.loadbase, args.ref)));
+}
+libc_hidden_def (__libc_dlsym)
+
+int
+__libc_dlclose (void *map)
+{
+#ifdef SHARED
+ if (__builtin_expect (_dl_open_hook != NULL, 0))
+ return _dl_open_hook->dlclose (map);
+#endif
+ return dlerror_run (do_dlclose, map);
+}
+libc_hidden_def (__libc_dlclose)
+
+
+libc_freeres_fn (free_mem)
+{
+ struct link_map *l;
+ struct r_search_path_elem *d;
+
+ /* Remove all search directories. */
+ d = GL(dl_all_dirs);
+ while (d != GLRO(dl_init_all_dirs))
+ {
+ struct r_search_path_elem *old = d;
+ d = d->next;
+ free (old);
+ }
+
+ /* Remove all additional names added to the objects. */
+ for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
+ for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next)
+ {
+ struct libname_list *lnp = l->l_libname->next;
+
+ l->l_libname->next = NULL;
+
+ while (lnp != NULL)
+ {
+ struct libname_list *old = lnp;
+ lnp = lnp->next;
+ if (! old->dont_free)
+ free (old);
+ }
+ }
+}
diff --git a/libc/elf/dl-load.c b/libc/elf/dl-load.c
new file mode 100644
index 000000000..902ffc410
--- /dev/null
+++ b/libc/elf/dl-load.c
@@ -0,0 +1,2310 @@
+/* Map in a shared object's segments from the file.
+ Copyright (C) 1995-2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <bits/wordsize.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "dynamic-link.h"
+#include <abi-tag.h>
+#include <stackinfo.h>
+#include <caller.h>
+#include <sysdep.h>
+
+#include <dl-dst.h>
+
+/* On some systems, no flag bits are given to specify file mapping. */
+#ifndef MAP_FILE
+# define MAP_FILE 0
+#endif
+
+/* The right way to map in the shared library files is MAP_COPY, which
+ makes a virtual copy of the data at the time of the mmap call; this
+ guarantees the mapped pages will be consistent even if the file is
+ overwritten. Some losing VM systems like Linux's lack MAP_COPY. All we
+ get is MAP_PRIVATE, which copies each page when it is modified; this
+ means if the file is overwritten, we may at some point get some pages
+ from the new version after starting with pages from the old version.
+
+ To make up for the lack and avoid the overwriting problem,
+ what Linux does have is MAP_DENYWRITE. This prevents anyone
+ from modifying the file while we have it mapped. */
+#ifndef MAP_COPY
+# ifdef MAP_DENYWRITE
+# define MAP_COPY (MAP_PRIVATE | MAP_DENYWRITE)
+# else
+# define MAP_COPY MAP_PRIVATE
+# endif
+#endif
+
+/* Some systems link their relocatable objects for another base address
+ than 0. We want to know the base address for these such that we can
+ subtract this address from the segment addresses during mapping.
+ This results in a more efficient address space usage. Defaults to
+ zero for almost all systems. */
+#ifndef MAP_BASE_ADDR
+# define MAP_BASE_ADDR(l) 0
+#endif
+
+
+#include <endian.h>
+#if BYTE_ORDER == BIG_ENDIAN
+# define byteorder ELFDATA2MSB
+#elif BYTE_ORDER == LITTLE_ENDIAN
+# define byteorder ELFDATA2LSB
+#else
+# error "Unknown BYTE_ORDER " BYTE_ORDER
+# define byteorder ELFDATANONE
+#endif
+
+#define STRING(x) __STRING (x)
+
+#ifdef MAP_ANON
+/* The fd is not examined when using MAP_ANON. */
+# define ANONFD -1
+#else
+int _dl_zerofd = -1;
+# define ANONFD _dl_zerofd
+#endif
+
+/* Handle situations where we have a preferred location in memory for
+ the shared objects. */
+#ifdef ELF_PREFERRED_ADDRESS_DATA
+ELF_PREFERRED_ADDRESS_DATA;
+#endif
+#ifndef ELF_PREFERRED_ADDRESS
+# define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref)
+#endif
+#ifndef ELF_FIXED_ADDRESS
+# define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0)
+#endif
+
+
+int __stack_prot attribute_hidden attribute_relro
+#if _STACK_GROWS_DOWN && defined PROT_GROWSDOWN
+ = PROT_GROWSDOWN;
+#elif _STACK_GROWS_UP && defined PROT_GROWSUP
+ = PROT_GROWSUP;
+#else
+ = 0;
+#endif
+
+
+/* Type for the buffer we put the ELF header and hopefully the program
+ header. This buffer does not really have to be too large. In most
+ cases the program header follows the ELF header directly. If this
+ is not the case all bets are off and we can make the header
+ arbitrarily large and still won't get it read. This means the only
+ question is how large are the ELF and program header combined. The
+ ELF header 32-bit files is 52 bytes long and in 64-bit files is 64
+ bytes long. Each program header entry is again 32 and 56 bytes
+ long respectively. I.e., even with a file which has 10 program
+ header entries we only have to read 372B/624B respectively. Add to
+ this a bit of margin for program notes and reading 512B and 832B
+ for 32-bit and 64-bit files respecitvely is enough. If this
+ heuristic should really fail for some file the code in
+ `_dl_map_object_from_fd' knows how to recover. */
+struct filebuf
+{
+ ssize_t len;
+#if __WORDSIZE == 32
+# define FILEBUF_SIZE 512
+#else
+# define FILEBUF_SIZE 832
+#endif
+ char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
+};
+
+/* This is the decomposed LD_LIBRARY_PATH search path. */
+static struct r_search_path_struct env_path_list attribute_relro;
+
+/* List of the hardware capabilities we might end up using. */
+static const struct r_strlenpair *capstr attribute_relro;
+static size_t ncapstr attribute_relro;
+static size_t max_capstrlen attribute_relro;
+
+
+/* Get the generated information about the trusted directories. */
+#include "trusted-dirs.h"
+
+static const char system_dirs[] = SYSTEM_DIRS;
+static const size_t system_dirs_len[] =
+{
+ SYSTEM_DIRS_LEN
+};
+#define nsystem_dirs_len \
+ (sizeof (system_dirs_len) / sizeof (system_dirs_len[0]))
+
+
+/* Local version of `strdup' function. */
+static inline char *
+local_strdup (const char *s)
+{
+ size_t len = strlen (s) + 1;
+ void *new = malloc (len);
+
+ if (new == NULL)
+ return NULL;
+
+ return (char *) memcpy (new, s, len);
+}
+
+
+static size_t
+is_dst (const char *start, const char *name, const char *str,
+ int is_path, int secure)
+{
+ size_t len;
+ bool is_curly = false;
+
+ if (name[0] == '{')
+ {
+ is_curly = true;
+ ++name;
+ }
+
+ len = 0;
+ while (name[len] == str[len] && name[len] != '\0')
+ ++len;
+
+ if (is_curly)
+ {
+ if (name[len] != '}')
+ return 0;
+
+ /* Point again at the beginning of the name. */
+ --name;
+ /* Skip over closing curly brace and adjust for the --name. */
+ len += 2;
+ }
+ else if (name[len] != '\0' && name[len] != '/'
+ && (!is_path || name[len] != ':'))
+ return 0;
+
+ if (__builtin_expect (secure, 0)
+ && ((name[len] != '\0' && (!is_path || name[len] != ':'))
+ || (name != start + 1 && (!is_path || name[-2] != ':'))))
+ return 0;
+
+ return len;
+}
+
+
+size_t
+_dl_dst_count (const char *name, int is_path)
+{
+ const char *const start = name;
+ size_t cnt = 0;
+
+ do
+ {
+ size_t len;
+
+ /* $ORIGIN is not expanded for SUID/GUID programs (except if it
+ is $ORIGIN alone) and it must always appear first in path. */
+ ++name;
+ if ((len = is_dst (start, name, "ORIGIN", is_path,
+ INTUSE(__libc_enable_secure))) != 0
+ || (len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0
+ || (len = is_dst (start, name, "LIB", is_path, 0)) != 0)
+ ++cnt;
+
+ name = strchr (name + len, '$');
+ }
+ while (name != NULL);
+
+ return cnt;
+}
+
+
+char *
+_dl_dst_substitute (struct link_map *l, const char *name, char *result,
+ int is_path)
+{
+ const char *const start = name;
+ char *last_elem, *wp;
+
+ /* Now fill the result path. While copying over the string we keep
+ track of the start of the last path element. When we come accross
+ a DST we copy over the value or (if the value is not available)
+ leave the entire path element out. */
+ last_elem = wp = result;
+
+ do
+ {
+ if (__builtin_expect (*name == '$', 0))
+ {
+ const char *repl = NULL;
+ size_t len;
+
+ ++name;
+ if ((len = is_dst (start, name, "ORIGIN", is_path,
+ INTUSE(__libc_enable_secure))) != 0)
+ {
+#ifndef SHARED
+ if (l == NULL)
+ repl = _dl_get_origin ();
+ else
+#endif
+ repl = l->l_origin;
+ }
+ else if ((len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0)
+ repl = GLRO(dl_platform);
+ else if ((len = is_dst (start, name, "LIB", is_path, 0)) != 0)
+ repl = DL_DST_LIB;
+
+ if (repl != NULL && repl != (const char *) -1)
+ {
+ wp = __stpcpy (wp, repl);
+ name += len;
+ }
+ else if (len > 1)
+ {
+ /* We cannot use this path element, the value of the
+ replacement is unknown. */
+ wp = last_elem;
+ name += len;
+ while (*name != '\0' && (!is_path || *name != ':'))
+ ++name;
+ }
+ else
+ /* No DST we recognize. */
+ *wp++ = '$';
+ }
+ else
+ {
+ *wp++ = *name++;
+ if (is_path && *name == ':')
+ last_elem = wp;
+ }
+ }
+ while (*name != '\0');
+
+ *wp = '\0';
+
+ return result;
+}
+
+
+/* Return copy of argument with all recognized dynamic string tokens
+ ($ORIGIN and $PLATFORM for now) replaced. On some platforms it
+ might not be possible to determine the path from which the object
+ belonging to the map is loaded. In this case the path element
+ containing $ORIGIN is left out. */
+static char *
+expand_dynamic_string_token (struct link_map *l, const char *s)
+{
+ /* We make two runs over the string. First we determine how large the
+ resulting string is and then we copy it over. Since this is now
+ frequently executed operation we are looking here not for performance
+ but rather for code size. */
+ size_t cnt;
+ size_t total;
+ char *result;
+
+ /* Determine the number of DST elements. */
+ cnt = DL_DST_COUNT (s, 1);
+
+ /* If we do not have to replace anything simply copy the string. */
+ if (__builtin_expect (cnt, 0) == 0)
+ return local_strdup (s);
+
+ /* Determine the length of the substituted string. */
+ total = DL_DST_REQUIRED (l, s, strlen (s), cnt);
+
+ /* Allocate the necessary memory. */
+ result = (char *) malloc (total + 1);
+ if (result == NULL)
+ return NULL;
+
+ return _dl_dst_substitute (l, s, result, 1);
+}
+
+
+/* Add `name' to the list of names for a particular shared object.
+ `name' is expected to have been allocated with malloc and will
+ be freed if the shared object already has this name.
+ Returns false if the object already had this name. */
+static void
+internal_function
+add_name_to_object (struct link_map *l, const char *name)
+{
+ struct libname_list *lnp, *lastp;
+ struct libname_list *newname;
+ size_t name_len;
+
+ lastp = NULL;
+ for (lnp = l->l_libname; lnp != NULL; lastp = lnp, lnp = lnp->next)
+ if (strcmp (name, lnp->name) == 0)
+ return;
+
+ name_len = strlen (name) + 1;
+ newname = (struct libname_list *) malloc (sizeof *newname + name_len);
+ if (newname == NULL)
+ {
+ /* No more memory. */
+ _dl_signal_error (ENOMEM, name, NULL, N_("cannot allocate name record"));
+ return;
+ }
+ /* The object should have a libname set from _dl_new_object. */
+ assert (lastp != NULL);
+
+ newname->name = memcpy (newname + 1, name, name_len);
+ newname->next = NULL;
+ newname->dont_free = 0;
+ lastp->next = newname;
+}
+
+/* Standard search directories. */
+static struct r_search_path_struct rtld_search_dirs attribute_relro;
+
+static size_t max_dirnamelen;
+
+static struct r_search_path_elem **
+fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
+ int check_trusted, const char *what, const char *where)
+{
+ char *cp;
+ size_t nelems = 0;
+
+ while ((cp = __strsep (&rpath, sep)) != NULL)
+ {
+ struct r_search_path_elem *dirp;
+ size_t len = strlen (cp);
+
+ /* `strsep' can pass an empty string. This has to be
+ interpreted as `use the current directory'. */
+ if (len == 0)
+ {
+ static const char curwd[] = "./";
+ cp = (char *) curwd;
+ }
+
+ /* Remove trailing slashes (except for "/"). */
+ while (len > 1 && cp[len - 1] == '/')
+ --len;
+
+ /* Now add one if there is none so far. */
+ if (len > 0 && cp[len - 1] != '/')
+ cp[len++] = '/';
+
+ /* Make sure we don't use untrusted directories if we run SUID. */
+ if (__builtin_expect (check_trusted, 0))
+ {
+ const char *trun = system_dirs;
+ size_t idx;
+ int unsecure = 1;
+
+ /* All trusted directories must be complete names. */
+ if (cp[0] == '/')
+ {
+ for (idx = 0; idx < nsystem_dirs_len; ++idx)
+ {
+ if (len == system_dirs_len[idx]
+ && memcmp (trun, cp, len) == 0)
+ {
+ /* Found it. */
+ unsecure = 0;
+ break;
+ }
+
+ trun += system_dirs_len[idx] + 1;
+ }
+ }
+
+ if (unsecure)
+ /* Simply drop this directory. */
+ continue;
+ }
+
+ /* See if this directory is already known. */
+ for (dirp = GL(dl_all_dirs); dirp != NULL; dirp = dirp->next)
+ if (dirp->dirnamelen == len && memcmp (cp, dirp->dirname, len) == 0)
+ break;
+
+ if (dirp != NULL)
+ {
+ /* It is available, see whether it's on our own list. */
+ size_t cnt;
+ for (cnt = 0; cnt < nelems; ++cnt)
+ if (result[cnt] == dirp)
+ break;
+
+ if (cnt == nelems)
+ result[nelems++] = dirp;
+ }
+ else
+ {
+ size_t cnt;
+ enum r_dir_status init_val;
+ size_t where_len = where ? strlen (where) + 1 : 0;
+
+ /* It's a new directory. Create an entry and add it. */
+ dirp = (struct r_search_path_elem *)
+ malloc (sizeof (*dirp) + ncapstr * sizeof (enum r_dir_status)
+ + where_len + len + 1);
+ if (dirp == NULL)
+ _dl_signal_error (ENOMEM, NULL, NULL,
+ N_("cannot create cache for search path"));
+
+ dirp->dirname = ((char *) dirp + sizeof (*dirp)
+ + ncapstr * sizeof (enum r_dir_status));
+ *((char *) __mempcpy ((char *) dirp->dirname, cp, len)) = '\0';
+ dirp->dirnamelen = len;
+
+ if (len > max_dirnamelen)
+ max_dirnamelen = len;
+
+ /* We have to make sure all the relative directories are
+ never ignored. The current directory might change and
+ all our saved information would be void. */
+ init_val = cp[0] != '/' ? existing : unknown;
+ for (cnt = 0; cnt < ncapstr; ++cnt)
+ dirp->status[cnt] = init_val;
+
+ dirp->what = what;
+ if (__builtin_expect (where != NULL, 1))
+ dirp->where = memcpy ((char *) dirp + sizeof (*dirp) + len + 1
+ + (ncapstr * sizeof (enum r_dir_status)),
+ where, where_len);
+ else
+ dirp->where = NULL;
+
+ dirp->next = GL(dl_all_dirs);
+ GL(dl_all_dirs) = dirp;
+
+ /* Put it in the result array. */
+ result[nelems++] = dirp;
+ }
+ }
+
+ /* Terminate the array. */
+ result[nelems] = NULL;
+
+ return result;
+}
+
+
+static void
+internal_function
+decompose_rpath (struct r_search_path_struct *sps,
+ const char *rpath, struct link_map *l, const char *what)
+{
+ /* Make a copy we can work with. */
+ const char *where = l->l_name;
+ char *copy;
+ char *cp;
+ struct r_search_path_elem **result;
+ size_t nelems;
+ /* Initialize to please the compiler. */
+ const char *errstring = NULL;
+
+ /* First see whether we must forget the RUNPATH and RPATH from this
+ object. */
+ if (__builtin_expect (GLRO(dl_inhibit_rpath) != NULL, 0)
+ && !INTUSE(__libc_enable_secure))
+ {
+ const char *inhp = GLRO(dl_inhibit_rpath);
+
+ do
+ {
+ const char *wp = where;
+
+ while (*inhp == *wp && *wp != '\0')
+ {
+ ++inhp;
+ ++wp;
+ }
+
+ if (*wp == '\0' && (*inhp == '\0' || *inhp == ':'))
+ {
+ /* This object is on the list of objects for which the
+ RUNPATH and RPATH must not be used. */
+ result = calloc (1, sizeof *result);
+ if (result == NULL)
+ {
+ signal_error_cache:
+ errstring = N_("cannot create cache for search path");
+ signal_error:
+ _dl_signal_error (ENOMEM, NULL, NULL, errstring);
+ }
+
+ sps->dirs = result;
+ sps->malloced = 1;
+
+ return;
+ }
+
+ while (*inhp != '\0')
+ if (*inhp++ == ':')
+ break;
+ }
+ while (*inhp != '\0');
+ }
+
+ /* Make a writable copy. At the same time expand possible dynamic
+ string tokens. */
+ copy = expand_dynamic_string_token (l, rpath);
+ if (copy == NULL)
+ {
+ errstring = N_("cannot create RUNPATH/RPATH copy");
+ goto signal_error;
+ }
+
+ /* Count the number of necessary elements in the result array. */
+ nelems = 0;
+ for (cp = copy; *cp != '\0'; ++cp)
+ if (*cp == ':')
+ ++nelems;
+
+ /* Allocate room for the result. NELEMS + 1 is an upper limit for the
+ number of necessary entries. */
+ result = (struct r_search_path_elem **) malloc ((nelems + 1 + 1)
+ * sizeof (*result));
+ if (result == NULL)
+ goto signal_error_cache;
+
+ fillin_rpath (copy, result, ":", 0, what, where);
+
+ /* Free the copied RPATH string. `fillin_rpath' make own copies if
+ necessary. */
+ free (copy);
+
+ sps->dirs = result;
+ /* The caller will change this value if we haven't used a real malloc. */
+ sps->malloced = 1;
+}
+
+/* Make sure cached path information is stored in *SP
+ and return true if there are any paths to search there. */
+static bool
+cache_rpath (struct link_map *l,
+ struct r_search_path_struct *sp,
+ int tag,
+ const char *what)
+{
+ if (sp->dirs == (void *) -1)
+ return false;
+
+ if (sp->dirs != NULL)
+ return true;
+
+ if (l->l_info[tag] == NULL)
+ {
+ /* There is no path. */
+ sp->dirs = (void *) -1;
+ return false;
+ }
+
+ /* Make sure the cache information is available. */
+ decompose_rpath (sp, (const char *) (D_PTR (l, l_info[DT_STRTAB])
+ + l->l_info[tag]->d_un.d_val),
+ l, what);
+ return true;
+}
+
+
+void
+internal_function
+_dl_init_paths (const char *llp)
+{
+ size_t idx;
+ const char *strp;
+ struct r_search_path_elem *pelem, **aelem;
+ size_t round_size;
+#ifdef SHARED
+ struct link_map *l;
+#endif
+ /* Initialize to please the compiler. */
+ const char *errstring = NULL;
+
+ /* Fill in the information about the application's RPATH and the
+ directories addressed by the LD_LIBRARY_PATH environment variable. */
+
+ /* Get the capabilities. */
+ capstr = _dl_important_hwcaps (GLRO(dl_platform), GLRO(dl_platformlen),
+ &ncapstr, &max_capstrlen);
+
+ /* First set up the rest of the default search directory entries. */
+ aelem = rtld_search_dirs.dirs = (struct r_search_path_elem **)
+ malloc ((nsystem_dirs_len + 1) * sizeof (struct r_search_path_elem *));
+ if (rtld_search_dirs.dirs == NULL)
+ {
+ errstring = N_("cannot create search path array");
+ signal_error:
+ _dl_signal_error (ENOMEM, NULL, NULL, errstring);
+ }
+
+ round_size = ((2 * sizeof (struct r_search_path_elem) - 1
+ + ncapstr * sizeof (enum r_dir_status))
+ / sizeof (struct r_search_path_elem));
+
+ rtld_search_dirs.dirs[0] = (struct r_search_path_elem *)
+ malloc ((sizeof (system_dirs) / sizeof (system_dirs[0]))
+ * round_size * sizeof (struct r_search_path_elem));
+ if (rtld_search_dirs.dirs[0] == NULL)
+ {
+ errstring = N_("cannot create cache for search path");
+ goto signal_error;
+ }
+
+ rtld_search_dirs.malloced = 0;
+ pelem = GL(dl_all_dirs) = rtld_search_dirs.dirs[0];
+ strp = system_dirs;
+ idx = 0;
+
+ do
+ {
+ size_t cnt;
+
+ *aelem++ = pelem;
+
+ pelem->what = "system search path";
+ pelem->where = NULL;
+
+ pelem->dirname = strp;
+ pelem->dirnamelen = system_dirs_len[idx];
+ strp += system_dirs_len[idx] + 1;
+
+ /* System paths must be absolute. */
+ assert (pelem->dirname[0] == '/');
+ for (cnt = 0; cnt < ncapstr; ++cnt)
+ pelem->status[cnt] = unknown;
+
+ pelem->next = (++idx == nsystem_dirs_len ? NULL : (pelem + round_size));
+
+ pelem += round_size;
+ }
+ while (idx < nsystem_dirs_len);
+
+ max_dirnamelen = SYSTEM_DIRS_MAX_LEN;
+ *aelem = NULL;
+
+#ifdef SHARED
+ /* This points to the map of the main object. */
+ l = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+ if (l != NULL)
+ {
+ assert (l->l_type != lt_loaded);
+
+ if (l->l_info[DT_RUNPATH])
+ {
+ /* Allocate room for the search path and fill in information
+ from RUNPATH. */
+ decompose_rpath (&l->l_runpath_dirs,
+ (const void *) (D_PTR (l, l_info[DT_STRTAB])
+ + l->l_info[DT_RUNPATH]->d_un.d_val),
+ l, "RUNPATH");
+
+ /* The RPATH is ignored. */
+ l->l_rpath_dirs.dirs = (void *) -1;
+ }
+ else
+ {
+ l->l_runpath_dirs.dirs = (void *) -1;
+
+ if (l->l_info[DT_RPATH])
+ {
+ /* Allocate room for the search path and fill in information
+ from RPATH. */
+ decompose_rpath (&l->l_rpath_dirs,
+ (const void *) (D_PTR (l, l_info[DT_STRTAB])
+ + l->l_info[DT_RPATH]->d_un.d_val),
+ l, "RPATH");
+ l->l_rpath_dirs.malloced = 0;
+ }
+ else
+ l->l_rpath_dirs.dirs = (void *) -1;
+ }
+ }
+#endif /* SHARED */
+
+ if (llp != NULL && *llp != '\0')
+ {
+ size_t nllp;
+ const char *cp = llp;
+ char *llp_tmp = strdupa (llp);
+
+ /* Decompose the LD_LIBRARY_PATH contents. First determine how many
+ elements it has. */
+ nllp = 1;
+ while (*cp)
+ {
+ if (*cp == ':' || *cp == ';')
+ ++nllp;
+ ++cp;
+ }
+
+ env_path_list.dirs = (struct r_search_path_elem **)
+ malloc ((nllp + 1) * sizeof (struct r_search_path_elem *));
+ if (env_path_list.dirs == NULL)
+ {
+ errstring = N_("cannot create cache for search path");
+ goto signal_error;
+ }
+
+ (void) fillin_rpath (llp_tmp, env_path_list.dirs, ":;",
+ INTUSE(__libc_enable_secure), "LD_LIBRARY_PATH",
+ NULL);
+
+ if (env_path_list.dirs[0] == NULL)
+ {
+ free (env_path_list.dirs);
+ env_path_list.dirs = (void *) -1;
+ }
+
+ env_path_list.malloced = 0;
+ }
+ else
+ env_path_list.dirs = (void *) -1;
+
+ /* Remember the last search directory added at startup. */
+ GLRO(dl_init_all_dirs) = GL(dl_all_dirs);
+}
+
+
+static void
+__attribute__ ((noreturn, noinline))
+lose (int code, int fd, const char *name, char *realname, struct link_map *l,
+ const char *msg, struct r_debug *r)
+{
+ /* The file might already be closed. */
+ if (fd != -1)
+ (void) __close (fd);
+ if (l != NULL)
+ {
+ /* Remove the stillborn object from the list and free it. */
+ assert (l->l_next == NULL);
+ if (l->l_prev == NULL)
+ /* No other module loaded. This happens only in the static library,
+ or in rtld under --verify. */
+ GL(dl_ns)[l->l_ns]._ns_loaded = NULL;
+ else
+ l->l_prev->l_next = NULL;
+ --GL(dl_ns)[l->l_ns]._ns_nloaded;
+ free (l);
+ }
+ free (realname);
+
+ if (r != NULL)
+ {
+ r->r_state = RT_CONSISTENT;
+ _dl_debug_state ();
+ }
+
+ _dl_signal_error (code, name, NULL, msg);
+}
+
+
+/* Map in the shared object NAME, actually located in REALNAME, and already
+ opened on FD. */
+
+#ifndef EXTERNAL_MAP_FROM_FD
+static
+#endif
+struct link_map *
+_dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp,
+ char *realname, struct link_map *loader, int l_type,
+ int mode, void **stack_endp, Lmid_t nsid)
+{
+ struct link_map *l = NULL;
+ const ElfW(Ehdr) *header;
+ const ElfW(Phdr) *phdr;
+ const ElfW(Phdr) *ph;
+ size_t maplength;
+ int type;
+ struct stat64 st;
+ /* Initialize to keep the compiler happy. */
+ const char *errstring = NULL;
+ int errval = 0;
+ struct r_debug *r = _dl_debug_initialize (0, nsid);
+ bool make_consistent = false;
+
+ /* Get file information. */
+ if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st) < 0, 0))
+ {
+ errstring = N_("cannot stat shared object");
+ call_lose_errno:
+ errval = errno;
+ call_lose:
+ lose (errval, fd, name, realname, l, errstring,
+ make_consistent ? r : NULL);
+ }
+
+ /* Look again to see if the real name matched another already loaded. */
+ for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
+ if (l->l_removed == 0 && l->l_ino == st.st_ino && l->l_dev == st.st_dev)
+ {
+ /* The object is already loaded.
+ Just bump its reference count and return it. */
+ __close (fd);
+
+ /* If the name is not in the list of names for this object add
+ it. */
+ free (realname);
+ add_name_to_object (l, name);
+
+ return l;
+ }
+
+#ifdef SHARED
+ /* When loading into a namespace other than the base one we must
+ avoid loading ld.so since there can only be one copy. Ever. */
+ if (__builtin_expect (nsid != LM_ID_BASE, 0)
+ && ((st.st_ino == GL(dl_rtld_map).l_ino
+ && st.st_dev == GL(dl_rtld_map).l_dev)
+ || _dl_name_match_p (name, &GL(dl_rtld_map))))
+ {
+ /* This is indeed ld.so. Create a new link_map which refers to
+ the real one for almost everything. */
+ l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
+ if (l == NULL)
+ goto fail_new;
+
+ /* Refer to the real descriptor. */
+ l->l_real = &GL(dl_rtld_map);
+
+ /* No need to bump the refcount of the real object, ld.so will
+ never be unloaded. */
+ __close (fd);
+
+ return l;
+ }
+#endif
+
+ if (mode & RTLD_NOLOAD)
+ /* We are not supposed to load the object unless it is already
+ loaded. So return now. */
+ return NULL;
+
+ /* Print debugging message. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0))
+ _dl_debug_printf ("file=%s [%lu]; generating link map\n", name, nsid);
+
+ /* This is the ELF header. We read it in `open_verify'. */
+ header = (void *) fbp->buf;
+
+#ifndef MAP_ANON
+# define MAP_ANON 0
+ if (_dl_zerofd == -1)
+ {
+ _dl_zerofd = _dl_sysdep_open_zero_fill ();
+ if (_dl_zerofd == -1)
+ {
+ __close (fd);
+ _dl_signal_error (errno, NULL, NULL,
+ N_("cannot open zero fill device"));
+ }
+ }
+#endif
+
+ /* Signal that we are going to add new objects. */
+ if (r->r_state == RT_CONSISTENT)
+ {
+#ifdef SHARED
+ /* Auditing checkpoint: we are going to add new objects. */
+ if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
+ {
+ struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
+ /* Do not call the functions for any auditing object. */
+ if (head->l_auditing == 0)
+ {
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->activity != NULL)
+ afct->activity (&head->l_audit[cnt].cookie, LA_ACT_ADD);
+
+ afct = afct->next;
+ }
+ }
+ }
+#endif
+
+ /* Notify the debugger we have added some objects. We need to
+ call _dl_debug_initialize in a static program in case dynamic
+ linking has not been used before. */
+ r->r_state = RT_ADD;
+ _dl_debug_state ();
+ make_consistent = true;
+ }
+ else
+ assert (r->r_state == RT_ADD);
+
+ /* Enter the new object in the list of loaded objects. */
+ l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
+ if (__builtin_expect (l == NULL, 0))
+ {
+#ifdef SHARED
+ fail_new:
+#endif
+ errstring = N_("cannot create shared object descriptor");
+ goto call_lose_errno;
+ }
+
+ /* Extract the remaining details we need from the ELF header
+ and then read in the program header table. */
+ l->l_entry = header->e_entry;
+ type = header->e_type;
+ l->l_phnum = header->e_phnum;
+
+ maplength = header->e_phnum * sizeof (ElfW(Phdr));
+ if (header->e_phoff + maplength <= (size_t) fbp->len)
+ phdr = (void *) (fbp->buf + header->e_phoff);
+ else
+ {
+ phdr = alloca (maplength);
+ __lseek (fd, header->e_phoff, SEEK_SET);
+ if ((size_t) __libc_read (fd, (void *) phdr, maplength) != maplength)
+ {
+ errstring = N_("cannot read file data");
+ goto call_lose_errno;
+ }
+ }
+
+ /* Presumed absent PT_GNU_STACK. */
+ uint_fast16_t stack_flags = PF_R|PF_W|PF_X;
+
+ {
+ /* Scan the program header table, collecting its load commands. */
+ struct loadcmd
+ {
+ ElfW(Addr) mapstart, mapend, dataend, allocend;
+ off_t mapoff;
+ int prot;
+ } loadcmds[l->l_phnum], *c;
+ size_t nloadcmds = 0;
+ bool has_holes = false;
+
+ /* The struct is initialized to zero so this is not necessary:
+ l->l_ld = 0;
+ l->l_phdr = 0;
+ l->l_addr = 0; */
+ for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph)
+ switch (ph->p_type)
+ {
+ /* These entries tell us where to find things once the file's
+ segments are mapped in. We record the addresses it says
+ verbatim, and later correct for the run-time load address. */
+ case PT_DYNAMIC:
+ l->l_ld = (void *) ph->p_vaddr;
+ l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
+ break;
+
+ case PT_PHDR:
+ l->l_phdr = (void *) ph->p_vaddr;
+ break;
+
+ case PT_LOAD:
+ /* A load command tells us to map in part of the file.
+ We record the load commands and process them all later. */
+ if (__builtin_expect ((ph->p_align & (GLRO(dl_pagesize) - 1)) != 0,
+ 0))
+ {
+ errstring = N_("ELF load command alignment not page-aligned");
+ goto call_lose;
+ }
+ if (__builtin_expect (((ph->p_vaddr - ph->p_offset)
+ & (ph->p_align - 1)) != 0, 0))
+ {
+ errstring
+ = N_("ELF load command address/offset not properly aligned");
+ goto call_lose;
+ }
+
+ c = &loadcmds[nloadcmds++];
+ c->mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1);
+ c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1)
+ & ~(GLRO(dl_pagesize) - 1));
+ c->dataend = ph->p_vaddr + ph->p_filesz;
+ c->allocend = ph->p_vaddr + ph->p_memsz;
+ c->mapoff = ph->p_offset & ~(GLRO(dl_pagesize) - 1);
+
+ /* Determine whether there is a gap between the last segment
+ and this one. */
+ if (nloadcmds > 1 && c[-1].mapend != c->mapstart)
+ has_holes = true;
+
+ /* Optimize a common case. */
+#if (PF_R | PF_W | PF_X) == 7 && (PROT_READ | PROT_WRITE | PROT_EXEC) == 7
+ c->prot = (PF_TO_PROT
+ >> ((ph->p_flags & (PF_R | PF_W | PF_X)) * 4)) & 0xf;
+#else
+ c->prot = 0;
+ if (ph->p_flags & PF_R)
+ c->prot |= PROT_READ;
+ if (ph->p_flags & PF_W)
+ c->prot |= PROT_WRITE;
+ if (ph->p_flags & PF_X)
+ c->prot |= PROT_EXEC;
+#endif
+ break;
+
+ case PT_TLS:
+#ifdef USE_TLS
+ if (ph->p_memsz == 0)
+ /* Nothing to do for an empty segment. */
+ break;
+
+ l->l_tls_blocksize = ph->p_memsz;
+ l->l_tls_align = ph->p_align;
+ if (ph->p_align == 0)
+ l->l_tls_firstbyte_offset = 0;
+ else
+ l->l_tls_firstbyte_offset = ph->p_vaddr & (ph->p_align - 1);
+ l->l_tls_initimage_size = ph->p_filesz;
+ /* Since we don't know the load address yet only store the
+ offset. We will adjust it later. */
+ l->l_tls_initimage = (void *) ph->p_vaddr;
+
+ /* If not loading the initial set of shared libraries,
+ check whether we should permit loading a TLS segment. */
+ if (__builtin_expect (l->l_type == lt_library, 1)
+ /* If GL(dl_tls_dtv_slotinfo_list) == NULL, then rtld.c did
+ not set up TLS data structures, so don't use them now. */
+ || __builtin_expect (GL(dl_tls_dtv_slotinfo_list) != NULL, 1))
+ {
+ /* Assign the next available module ID. */
+ l->l_tls_modid = _dl_next_tls_modid ();
+ break;
+ }
+
+# ifdef SHARED
+ if (l->l_prev == NULL || (mode & __RTLD_AUDIT) != 0)
+ /* We are loading the executable itself when the dynamic linker
+ was executed directly. The setup will happen later. */
+ break;
+
+ /* In a static binary there is no way to tell if we dynamically
+ loaded libpthread. */
+ if (GL(dl_error_catch_tsd) == &_dl_initial_error_catch_tsd)
+# endif
+ {
+ /* We have not yet loaded libpthread.
+ We can do the TLS setup right now! */
+
+ void *tcb;
+
+ /* The first call allocates TLS bookkeeping data structures.
+ Then we allocate the TCB for the initial thread. */
+ if (__builtin_expect (_dl_tls_setup (), 0)
+ || __builtin_expect ((tcb = _dl_allocate_tls (NULL)) == NULL,
+ 0))
+ {
+ errval = ENOMEM;
+ errstring = N_("\
+cannot allocate TLS data structures for initial thread");
+ goto call_lose;
+ }
+
+ /* Now we install the TCB in the thread register. */
+ errstring = TLS_INIT_TP (tcb, 0);
+ if (__builtin_expect (errstring == NULL, 1))
+ {
+ /* Now we are all good. */
+ l->l_tls_modid = ++GL(dl_tls_max_dtv_idx);
+ break;
+ }
+
+ /* The kernel is too old or somesuch. */
+ errval = 0;
+ _dl_deallocate_tls (tcb, 1);
+ goto call_lose;
+ }
+#endif
+
+ /* Uh-oh, the binary expects TLS support but we cannot
+ provide it. */
+ errval = 0;
+ errstring = N_("cannot handle TLS data");
+ goto call_lose;
+ break;
+
+ case PT_GNU_STACK:
+ stack_flags = ph->p_flags;
+ break;
+
+ case PT_GNU_RELRO:
+ l->l_relro_addr = ph->p_vaddr;
+ l->l_relro_size = ph->p_memsz;
+ break;
+ }
+
+ if (__builtin_expect (nloadcmds == 0, 0))
+ {
+ /* This only happens for a bogus object that will be caught with
+ another error below. But we don't want to go through the
+ calculations below using NLOADCMDS - 1. */
+ errstring = N_("object file has no loadable segments");
+ goto call_lose;
+ }
+
+ /* Now process the load commands and map segments into memory. */
+ c = loadcmds;
+
+ /* Length of the sections to be loaded. */
+ maplength = loadcmds[nloadcmds - 1].allocend - c->mapstart;
+
+ if (__builtin_expect (type, ET_DYN) == ET_DYN)
+ {
+ /* This is a position-independent shared object. We can let the
+ kernel map it anywhere it likes, but we must have space for all
+ the segments in their specified positions relative to the first.
+ So we map the first segment without MAP_FIXED, but with its
+ extent increased to cover all the segments. Then we remove
+ access from excess portion, and there is known sufficient space
+ there to remap from the later segments.
+
+ As a refinement, sometimes we have an address that we would
+ prefer to map such objects at; but this is only a preference,
+ the OS can do whatever it likes. */
+ ElfW(Addr) mappref;
+ mappref = (ELF_PREFERRED_ADDRESS (loader, maplength,
+ c->mapstart & GLRO(dl_use_load_bias))
+ - MAP_BASE_ADDR (l));
+
+ /* Remember which part of the address space this object uses. */
+ l->l_map_start = (ElfW(Addr)) __mmap ((void *) mappref, maplength,
+ c->prot,
+ MAP_COPY|MAP_FILE,
+ fd, c->mapoff);
+ if (__builtin_expect ((void *) l->l_map_start == MAP_FAILED, 0))
+ {
+ map_error:
+ errstring = N_("failed to map segment from shared object");
+ goto call_lose_errno;
+ }
+
+ l->l_map_end = l->l_map_start + maplength;
+ l->l_addr = l->l_map_start - c->mapstart;
+
+ if (has_holes)
+ /* Change protection on the excess portion to disallow all access;
+ the portions we do not remap later will be inaccessible as if
+ unallocated. Then jump into the normal segment-mapping loop to
+ handle the portion of the segment past the end of the file
+ mapping. */
+ __mprotect ((caddr_t) (l->l_addr + c->mapend),
+ loadcmds[nloadcmds - 1].mapstart - c->mapend,
+ PROT_NONE);
+
+ goto postmap;
+ }
+
+ /* This object is loaded at a fixed address. This must never
+ happen for objects loaded with dlopen(). */
+ if (__builtin_expect ((mode & __RTLD_OPENEXEC) == 0, 0))
+ {
+ errstring = N_("cannot dynamically load executable");
+ goto call_lose;
+ }
+
+ /* Notify ELF_PREFERRED_ADDRESS that we have to load this one
+ fixed. */
+ ELF_FIXED_ADDRESS (loader, c->mapstart);
+
+
+ /* Remember which part of the address space this object uses. */
+ l->l_map_start = c->mapstart + l->l_addr;
+ l->l_map_end = l->l_map_start + maplength;
+
+ while (c < &loadcmds[nloadcmds])
+ {
+ if (c->mapend > c->mapstart
+ /* Map the segment contents from the file. */
+ && (__mmap ((void *) (l->l_addr + c->mapstart),
+ c->mapend - c->mapstart, c->prot,
+ MAP_FIXED|MAP_COPY|MAP_FILE,
+ fd, c->mapoff)
+ == MAP_FAILED))
+ goto map_error;
+
+ postmap:
+ if (c->prot & PROT_EXEC)
+ l->l_text_end = l->l_addr + c->mapend;
+
+ if (l->l_phdr == 0
+ && (ElfW(Off)) c->mapoff <= header->e_phoff
+ && ((size_t) (c->mapend - c->mapstart + c->mapoff)
+ >= header->e_phoff + header->e_phnum * sizeof (ElfW(Phdr))))
+ /* Found the program header in this segment. */
+ l->l_phdr = (void *) (c->mapstart + header->e_phoff - c->mapoff);
+
+ if (c->allocend > c->dataend)
+ {
+ /* Extra zero pages should appear at the end of this segment,
+ after the data mapped from the file. */
+ ElfW(Addr) zero, zeroend, zeropage;
+
+ zero = l->l_addr + c->dataend;
+ zeroend = l->l_addr + c->allocend;
+ zeropage = ((zero + GLRO(dl_pagesize) - 1)
+ & ~(GLRO(dl_pagesize) - 1));
+
+ if (zeroend < zeropage)
+ /* All the extra data is in the last page of the segment.
+ We can just zero it. */
+ zeropage = zeroend;
+
+ if (zeropage > zero)
+ {
+ /* Zero the final part of the last page of the segment. */
+ if (__builtin_expect ((c->prot & PROT_WRITE) == 0, 0))
+ {
+ /* Dag nab it. */
+ if (__mprotect ((caddr_t) (zero
+ & ~(GLRO(dl_pagesize) - 1)),
+ GLRO(dl_pagesize), c->prot|PROT_WRITE) < 0)
+ {
+ errstring = N_("cannot change memory protections");
+ goto call_lose_errno;
+ }
+ }
+ memset ((void *) zero, '\0', zeropage - zero);
+ if (__builtin_expect ((c->prot & PROT_WRITE) == 0, 0))
+ __mprotect ((caddr_t) (zero & ~(GLRO(dl_pagesize) - 1)),
+ GLRO(dl_pagesize), c->prot);
+ }
+
+ if (zeroend > zeropage)
+ {
+ /* Map the remaining zero pages in from the zero fill FD. */
+ caddr_t mapat;
+ mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,
+ c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
+ ANONFD, 0);
+ if (__builtin_expect (mapat == MAP_FAILED, 0))
+ {
+ errstring = N_("cannot map zero-fill pages");
+ goto call_lose_errno;
+ }
+ }
+ }
+
+ ++c;
+ }
+ }
+
+ if (l->l_ld == 0)
+ {
+ if (__builtin_expect (type == ET_DYN, 0))
+ {
+ errstring = N_("object file has no dynamic section");
+ goto call_lose;
+ }
+ }
+ else
+ l->l_ld = (ElfW(Dyn) *) ((ElfW(Addr)) l->l_ld + l->l_addr);
+
+ elf_get_dynamic_info (l, NULL);
+
+ /* Make sure we are not dlopen'ing an object that has the
+ DF_1_NOOPEN flag set. */
+ if (__builtin_expect (l->l_flags_1 & DF_1_NOOPEN, 0)
+ && (mode & __RTLD_DLOPEN))
+ {
+ /* We are not supposed to load this object. Free all resources. */
+ __munmap ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
+
+ if (!l->l_libname->dont_free)
+ free (l->l_libname);
+
+ if (l->l_phdr_allocated)
+ free ((void *) l->l_phdr);
+
+ errstring = N_("shared object cannot be dlopen()ed");
+ goto call_lose;
+ }
+
+ if (l->l_phdr == NULL)
+ {
+ /* The program header is not contained in any of the segments.
+ We have to allocate memory ourself and copy it over from out
+ temporary place. */
+ ElfW(Phdr) *newp = (ElfW(Phdr) *) malloc (header->e_phnum
+ * sizeof (ElfW(Phdr)));
+ if (newp == NULL)
+ {
+ errstring = N_("cannot allocate memory for program header");
+ goto call_lose_errno;
+ }
+
+ l->l_phdr = memcpy (newp, phdr,
+ (header->e_phnum * sizeof (ElfW(Phdr))));
+ l->l_phdr_allocated = 1;
+ }
+ else
+ /* Adjust the PT_PHDR value by the runtime load address. */
+ l->l_phdr = (ElfW(Phdr) *) ((ElfW(Addr)) l->l_phdr + l->l_addr);
+
+ if (__builtin_expect ((stack_flags &~ GL(dl_stack_flags)) & PF_X, 0))
+ {
+ if (__builtin_expect (__check_caller (RETURN_ADDRESS (0), allow_ldso),
+ 0) != 0)
+ {
+ errstring = N_("invalid caller");
+ goto call_lose;
+ }
+
+ /* The stack is presently not executable, but this module
+ requires that it be executable. We must change the
+ protection of the variable which contains the flags used in
+ the mprotect calls. */
+#if defined HAVE_Z_RELRO && defined SHARED
+ if ((mode & (__RTLD_DLOPEN | __RTLD_AUDIT)) == __RTLD_DLOPEN)
+ {
+ const uintptr_t p = (uintptr_t) &__stack_prot & -GLRO(dl_pagesize);
+ const size_t s = (uintptr_t) (&__stack_prot + 1) - p;
+
+ struct link_map *const m = &GL(dl_rtld_map);
+ const uintptr_t relro_end = ((m->l_addr + m->l_relro_addr
+ + m->l_relro_size)
+ & -GLRO(dl_pagesize));
+ if (__builtin_expect (p + s <= relro_end, 1))
+ {
+ /* The variable lies in the region protected by RELRO. */
+ __mprotect ((void *) p, s, PROT_READ|PROT_WRITE);
+ __stack_prot |= PROT_READ|PROT_WRITE|PROT_EXEC;
+ __mprotect ((void *) p, s, PROT_READ);
+ }
+ else
+ __stack_prot |= PROT_READ|PROT_WRITE|PROT_EXEC;
+ }
+ else
+#endif
+ __stack_prot |= PROT_READ|PROT_WRITE|PROT_EXEC;
+
+#ifdef check_consistency
+ check_consistency ();
+#endif
+
+ errval = (*GL(dl_make_stack_executable_hook)) (stack_endp);
+ if (errval)
+ {
+ errstring = N_("\
+cannot enable executable stack as shared object requires");
+ goto call_lose;
+ }
+ }
+
+#ifdef USE_TLS
+ /* Adjust the address of the TLS initialization image. */
+ if (l->l_tls_initimage != NULL)
+ l->l_tls_initimage = (char *) l->l_tls_initimage + l->l_addr;
+#endif
+
+ /* We are done mapping in the file. We no longer need the descriptor. */
+ if (__builtin_expect (__close (fd) != 0, 0))
+ {
+ errstring = N_("cannot close file descriptor");
+ goto call_lose_errno;
+ }
+ /* Signal that we closed the file. */
+ fd = -1;
+
+ if (l->l_type == lt_library && type == ET_EXEC)
+ l->l_type = lt_executable;
+
+ l->l_entry += l->l_addr;
+
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0))
+ _dl_debug_printf ("\
+ dynamic: 0x%0*lx base: 0x%0*lx size: 0x%0*Zx\n\
+ entry: 0x%0*lx phdr: 0x%0*lx phnum: %*u\n\n",
+ (int) sizeof (void *) * 2,
+ (unsigned long int) l->l_ld,
+ (int) sizeof (void *) * 2,
+ (unsigned long int) l->l_addr,
+ (int) sizeof (void *) * 2, maplength,
+ (int) sizeof (void *) * 2,
+ (unsigned long int) l->l_entry,
+ (int) sizeof (void *) * 2,
+ (unsigned long int) l->l_phdr,
+ (int) sizeof (void *) * 2, l->l_phnum);
+
+ /* Set up the symbol hash table. */
+ _dl_setup_hash (l);
+
+ /* If this object has DT_SYMBOLIC set modify now its scope. We don't
+ have to do this for the main map. */
+ if ((mode & RTLD_DEEPBIND) == 0
+ && __builtin_expect (l->l_info[DT_SYMBOLIC] != NULL, 0)
+ && &l->l_searchlist != l->l_scope[0])
+ {
+ /* Create an appropriate searchlist. It contains only this map.
+ This is the definition of DT_SYMBOLIC in SysVr4. */
+ l->l_symbolic_searchlist.r_list =
+ (struct link_map **) malloc (sizeof (struct link_map *));
+
+ if (l->l_symbolic_searchlist.r_list == NULL)
+ {
+ errstring = N_("cannot create searchlist");
+ goto call_lose_errno;
+ }
+
+ l->l_symbolic_searchlist.r_list[0] = l;
+ l->l_symbolic_searchlist.r_nlist = 1;
+
+ /* Now move the existing entries one back. */
+ memmove (&l->l_scope[1], &l->l_scope[0],
+ (l->l_scope_max - 1) * sizeof (l->l_scope[0]));
+
+ /* Now add the new entry. */
+ l->l_scope[0] = &l->l_symbolic_searchlist;
+ }
+
+ /* Remember whether this object must be initialized first. */
+ if (l->l_flags_1 & DF_1_INITFIRST)
+ GL(dl_initfirst) = l;
+
+ /* Finally the file information. */
+ l->l_dev = st.st_dev;
+ l->l_ino = st.st_ino;
+
+ /* When we profile the SONAME might be needed for something else but
+ loading. Add it right away. */
+ if (__builtin_expect (GLRO(dl_profile) != NULL, 0)
+ && l->l_info[DT_SONAME] != NULL)
+ add_name_to_object (l, ((const char *) D_PTR (l, l_info[DT_STRTAB])
+ + l->l_info[DT_SONAME]->d_un.d_val));
+
+#ifdef SHARED
+ /* Auditing checkpoint: we have a new object. */
+ if (__builtin_expect (GLRO(dl_naudit) > 0, 0)
+ && !GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
+ {
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->objopen != NULL)
+ {
+ l->l_audit[cnt].bindflags
+ = afct->objopen (l, nsid, &l->l_audit[cnt].cookie);
+
+ l->l_audit_any_plt |= l->l_audit[cnt].bindflags != 0;
+ }
+
+ afct = afct->next;
+ }
+ }
+#endif
+
+ return l;
+}
+
+/* Print search path. */
+static void
+print_search_path (struct r_search_path_elem **list,
+ const char *what, const char *name)
+{
+ char buf[max_dirnamelen + max_capstrlen];
+ int first = 1;
+
+ _dl_debug_printf (" search path=");
+
+ while (*list != NULL && (*list)->what == what) /* Yes, ==. */
+ {
+ char *endp = __mempcpy (buf, (*list)->dirname, (*list)->dirnamelen);
+ size_t cnt;
+
+ for (cnt = 0; cnt < ncapstr; ++cnt)
+ if ((*list)->status[cnt] != nonexisting)
+ {
+ char *cp = __mempcpy (endp, capstr[cnt].str, capstr[cnt].len);
+ if (cp == buf || (cp == buf + 1 && buf[0] == '/'))
+ cp[0] = '\0';
+ else
+ cp[-1] = '\0';
+
+ _dl_debug_printf_c (first ? "%s" : ":%s", buf);
+ first = 0;
+ }
+
+ ++list;
+ }
+
+ if (name != NULL)
+ _dl_debug_printf_c ("\t\t(%s from file %s)\n", what,
+ name[0] ? name : rtld_progname);
+ else
+ _dl_debug_printf_c ("\t\t(%s)\n", what);
+}
+
+/* Open a file and verify it is an ELF file for this architecture. We
+ ignore only ELF files for other architectures. Non-ELF files and
+ ELF files with different header information cause fatal errors since
+ this could mean there is something wrong in the installation and the
+ user might want to know about this. */
+static int
+open_verify (const char *name, struct filebuf *fbp, struct link_map *loader,
+ int whatcode, bool *found_other_class, bool free_name)
+{
+ /* This is the expected ELF header. */
+#define ELF32_CLASS ELFCLASS32
+#define ELF64_CLASS ELFCLASS64
+#ifndef VALID_ELF_HEADER
+# define VALID_ELF_HEADER(hdr,exp,size) (memcmp (hdr, exp, size) == 0)
+# define VALID_ELF_OSABI(osabi) (osabi == ELFOSABI_SYSV)
+# define VALID_ELF_ABIVERSION(ver) (ver == 0)
+#endif
+ static const unsigned char expected[EI_PAD] =
+ {
+ [EI_MAG0] = ELFMAG0,
+ [EI_MAG1] = ELFMAG1,
+ [EI_MAG2] = ELFMAG2,
+ [EI_MAG3] = ELFMAG3,
+ [EI_CLASS] = ELFW(CLASS),
+ [EI_DATA] = byteorder,
+ [EI_VERSION] = EV_CURRENT,
+ [EI_OSABI] = ELFOSABI_SYSV,
+ [EI_ABIVERSION] = 0
+ };
+ static const struct
+ {
+ ElfW(Word) vendorlen;
+ ElfW(Word) datalen;
+ ElfW(Word) type;
+ char vendor[4];
+ } expected_note = { 4, 16, 1, "GNU" };
+ /* Initialize it to make the compiler happy. */
+ const char *errstring = NULL;
+ int errval = 0;
+
+#ifdef SHARED
+ /* Give the auditing libraries a chance. */
+ if (__builtin_expect (GLRO(dl_naudit) > 0, 0) && whatcode != 0
+ && loader->l_auditing == 0)
+ {
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->objsearch != NULL)
+ {
+ name = afct->objsearch (name, &loader->l_audit[cnt].cookie,
+ whatcode);
+ if (name == NULL)
+ /* Ignore the path. */
+ return -1;
+ }
+
+ afct = afct->next;
+ }
+ }
+#endif
+
+ /* Open the file. We always open files read-only. */
+ int fd = __open (name, O_RDONLY);
+ if (fd != -1)
+ {
+ ElfW(Ehdr) *ehdr;
+ ElfW(Phdr) *phdr, *ph;
+ ElfW(Word) *abi_note, abi_note_buf[8];
+ unsigned int osversion;
+ size_t maplength;
+
+ /* We successfully openened the file. Now verify it is a file
+ we can use. */
+ __set_errno (0);
+ fbp->len = __libc_read (fd, fbp->buf, sizeof (fbp->buf));
+
+ /* This is where the ELF header is loaded. */
+ assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr)));
+ ehdr = (ElfW(Ehdr) *) fbp->buf;
+
+ /* Now run the tests. */
+ if (__builtin_expect (fbp->len < (ssize_t) sizeof (ElfW(Ehdr)), 0))
+ {
+ errval = errno;
+ errstring = (errval == 0
+ ? N_("file too short") : N_("cannot read file data"));
+ call_lose:
+ if (free_name)
+ {
+ char *realname = (char *) name;
+ name = strdupa (realname);
+ free (realname);
+ }
+ lose (errval, fd, name, NULL, NULL, errstring, NULL);
+ }
+
+ /* See whether the ELF header is what we expect. */
+ if (__builtin_expect (! VALID_ELF_HEADER (ehdr->e_ident, expected,
+ EI_PAD), 0))
+ {
+ /* Something is wrong. */
+ if (*(Elf32_Word *) &ehdr->e_ident !=
+#if BYTE_ORDER == LITTLE_ENDIAN
+ ((ELFMAG0 << (EI_MAG0 * 8)) |
+ (ELFMAG1 << (EI_MAG1 * 8)) |
+ (ELFMAG2 << (EI_MAG2 * 8)) |
+ (ELFMAG3 << (EI_MAG3 * 8)))
+#else
+ ((ELFMAG0 << (EI_MAG3 * 8)) |
+ (ELFMAG1 << (EI_MAG2 * 8)) |
+ (ELFMAG2 << (EI_MAG1 * 8)) |
+ (ELFMAG3 << (EI_MAG0 * 8)))
+#endif
+ )
+ errstring = N_("invalid ELF header");
+ else if (ehdr->e_ident[EI_CLASS] != ELFW(CLASS))
+ {
+ /* This is not a fatal error. On architectures where
+ 32-bit and 64-bit binaries can be run this might
+ happen. */
+ *found_other_class = true;
+ goto close_and_out;
+ }
+ else if (ehdr->e_ident[EI_DATA] != byteorder)
+ {
+ if (BYTE_ORDER == BIG_ENDIAN)
+ errstring = N_("ELF file data encoding not big-endian");
+ else
+ errstring = N_("ELF file data encoding not little-endian");
+ }
+ else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT)
+ errstring
+ = N_("ELF file version ident does not match current one");
+ /* XXX We should be able so set system specific versions which are
+ allowed here. */
+ else if (!VALID_ELF_OSABI (ehdr->e_ident[EI_OSABI]))
+ errstring = N_("ELF file OS ABI invalid");
+ else if (!VALID_ELF_ABIVERSION (ehdr->e_ident[EI_ABIVERSION]))
+ errstring = N_("ELF file ABI version invalid");
+ else
+ /* Otherwise we don't know what went wrong. */
+ errstring = N_("internal error");
+
+ goto call_lose;
+ }
+
+ if (__builtin_expect (ehdr->e_version, EV_CURRENT) != EV_CURRENT)
+ {
+ errstring = N_("ELF file version does not match current one");
+ goto call_lose;
+ }
+ if (! __builtin_expect (elf_machine_matches_host (ehdr), 1))
+ goto close_and_out;
+ else if (__builtin_expect (ehdr->e_type, ET_DYN) != ET_DYN
+ && __builtin_expect (ehdr->e_type, ET_EXEC) != ET_EXEC)
+ {
+ errstring = N_("only ET_DYN and ET_EXEC can be loaded");
+ goto call_lose;
+ }
+ else if (__builtin_expect (ehdr->e_phentsize, sizeof (ElfW(Phdr)))
+ != sizeof (ElfW(Phdr)))
+ {
+ errstring = N_("ELF file's phentsize not the expected size");
+ goto call_lose;
+ }
+
+ maplength = ehdr->e_phnum * sizeof (ElfW(Phdr));
+ if (ehdr->e_phoff + maplength <= (size_t) fbp->len)
+ phdr = (void *) (fbp->buf + ehdr->e_phoff);
+ else
+ {
+ phdr = alloca (maplength);
+ __lseek (fd, ehdr->e_phoff, SEEK_SET);
+ if ((size_t) __libc_read (fd, (void *) phdr, maplength) != maplength)
+ {
+ read_error:
+ errval = errno;
+ errstring = N_("cannot read file data");
+ goto call_lose;
+ }
+ }
+
+ /* Check .note.ABI-tag if present. */
+ for (ph = phdr; ph < &phdr[ehdr->e_phnum]; ++ph)
+ if (ph->p_type == PT_NOTE && ph->p_filesz == 32 && ph->p_align >= 4)
+ {
+ if (ph->p_offset + 32 <= (size_t) fbp->len)
+ abi_note = (void *) (fbp->buf + ph->p_offset);
+ else
+ {
+ __lseek (fd, ph->p_offset, SEEK_SET);
+ if (__libc_read (fd, (void *) abi_note_buf, 32) != 32)
+ goto read_error;
+
+ abi_note = abi_note_buf;
+ }
+
+ if (memcmp (abi_note, &expected_note, sizeof (expected_note)))
+ continue;
+
+ osversion = (abi_note[5] & 0xff) * 65536
+ + (abi_note[6] & 0xff) * 256
+ + (abi_note[7] & 0xff);
+ if (abi_note[4] != __ABI_TAG_OS
+ || (GLRO(dl_osversion) && GLRO(dl_osversion) < osversion))
+ {
+ close_and_out:
+ __close (fd);
+ __set_errno (ENOENT);
+ fd = -1;
+ }
+
+ break;
+ }
+ }
+
+ return fd;
+}
+
+/* Try to open NAME in one of the directories in *DIRSP.
+ Return the fd, or -1. If successful, fill in *REALNAME
+ with the malloc'd full directory name. If it turns out
+ that none of the directories in *DIRSP exists, *DIRSP is
+ replaced with (void *) -1, and the old value is free()d
+ if MAY_FREE_DIRS is true. */
+
+static int
+open_path (const char *name, size_t namelen, int preloaded,
+ struct r_search_path_struct *sps, char **realname,
+ struct filebuf *fbp, struct link_map *loader, int whatcode,
+ bool *found_other_class)
+{
+ struct r_search_path_elem **dirs = sps->dirs;
+ char *buf;
+ int fd = -1;
+ const char *current_what = NULL;
+ int any = 0;
+
+ if (__builtin_expect (dirs == NULL, 0))
+ /* We're called before _dl_init_paths when loading the main executable
+ given on the command line when rtld is run directly. */
+ return -1;
+
+ buf = alloca (max_dirnamelen + max_capstrlen + namelen);
+ do
+ {
+ struct r_search_path_elem *this_dir = *dirs;
+ size_t buflen = 0;
+ size_t cnt;
+ char *edp;
+ int here_any = 0;
+ int err;
+
+ /* If we are debugging the search for libraries print the path
+ now if it hasn't happened now. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)
+ && current_what != this_dir->what)
+ {
+ current_what = this_dir->what;
+ print_search_path (dirs, current_what, this_dir->where);
+ }
+
+ edp = (char *) __mempcpy (buf, this_dir->dirname, this_dir->dirnamelen);
+ for (cnt = 0; fd == -1 && cnt < ncapstr; ++cnt)
+ {
+ /* Skip this directory if we know it does not exist. */
+ if (this_dir->status[cnt] == nonexisting)
+ continue;
+
+ buflen =
+ ((char *) __mempcpy (__mempcpy (edp, capstr[cnt].str,
+ capstr[cnt].len),
+ name, namelen)
+ - buf);
+
+ /* Print name we try if this is wanted. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
+ _dl_debug_printf (" trying file=%s\n", buf);
+
+ fd = open_verify (buf, fbp, loader, whatcode, found_other_class,
+ false);
+ if (this_dir->status[cnt] == unknown)
+ {
+ if (fd != -1)
+ this_dir->status[cnt] = existing;
+ /* Do not update the directory information when loading
+ auditing code. We must try to disturb the program as
+ little as possible. */
+ else if (loader == NULL
+ || GL(dl_ns)[loader->l_ns]._ns_loaded->l_auditing == 0)
+ {
+ /* We failed to open machine dependent library. Let's
+ test whether there is any directory at all. */
+ struct stat64 st;
+
+ buf[buflen - namelen - 1] = '\0';
+
+ if (__xstat64 (_STAT_VER, buf, &st) != 0
+ || ! S_ISDIR (st.st_mode))
+ /* The directory does not exist or it is no directory. */
+ this_dir->status[cnt] = nonexisting;
+ else
+ this_dir->status[cnt] = existing;
+ }
+ }
+
+ /* Remember whether we found any existing directory. */
+ here_any |= this_dir->status[cnt] != nonexisting;
+
+ if (fd != -1 && __builtin_expect (preloaded, 0)
+ && INTUSE(__libc_enable_secure))
+ {
+ /* This is an extra security effort to make sure nobody can
+ preload broken shared objects which are in the trusted
+ directories and so exploit the bugs. */
+ struct stat64 st;
+
+ if (__fxstat64 (_STAT_VER, fd, &st) != 0
+ || (st.st_mode & S_ISUID) == 0)
+ {
+ /* The shared object cannot be tested for being SUID
+ or this bit is not set. In this case we must not
+ use this object. */
+ __close (fd);
+ fd = -1;
+ /* We simply ignore the file, signal this by setting
+ the error value which would have been set by `open'. */
+ errno = ENOENT;
+ }
+ }
+ }
+
+ if (fd != -1)
+ {
+ *realname = (char *) malloc (buflen);
+ if (*realname != NULL)
+ {
+ memcpy (*realname, buf, buflen);
+ return fd;
+ }
+ else
+ {
+ /* No memory for the name, we certainly won't be able
+ to load and link it. */
+ __close (fd);
+ return -1;
+ }
+ }
+ if (here_any && (err = errno) != ENOENT && err != EACCES)
+ /* The file exists and is readable, but something went wrong. */
+ return -1;
+
+ /* Remember whether we found anything. */
+ any |= here_any;
+ }
+ while (*++dirs != NULL);
+
+ /* Remove the whole path if none of the directories exists. */
+ if (__builtin_expect (! any, 0))
+ {
+ /* Paths which were allocated using the minimal malloc() in ld.so
+ must not be freed using the general free() in libc. */
+ if (sps->malloced)
+ free (sps->dirs);
+#ifdef HAVE_Z_RELRO
+ /* rtld_search_dirs is attribute_relro, therefore avoid writing
+ into it. */
+ if (sps != &rtld_search_dirs)
+#endif
+ sps->dirs = (void *) -1;
+ }
+
+ return -1;
+}
+
+/* Map in the shared object file NAME. */
+
+struct link_map *
+internal_function
+_dl_map_object (struct link_map *loader, const char *name, int preloaded,
+ int type, int trace_mode, int mode, Lmid_t nsid)
+{
+ int fd;
+ char *realname;
+ char *name_copy;
+ struct link_map *l;
+ struct filebuf fb;
+
+ assert (nsid >= 0);
+ assert (nsid < DL_NNS);
+
+ /* Look for this name among those already loaded. */
+ for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
+ {
+ /* If the requested name matches the soname of a loaded object,
+ use that object. Elide this check for names that have not
+ yet been opened. */
+ if (__builtin_expect (l->l_faked, 0) != 0
+ || __builtin_expect (l->l_removed, 0) != 0)
+ continue;
+ if (!_dl_name_match_p (name, l))
+ {
+ const char *soname;
+
+ if (__builtin_expect (l->l_soname_added, 1)
+ || l->l_info[DT_SONAME] == NULL)
+ continue;
+
+ soname = ((const char *) D_PTR (l, l_info[DT_STRTAB])
+ + l->l_info[DT_SONAME]->d_un.d_val);
+ if (strcmp (name, soname) != 0)
+ continue;
+
+ /* We have a match on a new name -- cache it. */
+ add_name_to_object (l, soname);
+ l->l_soname_added = 1;
+ }
+
+ /* We have a match. */
+ return l;
+ }
+
+ /* Display information if we are debugging. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)
+ && loader != NULL)
+ _dl_debug_printf ("\nfile=%s [%lu]; needed by %s [%lu]\n", name, nsid,
+ loader->l_name[0]
+ ? loader->l_name : rtld_progname, loader->l_ns);
+
+#ifdef SHARED
+ /* Give the auditing libraries a chance to change the name before we
+ try anything. */
+ if (__builtin_expect (GLRO(dl_naudit) > 0, 0)
+ && (loader == NULL || loader->l_auditing == 0))
+ {
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->objsearch != NULL)
+ {
+ name = afct->objsearch (name, &loader->l_audit[cnt].cookie,
+ LA_SER_ORIG);
+ if (name == NULL)
+ {
+ /* Do not try anything further. */
+ fd = -1;
+ goto no_file;
+ }
+ }
+
+ afct = afct->next;
+ }
+ }
+#endif
+
+ /* Will be true if we found a DSO which is of the other ELF class. */
+ bool found_other_class = false;
+
+ if (strchr (name, '/') == NULL)
+ {
+ /* Search for NAME in several places. */
+
+ size_t namelen = strlen (name) + 1;
+
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
+ _dl_debug_printf ("find library=%s [%lu]; searching\n", name, nsid);
+
+ fd = -1;
+
+ /* When the object has the RUNPATH information we don't use any
+ RPATHs. */
+ if (loader == NULL || loader->l_info[DT_RUNPATH] == NULL)
+ {
+ /* This is the executable's map (if there is one). Make sure that
+ we do not look at it twice. */
+ struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+ bool did_main_map = false;
+
+ /* First try the DT_RPATH of the dependent object that caused NAME
+ to be loaded. Then that object's dependent, and on up. */
+ for (l = loader; l; l = l->l_loader)
+ if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH"))
+ {
+ fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs,
+ &realname, &fb, loader, LA_SER_RUNPATH,
+ &found_other_class);
+ if (fd != -1)
+ break;
+
+ did_main_map |= l == main_map;
+ }
+
+ /* If dynamically linked, try the DT_RPATH of the executable
+ itself. NB: we do this for lookups in any namespace. */
+ if (fd == -1 && !did_main_map
+ && main_map != NULL && main_map->l_type != lt_loaded
+ && cache_rpath (main_map, &main_map->l_rpath_dirs, DT_RPATH,
+ "RPATH"))
+ fd = open_path (name, namelen, preloaded, &main_map->l_rpath_dirs,
+ &realname, &fb, loader ?: main_map, LA_SER_RUNPATH,
+ &found_other_class);
+ }
+
+ /* Try the LD_LIBRARY_PATH environment variable. */
+ if (fd == -1 && env_path_list.dirs != (void *) -1)
+ fd = open_path (name, namelen, preloaded, &env_path_list,
+ &realname, &fb,
+ loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded,
+ LA_SER_LIBPATH, &found_other_class);
+
+ /* Look at the RUNPATH information for this binary. */
+ if (fd == -1 && loader != NULL
+ && cache_rpath (loader, &loader->l_runpath_dirs,
+ DT_RUNPATH, "RUNPATH"))
+ fd = open_path (name, namelen, preloaded,
+ &loader->l_runpath_dirs, &realname, &fb, loader,
+ LA_SER_RUNPATH, &found_other_class);
+
+ if (fd == -1
+ && (__builtin_expect (! preloaded, 1)
+ || ! INTUSE(__libc_enable_secure)))
+ {
+ /* Check the list of libraries in the file /etc/ld.so.cache,
+ for compatibility with Linux's ldconfig program. */
+ const char *cached = _dl_load_cache_lookup (name);
+
+ if (cached != NULL)
+ {
+#ifdef SHARED
+ // XXX Correct to unconditionally default to namespace 0?
+ l = loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+#else
+ l = loader;
+#endif
+
+ /* If the loader has the DF_1_NODEFLIB flag set we must not
+ use a cache entry from any of these directories. */
+ if (
+#ifndef SHARED
+ /* 'l' is always != NULL for dynamically linked objects. */
+ l != NULL &&
+#endif
+ __builtin_expect (l->l_flags_1 & DF_1_NODEFLIB, 0))
+ {
+ const char *dirp = system_dirs;
+ unsigned int cnt = 0;
+
+ do
+ {
+ if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0)
+ {
+ /* The prefix matches. Don't use the entry. */
+ cached = NULL;
+ break;
+ }
+
+ dirp += system_dirs_len[cnt] + 1;
+ ++cnt;
+ }
+ while (cnt < nsystem_dirs_len);
+ }
+
+ if (cached != NULL)
+ {
+ fd = open_verify (cached,
+ &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
+ LA_SER_CONFIG, &found_other_class, false);
+ if (__builtin_expect (fd != -1, 1))
+ {
+ realname = local_strdup (cached);
+ if (realname == NULL)
+ {
+ __close (fd);
+ fd = -1;
+ }
+ }
+ }
+ }
+ }
+
+ /* Finally, try the default path. */
+ if (fd == -1
+ && ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL
+ || __builtin_expect (!(l->l_flags_1 & DF_1_NODEFLIB), 1))
+ && rtld_search_dirs.dirs != (void *) -1)
+ fd = open_path (name, namelen, preloaded, &rtld_search_dirs,
+ &realname, &fb, l, LA_SER_DEFAULT, &found_other_class);
+
+ /* Add another newline when we are tracing the library loading. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
+ _dl_debug_printf ("\n");
+ }
+ else
+ {
+ /* The path may contain dynamic string tokens. */
+ realname = (loader
+ ? expand_dynamic_string_token (loader, name)
+ : local_strdup (name));
+ if (realname == NULL)
+ fd = -1;
+ else
+ {
+ fd = open_verify (realname, &fb,
+ loader ?: GL(dl_ns)[nsid]._ns_loaded, 0,
+ &found_other_class, true);
+ if (__builtin_expect (fd, 0) == -1)
+ free (realname);
+ }
+ }
+
+#ifdef SHARED
+ no_file:
+#endif
+ /* In case the LOADER information has only been provided to get to
+ the appropriate RUNPATH/RPATH information we do not need it
+ anymore. */
+ if (mode & __RTLD_CALLMAP)
+ loader = NULL;
+
+ if (__builtin_expect (fd, 0) == -1)
+ {
+ if (trace_mode
+ && __builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK, 0) == 0)
+ {
+ /* We haven't found an appropriate library. But since we
+ are only interested in the list of libraries this isn't
+ so severe. Fake an entry with all the information we
+ have. */
+ static const Elf_Symndx dummy_bucket = STN_UNDEF;
+
+ /* Enter the new object in the list of loaded objects. */
+ if ((name_copy = local_strdup (name)) == NULL
+ || (l = _dl_new_object (name_copy, name, type, loader,
+ mode, nsid)) == NULL)
+ {
+ free (name_copy);
+ _dl_signal_error (ENOMEM, name, NULL,
+ N_("cannot create shared object descriptor"));
+ }
+ /* Signal that this is a faked entry. */
+ l->l_faked = 1;
+ /* Since the descriptor is initialized with zero we do not
+ have do this here.
+ l->l_reserved = 0; */
+ l->l_buckets = &dummy_bucket;
+ l->l_nbuckets = 1;
+ l->l_relocated = 1;
+
+ return l;
+ }
+ else if (found_other_class)
+ _dl_signal_error (0, name, NULL,
+ ELFW(CLASS) == ELFCLASS32
+ ? N_("wrong ELF class: ELFCLASS64")
+ : N_("wrong ELF class: ELFCLASS32"));
+ else
+ _dl_signal_error (errno, name, NULL,
+ N_("cannot open shared object file"));
+ }
+
+ void *stack_end = __libc_stack_end;
+ return _dl_map_object_from_fd (name, fd, &fb, realname, loader, type, mode,
+ &stack_end, nsid);
+}
+
+
+void
+internal_function
+_dl_rtld_di_serinfo (struct link_map *loader, Dl_serinfo *si, bool counting)
+{
+ if (counting)
+ {
+ si->dls_cnt = 0;
+ si->dls_size = 0;
+ }
+
+ unsigned int idx = 0;
+ char *allocptr = (char *) &si->dls_serpath[si->dls_cnt];
+ void add_path (const struct r_search_path_struct *sps, unsigned int flags)
+# define add_path(sps, flags) add_path(sps, 0) /* XXX */
+ {
+ if (sps->dirs != (void *) -1)
+ {
+ struct r_search_path_elem **dirs = sps->dirs;
+ do
+ {
+ const struct r_search_path_elem *const r = *dirs++;
+ if (counting)
+ {
+ si->dls_cnt++;
+ si->dls_size += r->dirnamelen;
+ }
+ else
+ {
+ Dl_serpath *const sp = &si->dls_serpath[idx++];
+ sp->dls_name = allocptr;
+ allocptr = __mempcpy (allocptr,
+ r->dirname, r->dirnamelen - 1);
+ *allocptr++ = '\0';
+ sp->dls_flags = flags;
+ }
+ }
+ while (*dirs != NULL);
+ }
+ }
+
+ /* When the object has the RUNPATH information we don't use any RPATHs. */
+ if (loader->l_info[DT_RUNPATH] == NULL)
+ {
+ /* First try the DT_RPATH of the dependent object that caused NAME
+ to be loaded. Then that object's dependent, and on up. */
+
+ struct link_map *l = loader;
+ do
+ {
+ if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH"))
+ add_path (&l->l_rpath_dirs, XXX_RPATH);
+ l = l->l_loader;
+ }
+ while (l != NULL);
+
+ /* If dynamically linked, try the DT_RPATH of the executable itself. */
+ if (loader->l_ns == LM_ID_BASE)
+ {
+ l = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+ if (l != NULL && l->l_type != lt_loaded && l != loader)
+ if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH"))
+ add_path (&l->l_rpath_dirs, XXX_RPATH);
+ }
+ }
+
+ /* Try the LD_LIBRARY_PATH environment variable. */
+ add_path (&env_path_list, XXX_ENV);
+
+ /* Look at the RUNPATH information for this binary. */
+ if (cache_rpath (loader, &loader->l_runpath_dirs, DT_RUNPATH, "RUNPATH"))
+ add_path (&loader->l_runpath_dirs, XXX_RUNPATH);
+
+ /* XXX
+ Here is where ld.so.cache gets checked, but we don't have
+ a way to indicate that in the results for Dl_serinfo. */
+
+ /* Finally, try the default path. */
+ if (!(loader->l_flags_1 & DF_1_NODEFLIB))
+ add_path (&rtld_search_dirs, XXX_default);
+
+ if (counting)
+ /* Count the struct size before the string area, which we didn't
+ know before we completed dls_cnt. */
+ si->dls_size += (char *) &si->dls_serpath[si->dls_cnt] - (char *) si;
+}
diff --git a/libc/elf/dl-lookup.c b/libc/elf/dl-lookup.c
new file mode 100644
index 000000000..7cfcc620a
--- /dev/null
+++ b/libc/elf/dl-lookup.c
@@ -0,0 +1,484 @@
+/* Look up a symbol in the loaded objects.
+ Copyright (C) 1995-2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <alloca.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <dl-hash.h>
+#include <dl-machine.h>
+#include <bits/libc-lock.h>
+#include <tls.h>
+
+#include <assert.h>
+
+#define VERSTAG(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (tag))
+
+/* We need this string more than once. */
+static const char undefined_msg[] = "undefined symbol: ";
+
+
+struct sym_val
+ {
+ const ElfW(Sym) *s;
+ struct link_map *m;
+ };
+
+
+#define make_string(string, rest...) \
+ ({ \
+ const char *all[] = { string, ## rest }; \
+ size_t len, cnt; \
+ char *result, *cp; \
+ \
+ len = 1; \
+ for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
+ len += strlen (all[cnt]); \
+ \
+ cp = result = alloca (len); \
+ for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
+ cp = __stpcpy (cp, all[cnt]); \
+ \
+ result; \
+ })
+
+/* Statistics function. */
+#ifdef SHARED
+# define bump_num_relocations() ++GL(dl_num_relocations)
+#else
+# define bump_num_relocations() ((void) 0)
+#endif
+
+
+/* The actual lookup code. */
+#include "do-lookup.h"
+
+
+static uint_fast32_t
+dl_new_hash (const char *s)
+{
+ uint_fast32_t h = 5381;
+ for (unsigned char c = *s; c != '\0'; c = *++s)
+ h = h * 33 + c;
+ return h & 0xffffffff;
+}
+
+
+/* Add extra dependency on MAP to UNDEF_MAP. */
+static int
+internal_function
+add_dependency (struct link_map *undef_map, struct link_map *map)
+{
+ struct link_map **list;
+ struct link_map *runp;
+ unsigned int act;
+ unsigned int i;
+ int result = 0;
+
+ /* Avoid self-references and references to objects which cannot be
+ unloaded anyway. */
+ if (undef_map == map)
+ return 0;
+
+ /* Make sure nobody can unload the object while we are at it. */
+ __rtld_lock_lock_recursive (GL(dl_load_lock));
+
+ /* Avoid references to objects which cannot be unloaded anyway. */
+ if (map->l_type != lt_loaded
+ || (map->l_flags_1 & DF_1_NODELETE) != 0)
+ goto out;
+
+ /* If the object with the undefined reference cannot be removed ever
+ just make sure the same is true for the object which contains the
+ definition. */
+ if (undef_map->l_type != lt_loaded
+ || (undef_map->l_flags_1 & DF_1_NODELETE) != 0)
+ {
+ map->l_flags_1 |= DF_1_NODELETE;
+ goto out;
+ }
+
+ /* Determine whether UNDEF_MAP already has a reference to MAP. First
+ look in the normal dependencies. */
+ if (undef_map->l_initfini != NULL)
+ {
+ list = undef_map->l_initfini;
+
+ for (i = 0; list[i] != NULL; ++i)
+ if (list[i] == map)
+ goto out;
+ }
+
+ /* No normal dependency. See whether we already had to add it
+ to the special list of dynamic dependencies. */
+ list = undef_map->l_reldeps;
+ act = undef_map->l_reldepsact;
+
+ for (i = 0; i < act; ++i)
+ if (list[i] == map)
+ goto out;
+
+ /* The object is not yet in the dependency list. Before we add
+ it make sure just one more time the object we are about to
+ reference is still available. There is a brief period in
+ which the object could have been removed since we found the
+ definition. */
+ runp = GL(dl_ns)[undef_map->l_ns]._ns_loaded;
+ while (runp != NULL && runp != map)
+ runp = runp->l_next;
+
+ if (runp != NULL)
+ {
+ /* The object is still available. Add the reference now. */
+ if (__builtin_expect (act >= undef_map->l_reldepsmax, 0))
+ {
+ /* Allocate more memory for the dependency list. Since this
+ can never happen during the startup phase we can use
+ `realloc'. */
+ void *newp;
+
+ undef_map->l_reldepsmax += 5;
+ newp = realloc (undef_map->l_reldeps,
+ undef_map->l_reldepsmax
+ * sizeof (struct link_map *));
+
+ if (__builtin_expect (newp != NULL, 1))
+ undef_map->l_reldeps = (struct link_map **) newp;
+ else
+ /* Correct the addition. */
+ undef_map->l_reldepsmax -= 5;
+ }
+
+ /* If we didn't manage to allocate memory for the list this is
+ no fatal mistake. We simply increment the use counter of the
+ referenced object and don't record the dependencies. This
+ means this increment can never be reverted and the object
+ will never be unloaded. This is semantically the correct
+ behavior. */
+ if (__builtin_expect (act < undef_map->l_reldepsmax, 1))
+ undef_map->l_reldeps[undef_map->l_reldepsact++] = map;
+
+ /* Display information if we are debugging. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0))
+ _dl_debug_printf ("\
+\nfile=%s [%lu]; needed by %s [%lu] (relocation dependency)\n\n",
+ map->l_name[0] ? map->l_name : rtld_progname,
+ map->l_ns,
+ undef_map->l_name[0]
+ ? undef_map->l_name : rtld_progname,
+ undef_map->l_ns);
+ }
+ else
+ /* Whoa, that was bad luck. We have to search again. */
+ result = -1;
+
+ out:
+ /* Release the lock. */
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
+
+ return result;
+}
+
+static void
+internal_function
+_dl_debug_bindings (const char *undef_name, struct link_map *undef_map,
+ const ElfW(Sym) **ref, struct r_scope_elem *symbol_scope[],
+ struct sym_val *value,
+ const struct r_found_version *version, int type_class,
+ int protected);
+
+
+/* Search loaded objects' symbol tables for a definition of the symbol
+ UNDEF_NAME, perhaps with a requested version for the symbol. */
+lookup_t
+internal_function
+_dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
+ const ElfW(Sym) **ref,
+ struct r_scope_elem *symbol_scope[],
+ const struct r_found_version *version,
+ int type_class, int flags, struct link_map *skip_map)
+{
+ const uint_fast32_t new_hash = dl_new_hash (undef_name);
+ unsigned long int old_hash = 0xffffffff;
+ struct sym_val current_value = { NULL, NULL };
+ struct r_scope_elem **scope = symbol_scope;
+
+ bump_num_relocations ();
+
+ /* No other flag than DL_LOOKUP_ADD_DEPENDENCY is allowed if we look
+ up a versioned symbol. */
+ assert (version == NULL || flags == 0 || flags == DL_LOOKUP_ADD_DEPENDENCY);
+
+ size_t i = 0;
+ if (__builtin_expect (skip_map != NULL, 0))
+ {
+ /* Search the relevant loaded objects for a definition. */
+ while ((*scope)->r_list[i] != skip_map)
+ ++i;
+
+ assert (i < (*scope)->r_nlist);
+ }
+
+ /* Search the relevant loaded objects for a definition. */
+ for (size_t start = i; *scope != NULL; start = 0, ++scope)
+ {
+ int res = do_lookup_x (undef_name, new_hash, &old_hash, *ref,
+ &current_value, *scope, start, version, flags,
+ skip_map, type_class);
+ if (res > 0)
+ break;
+
+ if (__builtin_expect (res, 0) < 0 && skip_map == NULL)
+ {
+ /* Oh, oh. The file named in the relocation entry does not
+ contain the needed symbol. This code is never reached
+ for unversioned lookups. */
+ assert (version != NULL);
+ const char *reference_name = undef_map ? undef_map->l_name : NULL;
+
+ /* XXX We cannot translate the message. */
+ _dl_signal_cerror (0, (reference_name[0]
+ ? reference_name
+ : (rtld_progname ?: "<main program>")),
+ N_("relocation error"),
+ make_string ("symbol ", undef_name, ", version ",
+ version->name,
+ " not defined in file ",
+ version->filename,
+ " with link time reference",
+ res == -2
+ ? " (no version symbols)" : ""));
+ *ref = NULL;
+ return 0;
+ }
+ }
+
+ if (__builtin_expect (current_value.s == NULL, 0))
+ {
+ if ((*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
+ && skip_map == NULL)
+ {
+ /* We could find no value for a strong reference. */
+ const char *reference_name = undef_map ? undef_map->l_name : "";
+ const char *versionstr = version ? ", version " : "";
+ const char *versionname = (version && version->name
+ ? version->name : "");
+
+ /* XXX We cannot translate the message. */
+ _dl_signal_cerror (0, (reference_name[0]
+ ? reference_name
+ : (rtld_progname ?: "<main program>")),
+ N_("symbol lookup error"),
+ make_string (undefined_msg, undef_name,
+ versionstr, versionname));
+ }
+ *ref = NULL;
+ return 0;
+ }
+
+ int protected = (*ref
+ && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED);
+ if (__builtin_expect (protected != 0, 0))
+ {
+ /* It is very tricky. We need to figure out what value to
+ return for the protected symbol. */
+ if (type_class == ELF_RTYPE_CLASS_PLT)
+ {
+ if (current_value.s != NULL && current_value.m != undef_map)
+ {
+ current_value.s = *ref;
+ current_value.m = undef_map;
+ }
+ }
+ else
+ {
+ struct sym_val protected_value = { NULL, NULL };
+
+ for (scope = symbol_scope; *scope != NULL; i = 0, ++scope)
+ if (do_lookup_x (undef_name, new_hash, &old_hash, *ref,
+ &protected_value, *scope, i, version, flags,
+ skip_map, ELF_RTYPE_CLASS_PLT) != 0)
+ break;
+
+ if (protected_value.s != NULL && protected_value.m != undef_map)
+ {
+ current_value.s = *ref;
+ current_value.m = undef_map;
+ }
+ }
+ }
+
+ /* We have to check whether this would bind UNDEF_MAP to an object
+ in the global scope which was dynamically loaded. In this case
+ we have to prevent the latter from being unloaded unless the
+ UNDEF_MAP object is also unloaded. */
+ if (__builtin_expect (current_value.m->l_type == lt_loaded, 0)
+ /* Don't do this for explicit lookups as opposed to implicit
+ runtime lookups. */
+ && (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0
+ /* Add UNDEF_MAP to the dependencies. */
+ && add_dependency (undef_map, current_value.m) < 0)
+ /* Something went wrong. Perhaps the object we tried to reference
+ was just removed. Try finding another definition. */
+ return _dl_lookup_symbol_x (undef_name, undef_map, ref,
+ symbol_scope, version, type_class,
+ flags, skip_map);
+
+ /* The object is used. */
+ current_value.m->l_used = 1;
+
+ if (__builtin_expect (GLRO(dl_debug_mask)
+ & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK), 0))
+ _dl_debug_bindings (undef_name, undef_map, ref, symbol_scope,
+ &current_value, version, type_class, protected);
+
+ *ref = current_value.s;
+ return LOOKUP_VALUE (current_value.m);
+}
+
+
+/* Cache the location of MAP's hash table. */
+
+void
+internal_function
+_dl_setup_hash (struct link_map *map)
+{
+ Elf_Symndx *hash;
+ Elf_Symndx nchain;
+
+ if (__builtin_expect (map->l_info[DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM
+ + DT_THISPROCNUM + DT_VERSIONTAGNUM
+ + DT_EXTRANUM + DT_VALNUM] != NULL, 1))
+ {
+ Elf32_Word *hash32
+ = (void *) D_PTR (map, l_info[DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM
+ + DT_THISPROCNUM + DT_VERSIONTAGNUM
+ + DT_EXTRANUM + DT_VALNUM]);
+ map->l_nbuckets = *hash32++;
+ Elf32_Word symbias = *hash32++;
+ Elf32_Word bitmask_nwords = *hash32++;
+ /* Must be a power of two. */
+ assert ((bitmask_nwords & (bitmask_nwords - 1)) == 0);
+ map->l_gnu_bitmask_idxbits = bitmask_nwords - 1;
+ map->l_gnu_shift = *hash32++;
+
+ map->l_gnu_bitmask = (ElfW(Addr) *) hash32;
+ hash32 += __ELF_NATIVE_CLASS / 32 * bitmask_nwords;
+
+ map->l_gnu_buckets = hash32;
+ hash32 += map->l_nbuckets;
+ map->l_gnu_chain_zero = hash32 - symbias;
+ return;
+ }
+
+ if (!map->l_info[DT_HASH])
+ return;
+ hash = (void *) D_PTR (map, l_info[DT_HASH]);
+
+ map->l_nbuckets = *hash++;
+ nchain = *hash++;
+ map->l_buckets = hash;
+ hash += map->l_nbuckets;
+ map->l_chain = hash;
+}
+
+
+static void
+internal_function
+_dl_debug_bindings (const char *undef_name, struct link_map *undef_map,
+ const ElfW(Sym) **ref, struct r_scope_elem *symbol_scope[],
+ struct sym_val *value,
+ const struct r_found_version *version, int type_class,
+ int protected)
+{
+ const char *reference_name = undef_map->l_name;
+
+ if (GLRO(dl_debug_mask) & DL_DEBUG_BINDINGS)
+ {
+ _dl_debug_printf ("binding file %s [%lu] to %s [%lu]: %s symbol `%s'",
+ (reference_name[0]
+ ? reference_name
+ : (rtld_progname ?: "<main program>")),
+ undef_map->l_ns,
+ value->m->l_name[0] ? value->m->l_name : rtld_progname,
+ value->m->l_ns,
+ protected ? "protected" : "normal", undef_name);
+ if (version)
+ _dl_debug_printf_c (" [%s]\n", version->name);
+ else
+ _dl_debug_printf_c ("\n");
+ }
+#ifdef SHARED
+ if (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK)
+ {
+ int conflict = 0;
+ struct sym_val val = { NULL, NULL };
+
+ if ((GLRO(dl_trace_prelink_map) == NULL
+ || GLRO(dl_trace_prelink_map) == GL(dl_ns)[LM_ID_BASE]._ns_loaded)
+ && undef_map != GL(dl_ns)[LM_ID_BASE]._ns_loaded)
+ {
+ const uint_fast32_t new_hash = dl_new_hash (undef_name);
+ unsigned long int old_hash = 0xffffffff;
+
+ do_lookup_x (undef_name, new_hash, &old_hash, *ref, &val,
+ undef_map->l_local_scope[0], 0, version, 0, NULL,
+ type_class);
+
+ if (val.s != value->s || val.m != value->m)
+ conflict = 1;
+ }
+
+# ifdef USE_TLS
+ if (value->s
+ && (__builtin_expect (ELFW(ST_TYPE) (value->s->st_info)
+ == STT_TLS, 0)))
+ type_class = 4;
+# endif
+
+ if (conflict
+ || GLRO(dl_trace_prelink_map) == undef_map
+ || GLRO(dl_trace_prelink_map) == NULL
+ || type_class == 4)
+ {
+ _dl_printf ("%s 0x%0*Zx 0x%0*Zx -> 0x%0*Zx 0x%0*Zx ",
+ conflict ? "conflict" : "lookup",
+ (int) sizeof (ElfW(Addr)) * 2,
+ (size_t) undef_map->l_map_start,
+ (int) sizeof (ElfW(Addr)) * 2,
+ (size_t) (((ElfW(Addr)) *ref) - undef_map->l_map_start),
+ (int) sizeof (ElfW(Addr)) * 2,
+ (size_t) (value->s ? value->m->l_map_start : 0),
+ (int) sizeof (ElfW(Addr)) * 2,
+ (size_t) (value->s ? value->s->st_value : 0));
+
+ if (conflict)
+ _dl_printf ("x 0x%0*Zx 0x%0*Zx ",
+ (int) sizeof (ElfW(Addr)) * 2,
+ (size_t) (val.s ? val.m->l_map_start : 0),
+ (int) sizeof (ElfW(Addr)) * 2,
+ (size_t) (val.s ? val.s->st_value : 0));
+
+ _dl_printf ("/%x %s\n", type_class, undef_name);
+ }
+ }
+#endif
+}
diff --git a/libc/elf/dl-minimal.c b/libc/elf/dl-minimal.c
new file mode 100644
index 000000000..868d3bd2e
--- /dev/null
+++ b/libc/elf/dl-minimal.c
@@ -0,0 +1,362 @@
+/* Minimal replacements for basic facilities used in the dynamic linker.
+ Copyright (C) 1995-1998,2000-2002,2004,2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <tls.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <ldsodefs.h>
+#include <stdio-common/_itoa.h>
+
+#include <assert.h>
+
+/* Minimal `malloc' allocator for use while loading shared libraries.
+ No block is ever freed. */
+
+static void *alloc_ptr, *alloc_end, *alloc_last_block;
+
+/* Declarations of global functions. */
+extern void weak_function free (void *ptr);
+extern void * weak_function realloc (void *ptr, size_t n);
+extern unsigned long int weak_function __strtoul_internal (const char *nptr,
+ char **endptr,
+ int base,
+ int group);
+extern unsigned long int weak_function strtoul (const char *nptr,
+ char **endptr, int base);
+
+
+/* Allocate an aligned memory block. */
+void * weak_function
+__libc_memalign (size_t align, size_t n)
+{
+#ifdef MAP_ANON
+#define _dl_zerofd (-1)
+#else
+ extern int _dl_zerofd;
+
+ if (_dl_zerofd == -1)
+ _dl_zerofd = _dl_sysdep_open_zero_fill ();
+#define MAP_ANON 0
+#endif
+
+ if (alloc_end == 0)
+ {
+ /* Consume any unused space in the last page of our data segment. */
+ extern int _end attribute_hidden;
+ alloc_ptr = &_end;
+ alloc_end = (void *) 0 + (((alloc_ptr - (void *) 0)
+ + GLRO(dl_pagesize) - 1)
+ & ~(GLRO(dl_pagesize) - 1));
+ }
+
+ /* Make sure the allocation pointer is ideally aligned. */
+ alloc_ptr = (void *) 0 + (((alloc_ptr - (void *) 0) + align - 1)
+ & ~(align - 1));
+
+ if (alloc_ptr + n >= alloc_end)
+ {
+ /* Insufficient space left; allocate another page. */
+ caddr_t page;
+ size_t nup = (n + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1);
+ page = __mmap (0, nup, PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, _dl_zerofd, 0);
+ assert (page != MAP_FAILED);
+ if (page != alloc_end)
+ alloc_ptr = page;
+ alloc_end = page + nup;
+ }
+
+ alloc_last_block = (void *) alloc_ptr;
+ alloc_ptr += n;
+ return alloc_last_block;
+}
+
+void * weak_function
+malloc (size_t n)
+{
+ return __libc_memalign (sizeof (double), n);
+}
+
+/* We use this function occasionally since the real implementation may
+ be optimized when it can assume the memory it returns already is
+ set to NUL. */
+void * weak_function
+calloc (size_t nmemb, size_t size)
+{
+ /* New memory from the trivial malloc above is always already cleared.
+ (We make sure that's true in the rare occasion it might not be,
+ by clearing memory in free, below.) */
+ return malloc (nmemb * size);
+}
+
+/* This will rarely be called. */
+void weak_function
+free (void *ptr)
+{
+ /* We can free only the last block allocated. */
+ if (ptr == alloc_last_block)
+ {
+ /* Since this is rare, we clear the freed block here
+ so that calloc can presume malloc returns cleared memory. */
+ memset (alloc_last_block, '\0', alloc_ptr - alloc_last_block);
+ alloc_ptr = alloc_last_block;
+ }
+}
+
+/* This is only called with the most recent block returned by malloc. */
+void * weak_function
+realloc (void *ptr, size_t n)
+{
+ void *new;
+ if (ptr == NULL)
+ return malloc (n);
+ assert (ptr == alloc_last_block);
+ alloc_ptr = alloc_last_block;
+ new = malloc (n);
+ assert (new == ptr);
+ return new;
+}
+
+/* Avoid signal frobnication in setjmp/longjmp. Keeps things smaller. */
+
+#include <setjmp.h>
+
+int weak_function
+__sigjmp_save (sigjmp_buf env, int savemask __attribute__ ((unused)))
+{
+ env[0].__mask_was_saved = 0;
+ return 0;
+}
+
+/* Define our own version of the internal function used by strerror. We
+ only provide the messages for some common errors. This avoids pulling
+ in the whole error list. */
+
+char * weak_function
+__strerror_r (int errnum, char *buf, size_t buflen)
+{
+ char *msg;
+
+ switch (errnum)
+ {
+ case ENOMEM:
+ msg = (char *) "Cannot allocate memory";
+ break;
+ case EINVAL:
+ msg = (char *) "Invalid argument";
+ break;
+ case ENOENT:
+ msg = (char *) "No such file or directory";
+ break;
+ case EPERM:
+ msg = (char *) "Operation not permitted";
+ break;
+ case EIO:
+ msg = (char *) "Input/output error";
+ break;
+ case EACCES:
+ msg = (char *) "Permission denied";
+ break;
+ default:
+ /* No need to check buffer size, all calls in the dynamic linker
+ provide enough space. */
+ buf[buflen - 1] = '\0';
+ msg = _itoa (errnum, buf + buflen - 1, 10, 0);
+ msg = memcpy (msg - (sizeof ("Error ") - 1), "Error ",
+ sizeof ("Error ") - 1);
+ break;
+ }
+
+ return msg;
+}
+
+#ifndef NDEBUG
+
+/* Define (weakly) our own assert failure function which doesn't use stdio.
+ If we are linked into the user program (-ldl), the normal __assert_fail
+ defn can override this one. */
+
+void weak_function
+__assert_fail (const char *assertion,
+ const char *file, unsigned int line, const char *function)
+{
+ _dl_fatal_printf ("\
+Inconsistency detected by ld.so: %s: %u: %s%sAssertion `%s' failed!\n",
+ file, line, function ?: "", function ? ": " : "",
+ assertion);
+
+}
+rtld_hidden_weak(__assert_fail)
+
+void weak_function
+__assert_perror_fail (int errnum,
+ const char *file, unsigned int line,
+ const char *function)
+{
+ char errbuf[400];
+ _dl_fatal_printf ("\
+Inconsistency detected by ld.so: %s: %u: %s%sUnexpected error: %s.\n",
+ file, line, function ?: "", function ? ": " : "",
+ __strerror_r (errnum, errbuf, sizeof errbuf));
+
+}
+rtld_hidden_weak (__assert_perror_fail)
+#endif
+
+unsigned long int weak_function
+__strtoul_internal (const char *nptr, char **endptr, int base, int group)
+{
+ unsigned long int result = 0;
+ long int sign = 1;
+
+ while (*nptr == ' ' || *nptr == '\t')
+ ++nptr;
+
+ if (*nptr == '-')
+ {
+ sign = -1;
+ ++nptr;
+ }
+ else if (*nptr == '+')
+ ++nptr;
+
+ if (*nptr < '0' || *nptr > '9')
+ {
+ if (endptr != NULL)
+ *endptr = (char *) nptr;
+ return 0UL;
+ }
+
+ assert (base == 0);
+ base = 10;
+ if (*nptr == '0')
+ {
+ if (nptr[1] == 'x' || nptr[1] == 'X')
+ {
+ base = 16;
+ nptr += 2;
+ }
+ else
+ base = 8;
+ }
+
+ while (*nptr >= '0' && *nptr <= '9')
+ {
+ unsigned long int digval = *nptr - '0';
+ if (result > LONG_MAX / 10
+ || (result == ULONG_MAX / 10 && digval > ULONG_MAX % 10))
+ {
+ errno = ERANGE;
+ if (endptr != NULL)
+ *endptr = (char *) nptr;
+ return ULONG_MAX;
+ }
+ result *= base;
+ result += digval;
+ ++nptr;
+ }
+
+ if (endptr != NULL)
+ *endptr = (char *) nptr;
+ return result * sign;
+}
+
+
+/* We always use _itoa instead of _itoa_word in ld.so since the former
+ also has to be present and it is never about speed when these
+ functions are used. */
+char *
+_itoa (value, buflim, base, upper_case)
+ unsigned long long int value;
+ char *buflim;
+ unsigned int base;
+ int upper_case;
+{
+ extern const char INTUSE(_itoa_lower_digits)[] attribute_hidden;
+
+ assert (! upper_case);
+
+ do
+ *--buflim = INTUSE(_itoa_lower_digits)[value % base];
+ while ((value /= base) != 0);
+
+ return buflim;
+}
+
+
+/* The following is not a complete strsep implementation. It cannot
+ handle empty delimiter strings. But this isn't necessary for the
+ execution of ld.so. */
+#undef strsep
+#undef __strsep
+char *
+__strsep (char **stringp, const char *delim)
+{
+ char *begin;
+
+ assert (delim[0] != '\0');
+
+ begin = *stringp;
+ if (begin != NULL)
+ {
+ char *end = begin;
+
+ while (*end != '\0' || (end = NULL))
+ {
+ const char *dp = delim;
+
+ do
+ if (*dp == *end)
+ break;
+ while (*++dp != '\0');
+
+ if (*dp != '\0')
+ {
+ *end++ = '\0';
+ break;
+ }
+
+ ++end;
+ }
+
+ *stringp = end;
+ }
+
+ return begin;
+}
+weak_alias (__strsep, strsep)
+strong_alias (__strsep, __strsep_g)
+
+void
+__attribute__ ((noreturn))
+__chk_fail (void)
+{
+ _exit (127);
+}
+rtld_hidden_def (__chk_fail)
+
+/* The '_itoa_lower_digits' variable in libc.so is able to handle bases
+ up to 36. We don't need this here. */
+const char INTUSE(_itoa_lower_digits)[16] attribute_hidden
+ = "0123456789abcdef";
diff --git a/libc/elf/dl-misc.c b/libc/elf/dl-misc.c
new file mode 100644
index 000000000..6da1e2e4a
--- /dev/null
+++ b/libc/elf/dl-misc.c
@@ -0,0 +1,325 @@
+/* Miscellaneous support functions for dynamic linker
+ Copyright (C) 1997-2002, 2003, 2004, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <fcntl.h>
+#include <ldsodefs.h>
+#include <limits.h>
+#include <link.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sysdep.h>
+#include <stdio-common/_itoa.h>
+#include <bits/libc-lock.h>
+
+#ifndef MAP_ANON
+/* This is the only dl-sysdep.c function that is actually needed at run-time
+ by _dl_map_object. */
+
+int
+_dl_sysdep_open_zero_fill (void)
+{
+ return __open ("/dev/zero", O_RDONLY);
+}
+#endif
+
+/* Read the whole contents of FILE into new mmap'd space with given
+ protections. *SIZEP gets the size of the file. On error MAP_FAILED
+ is returned. */
+
+void *
+internal_function
+_dl_sysdep_read_whole_file (const char *file, size_t *sizep, int prot)
+{
+ void *result = MAP_FAILED;
+ struct stat64 st;
+ int fd = __open (file, O_RDONLY);
+ if (fd >= 0)
+ {
+ if (__fxstat64 (_STAT_VER, fd, &st) >= 0)
+ {
+ *sizep = st.st_size;
+
+ /* No need to map the file if it is empty. */
+ if (*sizep != 0)
+ /* Map a copy of the file contents. */
+ result = __mmap (NULL, *sizep, prot,
+#ifdef MAP_COPY
+ MAP_COPY
+#else
+ MAP_PRIVATE
+#endif
+#ifdef MAP_FILE
+ | MAP_FILE
+#endif
+ , fd, 0);
+ }
+ __close (fd);
+ }
+ return result;
+}
+
+
+/* Bare-bones printf implementation. This function only knows about
+ the formats and flags needed and can handle only up to 64 stripes in
+ the output. */
+static void
+_dl_debug_vdprintf (int fd, int tag_p, const char *fmt, va_list arg)
+{
+# define NIOVMAX 64
+ struct iovec iov[NIOVMAX];
+ int niov = 0;
+ pid_t pid = 0;
+ char pidbuf[12];
+
+ while (*fmt != '\0')
+ {
+ const char *startp = fmt;
+
+ if (tag_p > 0)
+ {
+ /* Generate the tag line once. It consists of the PID and a
+ colon followed by a tab. */
+ if (pid == 0)
+ {
+ char *p;
+ pid = __getpid ();
+ assert (pid >= 0 && sizeof (pid_t) <= 4);
+ p = _itoa (pid, &pidbuf[10], 10, 0);
+ while (p > pidbuf)
+ *--p = ' ';
+ pidbuf[10] = ':';
+ pidbuf[11] = '\t';
+ }
+
+ /* Append to the output. */
+ assert (niov < NIOVMAX);
+ iov[niov].iov_len = 12;
+ iov[niov++].iov_base = pidbuf;
+
+ /* No more tags until we see the next newline. */
+ tag_p = -1;
+ }
+
+ /* Skip everything except % and \n (if tags are needed). */
+ while (*fmt != '\0' && *fmt != '%' && (! tag_p || *fmt != '\n'))
+ ++fmt;
+
+ /* Append constant string. */
+ assert (niov < NIOVMAX);
+ if ((iov[niov].iov_len = fmt - startp) != 0)
+ iov[niov++].iov_base = (char *) startp;
+
+ if (*fmt == '%')
+ {
+ /* It is a format specifier. */
+ char fill = ' ';
+ int width = -1;
+ int prec = -1;
+#if LONG_MAX != INT_MAX
+ int long_mod = 0;
+#endif
+
+ /* Recognize zero-digit fill flag. */
+ if (*++fmt == '0')
+ {
+ fill = '0';
+ ++fmt;
+ }
+
+ /* See whether with comes from a parameter. Note that no other
+ way to specify the width is implemented. */
+ if (*fmt == '*')
+ {
+ width = va_arg (arg, int);
+ ++fmt;
+ }
+
+ /* Handle precision. */
+ if (*fmt == '.' && fmt[1] == '*')
+ {
+ prec = va_arg (arg, int);
+ fmt += 2;
+ }
+
+ /* Recognize the l modifier. It is only important on some
+ platforms where long and int have a different size. We
+ can use the same code for size_t. */
+ if (*fmt == 'l' || *fmt == 'Z')
+ {
+#if LONG_MAX != INT_MAX
+ long_mod = 1;
+#endif
+ ++fmt;
+ }
+
+ switch (*fmt)
+ {
+ /* Integer formatting. */
+ case 'u':
+ case 'x':
+ {
+ /* We have to make a difference if long and int have a
+ different size. */
+#if LONG_MAX != INT_MAX
+ unsigned long int num = (long_mod
+ ? va_arg (arg, unsigned long int)
+ : va_arg (arg, unsigned int));
+#else
+ unsigned long int num = va_arg (arg, unsigned int);
+#endif
+ /* We use alloca() to allocate the buffer with the most
+ pessimistic guess for the size. Using alloca() allows
+ having more than one integer formatting in a call. */
+ char *buf = (char *) alloca (3 * sizeof (unsigned long int));
+ char *endp = &buf[3 * sizeof (unsigned long int)];
+ char *cp = _itoa (num, endp, *fmt == 'x' ? 16 : 10, 0);
+
+ /* Pad to the width the user specified. */
+ if (width != -1)
+ while (endp - cp < width)
+ *--cp = fill;
+
+ iov[niov].iov_base = cp;
+ iov[niov].iov_len = endp - cp;
+ ++niov;
+ }
+ break;
+
+ case 's':
+ /* Get the string argument. */
+ iov[niov].iov_base = va_arg (arg, char *);
+ iov[niov].iov_len = strlen (iov[niov].iov_base);
+ if (prec != -1)
+ iov[niov].iov_len = MIN ((size_t) prec, iov[niov].iov_len);
+ ++niov;
+ break;
+
+ case '%':
+ iov[niov].iov_base = (void *) fmt;
+ iov[niov].iov_len = 1;
+ ++niov;
+ break;
+
+ default:
+ assert (! "invalid format specifier");
+ }
+ ++fmt;
+ }
+ else if (*fmt == '\n')
+ {
+ /* See whether we have to print a single newline character. */
+ if (fmt == startp)
+ {
+ iov[niov].iov_base = (char *) startp;
+ iov[niov++].iov_len = 1;
+ }
+ else
+ /* No, just add it to the rest of the string. */
+ ++iov[niov - 1].iov_len;
+
+ /* Next line, print a tag again. */
+ tag_p = 1;
+ ++fmt;
+ }
+ }
+
+ /* Finally write the result. */
+#ifdef HAVE_INLINED_SYSCALLS
+ INTERNAL_SYSCALL_DECL (err);
+ INTERNAL_SYSCALL (writev, err, 3, fd, &iov, niov);
+#elif RTLD_PRIVATE_ERRNO
+ /* We have to take this lock just to be sure we don't clobber the private
+ errno when it's being used by another thread that cares about it.
+ Yet we must be sure not to try calling the lock functions before
+ the thread library is fully initialized. */
+ if (__builtin_expect (INTUSE (_dl_starting_up), 0))
+ __writev (fd, iov, niov);
+ else
+ {
+ __rtld_lock_lock_recursive (GL(dl_load_lock));
+ __writev (fd, iov, niov);
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
+ }
+#else
+ __writev (fd, iov, niov);
+#endif
+}
+
+
+/* Write to debug file. */
+void
+_dl_debug_printf (const char *fmt, ...)
+{
+ va_list arg;
+
+ va_start (arg, fmt);
+ _dl_debug_vdprintf (GLRO(dl_debug_fd), 1, fmt, arg);
+ va_end (arg);
+}
+
+
+/* Write to debug file but don't start with a tag. */
+void
+_dl_debug_printf_c (const char *fmt, ...)
+{
+ va_list arg;
+
+ va_start (arg, fmt);
+ _dl_debug_vdprintf (GLRO(dl_debug_fd), -1, fmt, arg);
+ va_end (arg);
+}
+
+
+/* Write the given file descriptor. */
+void
+_dl_dprintf (int fd, const char *fmt, ...)
+{
+ va_list arg;
+
+ va_start (arg, fmt);
+ _dl_debug_vdprintf (fd, 0, fmt, arg);
+ va_end (arg);
+}
+
+
+/* Test whether given NAME matches any of the names of the given object. */
+int
+internal_function
+_dl_name_match_p (const char *name, const struct link_map *map)
+{
+ if (strcmp (name, map->l_name) == 0)
+ return 1;
+
+ struct libname_list *runp = map->l_libname;
+
+ while (runp != NULL)
+ if (strcmp (name, runp->name) == 0)
+ return 1;
+ else
+ runp = runp->next;
+
+ return 0;
+}
diff --git a/libc/elf/dl-object.c b/libc/elf/dl-object.c
new file mode 100644
index 000000000..86f7a8e4d
--- /dev/null
+++ b/libc/elf/dl-object.c
@@ -0,0 +1,205 @@
+/* Storage management for the chain of loaded shared objects.
+ Copyright (C) 1995-2002, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+
+#include <assert.h>
+
+
+/* Allocate a `struct link_map' for a new object being loaded,
+ and enter it into the _dl_loaded list. */
+
+struct link_map *
+internal_function
+_dl_new_object (char *realname, const char *libname, int type,
+ struct link_map *loader, int mode, Lmid_t nsid)
+{
+ struct link_map *l;
+ int idx;
+ size_t libname_len = strlen (libname) + 1;
+ struct link_map *new;
+ struct libname_list *newname;
+#ifdef SHARED
+ /* We create the map for the executable before we know whether we have
+ auditing libraries and if yes, how many. Assume the worst. */
+ unsigned int naudit = GLRO(dl_naudit) ?: ((mode & __RTLD_OPENEXEC)
+ ? DL_NNS : 0);
+ size_t audit_space = naudit * sizeof (new->l_audit[0]);
+#else
+# define audit_space 0
+#endif
+
+ new = (struct link_map *) calloc (sizeof (*new) + audit_space
+ + sizeof (*newname) + libname_len, 1);
+ if (new == NULL)
+ return NULL;
+
+ new->l_real = new;
+ new->l_libname = newname = (struct libname_list *) ((char *) (new + 1)
+ + audit_space);
+ newname->name = (char *) memcpy (newname + 1, libname, libname_len);
+ /* newname->next = NULL; We use calloc therefore not necessary. */
+ newname->dont_free = 1;
+
+ new->l_name = realname;
+ new->l_type = type;
+ new->l_loader = loader;
+#if defined USE_TLS && NO_TLS_OFFSET != 0
+ new->l_tls_offset = NO_TLS_OFFSET;
+#endif
+ new->l_ns = nsid;
+
+#ifdef SHARED
+ for (unsigned int cnt = 0; cnt < naudit; ++cnt)
+ {
+ new->l_audit[cnt].cookie = (uintptr_t) new;
+ /* new->l_audit[cnt].bindflags = 0; */
+ }
+#endif
+
+ /* new->l_global = 0; We use calloc therefore not necessary. */
+
+ /* Use the 'l_scope_mem' array by default for the the 'l_scope'
+ information. If we need more entries we will allocate a large
+ array dynamically. */
+ new->l_scope = new->l_scope_mem;
+ new->l_scope_max = sizeof (new->l_scope_mem) / sizeof (new->l_scope_mem[0]);
+
+ /* Counter for the scopes we have to handle. */
+ idx = 0;
+
+ if (GL(dl_ns)[nsid]._ns_loaded != NULL)
+ {
+ l = GL(dl_ns)[nsid]._ns_loaded;
+ while (l->l_next != NULL)
+ l = l->l_next;
+ new->l_prev = l;
+ /* new->l_next = NULL; Would be necessary but we use calloc. */
+ l->l_next = new;
+
+ /* Add the global scope. */
+ new->l_scope[idx++] = &GL(dl_ns)[nsid]._ns_loaded->l_searchlist;
+ }
+ else
+ GL(dl_ns)[nsid]._ns_loaded = new;
+ ++GL(dl_ns)[nsid]._ns_nloaded;
+ ++GL(dl_load_adds);
+
+ /* If we have no loader the new object acts as it. */
+ if (loader == NULL)
+ loader = new;
+ else
+ /* Determine the local scope. */
+ while (loader->l_loader != NULL)
+ loader = loader->l_loader;
+
+ /* Insert the scope if it isn't the global scope we already added. */
+ if (idx == 0 || &loader->l_searchlist != new->l_scope[0])
+ {
+ if ((mode & RTLD_DEEPBIND) != 0 && idx != 0)
+ {
+ new->l_scope[1] = new->l_scope[0];
+ idx = 0;
+ }
+
+ new->l_scope[idx] = &loader->l_searchlist;
+ }
+
+ new->l_local_scope[0] = &new->l_searchlist;
+
+ /* Don't try to find the origin for the main map which has the name "". */
+ if (realname[0] != '\0')
+ {
+ size_t realname_len = strlen (realname) + 1;
+ char *origin;
+ char *cp;
+
+ if (realname[0] == '/')
+ {
+ /* It is an absolute path. Use it. But we have to make a
+ copy since we strip out the trailing slash. */
+ cp = origin = (char *) malloc (realname_len);
+ if (origin == NULL)
+ {
+ origin = (char *) -1;
+ goto out;
+ }
+ }
+ else
+ {
+ size_t len = realname_len;
+ char *result = NULL;
+
+ /* Get the current directory name. */
+ origin = NULL;
+ do
+ {
+ char *new_origin;
+
+ len += 128;
+ new_origin = (char *) realloc (origin, len);
+ if (new_origin == NULL)
+ /* We exit the loop. Note that result == NULL. */
+ break;
+ origin = new_origin;
+ }
+ while ((result = __getcwd (origin, len - realname_len)) == NULL
+ && errno == ERANGE);
+
+ if (result == NULL)
+ {
+ /* We were not able to determine the current directory.
+ Note that free(origin) is OK if origin == NULL. */
+ free (origin);
+ origin = (char *) -1;
+ goto out;
+ }
+
+ /* Find the end of the path and see whether we have to add a
+ slash. We could use rawmemchr but this need not be
+ fast. */
+ cp = (strchr) (origin, '\0');
+ if (cp[-1] != '/')
+ *cp++ = '/';
+ }
+
+ /* Add the real file name. */
+ cp = __mempcpy (cp, realname, realname_len);
+
+ /* Now remove the filename and the slash. Leave the slash if
+ the name is something like "/foo". */
+ do
+ --cp;
+ while (*cp != '/');
+
+ if (cp == origin)
+ /* Keep the only slash which is the first character. */
+ ++cp;
+ *cp = '\0';
+
+ out:
+ new->l_origin = origin;
+ }
+
+ return new;
+}
diff --git a/libc/elf/dl-open.c b/libc/elf/dl-open.c
new file mode 100644
index 000000000..cdbb6601d
--- /dev/null
+++ b/libc/elf/dl-open.c
@@ -0,0 +1,646 @@
+/* Load a shared object at runtime, relocate it, and run its initializer.
+ Copyright (C) 1996-2004, 2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h> /* Check whether MAP_COPY is defined. */
+#include <sys/param.h>
+#include <bits/libc-lock.h>
+#include <ldsodefs.h>
+#include <bp-sym.h>
+#include <caller.h>
+
+#include <dl-dst.h>
+
+
+extern ElfW(Addr) _dl_sysdep_start (void **start_argptr,
+ void (*dl_main) (const ElfW(Phdr) *phdr,
+ ElfW(Word) phnum,
+ ElfW(Addr) *user_entry));
+weak_extern (BP_SYM (_dl_sysdep_start))
+
+extern int __libc_multiple_libcs; /* Defined in init-first.c. */
+
+/* Undefine the following for debugging. */
+/* #define SCOPE_DEBUG 1 */
+#ifdef SCOPE_DEBUG
+static void show_scope (struct link_map *new);
+#endif
+
+/* We must be carefull not to leave us in an inconsistent state. Thus we
+ catch any error and re-raise it after cleaning up. */
+
+struct dl_open_args
+{
+ const char *file;
+ int mode;
+ /* This is the caller of the dlopen() function. */
+ const void *caller_dlopen;
+ /* This is the caller if _dl_open(). */
+ const void *caller_dl_open;
+ struct link_map *map;
+ /* Namespace ID. */
+ Lmid_t nsid;
+ /* Original parameters to the program and the current environment. */
+ int argc;
+ char **argv;
+ char **env;
+};
+
+
+static int
+add_to_global (struct link_map *new)
+{
+ struct link_map **new_global;
+ unsigned int to_add = 0;
+ unsigned int cnt;
+
+ /* Count the objects we have to put in the global scope. */
+ for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt)
+ if (new->l_searchlist.r_list[cnt]->l_global == 0)
+ ++to_add;
+
+ /* The symbols of the new objects and its dependencies are to be
+ introduced into the global scope that will be used to resolve
+ references from other dynamically-loaded objects.
+
+ The global scope is the searchlist in the main link map. We
+ extend this list if necessary. There is one problem though:
+ since this structure was allocated very early (before the libc
+ is loaded) the memory it uses is allocated by the malloc()-stub
+ in the ld.so. When we come here these functions are not used
+ anymore. Instead the malloc() implementation of the libc is
+ used. But this means the block from the main map cannot be used
+ in an realloc() call. Therefore we allocate a completely new
+ array the first time we have to add something to the locale scope. */
+
+ if (GL(dl_ns)[new->l_ns]._ns_global_scope_alloc == 0)
+ {
+ /* This is the first dynamic object given global scope. */
+ GL(dl_ns)[new->l_ns]._ns_global_scope_alloc
+ = GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist + to_add + 8;
+ new_global = (struct link_map **)
+ malloc (GL(dl_ns)[new->l_ns]._ns_global_scope_alloc
+ * sizeof (struct link_map *));
+ if (new_global == NULL)
+ {
+ GL(dl_ns)[new->l_ns]._ns_global_scope_alloc = 0;
+ nomem:
+ _dl_signal_error (ENOMEM, new->l_libname->name, NULL,
+ N_("cannot extend global scope"));
+ return 1;
+ }
+
+ /* Copy over the old entries. */
+ GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list
+ = memcpy (new_global,
+ GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list,
+ (GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist
+ * sizeof (struct link_map *)));
+ }
+ else if (GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist + to_add
+ > GL(dl_ns)[new->l_ns]._ns_global_scope_alloc)
+ {
+ /* We have to extend the existing array of link maps in the
+ main map. */
+ new_global = (struct link_map **)
+ realloc (GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list,
+ ((GL(dl_ns)[new->l_ns]._ns_global_scope_alloc + to_add + 8)
+ * sizeof (struct link_map *)));
+ if (new_global == NULL)
+ goto nomem;
+
+ GL(dl_ns)[new->l_ns]._ns_global_scope_alloc += to_add + 8;
+ GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list = new_global;
+ }
+
+ /* Now add the new entries. */
+ for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt)
+ {
+ struct link_map *map = new->l_searchlist.r_list[cnt];
+
+ if (map->l_global == 0)
+ {
+ map->l_global = 1;
+ GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list[GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist]
+ = map;
+ ++GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist;
+ }
+ }
+
+ return 0;
+}
+
+
+static void
+dl_open_worker (void *a)
+{
+ struct dl_open_args *args = a;
+ const char *file = args->file;
+ int mode = args->mode;
+ struct link_map *new, *l;
+ int lazy;
+ unsigned int i;
+#ifdef USE_TLS
+ bool any_tls = false;
+#endif
+ struct link_map *call_map = NULL;
+
+ /* Check whether _dl_open() has been called from a valid DSO. */
+ if (__check_caller (args->caller_dl_open,
+ allow_libc|allow_libdl|allow_ldso) != 0)
+ _dl_signal_error (0, "dlopen", NULL, N_("invalid caller"));
+
+ /* Determine the caller's map if necessary. This is needed in case
+ we have a DST, when we don't know the namespace ID we have to put
+ the new object in, or when the file name has no path in which
+ case we need to look along the RUNPATH/RPATH of the caller. */
+ const char *dst = strchr (file, '$');
+ if (dst != NULL || args->nsid == __LM_ID_CALLER
+ || strchr (file, '/') == NULL)
+ {
+ const void *caller_dlopen = args->caller_dlopen;
+
+ /* We have to find out from which object the caller is calling.
+ By default we assume this is the main application. */
+ call_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+
+ for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
+ for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next)
+ if (caller_dlopen >= (const void *) l->l_map_start
+ && caller_dlopen < (const void *) l->l_map_end)
+ {
+ /* There must be exactly one DSO for the range of the virtual
+ memory. Otherwise something is really broken. */
+ assert (ns == l->l_ns);
+ call_map = l;
+ goto found_caller;
+ }
+
+ found_caller:
+ if (args->nsid == __LM_ID_CALLER)
+ {
+#ifndef SHARED
+ /* In statically linked apps there might be no loaded object. */
+ if (call_map == NULL)
+ args->nsid = LM_ID_BASE;
+ else
+#endif
+ args->nsid = call_map->l_ns;
+ }
+ }
+
+ assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
+
+ /* Maybe we have to expand a DST. */
+ if (__builtin_expect (dst != NULL, 0))
+ {
+ size_t len = strlen (file);
+ size_t required;
+ char *new_file;
+
+ /* DSTs must not appear in SUID/SGID programs. */
+ if (INTUSE(__libc_enable_secure))
+ /* This is an error. */
+ _dl_signal_error (0, "dlopen", NULL,
+ N_("DST not allowed in SUID/SGID programs"));
+
+
+ /* Determine how much space we need. We have to allocate the
+ memory locally. */
+ required = DL_DST_REQUIRED (call_map, file, len, _dl_dst_count (dst, 0));
+
+ /* Get space for the new file name. */
+ new_file = (char *) alloca (required + 1);
+
+ /* Generate the new file name. */
+ _dl_dst_substitute (call_map, file, new_file, 0);
+
+ /* If the substitution failed don't try to load. */
+ if (*new_file == '\0')
+ _dl_signal_error (0, "dlopen", NULL,
+ N_("empty dynamic string token substitution"));
+
+ /* Now we have a new file name. */
+ file = new_file;
+
+ /* It does not matter whether call_map is set even if we
+ computed it only because of the DST. Since the path contains
+ a slash the value is not used. See dl-load.c. */
+ }
+
+ /* Load the named object. */
+ args->map = new = _dl_map_object (call_map, file, 0, lt_loaded, 0,
+ mode | __RTLD_CALLMAP, args->nsid);
+
+ /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is
+ set and the object is not already loaded. */
+ if (new == NULL)
+ {
+ assert (mode & RTLD_NOLOAD);
+ return;
+ }
+
+ if (__builtin_expect (mode & __RTLD_SPROF, 0))
+ /* This happens only if we load a DSO for 'sprof'. */
+ return;
+
+ /* This object is directly loaded. */
+ ++new->l_direct_opencount;
+
+ /* It was already open. */
+ if (__builtin_expect (new->l_searchlist.r_list != NULL, 0))
+ {
+ /* Let the user know about the opencount. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0))
+ _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
+ new->l_name, new->l_ns, new->l_direct_opencount);
+
+ /* If the user requested the object to be in the global namespace
+ but it is not so far, add it now. */
+ if ((mode & RTLD_GLOBAL) && new->l_global == 0)
+ (void) add_to_global (new);
+
+ assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
+
+ return;
+ }
+
+ /* Load that object's dependencies. */
+ _dl_map_object_deps (new, NULL, 0, 0,
+ mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
+
+ /* So far, so good. Now check the versions. */
+ for (i = 0; i < new->l_searchlist.r_nlist; ++i)
+ if (new->l_searchlist.r_list[i]->l_real->l_versions == NULL)
+ (void) _dl_check_map_versions (new->l_searchlist.r_list[i]->l_real,
+ 0, 0);
+
+#ifdef SCOPE_DEBUG
+ show_scope (new);
+#endif
+
+#ifdef SHARED
+ /* Auditing checkpoint: we have added all objects. */
+ if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
+ {
+ struct link_map *head = GL(dl_ns)[new->l_ns]._ns_loaded;
+ /* Do not call the functions for any auditing object. */
+ if (head->l_auditing == 0)
+ {
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->activity != NULL)
+ afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT);
+
+ afct = afct->next;
+ }
+ }
+ }
+#endif
+
+ /* Notify the debugger all new objects are now ready to go. */
+ struct r_debug *r = _dl_debug_initialize (0, args->nsid);
+ r->r_state = RT_CONSISTENT;
+ _dl_debug_state ();
+
+ /* Only do lazy relocation if `LD_BIND_NOW' is not set. */
+ lazy = (mode & RTLD_BINDING_MASK) == RTLD_LAZY && GLRO(dl_lazy);
+
+ /* Relocate the objects loaded. We do this in reverse order so that copy
+ relocs of earlier objects overwrite the data written by later objects. */
+
+ l = new;
+ while (l->l_next)
+ l = l->l_next;
+ while (1)
+ {
+ if (! l->l_real->l_relocated)
+ {
+#ifdef SHARED
+ if (__builtin_expect (GLRO(dl_profile) != NULL, 0))
+ {
+ /* If this here is the shared object which we want to profile
+ make sure the profile is started. We can find out whether
+ this is necessary or not by observing the `_dl_profile_map'
+ variable. If was NULL but is not NULL afterwars we must
+ start the profiling. */
+ struct link_map *old_profile_map = GL(dl_profile_map);
+
+ _dl_relocate_object (l, l->l_scope, 1, 1);
+
+ if (old_profile_map == NULL && GL(dl_profile_map) != NULL)
+ {
+ /* We must prepare the profiling. */
+ _dl_start_profile ();
+
+ /* Prevent unloading the object. */
+ GL(dl_profile_map)->l_flags_1 |= DF_1_NODELETE;
+ }
+ }
+ else
+#endif
+ _dl_relocate_object (l, l->l_scope, lazy, 0);
+ }
+
+ if (l == new)
+ break;
+ l = l->l_prev;
+ }
+
+ /* If the file is not loaded now as a dependency, add the search
+ list of the newly loaded object to the scope. */
+ for (i = 0; i < new->l_searchlist.r_nlist; ++i)
+ {
+ struct link_map *imap = new->l_searchlist.r_list[i];
+
+ /* If the initializer has been called already, the object has
+ not been loaded here and now. */
+ if (imap->l_init_called && imap->l_type == lt_loaded)
+ {
+ struct r_scope_elem **runp = imap->l_scope;
+ size_t cnt = 0;
+
+ while (*runp != NULL)
+ {
+ ++cnt;
+ ++runp;
+ }
+
+ if (*runp != NULL)
+ /* Avoid duplicates. */
+ continue;
+
+ if (__builtin_expect (cnt + 1 >= imap->l_scope_max, 0))
+ {
+ /* The 'r_scope' array is too small. Allocate a new one
+ dynamically. */
+ struct r_scope_elem **newp;
+ size_t new_size = imap->l_scope_max * 2;
+
+ if (imap->l_scope == imap->l_scope_mem)
+ {
+ newp = (struct r_scope_elem **)
+ malloc (new_size * sizeof (struct r_scope_elem *));
+ if (newp == NULL)
+ _dl_signal_error (ENOMEM, "dlopen", NULL,
+ N_("cannot create scope list"));
+ imap->l_scope = memcpy (newp, imap->l_scope,
+ cnt * sizeof (imap->l_scope[0]));
+ }
+ else
+ {
+ newp = (struct r_scope_elem **)
+ realloc (imap->l_scope,
+ new_size * sizeof (struct r_scope_elem *));
+ if (newp == NULL)
+ _dl_signal_error (ENOMEM, "dlopen", NULL,
+ N_("cannot create scope list"));
+ imap->l_scope = newp;
+ }
+
+ imap->l_scope_max = new_size;
+ }
+
+ imap->l_scope[cnt++] = &new->l_searchlist;
+ imap->l_scope[cnt] = NULL;
+ }
+#if USE_TLS
+ /* Only add TLS memory if this object is loaded now and
+ therefore is not yet initialized. */
+ else if (! imap->l_init_called
+ /* Only if the module defines thread local data. */
+ && __builtin_expect (imap->l_tls_blocksize > 0, 0))
+ {
+ /* Now that we know the object is loaded successfully add
+ modules containing TLS data to the slot info table. We
+ might have to increase its size. */
+ _dl_add_to_slotinfo (imap);
+
+ if (imap->l_need_tls_init)
+ {
+ imap->l_need_tls_init = 0;
+# ifdef SHARED
+ /* Update the slot information data for at least the
+ generation of the DSO we are allocating data for. */
+ _dl_update_slotinfo (imap->l_tls_modid);
+# endif
+
+ GL(dl_init_static_tls) (imap);
+ assert (imap->l_need_tls_init == 0);
+ }
+
+ /* We have to bump the generation counter. */
+ any_tls = true;
+ }
+#endif
+ }
+
+#if USE_TLS
+ /* Bump the generation number if necessary. */
+ if (any_tls && __builtin_expect (++GL(dl_tls_generation) == 0, 0))
+ _dl_fatal_printf (N_("\
+TLS generation counter wrapped! Please report this."));
+#endif
+
+ /* Run the initializer functions of new objects. */
+ _dl_init (new, args->argc, args->argv, args->env);
+
+ /* Now we can make the new map available in the global scope. */
+ if (mode & RTLD_GLOBAL)
+ /* Move the object in the global namespace. */
+ if (add_to_global (new) != 0)
+ /* It failed. */
+ return;
+
+ /* Mark the object as not deletable if the RTLD_NODELETE flags was
+ passed. */
+ if (__builtin_expect (mode & RTLD_NODELETE, 0))
+ new->l_flags_1 |= DF_1_NODELETE;
+
+#ifndef SHARED
+ /* We must be the static _dl_open in libc.a. A static program that
+ has loaded a dynamic object now has competition. */
+ __libc_multiple_libcs = 1;
+#endif
+
+ /* Let the user know about the opencount. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0))
+ _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
+ new->l_name, new->l_ns, new->l_direct_opencount);
+}
+
+
+void *
+_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
+ int argc, char *argv[], char *env[])
+{
+ if ((mode & RTLD_BINDING_MASK) == 0)
+ /* One of the flags must be set. */
+ _dl_signal_error (EINVAL, file, NULL, N_("invalid mode for dlopen()"));
+
+ /* Make sure we are alone. */
+ __rtld_lock_lock_recursive (GL(dl_load_lock));
+
+ if (nsid == LM_ID_NEWLM)
+ {
+ /* Find a new namespace. */
+ for (nsid = 1; nsid < DL_NNS; ++nsid)
+ if (GL(dl_ns)[nsid]._ns_loaded == NULL)
+ break;
+
+ if (nsid == DL_NNS)
+ {
+ /* No more namespace available. */
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
+
+ _dl_signal_error (EINVAL, file, NULL, N_("\
+no more namespaces available for dlmopen()"));
+ }
+
+ _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT;
+ }
+ /* Never allow loading a DSO in a namespace which is empty. Such
+ direct placements is only causing problems. Also don't allow
+ loading into a namespace used for auditing. */
+ else if (nsid != LM_ID_BASE && nsid != __LM_ID_CALLER
+ && (GL(dl_ns)[nsid]._ns_nloaded == 0
+ || GL(dl_ns)[nsid]._ns_loaded->l_auditing))
+ _dl_signal_error (EINVAL, file, NULL,
+ N_("invalid target namespace in dlmopen()"));
+
+ struct dl_open_args args;
+ args.file = file;
+ args.mode = mode;
+ args.caller_dlopen = caller_dlopen;
+ args.caller_dl_open = RETURN_ADDRESS (0);
+ args.map = NULL;
+ args.nsid = nsid;
+ args.argc = argc;
+ args.argv = argv;
+ args.env = env;
+
+ const char *objname;
+ const char *errstring;
+ bool malloced;
+ int errcode = _dl_catch_error (&objname, &errstring, &malloced,
+ dl_open_worker, &args);
+
+#ifndef MAP_COPY
+ /* We must munmap() the cache file. */
+ _dl_unload_cache ();
+#endif
+
+ /* Release the lock. */
+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
+
+ if (__builtin_expect (errstring != NULL, 0))
+ {
+ /* Some error occurred during loading. */
+ char *local_errstring;
+ size_t len_errstring;
+
+ /* Remove the object from memory. It may be in an inconsistent
+ state if relocation failed, for example. */
+ if (args.map)
+ {
+#ifdef USE_TLS
+ /* Maybe some of the modules which were loaded use TLS.
+ Since it will be removed in the following _dl_close call
+ we have to mark the dtv array as having gaps to fill the
+ holes. This is a pessimistic assumption which won't hurt
+ if not true. There is no need to do this when we are
+ loading the auditing DSOs since TLS has not yet been set
+ up. */
+ if ((mode & __RTLD_AUDIT) == 0)
+ GL(dl_tls_dtv_gaps) = true;
+#endif
+
+ _dl_close (args.map);
+ }
+
+ /* Make a local copy of the error string so that we can release the
+ memory allocated for it. */
+ len_errstring = strlen (errstring) + 1;
+ if (objname == errstring + len_errstring)
+ {
+ size_t total_len = len_errstring + strlen (objname) + 1;
+ local_errstring = alloca (total_len);
+ memcpy (local_errstring, errstring, total_len);
+ objname = local_errstring + len_errstring;
+ }
+ else
+ {
+ local_errstring = alloca (len_errstring);
+ memcpy (local_errstring, errstring, len_errstring);
+ }
+
+ if (malloced)
+ free ((char *) errstring);
+
+ assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
+
+ /* Reraise the error. */
+ _dl_signal_error (errcode, objname, NULL, local_errstring);
+ }
+
+ assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
+
+#ifndef SHARED
+ DL_STATIC_INIT (args.map);
+#endif
+
+ return args.map;
+}
+
+
+#ifdef SCOPE_DEBUG
+#include <unistd.h>
+
+static void
+show_scope (struct link_map *new)
+{
+ int scope_cnt;
+
+ for (scope_cnt = 0; new->l_scope[scope_cnt] != NULL; ++scope_cnt)
+ {
+ char numbuf[2];
+ unsigned int cnt;
+
+ numbuf[0] = '0' + scope_cnt;
+ numbuf[1] = '\0';
+ _dl_printf ("scope %s:", numbuf);
+
+ for (cnt = 0; cnt < new->l_scope[scope_cnt]->r_nlist; ++cnt)
+ if (*new->l_scope[scope_cnt]->r_list[cnt]->l_name)
+ _dl_printf (" %s", new->l_scope[scope_cnt]->r_list[cnt]->l_name);
+ else
+ _dl_printf (" <main>");
+
+ _dl_printf ("\n");
+ }
+}
+#endif
diff --git a/libc/elf/dl-origin.c b/libc/elf/dl-origin.c
new file mode 100644
index 000000000..87619379b
--- /dev/null
+++ b/libc/elf/dl-origin.c
@@ -0,0 +1,51 @@
+/* Find path of executable.
+ Copyright (C) 1998, 1999, 2000, 2002, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <ldsodefs.h>
+
+#include <dl-dst.h>
+
+
+const char *
+_dl_get_origin (void)
+{
+ char *result = (char *) -1;
+ /* We use the environment variable LD_ORIGIN_PATH. If it is set make
+ a copy and strip out trailing slashes. */
+ if (GLRO(dl_origin_path) != NULL)
+ {
+ size_t len = strlen (GLRO(dl_origin_path));
+ result = (char *) malloc (len + 1);
+ if (result == NULL)
+ result = (char *) -1;
+ else
+ {
+ char *cp = __mempcpy (result, GLRO(dl_origin_path), len);
+ while (cp > result + 1 && cp[-1] == '/')
+ --cp;
+ *cp = '\0';
+ }
+ }
+
+ return result;
+}
diff --git a/libc/elf/dl-profile.c b/libc/elf/dl-profile.c
new file mode 100644
index 000000000..41214c1b0
--- /dev/null
+++ b/libc/elf/dl-profile.c
@@ -0,0 +1,555 @@
+/* Profiling of shared libraries.
+ Copyright (C) 1997-2002, 2003, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
+ Based on the BSD mcount implementation.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <sys/gmon.h>
+#include <sys/gmon_out.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <atomic.h>
+
+/* The LD_PROFILE feature has to be implemented different to the
+ normal profiling using the gmon/ functions. The problem is that an
+ arbitrary amount of processes simulataneously can be run using
+ profiling and all write the results in the same file. To provide
+ this mechanism one could implement a complicated mechanism to merge
+ the content of two profiling runs or one could extend the file
+ format to allow more than one data set. For the second solution we
+ would have the problem that the file can grow in size beyond any
+ limit and both solutions have the problem that the concurrency of
+ writing the results is a big problem.
+
+ Another much simpler method is to use mmap to map the same file in
+ all using programs and modify the data in the mmap'ed area and so
+ also automatically on the disk. Using the MAP_SHARED option of
+ mmap(2) this can be done without big problems in more than one
+ file.
+
+ This approach is very different from the normal profiling. We have
+ to use the profiling data in exactly the way they are expected to
+ be written to disk. But the normal format used by gprof is not usable
+ to do this. It is optimized for size. It writes the tags as single
+ bytes but this means that the following 32/64 bit values are
+ unaligned.
+
+ Therefore we use a new format. This will look like this
+
+ 0 1 2 3 <- byte is 32 bit word
+ 0000 g m o n
+ 0004 *version* <- GMON_SHOBJ_VERSION
+ 0008 00 00 00 00
+ 000c 00 00 00 00
+ 0010 00 00 00 00
+
+ 0014 *tag* <- GMON_TAG_TIME_HIST
+ 0018 ?? ?? ?? ??
+ ?? ?? ?? ?? <- 32/64 bit LowPC
+ 0018+A ?? ?? ?? ??
+ ?? ?? ?? ?? <- 32/64 bit HighPC
+ 0018+2*A *histsize*
+ 001c+2*A *profrate*
+ 0020+2*A s e c o
+ 0024+2*A n d s \0
+ 0028+2*A \0 \0 \0 \0
+ 002c+2*A \0 \0 \0
+ 002f+2*A s
+
+ 0030+2*A ?? ?? ?? ?? <- Count data
+ ... ...
+ 0030+2*A+K ?? ?? ?? ??
+
+ 0030+2*A+K *tag* <- GMON_TAG_CG_ARC
+ 0034+2*A+K *lastused*
+ 0038+2*A+K ?? ?? ?? ??
+ ?? ?? ?? ?? <- FromPC#1
+ 0038+3*A+K ?? ?? ?? ??
+ ?? ?? ?? ?? <- ToPC#1
+ 0038+4*A+K ?? ?? ?? ?? <- Count#1
+ ... ... ...
+ 0038+(2*(CN-1)+2)*A+(CN-1)*4+K ?? ?? ?? ??
+ ?? ?? ?? ?? <- FromPC#CGN
+ 0038+(2*(CN-1)+3)*A+(CN-1)*4+K ?? ?? ?? ??
+ ?? ?? ?? ?? <- ToPC#CGN
+ 0038+(2*CN+2)*A+(CN-1)*4+K ?? ?? ?? ?? <- Count#CGN
+
+ We put (for now?) no basic block information in the file since this would
+ introduce rase conditions among all the processes who want to write them.
+
+ `K' is the number of count entries which is computed as
+
+ textsize / HISTFRACTION
+
+ `CG' in the above table is the number of call graph arcs. Normally,
+ the table is sparse and the profiling code writes out only the those
+ entries which are really used in the program run. But since we must
+ not extend this table (the profiling file) we'll keep them all here.
+ So CN can be executed in advance as
+
+ MINARCS <= textsize*(ARCDENSITY/100) <= MAXARCS
+
+ Now the remaining question is: how to build the data structures we can
+ work with from this data. We need the from set and must associate the
+ froms with all the associated tos. We will do this by constructing this
+ data structures at the program start. To do this we'll simply visit all
+ entries in the call graph table and add it to the appropriate list. */
+
+extern int __profile_frequency (void);
+libc_hidden_proto (__profile_frequency)
+
+/* We define a special type to address the elements of the arc table.
+ This is basically the `gmon_cg_arc_record' format but it includes
+ the room for the tag and it uses real types. */
+struct here_cg_arc_record
+ {
+ uintptr_t from_pc;
+ uintptr_t self_pc;
+ uint32_t count;
+ } __attribute__ ((packed));
+
+static struct here_cg_arc_record *data;
+
+/* Nonzero if profiling is under way. */
+static int running;
+
+/* This is the number of entry which have been incorporated in the toset. */
+static uint32_t narcs;
+/* This is a pointer to the object representing the number of entries
+ currently in the mmaped file. At no point of time this has to be the
+ same as NARCS. If it is equal all entries from the file are in our
+ lists. */
+static volatile uint32_t *narcsp;
+
+
+struct here_fromstruct
+ {
+ struct here_cg_arc_record volatile *here;
+ uint16_t link;
+ };
+
+static volatile uint16_t *tos;
+
+static struct here_fromstruct *froms;
+static uint32_t fromlimit;
+static volatile uint32_t fromidx;
+
+static uintptr_t lowpc;
+static size_t textsize;
+static unsigned int log_hashfraction;
+
+
+
+/* Set up profiling data to profile object desribed by MAP. The output
+ file is found (or created) in OUTPUT_DIR. */
+void
+internal_function
+_dl_start_profile (void)
+{
+ char *filename;
+ int fd;
+ struct stat64 st;
+ const ElfW(Phdr) *ph;
+ ElfW(Addr) mapstart = ~((ElfW(Addr)) 0);
+ ElfW(Addr) mapend = 0;
+ struct gmon_hdr gmon_hdr;
+ struct gmon_hist_hdr hist_hdr;
+ char *hist, *cp;
+ size_t idx;
+ size_t tossize;
+ size_t fromssize;
+ uintptr_t highpc;
+ uint16_t *kcount;
+ size_t kcountsize;
+ struct gmon_hdr *addr = NULL;
+ off_t expected_size;
+ /* See profil(2) where this is described. */
+ int s_scale;
+#define SCALE_1_TO_1 0x10000L
+ const char *errstr = NULL;
+
+ /* Compute the size of the sections which contain program code. */
+ for (ph = GL(dl_profile_map)->l_phdr;
+ ph < &GL(dl_profile_map)->l_phdr[GL(dl_profile_map)->l_phnum]; ++ph)
+ if (ph->p_type == PT_LOAD && (ph->p_flags & PF_X))
+ {
+ ElfW(Addr) start = (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1));
+ ElfW(Addr) end = ((ph->p_vaddr + ph->p_memsz + GLRO(dl_pagesize) - 1)
+ & ~(GLRO(dl_pagesize) - 1));
+
+ if (start < mapstart)
+ mapstart = start;
+ if (end > mapend)
+ mapend = end;
+ }
+
+ /* Now we can compute the size of the profiling data. This is done
+ with the same formulars as in `monstartup' (see gmon.c). */
+ running = 0;
+ lowpc = ROUNDDOWN (mapstart + GL(dl_profile_map)->l_addr,
+ HISTFRACTION * sizeof (HISTCOUNTER));
+ highpc = ROUNDUP (mapend + GL(dl_profile_map)->l_addr,
+ HISTFRACTION * sizeof (HISTCOUNTER));
+ textsize = highpc - lowpc;
+ kcountsize = textsize / HISTFRACTION;
+ if ((HASHFRACTION & (HASHFRACTION - 1)) == 0)
+ {
+ /* If HASHFRACTION is a power of two, mcount can use shifting
+ instead of integer division. Precompute shift amount.
+
+ This is a constant but the compiler cannot compile the
+ expression away since the __ffs implementation is not known
+ to the compiler. Help the compiler by precomputing the
+ usual cases. */
+ assert (HASHFRACTION == 2);
+
+ if (sizeof (*froms) == 8)
+ log_hashfraction = 4;
+ else if (sizeof (*froms) == 16)
+ log_hashfraction = 5;
+ else
+ log_hashfraction = __ffs (HASHFRACTION * sizeof (*froms)) - 1;
+ }
+ else
+ log_hashfraction = -1;
+ tossize = textsize / HASHFRACTION;
+ fromlimit = textsize * ARCDENSITY / 100;
+ if (fromlimit < MINARCS)
+ fromlimit = MINARCS;
+ if (fromlimit > MAXARCS)
+ fromlimit = MAXARCS;
+ fromssize = fromlimit * sizeof (struct here_fromstruct);
+
+ expected_size = (sizeof (struct gmon_hdr)
+ + 4 + sizeof (struct gmon_hist_hdr) + kcountsize
+ + 4 + 4 + fromssize * sizeof (struct here_cg_arc_record));
+
+ /* Create the gmon_hdr we expect or write. */
+ memset (&gmon_hdr, '\0', sizeof (struct gmon_hdr));
+ memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie));
+ *(int32_t *) gmon_hdr.version = GMON_SHOBJ_VERSION;
+
+ /* Create the hist_hdr we expect or write. */
+ *(char **) hist_hdr.low_pc = (char *) mapstart;
+ *(char **) hist_hdr.high_pc = (char *) mapend;
+ *(int32_t *) hist_hdr.hist_size = kcountsize / sizeof (HISTCOUNTER);
+ *(int32_t *) hist_hdr.prof_rate = __profile_frequency ();
+ if (sizeof (hist_hdr.dimen) >= sizeof ("seconds"))
+ {
+ memcpy (hist_hdr.dimen, "seconds", sizeof ("seconds"));
+ memset (hist_hdr.dimen + sizeof ("seconds"), '\0',
+ sizeof (hist_hdr.dimen) - sizeof ("seconds"));
+ }
+ else
+ strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen));
+ hist_hdr.dimen_abbrev = 's';
+
+ /* First determine the output name. We write in the directory
+ OUTPUT_DIR and the name is composed from the shared objects
+ soname (or the file name) and the ending ".profile". */
+ filename = (char *) alloca (strlen (GLRO(dl_profile_output)) + 1
+ + strlen (GLRO(dl_profile)) + sizeof ".profile");
+ cp = __stpcpy (filename, GLRO(dl_profile_output));
+ *cp++ = '/';
+ __stpcpy (__stpcpy (cp, GLRO(dl_profile)), ".profile");
+
+#ifdef O_NOFOLLOW
+# define EXTRA_FLAGS | O_NOFOLLOW
+#else
+# define EXTRA_FLAGS
+#endif
+ fd = __open (filename, O_RDWR | O_CREAT EXTRA_FLAGS, DEFFILEMODE);
+ if (fd == -1)
+ {
+ char buf[400];
+ int errnum;
+
+ /* We cannot write the profiling data so don't do anything. */
+ errstr = "%s: cannot open file: %s\n";
+ print_error:
+ errnum = errno;
+ if (fd != -1)
+ __close (fd);
+ _dl_error_printf (errstr, filename,
+ __strerror_r (errnum, buf, sizeof buf));
+ return;
+ }
+
+ if (__fxstat64 (_STAT_VER, fd, &st) < 0 || !S_ISREG (st.st_mode))
+ {
+ /* Not stat'able or not a regular file => don't use it. */
+ errstr = "%s: cannot stat file: %s\n";
+ goto print_error;
+ }
+
+ /* Test the size. If it does not match what we expect from the size
+ values in the map MAP we don't use it and warn the user. */
+ if (st.st_size == 0)
+ {
+ /* We have to create the file. */
+ char buf[GLRO(dl_pagesize)];
+
+ memset (buf, '\0', GLRO(dl_pagesize));
+
+ if (__lseek (fd, expected_size & ~(GLRO(dl_pagesize) - 1), SEEK_SET) == -1)
+ {
+ cannot_create:
+ errstr = "%s: cannot create file: %s\n";
+ goto print_error;
+ }
+
+ if (TEMP_FAILURE_RETRY (__libc_write (fd, buf, (expected_size
+ & (GLRO(dl_pagesize)
+ - 1))))
+ < 0)
+ goto cannot_create;
+ }
+ else if (st.st_size != expected_size)
+ {
+ __close (fd);
+ wrong_format:
+
+ if (addr != NULL)
+ __munmap ((void *) addr, expected_size);
+
+ _dl_error_printf ("%s: file is no correct profile data file for `%s'\n",
+ filename, GLRO(dl_profile));
+ return;
+ }
+
+ addr = (struct gmon_hdr *) __mmap (NULL, expected_size, PROT_READ|PROT_WRITE,
+ MAP_SHARED|MAP_FILE, fd, 0);
+ if (addr == (struct gmon_hdr *) MAP_FAILED)
+ {
+ errstr = "%s: cannot map file: %s\n";
+ goto print_error;
+ }
+
+ /* We don't need the file descriptor anymore. */
+ __close (fd);
+
+ /* Pointer to data after the header. */
+ hist = (char *) (addr + 1);
+ kcount = (uint16_t *) ((char *) hist + sizeof (uint32_t)
+ + sizeof (struct gmon_hist_hdr));
+
+ /* Compute pointer to array of the arc information. */
+ narcsp = (uint32_t *) ((char *) kcount + kcountsize + sizeof (uint32_t));
+ data = (struct here_cg_arc_record *) ((char *) narcsp + sizeof (uint32_t));
+
+ if (st.st_size == 0)
+ {
+ /* Create the signature. */
+ memcpy (addr, &gmon_hdr, sizeof (struct gmon_hdr));
+
+ *(uint32_t *) hist = GMON_TAG_TIME_HIST;
+ memcpy (hist + sizeof (uint32_t), &hist_hdr,
+ sizeof (struct gmon_hist_hdr));
+
+ narcsp[-1] = GMON_TAG_CG_ARC;
+ }
+ else
+ {
+ /* Test the signature in the file. */
+ if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0
+ || *(uint32_t *) hist != GMON_TAG_TIME_HIST
+ || memcmp (hist + sizeof (uint32_t), &hist_hdr,
+ sizeof (struct gmon_hist_hdr)) != 0
+ || narcsp[-1] != GMON_TAG_CG_ARC)
+ goto wrong_format;
+ }
+
+ /* Allocate memory for the froms data and the pointer to the tos records. */
+ tos = (uint16_t *) calloc (tossize + fromssize, 1);
+ if (tos == NULL)
+ {
+ __munmap ((void *) addr, expected_size);
+ _dl_fatal_printf ("Out of memory while initializing profiler\n");
+ /* NOTREACHED */
+ }
+
+ froms = (struct here_fromstruct *) ((char *) tos + tossize);
+ fromidx = 0;
+
+ /* Now we have to process all the arc count entries. BTW: it is
+ not critical whether the *NARCSP value changes meanwhile. Before
+ we enter a new entry in to toset we will check that everything is
+ available in TOS. This happens in _dl_mcount.
+
+ Loading the entries in reverse order should help to get the most
+ frequently used entries at the front of the list. */
+ for (idx = narcs = MIN (*narcsp, fromlimit); idx > 0; )
+ {
+ size_t to_index;
+ size_t newfromidx;
+ --idx;
+ to_index = (data[idx].self_pc / (HASHFRACTION * sizeof (*tos)));
+ newfromidx = fromidx++;
+ froms[newfromidx].here = &data[idx];
+ froms[newfromidx].link = tos[to_index];
+ tos[to_index] = newfromidx;
+ }
+
+ /* Setup counting data. */
+ if (kcountsize < highpc - lowpc)
+ {
+#if 0
+ s_scale = ((double) kcountsize / (highpc - lowpc)) * SCALE_1_TO_1;
+#else
+ size_t range = highpc - lowpc;
+ size_t quot = range / kcountsize;
+
+ if (quot >= SCALE_1_TO_1)
+ s_scale = 1;
+ else if (quot >= SCALE_1_TO_1 / 256)
+ s_scale = SCALE_1_TO_1 / quot;
+ else if (range > ULONG_MAX / 256)
+ s_scale = (SCALE_1_TO_1 * 256) / (range / (kcountsize / 256));
+ else
+ s_scale = (SCALE_1_TO_1 * 256) / ((range * 256) / kcountsize);
+#endif
+ }
+ else
+ s_scale = SCALE_1_TO_1;
+
+ /* Start the profiler. */
+ __profil ((void *) kcount, kcountsize, lowpc, s_scale);
+
+ /* Turn on profiling. */
+ running = 1;
+}
+
+
+void
+_dl_mcount (ElfW(Addr) frompc, ElfW(Addr) selfpc)
+{
+ volatile uint16_t *topcindex;
+ size_t i, fromindex;
+ struct here_fromstruct *fromp;
+
+ if (! running)
+ return;
+
+ /* Compute relative addresses. The shared object can be loaded at
+ any address. The value of frompc could be anything. We cannot
+ restrict it in any way, just set to a fixed value (0) in case it
+ is outside the allowed range. These calls show up as calls from
+ <external> in the gprof output. */
+ frompc -= lowpc;
+ if (frompc >= textsize)
+ frompc = 0;
+ selfpc -= lowpc;
+ if (selfpc >= textsize)
+ goto done;
+
+ /* Getting here we now have to find out whether the location was
+ already used. If yes we are lucky and only have to increment a
+ counter (this also has to be atomic). If the entry is new things
+ are getting complicated... */
+
+ /* Avoid integer divide if possible. */
+ if ((HASHFRACTION & (HASHFRACTION - 1)) == 0)
+ i = selfpc >> log_hashfraction;
+ else
+ i = selfpc / (HASHFRACTION * sizeof (*tos));
+
+ topcindex = &tos[i];
+ fromindex = *topcindex;
+
+ if (fromindex == 0)
+ goto check_new_or_add;
+
+ fromp = &froms[fromindex];
+
+ /* We have to look through the chain of arcs whether there is already
+ an entry for our arc. */
+ while (fromp->here->from_pc != frompc)
+ {
+ if (fromp->link != 0)
+ do
+ fromp = &froms[fromp->link];
+ while (fromp->link != 0 && fromp->here->from_pc != frompc);
+
+ if (fromp->here->from_pc != frompc)
+ {
+ topcindex = &fromp->link;
+
+ check_new_or_add:
+ /* Our entry is not among the entries we read so far from the
+ data file. Now see whether we have to update the list. */
+ while (narcs != *narcsp && narcs < fromlimit)
+ {
+ size_t to_index;
+ size_t newfromidx;
+ to_index = (data[narcs].self_pc
+ / (HASHFRACTION * sizeof (*tos)));
+ newfromidx = atomic_exchange_and_add (&fromidx, 1) + 1;
+ froms[newfromidx].here = &data[narcs];
+ froms[newfromidx].link = tos[to_index];
+ tos[to_index] = newfromidx;
+ atomic_increment (&narcs);
+ }
+
+ /* If we still have no entry stop searching and insert. */
+ if (*topcindex == 0)
+ {
+ uint_fast32_t newarc = atomic_exchange_and_add (narcsp, 1);
+
+ /* In rare cases it could happen that all entries in FROMS are
+ occupied. So we cannot count this anymore. */
+ if (newarc >= fromlimit)
+ goto done;
+
+ *topcindex = atomic_exchange_and_add (&fromidx, 1) + 1;
+ fromp = &froms[*topcindex];
+
+ fromp->here = &data[newarc];
+ data[newarc].from_pc = frompc;
+ data[newarc].self_pc = selfpc;
+ data[newarc].count = 0;
+ fromp->link = 0;
+ atomic_increment (&narcs);
+
+ break;
+ }
+
+ fromp = &froms[*topcindex];
+ }
+ else
+ /* Found in. */
+ break;
+ }
+
+ /* Increment the counter. */
+ atomic_increment (&fromp->here->count);
+
+ done:
+ ;
+}
+INTDEF(_dl_mcount)
diff --git a/libc/elf/dl-profstub.c b/libc/elf/dl-profstub.c
new file mode 100644
index 000000000..ad0f003ab
--- /dev/null
+++ b/libc/elf/dl-profstub.c
@@ -0,0 +1,42 @@
+/* Helper definitions for profiling of shared libraries.
+ Copyright (C) 1998, 2000, 2002, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <dlfcn.h>
+#include <elf.h>
+#include <ldsodefs.h>
+
+/* This is the map for the shared object we profile. It is defined here
+ only because we test for this value being NULL or not. */
+
+
+void
+_dl_mcount_wrapper (void *selfpc)
+{
+ GLRO(dl_mcount) ((ElfW(Addr)) RETURN_ADDRESS (0), (ElfW(Addr)) selfpc);
+}
+
+
+void
+_dl_mcount_wrapper_check (void *selfpc)
+{
+ if (GL(dl_profile_map) != NULL)
+ GLRO(dl_mcount) ((ElfW(Addr)) RETURN_ADDRESS (0), (ElfW(Addr)) selfpc);
+}
+libc_hidden_def (_dl_mcount_wrapper_check)
diff --git a/libc/elf/dl-reloc.c b/libc/elf/dl-reloc.c
new file mode 100644
index 000000000..117410e92
--- /dev/null
+++ b/libc/elf/dl-reloc.c
@@ -0,0 +1,371 @@
+/* Relocate a shared object and resolve its references to other loaded objects.
+ Copyright (C) 1995-2004, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <errno.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include "dynamic-link.h"
+
+/* Statistics function. */
+#ifdef SHARED
+# define bump_num_cache_relocations() ++GL(dl_num_cache_relocations)
+#else
+# define bump_num_cache_relocations() ((void) 0)
+#endif
+
+
+#ifdef USE_TLS
+/* We are trying to perform a static TLS relocation in MAP, but it was
+ dynamically loaded. This can only work if there is enough surplus in
+ the static TLS area already allocated for each running thread. If this
+ object's TLS segment is too big to fit, we fail. If it fits,
+ we set MAP->l_tls_offset and return.
+ This function intentionally does not return any value but signals error
+ directly, as static TLS should be rare and code handling it should
+ not be inlined as much as possible. */
+void
+internal_function __attribute_noinline__
+_dl_allocate_static_tls (struct link_map *map)
+{
+ /* If the alignment requirements are too high fail. */
+ if (map->l_tls_align > GL(dl_tls_static_align))
+ {
+ fail:
+ _dl_signal_error (0, map->l_name, NULL, N_("\
+cannot allocate memory in static TLS block"));
+ }
+
+# if TLS_TCB_AT_TP
+ size_t freebytes;
+ size_t n;
+ size_t blsize;
+
+ freebytes = GL(dl_tls_static_size) - GL(dl_tls_static_used) - TLS_TCB_SIZE;
+
+ blsize = map->l_tls_blocksize + map->l_tls_firstbyte_offset;
+ if (freebytes < blsize)
+ goto fail;
+
+ n = (freebytes - blsize) / map->l_tls_align;
+
+ size_t offset = GL(dl_tls_static_used) + (freebytes - n * map->l_tls_align
+ - map->l_tls_firstbyte_offset);
+
+ map->l_tls_offset = GL(dl_tls_static_used) = offset;
+# elif TLS_DTV_AT_TP
+ size_t used;
+ size_t check;
+
+ size_t offset = roundup (GL(dl_tls_static_used), map->l_tls_align);
+ used = offset + map->l_tls_blocksize;
+ check = used;
+ /* dl_tls_static_used includes the TCB at the beginning. */
+
+ if (check > GL(dl_tls_static_size))
+ goto fail;
+
+ map->l_tls_offset = offset;
+ GL(dl_tls_static_used) = used;
+# else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+
+ /* If the object is not yet relocated we cannot initialize the
+ static TLS region. Delay it. */
+ if (map->l_real->l_relocated)
+ {
+#ifdef SHARED
+ if (__builtin_expect (THREAD_DTV()[0].counter != GL(dl_tls_generation),
+ 0))
+ /* Update the slot information data for at least the generation of
+ the DSO we are allocating data for. */
+ (void) _dl_update_slotinfo (map->l_tls_modid);
+#endif
+
+ GL(dl_init_static_tls) (map);
+ }
+ else
+ map->l_need_tls_init = 1;
+}
+
+/* Initialize static TLS area and DTV for current (only) thread.
+ libpthread implementations should provide their own hook
+ to handle all threads. */
+void
+_dl_nothread_init_static_tls (struct link_map *map)
+{
+# if TLS_TCB_AT_TP
+ void *dest = (char *) THREAD_SELF - map->l_tls_offset;
+# elif TLS_DTV_AT_TP
+ void *dest = (char *) THREAD_SELF + map->l_tls_offset + TLS_PRE_TCB_SIZE;
+# else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+
+ /* Fill in the DTV slot so that a later LD/GD access will find it. */
+ dtv_t *dtv = THREAD_DTV ();
+ assert (map->l_tls_modid <= dtv[-1].counter);
+ dtv[map->l_tls_modid].pointer.val = dest;
+ dtv[map->l_tls_modid].pointer.is_static = true;
+
+ /* Initialize the memory. */
+ memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size),
+ '\0', map->l_tls_blocksize - map->l_tls_initimage_size);
+}
+#endif
+
+
+void
+_dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
+ int lazy, int consider_profiling)
+{
+ struct textrels
+ {
+ caddr_t start;
+ size_t len;
+ int prot;
+ struct textrels *next;
+ } *textrels = NULL;
+ /* Initialize it to make the compiler happy. */
+ const char *errstring = NULL;
+
+#ifdef SHARED
+ /* If we are auditing, install the same handlers we need for profiling. */
+ consider_profiling |= GLRO(dl_audit) != NULL;
+#elif defined PROF
+ /* Never use dynamic linker profiling for gprof profiling code. */
+# define consider_profiling 0
+#endif
+
+ if (l->l_relocated)
+ return;
+
+ /* If DT_BIND_NOW is set relocate all references in this object. We
+ do not do this if we are profiling, of course. */
+ // XXX Correct for auditing?
+ if (!consider_profiling
+ && __builtin_expect (l->l_info[DT_BIND_NOW] != NULL, 0))
+ lazy = 0;
+
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_RELOC, 0))
+ _dl_debug_printf ("\nrelocation processing: %s%s\n",
+ l->l_name[0] ? l->l_name : rtld_progname,
+ lazy ? " (lazy)" : "");
+
+ /* DT_TEXTREL is now in level 2 and might phase out at some time.
+ But we rewrite the DT_FLAGS entry to a DT_TEXTREL entry to make
+ testing easier and therefore it will be available at all time. */
+ if (__builtin_expect (l->l_info[DT_TEXTREL] != NULL, 0))
+ {
+ /* Bletch. We must make read-only segments writable
+ long enough to relocate them. */
+ const ElfW(Phdr) *ph;
+ for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
+ if (ph->p_type == PT_LOAD && (ph->p_flags & PF_W) == 0)
+ {
+ struct textrels *newp;
+
+ newp = (struct textrels *) alloca (sizeof (*newp));
+ newp->len = (((ph->p_vaddr + ph->p_memsz + GLRO(dl_pagesize) - 1)
+ & ~(GLRO(dl_pagesize) - 1))
+ - (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1)));
+ newp->start = ((ph->p_vaddr & ~(GLRO(dl_pagesize) - 1))
+ + (caddr_t) l->l_addr);
+
+ if (__mprotect (newp->start, newp->len, PROT_READ|PROT_WRITE) < 0)
+ {
+ errstring = N_("cannot make segment writable for relocation");
+ call_error:
+ _dl_signal_error (errno, l->l_name, NULL, errstring);
+ }
+
+#if (PF_R | PF_W | PF_X) == 7 && (PROT_READ | PROT_WRITE | PROT_EXEC) == 7
+ newp->prot = (PF_TO_PROT
+ >> ((ph->p_flags & (PF_R | PF_W | PF_X)) * 4)) & 0xf;
+#else
+ newp->prot = 0;
+ if (ph->p_flags & PF_R)
+ newp->prot |= PROT_READ;
+ if (ph->p_flags & PF_W)
+ newp->prot |= PROT_WRITE;
+ if (ph->p_flags & PF_X)
+ newp->prot |= PROT_EXEC;
+#endif
+ newp->next = textrels;
+ textrels = newp;
+ }
+ }
+
+ {
+ /* Do the actual relocation of the object's GOT and other data. */
+
+ /* String table object symbols. */
+ const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
+
+ /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code. */
+#define RESOLVE_MAP(ref, version, r_type) \
+ (ELFW(ST_BIND) ((*ref)->st_info) != STB_LOCAL \
+ ? ((__builtin_expect ((*ref) == l->l_lookup_cache.sym, 0) \
+ && elf_machine_type_class (r_type) == l->l_lookup_cache.type_class) \
+ ? (bump_num_cache_relocations (), \
+ (*ref) = l->l_lookup_cache.ret, \
+ l->l_lookup_cache.value) \
+ : ({ lookup_t _lr; \
+ int _tc = elf_machine_type_class (r_type); \
+ l->l_lookup_cache.type_class = _tc; \
+ l->l_lookup_cache.sym = (*ref); \
+ const struct r_found_version *v = NULL; \
+ int flags = DL_LOOKUP_ADD_DEPENDENCY; \
+ if ((version) != NULL && (version)->hash != 0) \
+ { \
+ v = (version); \
+ flags = 0; \
+ } \
+ _lr = _dl_lookup_symbol_x (strtab + (*ref)->st_name, l, (ref), \
+ scope, v, _tc, flags, NULL); \
+ l->l_lookup_cache.ret = (*ref); \
+ l->l_lookup_cache.value = _lr; })) \
+ : l)
+
+ /* This macro is used as a callback from elf_machine_rel{a,} when a
+ static TLS reloc is about to be performed. Since (in dl-load.c) we
+ permit dynamic loading of objects that might use such relocs, we
+ have to check whether each use is actually doable. If the object
+ whose TLS segment the reference resolves to was allocated space in
+ the static TLS block at startup, then it's ok. Otherwise, we make
+ an attempt to allocate it in surplus space on the fly. If that
+ can't be done, we fall back to the error that DF_STATIC_TLS is
+ intended to produce. */
+#define CHECK_STATIC_TLS(map, sym_map) \
+ do { \
+ if (__builtin_expect ((sym_map)->l_tls_offset == NO_TLS_OFFSET, 0)) \
+ _dl_allocate_static_tls (sym_map); \
+ } while (0)
+
+#include "dynamic-link.h"
+
+ ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling);
+
+#ifndef PROF
+ if (__builtin_expect (consider_profiling, 0))
+ {
+ /* Allocate the array which will contain the already found
+ relocations. If the shared object lacks a PLT (for example
+ if it only contains lead function) the l_info[DT_PLTRELSZ]
+ will be NULL. */
+ if (l->l_info[DT_PLTRELSZ] == NULL)
+ {
+ errstring = N_("%s: no PLTREL found in object %s\n");
+ fatal:
+ _dl_fatal_printf (errstring,
+ rtld_progname ?: "<program name unknown>",
+ l->l_name);
+ }
+
+ l->l_reloc_result = calloc (sizeof (l->l_reloc_result[0]),
+ l->l_info[DT_PLTRELSZ]->d_un.d_val);
+ if (l->l_reloc_result == NULL)
+ {
+ errstring = N_("\
+%s: out of memory to store relocation results for %s\n");
+ goto fatal;
+ }
+ }
+#endif
+ }
+
+ /* Mark the object so we know this work has been done. */
+ l->l_relocated = 1;
+
+ /* Undo the segment protection changes. */
+ while (__builtin_expect (textrels != NULL, 0))
+ {
+ if (__mprotect (textrels->start, textrels->len, textrels->prot) < 0)
+ {
+ errstring = N_("cannot restore segment prot after reloc");
+ goto call_error;
+ }
+
+ textrels = textrels->next;
+ }
+
+ /* In case we can protect the data now that the relocations are
+ done, do it. */
+ if (l->l_relro_size != 0)
+ _dl_protect_relro (l);
+}
+
+
+void internal_function
+_dl_protect_relro (struct link_map *l)
+{
+ ElfW(Addr) start = ((l->l_addr + l->l_relro_addr)
+ & ~(GLRO(dl_pagesize) - 1));
+ ElfW(Addr) end = ((l->l_addr + l->l_relro_addr + l->l_relro_size)
+ & ~(GLRO(dl_pagesize) - 1));
+
+ if (start != end
+ && __mprotect ((void *) start, end - start, PROT_READ) < 0)
+ {
+ static const char errstring[] = N_("\
+cannot apply additional memory protection after relocation");
+ _dl_signal_error (errno, l->l_name, NULL, errstring);
+ }
+}
+
+void
+internal_function __attribute_noinline__
+_dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt)
+{
+ extern const char INTUSE(_itoa_lower_digits)[] attribute_hidden;
+#define DIGIT(b) INTUSE(_itoa_lower_digits)[(b) & 0xf];
+
+ /* XXX We cannot translate these messages. */
+ static const char msg[2][32
+#if __ELF_NATIVE_CLASS == 64
+ + 6
+#endif
+ ] = { "unexpected reloc type 0x",
+ "unexpected PLT reloc type 0x" };
+ char msgbuf[sizeof (msg[0])];
+ char *cp;
+
+ cp = __stpcpy (msgbuf, msg[plt]);
+#if __ELF_NATIVE_CLASS == 64
+ if (__builtin_expect(type > 0xff, 0))
+ {
+ *cp++ = DIGIT (type >> 28);
+ *cp++ = DIGIT (type >> 24);
+ *cp++ = DIGIT (type >> 20);
+ *cp++ = DIGIT (type >> 16);
+ *cp++ = DIGIT (type >> 12);
+ *cp++ = DIGIT (type >> 8);
+ }
+#endif
+ *cp++ = DIGIT (type >> 4);
+ *cp++ = DIGIT (type);
+ *cp = '\0';
+
+ _dl_signal_error (0, map->l_name, NULL, msgbuf);
+}
diff --git a/libc/elf/dl-runtime.c b/libc/elf/dl-runtime.c
new file mode 100644
index 000000000..f92cbe26b
--- /dev/null
+++ b/libc/elf/dl-runtime.c
@@ -0,0 +1,415 @@
+/* On-demand PLT fixup for shared objects.
+ Copyright (C) 1995-2002,2003,2004,2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#define IN_DL_RUNTIME 1 /* This can be tested in dl-machine.h. */
+
+#include <alloca.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <ldsodefs.h>
+#include "dynamic-link.h"
+
+#if (!defined ELF_MACHINE_NO_RELA && !defined ELF_MACHINE_PLT_REL) \
+ || ELF_MACHINE_NO_REL
+# define PLTREL ElfW(Rela)
+#else
+# define PLTREL ElfW(Rel)
+#endif
+
+#ifndef VERSYMIDX
+# define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym))
+#endif
+
+/* The fixup functions might have need special attributes. If none
+ are provided define the macro as empty. */
+#ifndef ARCH_FIXUP_ATTRIBUTE
+# define ARCH_FIXUP_ATTRIBUTE
+#endif
+
+
+/* This function is called through a special trampoline from the PLT the
+ first time each PLT entry is called. We must perform the relocation
+ specified in the PLT of the given shared object, and return the resolved
+ function address to the trampoline, which will restart the original call
+ to that address. Future calls will bounce directly from the PLT to the
+ function. */
+
+#ifndef ELF_MACHINE_NO_PLT
+DL_FIXUP_VALUE_TYPE
+__attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE
+_dl_fixup (
+# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
+ ELF_MACHINE_RUNTIME_FIXUP_ARGS,
+# endif
+ /* GKM FIXME: Fix trampoline to pass bounds so we can do
+ without the `__unbounded' qualifier. */
+ struct link_map *__unbounded l, ElfW(Word) reloc_offset)
+{
+ const ElfW(Sym) *const symtab
+ = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
+ const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
+
+ const PLTREL *const reloc
+ = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
+ const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
+ void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
+ lookup_t result;
+ DL_FIXUP_VALUE_TYPE value;
+
+ /* Sanity check that we're really looking at a PLT relocation. */
+ assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
+
+ /* Look up the target symbol. If the normal lookup rules are not
+ used don't look in the global scope. */
+ if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
+ {
+ const struct r_found_version *version = NULL;
+
+ if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
+ {
+ const ElfW(Half) *vernum =
+ (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
+ ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
+ version = &l->l_versions[ndx];
+ if (version->hash == 0)
+ version = NULL;
+ }
+
+ result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
+ l->l_scope, version, ELF_RTYPE_CLASS_PLT,
+ DL_LOOKUP_ADD_DEPENDENCY, NULL);
+
+ /* Currently result contains the base load address (or link map)
+ of the object that defines sym. Now add in the symbol
+ offset. */
+ value = DL_FIXUP_MAKE_VALUE (result,
+ sym ? LOOKUP_VALUE_ADDRESS (result)
+ + sym->st_value : 0);
+ }
+ else
+ {
+ /* We already found the symbol. The module (and therefore its load
+ address) is also known. */
+ value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value);
+ result = l;
+ }
+
+ /* And now perhaps the relocation addend. */
+ value = elf_machine_plt_value (l, reloc, value);
+
+ /* Finally, fix up the plt itself. */
+ if (__builtin_expect (GLRO(dl_bind_not), 0))
+ return value;
+
+ return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
+}
+#endif
+
+#if !defined PROF && !defined ELF_MACHINE_NO_PLT && !__BOUNDED_POINTERS__
+
+DL_FIXUP_VALUE_TYPE
+__attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE
+_dl_profile_fixup (
+#ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
+ ELF_MACHINE_RUNTIME_FIXUP_ARGS,
+#endif
+ struct link_map *l, ElfW(Word) reloc_offset,
+ ElfW(Addr) retaddr, void *regs, long int *framesizep)
+{
+ void (*mcount_fct) (ElfW(Addr), ElfW(Addr)) = INTUSE(_dl_mcount);
+
+ /* This is the address in the array where we store the result of previous
+ relocations. */
+ struct reloc_result *reloc_result
+ = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)];
+ DL_FIXUP_VALUE_TYPE *resultp = &reloc_result->addr;
+
+ DL_FIXUP_VALUE_TYPE value = *resultp;
+ if (DL_FIXUP_VALUE_CODE_ADDR (value) == 0)
+ {
+ /* This is the first time we have to relocate this object. */
+ const ElfW(Sym) *const symtab
+ = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
+ const char *strtab = (const char *) D_PTR (l, l_info[DT_STRTAB]);
+
+ const PLTREL *const reloc
+ = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
+ const ElfW(Sym) *refsym = &symtab[ELFW(R_SYM) (reloc->r_info)];
+ const ElfW(Sym) *defsym = refsym;
+ lookup_t result;
+
+ /* Sanity check that we're really looking at a PLT relocation. */
+ assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
+
+ /* Look up the target symbol. If the symbol is marked STV_PROTECTED
+ don't look in the global scope. */
+ if (__builtin_expect (ELFW(ST_VISIBILITY) (refsym->st_other), 0) == 0)
+ {
+ const struct r_found_version *version = NULL;
+
+ if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
+ {
+ const ElfW(Half) *vernum =
+ (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
+ ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
+ version = &l->l_versions[ndx];
+ if (version->hash == 0)
+ version = NULL;
+ }
+
+ result = _dl_lookup_symbol_x (strtab + refsym->st_name, l, &defsym,
+ l->l_scope, version,
+ ELF_RTYPE_CLASS_PLT,
+ DL_LOOKUP_ADD_DEPENDENCY, NULL);
+
+ /* Currently result contains the base load address (or link map)
+ of the object that defines sym. Now add in the symbol
+ offset. */
+ value = DL_FIXUP_MAKE_VALUE (result,
+ defsym != NULL
+ ? LOOKUP_VALUE_ADDRESS (result)
+ + defsym->st_value : 0);
+ }
+ else
+ {
+ /* We already found the symbol. The module (and therefore its load
+ address) is also known. */
+ value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + refsym->st_value);
+ result = l;
+ }
+ /* And now perhaps the relocation addend. */
+ value = elf_machine_plt_value (l, reloc, value);
+
+#ifdef SHARED
+ /* Auditing checkpoint: we have a new binding. Provide the
+ auditing libraries the possibility to change the value and
+ tell us whether further auditing is wanted. */
+ if (defsym != NULL && GLRO(dl_naudit) > 0)
+ {
+ reloc_result->bound = result;
+ /* Compute index of the symbol entry in the symbol table of
+ the DSO with the definition. */
+ reloc_result->boundndx = (defsym
+ - (ElfW(Sym) *) D_PTR (result,
+ l_info[DT_SYMTAB]));
+
+ /* Determine whether any of the two participating DSOs is
+ interested in auditing. */
+ if ((l->l_audit_any_plt | result->l_audit_any_plt) != 0)
+ {
+ unsigned int altvalue = 0;
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ /* Synthesize a symbol record where the st_value field is
+ the result. */
+ ElfW(Sym) sym = *defsym;
+ sym.st_value = DL_FIXUP_VALUE_ADDR (value);
+
+ /* Keep track whether there is any interest in tracing
+ the call in the lower two bits. */
+ assert (DL_NNS * 2 <= sizeof (reloc_result->flags) * 8);
+ assert ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) == 3);
+ reloc_result->enterexit = LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT;
+
+ const char *strtab2 = (const void *) D_PTR (result,
+ l_info[DT_STRTAB]);
+
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ /* XXX Check whether both DSOs must request action or
+ only one */
+ if ((l->l_audit[cnt].bindflags & LA_FLG_BINDFROM) != 0
+ && (result->l_audit[cnt].bindflags & LA_FLG_BINDTO) != 0)
+ {
+ unsigned int flags = altvalue;
+ if (afct->symbind != NULL)
+ {
+ uintptr_t new_value
+ = afct->symbind (&sym, reloc_result->boundndx,
+ &l->l_audit[cnt].cookie,
+ &result->l_audit[cnt].cookie,
+ &flags,
+ strtab2 + defsym->st_name);
+ if (new_value != (uintptr_t) sym.st_value)
+ {
+ altvalue = LA_SYMB_ALTVALUE;
+ sym.st_value = new_value;
+ }
+ }
+
+ /* Remember the results for every audit library and
+ store a summary in the first two bits. */
+ reloc_result->enterexit
+ &= flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT);
+ reloc_result->enterexit
+ |= ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT))
+ << ((cnt + 1) * 2));
+ }
+ else
+ /* If the bind flags say this auditor is not interested,
+ set the bits manually. */
+ reloc_result->enterexit
+ |= ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)
+ << ((cnt + 1) * 2));
+
+ afct = afct->next;
+ }
+
+ reloc_result->flags = altvalue;
+ value = DL_FIXUP_ADDR_VALUE (sym.st_value);
+ }
+ else
+ /* Set all bits since this symbol binding is not interesting. */
+ reloc_result->enterexit = (1u << DL_NNS) - 1;
+ }
+#endif
+
+ /* Store the result for later runs. */
+ if (__builtin_expect (! GLRO(dl_bind_not), 1))
+ *resultp = value;
+ }
+
+ /* By default we do not call the pltexit function. */
+ long int framesize = -1;
+
+#ifdef SHARED
+ /* Auditing checkpoint: report the PLT entering and allow the
+ auditors to change the value. */
+ if (DL_FIXUP_VALUE_CODE_ADDR (value) != 0 && GLRO(dl_naudit) > 0
+ /* Don't do anything if no auditor wants to intercept this call. */
+ && (reloc_result->enterexit & LA_SYMB_NOPLTENTER) == 0)
+ {
+ ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound,
+ l_info[DT_SYMTAB])
+ + reloc_result->boundndx);
+
+ /* Set up the sym parameter. */
+ ElfW(Sym) sym = *defsym;
+ sym.st_value = DL_FIXUP_VALUE_ADDR (value);
+
+ /* Get the symbol name. */
+ const char *strtab = (const void *) D_PTR (reloc_result->bound,
+ l_info[DT_STRTAB]);
+ const char *symname = strtab + sym.st_name;
+
+ /* Keep track of overwritten addresses. */
+ unsigned int altvalue = reloc_result->flags;
+
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->ARCH_LA_PLTENTER != NULL
+ && (reloc_result->enterexit
+ & (LA_SYMB_NOPLTENTER << (2 * (cnt + 1)))) == 0)
+ {
+ unsigned int flags = altvalue;
+ long int new_framesize = -1;
+ uintptr_t new_value
+ = afct->ARCH_LA_PLTENTER (&sym, reloc_result->boundndx,
+ &l->l_audit[cnt].cookie,
+ &reloc_result->bound->l_audit[cnt].cookie,
+ regs, &flags, symname,
+ &new_framesize);
+ if (new_value != (uintptr_t) sym.st_value)
+ {
+ altvalue = LA_SYMB_ALTVALUE;
+ sym.st_value = new_value;
+ }
+
+ /* Remember the results for every audit library and
+ store a summary in the first two bits. */
+ reloc_result->enterexit
+ |= ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT))
+ << (2 * (cnt + 1)));
+
+ if ((reloc_result->enterexit & (LA_SYMB_NOPLTEXIT
+ << (2 * (cnt + 1))))
+ == 0 && new_framesize != -1 && framesize != -2)
+ {
+ /* If this is the first call providing information,
+ use it. */
+ if (framesize == -1)
+ framesize = new_framesize;
+ /* If two pltenter calls provide conflicting information,
+ use the larger value. */
+ else if (new_framesize != framesize)
+ framesize = MAX (new_framesize, framesize);
+ }
+ }
+
+ afct = afct->next;
+ }
+
+ value = DL_FIXUP_ADDR_VALUE (sym.st_value);
+ }
+#endif
+
+ /* Store the frame size information. */
+ *framesizep = framesize;
+
+ (*mcount_fct) (retaddr, DL_FIXUP_VALUE_CODE_ADDR (value));
+
+ return value;
+}
+
+#endif /* PROF && ELF_MACHINE_NO_PLT */
+
+
+#include <stdio.h>
+void
+ARCH_FIXUP_ATTRIBUTE
+_dl_call_pltexit (struct link_map *l, ElfW(Word) reloc_offset,
+ const void *inregs, void *outregs)
+{
+#ifdef SHARED
+ /* This is the address in the array where we store the result of previous
+ relocations. */
+ // XXX Maybe the bound information must be stored on the stack since
+ // XXX with bind_not a new value could have been stored in the meantime.
+ struct reloc_result *reloc_result
+ = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)];
+ ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound,
+ l_info[DT_SYMTAB])
+ + reloc_result->boundndx);
+
+ /* Set up the sym parameter. */
+ ElfW(Sym) sym = *defsym;
+
+ /* Get the symbol name. */
+ const char *strtab = (const void *) D_PTR (reloc_result->bound,
+ l_info[DT_STRTAB]);
+ const char *symname = strtab + sym.st_name;
+
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->ARCH_LA_PLTEXIT != NULL
+ && (reloc_result->enterexit
+ & (LA_SYMB_NOPLTEXIT >> (2 * cnt))) == 0)
+ {
+ afct->ARCH_LA_PLTEXIT (&sym, reloc_result->boundndx,
+ &l->l_audit[cnt].cookie,
+ &reloc_result->bound->l_audit[cnt].cookie,
+ inregs, outregs, symname);
+ }
+
+ afct = afct->next;
+ }
+#endif
+}
diff --git a/libc/elf/dl-sbrk.c b/libc/elf/dl-sbrk.c
new file mode 100644
index 000000000..4713a9269
--- /dev/null
+++ b/libc/elf/dl-sbrk.c
@@ -0,0 +1,5 @@
+/* We can use the normal code but we also know the __curbrk is not exported
+ from ld.so. */
+extern void *__curbrk attribute_hidden;
+
+#include <sbrk.c>
diff --git a/libc/elf/dl-support.c b/libc/elf/dl-support.c
new file mode 100644
index 000000000..c3b6350ed
--- /dev/null
+++ b/libc/elf/dl-support.c
@@ -0,0 +1,313 @@
+/* Support for dynamic linking code in static libc.
+ Copyright (C) 1996-2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/* This file defines some things that for the dynamic linker are defined in
+ rtld.c and dl-sysdep.c in ways appropriate to bootstrap dynamic linking. */
+
+#include <errno.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <dl-machine.h>
+#include <bits/libc-lock.h>
+#include <dl-cache.h>
+#include <dl-librecon.h>
+#include <unsecvars.h>
+#include <hp-timing.h>
+
+extern char *__progname;
+char **_dl_argv = &__progname; /* This is checked for some error messages. */
+
+/* Name of the architecture. */
+const char *_dl_platform;
+size_t _dl_platformlen;
+
+int _dl_debug_mask;
+int _dl_lazy;
+ElfW(Addr) _dl_use_load_bias = -2;
+int _dl_dynamic_weak;
+
+/* If nonzero print warnings about problematic situations. */
+int _dl_verbose;
+
+/* We never do profiling. */
+const char *_dl_profile;
+const char *_dl_profile_output;
+
+/* Names of shared object for which the RUNPATHs and RPATHs should be
+ ignored. */
+const char *_dl_inhibit_rpath;
+
+/* The map for the object we will profile. */
+struct link_map *_dl_profile_map;
+
+/* This is the address of the last stack address ever used. */
+void *__libc_stack_end;
+
+/* Path where the binary is found. */
+const char *_dl_origin_path;
+
+/* Nonzero if runtime lookup should not update the .got/.plt. */
+int _dl_bind_not;
+
+/* Namespace information. */
+struct link_namespaces _dl_ns[DL_NNS];
+
+/* Incremented whenever something may have been added to dl_loaded. */
+unsigned long long _dl_load_adds;
+
+/* Fake scope. In dynamically linked binaries this is the scope of the
+ main application but here we don't have something like this. So
+ create a fake scope containing nothing. */
+struct r_scope_elem _dl_initial_searchlist;
+
+#ifndef HAVE_INLINED_SYSCALLS
+/* Nonzero during startup. */
+int _dl_starting_up = 1;
+#endif
+
+/* Get architecture specific initializer. */
+#include <dl-procinfo.c>
+
+/* We expect less than a second for relocation. */
+#ifdef HP_SMALL_TIMING_AVAIL
+# undef HP_TIMING_AVAIL
+# define HP_TIMING_AVAIL HP_SMALL_TIMING_AVAIL
+#endif
+
+/* Initial value of the CPU clock. */
+#ifndef HP_TIMING_NONAVAIL
+hp_timing_t _dl_cpuclock_offset;
+#endif
+
+#ifdef USE_TLS
+void (*_dl_init_static_tls) (struct link_map *) = &_dl_nothread_init_static_tls;
+#endif
+
+size_t _dl_pagesize;
+
+unsigned int _dl_osversion;
+
+/* All known directories in sorted order. */
+struct r_search_path_elem *_dl_all_dirs;
+
+/* All directories after startup. */
+struct r_search_path_elem *_dl_init_all_dirs;
+
+/* The object to be initialized first. */
+struct link_map *_dl_initfirst;
+
+/* Descriptor to write debug messages to. */
+int _dl_debug_fd = STDERR_FILENO;
+
+int _dl_correct_cache_id = _DL_CACHE_DEFAULT_ID;
+
+ElfW(Phdr) *_dl_phdr;
+size_t _dl_phnum;
+uint64_t _dl_hwcap __attribute__ ((nocommon));
+
+/* Prevailing state of the stack, PF_X indicating it's executable. */
+ElfW(Word) _dl_stack_flags = PF_R|PF_W|PF_X;
+
+/* If loading a shared object requires that we make the stack executable
+ when it was not, we do it by calling this function.
+ It returns an errno code or zero on success. */
+int (*_dl_make_stack_executable_hook) (void **) internal_function
+ = _dl_make_stack_executable;
+
+
+#ifdef NEED_DL_SYSINFO
+/* Needed for improved syscall handling on at least x86/Linux. */
+uintptr_t _dl_sysinfo = DL_SYSINFO_DEFAULT;
+#endif
+#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO
+/* Address of the ELF headers in the vsyscall page. */
+const ElfW(Ehdr) *_dl_sysinfo_dso;
+#endif
+
+/* During the program run we must not modify the global data of
+ loaded shared object simultanously in two threads. Therefore we
+ protect `_dl_open' and `_dl_close' in dl-close.c.
+
+ This must be a recursive lock since the initializer function of
+ the loaded object might as well require a call to this function.
+ At this time it is not anymore a problem to modify the tables. */
+__rtld_lock_define_initialized_recursive (, _dl_load_lock)
+
+
+#ifdef HAVE_AUX_VECTOR
+int _dl_clktck;
+
+void
+internal_function
+_dl_aux_init (ElfW(auxv_t) *av)
+{
+ int seen = 0;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ for (; av->a_type != AT_NULL; ++av)
+ switch (av->a_type)
+ {
+ case AT_PAGESZ:
+ GLRO(dl_pagesize) = av->a_un.a_val;
+ break;
+ case AT_CLKTCK:
+ GLRO(dl_clktck) = av->a_un.a_val;
+ break;
+ case AT_PHDR:
+ GL(dl_phdr) = (void *) av->a_un.a_val;
+ break;
+ case AT_PHNUM:
+ GL(dl_phnum) = av->a_un.a_val;
+ break;
+ case AT_HWCAP:
+ GLRO(dl_hwcap) = (unsigned long int) av->a_un.a_val;
+ break;
+#ifdef NEED_DL_SYSINFO
+ case AT_SYSINFO:
+ GL(dl_sysinfo) = av->a_un.a_val;
+ break;
+#endif
+#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO
+ case AT_SYSINFO_EHDR:
+ GL(dl_sysinfo_dso) = (void *) av->a_un.a_val;
+ break;
+#endif
+ case AT_UID:
+ uid ^= av->a_un.a_val;
+ seen |= 1;
+ break;
+ case AT_EUID:
+ uid ^= av->a_un.a_val;
+ seen |= 2;
+ break;
+ case AT_GID:
+ gid ^= av->a_un.a_val;
+ seen |= 4;
+ break;
+ case AT_EGID:
+ gid ^= av->a_un.a_val;
+ seen |= 8;
+ break;
+ case AT_SECURE:
+ seen = -1;
+ __libc_enable_secure = av->a_un.a_val;
+ __libc_enable_secure_decided = 1;
+ break;
+ }
+ if (seen == 0xf)
+ {
+ __libc_enable_secure = uid != 0 || gid != 0;
+ __libc_enable_secure_decided = 1;
+ }
+}
+#endif
+
+
+void
+internal_function
+_dl_non_dynamic_init (void)
+{
+ if (HP_TIMING_AVAIL)
+ HP_TIMING_NOW (_dl_cpuclock_offset);
+
+ if (!_dl_pagesize)
+ _dl_pagesize = __getpagesize ();
+
+ _dl_verbose = *(getenv ("LD_WARN") ?: "") == '\0' ? 0 : 1;
+
+ /* Initialize the data structures for the search paths for shared
+ objects. */
+ _dl_init_paths (getenv ("LD_LIBRARY_PATH"));
+
+ _dl_lazy = *(getenv ("LD_BIND_NOW") ?: "") == '\0';
+
+ _dl_bind_not = *(getenv ("LD_BIND_NOT") ?: "") != '\0';
+
+ _dl_dynamic_weak = *(getenv ("LD_DYNAMIC_WEAK") ?: "") == '\0';
+
+ _dl_profile_output = getenv ("LD_PROFILE_OUTPUT");
+ if (_dl_profile_output == NULL || _dl_profile_output[0] == '\0')
+ _dl_profile_output
+ = &"/var/tmp\0/var/profile"[__libc_enable_secure ? 9 : 0];
+
+ if (__libc_enable_secure)
+ {
+ static const char unsecure_envvars[] =
+ UNSECURE_ENVVARS
+#ifdef EXTRA_UNSECURE_ENVVARS
+ EXTRA_UNSECURE_ENVVARS
+#endif
+ ;
+ const char *cp = unsecure_envvars;
+
+ while (cp < unsecure_envvars + sizeof (unsecure_envvars))
+ {
+ __unsetenv (cp);
+ cp = (const char *) __rawmemchr (cp, '\0') + 1;
+ }
+
+ if (__access ("/etc/suid-debug", F_OK) != 0)
+ __unsetenv ("MALLOC_CHECK_");
+ }
+
+#ifdef DL_PLATFORM_INIT
+ DL_PLATFORM_INIT;
+#endif
+
+#ifdef DL_OSVERSION_INIT
+ DL_OSVERSION_INIT;
+#endif
+
+ /* Now determine the length of the platform string. */
+ if (_dl_platform != NULL)
+ _dl_platformlen = strlen (_dl_platform);
+
+ /* Scan for a program header telling us the stack is nonexecutable. */
+ if (_dl_phdr != NULL)
+ for (uint_fast16_t i = 0; i < _dl_phnum; ++i)
+ if (_dl_phdr[i].p_type == PT_GNU_STACK)
+ {
+ _dl_stack_flags = _dl_phdr[i].p_flags;
+ break;
+ }
+}
+
+
+const struct r_strlenpair *
+internal_function
+_dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
+ size_t *max_capstrlen)
+{
+ static struct r_strlenpair result;
+ static char buf[1];
+
+ result.str = buf; /* Does not really matter. */
+ result.len = 0;
+
+ *sz = 1;
+ return &result;
+}
+
+
+#ifdef DL_SYSINFO_IMPLEMENTATION
+DL_SYSINFO_IMPLEMENTATION
+#endif
diff --git a/libc/elf/dl-sym.c b/libc/elf/dl-sym.c
new file mode 100644
index 000000000..d2b0ec0da
--- /dev/null
+++ b/libc/elf/dl-sym.c
@@ -0,0 +1,210 @@
+/* Look up a symbol in a shared object loaded by `dlopen'.
+ Copyright (C) 1999,2000,2001,2002,2004,2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <stddef.h>
+#include <setjmp.h>
+#include <libintl.h>
+
+#include <dlfcn.h>
+#include <ldsodefs.h>
+#include <dl-hash.h>
+#ifdef USE_TLS
+# include <dl-tls.h>
+#endif
+
+
+#if defined USE_TLS && defined SHARED
+/* Systems which do not have tls_index also probably have to define
+ DONT_USE_TLS_INDEX. */
+
+# ifndef __TLS_GET_ADDR
+# define __TLS_GET_ADDR __tls_get_addr
+# endif
+
+/* Return the symbol address given the map of the module it is in and
+ the symbol record. This is used in dl-sym.c. */
+static void *
+internal_function
+_dl_tls_symaddr (struct link_map *map, const ElfW(Sym) *ref)
+{
+# ifndef DONT_USE_TLS_INDEX
+ tls_index tmp =
+ {
+ .ti_module = map->l_tls_modid,
+ .ti_offset = ref->st_value
+ };
+
+ return __TLS_GET_ADDR (&tmp);
+# else
+ return __TLS_GET_ADDR (map->l_tls_modid, ref->st_value);
+# endif
+}
+#endif
+
+
+static void *
+internal_function
+do_sym (void *handle, const char *name, void *who,
+ struct r_found_version *vers, int flags)
+{
+ const ElfW(Sym) *ref = NULL;
+ lookup_t result;
+ ElfW(Addr) caller = (ElfW(Addr)) who;
+
+ /* If the address is not recognized the call comes from the main
+ program (we hope). */
+ struct link_map *match = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+
+ /* Find the highest-addressed object that CALLER is not below. */
+ for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
+ for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL;
+ l = l->l_next)
+ if (caller >= l->l_map_start && caller < l->l_map_end)
+ {
+ /* There must be exactly one DSO for the range of the virtual
+ memory. Otherwise something is really broken. */
+ match = l;
+ break;
+ }
+
+ if (handle == RTLD_DEFAULT)
+ /* Search the global scope. */
+ result = GLRO(dl_lookup_symbol_x) (name, match, &ref, match->l_scope,
+ vers, 0, flags|DL_LOOKUP_ADD_DEPENDENCY,
+ NULL);
+ else if (handle == RTLD_NEXT)
+ {
+ if (__builtin_expect (match == GL(dl_ns)[LM_ID_BASE]._ns_loaded, 0))
+ {
+ if (match == NULL
+ || caller < match->l_map_start
+ || caller >= match->l_map_end)
+ GLRO(dl_signal_error) (0, NULL, NULL, N_("\
+RTLD_NEXT used in code not dynamically loaded"));
+ }
+
+ struct link_map *l = match;
+ while (l->l_loader != NULL)
+ l = l->l_loader;
+
+ result = GLRO(dl_lookup_symbol_x) (name, match, &ref, l->l_local_scope,
+ vers, 0, 0, match);
+ }
+ else
+ {
+ /* Search the scope of the given object. */
+ struct link_map *map = handle;
+ result = GLRO(dl_lookup_symbol_x) (name, map, &ref, map->l_local_scope,
+ vers, 0, flags, NULL);
+ }
+
+ if (ref != NULL)
+ {
+ void *value;
+
+#if defined USE_TLS && defined SHARED
+ if (ELFW(ST_TYPE) (ref->st_info) == STT_TLS)
+ /* The found symbol is a thread-local storage variable.
+ Return the address for to the current thread. */
+ value = _dl_tls_symaddr (result, ref);
+ else
+#endif
+ value = DL_SYMBOL_ADDRESS (result, ref);
+
+#ifdef SHARED
+ /* Auditing checkpoint: we have a new binding. Provide the
+ auditing libraries the possibility to change the value and
+ tell us whether further auditing is wanted. */
+ if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
+ {
+ const char *strtab = (const char *) D_PTR (result,
+ l_info[DT_STRTAB]);
+ /* Compute index of the symbol entry in the symbol table of
+ the DSO with the definition. */
+ unsigned int ndx = (ref - (ElfW(Sym) *) D_PTR (result,
+ l_info[DT_SYMTAB]));
+
+ if ((match->l_audit_any_plt | result->l_audit_any_plt) != 0)
+ {
+ unsigned int altvalue = 0;
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ /* Synthesize a symbol record where the st_value field is
+ the result. */
+ ElfW(Sym) sym = *ref;
+ sym.st_value = (ElfW(Addr)) value;
+
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->symbind != NULL
+ && ((match->l_audit[cnt].bindflags & LA_FLG_BINDFROM)
+ != 0
+ || ((result->l_audit[cnt].bindflags & LA_FLG_BINDTO)
+ != 0)))
+ {
+ unsigned int flags = altvalue | LA_SYMB_DLSYM;
+ uintptr_t new_value
+ = afct->symbind (&sym, ndx,
+ &match->l_audit[cnt].cookie,
+ &result->l_audit[cnt].cookie,
+ &flags, strtab + ref->st_name);
+ if (new_value != (uintptr_t) sym.st_value)
+ {
+ altvalue = LA_SYMB_ALTVALUE;
+ sym.st_value = new_value;
+ }
+ }
+
+ afct = afct->next;
+ }
+
+ value = (void *) sym.st_value;
+ }
+ }
+#endif
+
+ return value;
+ }
+
+ return NULL;
+}
+
+
+void *
+internal_function
+_dl_vsym (void *handle, const char *name, const char *version, void *who)
+{
+ struct r_found_version vers;
+
+ /* Compute hash value to the version string. */
+ vers.name = version;
+ vers.hidden = 1;
+ vers.hash = _dl_elf_hash (version);
+ /* We don't have a specific file where the symbol can be found. */
+ vers.filename = NULL;
+
+ return do_sym (handle, name, who, &vers, 0);
+}
+
+
+void *
+internal_function
+_dl_sym (void *handle, const char *name, void *who)
+{
+ return do_sym (handle, name, who, NULL, DL_LOOKUP_RETURN_NEWEST);
+}
diff --git a/libc/elf/dl-symaddr.c b/libc/elf/dl-symaddr.c
new file mode 100644
index 000000000..3c850ca83
--- /dev/null
+++ b/libc/elf/dl-symaddr.c
@@ -0,0 +1,33 @@
+/* Get the symbol address. Generic version.
+ Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <ldsodefs.h>
+#include <dl-fptr.h>
+
+void *
+_dl_symbol_address (struct link_map *map, const ElfW(Sym) *ref)
+{
+ ElfW(Addr) value = (map ? map->l_addr : 0) + ref->st_value;
+
+ /* Return the pointer to function descriptor. */
+ if (ELFW(ST_TYPE) (ref->st_info) == STT_FUNC)
+ return (void *) _dl_make_fptr (map, ref, value);
+ else
+ return (void *) value;
+}
diff --git a/libc/elf/dl-sysdep.c b/libc/elf/dl-sysdep.c
new file mode 100644
index 000000000..985e2b8f7
--- /dev/null
+++ b/libc/elf/dl-sysdep.c
@@ -0,0 +1,590 @@
+/* Operating system support for run-time dynamic linker. Generic Unix version.
+ Copyright (C) 1995-1998, 2000-2003, 2004, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <ldsodefs.h>
+#include <stdio-common/_itoa.h>
+#include <fpu_control.h>
+
+#include <entry.h>
+#include <dl-machine.h>
+#include <dl-procinfo.h>
+#include <dl-osinfo.h>
+#include <hp-timing.h>
+#include <tls.h>
+
+#ifdef _DL_FIRST_PLATFORM
+# define _DL_FIRST_EXTRA (_DL_FIRST_PLATFORM + _DL_PLATFORMS_COUNT)
+#else
+# define _DL_FIRST_EXTRA _DL_HWCAP_COUNT
+#endif
+
+extern char **_environ attribute_hidden;
+extern void _end attribute_hidden;
+
+/* Protect SUID program against misuse of file descriptors. */
+extern void __libc_check_standard_fds (void);
+
+#ifdef NEED_DL_BASE_ADDR
+ElfW(Addr) _dl_base_addr;
+#endif
+int __libc_enable_secure attribute_relro = 0;
+INTVARDEF(__libc_enable_secure)
+int __libc_multiple_libcs = 0; /* Defining this here avoids the inclusion
+ of init-first. */
+/* This variable contains the lowest stack address ever used. */
+void *__libc_stack_end attribute_relro = NULL;
+rtld_hidden_data_def(__libc_stack_end)
+static ElfW(auxv_t) *_dl_auxv attribute_relro;
+
+#ifndef DL_FIND_ARG_COMPONENTS
+# define DL_FIND_ARG_COMPONENTS(cookie, argc, argv, envp, auxp) \
+ do { \
+ void **_tmp; \
+ (argc) = *(long int *) cookie; \
+ (argv) = (char **) ((long int *) cookie + 1); \
+ (envp) = (argv) + (argc) + 1; \
+ for (_tmp = (void **) (envp); *_tmp; ++_tmp) \
+ continue; \
+ (auxp) = (void *) ++_tmp; \
+ } while (0)
+#endif
+
+#ifndef DL_STACK_END
+# define DL_STACK_END(cookie) ((void *) (cookie))
+#endif
+
+ElfW(Addr)
+_dl_sysdep_start (void **start_argptr,
+ void (*dl_main) (const ElfW(Phdr) *phdr, ElfW(Word) phnum,
+ ElfW(Addr) *user_entry))
+{
+ const ElfW(Phdr) *phdr = NULL;
+ ElfW(Word) phnum = 0;
+ ElfW(Addr) user_entry;
+ ElfW(auxv_t) *av;
+#ifdef HAVE_AUX_SECURE
+# define set_seen(tag) (tag) /* Evaluate for the side effects. */
+# define set_seen_secure() ((void) 0)
+#else
+ uid_t uid = 0;
+ gid_t gid = 0;
+ unsigned int seen = 0;
+# define set_seen_secure() (seen = -1)
+# ifdef HAVE_AUX_XID
+# define set_seen(tag) (tag) /* Evaluate for the side effects. */
+# else
+# define M(type) (1 << (type))
+# define set_seen(tag) seen |= M ((tag)->a_type)
+# endif
+#endif
+#ifdef NEED_DL_SYSINFO
+ uintptr_t new_sysinfo = 0;
+#endif
+
+ __libc_stack_end = DL_STACK_END (start_argptr);
+ DL_FIND_ARG_COMPONENTS (start_argptr, _dl_argc, INTUSE(_dl_argv), _environ,
+ _dl_auxv);
+
+ user_entry = (ElfW(Addr)) ENTRY_POINT;
+ GLRO(dl_platform) = NULL; /* Default to nothing known about the platform. */
+
+ for (av = _dl_auxv; av->a_type != AT_NULL; set_seen (av++))
+ switch (av->a_type)
+ {
+ case AT_PHDR:
+ phdr = (void *) av->a_un.a_val;
+ break;
+ case AT_PHNUM:
+ phnum = av->a_un.a_val;
+ break;
+ case AT_PAGESZ:
+ GLRO(dl_pagesize) = av->a_un.a_val;
+ break;
+ case AT_ENTRY:
+ user_entry = av->a_un.a_val;
+ break;
+#ifdef NEED_DL_BASE_ADDR
+ case AT_BASE:
+ _dl_base_addr = av->a_un.a_val;
+ break;
+#endif
+#ifndef HAVE_AUX_SECURE
+ case AT_UID:
+ case AT_EUID:
+ uid ^= av->a_un.a_val;
+ break;
+ case AT_GID:
+ case AT_EGID:
+ gid ^= av->a_un.a_val;
+ break;
+#endif
+ case AT_SECURE:
+#ifndef HAVE_AUX_SECURE
+ seen = -1;
+#endif
+ INTUSE(__libc_enable_secure) = av->a_un.a_val;
+ break;
+ case AT_PLATFORM:
+ GLRO(dl_platform) = (void *) av->a_un.a_val;
+ break;
+ case AT_HWCAP:
+ GLRO(dl_hwcap) = (unsigned long int) av->a_un.a_val;
+ break;
+ case AT_CLKTCK:
+ GLRO(dl_clktck) = av->a_un.a_val;
+ break;
+ case AT_FPUCW:
+ GLRO(dl_fpu_control) = av->a_un.a_val;
+ break;
+#ifdef NEED_DL_SYSINFO
+ case AT_SYSINFO:
+ new_sysinfo = av->a_un.a_val;
+ break;
+#endif
+#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO
+ case AT_SYSINFO_EHDR:
+ GLRO(dl_sysinfo_dso) = (void *) av->a_un.a_val;
+ break;
+#endif
+#ifdef DL_PLATFORM_AUXV
+ DL_PLATFORM_AUXV
+#endif
+ }
+
+#ifndef HAVE_AUX_SECURE
+ if (seen != -1)
+ {
+ /* Fill in the values we have not gotten from the kernel through the
+ auxiliary vector. */
+# ifndef HAVE_AUX_XID
+# define SEE(UID, var, uid) \
+ if ((seen & M (AT_##UID)) == 0) var ^= __get##uid ()
+ SEE (UID, uid, uid);
+ SEE (EUID, uid, euid);
+ SEE (GID, gid, gid);
+ SEE (EGID, gid, egid);
+# endif
+
+ /* If one of the two pairs of IDs does not match this is a setuid
+ or setgid run. */
+ INTUSE(__libc_enable_secure) = uid | gid;
+ }
+#endif
+
+#ifndef HAVE_AUX_PAGESIZE
+ if (GLRO(dl_pagesize) == 0)
+ GLRO(dl_pagesize) = __getpagesize ();
+#endif
+
+#if defined NEED_DL_SYSINFO
+ /* Only set the sysinfo value if we also have the vsyscall DSO. */
+ if (GLRO(dl_sysinfo_dso) != 0 && new_sysinfo)
+ GLRO(dl_sysinfo) = new_sysinfo;
+#endif
+
+#ifdef DL_SYSDEP_INIT
+ DL_SYSDEP_INIT;
+#endif
+
+#ifdef DL_PLATFORM_INIT
+ DL_PLATFORM_INIT;
+#endif
+
+ /* Determine the length of the platform name. */
+ if (GLRO(dl_platform) != NULL)
+ GLRO(dl_platformlen) = strlen (GLRO(dl_platform));
+
+ if (__sbrk (0) == &_end)
+ /* The dynamic linker was run as a program, and so the initial break
+ starts just after our bss, at &_end. The malloc in dl-minimal.c
+ will consume the rest of this page, so tell the kernel to move the
+ break up that far. When the user program examines its break, it
+ will see this new value and not clobber our data. */
+ __sbrk (GLRO(dl_pagesize)
+ - ((&_end - (void *) 0) & (GLRO(dl_pagesize) - 1)));
+
+ /* If this is a SUID program we make sure that FDs 0, 1, and 2 are
+ allocated. If necessary we are doing it ourself. If it is not
+ possible we stop the program. */
+ if (__builtin_expect (INTUSE(__libc_enable_secure), 0))
+ __libc_check_standard_fds ();
+
+ (*dl_main) (phdr, phnum, &user_entry);
+ return user_entry;
+}
+
+void
+internal_function
+_dl_sysdep_start_cleanup (void)
+{
+}
+
+void
+internal_function
+_dl_show_auxv (void)
+{
+ char buf[64];
+ ElfW(auxv_t) *av;
+
+ /* Terminate string. */
+ buf[63] = '\0';
+
+ /* The following code assumes that the AT_* values are encoded
+ starting from 0 with AT_NULL, 1 for AT_IGNORE, and all other values
+ close by (otherwise the array will be too large). In case we have
+ to support a platform where these requirements are not fulfilled
+ some alternative implementation has to be used. */
+ for (av = _dl_auxv; av->a_type != AT_NULL; ++av)
+ {
+ static const struct
+ {
+ const char label[20];
+ enum { unknown = 0, dec, hex, str, ignore } form;
+ } auxvars[] =
+ {
+ [AT_EXECFD - 2] = { "AT_EXECFD: ", dec },
+ [AT_PHDR - 2] = { "AT_PHDR: 0x", hex },
+ [AT_PHENT - 2] = { "AT_PHENT: ", dec },
+ [AT_PHNUM - 2] = { "AT_PHNUM: ", dec },
+ [AT_PAGESZ - 2] = { "AT_PAGESZ: ", dec },
+ [AT_BASE - 2] = { "AT_BASE: 0x", hex },
+ [AT_FLAGS - 2] = { "AT_FLAGS: 0x", hex },
+ [AT_ENTRY - 2] = { "AT_ENTRY: 0x", hex },
+ [AT_NOTELF - 2] = { "AT_NOTELF: ", hex },
+ [AT_UID - 2] = { "AT_UID: ", dec },
+ [AT_EUID - 2] = { "AT_EUID: ", dec },
+ [AT_GID - 2] = { "AT_GID: ", dec },
+ [AT_EGID - 2] = { "AT_EGID: ", dec },
+ [AT_PLATFORM - 2] = { "AT_PLATFORM: ", str },
+ [AT_HWCAP - 2] = { "AT_HWCAP: ", hex },
+ [AT_CLKTCK - 2] = { "AT_CLKTCK: ", dec },
+ [AT_FPUCW - 2] = { "AT_FPUCW: ", hex },
+ [AT_DCACHEBSIZE - 2] = { "AT_DCACHEBSIZE: 0x", hex },
+ [AT_ICACHEBSIZE - 2] = { "AT_ICACHEBSIZE: 0x", hex },
+ [AT_UCACHEBSIZE - 2] = { "AT_UCACHEBSIZE: 0x", hex },
+ [AT_IGNOREPPC - 2] = { "AT_IGNOREPPC", ignore },
+ [AT_SECURE - 2] = { "AT_SECURE: ", dec },
+ [AT_SYSINFO - 2] = { "AT_SYSINFO: 0x", hex },
+ [AT_SYSINFO_EHDR - 2] = { "AT_SYSINFO_EHDR: 0x", hex },
+ };
+ unsigned int idx = (unsigned int) (av->a_type - 2);
+
+ if ((unsigned int) av->a_type < 2u || auxvars[idx].form == ignore)
+ continue;
+
+ assert (AT_NULL == 0);
+ assert (AT_IGNORE == 1);
+
+ if (av->a_type == AT_HWCAP)
+ {
+ /* This is handled special. */
+ if (_dl_procinfo (av->a_un.a_val) == 0)
+ continue;
+ }
+
+ if (idx < sizeof (auxvars) / sizeof (auxvars[0])
+ && auxvars[idx].form != unknown)
+ {
+ const char *val = (char *) av->a_un.a_val;
+
+ if (__builtin_expect (auxvars[idx].form, dec) == dec)
+ val = _itoa ((unsigned long int) av->a_un.a_val,
+ buf + sizeof buf - 1, 10, 0);
+ else if (__builtin_expect (auxvars[idx].form, hex) == hex)
+ val = _itoa ((unsigned long int) av->a_un.a_val,
+ buf + sizeof buf - 1, 16, 0);
+
+ _dl_printf ("%s%s\n", auxvars[idx].label, val);
+
+ continue;
+ }
+
+ /* Unknown value: print a generic line. */
+ char buf2[17];
+ buf[sizeof (buf2) - 1] = '\0';
+ const char *val2 = _itoa ((unsigned long int) av->a_un.a_val,
+ buf2 + sizeof buf2 - 1, 16, 0);
+ const char *val = _itoa ((unsigned long int) av->a_type,
+ buf + sizeof buf - 1, 16, 0);
+ _dl_printf ("AT_??? (0x%s): 0x%s\n", val, val2);
+ }
+}
+
+
+/* Return an array of useful/necessary hardware capability names. */
+const struct r_strlenpair *
+internal_function
+_dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
+ size_t *max_capstrlen)
+{
+ /* Determine how many important bits are set. */
+ uint64_t masked = GLRO(dl_hwcap) & GLRO(dl_hwcap_mask);
+ size_t cnt = platform != NULL;
+ size_t n, m;
+ size_t total;
+ struct r_strlenpair *temp;
+ struct r_strlenpair *result;
+ struct r_strlenpair *rp;
+ char *cp;
+
+ /* Count the number of bits set in the masked value. */
+ for (n = 0; (~((1ULL << n) - 1) & masked) != 0; ++n)
+ if ((masked & (1ULL << n)) != 0)
+ ++cnt;
+
+#if (defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO) && defined SHARED
+ /* The system-supplied DSO can contain a note of type 2, vendor "GNU".
+ This gives us a list of names to treat as fake hwcap bits. */
+
+ const char *dsocaps = NULL;
+ size_t dsocapslen = 0;
+ if (GLRO(dl_sysinfo_map) != NULL)
+ {
+ const ElfW(Phdr) *const phdr = GLRO(dl_sysinfo_map)->l_phdr;
+ const ElfW(Word) phnum = GLRO(dl_sysinfo_map)->l_phnum;
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ if (phdr[i].p_type == PT_NOTE)
+ {
+ const ElfW(Addr) start = (phdr[i].p_vaddr
+ + GLRO(dl_sysinfo_map)->l_addr);
+ const struct
+ {
+ ElfW(Word) vendorlen;
+ ElfW(Word) datalen;
+ ElfW(Word) type;
+ } *note = (const void *) start;
+ while ((ElfW(Addr)) (note + 1) - start < phdr[i].p_memsz)
+ {
+#define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word)))
+ if (note->type == 2
+ && note->vendorlen == sizeof "GNU"
+ && !memcmp ((note + 1), "GNU", sizeof "GNU")
+ && note->datalen > 2 * sizeof (ElfW(Word)) + 2)
+ {
+ const ElfW(Word) *p = ((const void *) (note + 1)
+ + ROUND (sizeof "GNU"));
+ cnt += *p++;
+ ++p; /* Skip mask word. */
+ dsocaps = (const char *) p;
+ dsocapslen = note->datalen - sizeof *p * 2;
+ break;
+ }
+ note = ((const void *) (note + 1)
+ + ROUND (note->vendorlen) + ROUND (note->datalen));
+ }
+ if (dsocaps != NULL)
+ break;
+ }
+ }
+#endif
+
+#ifdef USE_TLS
+ /* For TLS enabled builds always add 'tls'. */
+ ++cnt;
+#else
+ if (cnt == 0)
+ {
+ /* If we no have platform name and no important capability we only
+ have the base directory to search. */
+ result = (struct r_strlenpair *) malloc (sizeof (*result));
+ if (result == NULL)
+ goto no_memory;
+
+ result[0].str = (char *) result; /* Does not really matter. */
+ result[0].len = 0;
+
+ *sz = 1;
+ return result;
+ }
+#endif
+
+ /* Create temporary data structure to generate result table. */
+ temp = (struct r_strlenpair *) alloca (cnt * sizeof (*temp));
+ m = 0;
+#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO
+ if (dsocaps != NULL)
+ {
+ const ElfW(Word) mask = ((const ElfW(Word) *) dsocaps)[-1];
+ GLRO(dl_hwcap) |= (uint64_t) mask << _DL_FIRST_EXTRA;
+ size_t len;
+ for (const char *p = dsocaps; p < dsocaps + dsocapslen; p += len + 1)
+ {
+ uint_fast8_t bit = *p++;
+ len = strlen (p);
+
+ /* Skip entries that are not enabled in the mask word. */
+ if (__builtin_expect (mask & ((ElfW(Word)) 1 << bit), 1))
+ {
+ temp[m].str = p;
+ temp[m].len = len;
+ ++m;
+ }
+ else
+ --cnt;
+ }
+ }
+#endif
+ for (n = 0; masked != 0; ++n)
+ if ((masked & (1ULL << n)) != 0)
+ {
+ temp[m].str = _dl_hwcap_string (n);
+ temp[m].len = strlen (temp[m].str);
+ masked ^= 1ULL << n;
+ ++m;
+ }
+ if (platform != NULL)
+ {
+ temp[m].str = platform;
+ temp[m].len = platform_len;
+ ++m;
+ }
+#ifdef USE_TLS
+ temp[m].str = "tls";
+ temp[m].len = 3;
+ ++m;
+#endif
+ assert (m == cnt);
+
+ /* Determine the total size of all strings together. */
+ if (cnt == 1)
+ total = temp[0].len + 1;
+ else
+ {
+ total = (1UL << (cnt - 2)) * (temp[0].len + temp[cnt - 1].len + 2);
+ for (n = 1; n + 1 < cnt; ++n)
+ total += (1UL << (cnt - 3)) * (temp[n].len + 1);
+ }
+
+ /* The result structure: we use a very compressed way to store the
+ various combinations of capability names. */
+ *sz = 1 << cnt;
+ result = (struct r_strlenpair *) malloc (*sz * sizeof (*result) + total);
+ if (result == NULL)
+ {
+#ifndef USE_TLS
+ no_memory:
+#endif
+ _dl_signal_error (ENOMEM, NULL, NULL,
+ N_("cannot create capability list"));
+ }
+
+ if (cnt == 1)
+ {
+ result[0].str = (char *) (result + *sz);
+ result[0].len = temp[0].len + 1;
+ result[1].str = (char *) (result + *sz);
+ result[1].len = 0;
+ cp = __mempcpy ((char *) (result + *sz), temp[0].str, temp[0].len);
+ *cp = '/';
+ *sz = 2;
+ *max_capstrlen = result[0].len;
+
+ return result;
+ }
+
+ /* Fill in the information. This follows the following scheme
+ (indeces from TEMP for four strings):
+ entry #0: 0, 1, 2, 3 binary: 1111
+ #1: 0, 1, 3 1101
+ #2: 0, 2, 3 1011
+ #3: 0, 3 1001
+ This allows the representation of all possible combinations of
+ capability names in the string. First generate the strings. */
+ result[1].str = result[0].str = cp = (char *) (result + *sz);
+#define add(idx) \
+ cp = __mempcpy (__mempcpy (cp, temp[idx].str, temp[idx].len), "/", 1);
+ if (cnt == 2)
+ {
+ add (1);
+ add (0);
+ }
+ else
+ {
+ n = 1 << (cnt - 1);
+ do
+ {
+ n -= 2;
+
+ /* We always add the last string. */
+ add (cnt - 1);
+
+ /* Add the strings which have the bit set in N. */
+ for (m = cnt - 2; m > 0; --m)
+ if ((n & (1 << m)) != 0)
+ add (m);
+
+ /* Always add the first string. */
+ add (0);
+ }
+ while (n != 0);
+ }
+#undef add
+
+ /* Now we are ready to install the string pointers and length. */
+ for (n = 0; n < (1UL << cnt); ++n)
+ result[n].len = 0;
+ n = cnt;
+ do
+ {
+ size_t mask = 1 << --n;
+
+ rp = result;
+ for (m = 1 << cnt; m > 0; ++rp)
+ if ((--m & mask) != 0)
+ rp->len += temp[n].len + 1;
+ }
+ while (n != 0);
+
+ /* The first half of the strings all include the first string. */
+ n = (1 << cnt) - 2;
+ rp = &result[2];
+ while (n != (1UL << (cnt - 1)))
+ {
+ if ((--n & 1) != 0)
+ rp[0].str = rp[-2].str + rp[-2].len;
+ else
+ rp[0].str = rp[-1].str;
+ ++rp;
+ }
+
+ /* The second half starts right after the first part of the string of
+ the corresponding entry in the first half. */
+ do
+ {
+ rp[0].str = rp[-(1 << (cnt - 1))].str + temp[cnt - 1].len + 1;
+ ++rp;
+ }
+ while (--n != 0);
+
+ /* The maximum string length. */
+ *max_capstrlen = result[0].len;
+
+ return result;
+}
diff --git a/libc/elf/dl-tls.c b/libc/elf/dl-tls.c
new file mode 100644
index 000000000..a0f4f77ff
--- /dev/null
+++ b/libc/elf/dl-tls.c
@@ -0,0 +1,843 @@
+/* Thread-local storage handling in the ELF dynamic linker. Generic version.
+ Copyright (C) 2002,2003,2004,2005,2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+#include <tls.h>
+
+/* We don't need any of this if TLS is not supported. */
+#ifdef USE_TLS
+
+# include <dl-tls.h>
+# include <ldsodefs.h>
+
+/* Amount of excess space to allocate in the static TLS area
+ to allow dynamic loading of modules defining IE-model TLS data. */
+# define TLS_STATIC_SURPLUS 64 + DL_NNS * 100
+
+/* Value used for dtv entries for which the allocation is delayed. */
+# define TLS_DTV_UNALLOCATED ((void *) -1l)
+
+
+/* Out-of-memory handler. */
+# ifdef SHARED
+static void
+__attribute__ ((__noreturn__))
+oom (void)
+{
+ _dl_fatal_printf ("cannot allocate memory for thread-local data: ABORT\n");
+}
+# endif
+
+
+size_t
+internal_function
+_dl_next_tls_modid (void)
+{
+ size_t result;
+
+ if (__builtin_expect (GL(dl_tls_dtv_gaps), false))
+ {
+ size_t disp = 0;
+ struct dtv_slotinfo_list *runp = GL(dl_tls_dtv_slotinfo_list);
+
+ /* Note that this branch will never be executed during program
+ start since there are no gaps at that time. Therefore it
+ does not matter that the dl_tls_dtv_slotinfo is not allocated
+ yet when the function is called for the first times.
+
+ NB: the offset +1 is due to the fact that DTV[0] is used
+ for something else. */
+ result = GL(dl_tls_static_nelem) + 1;
+ if (result <= GL(dl_tls_max_dtv_idx))
+ do
+ {
+ while (result - disp < runp->len)
+ {
+ if (runp->slotinfo[result - disp].map == NULL)
+ break;
+
+ ++result;
+ assert (result <= GL(dl_tls_max_dtv_idx) + 1);
+ }
+
+ if (result - disp < runp->len)
+ break;
+
+ disp += runp->len;
+ }
+ while ((runp = runp->next) != NULL);
+
+ if (result > GL(dl_tls_max_dtv_idx))
+ {
+ /* The new index must indeed be exactly one higher than the
+ previous high. */
+ assert (result == GL(dl_tls_max_dtv_idx) + 1);
+ /* There is no gap anymore. */
+ GL(dl_tls_dtv_gaps) = false;
+
+ goto nogaps;
+ }
+ }
+ else
+ {
+ /* No gaps, allocate a new entry. */
+ nogaps:
+
+ result = ++GL(dl_tls_max_dtv_idx);
+ }
+
+ return result;
+}
+
+
+# ifdef SHARED
+void
+internal_function
+_dl_determine_tlsoffset (void)
+{
+ size_t max_align = TLS_TCB_ALIGN;
+ size_t freetop = 0;
+ size_t freebottom = 0;
+
+ /* The first element of the dtv slot info list is allocated. */
+ assert (GL(dl_tls_dtv_slotinfo_list) != NULL);
+ /* There is at this point only one element in the
+ dl_tls_dtv_slotinfo_list list. */
+ assert (GL(dl_tls_dtv_slotinfo_list)->next == NULL);
+
+ struct dtv_slotinfo *slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo;
+
+ /* Determining the offset of the various parts of the static TLS
+ block has several dependencies. In addition we have to work
+ around bugs in some toolchains.
+
+ Each TLS block from the objects available at link time has a size
+ and an alignment requirement. The GNU ld computes the alignment
+ requirements for the data at the positions *in the file*, though.
+ I.e, it is not simply possible to allocate a block with the size
+ of the TLS program header entry. The data is layed out assuming
+ that the first byte of the TLS block fulfills
+
+ p_vaddr mod p_align == &TLS_BLOCK mod p_align
+
+ This means we have to add artificial padding at the beginning of
+ the TLS block. These bytes are never used for the TLS data in
+ this module but the first byte allocated must be aligned
+ according to mod p_align == 0 so that the first byte of the TLS
+ block is aligned according to p_vaddr mod p_align. This is ugly
+ and the linker can help by computing the offsets in the TLS block
+ assuming the first byte of the TLS block is aligned according to
+ p_align.
+
+ The extra space which might be allocated before the first byte of
+ the TLS block need not go unused. The code below tries to use
+ that memory for the next TLS block. This can work if the total
+ memory requirement for the next TLS block is smaller than the
+ gap. */
+
+# if TLS_TCB_AT_TP
+ /* We simply start with zero. */
+ size_t offset = 0;
+
+ for (size_t cnt = 0; slotinfo[cnt].map != NULL; ++cnt)
+ {
+ assert (cnt < GL(dl_tls_dtv_slotinfo_list)->len);
+
+ size_t firstbyte = (-slotinfo[cnt].map->l_tls_firstbyte_offset
+ & (slotinfo[cnt].map->l_tls_align - 1));
+ size_t off;
+ max_align = MAX (max_align, slotinfo[cnt].map->l_tls_align);
+
+ if (freebottom - freetop >= slotinfo[cnt].map->l_tls_blocksize)
+ {
+ off = roundup (freetop + slotinfo[cnt].map->l_tls_blocksize
+ - firstbyte, slotinfo[cnt].map->l_tls_align)
+ + firstbyte;
+ if (off <= freebottom)
+ {
+ freetop = off;
+
+ /* XXX For some architectures we perhaps should store the
+ negative offset. */
+ slotinfo[cnt].map->l_tls_offset = off;
+ continue;
+ }
+ }
+
+ off = roundup (offset + slotinfo[cnt].map->l_tls_blocksize - firstbyte,
+ slotinfo[cnt].map->l_tls_align) + firstbyte;
+ if (off > offset + slotinfo[cnt].map->l_tls_blocksize
+ + (freebottom - freetop))
+ {
+ freetop = offset;
+ freebottom = off - slotinfo[cnt].map->l_tls_blocksize;
+ }
+ offset = off;
+
+ /* XXX For some architectures we perhaps should store the
+ negative offset. */
+ slotinfo[cnt].map->l_tls_offset = off;
+ }
+
+ GL(dl_tls_static_used) = offset;
+ GL(dl_tls_static_size) = (roundup (offset + TLS_STATIC_SURPLUS, max_align)
+ + TLS_TCB_SIZE);
+# elif TLS_DTV_AT_TP
+ /* The TLS blocks start right after the TCB. */
+ size_t offset = TLS_TCB_SIZE;
+
+ for (size_t cnt = 0; slotinfo[cnt].map != NULL; ++cnt)
+ {
+ assert (cnt < GL(dl_tls_dtv_slotinfo_list)->len);
+
+ size_t firstbyte = (-slotinfo[cnt].map->l_tls_firstbyte_offset
+ & (slotinfo[cnt].map->l_tls_align - 1));
+ size_t off;
+ max_align = MAX (max_align, slotinfo[cnt].map->l_tls_align);
+
+ if (slotinfo[cnt].map->l_tls_blocksize <= freetop - freebottom)
+ {
+ off = roundup (freebottom, slotinfo[cnt].map->l_tls_align);
+ if (off - freebottom < firstbyte)
+ off += slotinfo[cnt].map->l_tls_align;
+ if (off + slotinfo[cnt].map->l_tls_blocksize - firstbyte <= freetop)
+ {
+ slotinfo[cnt].map->l_tls_offset = off - firstbyte;
+ freebottom = (off + slotinfo[cnt].map->l_tls_blocksize
+ - firstbyte);
+ continue;
+ }
+ }
+
+ off = roundup (offset, slotinfo[cnt].map->l_tls_align);
+ if (off - offset < firstbyte)
+ off += slotinfo[cnt].map->l_tls_align;
+
+ slotinfo[cnt].map->l_tls_offset = off - firstbyte;
+ if (off - firstbyte - offset > freetop - freebottom)
+ {
+ freebottom = offset;
+ freetop = off - firstbyte;
+ }
+
+ offset = off + slotinfo[cnt].map->l_tls_blocksize - firstbyte;
+ }
+
+ GL(dl_tls_static_used) = offset;
+ GL(dl_tls_static_size) = roundup (offset + TLS_STATIC_SURPLUS,
+ TLS_TCB_ALIGN);
+# else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+
+ /* The alignment requirement for the static TLS block. */
+ GL(dl_tls_static_align) = max_align;
+}
+
+
+/* This is called only when the data structure setup was skipped at startup,
+ when there was no need for it then. Now we have dynamically loaded
+ something needing TLS, or libpthread needs it. */
+int
+internal_function
+_dl_tls_setup (void)
+{
+ assert (GL(dl_tls_dtv_slotinfo_list) == NULL);
+ assert (GL(dl_tls_max_dtv_idx) == 0);
+
+ const size_t nelem = 2 + TLS_SLOTINFO_SURPLUS;
+
+ GL(dl_tls_dtv_slotinfo_list)
+ = calloc (1, (sizeof (struct dtv_slotinfo_list)
+ + nelem * sizeof (struct dtv_slotinfo)));
+ if (GL(dl_tls_dtv_slotinfo_list) == NULL)
+ return -1;
+
+ GL(dl_tls_dtv_slotinfo_list)->len = nelem;
+
+ /* Number of elements in the static TLS block. It can't be zero
+ because of various assumptions. The one element is null. */
+ GL(dl_tls_static_nelem) = GL(dl_tls_max_dtv_idx) = 1;
+
+ /* This initializes more variables for us. */
+ _dl_determine_tlsoffset ();
+
+ return 0;
+}
+rtld_hidden_def (_dl_tls_setup)
+# endif
+
+static void *
+internal_function
+allocate_dtv (void *result)
+{
+ dtv_t *dtv;
+ size_t dtv_length;
+
+ /* We allocate a few more elements in the dtv than are needed for the
+ initial set of modules. This should avoid in most cases expansions
+ of the dtv. */
+ dtv_length = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS;
+ dtv = calloc (dtv_length + 2, sizeof (dtv_t));
+ if (dtv != NULL)
+ {
+ /* This is the initial length of the dtv. */
+ dtv[0].counter = dtv_length;
+
+ /* The rest of the dtv (including the generation counter) is
+ Initialize with zero to indicate nothing there. */
+
+ /* Add the dtv to the thread data structures. */
+ INSTALL_DTV (result, dtv);
+ }
+ else
+ result = NULL;
+
+ return result;
+}
+
+
+/* Get size and alignment requirements of the static TLS block. */
+void
+internal_function
+_dl_get_tls_static_info (size_t *sizep, size_t *alignp)
+{
+ *sizep = GL(dl_tls_static_size);
+ *alignp = GL(dl_tls_static_align);
+}
+
+
+void *
+internal_function
+_dl_allocate_tls_storage (void)
+{
+ void *result;
+ size_t size = GL(dl_tls_static_size);
+
+# if TLS_DTV_AT_TP
+ /* Memory layout is:
+ [ TLS_PRE_TCB_SIZE ] [ TLS_TCB_SIZE ] [ TLS blocks ]
+ ^ This should be returned. */
+ size += (TLS_PRE_TCB_SIZE + GL(dl_tls_static_align) - 1)
+ & ~(GL(dl_tls_static_align) - 1);
+# endif
+
+ /* Allocate a correctly aligned chunk of memory. */
+ result = __libc_memalign (GL(dl_tls_static_align), size);
+ if (__builtin_expect (result != NULL, 1))
+ {
+ /* Allocate the DTV. */
+ void *allocated = result;
+
+# if TLS_TCB_AT_TP
+ /* The TCB follows the TLS blocks. */
+ result = (char *) result + size - TLS_TCB_SIZE;
+
+ /* Clear the TCB data structure. We can't ask the caller (i.e.
+ libpthread) to do it, because we will initialize the DTV et al. */
+ memset (result, '\0', TLS_TCB_SIZE);
+# elif TLS_DTV_AT_TP
+ result = (char *) result + size - GL(dl_tls_static_size);
+
+ /* Clear the TCB data structure and TLS_PRE_TCB_SIZE bytes before it.
+ We can't ask the caller (i.e. libpthread) to do it, because we will
+ initialize the DTV et al. */
+ memset ((char *) result - TLS_PRE_TCB_SIZE, '\0',
+ TLS_PRE_TCB_SIZE + TLS_TCB_SIZE);
+# endif
+
+ result = allocate_dtv (result);
+ if (result == NULL)
+ free (allocated);
+ }
+
+ return result;
+}
+
+
+void *
+internal_function
+_dl_allocate_tls_init (void *result)
+{
+ if (result == NULL)
+ /* The memory allocation failed. */
+ return NULL;
+
+ dtv_t *dtv = GET_DTV (result);
+ struct dtv_slotinfo_list *listp;
+ size_t total = 0;
+ size_t maxgen = 0;
+
+ /* We have to prepare the dtv for all currently loaded modules using
+ TLS. For those which are dynamically loaded we add the values
+ indicating deferred allocation. */
+ listp = GL(dl_tls_dtv_slotinfo_list);
+ while (1)
+ {
+ size_t cnt;
+
+ for (cnt = total == 0 ? 1 : 0; cnt < listp->len; ++cnt)
+ {
+ struct link_map *map;
+ void *dest;
+
+ /* Check for the total number of used slots. */
+ if (total + cnt > GL(dl_tls_max_dtv_idx))
+ break;
+
+ map = listp->slotinfo[cnt].map;
+ if (map == NULL)
+ /* Unused entry. */
+ continue;
+
+ /* Keep track of the maximum generation number. This might
+ not be the generation counter. */
+ maxgen = MAX (maxgen, listp->slotinfo[cnt].gen);
+
+ if (map->l_tls_offset == NO_TLS_OFFSET)
+ {
+ /* For dynamically loaded modules we simply store
+ the value indicating deferred allocation. */
+ dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED;
+ dtv[map->l_tls_modid].pointer.is_static = false;
+ continue;
+ }
+
+ assert (map->l_tls_modid == cnt);
+ assert (map->l_tls_blocksize >= map->l_tls_initimage_size);
+# if TLS_TCB_AT_TP
+ assert ((size_t) map->l_tls_offset >= map->l_tls_blocksize);
+ dest = (char *) result - map->l_tls_offset;
+# elif TLS_DTV_AT_TP
+ dest = (char *) result + map->l_tls_offset;
+# else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+
+ /* Copy the initialization image and clear the BSS part. */
+ dtv[map->l_tls_modid].pointer.val = dest;
+ dtv[map->l_tls_modid].pointer.is_static = true;
+ memset (__mempcpy (dest, map->l_tls_initimage,
+ map->l_tls_initimage_size), '\0',
+ map->l_tls_blocksize - map->l_tls_initimage_size);
+ }
+
+ total += cnt;
+ if (total >= GL(dl_tls_max_dtv_idx))
+ break;
+
+ listp = listp->next;
+ assert (listp != NULL);
+ }
+
+ /* The DTV version is up-to-date now. */
+ dtv[0].counter = maxgen;
+
+ return result;
+}
+rtld_hidden_def (_dl_allocate_tls_init)
+
+void *
+internal_function
+_dl_allocate_tls (void *mem)
+{
+ return _dl_allocate_tls_init (mem == NULL
+ ? _dl_allocate_tls_storage ()
+ : allocate_dtv (mem));
+}
+rtld_hidden_def (_dl_allocate_tls)
+
+
+void
+internal_function
+_dl_deallocate_tls (void *tcb, bool dealloc_tcb)
+{
+ dtv_t *dtv = GET_DTV (tcb);
+
+ /* We need to free the memory allocated for non-static TLS. */
+ for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt)
+ if (! dtv[1 + cnt].pointer.is_static
+ && dtv[1 + cnt].pointer.val != TLS_DTV_UNALLOCATED)
+ free (dtv[1 + cnt].pointer.val);
+
+ /* The array starts with dtv[-1]. */
+#ifdef SHARED
+ if (dtv != GL(dl_initial_dtv))
+#endif
+ free (dtv - 1);
+
+ if (dealloc_tcb)
+ {
+# if TLS_TCB_AT_TP
+ /* The TCB follows the TLS blocks. Back up to free the whole block. */
+ tcb -= GL(dl_tls_static_size) - TLS_TCB_SIZE;
+# elif TLS_DTV_AT_TP
+ /* Back up the TLS_PRE_TCB_SIZE bytes. */
+ tcb -= (TLS_PRE_TCB_SIZE + GL(dl_tls_static_align) - 1)
+ & ~(GL(dl_tls_static_align) - 1);
+# endif
+ free (tcb);
+ }
+}
+rtld_hidden_def (_dl_deallocate_tls)
+
+
+# ifdef SHARED
+/* The __tls_get_addr function has two basic forms which differ in the
+ arguments. The IA-64 form takes two parameters, the module ID and
+ offset. The form used, among others, on IA-32 takes a reference to
+ a special structure which contain the same information. The second
+ form seems to be more often used (in the moment) so we default to
+ it. Users of the IA-64 form have to provide adequate definitions
+ of the following macros. */
+# ifndef GET_ADDR_ARGS
+# define GET_ADDR_ARGS tls_index *ti
+# endif
+# ifndef GET_ADDR_MODULE
+# define GET_ADDR_MODULE ti->ti_module
+# endif
+# ifndef GET_ADDR_OFFSET
+# define GET_ADDR_OFFSET ti->ti_offset
+# endif
+
+
+static void *
+allocate_and_init (struct link_map *map)
+{
+ void *newp;
+
+ newp = __libc_memalign (map->l_tls_align, map->l_tls_blocksize);
+ if (newp == NULL)
+ oom ();
+
+ /* Initialize the memory. */
+ memset (__mempcpy (newp, map->l_tls_initimage, map->l_tls_initimage_size),
+ '\0', map->l_tls_blocksize - map->l_tls_initimage_size);
+
+ return newp;
+}
+
+
+struct link_map *
+_dl_update_slotinfo (unsigned long int req_modid)
+{
+ struct link_map *the_map = NULL;
+ dtv_t *dtv = THREAD_DTV ();
+
+ /* The global dl_tls_dtv_slotinfo array contains for each module
+ index the generation counter current when the entry was created.
+ This array never shrinks so that all module indices which were
+ valid at some time can be used to access it. Before the first
+ use of a new module index in this function the array was extended
+ appropriately. Access also does not have to be guarded against
+ modifications of the array. It is assumed that pointer-size
+ values can be read atomically even in SMP environments. It is
+ possible that other threads at the same time dynamically load
+ code and therefore add to the slotinfo list. This is a problem
+ since we must not pick up any information about incomplete work.
+ The solution to this is to ignore all dtv slots which were
+ created after the one we are currently interested. We know that
+ dynamic loading for this module is completed and this is the last
+ load operation we know finished. */
+ unsigned long int idx = req_modid;
+ struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list);
+
+ while (idx >= listp->len)
+ {
+ idx -= listp->len;
+ listp = listp->next;
+ }
+
+ if (dtv[0].counter < listp->slotinfo[idx].gen)
+ {
+ /* The generation counter for the slot is higher than what the
+ current dtv implements. We have to update the whole dtv but
+ only those entries with a generation counter <= the one for
+ the entry we need. */
+ size_t new_gen = listp->slotinfo[idx].gen;
+ size_t total = 0;
+
+ /* We have to look through the entire dtv slotinfo list. */
+ listp = GL(dl_tls_dtv_slotinfo_list);
+ do
+ {
+ for (size_t cnt = total == 0 ? 1 : 0; cnt < listp->len; ++cnt)
+ {
+ size_t gen = listp->slotinfo[cnt].gen;
+
+ if (gen > new_gen)
+ /* This is a slot for a generation younger than the
+ one we are handling now. It might be incompletely
+ set up so ignore it. */
+ continue;
+
+ /* If the entry is older than the current dtv layout we
+ know we don't have to handle it. */
+ if (gen <= dtv[0].counter)
+ continue;
+
+ /* If there is no map this means the entry is empty. */
+ struct link_map *map = listp->slotinfo[cnt].map;
+ if (map == NULL)
+ {
+ /* If this modid was used at some point the memory
+ might still be allocated. */
+ if (! dtv[total + cnt].pointer.is_static
+ && dtv[total + cnt].pointer.val != TLS_DTV_UNALLOCATED)
+ {
+ free (dtv[total + cnt].pointer.val);
+ dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED;
+ }
+
+ continue;
+ }
+
+ /* Check whether the current dtv array is large enough. */
+ size_t modid = map->l_tls_modid;
+ assert (total + cnt == modid);
+ if (dtv[-1].counter < modid)
+ {
+ /* Reallocate the dtv. */
+ dtv_t *newp;
+ size_t newsize = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS;
+ size_t oldsize = dtv[-1].counter;
+
+ assert (map->l_tls_modid <= newsize);
+
+ if (dtv == GL(dl_initial_dtv))
+ {
+ /* This is the initial dtv that was allocated
+ during rtld startup using the dl-minimal.c
+ malloc instead of the real malloc. We can't
+ free it, we have to abandon the old storage. */
+
+ newp = malloc ((2 + newsize) * sizeof (dtv_t));
+ if (newp == NULL)
+ oom ();
+ memcpy (newp, &dtv[-1], oldsize * sizeof (dtv_t));
+ }
+ else
+ {
+ newp = realloc (&dtv[-1],
+ (2 + newsize) * sizeof (dtv_t));
+ if (newp == NULL)
+ oom ();
+ }
+
+ newp[0].counter = newsize;
+
+ /* Clear the newly allocated part. */
+ memset (newp + 2 + oldsize, '\0',
+ (newsize - oldsize) * sizeof (dtv_t));
+
+ /* Point dtv to the generation counter. */
+ dtv = &newp[1];
+
+ /* Install this new dtv in the thread data
+ structures. */
+ INSTALL_NEW_DTV (dtv);
+ }
+
+ /* If there is currently memory allocate for this
+ dtv entry free it. */
+ /* XXX Ideally we will at some point create a memory
+ pool. */
+ if (! dtv[modid].pointer.is_static
+ && dtv[modid].pointer.val != TLS_DTV_UNALLOCATED)
+ /* Note that free is called for NULL is well. We
+ deallocate even if it is this dtv entry we are
+ supposed to load. The reason is that we call
+ memalign and not malloc. */
+ free (dtv[modid].pointer.val);
+
+ /* This module is loaded dynamically- We defer memory
+ allocation. */
+ dtv[modid].pointer.is_static = false;
+ dtv[modid].pointer.val = TLS_DTV_UNALLOCATED;
+
+ if (modid == req_modid)
+ the_map = map;
+ }
+
+ total += listp->len;
+ }
+ while ((listp = listp->next) != NULL);
+
+ /* This will be the new maximum generation counter. */
+ dtv[0].counter = new_gen;
+ }
+
+ return the_map;
+}
+
+
+/* The generic dynamic and local dynamic model cannot be used in
+ statically linked applications. */
+void *
+__tls_get_addr (GET_ADDR_ARGS)
+{
+ dtv_t *dtv = THREAD_DTV ();
+ struct link_map *the_map = NULL;
+ void *p;
+
+ if (__builtin_expect (dtv[0].counter != GL(dl_tls_generation), 0))
+ the_map = _dl_update_slotinfo (GET_ADDR_MODULE);
+
+ p = dtv[GET_ADDR_MODULE].pointer.val;
+
+ if (__builtin_expect (p == TLS_DTV_UNALLOCATED, 0))
+ {
+ /* The allocation was deferred. Do it now. */
+ if (the_map == NULL)
+ {
+ /* Find the link map for this module. */
+ size_t idx = GET_ADDR_MODULE;
+ struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list);
+
+ while (idx >= listp->len)
+ {
+ idx -= listp->len;
+ listp = listp->next;
+ }
+
+ the_map = listp->slotinfo[idx].map;
+ }
+
+ p = dtv[GET_ADDR_MODULE].pointer.val = allocate_and_init (the_map);
+ dtv[GET_ADDR_MODULE].pointer.is_static = false;
+ }
+
+ return (char *) p + GET_ADDR_OFFSET;
+}
+# endif
+
+
+/* Look up the module's TLS block as for __tls_get_addr,
+ but never touch anything. Return null if it's not allocated yet. */
+void *
+internal_function
+_dl_tls_get_addr_soft (struct link_map *l)
+{
+ if (__builtin_expect (l->l_tls_modid == 0, 0))
+ /* This module has no TLS segment. */
+ return NULL;
+
+ dtv_t *dtv = THREAD_DTV ();
+ if (__builtin_expect (dtv[0].counter != GL(dl_tls_generation), 0))
+ {
+ /* This thread's DTV is not completely current,
+ but it might already cover this module. */
+
+ if (l->l_tls_modid >= dtv[-1].counter)
+ /* Nope. */
+ return NULL;
+
+ size_t idx = l->l_tls_modid;
+ struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list);
+ while (idx >= listp->len)
+ {
+ idx -= listp->len;
+ listp = listp->next;
+ }
+
+ /* We've reached the slot for this module.
+ If its generation counter is higher than the DTV's,
+ this thread does not know about this module yet. */
+ if (dtv[0].counter < listp->slotinfo[idx].gen)
+ return NULL;
+ }
+
+ void *data = dtv[l->l_tls_modid].pointer.val;
+ if (__builtin_expect (data == TLS_DTV_UNALLOCATED, 0))
+ /* The DTV is current, but this thread has not yet needed
+ to allocate this module's segment. */
+ data = NULL;
+
+ return data;
+}
+
+
+void
+_dl_add_to_slotinfo (struct link_map *l)
+{
+ /* Now that we know the object is loaded successfully add
+ modules containing TLS data to the dtv info table. We
+ might have to increase its size. */
+ struct dtv_slotinfo_list *listp;
+ struct dtv_slotinfo_list *prevp;
+ size_t idx = l->l_tls_modid;
+
+ /* Find the place in the dtv slotinfo list. */
+ listp = GL(dl_tls_dtv_slotinfo_list);
+ prevp = NULL; /* Needed to shut up gcc. */
+ do
+ {
+ /* Does it fit in the array of this list element? */
+ if (idx < listp->len)
+ break;
+ idx -= listp->len;
+ prevp = listp;
+ listp = listp->next;
+ }
+ while (listp != NULL);
+
+ if (listp == NULL)
+ {
+ /* When we come here it means we have to add a new element
+ to the slotinfo list. And the new module must be in
+ the first slot. */
+ assert (idx == 0);
+
+ listp = prevp->next = (struct dtv_slotinfo_list *)
+ malloc (sizeof (struct dtv_slotinfo_list)
+ + TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo));
+ if (listp == NULL)
+ {
+ /* We ran out of memory. We will simply fail this
+ call but don't undo anything we did so far. The
+ application will crash or be terminated anyway very
+ soon. */
+
+ /* We have to do this since some entries in the dtv
+ slotinfo array might already point to this
+ generation. */
+ ++GL(dl_tls_generation);
+
+ _dl_signal_error (ENOMEM, "dlopen", NULL, N_("\
+cannot create TLS data structures"));
+ }
+
+ listp->len = TLS_SLOTINFO_SURPLUS;
+ listp->next = NULL;
+ memset (listp->slotinfo, '\0',
+ TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo));
+ }
+
+ /* Add the information into the slotinfo data structure. */
+ listp->slotinfo[idx].map = l;
+ listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1;
+}
+#endif /* use TLS */
diff --git a/libc/elf/dl-trampoline.c b/libc/elf/dl-trampoline.c
new file mode 100644
index 000000000..3ca89f387
--- /dev/null
+++ b/libc/elf/dl-trampoline.c
@@ -0,0 +1 @@
+#error "Architecture specific PLT trampolines must be defined."
diff --git a/libc/elf/dl-tsd.c b/libc/elf/dl-tsd.c
new file mode 100644
index 000000000..f01f8d652
--- /dev/null
+++ b/libc/elf/dl-tsd.c
@@ -0,0 +1,58 @@
+/* Thread-local data used by error handling for runtime dynamic linker.
+ Copyright (C) 2002, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifdef _LIBC_REENTRANT
+
+# include <ldsodefs.h>
+# include <tls.h>
+
+# ifndef SHARED
+
+/* _dl_error_catch_tsd points to this for the single-threaded case.
+ It's reset by the thread library for multithreaded programs
+ if we're not using __thread. */
+void ** __attribute__ ((const))
+_dl_initial_error_catch_tsd (void)
+{
+# if USE___THREAD
+ static __thread void *data;
+# else
+ static void *data;
+# endif
+ return &data;
+}
+void **(*_dl_error_catch_tsd) (void) __attribute__ ((const))
+ = &_dl_initial_error_catch_tsd;
+
+# elif USE___THREAD
+
+/* libpthread sets _dl_error_catch_tsd to point to this function.
+ We define it here instead of in libpthread so that it doesn't
+ need to have a TLS segment of its own just for this one pointer. */
+
+void ** __attribute__ ((const))
+__libc_dl_error_tsd (void)
+{
+ static __thread void *data attribute_tls_model_ie;
+ return &data;
+}
+
+# endif /* SHARED */
+
+#endif /* _LIBC_REENTRANT */
diff --git a/libc/elf/dl-version.c b/libc/elf/dl-version.c
new file mode 100644
index 000000000..9e881162a
--- /dev/null
+++ b/libc/elf/dl-version.c
@@ -0,0 +1,392 @@
+/* Handle symbol and library versioning.
+ Copyright (C) 1997-2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <elf.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ldsodefs.h>
+#include <stdio-common/_itoa.h>
+
+#include <assert.h>
+
+
+#ifndef VERSYMIDX
+# define VERSYMIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (tag))
+#endif
+
+
+#define make_string(string, rest...) \
+ ({ \
+ const char *all[] = { string, ## rest }; \
+ size_t len, cnt; \
+ char *result, *cp; \
+ \
+ len = 1; \
+ for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
+ len += strlen (all[cnt]); \
+ \
+ cp = result = alloca (len); \
+ for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
+ cp = __stpcpy (cp, all[cnt]); \
+ \
+ result; \
+ })
+
+
+static inline struct link_map *
+__attribute ((always_inline))
+find_needed (const char *name, struct link_map *map)
+{
+ struct link_map *tmap;
+ unsigned int n;
+
+ for (tmap = GL(dl_ns)[map->l_ns]._ns_loaded; tmap != NULL;
+ tmap = tmap->l_next)
+ if (_dl_name_match_p (name, tmap))
+ return tmap;
+
+ /* The required object is not in the global scope, look to see if it is
+ a dependency of the current object. */
+ for (n = 0; n < map->l_searchlist.r_nlist; n++)
+ if (_dl_name_match_p (name, map->l_searchlist.r_list[n]))
+ return map->l_searchlist.r_list[n];
+
+ /* Should never happen. */
+ return NULL;
+}
+
+
+static int
+internal_function
+match_symbol (const char *name, Lmid_t ns, ElfW(Word) hash, const char *string,
+ struct link_map *map, int verbose, int weak)
+{
+ const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+ ElfW(Addr) def_offset;
+ ElfW(Verdef) *def;
+ /* Initialize to make the compiler happy. */
+ const char *errstring = NULL;
+ int result = 0;
+
+ /* Display information about what we are doing while debugging. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_VERSIONS, 0))
+ _dl_debug_printf ("\
+checking for version `%s' in file %s [%lu] required by file %s [%lu]\n",
+ string, map->l_name[0] ? map->l_name : rtld_progname,
+ map->l_ns, name, ns);
+
+ if (__builtin_expect (map->l_info[VERSYMIDX (DT_VERDEF)] == NULL, 0))
+ {
+ /* The file has no symbol versioning. I.e., the dependent
+ object was linked against another version of this file. We
+ only print a message if verbose output is requested. */
+ if (verbose)
+ {
+ /* XXX We cannot translate the messages. */
+ errstring = make_string ("\
+no version information available (required by ", name, ")");
+ goto call_cerror;
+ }
+ return 0;
+ }
+
+ def_offset = map->l_info[VERSYMIDX (DT_VERDEF)]->d_un.d_ptr;
+ assert (def_offset != 0);
+
+ def = (ElfW(Verdef) *) ((char *) map->l_addr + def_offset);
+ while (1)
+ {
+ /* Currently the version number of the definition entry is 1.
+ Make sure all we see is this version. */
+ if (__builtin_expect (def->vd_version, 1) != 1)
+ {
+ char buf[20];
+ buf[sizeof (buf) - 1] = '\0';
+ /* XXX We cannot translate the message. */
+ errstring = make_string ("unsupported version ",
+ _itoa (def->vd_version,
+ &buf[sizeof (buf) - 1], 10, 0),
+ " of Verdef record");
+ result = 1;
+ goto call_cerror;
+ }
+
+ /* Compare the hash values. */
+ if (hash == def->vd_hash)
+ {
+ ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) def + def->vd_aux);
+
+ /* To be safe, compare the string as well. */
+ if (__builtin_expect (strcmp (string, strtab + aux->vda_name), 0)
+ == 0)
+ /* Bingo! */
+ return 0;
+ }
+
+ /* If no more definitions we failed to find what we want. */
+ if (def->vd_next == 0)
+ break;
+
+ /* Next definition. */
+ def = (ElfW(Verdef) *) ((char *) def + def->vd_next);
+ }
+
+ /* Symbol not found. If it was a weak reference it is not fatal. */
+ if (__builtin_expect (weak, 1))
+ {
+ if (verbose)
+ {
+ /* XXX We cannot translate the message. */
+ errstring = make_string ("weak version `", string,
+ "' not found (required by ", name, ")");
+ goto call_cerror;
+ }
+ return 0;
+ }
+
+ /* XXX We cannot translate the message. */
+ errstring = make_string ("version `", string, "' not found (required by ",
+ name, ")");
+ result = 1;
+ call_cerror:
+ _dl_signal_cerror (0, map->l_name[0] ? map->l_name : rtld_progname,
+ NULL, errstring);
+ return result;
+}
+
+
+int
+internal_function
+_dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
+{
+ int result = 0;
+ const char *strtab;
+ /* Pointer to section with needed versions. */
+ ElfW(Dyn) *dyn;
+ /* Pointer to dynamic section with definitions. */
+ ElfW(Dyn) *def;
+ /* We need to find out which is the highest version index used
+ in a dependecy. */
+ unsigned int ndx_high = 0;
+ /* Initialize to make the compiler happy. */
+ const char *errstring = NULL;
+ int errval = 0;
+
+ /* If we don't have a string table, we must be ok. */
+ if (map->l_info[DT_STRTAB] == NULL)
+ return 0;
+ strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+
+ dyn = map->l_info[VERSYMIDX (DT_VERNEED)];
+ def = map->l_info[VERSYMIDX (DT_VERDEF)];
+
+ if (dyn != NULL)
+ {
+ /* This file requires special versions from its dependencies. */
+ ElfW(Verneed) *ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
+
+ /* Currently the version number of the needed entry is 1.
+ Make sure all we see is this version. */
+ if (__builtin_expect (ent->vn_version, 1) != 1)
+ {
+ char buf[20];
+ buf[sizeof (buf) - 1] = '\0';
+ /* XXX We cannot translate the message. */
+ errstring = make_string ("unsupported version ",
+ _itoa (ent->vn_version,
+ &buf[sizeof (buf) - 1], 10, 0),
+ " of Verneed record\n");
+ call_error:
+ _dl_signal_error (errval, *map->l_name ? map->l_name : rtld_progname,
+ NULL, errstring);
+ }
+
+ while (1)
+ {
+ ElfW(Vernaux) *aux;
+ struct link_map *needed = find_needed (strtab + ent->vn_file, map);
+
+ /* If NEEDED is NULL this means a dependency was not found
+ and no stub entry was created. This should never happen. */
+ assert (needed != NULL);
+
+ /* Make sure this is no stub we created because of a missing
+ dependency. */
+ if (__builtin_expect (! trace_mode, 1)
+ || ! __builtin_expect (needed->l_faked, 0))
+ {
+ /* NEEDED is the map for the file we need. Now look for the
+ dependency symbols. */
+ aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
+ while (1)
+ {
+ /* Match the symbol. */
+ result |= match_symbol ((*map->l_name
+ ? map->l_name : rtld_progname),
+ map->l_ns, aux->vna_hash,
+ strtab + aux->vna_name,
+ needed->l_real, verbose,
+ aux->vna_flags & VER_FLG_WEAK);
+
+ /* Compare the version index. */
+ if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high)
+ ndx_high = aux->vna_other & 0x7fff;
+
+ if (aux->vna_next == 0)
+ /* No more symbols. */
+ break;
+
+ /* Next symbol. */
+ aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
+ }
+ }
+
+ if (ent->vn_next == 0)
+ /* No more dependencies. */
+ break;
+
+ /* Next dependency. */
+ ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
+ }
+ }
+
+ /* We also must store the names of the defined versions. Determine
+ the maximum index here as well.
+
+ XXX We could avoid the loop by just taking the number of definitions
+ as an upper bound of new indeces. */
+ if (def != NULL)
+ {
+ ElfW(Verdef) *ent;
+ ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr);
+ while (1)
+ {
+ if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high)
+ ndx_high = ent->vd_ndx & 0x7fff;
+
+ if (ent->vd_next == 0)
+ /* No more definitions. */
+ break;
+
+ ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
+ }
+ }
+
+ if (ndx_high > 0)
+ {
+ /* Now we are ready to build the array with the version names
+ which can be indexed by the version index in the VERSYM
+ section. */
+ map->l_versions = (struct r_found_version *)
+ calloc (ndx_high + 1, sizeof (*map->l_versions));
+ if (__builtin_expect (map->l_versions == NULL, 0))
+ {
+ errstring = N_("cannot allocate version reference table");
+ errval = ENOMEM;
+ goto call_error;
+ }
+
+ /* Store the number of available symbols. */
+ map->l_nversions = ndx_high + 1;
+
+ /* Compute the pointer to the version symbols. */
+ map->l_versyms = (void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
+
+ if (dyn != NULL)
+ {
+ ElfW(Verneed) *ent;
+ ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
+ while (1)
+ {
+ ElfW(Vernaux) *aux;
+ aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
+ while (1)
+ {
+ ElfW(Half) ndx = aux->vna_other & 0x7fff;
+ map->l_versions[ndx].hash = aux->vna_hash;
+ map->l_versions[ndx].hidden = aux->vna_other & 0x8000;
+ map->l_versions[ndx].name = &strtab[aux->vna_name];
+ map->l_versions[ndx].filename = &strtab[ent->vn_file];
+
+ if (aux->vna_next == 0)
+ /* No more symbols. */
+ break;
+
+ /* Advance to next symbol. */
+ aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
+ }
+
+ if (ent->vn_next == 0)
+ /* No more dependencies. */
+ break;
+
+ /* Advance to next dependency. */
+ ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
+ }
+ }
+
+ /* And insert the defined versions. */
+ if (def != NULL)
+ {
+ ElfW(Verdef) *ent;
+ ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr);
+ while (1)
+ {
+ ElfW(Verdaux) *aux;
+ aux = (ElfW(Verdaux) *) ((char *) ent + ent->vd_aux);
+
+ if ((ent->vd_flags & VER_FLG_BASE) == 0)
+ {
+ /* The name of the base version should not be
+ available for matching a versioned symbol. */
+ ElfW(Half) ndx = ent->vd_ndx & 0x7fff;
+ map->l_versions[ndx].hash = ent->vd_hash;
+ map->l_versions[ndx].name = &strtab[aux->vda_name];
+ map->l_versions[ndx].filename = NULL;
+ }
+
+ if (ent->vd_next == 0)
+ /* No more definitions. */
+ break;
+
+ ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
+ }
+ }
+ }
+
+ return result;
+}
+
+
+int
+internal_function
+_dl_check_all_versions (struct link_map *map, int verbose, int trace_mode)
+{
+ struct link_map *l;
+ int result = 0;
+
+ for (l = map; l != NULL; l = l->l_next)
+ result |= (! l->l_faked
+ && _dl_check_map_versions (l, verbose, trace_mode));
+
+ return result;
+}
diff --git a/libc/elf/do-lookup.h b/libc/elf/do-lookup.h
new file mode 100644
index 000000000..f40ab9d8d
--- /dev/null
+++ b/libc/elf/do-lookup.h
@@ -0,0 +1,261 @@
+/* Look up a symbol in the loaded objects.
+ Copyright (C) 1995-2004, 2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+
+/* Inner part of the lookup functions. We return a value > 0 if we
+ found the symbol, the value 0 if nothing is found and < 0 if
+ something bad happened. */
+static int
+__attribute_noinline__
+do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
+ unsigned long int *old_hash, const ElfW(Sym) *ref,
+ struct sym_val *result, struct r_scope_elem *scope, size_t i,
+ const struct r_found_version *const version, int flags,
+ struct link_map *skip, int type_class)
+{
+ struct link_map **list = scope->r_list;
+ size_t n = scope->r_nlist;
+
+ do
+ {
+ /* These variables are used in the nested function. */
+ Elf_Symndx symidx;
+ int num_versions = 0;
+ const ElfW(Sym) *versioned_sym = NULL;
+
+ const struct link_map *map = list[i]->l_real;
+
+ /* Here come the extra test needed for `_dl_lookup_symbol_skip'. */
+ if (map == skip)
+ continue;
+
+ /* Don't search the executable when resolving a copy reloc. */
+ if ((type_class & ELF_RTYPE_CLASS_COPY) && map->l_type == lt_executable)
+ continue;
+
+ /* Do not look into objects which are going to be removed. */
+ if (map->l_removed)
+ continue;
+
+ /* Print some debugging info if wanted. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS, 0))
+ _dl_debug_printf ("symbol=%s; lookup in file=%s [%lu]\n",
+ undef_name,
+ map->l_name[0] ? map->l_name : rtld_progname,
+ map->l_ns);
+
+ /* If the hash table is empty there is nothing to do here. */
+ if (map->l_nbuckets == 0)
+ continue;
+
+ /* The tables for this map. */
+ const ElfW(Sym) *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
+ const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+
+
+ /* Nested routine to check whether the symbol matches. */
+ const ElfW(Sym) *
+ __attribute_noinline__
+ check_match (const ElfW(Sym) *sym)
+ {
+ assert (ELF_RTYPE_CLASS_PLT == 1);
+ if (__builtin_expect ((sym->st_value == 0 /* No value. */
+ && ELFW(ST_TYPE) (sym->st_info) != STT_TLS)
+ || (type_class & (sym->st_shndx == SHN_UNDEF)),
+ 0))
+ return NULL;
+
+ if (__builtin_expect (ELFW(ST_TYPE) (sym->st_info) > STT_FUNC
+ && ELFW(ST_TYPE) (sym->st_info) != STT_TLS, 0))
+ /* Ignore all but STT_NOTYPE, STT_OBJECT and STT_FUNC
+ entries (and STT_TLS if TLS is supported) since these
+ are no code/data definitions. */
+ return NULL;
+
+ if (sym != ref && strcmp (strtab + sym->st_name, undef_name))
+ /* Not the symbol we are looking for. */
+ return NULL;
+
+ const ElfW(Half) *verstab = map->l_versyms;
+ if (version != NULL)
+ {
+ if (__builtin_expect (verstab == NULL, 0))
+ {
+ /* We need a versioned symbol but haven't found any. If
+ this is the object which is referenced in the verneed
+ entry it is a bug in the library since a symbol must
+ not simply disappear.
+
+ It would also be a bug in the object since it means that
+ the list of required versions is incomplete and so the
+ tests in dl-version.c haven't found a problem.*/
+ assert (version->filename == NULL
+ || ! _dl_name_match_p (version->filename, map));
+
+ /* Otherwise we accept the symbol. */
+ }
+ else
+ {
+ /* We can match the version information or use the
+ default one if it is not hidden. */
+ ElfW(Half) ndx = verstab[symidx] & 0x7fff;
+ if ((map->l_versions[ndx].hash != version->hash
+ || strcmp (map->l_versions[ndx].name, version->name))
+ && (version->hidden || map->l_versions[ndx].hash
+ || (verstab[symidx] & 0x8000)))
+ /* It's not the version we want. */
+ return NULL;
+ }
+ }
+ else
+ {
+ /* No specific version is selected. There are two ways we
+ can got here:
+
+ - a binary which does not include versioning information
+ is loaded
+
+ - dlsym() instead of dlvsym() is used to get a symbol which
+ might exist in more than one form
+
+ If the library does not provide symbol version information
+ there is no problem at at: we simply use the symbol if it
+ is defined.
+
+ These two lookups need to be handled differently if the
+ library defines versions. In the case of the old
+ unversioned application the oldest (default) version
+ should be used. In case of a dlsym() call the latest and
+ public interface should be returned. */
+ if (verstab != NULL)
+ {
+ if ((verstab[symidx] & 0x7fff)
+ >= ((flags & DL_LOOKUP_RETURN_NEWEST) ? 2 : 3))
+ {
+ /* Don't accept hidden symbols. */
+ if ((verstab[symidx] & 0x8000) == 0
+ && num_versions++ == 0)
+ /* No version so far. */
+ versioned_sym = sym;
+
+ return NULL;
+ }
+ }
+ }
+
+ /* There cannot be another entry for this symbol so stop here. */
+ return sym;
+ }
+
+ const ElfW(Sym) *sym;
+ const ElfW(Addr) *bitmask = map->l_gnu_bitmask;
+ if (__builtin_expect (bitmask != NULL, 1))
+ {
+ ElfW(Addr) bitmask_word
+ = bitmask[(new_hash / __ELF_NATIVE_CLASS)
+ & map->l_gnu_bitmask_idxbits];
+
+ unsigned int hashbit1 = new_hash & (__ELF_NATIVE_CLASS - 1);
+ unsigned int hashbit2 = ((new_hash >> map->l_gnu_shift)
+ & (__ELF_NATIVE_CLASS - 1));
+
+ if (__builtin_expect ((bitmask_word >> hashbit1)
+ & (bitmask_word >> hashbit2) & 1, 0))
+ {
+ Elf32_Word bucket = map->l_gnu_buckets[new_hash
+ % map->l_nbuckets];
+ if (bucket != 0)
+ {
+ const Elf32_Word *hasharr = &map->l_gnu_chain_zero[bucket];
+
+ do
+ if ((*hasharr & ~1u) == (new_hash & ~1u))
+ {
+ symidx = hasharr - map->l_gnu_chain_zero;
+ sym = check_match (&symtab[symidx]);
+ if (sym != NULL)
+ goto found_it;
+ }
+ while ((*hasharr++ & 1u) == 0);
+ }
+ }
+ }
+ else
+ {
+ if (*old_hash == 0xffffffff)
+ *old_hash = _dl_elf_hash (undef_name);
+
+ /* Use the old SysV-style hash table. Search the appropriate
+ hash bucket in this object's symbol table for a definition
+ for the same symbol name. */
+ for (symidx = map->l_buckets[*old_hash % map->l_nbuckets];
+ symidx != STN_UNDEF;
+ symidx = map->l_chain[symidx])
+ {
+ sym = check_match (&symtab[symidx]);
+ if (sym != NULL)
+ goto found_it;
+ }
+ }
+
+ /* If we have seen exactly one versioned symbol while we are
+ looking for an unversioned symbol and the version is not the
+ default version we still accept this symbol since there are
+ no possible ambiguities. */
+ sym = num_versions == 1 ? versioned_sym : NULL;
+
+ if (sym != NULL)
+ {
+ found_it:
+ switch (ELFW(ST_BIND) (sym->st_info))
+ {
+ case STB_WEAK:
+ /* Weak definition. Use this value if we don't find another. */
+ if (__builtin_expect (GLRO(dl_dynamic_weak), 0))
+ {
+ if (! result->s)
+ {
+ result->s = sym;
+ result->m = (struct link_map *) map;
+ }
+ break;
+ }
+ /* FALLTHROUGH */
+ case STB_GLOBAL:
+ /* Global definition. Just what we need. */
+ result->s = sym;
+ result->m = (struct link_map *) map;
+ return 1;
+ default:
+ /* Local symbols are ignored. */
+ break;
+ }
+ }
+
+ /* If this current map is the one mentioned in the verneed entry
+ and we have not found a weak entry, it is a bug. */
+ if (symidx == STN_UNDEF && version != NULL && version->filename != NULL
+ && __builtin_expect (_dl_name_match_p (version->filename, map), 0))
+ return -1;
+ }
+ while (++i < n);
+
+ /* We have not found anything until now. */
+ return 0;
+}
diff --git a/libc/elf/do-rel.h b/libc/elf/do-rel.h
new file mode 100644
index 000000000..990b9615e
--- /dev/null
+++ b/libc/elf/do-rel.h
@@ -0,0 +1,139 @@
+/* Do relocations for ELF dynamic linking.
+ Copyright (C) 1995-2003, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/* This file may be included twice, to define both
+ `elf_dynamic_do_rel' and `elf_dynamic_do_rela'. */
+
+#ifdef DO_RELA
+# define elf_dynamic_do_rel elf_dynamic_do_rela
+# define RELCOUNT_IDX VERSYMIDX (DT_RELACOUNT)
+# define Rel Rela
+# define elf_machine_rel elf_machine_rela
+# define elf_machine_rel_relative elf_machine_rela_relative
+#else
+# define RELCOUNT_IDX VERSYMIDX (DT_RELCOUNT)
+#endif
+
+#ifndef DO_ELF_MACHINE_REL_RELATIVE
+# define DO_ELF_MACHINE_REL_RELATIVE(map, l_addr, relative) \
+ elf_machine_rel_relative (l_addr, relative, \
+ (void *) (l_addr + relative->r_offset))
+#endif
+
+#ifndef VERSYMIDX
+# define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym))
+#endif
+#ifndef VALIDX
+# define VALIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \
+ + DT_EXTRANUM + DT_VALTAGIDX (tag))
+#endif
+
+/* Perform the relocations in MAP on the running program image as specified
+ by RELTAG, SZTAG. If LAZY is nonzero, this is the first pass on PLT
+ relocations; they should be set up to call _dl_runtime_resolve, rather
+ than fully resolved now. */
+
+auto inline void __attribute__ ((always_inline))
+elf_dynamic_do_rel (struct link_map *map,
+ ElfW(Addr) reladdr, ElfW(Addr) relsize,
+ int lazy)
+{
+ const ElfW(Rel) *r = (const void *) reladdr;
+ const ElfW(Rel) *end = (const void *) (reladdr + relsize);
+ ElfW(Addr) l_addr = map->l_addr;
+
+#if (!defined DO_RELA || !defined ELF_MACHINE_PLT_REL) && !defined RTLD_BOOTSTRAP
+ /* We never bind lazily during ld.so bootstrap. Unfortunately gcc is
+ not clever enough to see through all the function calls to realize
+ that. */
+ if (lazy)
+ {
+ /* Doing lazy PLT relocations; they need very little info. */
+ for (; r < end; ++r)
+ elf_machine_lazy_rel (map, l_addr, r);
+ }
+ else
+#endif
+ {
+ const ElfW(Sym) *const symtab =
+ (const void *) D_PTR (map, l_info[DT_SYMTAB]);
+ ElfW(Word) nrelative = (map->l_info[RELCOUNT_IDX] == NULL
+ ? 0 : map->l_info[RELCOUNT_IDX]->d_un.d_val);
+ const ElfW(Rel) *relative = r;
+ r = r + MIN (nrelative, relsize / sizeof (ElfW(Rel)));
+
+#ifndef RTLD_BOOTSTRAP
+ /* This is defined in rtld.c, but nowhere in the static libc.a; make
+ the reference weak so static programs can still link. This
+ declaration cannot be done when compiling rtld.c (i.e. #ifdef
+ RTLD_BOOTSTRAP) because rtld.c contains the common defn for
+ _dl_rtld_map, which is incompatible with a weak decl in the same
+ file. */
+# ifndef SHARED
+ weak_extern (GL(dl_rtld_map));
+# endif
+ if (map != &GL(dl_rtld_map)) /* Already done in rtld itself. */
+# if !defined DO_RELA || defined ELF_MACHINE_REL_RELATIVE
+ /* Rela platforms get the offset from r_addend and this must
+ be copied in the relocation address. Therefore we can skip
+ the relative relocations only if this is for rel
+ relocations or rela relocations if they are computed as
+ memory_loc += l_addr... */
+ if (l_addr != 0)
+# else
+ /* ...or we know the object has been prelinked. */
+ if (l_addr != 0 || ! map->l_info[VALIDX(DT_GNU_PRELINKED)])
+# endif
+#endif
+ for (; relative < r; ++relative)
+ DO_ELF_MACHINE_REL_RELATIVE (map, l_addr, relative);
+
+#ifdef RTLD_BOOTSTRAP
+ /* The dynamic linker always uses versioning. */
+ assert (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL);
+#else
+ if (map->l_info[VERSYMIDX (DT_VERSYM)])
+#endif
+ {
+ const ElfW(Half) *const version =
+ (const void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
+
+ for (; r < end; ++r)
+ {
+ ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
+ elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)],
+ &map->l_versions[ndx],
+ (void *) (l_addr + r->r_offset));
+ }
+ }
+#ifndef RTLD_BOOTSTRAP
+ else
+ for (; r < end; ++r)
+ elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], NULL,
+ (void *) (l_addr + r->r_offset));
+#endif
+ }
+}
+
+#undef elf_dynamic_do_rel
+#undef Rel
+#undef elf_machine_rel
+#undef elf_machine_rel_relative
+#undef DO_ELF_MACHINE_REL_RELATIVE
+#undef RELCOUNT_IDX
diff --git a/libc/elf/dynamic-link.h b/libc/elf/dynamic-link.h
new file mode 100644
index 000000000..7eb9a3613
--- /dev/null
+++ b/libc/elf/dynamic-link.h
@@ -0,0 +1,343 @@
+/* Inline functions for dynamic linking.
+ Copyright (C) 1995-2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <elf.h>
+#include <assert.h>
+
+#ifdef RESOLVE_MAP
+/* We pass reloc_addr as a pointer to void, as opposed to a pointer to
+ ElfW(Addr), because not all architectures can assume that the
+ relocated address is properly aligned, whereas the compiler is
+ entitled to assume that a pointer to a type is properly aligned for
+ the type. Even if we cast the pointer back to some other type with
+ less strict alignment requirements, the compiler might still
+ remember that the pointer was originally more aligned, thereby
+ optimizing away alignment tests or using word instructions for
+ copying memory, breaking the very code written to handle the
+ unaligned cases. */
+# if ! ELF_MACHINE_NO_REL
+auto inline void __attribute__((always_inline))
+elf_machine_rel (struct link_map *map, const ElfW(Rel) *reloc,
+ const ElfW(Sym) *sym, const struct r_found_version *version,
+ void *const reloc_addr);
+auto inline void __attribute__((always_inline))
+elf_machine_rel_relative (ElfW(Addr) l_addr, const ElfW(Rel) *reloc,
+ void *const reloc_addr);
+# endif
+# if ! ELF_MACHINE_NO_RELA
+auto inline void __attribute__((always_inline))
+elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
+ const ElfW(Sym) *sym, const struct r_found_version *version,
+ void *const reloc_addr);
+auto inline void __attribute__((always_inline))
+elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
+ void *const reloc_addr);
+# endif
+# if ELF_MACHINE_NO_RELA || defined ELF_MACHINE_PLT_REL
+auto inline void __attribute__((always_inline))
+elf_machine_lazy_rel (struct link_map *map,
+ ElfW(Addr) l_addr, const ElfW(Rel) *reloc);
+# else
+auto inline void __attribute__((always_inline))
+elf_machine_lazy_rel (struct link_map *map,
+ ElfW(Addr) l_addr, const ElfW(Rela) *reloc);
+# endif
+#endif
+
+#include <dl-machine.h>
+
+#ifndef VERSYMIDX
+# define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym))
+#endif
+
+
+/* Read the dynamic section at DYN and fill in INFO with indices DT_*. */
+#ifndef RESOLVE_MAP
+static
+#else
+auto
+#endif
+inline void __attribute__ ((unused, always_inline))
+elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
+{
+ ElfW(Dyn) *dyn = l->l_ld;
+ ElfW(Dyn) **info;
+
+#ifndef RTLD_BOOTSTRAP
+ if (dyn == NULL)
+ return;
+#endif
+
+ info = l->l_info;
+
+ while (dyn->d_tag != DT_NULL)
+ {
+ if (dyn->d_tag < DT_NUM)
+ info[dyn->d_tag] = dyn;
+ else if (dyn->d_tag >= DT_LOPROC &&
+ dyn->d_tag < DT_LOPROC + DT_THISPROCNUM)
+ info[dyn->d_tag - DT_LOPROC + DT_NUM] = dyn;
+ else if ((Elf32_Word) DT_VERSIONTAGIDX (dyn->d_tag) < DT_VERSIONTAGNUM)
+ info[VERSYMIDX (dyn->d_tag)] = dyn;
+ else if ((Elf32_Word) DT_EXTRATAGIDX (dyn->d_tag) < DT_EXTRANUM)
+ info[DT_EXTRATAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
+ + DT_VERSIONTAGNUM] = dyn;
+ else if ((Elf32_Word) DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM)
+ info[DT_VALTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
+ + DT_VERSIONTAGNUM + DT_EXTRANUM] = dyn;
+ else if ((Elf32_Word) DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM)
+ info[DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
+ + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn;
+ ++dyn;
+ }
+
+#define DL_RO_DYN_TEMP_CNT 8
+
+#ifndef DL_RO_DYN_SECTION
+ /* Don't adjust .dynamic unnecessarily. */
+ if (l->l_addr != 0)
+ {
+ ElfW(Addr) l_addr = l->l_addr;
+ int cnt = 0;
+
+# define ADJUST_DYN_INFO(tag) \
+ do \
+ if (info[tag] != NULL) \
+ { \
+ if (temp) \
+ { \
+ temp[cnt].d_tag = info[tag]->d_tag; \
+ temp[cnt].d_un.d_ptr = info[tag]->d_un.d_ptr + l_addr; \
+ info[tag] = temp + cnt++; \
+ } \
+ else \
+ info[tag]->d_un.d_ptr += l_addr; \
+ } \
+ while (0)
+
+ ADJUST_DYN_INFO (DT_HASH);
+ ADJUST_DYN_INFO (DT_PLTGOT);
+ ADJUST_DYN_INFO (DT_STRTAB);
+ ADJUST_DYN_INFO (DT_SYMTAB);
+# if ! ELF_MACHINE_NO_RELA
+ ADJUST_DYN_INFO (DT_RELA);
+# endif
+# if ! ELF_MACHINE_NO_REL
+ ADJUST_DYN_INFO (DT_REL);
+# endif
+ ADJUST_DYN_INFO (DT_JMPREL);
+ ADJUST_DYN_INFO (VERSYMIDX (DT_VERSYM));
+ ADJUST_DYN_INFO (DT_ADDRTAGIDX (DT_GNU_HASH) + DT_NUM + DT_THISPROCNUM
+ + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM);
+# undef ADJUST_DYN_INFO
+ assert (cnt <= DL_RO_DYN_TEMP_CNT);
+ }
+#endif
+ if (info[DT_PLTREL] != NULL)
+ {
+#if ELF_MACHINE_NO_RELA
+ assert (info[DT_PLTREL]->d_un.d_val == DT_REL);
+#elif ELF_MACHINE_NO_REL
+ assert (info[DT_PLTREL]->d_un.d_val == DT_RELA);
+#else
+ assert (info[DT_PLTREL]->d_un.d_val == DT_REL
+ || info[DT_PLTREL]->d_un.d_val == DT_RELA);
+#endif
+ }
+#if ! ELF_MACHINE_NO_RELA
+ if (info[DT_RELA] != NULL)
+ assert (info[DT_RELAENT]->d_un.d_val == sizeof (ElfW(Rela)));
+# endif
+# if ! ELF_MACHINE_NO_REL
+ if (info[DT_REL] != NULL)
+ assert (info[DT_RELENT]->d_un.d_val == sizeof (ElfW(Rel)));
+#endif
+#ifdef RTLD_BOOTSTRAP
+ /* Only the bind now flags are allowed. */
+ assert (info[VERSYMIDX (DT_FLAGS_1)] == NULL
+ || info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val == DF_1_NOW);
+ assert (info[DT_FLAGS] == NULL
+ || info[DT_FLAGS]->d_un.d_val == DF_BIND_NOW);
+ /* Flags must not be set for ld.so. */
+ assert (info[DT_RUNPATH] == NULL);
+ assert (info[DT_RPATH] == NULL);
+#else
+ if (info[DT_FLAGS] != NULL)
+ {
+ /* Flags are used. Translate to the old form where available.
+ Since these l_info entries are only tested for NULL pointers it
+ is ok if they point to the DT_FLAGS entry. */
+ l->l_flags = info[DT_FLAGS]->d_un.d_val;
+
+ if (l->l_flags & DF_SYMBOLIC)
+ info[DT_SYMBOLIC] = info[DT_FLAGS];
+ if (l->l_flags & DF_TEXTREL)
+ info[DT_TEXTREL] = info[DT_FLAGS];
+ if (l->l_flags & DF_BIND_NOW)
+ info[DT_BIND_NOW] = info[DT_FLAGS];
+ }
+ if (info[VERSYMIDX (DT_FLAGS_1)] != NULL)
+ {
+ l->l_flags_1 = info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val;
+
+ if (l->l_flags_1 & DF_1_NOW)
+ info[DT_BIND_NOW] = info[VERSYMIDX (DT_FLAGS_1)];
+ }
+ if (info[DT_RUNPATH] != NULL)
+ /* If both RUNPATH and RPATH are given, the latter is ignored. */
+ info[DT_RPATH] = NULL;
+#endif
+}
+
+#ifdef RESOLVE_MAP
+
+# ifdef RTLD_BOOTSTRAP
+# define ELF_DURING_STARTUP (1)
+# else
+# define ELF_DURING_STARTUP (0)
+# endif
+
+/* Get the definitions of `elf_dynamic_do_rel' and `elf_dynamic_do_rela'.
+ These functions are almost identical, so we use cpp magic to avoid
+ duplicating their code. It cannot be done in a more general function
+ because we must be able to completely inline. */
+
+/* On some machines, notably SPARC, DT_REL* includes DT_JMPREL in its
+ range. Note that according to the ELF spec, this is completely legal!
+ But conditionally define things so that on machines we know this will
+ not happen we do something more optimal. */
+
+# ifdef ELF_MACHINE_PLTREL_OVERLAP
+# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, do_lazy, test_rel) \
+ do { \
+ struct { ElfW(Addr) start, size; int lazy; } ranges[3]; \
+ int ranges_index; \
+ \
+ ranges[0].lazy = ranges[2].lazy = 0; \
+ ranges[1].lazy = 1; \
+ ranges[0].size = ranges[1].size = ranges[2].size = 0; \
+ \
+ if ((map)->l_info[DT_##RELOC]) \
+ { \
+ ranges[0].start = D_PTR ((map), l_info[DT_##RELOC]); \
+ ranges[0].size = (map)->l_info[DT_##RELOC##SZ]->d_un.d_val; \
+ } \
+ \
+ if ((do_lazy) \
+ && (map)->l_info[DT_PLTREL] \
+ && (!test_rel || (map)->l_info[DT_PLTREL]->d_un.d_val == DT_##RELOC)) \
+ { \
+ ranges[1].start = D_PTR ((map), l_info[DT_JMPREL]); \
+ ranges[1].size = (map)->l_info[DT_PLTRELSZ]->d_un.d_val; \
+ ranges[2].start = ranges[1].start + ranges[1].size; \
+ ranges[2].size = ranges[0].start + ranges[0].size - ranges[2].start; \
+ ranges[0].size = ranges[1].start - ranges[0].start; \
+ } \
+ \
+ for (ranges_index = 0; ranges_index < 3; ++ranges_index) \
+ elf_dynamic_do_##reloc ((map), \
+ ranges[ranges_index].start, \
+ ranges[ranges_index].size, \
+ ranges[ranges_index].lazy); \
+ } while (0)
+# else
+# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, do_lazy, test_rel) \
+ do { \
+ struct { ElfW(Addr) start, size; int lazy; } ranges[2]; \
+ ranges[0].lazy = 0; \
+ ranges[0].size = ranges[1].size = 0; \
+ ranges[0].start = 0; \
+ \
+ if ((map)->l_info[DT_##RELOC]) \
+ { \
+ ranges[0].start = D_PTR ((map), l_info[DT_##RELOC]); \
+ ranges[0].size = (map)->l_info[DT_##RELOC##SZ]->d_un.d_val; \
+ } \
+ if ((map)->l_info[DT_PLTREL] \
+ && (!test_rel || (map)->l_info[DT_PLTREL]->d_un.d_val == DT_##RELOC)) \
+ { \
+ ElfW(Addr) start = D_PTR ((map), l_info[DT_JMPREL]); \
+ \
+ if (! ELF_DURING_STARTUP \
+ && ((do_lazy) \
+ /* This test does not only detect whether the relocation \
+ sections are in the right order, it also checks whether \
+ there is a DT_REL/DT_RELA section. */ \
+ || ranges[0].start + ranges[0].size != start)) \
+ { \
+ ranges[1].start = start; \
+ ranges[1].size = (map)->l_info[DT_PLTRELSZ]->d_un.d_val; \
+ ranges[1].lazy = (do_lazy); \
+ } \
+ else \
+ { \
+ /* Combine processing the sections. */ \
+ assert (ranges[0].start + ranges[0].size == start); \
+ ranges[0].size += (map)->l_info[DT_PLTRELSZ]->d_un.d_val; \
+ } \
+ } \
+ \
+ if (ELF_DURING_STARTUP) \
+ elf_dynamic_do_##reloc ((map), ranges[0].start, ranges[0].size, 0); \
+ else \
+ { \
+ int ranges_index; \
+ for (ranges_index = 0; ranges_index < 2; ++ranges_index) \
+ elf_dynamic_do_##reloc ((map), \
+ ranges[ranges_index].start, \
+ ranges[ranges_index].size, \
+ ranges[ranges_index].lazy); \
+ } \
+ } while (0)
+# endif
+
+# if ELF_MACHINE_NO_REL || ELF_MACHINE_NO_RELA
+# define _ELF_CHECK_REL 0
+# else
+# define _ELF_CHECK_REL 1
+# endif
+
+# if ! ELF_MACHINE_NO_REL
+# include "do-rel.h"
+# define ELF_DYNAMIC_DO_REL(map, lazy) \
+ _ELF_DYNAMIC_DO_RELOC (REL, rel, map, lazy, _ELF_CHECK_REL)
+# else
+# define ELF_DYNAMIC_DO_REL(map, lazy) /* Nothing to do. */
+# endif
+
+# if ! ELF_MACHINE_NO_RELA
+# define DO_RELA
+# include "do-rel.h"
+# define ELF_DYNAMIC_DO_RELA(map, lazy) \
+ _ELF_DYNAMIC_DO_RELOC (RELA, rela, map, lazy, _ELF_CHECK_REL)
+# else
+# define ELF_DYNAMIC_DO_RELA(map, lazy) /* Nothing to do. */
+# endif
+
+/* This can't just be an inline function because GCC is too dumb
+ to inline functions containing inlines themselves. */
+# define ELF_DYNAMIC_RELOCATE(map, lazy, consider_profile) \
+ do { \
+ int edr_lazy = elf_machine_runtime_setup ((map), (lazy), \
+ (consider_profile)); \
+ ELF_DYNAMIC_DO_REL ((map), edr_lazy); \
+ ELF_DYNAMIC_DO_RELA ((map), edr_lazy); \
+ } while (0)
+
+#endif
diff --git a/libc/elf/elf.h b/libc/elf/elf.h
new file mode 100644
index 000000000..dae359778
--- /dev/null
+++ b/libc/elf/elf.h
@@ -0,0 +1,2608 @@
+/* This file defines standard ELF types, structures, and macros.
+ Copyright (C) 1995-2003,2004,2005,2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _ELF_H
+#define _ELF_H 1
+
+#include <features.h>
+
+__BEGIN_DECLS
+
+/* Standard ELF types. */
+
+#include <stdint.h>
+
+/* Type for a 16-bit quantity. */
+typedef uint16_t Elf32_Half;
+typedef uint16_t Elf64_Half;
+
+/* Types for signed and unsigned 32-bit quantities. */
+typedef uint32_t Elf32_Word;
+typedef int32_t Elf32_Sword;
+typedef uint32_t Elf64_Word;
+typedef int32_t Elf64_Sword;
+
+/* Types for signed and unsigned 64-bit quantities. */
+typedef uint64_t Elf32_Xword;
+typedef int64_t Elf32_Sxword;
+typedef uint64_t Elf64_Xword;
+typedef int64_t Elf64_Sxword;
+
+/* Type of addresses. */
+typedef uint32_t Elf32_Addr;
+typedef uint64_t Elf64_Addr;
+
+/* Type of file offsets. */
+typedef uint32_t Elf32_Off;
+typedef uint64_t Elf64_Off;
+
+/* Type for section indices, which are 16-bit quantities. */
+typedef uint16_t Elf32_Section;
+typedef uint16_t Elf64_Section;
+
+/* Type for version symbol information. */
+typedef Elf32_Half Elf32_Versym;
+typedef Elf64_Half Elf64_Versym;
+
+
+/* The ELF file header. This appears at the start of every ELF file. */
+
+#define EI_NIDENT (16)
+
+typedef struct
+{
+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
+ Elf32_Half e_type; /* Object file type */
+ Elf32_Half e_machine; /* Architecture */
+ Elf32_Word e_version; /* Object file version */
+ Elf32_Addr e_entry; /* Entry point virtual address */
+ Elf32_Off e_phoff; /* Program header table file offset */
+ Elf32_Off e_shoff; /* Section header table file offset */
+ Elf32_Word e_flags; /* Processor-specific flags */
+ Elf32_Half e_ehsize; /* ELF header size in bytes */
+ Elf32_Half e_phentsize; /* Program header table entry size */
+ Elf32_Half e_phnum; /* Program header table entry count */
+ Elf32_Half e_shentsize; /* Section header table entry size */
+ Elf32_Half e_shnum; /* Section header table entry count */
+ Elf32_Half e_shstrndx; /* Section header string table index */
+} Elf32_Ehdr;
+
+typedef struct
+{
+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
+ Elf64_Half e_type; /* Object file type */
+ Elf64_Half e_machine; /* Architecture */
+ Elf64_Word e_version; /* Object file version */
+ Elf64_Addr e_entry; /* Entry point virtual address */
+ Elf64_Off e_phoff; /* Program header table file offset */
+ Elf64_Off e_shoff; /* Section header table file offset */
+ Elf64_Word e_flags; /* Processor-specific flags */
+ Elf64_Half e_ehsize; /* ELF header size in bytes */
+ Elf64_Half e_phentsize; /* Program header table entry size */
+ Elf64_Half e_phnum; /* Program header table entry count */
+ Elf64_Half e_shentsize; /* Section header table entry size */
+ Elf64_Half e_shnum; /* Section header table entry count */
+ Elf64_Half e_shstrndx; /* Section header string table index */
+} Elf64_Ehdr;
+
+/* Fields in the e_ident array. The EI_* macros are indices into the
+ array. The macros under each EI_* macro are the values the byte
+ may have. */
+
+#define EI_MAG0 0 /* File identification byte 0 index */
+#define ELFMAG0 0x7f /* Magic number byte 0 */
+
+#define EI_MAG1 1 /* File identification byte 1 index */
+#define ELFMAG1 'E' /* Magic number byte 1 */
+
+#define EI_MAG2 2 /* File identification byte 2 index */
+#define ELFMAG2 'L' /* Magic number byte 2 */
+
+#define EI_MAG3 3 /* File identification byte 3 index */
+#define ELFMAG3 'F' /* Magic number byte 3 */
+
+/* Conglomeration of the identification bytes, for easy testing as a word. */
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+
+#define EI_CLASS 4 /* File class byte index */
+#define ELFCLASSNONE 0 /* Invalid class */
+#define ELFCLASS32 1 /* 32-bit objects */
+#define ELFCLASS64 2 /* 64-bit objects */
+#define ELFCLASSNUM 3
+
+#define EI_DATA 5 /* Data encoding byte index */
+#define ELFDATANONE 0 /* Invalid data encoding */
+#define ELFDATA2LSB 1 /* 2's complement, little endian */
+#define ELFDATA2MSB 2 /* 2's complement, big endian */
+#define ELFDATANUM 3
+
+#define EI_VERSION 6 /* File version byte index */
+ /* Value must be EV_CURRENT */
+
+#define EI_OSABI 7 /* OS ABI identification */
+#define ELFOSABI_NONE 0 /* UNIX System V ABI */
+#define ELFOSABI_SYSV 0 /* Alias. */
+#define ELFOSABI_HPUX 1 /* HP-UX */
+#define ELFOSABI_NETBSD 2 /* NetBSD. */
+#define ELFOSABI_LINUX 3 /* Linux. */
+#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */
+#define ELFOSABI_AIX 7 /* IBM AIX. */
+#define ELFOSABI_IRIX 8 /* SGI Irix. */
+#define ELFOSABI_FREEBSD 9 /* FreeBSD. */
+#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */
+#define ELFOSABI_MODESTO 11 /* Novell Modesto. */
+#define ELFOSABI_OPENBSD 12 /* OpenBSD. */
+#define ELFOSABI_ARM 97 /* ARM */
+#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
+
+#define EI_ABIVERSION 8 /* ABI version */
+
+#define EI_PAD 9 /* Byte index of padding bytes */
+
+/* Legal values for e_type (object file type). */
+
+#define ET_NONE 0 /* No file type */
+#define ET_REL 1 /* Relocatable file */
+#define ET_EXEC 2 /* Executable file */
+#define ET_DYN 3 /* Shared object file */
+#define ET_CORE 4 /* Core file */
+#define ET_NUM 5 /* Number of defined types */
+#define ET_LOOS 0xfe00 /* OS-specific range start */
+#define ET_HIOS 0xfeff /* OS-specific range end */
+#define ET_LOPROC 0xff00 /* Processor-specific range start */
+#define ET_HIPROC 0xffff /* Processor-specific range end */
+
+/* Legal values for e_machine (architecture). */
+
+#define EM_NONE 0 /* No machine */
+#define EM_M32 1 /* AT&T WE 32100 */
+#define EM_SPARC 2 /* SUN SPARC */
+#define EM_386 3 /* Intel 80386 */
+#define EM_68K 4 /* Motorola m68k family */
+#define EM_88K 5 /* Motorola m88k family */
+#define EM_860 7 /* Intel 80860 */
+#define EM_MIPS 8 /* MIPS R3000 big-endian */
+#define EM_S370 9 /* IBM System/370 */
+#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */
+
+#define EM_PARISC 15 /* HPPA */
+#define EM_VPP500 17 /* Fujitsu VPP500 */
+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
+#define EM_960 19 /* Intel 80960 */
+#define EM_PPC 20 /* PowerPC */
+#define EM_PPC64 21 /* PowerPC 64-bit */
+#define EM_S390 22 /* IBM S390 */
+
+#define EM_V800 36 /* NEC V800 series */
+#define EM_FR20 37 /* Fujitsu FR20 */
+#define EM_RH32 38 /* TRW RH-32 */
+#define EM_RCE 39 /* Motorola RCE */
+#define EM_ARM 40 /* ARM */
+#define EM_FAKE_ALPHA 41 /* Digital Alpha */
+#define EM_SH 42 /* Hitachi SH */
+#define EM_SPARCV9 43 /* SPARC v9 64-bit */
+#define EM_TRICORE 44 /* Siemens Tricore */
+#define EM_ARC 45 /* Argonaut RISC Core */
+#define EM_H8_300 46 /* Hitachi H8/300 */
+#define EM_H8_300H 47 /* Hitachi H8/300H */
+#define EM_H8S 48 /* Hitachi H8S */
+#define EM_H8_500 49 /* Hitachi H8/500 */
+#define EM_IA_64 50 /* Intel Merced */
+#define EM_MIPS_X 51 /* Stanford MIPS-X */
+#define EM_COLDFIRE 52 /* Motorola Coldfire */
+#define EM_68HC12 53 /* Motorola M68HC12 */
+#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/
+#define EM_PCP 55 /* Siemens PCP */
+#define EM_NCPU 56 /* Sony nCPU embeeded RISC */
+#define EM_NDR1 57 /* Denso NDR1 microprocessor */
+#define EM_STARCORE 58 /* Motorola Start*Core processor */
+#define EM_ME16 59 /* Toyota ME16 processor */
+#define EM_ST100 60 /* STMicroelectronic ST100 processor */
+#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/
+#define EM_X86_64 62 /* AMD x86-64 architecture */
+#define EM_PDSP 63 /* Sony DSP Processor */
+
+#define EM_FX66 66 /* Siemens FX66 microcontroller */
+#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */
+#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */
+#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */
+#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */
+#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */
+#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */
+#define EM_SVX 73 /* Silicon Graphics SVx */
+#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */
+#define EM_VAX 75 /* Digital VAX */
+#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */
+#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */
+#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */
+#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */
+#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */
+#define EM_HUANY 81 /* Harvard University machine-independent object files */
+#define EM_PRISM 82 /* SiTera Prism */
+#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */
+#define EM_FR30 84 /* Fujitsu FR30 */
+#define EM_D10V 85 /* Mitsubishi D10V */
+#define EM_D30V 86 /* Mitsubishi D30V */
+#define EM_V850 87 /* NEC v850 */
+#define EM_M32R 88 /* Mitsubishi M32R */
+#define EM_MN10300 89 /* Matsushita MN10300 */
+#define EM_MN10200 90 /* Matsushita MN10200 */
+#define EM_PJ 91 /* picoJava */
+#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */
+#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */
+#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */
+#define EM_NUM 95
+
+/* If it is necessary to assign new unofficial EM_* values, please
+ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the
+ chances of collision with official or non-GNU unofficial values. */
+
+#define EM_ALPHA 0x9026
+
+/* Legal values for e_version (version). */
+
+#define EV_NONE 0 /* Invalid ELF version */
+#define EV_CURRENT 1 /* Current version */
+#define EV_NUM 2
+
+/* Section header. */
+
+typedef struct
+{
+ Elf32_Word sh_name; /* Section name (string tbl index) */
+ Elf32_Word sh_type; /* Section type */
+ Elf32_Word sh_flags; /* Section flags */
+ Elf32_Addr sh_addr; /* Section virtual addr at execution */
+ Elf32_Off sh_offset; /* Section file offset */
+ Elf32_Word sh_size; /* Section size in bytes */
+ Elf32_Word sh_link; /* Link to another section */
+ Elf32_Word sh_info; /* Additional section information */
+ Elf32_Word sh_addralign; /* Section alignment */
+ Elf32_Word sh_entsize; /* Entry size if section holds table */
+} Elf32_Shdr;
+
+typedef struct
+{
+ Elf64_Word sh_name; /* Section name (string tbl index) */
+ Elf64_Word sh_type; /* Section type */
+ Elf64_Xword sh_flags; /* Section flags */
+ Elf64_Addr sh_addr; /* Section virtual addr at execution */
+ Elf64_Off sh_offset; /* Section file offset */
+ Elf64_Xword sh_size; /* Section size in bytes */
+ Elf64_Word sh_link; /* Link to another section */
+ Elf64_Word sh_info; /* Additional section information */
+ Elf64_Xword sh_addralign; /* Section alignment */
+ Elf64_Xword sh_entsize; /* Entry size if section holds table */
+} Elf64_Shdr;
+
+/* Special section indices. */
+
+#define SHN_UNDEF 0 /* Undefined section */
+#define SHN_LORESERVE 0xff00 /* Start of reserved indices */
+#define SHN_LOPROC 0xff00 /* Start of processor-specific */
+#define SHN_BEFORE 0xff00 /* Order section before all others
+ (Solaris). */
+#define SHN_AFTER 0xff01 /* Order section after all others
+ (Solaris). */
+#define SHN_HIPROC 0xff1f /* End of processor-specific */
+#define SHN_LOOS 0xff20 /* Start of OS-specific */
+#define SHN_HIOS 0xff3f /* End of OS-specific */
+#define SHN_ABS 0xfff1 /* Associated symbol is absolute */
+#define SHN_COMMON 0xfff2 /* Associated symbol is common */
+#define SHN_XINDEX 0xffff /* Index is in extra table. */
+#define SHN_HIRESERVE 0xffff /* End of reserved indices */
+
+/* Legal values for sh_type (section type). */
+
+#define SHT_NULL 0 /* Section header table entry unused */
+#define SHT_PROGBITS 1 /* Program data */
+#define SHT_SYMTAB 2 /* Symbol table */
+#define SHT_STRTAB 3 /* String table */
+#define SHT_RELA 4 /* Relocation entries with addends */
+#define SHT_HASH 5 /* Symbol hash table */
+#define SHT_DYNAMIC 6 /* Dynamic linking information */
+#define SHT_NOTE 7 /* Notes */
+#define SHT_NOBITS 8 /* Program space with no data (bss) */
+#define SHT_REL 9 /* Relocation entries, no addends */
+#define SHT_SHLIB 10 /* Reserved */
+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
+#define SHT_INIT_ARRAY 14 /* Array of constructors */
+#define SHT_FINI_ARRAY 15 /* Array of destructors */
+#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
+#define SHT_GROUP 17 /* Section group */
+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
+#define SHT_NUM 19 /* Number of defined types. */
+#define SHT_LOOS 0x60000000 /* Start OS-specific. */
+#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */
+#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */
+#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
+#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
+#define SHT_SUNW_move 0x6ffffffa
+#define SHT_SUNW_COMDAT 0x6ffffffb
+#define SHT_SUNW_syminfo 0x6ffffffc
+#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */
+#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */
+#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */
+#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
+#define SHT_HIOS 0x6fffffff /* End OS-specific type */
+#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
+#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
+#define SHT_LOUSER 0x80000000 /* Start of application-specific */
+#define SHT_HIUSER 0x8fffffff /* End of application-specific */
+
+/* Legal values for sh_flags (section flags). */
+
+#define SHF_WRITE (1 << 0) /* Writable */
+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
+#define SHF_EXECINSTR (1 << 2) /* Executable */
+#define SHF_MERGE (1 << 4) /* Might be merged */
+#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */
+#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */
+#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */
+#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling
+ required */
+#define SHF_GROUP (1 << 9) /* Section is member of a group. */
+#define SHF_TLS (1 << 10) /* Section hold thread-local data. */
+#define SHF_MASKOS 0x0ff00000 /* OS-specific. */
+#define SHF_MASKPROC 0xf0000000 /* Processor-specific */
+#define SHF_ORDERED (1 << 30) /* Special ordering requirement
+ (Solaris). */
+#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless
+ referenced or allocated (Solaris).*/
+
+/* Section group handling. */
+#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */
+
+/* Symbol table entry. */
+
+typedef struct
+{
+ Elf32_Word st_name; /* Symbol name (string tbl index) */
+ Elf32_Addr st_value; /* Symbol value */
+ Elf32_Word st_size; /* Symbol size */
+ unsigned char st_info; /* Symbol type and binding */
+ unsigned char st_other; /* Symbol visibility */
+ Elf32_Section st_shndx; /* Section index */
+} Elf32_Sym;
+
+typedef struct
+{
+ Elf64_Word st_name; /* Symbol name (string tbl index) */
+ unsigned char st_info; /* Symbol type and binding */
+ unsigned char st_other; /* Symbol visibility */
+ Elf64_Section st_shndx; /* Section index */
+ Elf64_Addr st_value; /* Symbol value */
+ Elf64_Xword st_size; /* Symbol size */
+} Elf64_Sym;
+
+/* The syminfo section if available contains additional information about
+ every dynamic symbol. */
+
+typedef struct
+{
+ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */
+ Elf32_Half si_flags; /* Per symbol flags */
+} Elf32_Syminfo;
+
+typedef struct
+{
+ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */
+ Elf64_Half si_flags; /* Per symbol flags */
+} Elf64_Syminfo;
+
+/* Possible values for si_boundto. */
+#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */
+#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */
+#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */
+
+/* Possible bitmasks for si_flags. */
+#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */
+#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */
+#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */
+#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy
+ loaded */
+/* Syminfo version values. */
+#define SYMINFO_NONE 0
+#define SYMINFO_CURRENT 1
+#define SYMINFO_NUM 2
+
+
+/* How to extract and insert information held in the st_info field. */
+
+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
+#define ELF32_ST_TYPE(val) ((val) & 0xf)
+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
+
+/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */
+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val)
+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val)
+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type))
+
+/* Legal values for ST_BIND subfield of st_info (symbol binding). */
+
+#define STB_LOCAL 0 /* Local symbol */
+#define STB_GLOBAL 1 /* Global symbol */
+#define STB_WEAK 2 /* Weak symbol */
+#define STB_NUM 3 /* Number of defined types. */
+#define STB_LOOS 10 /* Start of OS-specific */
+#define STB_HIOS 12 /* End of OS-specific */
+#define STB_LOPROC 13 /* Start of processor-specific */
+#define STB_HIPROC 15 /* End of processor-specific */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_NOTYPE 0 /* Symbol type is unspecified */
+#define STT_OBJECT 1 /* Symbol is a data object */
+#define STT_FUNC 2 /* Symbol is a code object */
+#define STT_SECTION 3 /* Symbol associated with a section */
+#define STT_FILE 4 /* Symbol's name is file name */
+#define STT_COMMON 5 /* Symbol is a common data object */
+#define STT_TLS 6 /* Symbol is thread-local data object*/
+#define STT_NUM 7 /* Number of defined types. */
+#define STT_LOOS 10 /* Start of OS-specific */
+#define STT_HIOS 12 /* End of OS-specific */
+#define STT_LOPROC 13 /* Start of processor-specific */
+#define STT_HIPROC 15 /* End of processor-specific */
+
+
+/* Symbol table indices are found in the hash buckets and chain table
+ of a symbol hash table section. This special index value indicates
+ the end of a chain, meaning no further symbols are found in that bucket. */
+
+#define STN_UNDEF 0 /* End of a chain. */
+
+
+/* How to extract and insert information held in the st_other field. */
+
+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
+
+/* For ELF64 the definitions are the same. */
+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
+
+/* Symbol visibility specification encoded in the st_other field. */
+#define STV_DEFAULT 0 /* Default symbol visibility rules */
+#define STV_INTERNAL 1 /* Processor specific hidden class */
+#define STV_HIDDEN 2 /* Sym unavailable in other modules */
+#define STV_PROTECTED 3 /* Not preemptible, not exported */
+
+
+/* Relocation table entry without addend (in section of type SHT_REL). */
+
+typedef struct
+{
+ Elf32_Addr r_offset; /* Address */
+ Elf32_Word r_info; /* Relocation type and symbol index */
+} Elf32_Rel;
+
+/* I have seen two different definitions of the Elf64_Rel and
+ Elf64_Rela structures, so we'll leave them out until Novell (or
+ whoever) gets their act together. */
+/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */
+
+typedef struct
+{
+ Elf64_Addr r_offset; /* Address */
+ Elf64_Xword r_info; /* Relocation type and symbol index */
+} Elf64_Rel;
+
+/* Relocation table entry with addend (in section of type SHT_RELA). */
+
+typedef struct
+{
+ Elf32_Addr r_offset; /* Address */
+ Elf32_Word r_info; /* Relocation type and symbol index */
+ Elf32_Sword r_addend; /* Addend */
+} Elf32_Rela;
+
+typedef struct
+{
+ Elf64_Addr r_offset; /* Address */
+ Elf64_Xword r_info; /* Relocation type and symbol index */
+ Elf64_Sxword r_addend; /* Addend */
+} Elf64_Rela;
+
+/* How to extract and insert information held in the r_info field. */
+
+#define ELF32_R_SYM(val) ((val) >> 8)
+#define ELF32_R_TYPE(val) ((val) & 0xff)
+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
+
+#define ELF64_R_SYM(i) ((i) >> 32)
+#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type))
+
+/* Program segment header. */
+
+typedef struct
+{
+ Elf32_Word p_type; /* Segment type */
+ Elf32_Off p_offset; /* Segment file offset */
+ Elf32_Addr p_vaddr; /* Segment virtual address */
+ Elf32_Addr p_paddr; /* Segment physical address */
+ Elf32_Word p_filesz; /* Segment size in file */
+ Elf32_Word p_memsz; /* Segment size in memory */
+ Elf32_Word p_flags; /* Segment flags */
+ Elf32_Word p_align; /* Segment alignment */
+} Elf32_Phdr;
+
+typedef struct
+{
+ Elf64_Word p_type; /* Segment type */
+ Elf64_Word p_flags; /* Segment flags */
+ Elf64_Off p_offset; /* Segment file offset */
+ Elf64_Addr p_vaddr; /* Segment virtual address */
+ Elf64_Addr p_paddr; /* Segment physical address */
+ Elf64_Xword p_filesz; /* Segment size in file */
+ Elf64_Xword p_memsz; /* Segment size in memory */
+ Elf64_Xword p_align; /* Segment alignment */
+} Elf64_Phdr;
+
+/* Legal values for p_type (segment type). */
+
+#define PT_NULL 0 /* Program header table entry unused */
+#define PT_LOAD 1 /* Loadable program segment */
+#define PT_DYNAMIC 2 /* Dynamic linking information */
+#define PT_INTERP 3 /* Program interpreter */
+#define PT_NOTE 4 /* Auxiliary information */
+#define PT_SHLIB 5 /* Reserved */
+#define PT_PHDR 6 /* Entry for header table itself */
+#define PT_TLS 7 /* Thread-local storage segment */
+#define PT_NUM 8 /* Number of defined types */
+#define PT_LOOS 0x60000000 /* Start of OS-specific */
+#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */
+#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */
+#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */
+#define PT_LOSUNW 0x6ffffffa
+#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */
+#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */
+#define PT_HISUNW 0x6fffffff
+#define PT_HIOS 0x6fffffff /* End of OS-specific */
+#define PT_LOPROC 0x70000000 /* Start of processor-specific */
+#define PT_HIPROC 0x7fffffff /* End of processor-specific */
+
+/* Legal values for p_flags (segment flags). */
+
+#define PF_X (1 << 0) /* Segment is executable */
+#define PF_W (1 << 1) /* Segment is writable */
+#define PF_R (1 << 2) /* Segment is readable */
+#define PF_MASKOS 0x0ff00000 /* OS-specific */
+#define PF_MASKPROC 0xf0000000 /* Processor-specific */
+
+/* Legal values for note segment descriptor types for core files. */
+
+#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */
+#define NT_FPREGSET 2 /* Contains copy of fpregset struct */
+#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */
+#define NT_PRXREG 4 /* Contains copy of prxregset struct */
+#define NT_TASKSTRUCT 4 /* Contains copy of task structure */
+#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */
+#define NT_AUXV 6 /* Contains copy of auxv array */
+#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */
+#define NT_ASRS 8 /* Contains copy of asrset struct */
+#define NT_PSTATUS 10 /* Contains copy of pstatus struct */
+#define NT_PSINFO 13 /* Contains copy of psinfo struct */
+#define NT_PRCRED 14 /* Contains copy of prcred struct */
+#define NT_UTSNAME 15 /* Contains copy of utsname struct */
+#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */
+#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */
+#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/
+
+/* Legal values for the note segment descriptor types for object files. */
+
+#define NT_VERSION 1 /* Contains a version string. */
+
+
+/* Dynamic section entry. */
+
+typedef struct
+{
+ Elf32_Sword d_tag; /* Dynamic entry type */
+ union
+ {
+ Elf32_Word d_val; /* Integer value */
+ Elf32_Addr d_ptr; /* Address value */
+ } d_un;
+} Elf32_Dyn;
+
+typedef struct
+{
+ Elf64_Sxword d_tag; /* Dynamic entry type */
+ union
+ {
+ Elf64_Xword d_val; /* Integer value */
+ Elf64_Addr d_ptr; /* Address value */
+ } d_un;
+} Elf64_Dyn;
+
+/* Legal values for d_tag (dynamic entry type). */
+
+#define DT_NULL 0 /* Marks end of dynamic section */
+#define DT_NEEDED 1 /* Name of needed library */
+#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */
+#define DT_PLTGOT 3 /* Processor defined value */
+#define DT_HASH 4 /* Address of symbol hash table */
+#define DT_STRTAB 5 /* Address of string table */
+#define DT_SYMTAB 6 /* Address of symbol table */
+#define DT_RELA 7 /* Address of Rela relocs */
+#define DT_RELASZ 8 /* Total size of Rela relocs */
+#define DT_RELAENT 9 /* Size of one Rela reloc */
+#define DT_STRSZ 10 /* Size of string table */
+#define DT_SYMENT 11 /* Size of one symbol table entry */
+#define DT_INIT 12 /* Address of init function */
+#define DT_FINI 13 /* Address of termination function */
+#define DT_SONAME 14 /* Name of shared object */
+#define DT_RPATH 15 /* Library search path (deprecated) */
+#define DT_SYMBOLIC 16 /* Start symbol search here */
+#define DT_REL 17 /* Address of Rel relocs */
+#define DT_RELSZ 18 /* Total size of Rel relocs */
+#define DT_RELENT 19 /* Size of one Rel reloc */
+#define DT_PLTREL 20 /* Type of reloc in PLT */
+#define DT_DEBUG 21 /* For debugging; unspecified */
+#define DT_TEXTREL 22 /* Reloc might modify .text */
+#define DT_JMPREL 23 /* Address of PLT relocs */
+#define DT_BIND_NOW 24 /* Process relocations of object */
+#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */
+#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */
+#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */
+#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */
+#define DT_RUNPATH 29 /* Library search path */
+#define DT_FLAGS 30 /* Flags for the object being loaded */
+#define DT_ENCODING 32 /* Start of encoded range */
+#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/
+#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */
+#define DT_NUM 34 /* Number used */
+#define DT_LOOS 0x6000000d /* Start of OS-specific */
+#define DT_HIOS 0x6ffff000 /* End of OS-specific */
+#define DT_LOPROC 0x70000000 /* Start of processor-specific */
+#define DT_HIPROC 0x7fffffff /* End of processor-specific */
+#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */
+
+/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
+ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's
+ approach. */
+#define DT_VALRNGLO 0x6ffffd00
+#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */
+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */
+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */
+#define DT_CHECKSUM 0x6ffffdf8
+#define DT_PLTPADSZ 0x6ffffdf9
+#define DT_MOVEENT 0x6ffffdfa
+#define DT_MOVESZ 0x6ffffdfb
+#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */
+#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting
+ the following DT_* entry. */
+#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */
+#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */
+#define DT_VALRNGHI 0x6ffffdff
+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */
+#define DT_VALNUM 12
+
+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
+ Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
+
+ If any adjustment is made to the ELF object after it has been
+ built these entries will need to be adjusted. */
+#define DT_ADDRRNGLO 0x6ffffe00
+#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */
+#define DT_TLSDESC_PLT 0x6ffffef6
+#define DT_TLSDESC_GOT 0x6ffffef7
+#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */
+#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */
+#define DT_CONFIG 0x6ffffefa /* Configuration information. */
+#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */
+#define DT_AUDIT 0x6ffffefc /* Object auditing. */
+#define DT_PLTPAD 0x6ffffefd /* PLT padding. */
+#define DT_MOVETAB 0x6ffffefe /* Move table. */
+#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */
+#define DT_ADDRRNGHI 0x6ffffeff
+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */
+#define DT_ADDRNUM 11
+
+/* The versioning entry types. The next are defined as part of the
+ GNU extension. */
+#define DT_VERSYM 0x6ffffff0
+
+#define DT_RELACOUNT 0x6ffffff9
+#define DT_RELCOUNT 0x6ffffffa
+
+/* These were chosen by Sun. */
+#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */
+#define DT_VERDEF 0x6ffffffc /* Address of version definition
+ table */
+#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */
+#define DT_VERNEED 0x6ffffffe /* Address of table with needed
+ versions */
+#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */
+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */
+#define DT_VERSIONTAGNUM 16
+
+/* Sun added these machine-independent extensions in the "processor-specific"
+ range. Be compatible. */
+#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */
+#define DT_FILTER 0x7fffffff /* Shared object to get values from */
+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
+#define DT_EXTRANUM 3
+
+/* Values of `d_un.d_val' in the DT_FLAGS entry. */
+#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */
+#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */
+#define DF_TEXTREL 0x00000004 /* Object contains text relocations */
+#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */
+#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */
+
+/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1
+ entry in the dynamic section. */
+#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */
+#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */
+#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */
+#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/
+#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/
+#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/
+#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */
+#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */
+#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */
+#define DF_1_TRANS 0x00000200
+#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */
+#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */
+#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */
+#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/
+#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */
+#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */
+#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */
+
+/* Flags for the feature selection in DT_FEATURE_1. */
+#define DTF_1_PARINIT 0x00000001
+#define DTF_1_CONFEXP 0x00000002
+
+/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */
+#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */
+#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not
+ generally available. */
+
+/* Version definition sections. */
+
+typedef struct
+{
+ Elf32_Half vd_version; /* Version revision */
+ Elf32_Half vd_flags; /* Version information */
+ Elf32_Half vd_ndx; /* Version Index */
+ Elf32_Half vd_cnt; /* Number of associated aux entries */
+ Elf32_Word vd_hash; /* Version name hash value */
+ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */
+ Elf32_Word vd_next; /* Offset in bytes to next verdef
+ entry */
+} Elf32_Verdef;
+
+typedef struct
+{
+ Elf64_Half vd_version; /* Version revision */
+ Elf64_Half vd_flags; /* Version information */
+ Elf64_Half vd_ndx; /* Version Index */
+ Elf64_Half vd_cnt; /* Number of associated aux entries */
+ Elf64_Word vd_hash; /* Version name hash value */
+ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */
+ Elf64_Word vd_next; /* Offset in bytes to next verdef
+ entry */
+} Elf64_Verdef;
+
+
+/* Legal values for vd_version (version revision). */
+#define VER_DEF_NONE 0 /* No version */
+#define VER_DEF_CURRENT 1 /* Current version */
+#define VER_DEF_NUM 2 /* Given version number */
+
+/* Legal values for vd_flags (version information flags). */
+#define VER_FLG_BASE 0x1 /* Version definition of file itself */
+#define VER_FLG_WEAK 0x2 /* Weak version identifier */
+
+/* Versym symbol index values. */
+#define VER_NDX_LOCAL 0 /* Symbol is local. */
+#define VER_NDX_GLOBAL 1 /* Symbol is global. */
+#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */
+#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */
+
+/* Auxialiary version information. */
+
+typedef struct
+{
+ Elf32_Word vda_name; /* Version or dependency names */
+ Elf32_Word vda_next; /* Offset in bytes to next verdaux
+ entry */
+} Elf32_Verdaux;
+
+typedef struct
+{
+ Elf64_Word vda_name; /* Version or dependency names */
+ Elf64_Word vda_next; /* Offset in bytes to next verdaux
+ entry */
+} Elf64_Verdaux;
+
+
+/* Version dependency section. */
+
+typedef struct
+{
+ Elf32_Half vn_version; /* Version of structure */
+ Elf32_Half vn_cnt; /* Number of associated aux entries */
+ Elf32_Word vn_file; /* Offset of filename for this
+ dependency */
+ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */
+ Elf32_Word vn_next; /* Offset in bytes to next verneed
+ entry */
+} Elf32_Verneed;
+
+typedef struct
+{
+ Elf64_Half vn_version; /* Version of structure */
+ Elf64_Half vn_cnt; /* Number of associated aux entries */
+ Elf64_Word vn_file; /* Offset of filename for this
+ dependency */
+ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */
+ Elf64_Word vn_next; /* Offset in bytes to next verneed
+ entry */
+} Elf64_Verneed;
+
+
+/* Legal values for vn_version (version revision). */
+#define VER_NEED_NONE 0 /* No version */
+#define VER_NEED_CURRENT 1 /* Current version */
+#define VER_NEED_NUM 2 /* Given version number */
+
+/* Auxiliary needed version information. */
+
+typedef struct
+{
+ Elf32_Word vna_hash; /* Hash value of dependency name */
+ Elf32_Half vna_flags; /* Dependency specific information */
+ Elf32_Half vna_other; /* Unused */
+ Elf32_Word vna_name; /* Dependency name string offset */
+ Elf32_Word vna_next; /* Offset in bytes to next vernaux
+ entry */
+} Elf32_Vernaux;
+
+typedef struct
+{
+ Elf64_Word vna_hash; /* Hash value of dependency name */
+ Elf64_Half vna_flags; /* Dependency specific information */
+ Elf64_Half vna_other; /* Unused */
+ Elf64_Word vna_name; /* Dependency name string offset */
+ Elf64_Word vna_next; /* Offset in bytes to next vernaux
+ entry */
+} Elf64_Vernaux;
+
+
+/* Legal values for vna_flags. */
+#define VER_FLG_WEAK 0x2 /* Weak version identifier */
+
+
+/* Auxiliary vector. */
+
+/* This vector is normally only used by the program interpreter. The
+ usual definition in an ABI supplement uses the name auxv_t. The
+ vector is not usually defined in a standard <elf.h> file, but it
+ can't hurt. We rename it to avoid conflicts. The sizes of these
+ types are an arrangement between the exec server and the program
+ interpreter, so we don't fully specify them here. */
+
+typedef struct
+{
+ uint32_t a_type; /* Entry type */
+ union
+ {
+ uint32_t a_val; /* Integer value */
+ /* We use to have pointer elements added here. We cannot do that,
+ though, since it does not work when using 32-bit definitions
+ on 64-bit platforms and vice versa. */
+ } a_un;
+} Elf32_auxv_t;
+
+typedef struct
+{
+ uint64_t a_type; /* Entry type */
+ union
+ {
+ uint64_t a_val; /* Integer value */
+ /* We use to have pointer elements added here. We cannot do that,
+ though, since it does not work when using 32-bit definitions
+ on 64-bit platforms and vice versa. */
+ } a_un;
+} Elf64_auxv_t;
+
+/* Legal values for a_type (entry type). */
+
+#define AT_NULL 0 /* End of vector */
+#define AT_IGNORE 1 /* Entry should be ignored */
+#define AT_EXECFD 2 /* File descriptor of program */
+#define AT_PHDR 3 /* Program headers for program */
+#define AT_PHENT 4 /* Size of program header entry */
+#define AT_PHNUM 5 /* Number of program headers */
+#define AT_PAGESZ 6 /* System page size */
+#define AT_BASE 7 /* Base address of interpreter */
+#define AT_FLAGS 8 /* Flags */
+#define AT_ENTRY 9 /* Entry point of program */
+#define AT_NOTELF 10 /* Program is not ELF */
+#define AT_UID 11 /* Real uid */
+#define AT_EUID 12 /* Effective uid */
+#define AT_GID 13 /* Real gid */
+#define AT_EGID 14 /* Effective gid */
+#define AT_CLKTCK 17 /* Frequency of times() */
+
+/* Some more special a_type values describing the hardware. */
+#define AT_PLATFORM 15 /* String identifying platform. */
+#define AT_HWCAP 16 /* Machine dependent hints about
+ processor capabilities. */
+
+/* This entry gives some information about the FPU initialization
+ performed by the kernel. */
+#define AT_FPUCW 18 /* Used FPU control word. */
+
+/* Cache block sizes. */
+#define AT_DCACHEBSIZE 19 /* Data cache block size. */
+#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */
+#define AT_UCACHEBSIZE 21 /* Unified cache block size. */
+
+/* A special ignored value for PPC, used by the kernel to control the
+ interpretation of the AUXV. Must be > 16. */
+#define AT_IGNOREPPC 22 /* Entry should be ignored. */
+
+#define AT_SECURE 23 /* Boolean, was exec setuid-like? */
+
+/* Pointer to the global system page used for system calls and other
+ nice things. */
+#define AT_SYSINFO 32
+#define AT_SYSINFO_EHDR 33
+
+/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains
+ log2 of line size; mask those to get cache size. */
+#define AT_L1I_CACHESHAPE 34
+#define AT_L1D_CACHESHAPE 35
+#define AT_L2_CACHESHAPE 36
+#define AT_L3_CACHESHAPE 37
+
+/* Note section contents. Each entry in the note section begins with
+ a header of a fixed form. */
+
+typedef struct
+{
+ Elf32_Word n_namesz; /* Length of the note's name. */
+ Elf32_Word n_descsz; /* Length of the note's descriptor. */
+ Elf32_Word n_type; /* Type of the note. */
+} Elf32_Nhdr;
+
+typedef struct
+{
+ Elf64_Word n_namesz; /* Length of the note's name. */
+ Elf64_Word n_descsz; /* Length of the note's descriptor. */
+ Elf64_Word n_type; /* Type of the note. */
+} Elf64_Nhdr;
+
+/* Known names of notes. */
+
+/* Solaris entries in the note section have this name. */
+#define ELF_NOTE_SOLARIS "SUNW Solaris"
+
+/* Note entries for GNU systems have this name. */
+#define ELF_NOTE_GNU "GNU"
+
+
+/* Defined types of notes for Solaris. */
+
+/* Value of descriptor (one word) is desired pagesize for the binary. */
+#define ELF_NOTE_PAGESIZE_HINT 1
+
+
+/* Defined note types for GNU systems. */
+
+/* ABI information. The descriptor consists of words:
+ word 0: OS descriptor
+ word 1: major version of the ABI
+ word 2: minor version of the ABI
+ word 3: subminor version of the ABI
+*/
+#define ELF_NOTE_ABI 1
+
+/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI
+ note section entry. */
+#define ELF_NOTE_OS_LINUX 0
+#define ELF_NOTE_OS_GNU 1
+#define ELF_NOTE_OS_SOLARIS2 2
+#define ELF_NOTE_OS_FREEBSD 3
+
+
+/* Move records. */
+typedef struct
+{
+ Elf32_Xword m_value; /* Symbol value. */
+ Elf32_Word m_info; /* Size and index. */
+ Elf32_Word m_poffset; /* Symbol offset. */
+ Elf32_Half m_repeat; /* Repeat count. */
+ Elf32_Half m_stride; /* Stride info. */
+} Elf32_Move;
+
+typedef struct
+{
+ Elf64_Xword m_value; /* Symbol value. */
+ Elf64_Xword m_info; /* Size and index. */
+ Elf64_Xword m_poffset; /* Symbol offset. */
+ Elf64_Half m_repeat; /* Repeat count. */
+ Elf64_Half m_stride; /* Stride info. */
+} Elf64_Move;
+
+/* Macro to construct move records. */
+#define ELF32_M_SYM(info) ((info) >> 8)
+#define ELF32_M_SIZE(info) ((unsigned char) (info))
+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size))
+
+#define ELF64_M_SYM(info) ELF32_M_SYM (info)
+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info)
+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size)
+
+
+/* Motorola 68k specific definitions. */
+
+/* Values for Elf32_Ehdr.e_flags. */
+#define EF_CPU32 0x00810000
+
+/* m68k relocs. */
+
+#define R_68K_NONE 0 /* No reloc */
+#define R_68K_32 1 /* Direct 32 bit */
+#define R_68K_16 2 /* Direct 16 bit */
+#define R_68K_8 3 /* Direct 8 bit */
+#define R_68K_PC32 4 /* PC relative 32 bit */
+#define R_68K_PC16 5 /* PC relative 16 bit */
+#define R_68K_PC8 6 /* PC relative 8 bit */
+#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */
+#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */
+#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */
+#define R_68K_GOT32O 10 /* 32 bit GOT offset */
+#define R_68K_GOT16O 11 /* 16 bit GOT offset */
+#define R_68K_GOT8O 12 /* 8 bit GOT offset */
+#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */
+#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */
+#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */
+#define R_68K_PLT32O 16 /* 32 bit PLT offset */
+#define R_68K_PLT16O 17 /* 16 bit PLT offset */
+#define R_68K_PLT8O 18 /* 8 bit PLT offset */
+#define R_68K_COPY 19 /* Copy symbol at runtime */
+#define R_68K_GLOB_DAT 20 /* Create GOT entry */
+#define R_68K_JMP_SLOT 21 /* Create PLT entry */
+#define R_68K_RELATIVE 22 /* Adjust by program base */
+/* Keep this the last entry. */
+#define R_68K_NUM 23
+
+/* Intel 80386 specific definitions. */
+
+/* i386 relocs. */
+
+#define R_386_NONE 0 /* No reloc */
+#define R_386_32 1 /* Direct 32 bit */
+#define R_386_PC32 2 /* PC relative 32 bit */
+#define R_386_GOT32 3 /* 32 bit GOT entry */
+#define R_386_PLT32 4 /* 32 bit PLT address */
+#define R_386_COPY 5 /* Copy symbol at runtime */
+#define R_386_GLOB_DAT 6 /* Create GOT entry */
+#define R_386_JMP_SLOT 7 /* Create PLT entry */
+#define R_386_RELATIVE 8 /* Adjust by program base */
+#define R_386_GOTOFF 9 /* 32 bit offset to GOT */
+#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */
+#define R_386_32PLT 11
+#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */
+#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS
+ block offset */
+#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block
+ offset */
+#define R_386_TLS_LE 17 /* Offset relative to static TLS
+ block */
+#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of
+ general dynamic thread local data */
+#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of
+ local dynamic thread local data
+ in LE code */
+#define R_386_16 20
+#define R_386_PC16 21
+#define R_386_8 22
+#define R_386_PC8 23
+#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic
+ thread local data */
+#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */
+#define R_386_TLS_GD_CALL 26 /* Relocation for call to
+ __tls_get_addr() */
+#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */
+#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic
+ thread local data in LE code */
+#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */
+#define R_386_TLS_LDM_CALL 30 /* Relocation for call to
+ __tls_get_addr() in LDM code */
+#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */
+#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */
+#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS
+ block offset */
+#define R_386_TLS_LE_32 34 /* Negated offset relative to static
+ TLS block */
+#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */
+#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */
+#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */
+/* Keep this the last entry. */
+#define R_386_NUM 38
+
+/* SUN SPARC specific definitions. */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */
+
+/* Values for Elf64_Ehdr.e_flags. */
+
+#define EF_SPARCV9_MM 3
+#define EF_SPARCV9_TSO 0
+#define EF_SPARCV9_PSO 1
+#define EF_SPARCV9_RMO 2
+#define EF_SPARC_LEDATA 0x800000 /* little endian data */
+#define EF_SPARC_EXT_MASK 0xFFFF00
+#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */
+#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */
+#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */
+#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */
+
+/* SPARC relocs. */
+
+#define R_SPARC_NONE 0 /* No reloc */
+#define R_SPARC_8 1 /* Direct 8 bit */
+#define R_SPARC_16 2 /* Direct 16 bit */
+#define R_SPARC_32 3 /* Direct 32 bit */
+#define R_SPARC_DISP8 4 /* PC relative 8 bit */
+#define R_SPARC_DISP16 5 /* PC relative 16 bit */
+#define R_SPARC_DISP32 6 /* PC relative 32 bit */
+#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */
+#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */
+#define R_SPARC_HI22 9 /* High 22 bit */
+#define R_SPARC_22 10 /* Direct 22 bit */
+#define R_SPARC_13 11 /* Direct 13 bit */
+#define R_SPARC_LO10 12 /* Truncated 10 bit */
+#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */
+#define R_SPARC_GOT13 14 /* 13 bit GOT entry */
+#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */
+#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */
+#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */
+#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */
+#define R_SPARC_COPY 19 /* Copy symbol at runtime */
+#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */
+#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */
+#define R_SPARC_RELATIVE 22 /* Adjust by program base */
+#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */
+
+/* Additional Sparc64 relocs. */
+
+#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */
+#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */
+#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */
+#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */
+#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */
+#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */
+#define R_SPARC_10 30 /* Direct 10 bit */
+#define R_SPARC_11 31 /* Direct 11 bit */
+#define R_SPARC_64 32 /* Direct 64 bit */
+#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */
+#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */
+#define R_SPARC_HM10 35 /* High middle 10 bits of ... */
+#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */
+#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */
+#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */
+#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */
+#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */
+#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */
+#define R_SPARC_7 43 /* Direct 7 bit */
+#define R_SPARC_5 44 /* Direct 5 bit */
+#define R_SPARC_6 45 /* Direct 6 bit */
+#define R_SPARC_DISP64 46 /* PC relative 64 bit */
+#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */
+#define R_SPARC_HIX22 48 /* High 22 bit complemented */
+#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */
+#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */
+#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */
+#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */
+#define R_SPARC_REGISTER 53 /* Global register usage */
+#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */
+#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */
+#define R_SPARC_TLS_GD_HI22 56
+#define R_SPARC_TLS_GD_LO10 57
+#define R_SPARC_TLS_GD_ADD 58
+#define R_SPARC_TLS_GD_CALL 59
+#define R_SPARC_TLS_LDM_HI22 60
+#define R_SPARC_TLS_LDM_LO10 61
+#define R_SPARC_TLS_LDM_ADD 62
+#define R_SPARC_TLS_LDM_CALL 63
+#define R_SPARC_TLS_LDO_HIX22 64
+#define R_SPARC_TLS_LDO_LOX10 65
+#define R_SPARC_TLS_LDO_ADD 66
+#define R_SPARC_TLS_IE_HI22 67
+#define R_SPARC_TLS_IE_LO10 68
+#define R_SPARC_TLS_IE_LD 69
+#define R_SPARC_TLS_IE_LDX 70
+#define R_SPARC_TLS_IE_ADD 71
+#define R_SPARC_TLS_LE_HIX22 72
+#define R_SPARC_TLS_LE_LOX10 73
+#define R_SPARC_TLS_DTPMOD32 74
+#define R_SPARC_TLS_DTPMOD64 75
+#define R_SPARC_TLS_DTPOFF32 76
+#define R_SPARC_TLS_DTPOFF64 77
+#define R_SPARC_TLS_TPOFF32 78
+#define R_SPARC_TLS_TPOFF64 79
+/* Keep this the last entry. */
+#define R_SPARC_NUM 80
+
+/* For Sparc64, legal values for d_tag of Elf64_Dyn. */
+
+#define DT_SPARC_REGISTER 0x70000001
+#define DT_SPARC_NUM 2
+
+/* Bits present in AT_HWCAP on SPARC. */
+
+#define HWCAP_SPARC_FLUSH 1 /* The CPU supports flush insn. */
+#define HWCAP_SPARC_STBAR 2
+#define HWCAP_SPARC_SWAP 4
+#define HWCAP_SPARC_MULDIV 8
+#define HWCAP_SPARC_V9 16 /* The CPU is v9, so v8plus is ok. */
+#define HWCAP_SPARC_ULTRA3 32
+#define HWCAP_SPARC_BLKINIT 64 /* Sun4v with block-init/load-twin. */
+
+/* MIPS R3000 specific definitions. */
+
+/* Legal values for e_flags field of Elf32_Ehdr. */
+
+#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */
+#define EF_MIPS_PIC 2 /* Contains PIC code */
+#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */
+#define EF_MIPS_XGOT 8
+#define EF_MIPS_64BIT_WHIRL 16
+#define EF_MIPS_ABI2 32
+#define EF_MIPS_ABI_ON32 64
+#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */
+
+/* Legal values for MIPS architecture level. */
+
+#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
+#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
+#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
+#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
+#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
+#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */
+#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */
+
+/* The following are non-official names and should not be used. */
+
+#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
+#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
+#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
+#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
+#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
+#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */
+#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */
+
+/* Special section indices. */
+
+#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */
+#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */
+#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */
+#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */
+#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */
+
+/* Legal values for sh_type field of Elf32_Shdr. */
+
+#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */
+#define SHT_MIPS_MSYM 0x70000001
+#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */
+#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */
+#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */
+#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/
+#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */
+#define SHT_MIPS_PACKAGE 0x70000007
+#define SHT_MIPS_PACKSYM 0x70000008
+#define SHT_MIPS_RELD 0x70000009
+#define SHT_MIPS_IFACE 0x7000000b
+#define SHT_MIPS_CONTENT 0x7000000c
+#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */
+#define SHT_MIPS_SHDR 0x70000010
+#define SHT_MIPS_FDESC 0x70000011
+#define SHT_MIPS_EXTSYM 0x70000012
+#define SHT_MIPS_DENSE 0x70000013
+#define SHT_MIPS_PDESC 0x70000014
+#define SHT_MIPS_LOCSYM 0x70000015
+#define SHT_MIPS_AUXSYM 0x70000016
+#define SHT_MIPS_OPTSYM 0x70000017
+#define SHT_MIPS_LOCSTR 0x70000018
+#define SHT_MIPS_LINE 0x70000019
+#define SHT_MIPS_RFDESC 0x7000001a
+#define SHT_MIPS_DELTASYM 0x7000001b
+#define SHT_MIPS_DELTAINST 0x7000001c
+#define SHT_MIPS_DELTACLASS 0x7000001d
+#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */
+#define SHT_MIPS_DELTADECL 0x7000001f
+#define SHT_MIPS_SYMBOL_LIB 0x70000020
+#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */
+#define SHT_MIPS_TRANSLATE 0x70000022
+#define SHT_MIPS_PIXIE 0x70000023
+#define SHT_MIPS_XLATE 0x70000024
+#define SHT_MIPS_XLATE_DEBUG 0x70000025
+#define SHT_MIPS_WHIRL 0x70000026
+#define SHT_MIPS_EH_REGION 0x70000027
+#define SHT_MIPS_XLATE_OLD 0x70000028
+#define SHT_MIPS_PDR_EXCEPTION 0x70000029
+
+/* Legal values for sh_flags field of Elf32_Shdr. */
+
+#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */
+#define SHF_MIPS_MERGE 0x20000000
+#define SHF_MIPS_ADDR 0x40000000
+#define SHF_MIPS_STRINGS 0x80000000
+#define SHF_MIPS_NOSTRIP 0x08000000
+#define SHF_MIPS_LOCAL 0x04000000
+#define SHF_MIPS_NAMES 0x02000000
+#define SHF_MIPS_NODUPE 0x01000000
+
+
+/* Symbol tables. */
+
+/* MIPS specific values for `st_other'. */
+#define STO_MIPS_DEFAULT 0x0
+#define STO_MIPS_INTERNAL 0x1
+#define STO_MIPS_HIDDEN 0x2
+#define STO_MIPS_PROTECTED 0x3
+#define STO_MIPS_SC_ALIGN_UNUSED 0xff
+
+/* MIPS specific values for `st_info'. */
+#define STB_MIPS_SPLIT_COMMON 13
+
+/* Entries found in sections of type SHT_MIPS_GPTAB. */
+
+typedef union
+{
+ struct
+ {
+ Elf32_Word gt_current_g_value; /* -G value used for compilation */
+ Elf32_Word gt_unused; /* Not used */
+ } gt_header; /* First entry in section */
+ struct
+ {
+ Elf32_Word gt_g_value; /* If this value were used for -G */
+ Elf32_Word gt_bytes; /* This many bytes would be used */
+ } gt_entry; /* Subsequent entries in section */
+} Elf32_gptab;
+
+/* Entry found in sections of type SHT_MIPS_REGINFO. */
+
+typedef struct
+{
+ Elf32_Word ri_gprmask; /* General registers used */
+ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */
+ Elf32_Sword ri_gp_value; /* $gp register value */
+} Elf32_RegInfo;
+
+/* Entries found in sections of type SHT_MIPS_OPTIONS. */
+
+typedef struct
+{
+ unsigned char kind; /* Determines interpretation of the
+ variable part of descriptor. */
+ unsigned char size; /* Size of descriptor, including header. */
+ Elf32_Section section; /* Section header index of section affected,
+ 0 for global options. */
+ Elf32_Word info; /* Kind-specific information. */
+} Elf_Options;
+
+/* Values for `kind' field in Elf_Options. */
+
+#define ODK_NULL 0 /* Undefined. */
+#define ODK_REGINFO 1 /* Register usage information. */
+#define ODK_EXCEPTIONS 2 /* Exception processing options. */
+#define ODK_PAD 3 /* Section padding options. */
+#define ODK_HWPATCH 4 /* Hardware workarounds performed */
+#define ODK_FILL 5 /* record the fill value used by the linker. */
+#define ODK_TAGS 6 /* reserve space for desktop tools to write. */
+#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */
+#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */
+
+/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */
+
+#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */
+#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */
+#define OEX_PAGE0 0x10000 /* page zero must be mapped. */
+#define OEX_SMM 0x20000 /* Force sequential memory mode? */
+#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */
+#define OEX_PRECISEFP OEX_FPDBUG
+#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */
+
+#define OEX_FPU_INVAL 0x10
+#define OEX_FPU_DIV0 0x08
+#define OEX_FPU_OFLO 0x04
+#define OEX_FPU_UFLO 0x02
+#define OEX_FPU_INEX 0x01
+
+/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */
+
+#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */
+#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */
+#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */
+#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */
+
+#define OPAD_PREFIX 0x1
+#define OPAD_POSTFIX 0x2
+#define OPAD_SYMBOL 0x4
+
+/* Entry found in `.options' section. */
+
+typedef struct
+{
+ Elf32_Word hwp_flags1; /* Extra flags. */
+ Elf32_Word hwp_flags2; /* Extra flags. */
+} Elf_Options_Hw;
+
+/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */
+
+#define OHWA0_R4KEOP_CHECKED 0x00000001
+#define OHWA1_R4KEOP_CLEAN 0x00000002
+
+/* MIPS relocs. */
+
+#define R_MIPS_NONE 0 /* No reloc */
+#define R_MIPS_16 1 /* Direct 16 bit */
+#define R_MIPS_32 2 /* Direct 32 bit */
+#define R_MIPS_REL32 3 /* PC relative 32 bit */
+#define R_MIPS_26 4 /* Direct 26 bit shifted */
+#define R_MIPS_HI16 5 /* High 16 bit */
+#define R_MIPS_LO16 6 /* Low 16 bit */
+#define R_MIPS_GPREL16 7 /* GP relative 16 bit */
+#define R_MIPS_LITERAL 8 /* 16 bit literal entry */
+#define R_MIPS_GOT16 9 /* 16 bit GOT entry */
+#define R_MIPS_PC16 10 /* PC relative 16 bit */
+#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */
+#define R_MIPS_GPREL32 12 /* GP relative 32 bit */
+
+#define R_MIPS_SHIFT5 16
+#define R_MIPS_SHIFT6 17
+#define R_MIPS_64 18
+#define R_MIPS_GOT_DISP 19
+#define R_MIPS_GOT_PAGE 20
+#define R_MIPS_GOT_OFST 21
+#define R_MIPS_GOT_HI16 22
+#define R_MIPS_GOT_LO16 23
+#define R_MIPS_SUB 24
+#define R_MIPS_INSERT_A 25
+#define R_MIPS_INSERT_B 26
+#define R_MIPS_DELETE 27
+#define R_MIPS_HIGHER 28
+#define R_MIPS_HIGHEST 29
+#define R_MIPS_CALL_HI16 30
+#define R_MIPS_CALL_LO16 31
+#define R_MIPS_SCN_DISP 32
+#define R_MIPS_REL16 33
+#define R_MIPS_ADD_IMMEDIATE 34
+#define R_MIPS_PJUMP 35
+#define R_MIPS_RELGOT 36
+#define R_MIPS_JALR 37
+#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */
+#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */
+#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */
+#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */
+#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */
+#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */
+#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */
+#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */
+#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */
+#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */
+#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */
+#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */
+#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */
+/* Keep this the last entry. */
+#define R_MIPS_NUM 51
+
+/* Legal values for p_type field of Elf32_Phdr. */
+
+#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */
+#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */
+#define PT_MIPS_OPTIONS 0x70000002
+
+/* Special program header types. */
+
+#define PF_MIPS_LOCAL 0x10000000
+
+/* Legal values for d_tag field of Elf32_Dyn. */
+
+#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */
+#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */
+#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */
+#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */
+#define DT_MIPS_FLAGS 0x70000005 /* Flags */
+#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */
+#define DT_MIPS_MSYM 0x70000007
+#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */
+#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */
+#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */
+#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */
+#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */
+#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */
+#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */
+#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */
+#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */
+#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */
+#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */
+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in
+ DT_MIPS_DELTA_CLASS. */
+#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */
+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in
+ DT_MIPS_DELTA_INSTANCE. */
+#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */
+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in
+ DT_MIPS_DELTA_RELOC. */
+#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta
+ relocations refer to. */
+#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in
+ DT_MIPS_DELTA_SYM. */
+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the
+ class declaration. */
+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in
+ DT_MIPS_DELTA_CLASSSYM. */
+#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */
+#define DT_MIPS_PIXIE_INIT 0x70000023
+#define DT_MIPS_SYMBOL_LIB 0x70000024
+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025
+#define DT_MIPS_LOCAL_GOTIDX 0x70000026
+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027
+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028
+#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */
+#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */
+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b
+#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */
+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve
+ function stored in GOT. */
+#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added
+ by rld on dlopen() calls. */
+#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */
+#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */
+#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */
+#define DT_MIPS_NUM 0x32
+
+/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */
+
+#define RHF_NONE 0 /* No flags */
+#define RHF_QUICKSTART (1 << 0) /* Use quickstart */
+#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */
+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */
+#define RHF_NO_MOVE (1 << 3)
+#define RHF_SGI_ONLY (1 << 4)
+#define RHF_GUARANTEE_INIT (1 << 5)
+#define RHF_DELTA_C_PLUS_PLUS (1 << 6)
+#define RHF_GUARANTEE_START_INIT (1 << 7)
+#define RHF_PIXIE (1 << 8)
+#define RHF_DEFAULT_DELAY_LOAD (1 << 9)
+#define RHF_REQUICKSTART (1 << 10)
+#define RHF_REQUICKSTARTED (1 << 11)
+#define RHF_CORD (1 << 12)
+#define RHF_NO_UNRES_UNDEF (1 << 13)
+#define RHF_RLD_ORDER_SAFE (1 << 14)
+
+/* Entries found in sections of type SHT_MIPS_LIBLIST. */
+
+typedef struct
+{
+ Elf32_Word l_name; /* Name (string table index) */
+ Elf32_Word l_time_stamp; /* Timestamp */
+ Elf32_Word l_checksum; /* Checksum */
+ Elf32_Word l_version; /* Interface version */
+ Elf32_Word l_flags; /* Flags */
+} Elf32_Lib;
+
+typedef struct
+{
+ Elf64_Word l_name; /* Name (string table index) */
+ Elf64_Word l_time_stamp; /* Timestamp */
+ Elf64_Word l_checksum; /* Checksum */
+ Elf64_Word l_version; /* Interface version */
+ Elf64_Word l_flags; /* Flags */
+} Elf64_Lib;
+
+
+/* Legal values for l_flags. */
+
+#define LL_NONE 0
+#define LL_EXACT_MATCH (1 << 0) /* Require exact match */
+#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */
+#define LL_REQUIRE_MINOR (1 << 2)
+#define LL_EXPORTS (1 << 3)
+#define LL_DELAY_LOAD (1 << 4)
+#define LL_DELTA (1 << 5)
+
+/* Entries found in sections of type SHT_MIPS_CONFLICT. */
+
+typedef Elf32_Addr Elf32_Conflict;
+
+
+/* HPPA specific definitions. */
+
+/* Legal values for e_flags field of Elf32_Ehdr. */
+
+#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */
+#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */
+#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */
+#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */
+#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch
+ prediction. */
+#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */
+#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */
+
+/* Defined values for `e_flags & EF_PARISC_ARCH' are: */
+
+#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */
+#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */
+#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */
+
+/* Additional section indeces. */
+
+#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared
+ symbols in ANSI C. */
+#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */
+
+/* Legal values for sh_type field of Elf32_Shdr. */
+
+#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */
+#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */
+#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */
+
+/* Legal values for sh_flags field of Elf32_Shdr. */
+
+#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */
+#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */
+#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */
+
+#define STT_HP_OPAQUE (STT_LOOS + 0x1)
+#define STT_HP_STUB (STT_LOOS + 0x2)
+
+/* HPPA relocs. */
+
+#define R_PARISC_NONE 0 /* No reloc. */
+#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */
+#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */
+#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */
+#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */
+#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */
+#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */
+#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */
+#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */
+#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */
+#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */
+#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */
+#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */
+#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */
+#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */
+#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */
+#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */
+#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */
+#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */
+#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */
+#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */
+#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */
+#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */
+#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */
+#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */
+#define R_PARISC_FPTR64 64 /* 64 bits function address. */
+#define R_PARISC_PLABEL32 65 /* 32 bits function address. */
+#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */
+#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */
+#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */
+#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */
+#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */
+#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */
+#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */
+#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */
+#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */
+#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */
+#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */
+#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */
+#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */
+#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */
+#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */
+#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */
+#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */
+#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */
+#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */
+#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */
+#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */
+#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */
+#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */
+#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */
+#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */
+#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */
+#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */
+#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */
+#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */
+#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */
+#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LORESERVE 128
+#define R_PARISC_COPY 128 /* Copy relocation. */
+#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */
+#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */
+#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */
+#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */
+#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */
+#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */
+#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */
+#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */
+#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */
+#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */
+#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_HIRESERVE 255
+
+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */
+
+#define PT_HP_TLS (PT_LOOS + 0x0)
+#define PT_HP_CORE_NONE (PT_LOOS + 0x1)
+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2)
+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3)
+#define PT_HP_CORE_COMM (PT_LOOS + 0x4)
+#define PT_HP_CORE_PROC (PT_LOOS + 0x5)
+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6)
+#define PT_HP_CORE_STACK (PT_LOOS + 0x7)
+#define PT_HP_CORE_SHM (PT_LOOS + 0x8)
+#define PT_HP_CORE_MMF (PT_LOOS + 0x9)
+#define PT_HP_PARALLEL (PT_LOOS + 0x10)
+#define PT_HP_FASTBIND (PT_LOOS + 0x11)
+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12)
+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13)
+#define PT_HP_STACK (PT_LOOS + 0x14)
+
+#define PT_PARISC_ARCHEXT 0x70000000
+#define PT_PARISC_UNWIND 0x70000001
+
+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */
+
+#define PF_PARISC_SBP 0x08000000
+
+#define PF_HP_PAGE_SIZE 0x00100000
+#define PF_HP_FAR_SHARED 0x00200000
+#define PF_HP_NEAR_SHARED 0x00400000
+#define PF_HP_CODE 0x01000000
+#define PF_HP_MODIFY 0x02000000
+#define PF_HP_LAZYSWAP 0x04000000
+#define PF_HP_SBP 0x08000000
+
+
+/* Alpha specific definitions. */
+
+/* Legal values for e_flags field of Elf64_Ehdr. */
+
+#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */
+#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */
+
+/* Legal values for sh_type field of Elf64_Shdr. */
+
+/* These two are primerily concerned with ECOFF debugging info. */
+#define SHT_ALPHA_DEBUG 0x70000001
+#define SHT_ALPHA_REGINFO 0x70000002
+
+/* Legal values for sh_flags field of Elf64_Shdr. */
+
+#define SHF_ALPHA_GPREL 0x10000000
+
+/* Legal values for st_other field of Elf64_Sym. */
+#define STO_ALPHA_NOPV 0x80 /* No PV required. */
+#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */
+
+/* Alpha relocs. */
+
+#define R_ALPHA_NONE 0 /* No reloc */
+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */
+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */
+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */
+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */
+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */
+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */
+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */
+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */
+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */
+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */
+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */
+#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */
+#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */
+#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */
+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */
+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */
+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */
+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */
+#define R_ALPHA_TLS_GD_HI 28
+#define R_ALPHA_TLSGD 29
+#define R_ALPHA_TLS_LDM 30
+#define R_ALPHA_DTPMOD64 31
+#define R_ALPHA_GOTDTPREL 32
+#define R_ALPHA_DTPREL64 33
+#define R_ALPHA_DTPRELHI 34
+#define R_ALPHA_DTPRELLO 35
+#define R_ALPHA_DTPREL16 36
+#define R_ALPHA_GOTTPREL 37
+#define R_ALPHA_TPREL64 38
+#define R_ALPHA_TPRELHI 39
+#define R_ALPHA_TPRELLO 40
+#define R_ALPHA_TPREL16 41
+/* Keep this the last entry. */
+#define R_ALPHA_NUM 46
+
+/* Magic values of the LITUSE relocation addend. */
+#define LITUSE_ALPHA_ADDR 0
+#define LITUSE_ALPHA_BASE 1
+#define LITUSE_ALPHA_BYTOFF 2
+#define LITUSE_ALPHA_JSR 3
+#define LITUSE_ALPHA_TLS_GD 4
+#define LITUSE_ALPHA_TLS_LDM 5
+
+/* Legal values for d_tag of Elf64_Dyn. */
+#define DT_ALPHA_PLTRO (DT_LOPROC + 0)
+#define DT_ALPHA_NUM 1
+
+/* PowerPC specific declarations */
+
+/* Values for Elf32/64_Ehdr.e_flags. */
+#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */
+
+/* Cygnus local bits below */
+#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/
+#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib
+ flag */
+
+/* PowerPC relocations defined by the ABIs */
+#define R_PPC_NONE 0
+#define R_PPC_ADDR32 1 /* 32bit absolute address */
+#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */
+#define R_PPC_ADDR16 3 /* 16bit absolute address */
+#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */
+#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */
+#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */
+#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */
+#define R_PPC_ADDR14_BRTAKEN 8
+#define R_PPC_ADDR14_BRNTAKEN 9
+#define R_PPC_REL24 10 /* PC relative 26 bit */
+#define R_PPC_REL14 11 /* PC relative 16 bit */
+#define R_PPC_REL14_BRTAKEN 12
+#define R_PPC_REL14_BRNTAKEN 13
+#define R_PPC_GOT16 14
+#define R_PPC_GOT16_LO 15
+#define R_PPC_GOT16_HI 16
+#define R_PPC_GOT16_HA 17
+#define R_PPC_PLTREL24 18
+#define R_PPC_COPY 19
+#define R_PPC_GLOB_DAT 20
+#define R_PPC_JMP_SLOT 21
+#define R_PPC_RELATIVE 22
+#define R_PPC_LOCAL24PC 23
+#define R_PPC_UADDR32 24
+#define R_PPC_UADDR16 25
+#define R_PPC_REL32 26
+#define R_PPC_PLT32 27
+#define R_PPC_PLTREL32 28
+#define R_PPC_PLT16_LO 29
+#define R_PPC_PLT16_HI 30
+#define R_PPC_PLT16_HA 31
+#define R_PPC_SDAREL16 32
+#define R_PPC_SECTOFF 33
+#define R_PPC_SECTOFF_LO 34
+#define R_PPC_SECTOFF_HI 35
+#define R_PPC_SECTOFF_HA 36
+
+/* PowerPC relocations defined for the TLS access ABI. */
+#define R_PPC_TLS 67 /* none (sym+add)@tls */
+#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */
+#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */
+#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */
+#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */
+#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */
+#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */
+#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */
+#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */
+#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */
+#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */
+#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */
+#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */
+#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */
+#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */
+#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */
+#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */
+#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */
+#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */
+#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */
+#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */
+#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */
+#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */
+#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */
+#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */
+#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */
+#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */
+#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */
+
+/* Keep this the last entry. */
+#define R_PPC_NUM 95
+
+/* The remaining relocs are from the Embedded ELF ABI, and are not
+ in the SVR4 ELF ABI. */
+#define R_PPC_EMB_NADDR32 101
+#define R_PPC_EMB_NADDR16 102
+#define R_PPC_EMB_NADDR16_LO 103
+#define R_PPC_EMB_NADDR16_HI 104
+#define R_PPC_EMB_NADDR16_HA 105
+#define R_PPC_EMB_SDAI16 106
+#define R_PPC_EMB_SDA2I16 107
+#define R_PPC_EMB_SDA2REL 108
+#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */
+#define R_PPC_EMB_MRKREF 110
+#define R_PPC_EMB_RELSEC16 111
+#define R_PPC_EMB_RELST_LO 112
+#define R_PPC_EMB_RELST_HI 113
+#define R_PPC_EMB_RELST_HA 114
+#define R_PPC_EMB_BIT_FLD 115
+#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */
+
+/* Diab tool relocations. */
+#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */
+#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */
+#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */
+#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */
+#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */
+#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */
+
+/* GNU relocs used in PIC code sequences. */
+#define R_PPC_REL16 249 /* word32 (sym-.) */
+#define R_PPC_REL16_LO 250 /* half16 (sym-.)@l */
+#define R_PPC_REL16_HI 251 /* half16 (sym-.)@h */
+#define R_PPC_REL16_HA 252 /* half16 (sym-.)@ha */
+
+/* This is a phony reloc to handle any old fashioned TOC16 references
+ that may still be in object files. */
+#define R_PPC_TOC16 255
+
+/* PowerPC specific values for the Dyn d_tag field. */
+#define DT_PPC_GOT (DT_LOPROC + 0)
+#define DT_PPC_NUM 1
+
+/* PowerPC64 relocations defined by the ABIs */
+#define R_PPC64_NONE R_PPC_NONE
+#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */
+#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */
+#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */
+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */
+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */
+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */
+#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */
+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN
+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN
+#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */
+#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */
+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN
+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN
+#define R_PPC64_GOT16 R_PPC_GOT16
+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO
+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI
+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA
+
+#define R_PPC64_COPY R_PPC_COPY
+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT
+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT
+#define R_PPC64_RELATIVE R_PPC_RELATIVE
+
+#define R_PPC64_UADDR32 R_PPC_UADDR32
+#define R_PPC64_UADDR16 R_PPC_UADDR16
+#define R_PPC64_REL32 R_PPC_REL32
+#define R_PPC64_PLT32 R_PPC_PLT32
+#define R_PPC64_PLTREL32 R_PPC_PLTREL32
+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO
+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI
+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA
+
+#define R_PPC64_SECTOFF R_PPC_SECTOFF
+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO
+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI
+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA
+#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */
+#define R_PPC64_ADDR64 38 /* doubleword64 S + A */
+#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */
+#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */
+#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */
+#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */
+#define R_PPC64_UADDR64 43 /* doubleword64 S + A */
+#define R_PPC64_REL64 44 /* doubleword64 S + A - P */
+#define R_PPC64_PLT64 45 /* doubleword64 L + A */
+#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */
+#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */
+#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */
+#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */
+#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */
+#define R_PPC64_TOC 51 /* doubleword64 .TOC */
+#define R_PPC64_PLTGOT16 52 /* half16* M + A */
+#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */
+#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */
+#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */
+
+#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */
+#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */
+#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */
+#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */
+#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */
+#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */
+#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */
+#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */
+#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */
+#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */
+#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */
+
+/* PowerPC64 relocations defined for the TLS access ABI. */
+#define R_PPC64_TLS 67 /* none (sym+add)@tls */
+#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */
+#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */
+#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */
+#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */
+#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */
+#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */
+#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */
+#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */
+#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */
+#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */
+#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */
+#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */
+#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */
+#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */
+#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */
+#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */
+#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */
+#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */
+#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */
+#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */
+#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */
+#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */
+#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */
+#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */
+#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */
+#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */
+#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */
+#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */
+#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */
+#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */
+#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */
+#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */
+#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */
+#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */
+#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */
+#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */
+#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */
+#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */
+#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */
+
+/* Keep this the last entry. */
+#define R_PPC64_NUM 107
+
+/* PowerPC64 specific values for the Dyn d_tag field. */
+#define DT_PPC64_GLINK (DT_LOPROC + 0)
+#define DT_PPC64_OPD (DT_LOPROC + 1)
+#define DT_PPC64_OPDSZ (DT_LOPROC + 2)
+#define DT_PPC64_NUM 3
+
+
+/* ARM specific declarations */
+
+/* Processor specific flags for the ELF header e_flags field. */
+#define EF_ARM_RELEXEC 0x01
+#define EF_ARM_HASENTRY 0x02
+#define EF_ARM_INTERWORK 0x04
+#define EF_ARM_APCS_26 0x08
+#define EF_ARM_APCS_FLOAT 0x10
+#define EF_ARM_PIC 0x20
+#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */
+#define EF_ARM_NEW_ABI 0x80
+#define EF_ARM_OLD_ABI 0x100
+
+/* Other constants defined in the ARM ELF spec. version B-01. */
+/* NB. These conflict with values defined above. */
+#define EF_ARM_SYMSARESORTED 0x04
+#define EF_ARM_DYNSYMSUSESEGIDX 0x08
+#define EF_ARM_MAPSYMSFIRST 0x10
+#define EF_ARM_EABIMASK 0XFF000000
+
+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK)
+#define EF_ARM_EABI_UNKNOWN 0x00000000
+#define EF_ARM_EABI_VER1 0x01000000
+#define EF_ARM_EABI_VER2 0x02000000
+
+/* Additional symbol types for Thumb */
+#define STT_ARM_TFUNC 0xd
+
+/* ARM-specific values for sh_flags */
+#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */
+#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined
+ in the input to a link step */
+
+/* ARM-specific program header flags */
+#define PF_ARM_SB 0x10000000 /* Segment contains the location
+ addressed by the static base */
+
+/* Processor specific values for the Phdr p_type field. */
+#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */
+
+/* ARM relocs. */
+
+#define R_ARM_NONE 0 /* No reloc */
+#define R_ARM_PC24 1 /* PC relative 26 bit branch */
+#define R_ARM_ABS32 2 /* Direct 32 bit */
+#define R_ARM_REL32 3 /* PC relative 32 bit */
+#define R_ARM_PC13 4
+#define R_ARM_ABS16 5 /* Direct 16 bit */
+#define R_ARM_ABS12 6 /* Direct 12 bit */
+#define R_ARM_THM_ABS5 7
+#define R_ARM_ABS8 8 /* Direct 8 bit */
+#define R_ARM_SBREL32 9
+#define R_ARM_THM_PC22 10
+#define R_ARM_THM_PC8 11
+#define R_ARM_AMP_VCALL9 12
+#define R_ARM_SWI24 13
+#define R_ARM_THM_SWI8 14
+#define R_ARM_XPC25 15
+#define R_ARM_THM_XPC22 16
+#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */
+#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */
+#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */
+#define R_ARM_COPY 20 /* Copy symbol at runtime */
+#define R_ARM_GLOB_DAT 21 /* Create GOT entry */
+#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */
+#define R_ARM_RELATIVE 23 /* Adjust by program base */
+#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */
+#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */
+#define R_ARM_GOT32 26 /* 32 bit GOT entry */
+#define R_ARM_PLT32 27 /* 32 bit PLT address */
+#define R_ARM_ALU_PCREL_7_0 32
+#define R_ARM_ALU_PCREL_15_8 33
+#define R_ARM_ALU_PCREL_23_15 34
+#define R_ARM_LDR_SBREL_11_0 35
+#define R_ARM_ALU_SBREL_19_12 36
+#define R_ARM_ALU_SBREL_27_20 37
+#define R_ARM_GNU_VTENTRY 100
+#define R_ARM_GNU_VTINHERIT 101
+#define R_ARM_THM_PC11 102 /* thumb unconditional branch */
+#define R_ARM_THM_PC9 103 /* thumb conditional branch */
+#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic
+ thread local data */
+#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic
+ thread local data */
+#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS
+ block */
+#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of
+ static TLS block offset */
+#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static
+ TLS block */
+#define R_ARM_RXPC25 249
+#define R_ARM_RSBREL32 250
+#define R_ARM_THM_RPC22 251
+#define R_ARM_RREL32 252
+#define R_ARM_RABS22 253
+#define R_ARM_RPC24 254
+#define R_ARM_RBASE 255
+/* Keep this the last entry. */
+#define R_ARM_NUM 256
+
+/* IA-64 specific declarations. */
+
+/* Processor specific flags for the Ehdr e_flags field. */
+#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */
+#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */
+#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */
+
+/* Processor specific values for the Phdr p_type field. */
+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */
+#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */
+#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12)
+#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13)
+#define PT_IA_64_HP_STACK (PT_LOOS + 0x14)
+
+/* Processor specific flags for the Phdr p_flags field. */
+#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */
+
+/* Processor specific values for the Shdr sh_type field. */
+#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */
+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */
+
+/* Processor specific flags for the Shdr sh_flags field. */
+#define SHF_IA_64_SHORT 0x10000000 /* section near gp */
+#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */
+
+/* Processor specific values for the Dyn d_tag field. */
+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0)
+#define DT_IA_64_NUM 1
+
+/* IA-64 relocations. */
+#define R_IA64_NONE 0x00 /* none */
+#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */
+#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */
+#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */
+#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */
+#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */
+#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */
+#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */
+#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */
+#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */
+#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */
+#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */
+#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */
+#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */
+#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */
+#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */
+#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */
+#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */
+#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */
+#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */
+#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */
+#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */
+#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */
+#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */
+#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */
+#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */
+#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */
+#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */
+#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */
+#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */
+#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */
+#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */
+#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */
+#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */
+#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */
+#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */
+#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */
+#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */
+#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */
+#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */
+#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */
+#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */
+#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */
+#define R_IA64_REL32MSB 0x6c /* data 4 + REL */
+#define R_IA64_REL32LSB 0x6d /* data 4 + REL */
+#define R_IA64_REL64MSB 0x6e /* data 8 + REL */
+#define R_IA64_REL64LSB 0x6f /* data 8 + REL */
+#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */
+#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */
+#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */
+#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */
+#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */
+#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */
+#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */
+#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */
+#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */
+#define R_IA64_COPY 0x84 /* copy relocation */
+#define R_IA64_SUB 0x85 /* Addend and symbol difference */
+#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */
+#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */
+#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */
+#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */
+#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */
+#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */
+#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */
+#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */
+#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */
+#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */
+#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */
+#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */
+#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */
+#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */
+#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */
+#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */
+
+/* SH specific declarations */
+
+/* SH relocs. */
+#define R_SH_NONE 0
+#define R_SH_DIR32 1
+#define R_SH_REL32 2
+#define R_SH_DIR8WPN 3
+#define R_SH_IND12W 4
+#define R_SH_DIR8WPL 5
+#define R_SH_DIR8WPZ 6
+#define R_SH_DIR8BP 7
+#define R_SH_DIR8W 8
+#define R_SH_DIR8L 9
+#define R_SH_SWITCH16 25
+#define R_SH_SWITCH32 26
+#define R_SH_USES 27
+#define R_SH_COUNT 28
+#define R_SH_ALIGN 29
+#define R_SH_CODE 30
+#define R_SH_DATA 31
+#define R_SH_LABEL 32
+#define R_SH_SWITCH8 33
+#define R_SH_GNU_VTINHERIT 34
+#define R_SH_GNU_VTENTRY 35
+#define R_SH_TLS_GD_32 144
+#define R_SH_TLS_LD_32 145
+#define R_SH_TLS_LDO_32 146
+#define R_SH_TLS_IE_32 147
+#define R_SH_TLS_LE_32 148
+#define R_SH_TLS_DTPMOD32 149
+#define R_SH_TLS_DTPOFF32 150
+#define R_SH_TLS_TPOFF32 151
+#define R_SH_GOT32 160
+#define R_SH_PLT32 161
+#define R_SH_COPY 162
+#define R_SH_GLOB_DAT 163
+#define R_SH_JMP_SLOT 164
+#define R_SH_RELATIVE 165
+#define R_SH_GOTOFF 166
+#define R_SH_GOTPC 167
+/* Keep this the last entry. */
+#define R_SH_NUM 256
+
+/* Additional s390 relocs */
+
+#define R_390_NONE 0 /* No reloc. */
+#define R_390_8 1 /* Direct 8 bit. */
+#define R_390_12 2 /* Direct 12 bit. */
+#define R_390_16 3 /* Direct 16 bit. */
+#define R_390_32 4 /* Direct 32 bit. */
+#define R_390_PC32 5 /* PC relative 32 bit. */
+#define R_390_GOT12 6 /* 12 bit GOT offset. */
+#define R_390_GOT32 7 /* 32 bit GOT offset. */
+#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */
+#define R_390_COPY 9 /* Copy symbol at runtime. */
+#define R_390_GLOB_DAT 10 /* Create GOT entry. */
+#define R_390_JMP_SLOT 11 /* Create PLT entry. */
+#define R_390_RELATIVE 12 /* Adjust by program base. */
+#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */
+#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */
+#define R_390_GOT16 15 /* 16 bit GOT offset. */
+#define R_390_PC16 16 /* PC relative 16 bit. */
+#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */
+#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */
+#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */
+#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */
+#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */
+#define R_390_64 22 /* Direct 64 bit. */
+#define R_390_PC64 23 /* PC relative 64 bit. */
+#define R_390_GOT64 24 /* 64 bit GOT offset. */
+#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */
+#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */
+#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */
+#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */
+#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */
+#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */
+#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */
+#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */
+#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */
+#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */
+#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */
+#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */
+#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */
+#define R_390_TLS_GDCALL 38 /* Tag for function call in general
+ dynamic TLS code. */
+#define R_390_TLS_LDCALL 39 /* Tag for function call in local
+ dynamic TLS code. */
+#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic
+ thread local data. */
+#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic
+ thread local data. */
+#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic
+ thread local data in LE code. */
+#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic
+ thread local data in LE code. */
+#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to
+ static TLS block. */
+#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to
+ static TLS block. */
+#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS
+ block. */
+#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS
+ block. */
+#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */
+#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */
+#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS
+ block. */
+#define R_390_20 57 /* Direct 20 bit. */
+#define R_390_GOT20 58 /* 20 bit GOT offset. */
+#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */
+#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS
+ block offset. */
+/* Keep this the last entry. */
+#define R_390_NUM 61
+
+
+/* CRIS relocations. */
+#define R_CRIS_NONE 0
+#define R_CRIS_8 1
+#define R_CRIS_16 2
+#define R_CRIS_32 3
+#define R_CRIS_8_PCREL 4
+#define R_CRIS_16_PCREL 5
+#define R_CRIS_32_PCREL 6
+#define R_CRIS_GNU_VTINHERIT 7
+#define R_CRIS_GNU_VTENTRY 8
+#define R_CRIS_COPY 9
+#define R_CRIS_GLOB_DAT 10
+#define R_CRIS_JUMP_SLOT 11
+#define R_CRIS_RELATIVE 12
+#define R_CRIS_16_GOT 13
+#define R_CRIS_32_GOT 14
+#define R_CRIS_16_GOTPLT 15
+#define R_CRIS_32_GOTPLT 16
+#define R_CRIS_32_GOTREL 17
+#define R_CRIS_32_PLT_GOTREL 18
+#define R_CRIS_32_PLT_PCREL 19
+
+#define R_CRIS_NUM 20
+
+
+/* AMD x86-64 relocations. */
+#define R_X86_64_NONE 0 /* No reloc */
+#define R_X86_64_64 1 /* Direct 64 bit */
+#define R_X86_64_PC32 2 /* PC relative 32 bit signed */
+#define R_X86_64_GOT32 3 /* 32 bit GOT entry */
+#define R_X86_64_PLT32 4 /* 32 bit PLT address */
+#define R_X86_64_COPY 5 /* Copy symbol at runtime */
+#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */
+#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */
+#define R_X86_64_RELATIVE 8 /* Adjust by program base */
+#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative
+ offset to GOT */
+#define R_X86_64_32 10 /* Direct 32 bit zero extended */
+#define R_X86_64_32S 11 /* Direct 32 bit sign extended */
+#define R_X86_64_16 12 /* Direct 16 bit zero extended */
+#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */
+#define R_X86_64_8 14 /* Direct 8 bit sign extended */
+#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */
+#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */
+#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */
+#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */
+#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset
+ to two GOT entries for GD symbol */
+#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset
+ to two GOT entries for LD symbol */
+#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */
+#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset
+ to GOT entry for IE symbol */
+#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */
+
+#define R_X86_64_NUM 24
+
+
+/* AM33 relocations. */
+#define R_MN10300_NONE 0 /* No reloc. */
+#define R_MN10300_32 1 /* Direct 32 bit. */
+#define R_MN10300_16 2 /* Direct 16 bit. */
+#define R_MN10300_8 3 /* Direct 8 bit. */
+#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */
+#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */
+#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */
+#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */
+#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */
+#define R_MN10300_24 9 /* Direct 24 bit. */
+#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */
+#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */
+#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */
+#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */
+#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */
+#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */
+#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */
+#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */
+#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */
+#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */
+#define R_MN10300_COPY 20 /* Copy symbol at runtime. */
+#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */
+#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */
+#define R_MN10300_RELATIVE 23 /* Adjust by program base. */
+
+#define R_MN10300_NUM 24
+
+
+/* M32R relocs. */
+#define R_M32R_NONE 0 /* No reloc. */
+#define R_M32R_16 1 /* Direct 16 bit. */
+#define R_M32R_32 2 /* Direct 32 bit. */
+#define R_M32R_24 3 /* Direct 24 bit. */
+#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */
+#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */
+#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */
+#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */
+#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */
+#define R_M32R_LO16 9 /* Low 16 bit. */
+#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */
+#define R_M32R_GNU_VTINHERIT 11
+#define R_M32R_GNU_VTENTRY 12
+/* M32R relocs use SHT_RELA. */
+#define R_M32R_16_RELA 33 /* Direct 16 bit. */
+#define R_M32R_32_RELA 34 /* Direct 32 bit. */
+#define R_M32R_24_RELA 35 /* Direct 24 bit. */
+#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */
+#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */
+#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */
+#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */
+#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */
+#define R_M32R_LO16_RELA 41 /* Low 16 bit */
+#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */
+#define R_M32R_RELA_GNU_VTINHERIT 43
+#define R_M32R_RELA_GNU_VTENTRY 44
+#define R_M32R_REL32 45 /* PC relative 32 bit. */
+
+#define R_M32R_GOT24 48 /* 24 bit GOT entry */
+#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */
+#define R_M32R_COPY 50 /* Copy symbol at runtime */
+#define R_M32R_GLOB_DAT 51 /* Create GOT entry */
+#define R_M32R_JMP_SLOT 52 /* Create PLT entry */
+#define R_M32R_RELATIVE 53 /* Adjust by program base */
+#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */
+#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */
+#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned
+ low */
+#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed
+ low */
+#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */
+#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to
+ GOT with unsigned low */
+#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to
+ GOT with signed low */
+#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to
+ GOT */
+#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT
+ with unsigned low */
+#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT
+ with signed low */
+#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */
+#define R_M32R_NUM 256 /* Keep this the last entry. */
+
+
+__END_DECLS
+
+#endif /* elf.h */
diff --git a/libc/elf/enbl-secure.c b/libc/elf/enbl-secure.c
new file mode 100644
index 000000000..fac3b9c52
--- /dev/null
+++ b/libc/elf/enbl-secure.c
@@ -0,0 +1,37 @@
+/* Define and initialize the `__libc_enable_secure' flag. Generic version.
+ Copyright (C) 1996, 1997, 1998, 2000, 2003 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/* This file is used in the static libc. For the shared library,
+ dl-sysdep.c defines and initializes __libc_enable_secure. */
+
+#include <unistd.h>
+#include <libc-internal.h>
+
+/* If nonzero __libc_enable_secure is already set. */
+int __libc_enable_secure_decided;
+/* Safest assumption, if somehow the initializer isn't run. */
+int __libc_enable_secure = 1;
+
+void
+__libc_init_secure (void)
+{
+ if (__libc_enable_secure_decided == 0)
+ __libc_enable_secure = (__geteuid () != __getuid ()
+ || __getegid () != __getgid ());
+}
diff --git a/libc/elf/failobj.c b/libc/elf/failobj.c
new file mode 100644
index 000000000..500606382
--- /dev/null
+++ b/libc/elf/failobj.c
@@ -0,0 +1,10 @@
+/* This function is supposed to not exist. */
+extern int xyzzy (int);
+
+extern int foo (int);
+
+int
+foo (int a)
+{
+ return xyzzy (a);
+}
diff --git a/libc/elf/filter.c b/libc/elf/filter.c
new file mode 100644
index 000000000..46aa15ba1
--- /dev/null
+++ b/libc/elf/filter.c
@@ -0,0 +1,19 @@
+#include <mcheck.h>
+#include <stdio.h>
+#include <string.h>
+
+extern const char *foo (void);
+
+int
+main (void)
+{
+ const char *s;
+
+ mtrace ();
+
+ s = foo ();
+
+ printf ("called `foo' from `%s'\n", s);
+
+ return strcmp (s, "filtmod2.c");
+}
diff --git a/libc/elf/filtmod1.c b/libc/elf/filtmod1.c
new file mode 100644
index 000000000..1d9b19481
--- /dev/null
+++ b/libc/elf/filtmod1.c
@@ -0,0 +1,7 @@
+extern const char *foo (void);
+
+const char *
+foo (void)
+{
+ return __FILE__;
+}
diff --git a/libc/elf/filtmod2.c b/libc/elf/filtmod2.c
new file mode 100644
index 000000000..1d9b19481
--- /dev/null
+++ b/libc/elf/filtmod2.c
@@ -0,0 +1,7 @@
+extern const char *foo (void);
+
+const char *
+foo (void)
+{
+ return __FILE__;
+}
diff --git a/libc/elf/firstobj.c b/libc/elf/firstobj.c
new file mode 100644
index 000000000..2e6033eab
--- /dev/null
+++ b/libc/elf/firstobj.c
@@ -0,0 +1,10 @@
+#include <errno.h>
+
+extern int foo (void);
+
+int
+foo (void)
+{
+ errno = 0;
+ return 0;
+}
diff --git a/libc/elf/gen-trusted-dirs.awk b/libc/elf/gen-trusted-dirs.awk
new file mode 100644
index 000000000..59f10a485
--- /dev/null
+++ b/libc/elf/gen-trusted-dirs.awk
@@ -0,0 +1,37 @@
+BEGIN {
+ FS = " ";
+}
+
+{
+ for (i = 1; i <= NF; ++i) {
+ s[cnt++] = $i"/";
+ }
+}
+
+END {
+ printf ("#define SYSTEM_DIRS \\\n");
+
+ printf (" \"%s\"", s[0]);
+
+ for (i = 1; i < cnt; ++i) {
+ printf (" \"\\0\" \"%s\"", s[i]);
+ }
+
+ printf ("\n\n");
+
+ printf ("#define SYSTEM_DIRS_LEN \\\n");
+
+ printf (" %d", length (s[0]));
+ m = length (s[0]);
+
+ for (i = 1; i < cnt; ++i) {
+ printf (", %d", length(s[i]));
+ if (length(s[i]) > m) {
+ m = length(s[i]);
+ }
+ }
+
+ printf ("\n\n");
+
+ printf ("#define SYSTEM_DIRS_MAX_LEN\t%d\n", m);
+}
diff --git a/libc/elf/genrtldtbl.awk b/libc/elf/genrtldtbl.awk
new file mode 100644
index 000000000..54cdc8c58
--- /dev/null
+++ b/libc/elf/genrtldtbl.awk
@@ -0,0 +1,29 @@
+#! /usr/bin/awk
+BEGIN {
+ FS=":";
+ count=0;
+}
+{
+ for (i = 1; i <= NF; ++i) {
+ gsub (/\/*$/, "", $i);
+ dir[count++] = $i;
+ }
+}
+END {
+ for (i = 0; i < count; ++i) {
+ printf ("static struct r_search_path_elem rtld_search_dir%d =\n", i+1);
+ printf (" { \"%s/\", %d, unknown, 0, nonexisting, NULL, NULL, ",
+ dir[i], length (dir[i]) + 1);
+ if (i== 0)
+ printf ("NULL };\n");
+ else
+ printf ("&rtld_search_dir%d };\n", i);
+ }
+ printf ("\nstatic struct r_search_path_elem *rtld_search_dirs[] =\n{\n");
+ for (i = 0; i < count; ++i) {
+ printf (" &rtld_search_dir%d,\n", i + 1);
+ }
+ printf (" NULL\n};\n\n");
+ printf ("static struct r_search_path_elem *all_dirs = &rtld_search_dir%d;\n",
+ count);
+}
diff --git a/libc/elf/global.c b/libc/elf/global.c
new file mode 100644
index 000000000..c675858b6
--- /dev/null
+++ b/libc/elf/global.c
@@ -0,0 +1,7 @@
+extern int test (void);
+
+int
+main (void)
+{
+ return test ();
+}
diff --git a/libc/elf/globalmod1.c b/libc/elf/globalmod1.c
new file mode 100644
index 000000000..3f8082226
--- /dev/null
+++ b/libc/elf/globalmod1.c
@@ -0,0 +1,17 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+extern int test (void);
+
+int
+test (void)
+{
+ (void) dlopen ("reldepmod4.so", RTLD_LAZY | RTLD_GLOBAL);
+ if (dlsym (RTLD_DEFAULT, "call_me") != NULL)
+ {
+ puts ("found \"call_me\"");
+ return 0;
+ }
+ puts ("didn't find \"call_me\"");
+ return 1;
+}
diff --git a/libc/elf/initfirst.c b/libc/elf/initfirst.c
new file mode 100644
index 000000000..5ca83d21b
--- /dev/null
+++ b/libc/elf/initfirst.c
@@ -0,0 +1,22 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+int
+main (void)
+{
+ void *h = dlopen ("firstobj.so", RTLD_LAZY);
+ void *f;
+ if (! h)
+ {
+ printf ("cannot find firstobj.so: %s\n", dlerror ());
+ return 1;
+ }
+ f = dlsym (h, "foo");
+ if (! f)
+ {
+ printf ("cannot find symbol foo: %s\n", dlerror ());
+ return 2;
+ }
+ ((void (*) (void)) f) ();
+ return 0;
+}
diff --git a/libc/elf/interp.c b/libc/elf/interp.c
new file mode 100644
index 000000000..cdc6bb429
--- /dev/null
+++ b/libc/elf/interp.c
@@ -0,0 +1,21 @@
+/* interp - add information about dynamic loader to shared library objects.
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+const char __invoke_dynamic_linker__[] __attribute__ ((section (".interp")))
+ = RUNTIME_LINKER;
diff --git a/libc/elf/lateglobal.c b/libc/elf/lateglobal.c
new file mode 100644
index 000000000..4a1a7cd08
--- /dev/null
+++ b/libc/elf/lateglobal.c
@@ -0,0 +1,47 @@
+#include <dlfcn.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ void *h[2];
+ int fail;
+ int (*fp) (void);
+
+ mtrace ();
+
+ h[0] = dlopen ("ltglobmod1.so", RTLD_LAZY);
+ if (h[0] == NULL)
+ {
+ printf ("%s: cannot open %s: %s",
+ __FUNCTION__, "ltglobmod1.so", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+ h[1] = dlopen ("ltglobmod2.so", RTLD_LAZY);
+ if (h[1] == NULL)
+ {
+ printf ("%s: cannot open %s: %s",
+ __FUNCTION__, "ltglobmod2.so", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ puts ("loaded \"ltglobmod1.so\" without RTLD_GLOBAL");
+
+ fp = dlsym (h[1], "foo");
+ if (fp == NULL)
+ {
+ printf ("cannot get address of `foo': %s", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ fail = fp ();
+
+ puts ("back in main");
+
+ dlclose (h[1]);
+ dlclose (h[0]);
+
+ return fail;
+}
diff --git a/libc/elf/ldconfig.c b/libc/elf/ldconfig.c
new file mode 100644
index 000000000..5b9a5b174
--- /dev/null
+++ b/libc/elf/ldconfig.c
@@ -0,0 +1,1297 @@
+/* Copyright (C) 1999-2004, 2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Andreas Jaeger <aj@suse.de>, 1999.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define PROCINFO_CLASS static
+#include <alloca.h>
+#include <argp.h>
+#include <dirent.h>
+#include <elf.h>
+#include <error.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <glob.h>
+#include <libgen.h>
+
+#include <ldconfig.h>
+#include <dl-cache.h>
+
+#include <dl-procinfo.h>
+
+#ifdef _DL_FIRST_PLATFORM
+# define _DL_FIRST_EXTRA (_DL_FIRST_PLATFORM + _DL_PLATFORMS_COUNT)
+#else
+# define _DL_FIRST_EXTRA _DL_HWCAP_COUNT
+#endif
+
+#ifndef LD_SO_CONF
+# define LD_SO_CONF SYSCONFDIR "/ld.so.conf"
+#endif
+
+/* Get libc version number. */
+#include <version.h>
+
+#define PACKAGE _libc_intl_domainname
+
+static const struct
+{
+ const char *name;
+ int flag;
+} lib_types[] =
+{
+ {"libc4", FLAG_LIBC4},
+ {"libc5", FLAG_ELF_LIBC5},
+ {"libc6", FLAG_ELF_LIBC6},
+ {"glibc2", FLAG_ELF_LIBC6}
+};
+
+
+/* List of directories to handle. */
+struct dir_entry
+{
+ char *path;
+ int flag;
+ ino64_t ino;
+ dev_t dev;
+ struct dir_entry *next;
+};
+
+/* The list is unsorted, contains no duplicates. Entries are added at
+ the end. */
+static struct dir_entry *dir_entries;
+
+/* Flags for different options. */
+/* Print Cache. */
+static int opt_print_cache;
+
+/* Be verbose. */
+int opt_verbose;
+
+/* Format to support. */
+/* 0: only libc5/glibc2; 1: both; 2: only glibc 2.2. */
+int opt_format = 1;
+
+/* Build cache. */
+static int opt_build_cache = 1;
+
+/* Generate links. */
+static int opt_link = 1;
+
+/* Only process directories specified on the command line. */
+static int opt_only_cline;
+
+/* Path to root for chroot. */
+static char *opt_chroot;
+
+/* Manually link given shared libraries. */
+static int opt_manual_link;
+
+/* Cache file to use. */
+static char *cache_file;
+
+/* Configuration file. */
+static const char *config_file;
+
+/* Mask to use for important hardware capabilities. */
+static unsigned long int hwcap_mask = HWCAP_IMPORTANT;
+
+/* Configuration-defined capabilities defined in kernel vDSOs. */
+static const char *hwcap_extra[64 - _DL_FIRST_EXTRA];
+
+/* Name and version of program. */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *)
+ = print_version;
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { "print-cache", 'p', NULL, 0, N_("Print cache"), 0},
+ { "verbose", 'v', NULL, 0, N_("Generate verbose messages"), 0},
+ { NULL, 'N', NULL, 0, N_("Don't build cache"), 0},
+ { NULL, 'X', NULL, 0, N_("Don't generate links"), 0},
+ { NULL, 'r', N_("ROOT"), 0, N_("Change to and use ROOT as root directory"), 0},
+ { NULL, 'C', N_("CACHE"), 0, N_("Use CACHE as cache file"), 0},
+ { NULL, 'f', N_("CONF"), 0, N_("Use CONF as configuration file"), 0},
+ { NULL, 'n', NULL, 0, N_("Only process directories specified on the command line. Don't build cache."), 0},
+ { NULL, 'l', NULL, 0, N_("Manually link individual libraries."), 0},
+ { "format", 'c', N_("FORMAT"), 0, N_("Format to use: new, old or compat (default)"), 0},
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+#define PROCINFO_CLASS static
+#include <dl-procinfo.c>
+
+/* Short description of program. */
+static const char doc[] = N_("Configure Dynamic Linker Run Time Bindings.");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, NULL, doc, NULL, NULL, NULL
+};
+
+/* Check if string corresponds to an important hardware capability or
+ a platform. */
+static int
+is_hwcap_platform (const char *name)
+{
+ int hwcap_idx = _dl_string_hwcap (name);
+
+ if (hwcap_idx != -1 && ((1 << hwcap_idx) & hwcap_mask))
+ return 1;
+
+ hwcap_idx = _dl_string_platform (name);
+ if (hwcap_idx != -1)
+ return 1;
+
+ for (hwcap_idx = _DL_FIRST_EXTRA; hwcap_idx < 64; ++hwcap_idx)
+ if (hwcap_extra[hwcap_idx - _DL_FIRST_EXTRA] != NULL
+ && !strcmp (name, hwcap_extra[hwcap_idx - _DL_FIRST_EXTRA]))
+ return 1;
+
+ return 0;
+}
+
+/* Get hwcap (including platform) encoding of path. */
+static uint64_t
+path_hwcap (const char *path)
+{
+ char *str = xstrdup (path);
+ char *ptr;
+ uint64_t hwcap = 0;
+ uint64_t h;
+
+ size_t len;
+
+ len = strlen (str);
+ if (str[len] == '/')
+ str[len] = '\0';
+
+ /* Search pathname from the end and check for hwcap strings. */
+ for (;;)
+ {
+ ptr = strrchr (str, '/');
+
+ if (ptr == NULL)
+ break;
+
+ h = _dl_string_hwcap (ptr + 1);
+
+ if (h == (uint64_t) -1)
+ {
+ h = _dl_string_platform (ptr + 1);
+ if (h == (uint64_t) -1)
+ {
+ for (h = _DL_FIRST_EXTRA; h < 64; ++h)
+ if (hwcap_extra[h - _DL_FIRST_EXTRA] != NULL
+ && !strcmp (ptr + 1, hwcap_extra[h - _DL_FIRST_EXTRA]))
+ break;
+ if (h == 64)
+ break;
+ }
+ }
+ hwcap += 1ULL << h;
+
+ /* Search the next part of the path. */
+ *ptr = '\0';
+ }
+
+ free (str);
+ return hwcap;
+}
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'C':
+ cache_file = arg;
+ break;
+ case 'f':
+ config_file = arg;
+ break;
+ case 'l':
+ opt_manual_link = 1;
+ break;
+ case 'N':
+ opt_build_cache = 0;
+ break;
+ case 'n':
+ opt_build_cache = 0;
+ opt_only_cline = 1;
+ break;
+ case 'p':
+ opt_print_cache = 1;
+ break;
+ case 'r':
+ opt_chroot = arg;
+ break;
+ case 'v':
+ opt_verbose = 1;
+ break;
+ case 'X':
+ opt_link = 0;
+ break;
+ case 'c':
+ if (strcmp (arg, "old") == 0)
+ opt_format = 0;
+ else if (strcmp (arg, "compat") == 0)
+ opt_format = 1;
+ else if (strcmp (arg, "new") == 0)
+ opt_format = 2;
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/* Print the version information. */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+ fprintf (stream, "ldconfig (GNU %s) %s\n", PACKAGE, VERSION);
+ fprintf (stream, gettext ("\
+Copyright (C) %s Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions. There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "2006");
+ fprintf (stream, gettext ("Written by %s.\n"),
+ "Andreas Jaeger");
+}
+
+/* Add a single directory entry. */
+static void
+add_single_dir (struct dir_entry *entry, int verbose)
+{
+ struct dir_entry *ptr, *prev;
+
+ ptr = dir_entries;
+ prev = ptr;
+ while (ptr != NULL)
+ {
+ /* Check for duplicates. */
+ if (ptr->ino == entry->ino && ptr->dev == entry->dev)
+ {
+ if (opt_verbose && verbose)
+ error (0, 0, _("Path `%s' given more than once"), entry->path);
+ /* Use the newer information. */
+ ptr->flag = entry->flag;
+ free (entry->path);
+ free (entry);
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ /* Is this the first entry? */
+ if (ptr == NULL && dir_entries == NULL)
+ dir_entries = entry;
+ else if (ptr == NULL)
+ prev->next = entry;
+}
+
+/* Add one directory to the list of directories to process. */
+static void
+add_dir (const char *line)
+{
+ unsigned int i;
+ struct dir_entry *entry = xmalloc (sizeof (struct dir_entry));
+ entry->next = NULL;
+
+ /* Search for an '=' sign. */
+ entry->path = xstrdup (line);
+ char *equal_sign = strchr (entry->path, '=');
+ if (equal_sign)
+ {
+ *equal_sign = '\0';
+ ++equal_sign;
+ entry->flag = FLAG_ANY;
+ for (i = 0; i < sizeof (lib_types) / sizeof (lib_types[0]); ++i)
+ if (strcmp (equal_sign, lib_types[i].name) == 0)
+ {
+ entry->flag = lib_types[i].flag;
+ break;
+ }
+ if (entry->flag == FLAG_ANY)
+ error (0, 0, _("%s is not a known library type"), equal_sign);
+ }
+ else
+ {
+ entry->flag = FLAG_ANY;
+ }
+
+ /* Canonify path: for now only remove leading and trailing
+ whitespace and the trailing slashes slashes. */
+ i = strlen (entry->path) - 1;
+
+ while (isspace (entry->path[i]) && i > 0)
+ entry->path[i--] = '\0';
+
+ while (entry->path[i] == '/' && i > 0)
+ entry->path[i--] = '\0';
+
+ char *path = entry->path;
+ if (opt_chroot)
+ path = chroot_canon (opt_chroot, path);
+
+ struct stat64 stat_buf;
+ if (path == NULL || stat64 (path, &stat_buf))
+ {
+ if (opt_verbose)
+ error (0, errno, _("Can't stat %s"), entry->path);
+ free (entry->path);
+ free (entry);
+ }
+ else
+ {
+ entry->ino = stat_buf.st_ino;
+ entry->dev = stat_buf.st_dev;
+
+ add_single_dir (entry, 1);
+ }
+
+ if (opt_chroot)
+ free (path);
+}
+
+
+static int
+chroot_stat (const char *real_path, const char *path, struct stat64 *st)
+{
+ int ret;
+ char *canon_path;
+
+ if (!opt_chroot)
+ return stat64 (real_path, st);
+
+ ret = lstat64 (real_path, st);
+ if (ret || !S_ISLNK (st->st_mode))
+ return ret;
+
+ canon_path = chroot_canon (opt_chroot, path);
+ if (canon_path == NULL)
+ return -1;
+
+ ret = stat64 (canon_path, st);
+ free (canon_path);
+ return ret;
+}
+
+/* Create a symbolic link from soname to libname in directory path. */
+static void
+create_links (const char *real_path, const char *path, const char *libname,
+ const char *soname)
+{
+ char *full_libname, *full_soname;
+ char *real_full_libname, *real_full_soname;
+ struct stat64 stat_lib, stat_so, lstat_so;
+ int do_link = 1;
+ int do_remove = 1;
+ /* XXX: The logics in this function should be simplified. */
+
+ /* Get complete path. */
+ full_libname = alloca (strlen (path) + strlen (libname) + 2);
+ full_soname = alloca (strlen (path) + strlen (soname) + 2);
+ sprintf (full_libname, "%s/%s", path, libname);
+ sprintf (full_soname, "%s/%s", path, soname);
+ if (opt_chroot)
+ {
+ real_full_libname = alloca (strlen (real_path) + strlen (libname) + 2);
+ real_full_soname = alloca (strlen (real_path) + strlen (soname) + 2);
+ sprintf (real_full_libname, "%s/%s", real_path, libname);
+ sprintf (real_full_soname, "%s/%s", real_path, soname);
+ }
+ else
+ {
+ real_full_libname = full_libname;
+ real_full_soname = full_soname;
+ }
+
+ /* Does soname already exist and point to the right library? */
+ if (chroot_stat (real_full_soname, full_soname, &stat_so) == 0)
+ {
+ if (chroot_stat (real_full_libname, full_libname, &stat_lib))
+ {
+ error (0, 0, _("Can't stat %s\n"), full_libname);
+ return;
+ }
+ if (stat_lib.st_dev == stat_so.st_dev
+ && stat_lib.st_ino == stat_so.st_ino)
+ /* Link is already correct. */
+ do_link = 0;
+ else if (lstat64 (full_soname, &lstat_so) == 0
+ && !S_ISLNK (lstat_so.st_mode))
+ {
+ error (0, 0, _("%s is not a symbolic link\n"), full_soname);
+ do_link = 0;
+ do_remove = 0;
+ }
+ }
+ else if (lstat64 (real_full_soname, &lstat_so) != 0
+ || !S_ISLNK (lstat_so.st_mode))
+ /* Unless it is a stale symlink, there is no need to remove. */
+ do_remove = 0;
+
+ if (opt_verbose)
+ printf ("\t%s -> %s", soname, libname);
+
+ if (do_link && opt_link)
+ {
+ /* Remove old link. */
+ if (do_remove)
+ if (unlink (real_full_soname))
+ {
+ error (0, 0, _("Can't unlink %s"), full_soname);
+ do_link = 0;
+ }
+ /* Create symbolic link. */
+ if (do_link && symlink (libname, real_full_soname))
+ {
+ error (0, 0, _("Can't link %s to %s"), full_soname, libname);
+ do_link = 0;
+ }
+ if (opt_verbose)
+ {
+ if (do_link)
+ fputs (_(" (changed)\n"), stdout);
+ else
+ fputs (_(" (SKIPPED)\n"), stdout);
+ }
+ }
+ else if (opt_verbose)
+ fputs ("\n", stdout);
+}
+
+/* Manually link the given library. */
+static void
+manual_link (char *library)
+{
+ char *path;
+ char *real_path;
+ char *real_library;
+ char *libname;
+ char *soname;
+ struct stat64 stat_buf;
+ int flag;
+ unsigned int osversion;
+
+ /* Prepare arguments for create_links call. Split library name in
+ directory and filename first. Since path is allocated, we've got
+ to be careful to free at the end. */
+ path = xstrdup (library);
+ libname = strrchr (path, '/');
+
+ if (libname)
+ {
+ /* Successfully split names. Check if path is just "/" to avoid
+ an empty path. */
+ if (libname == path)
+ {
+ libname = library + 1;
+ path = xrealloc (path, 2);
+ strcpy (path, "/");
+ }
+ else
+ {
+ *libname = '\0';
+ ++libname;
+ }
+ }
+ else
+ {
+ /* There's no path, construct one. */
+ libname = library;
+ path = xrealloc (path, 2);
+ strcpy (path, ".");
+ }
+
+ if (opt_chroot)
+ {
+ real_path = chroot_canon (opt_chroot, path);
+ if (real_path == NULL)
+ {
+ error (0, errno, _("Can't find %s"), path);
+ free (path);
+ return;
+ }
+ real_library = alloca (strlen (real_path) + strlen (libname) + 2);
+ sprintf (real_library, "%s/%s", real_path, libname);
+ }
+ else
+ {
+ real_path = path;
+ real_library = library;
+ }
+
+ /* Do some sanity checks first. */
+ if (lstat64 (real_library, &stat_buf))
+ {
+ error (0, errno, _("Can't lstat %s"), library);
+ free (path);
+ return;
+ }
+ /* We don't want links here! */
+ else if (!S_ISREG (stat_buf.st_mode))
+ {
+ error (0, 0, _("Ignored file %s since it is not a regular file."),
+ library);
+ free (path);
+ return;
+ }
+ if (process_file (real_library, library, libname, &flag, &osversion,
+ &soname, 0))
+ {
+ error (0, 0, _("No link created since soname could not be found for %s"),
+ library);
+ free (path);
+ return;
+ }
+ create_links (real_path, path, libname, soname);
+ free (soname);
+ free (path);
+}
+
+
+/* Read a whole directory and search for libraries.
+ The purpose is two-fold:
+ - search for libraries which will be added to the cache
+ - create symbolic links to the soname for each library
+
+ This has to be done separatly for each directory.
+
+ To keep track of which libraries to add to the cache and which
+ links to create, we save a list of all libraries.
+
+ The algorithm is basically:
+ for all libraries in the directory do
+ get soname of library
+ if soname is already in list
+ if new library is newer, replace entry
+ otherwise ignore this library
+ otherwise add library to list
+
+ For example, if the two libraries libxy.so.1.1 and libxy.so.1.2
+ exist and both have the same soname, e.g. libxy.so, a symbolic link
+ is created from libxy.so.1.2 (the newer one) to libxy.so.
+ libxy.so.1.2 and libxy.so are added to the cache - but not
+ libxy.so.1.1. */
+
+/* Information for one library. */
+struct dlib_entry
+{
+ char *name;
+ char *soname;
+ int flag;
+ int is_link;
+ unsigned int osversion;
+ struct dlib_entry *next;
+};
+
+
+static void
+search_dir (const struct dir_entry *entry)
+{
+ DIR *dir;
+ struct dirent64 *direntry;
+ char *file_name, *dir_name, *real_file_name, *real_name;
+ int file_name_len, real_file_name_len, len;
+ char *soname;
+ struct dlib_entry *dlibs;
+ struct dlib_entry *dlib_ptr;
+ struct stat64 lstat_buf, stat_buf;
+ int is_link, is_dir;
+ uint64_t hwcap = path_hwcap (entry->path);
+ unsigned int osversion;
+
+ file_name_len = PATH_MAX;
+ file_name = alloca (file_name_len);
+
+ dlibs = NULL;
+
+ if (opt_verbose)
+ {
+ if (hwcap != 0)
+ printf ("%s: (hwcap: %#.16" PRIx64 ")\n", entry->path, hwcap);
+ else
+ printf ("%s:\n", entry->path);
+ }
+
+ if (opt_chroot)
+ {
+ dir_name = chroot_canon (opt_chroot, entry->path);
+ real_file_name_len = PATH_MAX;
+ real_file_name = alloca (real_file_name_len);
+ }
+ else
+ {
+ dir_name = entry->path;
+ real_file_name_len = 0;
+ real_file_name = file_name;
+ }
+
+ if (dir_name == NULL || (dir = opendir (dir_name)) == NULL)
+ {
+ if (opt_verbose)
+ error (0, errno, _("Can't open directory %s"), entry->path);
+ if (opt_chroot && dir_name)
+ free (dir_name);
+ return;
+ }
+
+ while ((direntry = readdir64 (dir)) != NULL)
+ {
+ int flag;
+#ifdef _DIRENT_HAVE_D_TYPE
+ /* We only look at links and regular files. */
+ if (direntry->d_type != DT_UNKNOWN
+ && direntry->d_type != DT_LNK
+ && direntry->d_type != DT_REG
+ && direntry->d_type != DT_DIR)
+ continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+ /* Does this file look like a shared library or is it a hwcap
+ subdirectory? The dynamic linker is also considered as
+ shared library. */
+ if (((strncmp (direntry->d_name, "lib", 3) != 0
+ && strncmp (direntry->d_name, "ld-", 3) != 0)
+ || strstr (direntry->d_name, ".so") == NULL)
+ && (
+#ifdef _DIRENT_HAVE_D_TYPE
+ direntry->d_type == DT_REG ||
+#endif
+ !is_hwcap_platform (direntry->d_name)))
+ continue;
+ len = strlen (direntry->d_name);
+ /* Skip temporary files created by the prelink program. Files with
+ names like these are never really DSOs we want to look at. */
+ if (len >= sizeof (".#prelink#") - 1)
+ {
+ if (strcmp (direntry->d_name + len - sizeof (".#prelink#") + 1,
+ ".#prelink#") == 0)
+ continue;
+ if (len >= sizeof (".#prelink#.XXXXXX") - 1
+ && memcmp (direntry->d_name + len - sizeof (".#prelink#.XXXXXX")
+ + 1, ".#prelink#.", sizeof (".#prelink#.") - 1) == 0)
+ continue;
+ }
+ len += strlen (entry->path);
+ if (len > file_name_len)
+ {
+ file_name_len = len + 1;
+ file_name = alloca (file_name_len);
+ if (!opt_chroot)
+ real_file_name = file_name;
+ }
+ sprintf (file_name, "%s/%s", entry->path, direntry->d_name);
+ if (opt_chroot)
+ {
+ len = strlen (dir_name) + strlen (direntry->d_name);
+ if (len > real_file_name_len)
+ {
+ real_file_name_len = len + 1;
+ real_file_name = alloca (real_file_name_len);
+ }
+ sprintf (real_file_name, "%s/%s", dir_name, direntry->d_name);
+ }
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (direntry->d_type != DT_UNKNOWN)
+ lstat_buf.st_mode = DTTOIF (direntry->d_type);
+ else
+#endif
+ if (__builtin_expect (lstat64 (real_file_name, &lstat_buf), 0))
+ {
+ error (0, errno, _("Cannot lstat %s"), file_name);
+ continue;
+ }
+
+ is_link = S_ISLNK (lstat_buf.st_mode);
+ if (is_link)
+ {
+ /* In case of symlink, we check if the symlink refers to
+ a directory. */
+ if (__builtin_expect (stat64 (real_file_name, &stat_buf), 0))
+ {
+ if (opt_verbose)
+ error (0, errno, _("Cannot stat %s"), file_name);
+
+ /* Remove stale symlinks. */
+ if (strstr (direntry->d_name, ".so."))
+ unlink (real_file_name);
+ continue;
+ }
+ is_dir = S_ISDIR (stat_buf.st_mode);
+ }
+ else
+ is_dir = S_ISDIR (lstat_buf.st_mode);
+
+ if (is_dir && is_hwcap_platform (direntry->d_name))
+ {
+ /* Handle subdirectory later. */
+ struct dir_entry *new_entry;
+
+ new_entry = xmalloc (sizeof (struct dir_entry));
+ new_entry->path = xstrdup (file_name);
+ new_entry->flag = entry->flag;
+ new_entry->next = NULL;
+ if (is_link)
+ {
+ new_entry->ino = stat_buf.st_ino;
+ new_entry->dev = stat_buf.st_dev;
+ }
+ else
+ {
+#ifdef _DIRENT_HAVE_D_TYPE
+ /* We have filled in lstat only #ifndef
+ _DIRENT_HAVE_D_TYPE. Fill it in if needed. */
+ if (direntry->d_type != DT_UNKNOWN
+ && __builtin_expect (lstat64 (real_file_name, &lstat_buf),
+ 0))
+ {
+ error (0, errno, _("Cannot lstat %s"), file_name);
+ free (new_entry->path);
+ free (new_entry);
+ continue;
+ }
+#endif
+
+ new_entry->ino = lstat_buf.st_ino;
+ new_entry->dev = lstat_buf.st_dev;
+ }
+ add_single_dir (new_entry, 0);
+ continue;
+ }
+ else if (!S_ISREG (lstat_buf.st_mode) && !is_link)
+ continue;
+
+ if (opt_chroot && is_link)
+ {
+ real_name = chroot_canon (opt_chroot, file_name);
+ if (real_name == NULL)
+ {
+ if (strstr (file_name, ".so") == NULL)
+ error (0, 0, _("Input file %s not found.\n"), file_name);
+ continue;
+ }
+ }
+ else
+ real_name = real_file_name;
+
+ if (process_file (real_name, file_name, direntry->d_name, &flag,
+ &osversion, &soname, is_link))
+ {
+ if (real_name != real_file_name)
+ free (real_name);
+ continue;
+ }
+
+
+ /* A link may just point to itself. */
+ if (is_link)
+ {
+ /* If the path the link points to isn't its soname and it is not
+ .so symlink for ld(1) only, we treat it as a normal file. */
+ const char *real_base_name = basename (real_file_name);
+
+ if (strcmp (real_base_name, soname) != 0)
+ {
+ len = strlen (real_base_name);
+ if (len < strlen (".so")
+ || strcmp (real_base_name + len - strlen (".so"), ".so") != 0
+ || strncmp (real_base_name, soname, len) != 0)
+ is_link = 0;
+ }
+ }
+
+ if (real_name != real_file_name)
+ free (real_name);
+
+ if (is_link)
+ {
+ free (soname);
+ soname = xstrdup (direntry->d_name);
+ }
+
+ if (flag == FLAG_ELF
+ && (entry->flag == FLAG_ELF_LIBC5
+ || entry->flag == FLAG_ELF_LIBC6))
+ flag = entry->flag;
+ /* Some sanity checks to print warnings. */
+ if (opt_verbose)
+ {
+ if (flag == FLAG_ELF_LIBC5 && entry->flag != FLAG_ELF_LIBC5
+ && entry->flag != FLAG_ANY)
+ error (0, 0, _("libc5 library %s in wrong directory"), file_name);
+ if (flag == FLAG_ELF_LIBC6 && entry->flag != FLAG_ELF_LIBC6
+ && entry->flag != FLAG_ANY)
+ error (0, 0, _("libc6 library %s in wrong directory"), file_name);
+ if (flag == FLAG_LIBC4 && entry->flag != FLAG_LIBC4
+ && entry->flag != FLAG_ANY)
+ error (0, 0, _("libc4 library %s in wrong directory"), file_name);
+ }
+
+ /* Add library to list. */
+ for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
+ {
+ /* Is soname already in list? */
+ if (strcmp (dlib_ptr->soname, soname) == 0)
+ {
+ /* Prefer a file to a link, otherwise check which one
+ is newer. */
+ if ((!is_link && dlib_ptr->is_link)
+ || (is_link == dlib_ptr->is_link
+ && _dl_cache_libcmp (dlib_ptr->name, direntry->d_name) < 0))
+ {
+ /* It's newer - add it. */
+ /* Flag should be the same - sanity check. */
+ if (dlib_ptr->flag != flag)
+ {
+ if (dlib_ptr->flag == FLAG_ELF
+ && (flag == FLAG_ELF_LIBC5 || flag == FLAG_ELF_LIBC6))
+ dlib_ptr->flag = flag;
+ else if ((dlib_ptr->flag == FLAG_ELF_LIBC5
+ || dlib_ptr->flag == FLAG_ELF_LIBC6)
+ && flag == FLAG_ELF)
+ dlib_ptr->flag = flag;
+ else
+ error (0, 0, _("libraries %s and %s in directory %s have same soname but different type."),
+ dlib_ptr->name, direntry->d_name, entry->path);
+ }
+ free (dlib_ptr->name);
+ dlib_ptr->osversion = osversion;
+ dlib_ptr->name = xstrdup (direntry->d_name);
+ dlib_ptr->is_link = is_link;
+ }
+ /* Don't add this library, abort loop. */
+ /* Also free soname, since it's dynamically allocated. */
+ free (soname);
+ break;
+ }
+ }
+ /* Add the library if it's not already in. */
+ if (dlib_ptr == NULL)
+ {
+ dlib_ptr = (struct dlib_entry *)xmalloc (sizeof (struct dlib_entry));
+ dlib_ptr->name = xstrdup (direntry->d_name);
+ dlib_ptr->flag = flag;
+ dlib_ptr->osversion = osversion;
+ dlib_ptr->soname = soname;
+ dlib_ptr->is_link = is_link;
+ /* Add at head of list. */
+ dlib_ptr->next = dlibs;
+ dlibs = dlib_ptr;
+ }
+ }
+
+ closedir (dir);
+
+ /* Now dlibs contains a list of all libs - add those to the cache
+ and created all symbolic links. */
+ for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
+ {
+ /* Don't create links to links. */
+ if (dlib_ptr->is_link == 0)
+ create_links (dir_name, entry->path, dlib_ptr->name,
+ dlib_ptr->soname);
+ if (opt_build_cache)
+ add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag,
+ dlib_ptr->osversion, hwcap);
+ }
+
+ /* Free all resources. */
+ while (dlibs)
+ {
+ dlib_ptr = dlibs;
+ free (dlib_ptr->soname);
+ free (dlib_ptr->name);
+ dlibs = dlibs->next;
+ free (dlib_ptr);
+ }
+
+ if (opt_chroot && dir_name)
+ free (dir_name);
+}
+
+/* Search through all libraries. */
+static void
+search_dirs (void)
+{
+ struct dir_entry *entry;
+
+ for (entry = dir_entries; entry != NULL; entry = entry->next)
+ search_dir (entry);
+
+ /* Free all allocated memory. */
+ while (dir_entries)
+ {
+ entry = dir_entries;
+ dir_entries = dir_entries->next;
+ free (entry->path);
+ free (entry);
+ }
+}
+
+
+static void parse_conf_include (const char *config_file, unsigned int lineno,
+ bool do_chroot, const char *pattern);
+
+/* Parse configuration file. */
+static void
+parse_conf (const char *filename, bool do_chroot)
+{
+ FILE *file = NULL;
+ char *line = NULL;
+ const char *canon;
+ size_t len = 0;
+ unsigned int lineno;
+
+ if (do_chroot && opt_chroot)
+ {
+ canon = chroot_canon (opt_chroot, filename);
+ if (canon)
+ file = fopen (canon, "r");
+ else
+ canon = filename;
+ }
+ else
+ {
+ canon = filename;
+ file = fopen (filename, "r");
+ }
+
+ if (file == NULL)
+ {
+ error (0, errno, _("Can't open configuration file %s"), canon);
+ if (canon != filename)
+ free ((char *) canon);
+ return;
+ }
+
+ /* No threads use this stream. */
+ __fsetlocking (file, FSETLOCKING_BYCALLER);
+
+ if (canon != filename)
+ free ((char *) canon);
+
+ lineno = 0;
+ do
+ {
+ ssize_t n = getline (&line, &len, file);
+ if (n < 0)
+ break;
+
+ ++lineno;
+ if (line[n - 1] == '\n')
+ line[n - 1] = '\0';
+
+ /* Because the file format does not know any form of quoting we
+ can search forward for the next '#' character and if found
+ make it terminating the line. */
+ *strchrnul (line, '#') = '\0';
+
+ /* Remove leading whitespace. NUL is no whitespace character. */
+ char *cp = line;
+ while (isspace (*cp))
+ ++cp;
+
+ /* If the line is blank it is ignored. */
+ if (cp[0] == '\0')
+ continue;
+
+ if (!strncmp (cp, "include", 7) && isblank (cp[7]))
+ {
+ char *dir;
+ cp += 8;
+ while ((dir = strsep (&cp, " \t")) != NULL)
+ if (dir[0] != '\0')
+ parse_conf_include (filename, lineno, do_chroot, dir);
+ }
+ else if (!strncasecmp (cp, "hwcap", 5) && isblank (cp[5]))
+ {
+ cp += 6;
+ char *p, *name = NULL;
+ unsigned long int n = strtoul (cp, &cp, 0);
+ if (cp != NULL && isblank (*cp))
+ while ((p = strsep (&cp, " \t")) != NULL)
+ if (p[0] != '\0')
+ {
+ if (name == NULL)
+ name = p;
+ else
+ {
+ name = NULL;
+ break;
+ }
+ }
+ if (name == NULL)
+ {
+ error (EXIT_FAILURE, 0, _("%s:%u: bad syntax in hwcap line"),
+ filename, lineno);
+ break;
+ }
+ if (n >= (64 - _DL_FIRST_EXTRA))
+ error (EXIT_FAILURE, 0,
+ _("%s:%u: hwcap index %lu above maximum %u"),
+ filename, lineno, n, 64 - _DL_FIRST_EXTRA - 1);
+ if (hwcap_extra[n] == NULL)
+ {
+ for (unsigned long int h = 0; h < (64 - _DL_FIRST_EXTRA); ++h)
+ if (hwcap_extra[h] != NULL && !strcmp (name, hwcap_extra[h]))
+ error (EXIT_FAILURE, 0,
+ _("%s:%u: hwcap index %lu already defined as %s"),
+ filename, lineno, h, name);
+ hwcap_extra[n] = xstrdup (name);
+ }
+ else
+ {
+ if (strcmp (name, hwcap_extra[n]))
+ error (EXIT_FAILURE, 0,
+ _("%s:%u: hwcap index %lu already defined as %s"),
+ filename, lineno, n, hwcap_extra[n]);
+ if (opt_verbose)
+ error (0, 0, _("%s:%u: duplicate hwcap %lu %s"),
+ filename, lineno, n, name);
+ }
+ }
+ else
+ add_dir (cp);
+ }
+ while (!feof_unlocked (file));
+
+ /* Free buffer and close file. */
+ free (line);
+ fclose (file);
+}
+
+/* Handle one word in an `include' line, a glob pattern of additional
+ config files to read. */
+static void
+parse_conf_include (const char *config_file, unsigned int lineno,
+ bool do_chroot, const char *pattern)
+{
+ if (opt_chroot && pattern[0] != '/')
+ error (EXIT_FAILURE, 0,
+ _("need absolute file name for configuration file when using -r"));
+
+ char *copy = NULL;
+ if (pattern[0] != '/' && strchr (config_file, '/') != NULL)
+ {
+ if (asprintf (&copy, "%s/%s", dirname (strdupa (config_file)),
+ pattern) < 0)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+ pattern = copy;
+ }
+
+ glob64_t gl;
+ int result;
+ if (do_chroot && opt_chroot)
+ {
+ char *canon = chroot_canon (opt_chroot, pattern);
+ result = glob64 (canon ?: pattern, 0, NULL, &gl);
+ free (canon);
+ }
+ else
+ result = glob64 (pattern, 0, NULL, &gl);
+
+ switch (result)
+ {
+ case 0:
+ for (size_t i = 0; i < gl.gl_pathc; ++i)
+ parse_conf (gl.gl_pathv[i], false);
+ globfree64 (&gl);
+ break;
+
+ case GLOB_NOMATCH:
+ break;
+
+ case GLOB_NOSPACE:
+ errno = ENOMEM;
+ case GLOB_ABORTED:
+ if (opt_verbose)
+ error (0, errno, _("%s:%u: cannot read directory %s"),
+ config_file, lineno, pattern);
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ if (copy)
+ free (copy);
+}
+
+/* Honour LD_HWCAP_MASK. */
+static void
+set_hwcap (void)
+{
+ char *mask = getenv ("LD_HWCAP_MASK");
+
+ if (mask)
+ hwcap_mask = strtoul (mask, NULL, 0);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int remaining;
+
+ /* Parse and process arguments. */
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* Remaining arguments are additional directories if opt_manual_link
+ is not set. */
+ if (remaining != argc && !opt_manual_link)
+ {
+ int i;
+ for (i = remaining; i < argc; ++i)
+ if (opt_build_cache && argv[i][0] != '/')
+ error (EXIT_FAILURE, 0,
+ _("relative path `%s' used to build cache"),
+ argv[i]);
+ else
+ add_dir (argv[i]);
+ }
+
+#ifdef USE_TLS
+ hwcap_extra[63 - _DL_FIRST_EXTRA] = "tls";
+#endif
+
+ set_hwcap ();
+
+ if (opt_chroot)
+ {
+ /* Normalize the path a bit, we might need it for printing later. */
+ char *endp = rawmemchr (opt_chroot, '\0');
+ while (endp > opt_chroot && endp[-1] == '/')
+ --endp;
+ *endp = '\0';
+ if (endp == opt_chroot)
+ opt_chroot = NULL;
+
+ if (opt_chroot)
+ {
+ /* It is faster to use chroot if we can. */
+ if (!chroot (opt_chroot))
+ {
+ if (chdir ("/"))
+ error (EXIT_FAILURE, errno, _("Can't chdir to /"));
+ opt_chroot = NULL;
+ }
+ }
+ }
+
+ if (cache_file == NULL)
+ {
+ cache_file = alloca (strlen (LD_SO_CACHE) + 1);
+ strcpy (cache_file, LD_SO_CACHE);
+ }
+
+ if (config_file == NULL)
+ config_file = LD_SO_CONF;
+
+ if (opt_print_cache)
+ {
+ if (opt_chroot)
+ {
+ char *p = chroot_canon (opt_chroot, cache_file);
+ if (p == NULL)
+ error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"),
+ cache_file);
+ cache_file = p;
+ }
+ print_cache (cache_file);
+ if (opt_chroot)
+ free (cache_file);
+ exit (0);
+ }
+
+ if (opt_chroot)
+ {
+ /* Canonicalize the directory name of cache_file, not cache_file,
+ because we'll rename a temporary cache file to it. */
+ char *p = strrchr (cache_file, '/');
+ char *canon = chroot_canon (opt_chroot,
+ p ? (*p = '\0', cache_file) : "/");
+
+ if (canon == NULL)
+ {
+ error (EXIT_FAILURE, errno,
+ _("Can't open cache file directory %s\n"),
+ p ? cache_file : "/");
+ }
+
+ if (p)
+ ++p;
+ else
+ p = cache_file;
+
+ cache_file = alloca (strlen (canon) + strlen (p) + 2);
+ sprintf (cache_file, "%s/%s", canon, p);
+ free (canon);
+ }
+
+ if (opt_manual_link)
+ {
+ /* Link all given libraries manually. */
+ int i;
+
+ for (i = remaining; i < argc; ++i)
+ manual_link (argv[i]);
+
+ exit (0);
+ }
+
+
+ if (opt_build_cache)
+ init_cache ();
+
+ if (!opt_only_cline)
+ {
+ parse_conf (config_file, true);
+
+ /* Always add the standard search paths. */
+ add_system_dir (SLIBDIR);
+ if (strcmp (SLIBDIR, LIBDIR))
+ add_system_dir (LIBDIR);
+ }
+
+ search_dirs ();
+
+ if (opt_build_cache)
+ save_cache (cache_file);
+
+ return 0;
+}
diff --git a/libc/elf/ldd.bash.in b/libc/elf/ldd.bash.in
new file mode 100644
index 000000000..d1591a578
--- /dev/null
+++ b/libc/elf/ldd.bash.in
@@ -0,0 +1,204 @@
+#! @BASH@
+# Copyright (C) 1996-2004, 2005, 2006 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, write to the Free
+# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+# 02111-1307 USA.
+
+
+# This is the `ldd' command, which lists what shared libraries are
+# used by given dynamically-linked executables. It works by invoking the
+# run-time dynamic linker as a command and setting the environment
+# variable LD_TRACE_LOADED_OBJECTS to a non-empty value.
+
+# We should be able to find the translation right at the beginning.
+TEXTDOMAIN=libc
+TEXTDOMAINDIR=@TEXTDOMAINDIR@
+
+RTLDLIST=@RTLD@
+warn=
+bind_now=
+verbose=
+
+while test $# -gt 0; do
+ case "$1" in
+ --vers | --versi | --versio | --version)
+ echo 'ldd (GNU libc) @VERSION@'
+ printf $"Copyright (C) %s Free Software Foundation, Inc.
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+" "2006"
+ printf $"Written by %s and %s.
+" "Roland McGrath" "Ulrich Drepper"
+ exit 0
+ ;;
+ --h | --he | --hel | --help)
+ echo $"Usage: ldd [OPTION]... FILE...
+ --help print this help and exit
+ --version print version information and exit
+ -d, --data-relocs process data relocations
+ -r, --function-relocs process data and function relocations
+ -u, --unused print unused direct dependencies
+ -v, --verbose print all information
+For bug reporting instructions, please see:
+<http://www.gnu.org/software/libc/bugs.html>."
+ exit 0
+ ;;
+ -d | --d | --da | --dat | --data | --data- | --data-r | --data-re | \
+ --data-rel | --data-relo | --data-reloc | --data-relocs)
+ warn=yes
+ shift
+ ;;
+ -r | --f | --fu | --fun | --func | --funct | --functi | --functio | \
+ --function | --function- | --function-r | --function-re | --function-rel | \
+ --function-relo | --function-reloc | --function-relocs)
+ warn=yes
+ bind_now=yes
+ shift
+ ;;
+ -v | --verb | --verbo | --verbos | --verbose)
+ verbose=yes
+ shift
+ ;;
+ -u | --u | --un | --unu | --unus | --unuse | --unused)
+ unused=yes
+ shift
+ ;;
+ --v | --ve | --ver)
+ echo >&2 $"ldd: option \`$1' is ambiguous"
+ exit 1
+ ;;
+ --) # Stop option processing.
+ shift; break
+ ;;
+ -*)
+ echo >&2 'ldd:' $"unrecognized option" "\`$1'"
+ echo >&2 $"Try \`ldd --help' for more information."
+ exit 1
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+nonelf ()
+{
+ # Maybe extra code for non-ELF binaries.
+ return 1;
+}
+
+add_env="LD_TRACE_LOADED_OBJECTS=1 LD_WARN=$warn LD_BIND_NOW=$bind_now"
+add_env="$add_env LD_VERBOSE=$verbose"
+if test "$unused" = yes; then
+ add_env="$add_env LD_DEBUG=\"$LD_DEBUG${LD_DEBUG:+,}unused\""
+fi
+
+# The following use of cat is needed to make ldd work in SELinux
+# environments where the executed program might not have permissions
+# to write to the console/tty. But only bash 3.x supports the pipefail
+# option, and we don't bother to handle the case for older bash versions.
+if set -o pipefail 2> /dev/null; then
+ try_trace() {
+ eval $add_env '"$@"' | cat
+ }
+else
+ try_trace() {
+ eval $add_env '"$@"'
+ }
+fi
+
+case $# in
+0)
+ echo >&2 'ldd:' $"missing file arguments"
+ echo >&2 $"Try \`ldd --help' for more information."
+ exit 1
+ ;;
+1)
+ single_file=t
+ ;;
+*)
+ single_file=f
+ ;;
+esac
+
+result=0
+for file do
+ # We don't list the file name when there is only one.
+ test $single_file = t || echo "${file}:"
+ case $file in
+ */*) :
+ ;;
+ *) file=./$file
+ ;;
+ esac
+ if test ! -e "$file"; then
+ echo "ldd: ${file}:" $"No such file or directory" >&2
+ result=1
+ elif test ! -f "$file"; then
+ echo "ldd: ${file}:" $"not regular file" >&2
+ result=1
+ elif test -r "$file"; then
+ test -x "$file" || echo 'ldd:' $"\
+warning: you do not have execution permission for" "\`$file'" >&2
+ RTLD=
+ ret=1
+ for rtld in ${RTLDLIST}; do
+ if test -x $rtld; then
+ verify_out=`${rtld} --verify "$file"`
+ ret=$?
+ case $ret in
+ [02]) RTLD=${rtld}; break;;
+ esac
+ fi
+ done
+ case $ret in
+ 0)
+ # If the program exits with exit code 5, it means the process has been
+ # invoked with __libc_enable_secure. Fall back to running it through
+ # the dynamic linker.
+ try_trace "$file"
+ rc=$?
+ if [ $rc = 5 ]; then
+ try_trace "$RTLD" "$file"
+ rc=$?
+ fi
+ [ $rc = 0 ] || result=1
+ ;;
+ 1)
+ # This can be a non-ELF binary or no binary at all.
+ nonelf "$file" || {
+ echo $" not a dynamic executable"
+ result=1
+ }
+ ;;
+ 2)
+ try_trace "$RTLD" "$file" || result=1
+ ;;
+ *)
+ echo 'ldd:' ${RTLD} $"exited with unknown exit code" "($ret)" >&2
+ exit 1
+ ;;
+ esac
+ else
+ echo 'ldd:' $"error: you do not have read permission for" "\`$file'" >&2
+ result=1
+ fi
+done
+
+exit $result
+# Local Variables:
+# mode:ksh
+# End:
diff --git a/libc/elf/link.h b/libc/elf/link.h
new file mode 100644
index 000000000..076531d6e
--- /dev/null
+++ b/libc/elf/link.h
@@ -0,0 +1,194 @@
+/* Data structure for communication from the run-time dynamic linker for
+ loaded ELF shared objects.
+ Copyright (C) 1995-2001, 2004, 2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _LINK_H
+#define _LINK_H 1
+
+#include <features.h>
+#include <elf.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+
+/* We use this macro to refer to ELF types independent of the native wordsize.
+ `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */
+#define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type)
+#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
+#define _ElfW_1(e,w,t) e##w##t
+
+#include <bits/elfclass.h> /* Defines __ELF_NATIVE_CLASS. */
+#include <bits/link.h>
+
+/* Rendezvous structure used by the run-time dynamic linker to communicate
+ details of shared object loading to the debugger. If the executable's
+ dynamic section has a DT_DEBUG element, the run-time linker sets that
+ element's value to the address where this structure can be found. */
+
+struct r_debug
+ {
+ int r_version; /* Version number for this protocol. */
+
+ struct link_map *r_map; /* Head of the chain of loaded objects. */
+
+ /* This is the address of a function internal to the run-time linker,
+ that will always be called when the linker begins to map in a
+ library or unmap it, and again when the mapping change is complete.
+ The debugger can set a breakpoint at this address if it wants to
+ notice shared object mapping changes. */
+ ElfW(Addr) r_brk;
+ enum
+ {
+ /* This state value describes the mapping change taking place when
+ the `r_brk' address is called. */
+ RT_CONSISTENT, /* Mapping change is complete. */
+ RT_ADD, /* Beginning to add a new object. */
+ RT_DELETE /* Beginning to remove an object mapping. */
+ } r_state;
+
+ ElfW(Addr) r_ldbase; /* Base address the linker is loaded at. */
+ };
+
+/* This is the instance of that structure used by the dynamic linker. */
+extern struct r_debug _r_debug;
+
+/* This symbol refers to the "dynamic structure" in the `.dynamic' section
+ of whatever module refers to `_DYNAMIC'. So, to find its own
+ `struct r_debug', a program could do:
+ for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn)
+ if (dyn->d_tag == DT_DEBUG)
+ r_debug = (struct r_debug *) dyn->d_un.d_ptr;
+ */
+extern ElfW(Dyn) _DYNAMIC[];
+
+/* Structure describing a loaded shared object. The `l_next' and `l_prev'
+ members form a chain of all the shared objects loaded at startup.
+
+ These data structures exist in space used by the run-time dynamic linker;
+ modifying them may have disastrous results. */
+
+struct link_map
+ {
+ /* These first few members are part of the protocol with the debugger.
+ This is the same format used in SVR4. */
+
+ ElfW(Addr) l_addr; /* Base address shared object is loaded at. */
+ char *l_name; /* Absolute file name object was found in. */
+ ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. */
+ struct link_map *l_next, *l_prev; /* Chain of loaded objects. */
+ };
+
+#ifdef __USE_GNU
+
+/* Version numbers for la_version handshake interface. */
+#define LAV_CURRENT 1
+
+/* Activity types signaled through la_activity. */
+enum
+ {
+ LA_ACT_CONSISTENT, /* Link map consistent again. */
+ LA_ACT_ADD, /* New object will be added. */
+ LA_ACT_DELETE /* Objects will be removed. */
+ };
+
+/* Values representing origin of name for dynamic loading. */
+enum
+ {
+ LA_SER_ORIG = 0x01, /* Original name. */
+ LA_SER_LIBPATH = 0x02, /* Directory from LD_LIBRARY_PATH. */
+ LA_SER_RUNPATH = 0x04, /* Directory from RPATH/RUNPATH. */
+ LA_SER_CONFIG = 0x08, /* Found through ldconfig. */
+ LA_SER_DEFAULT = 0x40, /* Default directory. */
+ LA_SER_SECURE = 0x80 /* Unused. */
+ };
+
+/* Values for la_objopen return value. */
+enum
+ {
+ LA_FLG_BINDTO = 0x01, /* Audit symbols bound to this object. */
+ LA_FLG_BINDFROM = 0x02 /* Audit symbols bound from this object. */
+ };
+
+/* Values for la_symbind flags parameter. */
+enum
+ {
+ LA_SYMB_NOPLTENTER = 0x01, /* la_pltenter will not be called. */
+ LA_SYMB_NOPLTEXIT = 0x02, /* la_pltexit will not be called. */
+ LA_SYMB_STRUCTCALL = 0x04, /* Return value is a structure. */
+ LA_SYMB_DLSYM = 0x08, /* Binding due to dlsym call. */
+ LA_SYMB_ALTVALUE = 0x10 /* Value has been changed by a previous
+ la_symbind call. */
+ };
+
+struct dl_phdr_info
+ {
+ ElfW(Addr) dlpi_addr;
+ const char *dlpi_name;
+ const ElfW(Phdr) *dlpi_phdr;
+ ElfW(Half) dlpi_phnum;
+
+ /* Note: Following members were introduced after the first
+ version of this structure was available. Check the SIZE
+ argument passed to the dl_iterate_phdr callback to determine
+ whether or not each later member is available. */
+
+ /* Incremented when a new object may have been added. */
+ unsigned long long int dlpi_adds;
+ /* Incremented when an object may have been removed. */
+ unsigned long long int dlpi_subs;
+
+ /* If there is a PT_TLS segment, its module ID as used in
+ TLS relocations, else zero. */
+ size_t dlpi_tls_modid;
+
+ /* The address of the calling thread's instance of this module's
+ PT_TLS segment, if it has one and it has been allocated
+ in the calling thread, otherwise a null pointer. */
+ void *dlpi_tls_data;
+ };
+
+__BEGIN_DECLS
+
+extern int dl_iterate_phdr (int (*__callback) (struct dl_phdr_info *,
+ size_t, void *),
+ void *__data);
+
+
+/* Prototypes for the ld.so auditing interfaces. These are not
+ defined anywhere in ld.so but instead have to be provided by the
+ auditing DSO. */
+extern unsigned int la_version (unsigned int __version);
+extern void la_activity (uintptr_t *__cookie, unsigned int __flag);
+extern char *la_objsearch (const char *__name, uintptr_t *__cookie,
+ unsigned int __flag);
+extern unsigned int la_objopen (struct link_map *__map, Lmid_t __lmid,
+ uintptr_t *__cookie);
+extern void la_preinit (uintptr_t *__cookie);
+extern uintptr_t la_symbind32 (Elf32_Sym *__sym, unsigned int __ndx,
+ uintptr_t *__refcook, uintptr_t *__defcook,
+ unsigned int *__flags, const char *__symname);
+extern uintptr_t la_symbind64 (Elf64_Sym *__sym, unsigned int __ndx,
+ uintptr_t *__refcook, uintptr_t *__defcook,
+ unsigned int *__flags, const char *__symname);
+extern unsigned int la_objclose (uintptr_t *__cookie);
+
+__END_DECLS
+
+#endif
+
+#endif /* link.h */
diff --git a/libc/elf/loadfail.c b/libc/elf/loadfail.c
new file mode 100644
index 000000000..7531aa958
--- /dev/null
+++ b/libc/elf/loadfail.c
@@ -0,0 +1,42 @@
+#include <dlfcn.h>
+#include <error.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ void *su[5];
+ void *h;
+ int n;
+
+ mtrace ();
+
+ if ((su[0] = dlopen ("testobj1.so", RTLD_GLOBAL | RTLD_NOW)) == NULL
+ || (su[1] = dlopen ("testobj2.so", RTLD_GLOBAL | RTLD_NOW)) == NULL
+ || (su[2] = dlopen ("testobj3.so", RTLD_GLOBAL | RTLD_NOW)) == NULL
+ || (su[3] = dlopen ("testobj4.so", RTLD_GLOBAL | RTLD_NOW)) == NULL
+ || (su[4] = dlopen ("testobj5.so", RTLD_GLOBAL | RTLD_NOW)) == NULL)
+ error (EXIT_FAILURE, 0, "failed to load shared object: %s", dlerror ());
+
+ h = dlopen ("failobj.so", RTLD_GLOBAL | RTLD_NOW);
+
+ printf ("h = %p, %s\n", h, h == NULL ? "ok" : "fail");
+
+ for (n = 0; n < 5; ++n)
+ if (dlclose (su[n]) != 0)
+ {
+ printf ("failed to unload su[%d]: %s\n", n, dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ return h != NULL;
+}
+
+extern int foo (int a);
+int
+foo (int a)
+{
+ return 10;
+}
diff --git a/libc/elf/loadtest.c b/libc/elf/loadtest.c
new file mode 100644
index 000000000..727469b49
--- /dev/null
+++ b/libc/elf/loadtest.c
@@ -0,0 +1,202 @@
+#include <assert.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <error.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* How many load/unload operations do we do. */
+#define TEST_ROUNDS 1000
+
+
+static struct
+{
+ /* Name of the module. */
+ const char *name;
+ /* The handle. */
+ void *handle;
+} testobjs[] =
+{
+ { "testobj1.so", NULL },
+ { "testobj2.so", NULL },
+ { "testobj3.so", NULL },
+ { "testobj4.so", NULL },
+ { "testobj5.so", NULL },
+ { "testobj6.so", NULL },
+};
+#define NOBJS (sizeof (testobjs) / sizeof (testobjs[0]))
+
+
+static const struct
+{
+ /* Name of a function to call. */
+ const char *fname;
+ /* Index in status and handle array. */
+ int index;
+ /* Options while loading the module. */
+ int options;
+} tests[] =
+{
+ { "obj1func2", 0, RTLD_LAZY },
+ { "obj1func1", 0, RTLD_LAZY | RTLD_GLOBAL },
+ { "obj1func1", 0, RTLD_NOW, },
+ { "obj1func2", 0, RTLD_NOW | RTLD_GLOBAL },
+ { "obj2func2", 1, RTLD_LAZY },
+ { "obj2func1", 1, RTLD_LAZY | RTLD_GLOBAL, },
+ { "obj2func1", 1, RTLD_NOW, },
+ { "obj2func2", 1, RTLD_NOW | RTLD_GLOBAL },
+ { "obj3func2", 2, RTLD_LAZY },
+ { "obj3func1", 2, RTLD_LAZY | RTLD_GLOBAL },
+ { "obj3func1", 2, RTLD_NOW },
+ { "obj3func2", 2, RTLD_NOW | RTLD_GLOBAL },
+ { "obj4func2", 3, RTLD_LAZY },
+ { "obj4func1", 3, RTLD_LAZY | RTLD_GLOBAL },
+ { "obj4func1", 3, RTLD_NOW },
+ { "obj4func2", 3, RTLD_NOW | RTLD_GLOBAL },
+ { "obj5func2", 4, RTLD_LAZY },
+ { "obj5func1", 4, RTLD_LAZY | RTLD_GLOBAL },
+ { "obj5func1", 4, RTLD_NOW },
+ { "obj5func2", 4, RTLD_NOW | RTLD_GLOBAL },
+ { "obj6func2", 5, RTLD_LAZY },
+ { "obj6func1", 5, RTLD_LAZY | RTLD_GLOBAL },
+ { "obj6func1", 5, RTLD_NOW },
+ { "obj6func2", 5, RTLD_NOW | RTLD_GLOBAL },
+};
+#define NTESTS (sizeof (tests) / sizeof (tests[0]))
+
+
+#include <include/link.h>
+
+#define MAPS ((struct link_map *) _r_debug.r_map)
+
+#define OUT \
+ for (map = MAPS; map != NULL; map = map->l_next) \
+ if (map->l_type == lt_loaded) \
+ printf ("name = \"%s\", direct_opencount = %d\n", \
+ map->l_name, (int) map->l_direct_opencount); \
+ fflush (stdout)
+
+
+int
+main (int argc, char *argv[])
+{
+ int debug = argc > 1 && argv[1][0] != '\0';
+ int count = TEST_ROUNDS;
+ int result = 0;
+ struct link_map *map;
+
+ mtrace ();
+
+ /* Just a seed. */
+ srandom (TEST_ROUNDS);
+
+ if (debug)
+ {
+ puts ("in the beginning");
+ OUT;
+ }
+
+ while (count--)
+ {
+ int nr = random () % NTESTS;
+ int index = tests[nr].index;
+
+ printf ("%4d: %4d: ", count + 1, nr);
+ fflush (stdout);
+
+ if (testobjs[index].handle == NULL)
+ {
+ int (*fct) (int);
+
+ /* Load the object. */
+ testobjs[index].handle = dlopen (testobjs[index].name,
+ tests[nr].options);
+ if (testobjs[index].handle == NULL)
+ error (EXIT_FAILURE, 0, "cannot load `%s': %s",
+ testobjs[index].name, dlerror ());
+
+ /* Test the function call. */
+ fct = dlsym (testobjs[index].handle, tests[nr].fname);
+ if (fct == NULL)
+ error (EXIT_FAILURE, 0,
+ "cannot get function `%s' from shared object `%s': %s",
+ tests[nr].fname, testobjs[index].name, dlerror ());
+
+ fct (10);
+
+ printf ("successfully loaded `%s', handle %p\n",
+ testobjs[index].name, testobjs[index].handle);
+ }
+ else
+ {
+ if (dlclose (testobjs[index].handle) != 0)
+ {
+ printf ("failed to close %s\n", testobjs[index].name);
+ result = 1;
+ }
+ else
+ printf ("successfully unloaded `%s', handle %p\n",
+ testobjs[index].name, testobjs[index].handle);
+
+ testobjs[index].handle = NULL;
+
+ if (testobjs[0].handle == NULL
+ && testobjs[1].handle == NULL
+ && testobjs[5].handle == NULL)
+ {
+ /* In this case none of the objects above should be
+ present. */
+ for (map = MAPS; map != NULL; map = map->l_next)
+ if (map->l_type == lt_loaded
+ && (strstr (map->l_name, testobjs[0].name) != NULL
+ || strstr (map->l_name, testobjs[1].name) != NULL
+ || strstr (map->l_name, testobjs[5].name) != NULL))
+ {
+ printf ("`%s' is still loaded\n", map->l_name);
+ result = 1;
+ }
+ }
+ }
+
+ if (debug)
+ OUT;
+ }
+
+ /* Unload all loaded modules. */
+ for (count = 0; count < (int) NOBJS; ++count)
+ if (testobjs[count].handle != NULL)
+ {
+ printf ("\nclose: %s: l_initfini = %p, l_versions = %p\n",
+ testobjs[count].name,
+ ((struct link_map *) testobjs[count].handle)->l_initfini,
+ ((struct link_map *) testobjs[count].handle)->l_versions);
+
+ if (dlclose (testobjs[count].handle) != 0)
+ {
+ printf ("failed to close %s\n", testobjs[count].name);
+ result = 1;
+ }
+ }
+
+ /* Check whether all files are unloaded. */
+ for (map = MAPS; map != NULL; map = map->l_next)
+ if (map->l_type == lt_loaded)
+ {
+ printf ("name = \"%s\", direct_opencount = %d\n",
+ map->l_name, (int) map->l_direct_opencount);
+ result = 1;
+ }
+
+ return result;
+}
+
+
+extern int foo (int a);
+int
+foo (int a)
+{
+ return a - 1;
+}
diff --git a/libc/elf/ltglobmod1.c b/libc/elf/ltglobmod1.c
new file mode 100644
index 000000000..300fa9a89
--- /dev/null
+++ b/libc/elf/ltglobmod1.c
@@ -0,0 +1,7 @@
+extern int bar (void);
+
+int
+bar (void)
+{
+ return 42;
+}
diff --git a/libc/elf/ltglobmod2.c b/libc/elf/ltglobmod2.c
new file mode 100644
index 000000000..33f14cc98
--- /dev/null
+++ b/libc/elf/ltglobmod2.c
@@ -0,0 +1,33 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int bar (void);
+extern int foo (void);
+
+int
+foo (void)
+{
+ void *h;
+ int res;
+
+ /* Load ltglobalmod1 in the global namespace. */
+ h = dlopen ("ltglobmod1.so", RTLD_GLOBAL | RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("%s: cannot open %s: %s",
+ __FUNCTION__, "ltglobmod1.so", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ /* Call bar. This is undefined in the DSO. */
+ puts ("about to call `bar'");
+ fflush (stdout);
+ res = bar ();
+
+ printf ("bar returned %d\n", res);
+
+ dlclose (h);
+
+ return res != 42;
+}
diff --git a/libc/elf/multiload.c b/libc/elf/multiload.c
new file mode 100644
index 000000000..e85cc9658
--- /dev/null
+++ b/libc/elf/multiload.c
@@ -0,0 +1,105 @@
+#include <dlfcn.h>
+#include <errno.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+main (void)
+{
+ void *a;
+ void *b;
+ void *c;
+ void *d;
+ char *wd;
+ char *base;
+ char *buf;
+
+ mtrace ();
+
+ /* Change to the binary directory. */
+ if (chdir (OBJDIR) != 0)
+ {
+ printf ("cannot change to `%s': %m", OBJDIR);
+ exit (EXIT_FAILURE);
+ }
+
+ wd = getcwd (NULL, 0);
+ base = basename (wd);
+ buf = alloca (strlen (wd) + strlen (base) + 5 + sizeof "testobj1.so");
+
+ printf ("loading `%s'\n", "./testobj1.so");
+ a = dlopen ("./testobj1.so", RTLD_NOW);
+ if (a == NULL)
+ {
+ printf ("cannot load `./testobj1.so': %s\n", dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ stpcpy (stpcpy (stpcpy (buf, "../"), base), "/testobj1.so");
+ printf ("loading `%s'\n", buf);
+ b = dlopen (buf, RTLD_NOW);
+ if (b == NULL)
+ {
+ printf ("cannot load `%s': %s\n", buf, dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ stpcpy (stpcpy (buf, wd), "/testobj1.so");
+ printf ("loading `%s'\n", buf);
+ c = dlopen (buf, RTLD_NOW);
+ if (c == NULL)
+ {
+ printf ("cannot load `%s': %s\n", buf, dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ stpcpy (stpcpy (stpcpy (stpcpy (buf, wd), "/../"), base), "/testobj1.so");
+ printf ("loading `%s'\n", buf);
+ d = dlopen (buf, RTLD_NOW);
+ if (d == NULL)
+ {
+ printf ("cannot load `%s': %s\n", buf, dlerror ());
+ exit (EXIT_FAILURE);
+ }
+
+ if (a != b || b != c || c != d)
+ {
+ puts ("shared object loaded more than once");
+ exit (EXIT_FAILURE);
+ }
+
+ if (dlclose (a) != 0)
+ {
+ puts ("closing `a' failed");
+ exit (EXIT_FAILURE);
+ }
+ if (dlclose (b) != 0)
+ {
+ puts ("closing `a' failed");
+ exit (EXIT_FAILURE);
+ }
+ if (dlclose (c) != 0)
+ {
+ puts ("closing `a' failed");
+ exit (EXIT_FAILURE);
+ }
+ if (dlclose (d) != 0)
+ {
+ puts ("closing `a' failed");
+ exit (EXIT_FAILURE);
+ }
+
+ free (wd);
+
+ return 0;
+}
+
+extern int foo (int a);
+int
+foo (int a)
+{
+ return a;
+}
diff --git a/libc/elf/neededobj1.c b/libc/elf/neededobj1.c
new file mode 100644
index 000000000..eb55adab3
--- /dev/null
+++ b/libc/elf/neededobj1.c
@@ -0,0 +1,6 @@
+extern void c_function (void);
+
+void
+c_function (void)
+{
+}
diff --git a/libc/elf/neededobj2.c b/libc/elf/neededobj2.c
new file mode 100644
index 000000000..5ad8a51d6
--- /dev/null
+++ b/libc/elf/neededobj2.c
@@ -0,0 +1,8 @@
+extern void b_function (void);
+extern void c_function (void);
+
+void
+b_function (void)
+{
+ c_function();
+}
diff --git a/libc/elf/neededobj3.c b/libc/elf/neededobj3.c
new file mode 100644
index 000000000..da25329aa
--- /dev/null
+++ b/libc/elf/neededobj3.c
@@ -0,0 +1,10 @@
+extern void a_function (void);
+extern void b_function (void);
+extern void c_function (void);
+
+void
+a_function (void)
+{
+ b_function ();
+ c_function ();
+}
diff --git a/libc/elf/neededobj4.c b/libc/elf/neededobj4.c
new file mode 100644
index 000000000..3ea854004
--- /dev/null
+++ b/libc/elf/neededobj4.c
@@ -0,0 +1,12 @@
+extern void a_function (void);
+extern void b_function (void);
+extern void c_function (void);
+extern void d_function (void);
+
+void
+d_function (void)
+{
+ a_function ();
+ b_function ();
+ c_function ();
+}
diff --git a/libc/elf/neededobj5.c b/libc/elf/neededobj5.c
new file mode 100644
index 000000000..2d629b088
--- /dev/null
+++ b/libc/elf/neededobj5.c
@@ -0,0 +1,5 @@
+extern void a1_function (void);
+
+void a1_function (void)
+{
+}
diff --git a/libc/elf/neededobj6.c b/libc/elf/neededobj6.c
new file mode 100644
index 000000000..639b6f195
--- /dev/null
+++ b/libc/elf/neededobj6.c
@@ -0,0 +1,7 @@
+extern void a1_function (void);
+extern void a2_function (void);
+
+void a2_function (void)
+{
+ a1_function ();
+}
diff --git a/libc/elf/neededtest.c b/libc/elf/neededtest.c
new file mode 100644
index 000000000..3cea49931
--- /dev/null
+++ b/libc/elf/neededtest.c
@@ -0,0 +1,125 @@
+#include <dlfcn.h>
+#include <libintl.h>
+#include <link.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAPS ((struct link_map *) _r_debug.r_map)
+
+static int
+check_loaded_objects (const char **loaded)
+{
+ struct link_map *lm;
+ int n;
+ int *found = NULL;
+ int errors = 0;
+
+ for (n = 0; loaded[n]; n++)
+ /* NOTHING */;
+
+ if (n)
+ {
+ found = (int *) alloca (sizeof (int) * n);
+ memset (found, 0, sizeof (int) * n);
+ }
+
+ printf(" Name\n");
+ printf(" --------------------------------------------------------\n");
+ for (lm = MAPS; lm; lm = lm->l_next)
+ {
+ if (lm->l_name && lm->l_name[0])
+ printf(" %s, count = %d\n", lm->l_name, (int) lm->l_direct_opencount);
+ if (lm->l_type == lt_loaded && lm->l_name)
+ {
+ int match = 0;
+ for (n = 0; loaded[n] != NULL; n++)
+ {
+ if (strcmp (basename (loaded[n]), basename (lm->l_name)) == 0)
+ {
+ found[n] = 1;
+ match = 1;
+ break;
+ }
+ }
+
+ if (match == 0)
+ {
+ ++errors;
+ printf ("ERRORS: %s is not unloaded\n", lm->l_name);
+ }
+ }
+ }
+
+ for (n = 0; loaded[n] != NULL; n++)
+ {
+ if (found[n] == 0)
+ {
+ ++errors;
+ printf ("ERRORS: %s is not loaded\n", loaded[n]);
+ }
+ }
+
+ return errors;
+}
+
+int
+main (void)
+{
+ void *obj2[2];
+ void *obj3;
+ const char *loaded[] = { NULL, NULL, NULL, NULL };
+ int errors = 0;
+
+ printf ("\nThis is what is in memory now:\n");
+ errors += check_loaded_objects (loaded);
+
+ printf( "Loading shared object neededobj3.so\n");
+ obj3 = dlopen( "neededobj3.so", RTLD_LAZY);
+ if (obj3 == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ loaded[0] = "neededobj1.so";
+ loaded[1] = "neededobj2.so";
+ loaded[2] = "neededobj3.so";
+ errors += check_loaded_objects (loaded);
+
+ printf ("Now loading shared object neededobj2.so\n");
+ obj2[0] = dlopen ("neededobj2.so", RTLD_LAZY);
+ if (obj2[0] == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ errors += check_loaded_objects (loaded);
+
+ printf ("And loading shared object neededobj2.so again\n");
+ obj2[1] = dlopen ("neededobj2.so", RTLD_LAZY);
+ if (obj2[1] == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ errors += check_loaded_objects (loaded);
+
+ printf ("Closing neededobj2.so for the first time\n");
+ dlclose (obj2[0]);
+ errors += check_loaded_objects (loaded);
+
+ printf ("Closing neededobj3.so\n");
+ dlclose (obj3);
+ loaded[2] = NULL;
+ errors += check_loaded_objects (loaded);
+
+ printf ("Closing neededobj2.so for the second time\n");
+ dlclose (obj2[1]);
+ loaded[0] = NULL;
+ loaded[1] = NULL;
+ errors += check_loaded_objects (loaded);
+
+ if (errors != 0)
+ printf ("%d errors found\n", errors);
+ return errors;
+}
diff --git a/libc/elf/neededtest2.c b/libc/elf/neededtest2.c
new file mode 100644
index 000000000..17c75f2ba
--- /dev/null
+++ b/libc/elf/neededtest2.c
@@ -0,0 +1,118 @@
+#include <dlfcn.h>
+#include <libintl.h>
+#include <link.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAPS ((struct link_map *) _r_debug.r_map)
+
+static int
+check_loaded_objects (const char **loaded)
+{
+ struct link_map *lm;
+ int n;
+ int *found = NULL;
+ int errors = 0;
+
+ for (n = 0; loaded[n]; n++)
+ /* NOTHING */;
+
+ if (n)
+ {
+ found = (int *) alloca (sizeof (int) * n);
+ memset (found, 0, sizeof (int) * n);
+ }
+
+ printf(" Name\n");
+ printf(" --------------------------------------------------------\n");
+ for (lm = MAPS; lm; lm = lm->l_next)
+ {
+ if (lm->l_name && lm->l_name[0])
+ printf(" %s, count = %d\n", lm->l_name, (int) lm->l_direct_opencount);
+ if (lm->l_type == lt_loaded && lm->l_name)
+ {
+ int match = 0;
+ for (n = 0; loaded[n] != NULL; n++)
+ {
+ if (strcmp (basename (loaded[n]), basename (lm->l_name)) == 0)
+ {
+ found[n] = 1;
+ match = 1;
+ break;
+ }
+ }
+
+ if (match == 0)
+ {
+ ++errors;
+ printf ("ERRORS: %s is not unloaded\n", lm->l_name);
+ }
+ }
+ }
+
+ for (n = 0; loaded[n] != NULL; n++)
+ {
+ if (found[n] == 0)
+ {
+ ++errors;
+ printf ("ERRORS: %s is not loaded\n", loaded[n]);
+ }
+ }
+
+ return errors;
+}
+
+int
+main (void)
+{
+ void *obj2;
+ void *obj3[2];
+ const char *loaded[] = { NULL, NULL, NULL, NULL };
+ int errors = 0;
+
+ printf ("\nThis is what is in memory now:\n");
+ errors += check_loaded_objects (loaded);
+ printf ("\nLoading shared object neededobj2.so\n");
+ obj2 = dlopen ("neededobj2.so", RTLD_LAZY);
+ if (obj2 == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ loaded[0] = "neededobj1.so";
+ loaded[1] = "neededobj2.so";
+ errors += check_loaded_objects (loaded);
+ printf ("\nLoading shared object neededobj3.so\n");
+ obj3[0] = dlopen( "neededobj3.so", RTLD_LAZY);
+ if (obj3[0] == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ loaded[2] = "neededobj3.so";
+ errors += check_loaded_objects (loaded);
+ printf ("\nNow loading shared object neededobj3.so again\n");
+ obj3[1] = dlopen ("neededobj3.so", RTLD_LAZY);
+ if (obj3[1] == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ errors += check_loaded_objects (loaded);
+ printf ("\nClosing neededobj3.so once\n");
+ dlclose (obj3[0]);
+ errors += check_loaded_objects (loaded);
+ printf ("\nClosing neededobj2.so\n");
+ dlclose (obj2);
+ errors += check_loaded_objects (loaded);
+ printf ("\nClosing neededobj3.so for the second time\n");
+ dlclose (obj3[1]);
+ loaded[0] = NULL;
+ loaded[1] = NULL;
+ loaded[2] = NULL;
+ errors += check_loaded_objects (loaded);
+ if (errors != 0)
+ printf ("%d errors found\n", errors);
+ return errors;
+}
diff --git a/libc/elf/neededtest3.c b/libc/elf/neededtest3.c
new file mode 100644
index 000000000..41970cf2c
--- /dev/null
+++ b/libc/elf/neededtest3.c
@@ -0,0 +1,129 @@
+#include <dlfcn.h>
+#include <libintl.h>
+#include <link.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAPS ((struct link_map *) _r_debug.r_map)
+
+static int
+check_loaded_objects (const char **loaded)
+{
+ struct link_map *lm;
+ int n;
+ int *found = NULL;
+ int errors = 0;
+
+ for (n = 0; loaded[n]; n++)
+ /* NOTHING */;
+
+ if (n)
+ {
+ found = (int *) alloca (sizeof (int) * n);
+ memset (found, 0, sizeof (int) * n);
+ }
+
+ printf(" Name\n");
+ printf(" --------------------------------------------------------\n");
+ for (lm = MAPS; lm; lm = lm->l_next)
+ {
+ if (lm->l_name && lm->l_name[0])
+ printf(" %s, count = %d\n", lm->l_name, (int) lm->l_direct_opencount);
+ if (lm->l_type == lt_loaded && lm->l_name)
+ {
+ int match = 0;
+ for (n = 0; loaded[n] != NULL; n++)
+ {
+ if (strcmp (basename (loaded[n]), basename (lm->l_name)) == 0)
+ {
+ found[n] = 1;
+ match = 1;
+ break;
+ }
+ }
+
+ if (match == 0)
+ {
+ ++errors;
+ printf ("ERRORS: %s is not unloaded\n", lm->l_name);
+ }
+ }
+ }
+
+ for (n = 0; loaded[n] != NULL; n++)
+ {
+ if (found[n] == 0)
+ {
+ ++errors;
+ printf ("ERRORS: %s is not loaded\n", loaded[n]);
+ }
+ }
+
+ return errors;
+}
+
+int
+main (void)
+{
+ void *obj2;
+ void *obj3;
+ void *obj4;
+ const char *loaded[] = { NULL, NULL, NULL, NULL, NULL };
+ int errors = 0;
+
+ printf ("\nThis is what is in memory now:\n");
+ errors += check_loaded_objects (loaded);
+
+ printf ("Now loading shared object neededobj2.so\n");
+ obj2 = dlopen ("neededobj2.so", RTLD_LAZY);
+ if (obj2 == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ loaded[0] = "neededobj1.so";
+ loaded[1] = "neededobj2.so";
+ errors += check_loaded_objects (loaded);
+
+ printf( "Loading shared object neededobj3.so\n");
+ obj3 = dlopen( "neededobj3.so", RTLD_LAZY);
+ if (obj3 == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ loaded[2] = "neededobj3.so";
+ errors += check_loaded_objects (loaded);
+
+
+ printf( "Loading shared object neededobj4.so\n");
+ obj4 = dlopen( "neededobj4.so", RTLD_LAZY);
+ if (obj4 == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ loaded[3] = "neededobj4.so";
+ errors += check_loaded_objects (loaded);
+
+ printf ("Closing neededobj2.so\n");
+ dlclose (obj2);
+ errors += check_loaded_objects (loaded);
+
+ printf ("Closing neededobj3.so\n");
+ dlclose (obj3);
+ errors += check_loaded_objects (loaded);
+
+ printf ("Closing neededobj4.so\n");
+ dlclose (obj4);
+ loaded[0] = NULL;
+ loaded[1] = NULL;
+ loaded[2] = NULL;
+ loaded[3] = NULL;
+ errors += check_loaded_objects (loaded);
+
+ if (errors != 0)
+ printf ("%d errors found\n", errors);
+ return errors;
+}
diff --git a/libc/elf/neededtest4.c b/libc/elf/neededtest4.c
new file mode 100644
index 000000000..bd79341fb
--- /dev/null
+++ b/libc/elf/neededtest4.c
@@ -0,0 +1,158 @@
+#include <dlfcn.h>
+#include <libintl.h>
+#include <link.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAPS ((struct link_map *) _r_debug.r_map)
+
+static int
+check_loaded_objects (const char **loaded)
+{
+ struct link_map *lm;
+ int n;
+ int *found = NULL;
+ int errors = 0;
+
+ for (n = 0; loaded[n]; n++)
+ /* NOTHING */;
+
+ if (n)
+ {
+ found = (int *) alloca (sizeof (int) * n);
+ memset (found, 0, sizeof (int) * n);
+ }
+
+ printf(" Name\n");
+ printf(" --------------------------------------------------------\n");
+ for (lm = MAPS; lm; lm = lm->l_next)
+ {
+ if (lm->l_name && lm->l_name[0])
+ printf(" %s, count = %d\n", lm->l_name, (int) lm->l_direct_opencount);
+ if (lm->l_type == lt_loaded && lm->l_name)
+ {
+ int match = 0;
+ for (n = 0; loaded[n] != NULL; n++)
+ {
+ if (strcmp (basename (loaded[n]), basename (lm->l_name)) == 0)
+ {
+ found[n] = 1;
+ match = 1;
+ break;
+ }
+ }
+
+ if (match == 0)
+ {
+ ++errors;
+ printf ("ERRORS: %s is not unloaded\n", lm->l_name);
+ }
+ }
+ }
+
+ for (n = 0; loaded[n] != NULL; n++)
+ {
+ if (found[n] == 0)
+ {
+ ++errors;
+ printf ("ERRORS: %s is not loaded\n", loaded[n]);
+ }
+ }
+
+ return errors;
+}
+
+extern void c_function (void);
+extern char *dirname (__const char *__filename);
+
+int
+main (int argc, char **argv)
+{
+ void *obj;
+ const char *loaded[] = { NULL, NULL, NULL};
+ int errors = 0;
+ void (*f) (void);
+ const char *dir = dirname (argv [0]);
+ char *oldfilename;
+ char *newfilename;
+
+ c_function ();
+
+ printf ("\nThis is what is in memory now:\n");
+ errors += check_loaded_objects (loaded);
+
+ printf( "Loading shared object neededobj6.so\n");
+ obj = dlopen( "neededobj6.so", RTLD_LAZY);
+ if (obj == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ f = dlsym (obj, "a2_function");
+ if (f == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ f ();
+ loaded[0] = "neededobj5.so";
+ loaded[1] = "neededobj6.so";
+ errors += check_loaded_objects (loaded);
+
+ printf ("Closing neededobj6.so\n");
+ dlclose (obj);
+ loaded[0] = NULL;
+ errors += check_loaded_objects (loaded);
+
+ printf ("Rename neededobj5.so\n");
+ oldfilename = alloca (strlen (dir) + 1 + sizeof ("neededobj5.so"));
+ strcpy (oldfilename, dir);
+ strcat (oldfilename, "/");
+ strcat (oldfilename, "neededobj5.so");
+ newfilename = alloca (strlen (oldfilename) + sizeof (".renamed"));
+ strcpy (newfilename, oldfilename);
+ strcat (newfilename, ".renamed");
+ if (rename (oldfilename, newfilename))
+ {
+ perror ("rename");
+ exit (1);
+ }
+
+ printf( "Loading shared object neededobj6.so\n");
+ obj = dlopen( "neededobj6.so", RTLD_LAZY);
+ if (obj == NULL)
+ printf ("%s\n", dlerror ());
+ else
+ {
+ printf ("neededobj6.so should fail to load\n");
+ exit (1);
+ }
+
+ printf( "Loading shared object neededobj1.so\n");
+ obj = dlopen( "neededobj1.so", RTLD_LAZY);
+ if (obj == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ errors += check_loaded_objects (loaded);
+ f = dlsym (obj, "c_function");
+ if (f == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ f ();
+
+ printf ("Restore neededobj5.so\n");
+ if (rename (newfilename, oldfilename))
+ {
+ perror ("rename");
+ exit (1);
+ }
+
+ if (errors != 0)
+ printf ("%d errors found\n", errors);
+ return errors;
+}
diff --git a/libc/elf/next.c b/libc/elf/next.c
new file mode 100644
index 000000000..6a3670c21
--- /dev/null
+++ b/libc/elf/next.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+
+
+extern int successful_rtld_next_test (void);
+extern void *failing_rtld_next_use (void);
+
+
+static int
+do_test (void)
+{
+ int result;
+ void *addr;
+
+ /* First try call a function which uses RTLD_NEXT and calls that
+ function. */
+ result = successful_rtld_next_test ();
+ if (result == 42)
+ {
+ puts ("RTLD_NEXT seems to work for existing functions");
+ result = 0;
+ }
+ else
+ {
+ printf ("Heh? `successful_rtld_next_test' returned %d\n", result);
+ result = 1;
+ }
+
+ /* Next try a function which tries to get a function with RTLD_NEXT
+ but that fails. This dlsym() call should return a NULL pointer
+ and do nothing else. */
+ addr = failing_rtld_next_use ();
+ if (addr == NULL)
+ puts ("dlsym returned NULL for non-existing function. Good");
+ else
+ {
+ puts ("dlsym found something !?");
+ result = 1;
+ }
+
+ return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/nextmod1.c b/libc/elf/nextmod1.c
new file mode 100644
index 000000000..56de3536a
--- /dev/null
+++ b/libc/elf/nextmod1.c
@@ -0,0 +1,30 @@
+#include <dlfcn.h>
+
+extern int successful_rtld_next_test (void);
+extern void *failing_rtld_next_use (void);
+
+int nextmod1_dummy_var;
+
+int
+successful_rtld_next_test (void)
+{
+ int (*fp) (void);
+
+ /* Get the next function... */
+ fp = (int (*) (void)) dlsym (RTLD_NEXT, __FUNCTION__);
+
+ /* ...and simply call it. */
+ return fp ();
+}
+
+
+void *
+failing_rtld_next_use (void)
+{
+ void *ret = dlsym (RTLD_NEXT, __FUNCTION__);
+
+ /* Ensure we are not tail call optimized, because then RTLD_NEXT
+ might return this function. */
+ ++nextmod1_dummy_var;
+ return ret;
+}
diff --git a/libc/elf/nextmod2.c b/libc/elf/nextmod2.c
new file mode 100644
index 000000000..b2c435f34
--- /dev/null
+++ b/libc/elf/nextmod2.c
@@ -0,0 +1,10 @@
+/* Very elaborated function. */
+
+extern int successful_rtld_next_test (void);
+
+
+int
+successful_rtld_next_test (void)
+{
+ return 42;
+}
diff --git a/libc/elf/nodel2mod1.c b/libc/elf/nodel2mod1.c
new file mode 100644
index 000000000..acddc4cf8
--- /dev/null
+++ b/libc/elf/nodel2mod1.c
@@ -0,0 +1,19 @@
+#include <stdlib.h>
+void
+foo (void)
+{
+ exit (0);
+}
+
+void
+__attribute__((destructor))
+bar (void)
+{
+ static int i;
+ foo ();
+ ++i;
+}
+void
+baz (void)
+{
+}
diff --git a/libc/elf/nodel2mod2.c b/libc/elf/nodel2mod2.c
new file mode 100644
index 000000000..d0020240a
--- /dev/null
+++ b/libc/elf/nodel2mod2.c
@@ -0,0 +1,7 @@
+void
+__attribute__((constructor))
+xxx (void)
+{
+ extern void baz (void);
+ baz ();
+}
diff --git a/libc/elf/nodel2mod3.c b/libc/elf/nodel2mod3.c
new file mode 100644
index 000000000..6d1a0d47b
--- /dev/null
+++ b/libc/elf/nodel2mod3.c
@@ -0,0 +1 @@
+int x;
diff --git a/libc/elf/nodelete.c b/libc/elf/nodelete.c
new file mode 100644
index 000000000..78364a278
--- /dev/null
+++ b/libc/elf/nodelete.c
@@ -0,0 +1,211 @@
+#include <dlfcn.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+
+
+static sigjmp_buf jmpbuf;
+
+
+int fini_ran;
+
+
+static void
+__attribute__ ((noreturn))
+handler (int sig)
+{
+ siglongjmp (jmpbuf, 1);
+}
+
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+ /* We are testing the two possibilities to mark an object as not deletable:
+ - marked on the linker commandline with `-z nodelete'
+ - with the RTLD_NODELETE flag at dlopen()-time.
+
+ The test we are performing should be safe. We are loading the objects,
+ get the address of variables in the respective object, unload the object
+ and then try to read the variable. If the object is unloaded this
+ should lead to an segmentation fault. */
+ int result = 0;
+ void *p;
+ struct sigaction sa;
+
+ sa.sa_handler = handler;
+ sigfillset (&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction (SIGSEGV, &sa, NULL) == -1)
+ printf ("cannot install signal handler: %m\n");
+
+ p = dlopen ("nodelmod1.so", RTLD_LAZY);
+ if (p == NULL)
+ {
+ printf ("failed to load \"nodelmod1.so\": %s\n", dlerror ());
+ result = 1;
+ }
+ else
+ {
+ int *varp;
+
+ puts ("succeeded loading \"nodelmod1.so\"");
+
+ varp = dlsym (p, "var1");
+ if (varp == NULL)
+ {
+ puts ("failed to get address of \"var1\" in \"nodelmod1.so\"");
+ result = 1;
+ }
+ else
+ {
+ *varp = 20000720;
+
+ /* Now close the object. */
+ fini_ran = 0;
+ if (dlclose (p) != 0)
+ {
+ puts ("failed to close \"nodelmod1.so\"");
+ result = 1;
+ }
+ else if (! sigsetjmp (jmpbuf, 1))
+ {
+ /* Access the variable again. */
+ if (*varp != 20000720)
+ {
+ puts ("\"var1\" value not correct");
+ result = 1;
+ }
+ else if (fini_ran != 0)
+ {
+ puts ("destructor of \"nodelmod1.so\" ran");
+ result = 1;
+ }
+ else
+ puts ("-z nodelete test succeeded");
+ }
+ else
+ {
+ /* We caught an segmentation fault. */
+ puts ("\"nodelmod1.so\" got deleted");
+ result = 1;
+ }
+ }
+ }
+
+ p = dlopen ("nodelmod2.so", RTLD_LAZY | RTLD_NODELETE);
+ if (p == NULL)
+ {
+ printf ("failed to load \"nodelmod2.so\": %s\n", dlerror ());
+ result = 1;
+ }
+ else
+ {
+ int *varp;
+
+ puts ("succeeded loading \"nodelmod2.so\"");
+
+ varp = dlsym (p, "var2");
+ if (varp == NULL)
+ {
+ puts ("failed to get address of \"var2\" in \"nodelmod2.so\"");
+ result = 1;
+ }
+ else
+ {
+ *varp = 42;
+
+ /* Now close the object. */
+ fini_ran = 0;
+ if (dlclose (p) != 0)
+ {
+ puts ("failed to close \"nodelmod2.so\"");
+ result = 1;
+ }
+ else if (! sigsetjmp (jmpbuf, 1))
+ {
+ /* Access the variable again. */
+ if (*varp != 42)
+ {
+ puts ("\"var2\" value not correct");
+ result = 1;
+ }
+ else if (fini_ran != 0)
+ {
+ puts ("destructor of \"nodelmod2.so\" ran");
+ result = 1;
+ }
+ else
+ puts ("RTLD_NODELETE test succeeded");
+ }
+ else
+ {
+ /* We caught an segmentation fault. */
+ puts ("\"nodelmod2.so\" got deleted");
+ result = 1;
+ }
+ }
+ }
+
+ p = dlopen ("nodelmod3.so", RTLD_LAZY);
+ if (p == NULL)
+ {
+ printf ("failed to load \"nodelmod3.so\": %s\n", dlerror ());
+ result = 1;
+ }
+ else
+ {
+ int *(*fctp) (void);
+
+ puts ("succeeded loading \"nodelmod3.so\"");
+
+ fctp = dlsym (p, "addr");
+ if (fctp == NULL)
+ {
+ puts ("failed to get address of \"addr\" in \"nodelmod3.so\"");
+ result = 1;
+ }
+ else
+ {
+ int *varp = fctp ();
+
+ *varp = -1;
+
+ /* Now close the object. */
+ fini_ran = 0;
+ if (dlclose (p) != 0)
+ {
+ puts ("failed to close \"nodelmod3.so\"");
+ result = 1;
+ }
+ else if (! sigsetjmp (jmpbuf, 1))
+ {
+ /* Access the variable again. */
+ if (*varp != -1)
+ {
+ puts ("\"var_in_mod4\" value not correct");
+ result = 1;
+ }
+ else if (fini_ran != 0)
+ {
+ puts ("destructor of \"nodelmod4.so\" ran");
+ result = 1;
+ }
+ else
+ puts ("-z nodelete in dependency succeeded");
+ }
+ else
+ {
+ /* We caught an segmentation fault. */
+ puts ("\"nodelmod4.so\" got deleted");
+ result = 1;
+ }
+ }
+ }
+
+ return result;
+}
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/nodelete2.c b/libc/elf/nodelete2.c
new file mode 100644
index 000000000..b3d7e31a0
--- /dev/null
+++ b/libc/elf/nodelete2.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+int
+main (void)
+{
+ void *handle = dlopen ("nodel2mod3.so", RTLD_LAZY);
+ if (handle == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ dlclose (handle);
+ exit (1);
+}
diff --git a/libc/elf/nodelmod1.c b/libc/elf/nodelmod1.c
new file mode 100644
index 000000000..fc24a7b17
--- /dev/null
+++ b/libc/elf/nodelmod1.c
@@ -0,0 +1,9 @@
+int var1 = 42;
+
+static void
+__attribute__ ((__destructor__))
+destr (void)
+{
+ extern int fini_ran;
+ fini_ran = 1;
+}
diff --git a/libc/elf/nodelmod2.c b/libc/elf/nodelmod2.c
new file mode 100644
index 000000000..6bd7108a1
--- /dev/null
+++ b/libc/elf/nodelmod2.c
@@ -0,0 +1,9 @@
+int var2 = 100;
+
+static void
+__attribute__ ((__destructor__))
+destr (void)
+{
+ extern int fini_ran;
+ fini_ran = 1;
+}
diff --git a/libc/elf/nodelmod3.c b/libc/elf/nodelmod3.c
new file mode 100644
index 000000000..817c94db6
--- /dev/null
+++ b/libc/elf/nodelmod3.c
@@ -0,0 +1,8 @@
+extern int var_in_mod4;
+extern int *addr (void);
+
+int *
+addr (void)
+{
+ return &var_in_mod4;
+}
diff --git a/libc/elf/nodelmod4.c b/libc/elf/nodelmod4.c
new file mode 100644
index 000000000..2cca43a6e
--- /dev/null
+++ b/libc/elf/nodelmod4.c
@@ -0,0 +1,9 @@
+int var_in_mod4 = 99;
+
+static void
+__attribute__ ((__destructor__))
+destr (void)
+{
+ extern int fini_ran;
+ fini_ran = 1;
+}
diff --git a/libc/elf/nodlopen.c b/libc/elf/nodlopen.c
new file mode 100644
index 000000000..642bdb301
--- /dev/null
+++ b/libc/elf/nodlopen.c
@@ -0,0 +1,15 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+int
+main (void)
+{
+ if (dlopen ("nodlopenmod.so", RTLD_LAZY) != NULL)
+ {
+ puts ("opening \"nodlopenmod.so\" succeeded, FAIL");
+ return 1;
+ }
+
+ puts ("opening \"nodlopenmod.so\" failed, OK");
+ return 0;
+}
diff --git a/libc/elf/nodlopen2.c b/libc/elf/nodlopen2.c
new file mode 100644
index 000000000..a223f3683
--- /dev/null
+++ b/libc/elf/nodlopen2.c
@@ -0,0 +1,15 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+int
+main (void)
+{
+ if (dlopen ("nodlopenmod2.so", RTLD_LAZY) != NULL)
+ {
+ puts ("opening \"nodlopenmod2.so\" succeeded, FAIL");
+ return 1;
+ }
+
+ puts ("opening \"nodlopenmod2.so\" failed, OK");
+ return 0;
+}
diff --git a/libc/elf/nodlopenmod.c b/libc/elf/nodlopenmod.c
new file mode 100644
index 000000000..4bcf8c978
--- /dev/null
+++ b/libc/elf/nodlopenmod.c
@@ -0,0 +1 @@
+int a = 42;
diff --git a/libc/elf/nodlopenmod2.c b/libc/elf/nodlopenmod2.c
new file mode 100644
index 000000000..e72ae53e9
--- /dev/null
+++ b/libc/elf/nodlopenmod2.c
@@ -0,0 +1,9 @@
+extern int a;
+
+extern int foo (void);
+
+int
+foo (void)
+{
+ return a;
+}
diff --git a/libc/elf/noload.c b/libc/elf/noload.c
new file mode 100644
index 000000000..9281ec714
--- /dev/null
+++ b/libc/elf/noload.c
@@ -0,0 +1,71 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+int
+main (void)
+{
+ int result = 0;
+
+ /* First try to load an object which is a dependency. This should
+ succeed. */
+ if (dlopen ("testobj1.so", RTLD_LAZY | RTLD_NOLOAD) == NULL)
+ {
+ printf ("cannot open \"testobj1.so\": %s\n", dlerror ());
+ result = 1;
+ }
+ else
+ puts ("loading \"testobj1.so\" succeeded, OK");
+
+ /* Now try loading an object which is not already loaded. */
+ if (dlopen ("testobj5.so", RTLD_LAZY | RTLD_NOLOAD) != NULL)
+ {
+ puts ("succeeded in loading \"testobj5.so\"");
+ result = 1;
+ }
+ else
+ {
+ /* Load the object and run the same test again. */
+ void *p;
+
+ puts ("\"testobj5.so\" wasn't loaded and RTLD_NOLOAD prevented it, OK");
+
+ p = dlopen ("testobj5.so", RTLD_LAZY);
+
+ if (p == NULL)
+ {
+ printf ("cannot open \"testobj5.so\" without RTLD_NOLOAD: %s\n",
+ dlerror ());
+ result = 1;
+ }
+ else
+ {
+ puts ("loading \"testobj5.so\" succeeded, OK");
+
+ if (dlopen ("testobj5.so", RTLD_LAZY | RTLD_NOLOAD) == NULL)
+ {
+ printf ("cannot open \"testobj5.so\": %s\n", dlerror ());
+ result = 1;
+ }
+ else
+ puts ("loading \"testobj5.so\" with RTLD_NOLOAD succeeded, OK");
+
+ if (dlclose (p) != 0)
+ {
+ printf ("cannot close \"testobj5.so\": %s\n", dlerror ());
+ result = 1;
+ }
+ else
+ puts ("closing \"testobj5.so\" succeeded, OK");
+ }
+ }
+
+ return result;
+}
+
+
+extern int foo (int a);
+int
+foo (int a)
+{
+ return 42 + a;
+}
diff --git a/libc/elf/order.c b/libc/elf/order.c
new file mode 100644
index 000000000..ca617cbc0
--- /dev/null
+++ b/libc/elf/order.c
@@ -0,0 +1,25 @@
+#include <unistd.h>
+
+void init (void) __attribute__ ((constructor));
+void
+__attribute__ ((constructor))
+init (void)
+{
+ write (1, "4", 1);
+}
+
+void fini (void) __attribute__ ((destructor));
+void
+__attribute__ ((destructor))
+fini (void)
+{
+ write (1, "5", 1);
+}
+
+extern int dep1 (void);
+
+int
+main (void)
+{
+ return dep1 () != 42;
+}
diff --git a/libc/elf/order2.c b/libc/elf/order2.c
new file mode 100644
index 000000000..3dbfdd153
--- /dev/null
+++ b/libc/elf/order2.c
@@ -0,0 +1,46 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+
+int call_puts;
+
+static int
+do_test (void)
+{
+ call_puts = 1;
+
+ void *h1 = dlopen ("$ORIGIN/order2mod1.so", RTLD_LAZY | RTLD_GLOBAL);
+ if (h1 == NULL)
+ {
+ puts ("cannot load order2mod1");
+ return 1;
+ }
+ void *h2 = dlopen ("$ORIGIN/order2mod2.so", RTLD_LAZY);
+ if (h2 == NULL)
+ {
+ puts ("cannot load order2mod2");
+ return 1;
+ }
+ if (dlclose (h1) != 0)
+ {
+ puts ("dlclose order2mod1 failed");
+ return 1;
+ }
+ if (dlclose (h2) != 0)
+ {
+ puts ("dlclose order2mod2 failed");
+ return 1;
+ }
+ return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+static void
+__attribute__ ((destructor))
+fini (void)
+{
+ if (call_puts)
+ puts ("5");
+}
diff --git a/libc/elf/order2mod1.c b/libc/elf/order2mod1.c
new file mode 100644
index 000000000..b695db29b
--- /dev/null
+++ b/libc/elf/order2mod1.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+static void
+__attribute__ ((destructor))
+fini (void)
+{
+ putchar ('1');
+}
diff --git a/libc/elf/order2mod2.c b/libc/elf/order2mod2.c
new file mode 100644
index 000000000..026cd2acc
--- /dev/null
+++ b/libc/elf/order2mod2.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+
+extern int foo (void);
+extern int bar (void);
+
+void
+__attribute__ ((constructor))
+init (void)
+{
+ (void) (foo () - bar ());
+}
+
+static void
+__attribute__ ((destructor))
+fini (void)
+{
+ putchar ('2');
+}
diff --git a/libc/elf/order2mod3.c b/libc/elf/order2mod3.c
new file mode 100644
index 000000000..7913a7992
--- /dev/null
+++ b/libc/elf/order2mod3.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+int
+bar (void)
+{
+ return 1;
+}
+
+static void
+__attribute__ ((destructor))
+fini (void)
+{
+ putchar ('4');
+}
diff --git a/libc/elf/order2mod4.c b/libc/elf/order2mod4.c
new file mode 100644
index 000000000..4f2026f04
--- /dev/null
+++ b/libc/elf/order2mod4.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+extern int bar (void);
+
+int
+foo (void)
+{
+ return 42 + bar ();
+}
+
+static void
+__attribute__ ((destructor))
+fini (void)
+{
+ putchar ('3');
+}
diff --git a/libc/elf/origtest.c b/libc/elf/origtest.c
new file mode 100644
index 000000000..1cacabcc3
--- /dev/null
+++ b/libc/elf/origtest.c
@@ -0,0 +1,39 @@
+#include <dlfcn.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ void *h;
+ int (*fp) (int);
+ int res;
+
+ h = dlopen ("${ORIGIN}/testobj1.so", RTLD_LAZY);
+ if (h == NULL)
+ error (EXIT_FAILURE, 0, "while loading `%s': %s", "testobj1.so",
+ dlerror ());
+
+ fp = dlsym (h, "obj1func1");
+ if (fp == NULL)
+ error (EXIT_FAILURE, 0, "getting `obj1func1' in `%s': %s",
+ "testobj1.so", dlerror ());
+
+ res = fp (10);
+ printf ("fp(10) = %d\n", res);
+
+ if (dlclose (h) != 0)
+ error (EXIT_FAILURE, 0, "while close `%s': %s",
+ "testobj1.so", dlerror ());
+
+ return res != 42;
+}
+
+
+extern int foo (int a);
+int
+foo (int a)
+{
+ return a + 10;
+}
diff --git a/libc/elf/pathoptobj.c b/libc/elf/pathoptobj.c
new file mode 100644
index 000000000..a452c2d7d
--- /dev/null
+++ b/libc/elf/pathoptobj.c
@@ -0,0 +1,8 @@
+extern int in_renamed (int);
+
+
+int
+in_renamed (int a)
+{
+ return a - 10;
+}
diff --git a/libc/elf/preloadtest.c b/libc/elf/preloadtest.c
new file mode 100644
index 000000000..7ea10b9b5
--- /dev/null
+++ b/libc/elf/preloadtest.c
@@ -0,0 +1,19 @@
+#include <stdio.h>
+
+#include "testobj.h"
+
+int
+main (void)
+{
+ int res = preload (42);
+
+ printf ("preload (42) = %d, %s\n", res, res == 92 ? "ok" : "wrong");
+
+ return res != 92;
+}
+
+int
+foo (int a)
+{
+ return a;
+}
diff --git a/libc/elf/readelflib.c b/libc/elf/readelflib.c
new file mode 100644
index 000000000..26444ad6b
--- /dev/null
+++ b/libc/elf/readelflib.c
@@ -0,0 +1,220 @@
+/* Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Andreas Jaeger <aj@suse.de>, 1999 and
+ Jakub Jelinek <jakub@redhat.com>, 1999.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/* This code is a heavily simplified version of the readelf program
+ that's part of the current binutils development version. For architectures
+ which need to handle both 32bit and 64bit ELF libraries, this file is
+ included twice for each arch size. */
+
+/* check_ptr checks that a pointer is in the mmaped file and doesn't
+ point outside it. */
+#undef check_ptr
+#define check_ptr(ptr) \
+do \
+ { \
+ if ((void *)(ptr) < file_contents \
+ || (void *)(ptr) > (file_contents+file_length)) \
+ { \
+ error (0, 0, _("file %s is truncated\n"), file_name); \
+ return 1; \
+ } \
+ } \
+ while (0);
+
+/* Returns 0 if everything is ok, != 0 in case of error. */
+int
+process_elf_file (const char *file_name, const char *lib, int *flag,
+ unsigned int *osversion, char **soname, void *file_contents,
+ size_t file_length)
+{
+ int i;
+ unsigned int j;
+ ElfW(Addr) loadaddr;
+ unsigned int dynamic_addr;
+ size_t dynamic_size;
+ char *program_interpreter;
+
+ ElfW(Ehdr) *elf_header;
+ ElfW(Phdr) *elf_pheader, *segment;
+ ElfW(Dyn) *dynamic_segment, *dyn_entry;
+ char *dynamic_strings;
+
+ elf_header = (ElfW(Ehdr) *) file_contents;
+ *osversion = 0;
+
+ if (elf_header->e_ident [EI_CLASS] != ElfW (CLASS))
+ {
+ if (opt_verbose)
+ {
+ if (elf_header->e_ident [EI_CLASS] == ELFCLASS32)
+ error (0, 0, _("%s is a 32 bit ELF file.\n"), file_name);
+ else if (elf_header->e_ident [EI_CLASS] == ELFCLASS64)
+ error (0, 0, _("%s is a 64 bit ELF file.\n"), file_name);
+ else
+ error (0, 0, _("Unknown ELFCLASS in file %s.\n"), file_name);
+ }
+ return 1;
+ }
+
+ if (elf_header->e_type != ET_DYN)
+ {
+ error (0, 0, _("%s is not a shared object file (Type: %d).\n"), file_name,
+ elf_header->e_type);
+ return 1;
+ }
+
+ /* Get information from elf program header. */
+ elf_pheader = (ElfW(Phdr) *) (elf_header->e_phoff + file_contents);
+ check_ptr (elf_pheader);
+
+ /* The library is an elf library, now search for soname and
+ libc5/libc6. */
+ *flag = FLAG_ELF;
+
+ loadaddr = -1;
+ dynamic_addr = 0;
+ dynamic_size = 0;
+ program_interpreter = NULL;
+ for (i = 0, segment = elf_pheader;
+ i < elf_header->e_phnum; i++, segment++)
+ {
+ check_ptr (segment);
+
+ switch (segment->p_type)
+ {
+ case PT_LOAD:
+ if (loadaddr == (ElfW(Addr)) -1)
+ loadaddr = segment->p_vaddr - segment->p_offset;
+ break;
+
+ case PT_DYNAMIC:
+ if (dynamic_addr)
+ error (0, 0, _("more than one dynamic segment\n"));
+
+ dynamic_addr = segment->p_offset;
+ dynamic_size = segment->p_filesz;
+ break;
+
+ case PT_INTERP:
+ program_interpreter = (char *) (file_contents + segment->p_offset);
+ check_ptr (program_interpreter);
+
+ /* Check if this is enough to classify the binary. */
+ for (j = 0; j < sizeof (interpreters) / sizeof (interpreters [0]);
+ ++j)
+ if (strcmp (program_interpreter, interpreters[j].soname) == 0)
+ {
+ *flag = interpreters[j].flag;
+ break;
+ }
+ break;
+
+ case PT_NOTE:
+ if (!*osversion && segment->p_filesz == 32 && segment->p_align >= 4)
+ {
+ ElfW(Word) *abi_note = (ElfW(Word) *) (file_contents
+ + segment->p_offset);
+ if (abi_note [0] == 4 && abi_note [1] == 16 && abi_note [2] == 1
+ && memcmp (abi_note + 3, "GNU", 4) == 0)
+ *osversion = (abi_note [4] << 24) |
+ ((abi_note [5] & 0xff) << 16) |
+ ((abi_note [6] & 0xff) << 8) |
+ (abi_note [7] & 0xff);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ }
+ if (loadaddr == (ElfW(Addr)) -1)
+ {
+ /* Very strange. */
+ loadaddr = 0;
+ }
+
+ /* Now we can read the dynamic sections. */
+ if (dynamic_size == 0)
+ return 1;
+
+ dynamic_segment = (ElfW(Dyn) *) (file_contents + dynamic_addr);
+ check_ptr (dynamic_segment);
+
+ /* Find the string table. */
+ dynamic_strings = NULL;
+ for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
+ ++dyn_entry)
+ {
+ check_ptr (dyn_entry);
+ if (dyn_entry->d_tag == DT_STRTAB)
+ {
+ dynamic_strings = (char *) (file_contents + dyn_entry->d_un.d_val - loadaddr);
+ check_ptr (dynamic_strings);
+ break;
+ }
+ }
+
+ if (dynamic_strings == NULL)
+ return 1;
+
+ /* Now read the DT_NEEDED and DT_SONAME entries. */
+ for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL;
+ ++dyn_entry)
+ {
+ if (dyn_entry->d_tag == DT_NEEDED || dyn_entry->d_tag == DT_SONAME)
+ {
+ char *name = dynamic_strings + dyn_entry->d_un.d_val;
+ check_ptr (name);
+
+ if (dyn_entry->d_tag == DT_NEEDED)
+ {
+
+ if (*flag == FLAG_ELF)
+ {
+ /* Check if this is enough to classify the binary. */
+ for (j = 0;
+ j < sizeof (known_libs) / sizeof (known_libs [0]);
+ ++j)
+ if (strcmp (name, known_libs [j].soname) == 0)
+ {
+ *flag = known_libs [j].flag;
+ break;
+ }
+ }
+ }
+
+ else if (dyn_entry->d_tag == DT_SONAME)
+ *soname = xstrdup (name);
+
+ /* Do we have everything we need? */
+ if (*soname && *flag != FLAG_ELF)
+ return 0;
+ }
+ }
+
+ /* We reach this point only if the file doesn't contain a DT_SONAME
+ or if we can't classify the library. If it doesn't have a
+ soname, return the name of the library. */
+ if (*soname == NULL)
+ *soname = xstrdup (lib);
+
+ return 0;
+}
diff --git a/libc/elf/readlib.c b/libc/elf/readlib.c
new file mode 100644
index 000000000..8896bbdac
--- /dev/null
+++ b/libc/elf/readlib.c
@@ -0,0 +1,181 @@
+/* Copyright (C) 1999-2003, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Andreas Jaeger <aj@suse.de>, 1999 and
+ Jakub Jelinek <jakub@redhat.com>, 1999.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* The code in this file and in readelflib is a heavily simplified
+ version of the readelf program that's part of the current binutils
+ development version. Besides the simplification, it has also been
+ modified to read some other file formats. */
+
+#include <a.out.h>
+#include <elf.h>
+#include <error.h>
+#include <libintl.h>
+#include <link.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <gnu/lib-names.h>
+
+#include <ldconfig.h>
+
+#define Elf32_CLASS ELFCLASS32
+#define Elf64_CLASS ELFCLASS64
+
+struct known_names
+{
+ const char *soname;
+ int flag;
+};
+
+static struct known_names interpreters[] =
+{
+ { "/lib/" LD_SO, FLAG_ELF_LIBC6 },
+#ifdef SYSDEP_KNOWN_INTERPRETER_NAMES
+ SYSDEP_KNOWN_INTERPRETER_NAMES
+#endif
+};
+
+static struct known_names known_libs[] =
+{
+ { LIBC_SO, FLAG_ELF_LIBC6 },
+ { LIBM_SO, FLAG_ELF_LIBC6 },
+#ifdef SYSDEP_KNOWN_LIBRARY_NAMES
+ SYSDEP_KNOWN_LIBRARY_NAMES
+#endif
+};
+
+
+
+/* Returns 0 if everything is ok, != 0 in case of error. */
+int
+process_file (const char *real_file_name, const char *file_name,
+ const char *lib, int *flag, unsigned int *osversion,
+ char **soname, int is_link)
+{
+ FILE *file;
+ struct stat64 statbuf;
+ void *file_contents;
+ int ret;
+ ElfW(Ehdr) *elf_header;
+ struct exec *aout_header;
+
+ ret = 0;
+ *flag = FLAG_ANY;
+ *soname = NULL;
+
+ file = fopen (real_file_name, "rb");
+ if (file == NULL)
+ {
+ /* No error for stale symlink. */
+ if (is_link && strstr (file_name, ".so") != NULL)
+ return 1;
+ error (0, 0, _("Input file %s not found.\n"), file_name);
+ return 1;
+ }
+
+ if (fstat64 (fileno (file), &statbuf) < 0)
+ {
+ error (0, 0, _("Cannot fstat file %s.\n"), file_name);
+ fclose (file);
+ return 1;
+ }
+
+ /* Check that the file is large enough so that we can access the
+ information. We're only checking the size of the headers here. */
+ if ((size_t) statbuf.st_size < sizeof (struct exec)
+ || (size_t) statbuf.st_size < sizeof (ElfW(Ehdr)))
+ {
+ if (statbuf.st_size == 0)
+ error (0, 0, _("File %s is empty, not checked."), file_name);
+ else
+ {
+ char buf[SELFMAG];
+ size_t n = MIN (statbuf.st_size, SELFMAG);
+ if (fread (buf, n, 1, file) == 1 && memcmp (buf, ELFMAG, n) == 0)
+ error (0, 0, _("File %s is too small, not checked."), file_name);
+ }
+ fclose (file);
+ return 1;
+ }
+
+ file_contents = mmap (0, statbuf.st_size, PROT_READ, MAP_SHARED,
+ fileno (file), 0);
+ if (file_contents == MAP_FAILED)
+ {
+ error (0, 0, _("Cannot mmap file %s.\n"), file_name);
+ fclose (file);
+ return 1;
+ }
+
+ /* First check if this is an aout file. */
+ aout_header = (struct exec *) file_contents;
+ if (N_MAGIC (*aout_header) == ZMAGIC
+#ifdef QMAGIC /* Linuxism. */
+ || N_MAGIC (*aout_header) == QMAGIC
+#endif
+ )
+ {
+ /* Aout files don't have a soname, just return the name
+ including the major number. */
+ char *copy, *major, *dot;
+ copy = xstrdup (lib);
+ major = strstr (copy, ".so.");
+ if (major)
+ {
+ dot = strstr (major + 4, ".");
+ if (dot)
+ *dot = '\0';
+ }
+ *soname = copy;
+ *flag = FLAG_LIBC4;
+ goto done;
+ }
+
+ elf_header = (ElfW(Ehdr) *) file_contents;
+ if (memcmp (elf_header->e_ident, ELFMAG, SELFMAG) != 0)
+ {
+ /* The file is neither ELF nor aout. Check if it's a linker
+ script, like libc.so - otherwise complain. Only search the
+ beginning of the file. */
+ size_t len = MIN (statbuf.st_size, 512);
+ if (memmem (file_contents, len, "GROUP", 5) == NULL
+ && memmem (file_contents, len, "GNU ld script", 13) == NULL)
+ error (0, 0, _("%s is not an ELF file - it has the wrong magic bytes at the start.\n"),
+ file_name);
+ ret = 1;
+ }
+ /* Libraries have to be shared object files. */
+ else if (elf_header->e_type != ET_DYN)
+ ret = 1;
+ else if (process_elf_file (file_name, lib, flag, osversion, soname,
+ file_contents, statbuf.st_size))
+ ret = 1;
+
+ done:
+ /* Clean up allocated memory and resources. */
+ munmap (file_contents, statbuf.st_size);
+ fclose (file);
+
+ return ret;
+}
+
+/* Get architecture specific version of process_elf_file. */
+#include <readelflib.c>
diff --git a/libc/elf/reldep.c b/libc/elf/reldep.c
new file mode 100644
index 000000000..44b239b6c
--- /dev/null
+++ b/libc/elf/reldep.c
@@ -0,0 +1,111 @@
+#include <dlfcn.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ void *h1;
+ void *h2;
+ int (*fp) (void);
+ int *vp;
+
+ mtrace ();
+
+ /* Open the two objects. */
+ h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL);
+ if (h1 == NULL)
+ {
+ printf ("cannot open reldepmod1.so: %s\n", dlerror ());
+ exit (1);
+ }
+ h2 = dlopen ("reldepmod2.so", RTLD_LAZY);
+ if (h2 == NULL)
+ {
+ printf ("cannot open reldepmod2.so: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Get the address of the variable in reldepmod1.so. */
+ vp = dlsym (h1, "some_var");
+ if (vp == NULL)
+ {
+ printf ("cannot get address of \"some_var\": %s\n", dlerror ());
+ exit (1);
+ }
+
+ *vp = 42;
+
+ /* Get the function `call_me' in the second object. This has a
+ dependency which is resolved by a definition in reldepmod1.so. */
+ fp = dlsym (h2, "call_me");
+ if (fp == NULL)
+ {
+ printf ("cannot get address of \"call_me\": %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Call the function. */
+ if (fp () != 0)
+ {
+ puts ("function \"call_me\" returned wrong result");
+ exit (1);
+ }
+
+ /* Now close the first object. If must still be around since we have
+ a implicit dependency. */
+ if (dlclose (h1) != 0)
+ {
+ printf ("closing h1 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Try calling the function again. This will fail if the first object
+ got unloaded. */
+ if (fp () != 0)
+ {
+ puts ("second call of function \"call_me\" returned wrong result");
+ exit (1);
+ }
+
+ /* Now close the second file as well. */
+ if (dlclose (h2) != 0)
+ {
+ printf ("closing h2 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Finally, open the first object again. */
+ h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL);
+ if (h1 == NULL)
+ {
+ printf ("cannot open reldepmod1.so the second time: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* And get the variable address again. */
+ vp = dlsym (h1, "some_var");
+ if (vp == NULL)
+ {
+ printf ("cannot get address of \"some_var\" the second time: %s\n",
+ dlerror ());
+ exit (1);
+ }
+
+ /* The variable now must have its originial value. */
+ if (*vp != 0)
+ {
+ puts ("variable \"some_var\" not reset");
+ exit (1);
+ }
+
+ /* Close the first object again, we are done. */
+ if (dlclose (h1) != 0)
+ {
+ printf ("closing h1 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ return 0;
+}
diff --git a/libc/elf/reldep2.c b/libc/elf/reldep2.c
new file mode 100644
index 000000000..ba5ab222f
--- /dev/null
+++ b/libc/elf/reldep2.c
@@ -0,0 +1,101 @@
+#include <dlfcn.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ void *h1;
+ void *h2;
+ int (*fp) (void);
+ int *vp;
+
+ mtrace ();
+
+ /* Open the two objects. */
+ h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL);
+ if (h1 == NULL)
+ {
+ printf ("cannot open reldepmod1.so: %s\n", dlerror ());
+ exit (1);
+ }
+ h2 = dlopen ("reldepmod3.so", RTLD_LAZY);
+ if (h2 == NULL)
+ {
+ printf ("cannot open reldepmod3.so: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Get the address of the variable in reldepmod1.so. */
+ vp = dlsym (h1, "some_var");
+ if (vp == NULL)
+ {
+ printf ("cannot get address of \"some_var\": %s\n", dlerror ());
+ exit (1);
+ }
+
+ *vp = 42;
+
+ /* Get the function `call_me' in the second object. This has a
+ dependency which is resolved by a definition in reldepmod1.so. */
+ fp = dlsym (h2, "call_me");
+ if (fp == NULL)
+ {
+ printf ("cannot get address of \"call_me\": %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Call the function. */
+ if (fp () != 0)
+ {
+ puts ("function \"call_me\" returned wrong result");
+ exit (1);
+ }
+
+ /* Now close the first object. It must still be around since we have
+ an implicit dependency. */
+ if (dlclose (h1) != 0)
+ {
+ printf ("closing h1 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Open the first object again. */
+ h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL);
+ if (h1 == NULL)
+ {
+ printf ("cannot open reldepmod1.so the second time: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Get the variable address again. */
+ vp = dlsym (h1, "some_var");
+ if (vp == NULL)
+ {
+ printf ("cannot get address of \"some_var\" the second time: %s\n",
+ dlerror ());
+ exit (1);
+ }
+
+ /* The variable now must have its originial value. */
+ if (*vp != 42)
+ {
+ puts ("variable \"some_var\" reset");
+ exit (1);
+ }
+
+ /* Close the first object again, we are done. */
+ if (dlclose (h1) != 0)
+ {
+ printf ("closing h1 failed: %s\n", dlerror ());
+ exit (1);
+ }
+ if (dlclose (h2) != 0)
+ {
+ printf ("closing h2 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ return 0;
+}
diff --git a/libc/elf/reldep3.c b/libc/elf/reldep3.c
new file mode 100644
index 000000000..b051c41db
--- /dev/null
+++ b/libc/elf/reldep3.c
@@ -0,0 +1,101 @@
+#include <dlfcn.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ void *h1;
+ void *h2;
+ int (*fp) (void);
+ int *vp;
+
+ mtrace ();
+
+ /* Open the two objects. */
+ h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL);
+ if (h1 == NULL)
+ {
+ printf ("cannot open reldepmod1.so: %s\n", dlerror ());
+ exit (1);
+ }
+ h2 = dlopen ("reldepmod4.so", RTLD_LAZY);
+ if (h2 == NULL)
+ {
+ printf ("cannot open reldepmod4.so: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Get the address of the variable in reldepmod1.so. */
+ vp = dlsym (h1, "some_var");
+ if (vp == NULL)
+ {
+ printf ("cannot get address of \"some_var\": %s\n", dlerror ());
+ exit (1);
+ }
+
+ *vp = 42;
+
+ /* Get the function `call_me' in the second object. This has a
+ dependency which is resolved by a definition in reldepmod1.so. */
+ fp = dlsym (h2, "call_me");
+ if (fp == NULL)
+ {
+ printf ("cannot get address of \"call_me\": %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Call the function. */
+ if (fp () != 0)
+ {
+ puts ("function \"call_me\" returned wrong result");
+ exit (1);
+ }
+
+ /* Now close the first object. If must still be around since we have
+ a implicit dependency. */
+ if (dlclose (h1) != 0)
+ {
+ printf ("closing h1 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Open the first object again. */
+ h1 = dlopen ("reldepmod1.so", RTLD_LAZY | RTLD_GLOBAL);
+ if (h1 == NULL)
+ {
+ printf ("cannot open reldepmod1.so the second time: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Get the variable address again. */
+ vp = dlsym (h1, "some_var");
+ if (vp == NULL)
+ {
+ printf ("cannot get address of \"some_var\" the second time: %s\n",
+ dlerror ());
+ exit (1);
+ }
+
+ /* The variable now must have its originial value. */
+ if (*vp != 0)
+ {
+ puts ("variable \"some_var\" not reset");
+ exit (1);
+ }
+
+ /* Close the first object again, we are done. */
+ if (dlclose (h1) != 0)
+ {
+ printf ("closing h1 failed: %s\n", dlerror ());
+ exit (1);
+ }
+ if (dlclose (h2) != 0)
+ {
+ printf ("closing h2 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ return 0;
+}
diff --git a/libc/elf/reldep4.c b/libc/elf/reldep4.c
new file mode 100644
index 000000000..ba12e7d9c
--- /dev/null
+++ b/libc/elf/reldep4.c
@@ -0,0 +1,40 @@
+#include <dlfcn.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ int i;
+ void *h1, *h2;
+
+ mtrace ();
+
+ for (i = 0; i < 3; i++)
+ {
+ h1 = dlopen ("reldep4mod1.so", RTLD_NOW | RTLD_GLOBAL);
+ if (h1 == NULL)
+ {
+ printf ("cannot open reldep4mod1.so: %s\n", dlerror ());
+ exit (1);
+ }
+ h2 = dlopen ("reldep4mod2.so", RTLD_NOW | RTLD_GLOBAL);
+ if (h2 == NULL)
+ {
+ printf ("cannot open reldep4mod2.so: %s\n", dlerror ());
+ exit (1);
+ }
+ if (dlclose (h1) != 0)
+ {
+ printf ("closing h1 failed: %s\n", dlerror ());
+ exit (1);
+ }
+ if (dlclose (h2) != 0)
+ {
+ printf ("closing h2 failed: %s\n", dlerror ());
+ exit (1);
+ }
+ }
+ return 0;
+}
diff --git a/libc/elf/reldep4mod1.c b/libc/elf/reldep4mod1.c
new file mode 100644
index 000000000..934a68096
--- /dev/null
+++ b/libc/elf/reldep4mod1.c
@@ -0,0 +1,7 @@
+int foo (void);
+
+int foo (void)
+{
+ return 0;
+}
+
diff --git a/libc/elf/reldep4mod2.c b/libc/elf/reldep4mod2.c
new file mode 100644
index 000000000..26ce6bf13
--- /dev/null
+++ b/libc/elf/reldep4mod2.c
@@ -0,0 +1,8 @@
+extern int foo (void);
+extern int bar (void);
+
+int
+bar (void)
+{
+ return foo ();
+}
diff --git a/libc/elf/reldep4mod3.c b/libc/elf/reldep4mod3.c
new file mode 100644
index 000000000..934a68096
--- /dev/null
+++ b/libc/elf/reldep4mod3.c
@@ -0,0 +1,7 @@
+int foo (void);
+
+int foo (void)
+{
+ return 0;
+}
+
diff --git a/libc/elf/reldep4mod4.c b/libc/elf/reldep4mod4.c
new file mode 100644
index 000000000..26ce6bf13
--- /dev/null
+++ b/libc/elf/reldep4mod4.c
@@ -0,0 +1,8 @@
+extern int foo (void);
+extern int bar (void);
+
+int
+bar (void)
+{
+ return foo ();
+}
diff --git a/libc/elf/reldep5.c b/libc/elf/reldep5.c
new file mode 100644
index 000000000..881d519ff
--- /dev/null
+++ b/libc/elf/reldep5.c
@@ -0,0 +1,70 @@
+#include <dlfcn.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ void *h1;
+ void *h2;
+ int (*fp) (void);
+
+ mtrace ();
+
+ /* Open the two objects. */
+ h1 = dlopen ("reldepmod5.so", RTLD_LAZY);
+ if (h1 == NULL)
+ {
+ printf ("cannot open reldepmod5.so: %s\n", dlerror ());
+ exit (1);
+ }
+ h2 = dlopen ("reldepmod6.so", RTLD_LAZY);
+ if (h2 == NULL)
+ {
+ printf ("cannot open reldepmod6.so: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Get the address of the variable in reldepmod1.so. */
+ fp = dlsym (h2, "bar");
+ if (fp == NULL)
+ {
+ printf ("cannot get address of \"bar\": %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Call the function. */
+ puts ("calling fp for the first time");
+ if (fp () != 0)
+ {
+ puts ("function \"call_me\" returned wrong result");
+ exit (1);
+ }
+
+ /* Now close the first object. It must still be around since we have
+ an implicit dependency. */
+ if (dlclose (h1) != 0)
+ {
+ printf ("closing h1 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Calling the function must still work. */
+ puts ("calling fp for the second time");
+ if (fp () != 0)
+ {
+ puts ("function \"call_me\" the second time returned wrong result");
+ exit (1);
+ }
+ puts ("second call suceeded as well");
+
+ /* Close the second object, we are done. */
+ if (dlclose (h2) != 0)
+ {
+ printf ("closing h2 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ return 0;
+}
diff --git a/libc/elf/reldep6.c b/libc/elf/reldep6.c
new file mode 100644
index 000000000..1eeec6c86
--- /dev/null
+++ b/libc/elf/reldep6.c
@@ -0,0 +1,109 @@
+#include <dlfcn.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef int (*fn)(void);
+#define CHUNKS 1024
+#define REPEAT 64
+
+int
+main (void)
+{
+ void *h1;
+ void *h2;
+ fn **foopp;
+ fn bar, baz;
+ int i, j;
+ int n;
+ void *allocs[REPEAT][CHUNKS];
+
+ mtrace ();
+
+ /* Open the two objects. */
+ h1 = dlopen ("reldep6mod3.so", RTLD_LAZY);
+ if (h1 == NULL)
+ {
+ printf ("cannot open reldep6mod3.so: %s\n", dlerror ());
+ exit (1);
+ }
+
+ foopp = dlsym (h1, "foopp");
+ if (foopp == NULL)
+ {
+ printf ("cannot get address of \"foopp\": %s\n", dlerror ());
+ exit (1);
+ }
+ n = (**foopp) ();
+ if (n != 20)
+ {
+ printf ("(**foopp)() return %d, not return 20\n", n);
+ exit (1);
+ }
+
+ h2 = dlopen ("reldep6mod4.so", RTLD_LAZY);
+ if (h2 == NULL)
+ {
+ printf ("cannot open reldep6mod4.so: %s\n", dlerror ());
+ exit (1);
+ }
+
+ baz = dlsym (h2, "baz");
+ if (baz == NULL)
+ {
+ printf ("cannot get address of \"baz\": %s\n", dlerror ());
+ exit (1);
+ }
+ if (baz () != 31)
+ {
+ printf ("baz() did not return 31\n");
+ exit (1);
+ }
+
+ if (dlclose (h1) != 0)
+ {
+ printf ("closing h1 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ /* Clobber memory. */
+ for (i = 0; i < REPEAT; ++i)
+ for (j = 0; j < CHUNKS; ++j)
+ allocs[i][j] = calloc (1, j + 1);
+
+ bar = dlsym (h2, "bar");
+ if (bar == NULL)
+ {
+ printf ("cannot get address of \"bar\": %s\n", dlerror ());
+ exit (1);
+ }
+ if (bar () != 40)
+ {
+ printf ("bar() did not return 40\n");
+ exit (1);
+ }
+
+ baz = dlsym (h2, "baz");
+ if (baz == NULL)
+ {
+ printf ("cannot get address of \"baz\": %s\n", dlerror ());
+ exit (1);
+ }
+ if (baz () != 31)
+ {
+ printf ("baz() did not return 31\n");
+ exit (1);
+ }
+
+ for (i = 0; i < REPEAT; ++i)
+ for (j = 0; j < CHUNKS; ++j)
+ free (allocs[i][j]);
+
+ if (dlclose (h2) != 0)
+ {
+ printf ("closing h2 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ return 0;
+}
diff --git a/libc/elf/reldep6mod0.c b/libc/elf/reldep6mod0.c
new file mode 100644
index 000000000..58f3745fb
--- /dev/null
+++ b/libc/elf/reldep6mod0.c
@@ -0,0 +1,8 @@
+int bar (void);
+extern void free (void *);
+
+int bar (void)
+{
+ free (0);
+ return 40;
+}
diff --git a/libc/elf/reldep6mod1.c b/libc/elf/reldep6mod1.c
new file mode 100644
index 000000000..037a73a19
--- /dev/null
+++ b/libc/elf/reldep6mod1.c
@@ -0,0 +1,14 @@
+int foo (void);
+int baz (void);
+extern int weak (void);
+asm (".weak weak");
+
+int foo (void)
+{
+ return 20;
+}
+
+int baz (void)
+{
+ return weak () + 1;
+}
diff --git a/libc/elf/reldep6mod2.c b/libc/elf/reldep6mod2.c
new file mode 100644
index 000000000..c2ef3f9bc
--- /dev/null
+++ b/libc/elf/reldep6mod2.c
@@ -0,0 +1,3 @@
+extern int foo (void);
+
+void *foop = (void *) foo;
diff --git a/libc/elf/reldep6mod3.c b/libc/elf/reldep6mod3.c
new file mode 100644
index 000000000..881828ef6
--- /dev/null
+++ b/libc/elf/reldep6mod3.c
@@ -0,0 +1,3 @@
+extern void *foop;
+
+void **foopp = &foop;
diff --git a/libc/elf/reldep6mod4.c b/libc/elf/reldep6mod4.c
new file mode 100644
index 000000000..8fa89de64
--- /dev/null
+++ b/libc/elf/reldep6mod4.c
@@ -0,0 +1,12 @@
+int foo (void);
+int weak (void);
+
+int foo (void)
+{
+ return 10;
+}
+
+int weak (void)
+{
+ return 30;
+}
diff --git a/libc/elf/reldep7.c b/libc/elf/reldep7.c
new file mode 100644
index 000000000..5275a0e8f
--- /dev/null
+++ b/libc/elf/reldep7.c
@@ -0,0 +1,58 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ void *h1;
+ void *h2;
+ void *mod1_bar, *mod2_bar;
+
+ h1 = dlopen ("reldep7mod1.so", RTLD_GLOBAL | RTLD_LAZY);
+ if (h1 == NULL)
+ {
+ printf ("cannot open reldep7mod1.so: %s\n", dlerror ());
+ exit (1);
+ }
+
+ h2 = dlopen ("reldep7mod2.so", RTLD_GLOBAL | RTLD_LAZY);
+ if (h2 == NULL)
+ {
+ printf ("cannot open reldep7mod1.so: %s\n", dlerror ());
+ exit (1);
+ }
+
+ mod1_bar = dlsym (h1, "mod1_bar");
+ if (mod1_bar == NULL)
+ {
+ printf ("cannot get address of \"mod1_bar\": %s\n", dlerror ());
+ exit (1);
+ }
+
+ mod2_bar = dlsym (h2, "mod2_bar");
+ if (mod2_bar == NULL)
+ {
+ printf ("cannot get address of \"mod2_bar\": %s\n", dlerror ());
+ exit (1);
+ }
+
+ printf ("%d\n", ((int (*) (void)) mod1_bar) ());
+ printf ("%d\n", ((int (*) (void)) mod2_bar) ());
+
+ if (dlclose (h1) != 0)
+ {
+ printf ("closing h1 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ printf ("%d\n", ((int (*) (void)) mod2_bar) ());
+
+ if (dlclose (h2) != 0)
+ {
+ printf ("closing h2 failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ return 0;
+}
diff --git a/libc/elf/reldep7mod1.c b/libc/elf/reldep7mod1.c
new file mode 100644
index 000000000..de1bb3a6c
--- /dev/null
+++ b/libc/elf/reldep7mod1.c
@@ -0,0 +1,12 @@
+int foo (void) __attribute__ ((weak));
+int
+foo (void)
+{
+ return 1;
+}
+
+int
+mod1_bar (void)
+{
+ return foo ();
+}
diff --git a/libc/elf/reldep7mod2.c b/libc/elf/reldep7mod2.c
new file mode 100644
index 000000000..3fa336879
--- /dev/null
+++ b/libc/elf/reldep7mod2.c
@@ -0,0 +1,12 @@
+int foo (void) __attribute__ ((weak));
+int
+foo (void)
+{
+ return 2;
+}
+
+int
+mod2_bar (void)
+{
+ return foo ();
+}
diff --git a/libc/elf/reldep8.c b/libc/elf/reldep8.c
new file mode 100644
index 000000000..90009a560
--- /dev/null
+++ b/libc/elf/reldep8.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+int
+main (void)
+{
+ void *handle = dlopen ("reldep8mod3.so", RTLD_LAZY);
+ if (handle == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ dlclose (handle);
+ abort ();
+}
diff --git a/libc/elf/reldep8mod1.c b/libc/elf/reldep8mod1.c
new file mode 100644
index 000000000..acddc4cf8
--- /dev/null
+++ b/libc/elf/reldep8mod1.c
@@ -0,0 +1,19 @@
+#include <stdlib.h>
+void
+foo (void)
+{
+ exit (0);
+}
+
+void
+__attribute__((destructor))
+bar (void)
+{
+ static int i;
+ foo ();
+ ++i;
+}
+void
+baz (void)
+{
+}
diff --git a/libc/elf/reldep8mod2.c b/libc/elf/reldep8mod2.c
new file mode 100644
index 000000000..d0020240a
--- /dev/null
+++ b/libc/elf/reldep8mod2.c
@@ -0,0 +1,7 @@
+void
+__attribute__((constructor))
+xxx (void)
+{
+ extern void baz (void);
+ baz ();
+}
diff --git a/libc/elf/reldep8mod3.c b/libc/elf/reldep8mod3.c
new file mode 100644
index 000000000..6d1a0d47b
--- /dev/null
+++ b/libc/elf/reldep8mod3.c
@@ -0,0 +1 @@
+int x;
diff --git a/libc/elf/reldep9.c b/libc/elf/reldep9.c
new file mode 100644
index 000000000..51c7a8bb9
--- /dev/null
+++ b/libc/elf/reldep9.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+int
+main (void)
+{
+ void *handle = dlopen ("reldep9mod3.so", RTLD_LAZY);
+ if (handle == NULL)
+ {
+ printf ("%s\n", dlerror ());
+ exit (1);
+ }
+ dlclose (handle);
+ abort ();
+}
diff --git a/libc/elf/reldep9mod1.c b/libc/elf/reldep9mod1.c
new file mode 100644
index 000000000..249a2bae1
--- /dev/null
+++ b/libc/elf/reldep9mod1.c
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+void
+foo (void)
+{
+ exit (0);
+}
+
+void
+__attribute__((destructor))
+bar (void)
+{
+ static int i;
+ foo ();
+ ++i;
+}
+
+void
+__attribute__((constructor))
+destr (void)
+{
+ extern void baz (void);
+ baz ();
+}
diff --git a/libc/elf/reldep9mod2.c b/libc/elf/reldep9mod2.c
new file mode 100644
index 000000000..090966e3e
--- /dev/null
+++ b/libc/elf/reldep9mod2.c
@@ -0,0 +1,3 @@
+void baz (void)
+{
+}
diff --git a/libc/elf/reldep9mod3.c b/libc/elf/reldep9mod3.c
new file mode 100644
index 000000000..6d1a0d47b
--- /dev/null
+++ b/libc/elf/reldep9mod3.c
@@ -0,0 +1 @@
+int x;
diff --git a/libc/elf/reldepmod1.c b/libc/elf/reldepmod1.c
new file mode 100644
index 000000000..b8ef6401e
--- /dev/null
+++ b/libc/elf/reldepmod1.c
@@ -0,0 +1,10 @@
+extern int foo (void);
+
+int some_var;
+
+
+int
+foo (void)
+{
+ return some_var;
+}
diff --git a/libc/elf/reldepmod2.c b/libc/elf/reldepmod2.c
new file mode 100644
index 000000000..b7edebae8
--- /dev/null
+++ b/libc/elf/reldepmod2.c
@@ -0,0 +1,8 @@
+extern int foo (void);
+extern int call_me (void);
+
+int
+call_me (void)
+{
+ return foo () - 42;
+}
diff --git a/libc/elf/reldepmod3.c b/libc/elf/reldepmod3.c
new file mode 100644
index 000000000..66a996cd9
--- /dev/null
+++ b/libc/elf/reldepmod3.c
@@ -0,0 +1,20 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int call_me (void);
+
+int
+call_me (void)
+{
+ int (*fp) (void);
+
+ fp = dlsym (RTLD_DEFAULT, "foo");
+ if (fp == NULL)
+ {
+ printf ("cannot get address of foo in global scope: %s\n", dlerror ());
+ exit (1);
+ }
+
+ return fp () - 42;
+}
diff --git a/libc/elf/reldepmod4.c b/libc/elf/reldepmod4.c
new file mode 100644
index 000000000..dcb503bba
--- /dev/null
+++ b/libc/elf/reldepmod4.c
@@ -0,0 +1,37 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int call_me (void);
+
+int
+call_me (void)
+{
+ void *h;
+ int (*fp) (void);
+ int res;
+
+ h = dlopen ("reldepmod1.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot open reldepmod1.so in %s: %s\n", __FILE__, dlerror ());
+ exit (1);
+ }
+
+ fp = dlsym (h, "foo");
+ if (fp == NULL)
+ {
+ printf ("cannot get address of foo in global scope: %s\n", dlerror ());
+ exit (1);
+ }
+
+ res = fp () - 42;
+
+ if (dlclose (h) != 0)
+ {
+ printf ("failure when closing h in %s: %s\n", __FILE__, dlerror ());
+ exit (1);
+ }
+
+ return res;
+}
diff --git a/libc/elf/reldepmod5.c b/libc/elf/reldepmod5.c
new file mode 100644
index 000000000..62df69716
--- /dev/null
+++ b/libc/elf/reldepmod5.c
@@ -0,0 +1,7 @@
+extern int foo (void);
+
+int
+foo (void)
+{
+ return 42;
+}
diff --git a/libc/elf/reldepmod6.c b/libc/elf/reldepmod6.c
new file mode 100644
index 000000000..cd2aeb400
--- /dev/null
+++ b/libc/elf/reldepmod6.c
@@ -0,0 +1,8 @@
+extern int call_me (void);
+extern int bar (void);
+
+int
+bar (void)
+{
+ return call_me ();
+}
diff --git a/libc/elf/resolvfail.c b/libc/elf/resolvfail.c
new file mode 100644
index 000000000..ebd635d15
--- /dev/null
+++ b/libc/elf/resolvfail.c
@@ -0,0 +1,31 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+static const char obj[] = "testobj1.so";
+
+int
+main (void)
+{
+ void *d = dlopen (obj, RTLD_LAZY);
+ int n;
+
+ if (d == NULL)
+ {
+ printf ("cannot load %s: %s\n", obj, dlerror ());
+ return 1;
+ }
+
+ for (n = 0; n < 10000; ++n)
+ if (dlsym (d, "does not exist") != NULL)
+ {
+ puts ("dlsym() did not fail");
+ return 1;
+ }
+ else if (dlerror () == NULL)
+ {
+ puts ("dlerror() didn't return a string");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/libc/elf/restest1.c b/libc/elf/restest1.c
new file mode 100644
index 000000000..eb5aeca59
--- /dev/null
+++ b/libc/elf/restest1.c
@@ -0,0 +1,57 @@
+#include <dlfcn.h>
+#include <error.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ void *h1;
+ int (*fp1) (int);
+ void *h2;
+ int (*fp2) (int);
+ int res1;
+ int res2;
+
+ mtrace ();
+
+ h1 = dlopen ("testobj1.so", RTLD_LAZY);
+ if (h1 == NULL)
+ error (EXIT_FAILURE, 0, "while loading `%s': %s", "testobj1.so",
+ dlerror ());
+
+ h2 = dlopen ("testobj1_1.so", RTLD_LAZY);
+ if (h1 == NULL)
+ error (EXIT_FAILURE, 0, "while loading `%s': %s", "testobj1_1.so",
+ dlerror ());
+
+ fp1 = dlsym (h1, "obj1func1");
+ if (fp1 == NULL)
+ error (EXIT_FAILURE, 0, "getting `obj1func1' in `%s': %s",
+ "testobj1.so", dlerror ());
+
+ fp2 = dlsym (h2, "obj1func1");
+ if (fp2 == NULL)
+ error (EXIT_FAILURE, 0, "getting `obj1func1' in `%s': %s",
+ "testobj1_1.so", dlerror ());
+
+ res1 = fp1 (10);
+ res2 = fp2 (10);
+ printf ("fp1(10) = %d\nfp2(10) = %d\n", res1, res2);
+
+ if (dlclose (h1) != 0)
+ error (EXIT_FAILURE, 0, "cannot close testobj1.so: %s\n", dlerror ());
+ if (dlclose (h2) != 0)
+ error (EXIT_FAILURE, 0, "cannot close testobj1_1.so: %s\n", dlerror ());
+
+ return res1 != 42 || res2 != 72;
+}
+
+
+extern int foo (int a);
+int
+foo (int a)
+{
+ return a + 10;
+}
diff --git a/libc/elf/restest2.c b/libc/elf/restest2.c
new file mode 100644
index 000000000..f959f030a
--- /dev/null
+++ b/libc/elf/restest2.c
@@ -0,0 +1,33 @@
+#include <sys/types.h>
+#include <dlfcn.h>
+#include <error.h>
+#include <mcheck.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+pid_t pid, pid2;
+
+pid_t getpid(void)
+{
+ pid_t (*f)(void);
+ f = (pid_t (*)(void)) dlsym (RTLD_NEXT, "getpid");
+ if (f == NULL)
+ error (EXIT_FAILURE, 0, "dlsym (RTLD_NEXT, \"getpid\"): %s", dlerror ());
+ return (pid2 = f()) + 26;
+}
+
+int
+main (void)
+{
+ pid_t (*f)(void);
+
+ mtrace ();
+
+ f = (pid_t (*)(void)) dlsym (RTLD_DEFAULT, "getpid");
+ if (f == NULL)
+ error (EXIT_FAILURE, 0, "dlsym (RTLD_DEFAULT, \"getpid\"): %s", dlerror ());
+ pid = f();
+ if (pid != pid2 + 26)
+ error (EXIT_FAILURE, 0, "main getpid() not called");
+ return 0;
+}
diff --git a/libc/elf/rtld-Rules b/libc/elf/rtld-Rules
new file mode 100644
index 000000000..01fbbdf0c
--- /dev/null
+++ b/libc/elf/rtld-Rules
@@ -0,0 +1,133 @@
+# Subroutine makefile for compiling libc modules linked into dynamic linker.
+
+# Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, write to the Free
+# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+# 02111-1307 USA.
+
+# This makefile is never used by itself, but only from the rtld-libc.a
+# rule in Makefile, which does make -f librtld.mk -f rtld-Rules.
+# librtld.mk is the generated file containing variable definitions for
+# `rtld-subdirs', a subset of the top-level $(subdirs) list; and for each
+# SUBDIR in $(rtld-subdirs), `rtld-SUBDIR' listing `module.os' file names.
+
+.PHONY: rtld-all
+rtld-all:
+
+# When run from the elf/Makefile to build rtld-libc.a, $(subdir) is elf.
+ifneq ($(subdir),elf)
+ifndef rtld-modules
+error rtld-modules not set
+endif
+endif
+
+ifndef rtld-modules
+# Running to build rtld-libc.a, driving runs of $(rtld-subdir-make), below.
+
+ifndef rtld-subdirs
+error This makefile is a subroutine of elf/Makefile not to be used directly
+endif
+
+include ../Makeconfig
+
+rtld-all: $(objpfx)rtld-libc.a
+
+$(objpfx)rtld-libc.a: $(foreach dir,$(rtld-subdirs),\
+ $(addprefix $(common-objpfx)$(dir)/rtld-,\
+ $(rtld-$(dir))))
+ @-rm -f $@T
+ $(AR) cq$(verbose) $@T $^
+ $(RANLIB) $@T
+ mv -f $@T $@
+
+# Use the verbose option of ar and tar when not running silently.
+ifeq "$(findstring s,$(MAKEFLAGS))" "" # if not -s
+verbose := v
+else # -s
+verbose :=
+endif # not -s
+
+
+# For each subdirectory, define a pattern rule that makes all of that
+# subdirectory's modules at once with one recursive make command.
+object-suffixes-left := $(rtld-subdirs)
+define o-iterator-doit
+$(foreach obj,$(rtld-$o),$(common-objpfx)%/rtld-$(obj)): FORCE ; \
+ +$$(rtld-subdir-make)
+endef
+include $(patsubst %,../o-iterator.mk,$(object-suffixes-left))
+
+# This is how we descend into each subdirectory. See below.
+define rtld-subdir-make
+$(MAKE) $(subdir-args) objdir=$(objdir) \
+ -f Makefile -f ../elf/rtld-Rules rtld-all \
+ rtld-modules='$(addprefix rtld-,$(rtld-$*))'
+endef
+
+# See subdir-target-args in ../Makefile for the model.
+subdir-args = subdir=$*$(if $($*-srcdir),\
+ -C $($*-srcdir) ..=`pwd`/,\
+ -C $(..)$* ..=../)
+
+FORCE:
+
+else
+
+# In this case we are being run by $(rtld-subdir-make), above.
+# Some other subdir's Makefile has provided all its normal rules,
+# and we just provide some additional definitions.
+
+# These are the basic compilation rules corresponding to the Makerules ones.
+# The sysd-rules generated makefile already defines pattern rules for rtld-%
+# targets built from sysdeps source files.
+$(objpfx)rtld-%.os: %.S $(before-compile); $(compile-command.S)
+$(objpfx)rtld-%.os: %.s $(before-compile); $(compile-command.s)
+$(objpfx)rtld-%.os: %.c $(before-compile); $(compile-command.c)
+
+# The rules for generated source files.
+$(objpfx)rtld-%.os: $(objpfx)%.S $(before-compile); $(compile-command.S)
+$(objpfx)rtld-%.os: $(objpfx)%.s $(before-compile); $(compile-command.s)
+$(objpfx)rtld-%.os: $(objpfx)%.c $(before-compile); $(compile-command.c)
+
+# The command line setting of rtld-modules (see above) tells us
+# what we need to build, and that tells us what dependency files we need.
+rtld-all: $(addprefix $(objpfx),$(rtld-modules))
+
+# Figure out the dependency files we need. After respecting the $(omit-deps)
+# list as applied to the names without the `rtld-', there may be none left.
+rtld-depfiles := $(patsubst %,$(objpfx)rtld-%.os.d,\
+ $(filter-out $(omit-deps),\
+ $(rtld-modules:rtld-%.os=%)))
+rtld-depfiles := $(strip $(wildcard $(rtld-depfiles)) \
+ $(patsubst %.dt,%.d,\
+ $(wildcard $(rtld-depfiles:.d=.dt))))
+ifdef rtld-depfiles
+-include $(rtld-depfiles)
+endif
+
+# Just in case we wind up e.g. regenerating dependencies for non-rtld objects,
+# we don't unconditionally modify the flags. For rtld-% targets, use the
+# special flags set below.
+CFLAGS += $(if $(filter rtld-%,$(@F)),$(CFLAGS-rtld))
+CPPFLAGS += $(if $(filter rtld-%,$(@F)),$(CPPFLAGS-rtld))
+
+
+# This here is the whole point of all the shenanigans.
+CPPFLAGS-rtld := -DNOT_IN_libc=1 -DIS_IN_rtld=1
+CFLAGS-rtld := # blah
+
+
+endif
diff --git a/libc/elf/rtld.c b/libc/elf/rtld.c
new file mode 100644
index 000000000..9a21b8bc6
--- /dev/null
+++ b/libc/elf/rtld.c
@@ -0,0 +1,2826 @@
+/* Run time dynamic linker.
+ Copyright (C) 1995-2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <errno.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h> /* Check if MAP_ANON is defined. */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <ldsodefs.h>
+#include <stdio-common/_itoa.h>
+#include <entry.h>
+#include <fpu_control.h>
+#include <hp-timing.h>
+#include <bits/libc-lock.h>
+#include "dynamic-link.h"
+#include <dl-librecon.h>
+#include <unsecvars.h>
+#include <dl-cache.h>
+#include <dl-osinfo.h>
+#include <dl-procinfo.h>
+#include <tls.h>
+
+#include <assert.h>
+
+/* Avoid PLT use for our local calls at startup. */
+extern __typeof (__mempcpy) __mempcpy attribute_hidden;
+
+/* GCC has mental blocks about _exit. */
+extern __typeof (_exit) exit_internal asm ("_exit") attribute_hidden;
+#define _exit exit_internal
+
+/* Helper function to handle errors while resolving symbols. */
+static void print_unresolved (int errcode, const char *objname,
+ const char *errsting);
+
+/* Helper function to handle errors when a version is missing. */
+static void print_missing_version (int errcode, const char *objname,
+ const char *errsting);
+
+/* Print the various times we collected. */
+static void print_statistics (hp_timing_t *total_timep);
+
+/* Add audit objects. */
+static void process_dl_audit (char *str);
+
+/* This is a list of all the modes the dynamic loader can be in. */
+enum mode { normal, list, verify, trace };
+
+/* Process all environments variables the dynamic linker must recognize.
+ Since all of them start with `LD_' we are a bit smarter while finding
+ all the entries. */
+static void process_envvars (enum mode *modep);
+
+#ifdef DL_ARGV_NOT_RELRO
+int _dl_argc attribute_hidden;
+char **_dl_argv = NULL;
+/* Nonzero if we were run directly. */
+unsigned int _dl_skip_args attribute_hidden;
+#else
+int _dl_argc attribute_relro attribute_hidden;
+char **_dl_argv attribute_relro = NULL;
+unsigned int _dl_skip_args attribute_relro attribute_hidden;
+#endif
+INTDEF(_dl_argv)
+
+#ifndef THREAD_SET_STACK_GUARD
+/* Only exported for architectures that don't store the stack guard canary
+ in thread local area. */
+uintptr_t __stack_chk_guard attribute_relro;
+#endif
+
+/* Only exported for architectures that don't store the pointer guard
+ value in thread local area. */
+uintptr_t __pointer_chk_guard_local
+ attribute_relro attribute_hidden __attribute__ ((nocommon));
+#ifndef THREAD_SET_POINTER_GUARD
+strong_alias (__pointer_chk_guard_local, __pointer_chk_guard)
+#endif
+
+
+/* List of auditing DSOs. */
+static struct audit_list
+{
+ const char *name;
+ struct audit_list *next;
+} *audit_list;
+
+#ifndef HAVE_INLINED_SYSCALLS
+/* Set nonzero during loading and initialization of executable and
+ libraries, cleared before the executable's entry point runs. This
+ must not be initialized to nonzero, because the unused dynamic
+ linker loaded in for libc.so's "ld.so.1" dep will provide the
+ definition seen by libc.so's initializer; that value must be zero,
+ and will be since that dynamic linker's _dl_start and dl_main will
+ never be called. */
+int _dl_starting_up = 0;
+INTVARDEF(_dl_starting_up)
+#endif
+
+/* This is the structure which defines all variables global to ld.so
+ (except those which cannot be added for some reason). */
+struct rtld_global _rtld_global =
+ {
+ /* Default presumption without further information is executable stack. */
+ ._dl_stack_flags = PF_R|PF_W|PF_X,
+#ifdef _LIBC_REENTRANT
+ ._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER
+#endif
+ };
+/* If we would use strong_alias here the compiler would see a
+ non-hidden definition. This would undo the effect of the previous
+ declaration. So spell out was strong_alias does plus add the
+ visibility attribute. */
+extern struct rtld_global _rtld_local
+ __attribute__ ((alias ("_rtld_global"), visibility ("hidden")));
+
+
+/* This variable is similar to _rtld_local, but all values are
+ read-only after relocation. */
+struct rtld_global_ro _rtld_global_ro attribute_relro =
+ {
+ /* Get architecture specific initializer. */
+#include <dl-procinfo.c>
+#ifdef NEED_DL_SYSINFO
+ ._dl_sysinfo = DL_SYSINFO_DEFAULT,
+#endif
+ ._dl_debug_fd = STDERR_FILENO,
+ ._dl_use_load_bias = -2,
+ ._dl_correct_cache_id = _DL_CACHE_DEFAULT_ID,
+ ._dl_hwcap_mask = HWCAP_IMPORTANT,
+ ._dl_lazy = 1,
+ ._dl_fpu_control = _FPU_DEFAULT,
+ ._dl_pointer_guard = 1,
+
+ /* Function pointers. */
+ ._dl_debug_printf = _dl_debug_printf,
+ ._dl_catch_error = _dl_catch_error,
+ ._dl_signal_error = _dl_signal_error,
+ ._dl_mcount = _dl_mcount_internal,
+ ._dl_lookup_symbol_x = _dl_lookup_symbol_x,
+ ._dl_check_caller = _dl_check_caller,
+ ._dl_open = _dl_open,
+ ._dl_close = _dl_close
+ };
+/* If we would use strong_alias here the compiler would see a
+ non-hidden definition. This would undo the effect of the previous
+ declaration. So spell out was strong_alias does plus add the
+ visibility attribute. */
+extern struct rtld_global_ro _rtld_local_ro
+ __attribute__ ((alias ("_rtld_global_ro"), visibility ("hidden")));
+
+
+static void dl_main (const ElfW(Phdr) *phdr, ElfW(Word) phnum,
+ ElfW(Addr) *user_entry);
+
+/* These two variables cannot be moved into .data.rel.ro. */
+static struct libname_list _dl_rtld_libname;
+static struct libname_list _dl_rtld_libname2;
+
+/* We expect less than a second for relocation. */
+#ifdef HP_SMALL_TIMING_AVAIL
+# undef HP_TIMING_AVAIL
+# define HP_TIMING_AVAIL HP_SMALL_TIMING_AVAIL
+#endif
+
+/* Variable for statistics. */
+#ifndef HP_TIMING_NONAVAIL
+static hp_timing_t relocate_time;
+static hp_timing_t load_time attribute_relro;
+static hp_timing_t start_time attribute_relro;
+#endif
+
+/* Additional definitions needed by TLS initialization. */
+#ifdef TLS_INIT_HELPER
+TLS_INIT_HELPER
+#endif
+
+/* Helper function for syscall implementation. */
+#ifdef DL_SYSINFO_IMPLEMENTATION
+DL_SYSINFO_IMPLEMENTATION
+#endif
+
+/* Before ld.so is relocated we must not access variables which need
+ relocations. This means variables which are exported. Variables
+ declared as static are fine. If we can mark a variable hidden this
+ is fine, too. The latter is important here. We can avoid setting
+ up a temporary link map for ld.so if we can mark _rtld_global as
+ hidden. */
+#if defined PI_STATIC_AND_HIDDEN && defined HAVE_HIDDEN \
+ && defined HAVE_VISIBILITY_ATTRIBUTE
+# define DONT_USE_BOOTSTRAP_MAP 1
+#endif
+
+#ifdef DONT_USE_BOOTSTRAP_MAP
+static ElfW(Addr) _dl_start_final (void *arg);
+#else
+struct dl_start_final_info
+{
+ struct link_map l;
+#if !defined HP_TIMING_NONAVAIL && HP_TIMING_INLINE
+ hp_timing_t start_time;
+#endif
+};
+static ElfW(Addr) _dl_start_final (void *arg,
+ struct dl_start_final_info *info);
+#endif
+
+/* These defined magically in the linker script. */
+extern char _begin[] attribute_hidden;
+extern char _etext[] attribute_hidden;
+extern char _end[] attribute_hidden;
+
+
+#ifdef RTLD_START
+RTLD_START
+#else
+# error "sysdeps/MACHINE/dl-machine.h fails to define RTLD_START"
+#endif
+
+#ifndef VALIDX
+# define VALIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \
+ + DT_EXTRANUM + DT_VALTAGIDX (tag))
+#endif
+#ifndef ADDRIDX
+# define ADDRIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \
+ + DT_EXTRANUM + DT_VALNUM + DT_ADDRTAGIDX (tag))
+#endif
+
+/* This is the second half of _dl_start (below). It can be inlined safely
+ under DONT_USE_BOOTSTRAP_MAP, where it is careful not to make any GOT
+ references. When the tools don't permit us to avoid using a GOT entry
+ for _dl_rtld_global (no attribute_hidden support), we must make sure
+ this function is not inlined (see below). */
+
+#ifdef DONT_USE_BOOTSTRAP_MAP
+static inline ElfW(Addr) __attribute__ ((always_inline))
+_dl_start_final (void *arg)
+#else
+static ElfW(Addr) __attribute__ ((noinline))
+_dl_start_final (void *arg, struct dl_start_final_info *info)
+#endif
+{
+ ElfW(Addr) start_addr;
+
+ if (HP_TIMING_AVAIL)
+ {
+ /* If it hasn't happen yet record the startup time. */
+ if (! HP_TIMING_INLINE)
+ HP_TIMING_NOW (start_time);
+#if !defined DONT_USE_BOOTSTRAP_MAP && !defined HP_TIMING_NONAVAIL
+ else
+ start_time = info->start_time;
+#endif
+
+ /* Initialize the timing functions. */
+ HP_TIMING_DIFF_INIT ();
+ }
+
+ /* Transfer data about ourselves to the permanent link_map structure. */
+#ifndef DONT_USE_BOOTSTRAP_MAP
+ GL(dl_rtld_map).l_addr = info->l.l_addr;
+ GL(dl_rtld_map).l_ld = info->l.l_ld;
+ memcpy (GL(dl_rtld_map).l_info, info->l.l_info,
+ sizeof GL(dl_rtld_map).l_info);
+ GL(dl_rtld_map).l_mach = info->l.l_mach;
+ GL(dl_rtld_map).l_relocated = 1;
+#endif
+ _dl_setup_hash (&GL(dl_rtld_map));
+ GL(dl_rtld_map).l_real = &GL(dl_rtld_map);
+ GL(dl_rtld_map).l_map_start = (ElfW(Addr)) _begin;
+ GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;
+ GL(dl_rtld_map).l_text_end = (ElfW(Addr)) _etext;
+ /* Copy the TLS related data if necessary. */
+#if USE_TLS && !defined DONT_USE_BOOTSTRAP_MAP
+# if USE___THREAD
+ assert (info->l.l_tls_modid != 0);
+ GL(dl_rtld_map).l_tls_blocksize = info->l.l_tls_blocksize;
+ GL(dl_rtld_map).l_tls_align = info->l.l_tls_align;
+ GL(dl_rtld_map).l_tls_firstbyte_offset = info->l.l_tls_firstbyte_offset;
+ GL(dl_rtld_map).l_tls_initimage_size = info->l.l_tls_initimage_size;
+ GL(dl_rtld_map).l_tls_initimage = info->l.l_tls_initimage;
+ GL(dl_rtld_map).l_tls_offset = info->l.l_tls_offset;
+ GL(dl_rtld_map).l_tls_modid = 1;
+# else
+ assert (info->l.l_tls_modid == 0);
+# if NO_TLS_OFFSET != 0
+ GL(dl_rtld_map).l_tls_offset = NO_TLS_OFFSET;
+# endif
+# endif
+
+#endif
+
+#if HP_TIMING_AVAIL
+ HP_TIMING_NOW (GL(dl_cpuclock_offset));
+#endif
+
+ /* Initialize the stack end variable. */
+ __libc_stack_end = __builtin_frame_address (0);
+
+ /* Call the OS-dependent function to set up life so we can do things like
+ file access. It will call `dl_main' (below) to do all the real work
+ of the dynamic linker, and then unwind our frame and run the user
+ entry point on the same stack we entered on. */
+ start_addr = _dl_sysdep_start (arg, &dl_main);
+
+#ifndef HP_TIMING_NONAVAIL
+ hp_timing_t rtld_total_time;
+ if (HP_TIMING_AVAIL)
+ {
+ hp_timing_t end_time;
+
+ /* Get the current time. */
+ HP_TIMING_NOW (end_time);
+
+ /* Compute the difference. */
+ HP_TIMING_DIFF (rtld_total_time, start_time, end_time);
+ }
+#endif
+
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS, 0))
+ {
+#ifndef HP_TIMING_NONAVAIL
+ print_statistics (&rtld_total_time);
+#else
+ print_statistics (NULL);
+#endif
+ }
+
+ return start_addr;
+}
+
+static ElfW(Addr) __attribute_used__ internal_function
+_dl_start (void *arg)
+{
+#ifdef DONT_USE_BOOTSTRAP_MAP
+# define bootstrap_map GL(dl_rtld_map)
+#else
+ struct dl_start_final_info info;
+# define bootstrap_map info.l
+#endif
+
+ /* This #define produces dynamic linking inline functions for
+ bootstrap relocation instead of general-purpose relocation. */
+#define RTLD_BOOTSTRAP
+#define RESOLVE_MAP(sym, version, flags) \
+ ((*(sym))->st_shndx == SHN_UNDEF ? 0 : &bootstrap_map)
+#include "dynamic-link.h"
+
+ if (HP_TIMING_INLINE && HP_TIMING_AVAIL)
+#ifdef DONT_USE_BOOTSTRAP_MAP
+ HP_TIMING_NOW (start_time);
+#else
+ HP_TIMING_NOW (info.start_time);
+#endif
+
+ /* Partly clean the `bootstrap_map' structure up. Don't use
+ `memset' since it might not be built in or inlined and we cannot
+ make function calls at this point. Use '__builtin_memset' if we
+ know it is available. We do not have to clear the memory if we
+ do not have to use the temporary bootstrap_map. Global variables
+ are initialized to zero by default. */
+#ifndef DONT_USE_BOOTSTRAP_MAP
+# ifdef HAVE_BUILTIN_MEMSET
+ __builtin_memset (bootstrap_map.l_info, '\0', sizeof (bootstrap_map.l_info));
+# else
+ for (size_t cnt = 0;
+ cnt < sizeof (bootstrap_map.l_info) / sizeof (bootstrap_map.l_info[0]);
+ ++cnt)
+ bootstrap_map.l_info[cnt] = 0;
+# endif
+#endif
+
+ /* Figure out the run-time load address of the dynamic linker itself. */
+ bootstrap_map.l_addr = elf_machine_load_address ();
+
+ /* Read our own dynamic section and fill in the info array. */
+ bootstrap_map.l_ld = (void *) bootstrap_map.l_addr + elf_machine_dynamic ();
+ elf_get_dynamic_info (&bootstrap_map, NULL);
+
+#if defined USE_TLS && NO_TLS_OFFSET != 0
+ bootstrap_map.l_tls_offset = NO_TLS_OFFSET;
+#endif
+
+ /* Get the dynamic linker's own program header. First we need the ELF
+ file header. The `_begin' symbol created by the linker script points
+ to it. When we have something like GOTOFF relocs, we can use a plain
+ reference to find the runtime address. Without that, we have to rely
+ on the `l_addr' value, which is not the value we want when prelinked. */
+#if USE___THREAD
+ dtv_t initdtv[3];
+ ElfW(Ehdr) *ehdr
+# ifdef DONT_USE_BOOTSTRAP_MAP
+ = (ElfW(Ehdr) *) &_begin;
+# else
+# error This will not work with prelink.
+ = (ElfW(Ehdr) *) bootstrap_map.l_addr;
+# endif
+ ElfW(Phdr) *phdr = (ElfW(Phdr) *) ((void *) ehdr + ehdr->e_phoff);
+ size_t cnt = ehdr->e_phnum; /* PT_TLS is usually the last phdr. */
+ while (cnt-- > 0)
+ if (phdr[cnt].p_type == PT_TLS)
+ {
+ void *tlsblock;
+ size_t max_align = MAX (TLS_INIT_TCB_ALIGN, phdr[cnt].p_align);
+ char *p;
+
+ bootstrap_map.l_tls_blocksize = phdr[cnt].p_memsz;
+ bootstrap_map.l_tls_align = phdr[cnt].p_align;
+ if (phdr[cnt].p_align == 0)
+ bootstrap_map.l_tls_firstbyte_offset = 0;
+ else
+ bootstrap_map.l_tls_firstbyte_offset = (phdr[cnt].p_vaddr
+ & (phdr[cnt].p_align - 1));
+ assert (bootstrap_map.l_tls_blocksize != 0);
+ bootstrap_map.l_tls_initimage_size = phdr[cnt].p_filesz;
+ bootstrap_map.l_tls_initimage = (void *) (bootstrap_map.l_addr
+ + phdr[cnt].p_vaddr);
+
+ /* We can now allocate the initial TLS block. This can happen
+ on the stack. We'll get the final memory later when we
+ know all about the various objects loaded at startup
+ time. */
+# if TLS_TCB_AT_TP
+ tlsblock = alloca (roundup (bootstrap_map.l_tls_blocksize,
+ TLS_INIT_TCB_ALIGN)
+ + TLS_INIT_TCB_SIZE
+ + max_align);
+# elif TLS_DTV_AT_TP
+ tlsblock = alloca (roundup (TLS_INIT_TCB_SIZE,
+ bootstrap_map.l_tls_align)
+ + bootstrap_map.l_tls_blocksize
+ + max_align);
+# else
+ /* In case a model with a different layout for the TCB and DTV
+ is defined add another #elif here and in the following #ifs. */
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+ /* Align the TLS block. */
+ tlsblock = (void *) (((uintptr_t) tlsblock + max_align - 1)
+ & ~(max_align - 1));
+
+ /* Initialize the dtv. [0] is the length, [1] the generation
+ counter. */
+ initdtv[0].counter = 1;
+ initdtv[1].counter = 0;
+
+ /* Initialize the TLS block. */
+# if TLS_TCB_AT_TP
+ initdtv[2].pointer = tlsblock;
+# elif TLS_DTV_AT_TP
+ bootstrap_map.l_tls_offset = roundup (TLS_INIT_TCB_SIZE,
+ bootstrap_map.l_tls_align);
+ initdtv[2].pointer = (char *) tlsblock + bootstrap_map.l_tls_offset;
+# else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+ p = __mempcpy (initdtv[2].pointer, bootstrap_map.l_tls_initimage,
+ bootstrap_map.l_tls_initimage_size);
+# ifdef HAVE_BUILTIN_MEMSET
+ __builtin_memset (p, '\0', (bootstrap_map.l_tls_blocksize
+ - bootstrap_map.l_tls_initimage_size));
+# else
+ {
+ size_t remaining = (bootstrap_map.l_tls_blocksize
+ - bootstrap_map.l_tls_initimage_size);
+ while (remaining-- > 0)
+ *p++ = '\0';
+ }
+# endif
+
+ /* Install the pointer to the dtv. */
+
+ /* Initialize the thread pointer. */
+# if TLS_TCB_AT_TP
+ bootstrap_map.l_tls_offset
+ = roundup (bootstrap_map.l_tls_blocksize, TLS_INIT_TCB_ALIGN);
+
+ INSTALL_DTV ((char *) tlsblock + bootstrap_map.l_tls_offset,
+ initdtv);
+
+ const char *lossage = TLS_INIT_TP ((char *) tlsblock
+ + bootstrap_map.l_tls_offset, 0);
+# elif TLS_DTV_AT_TP
+ INSTALL_DTV (tlsblock, initdtv);
+ const char *lossage = TLS_INIT_TP (tlsblock, 0);
+# else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+ if (__builtin_expect (lossage != NULL, 0))
+ _dl_fatal_printf ("cannot set up thread-local storage: %s\n",
+ lossage);
+
+ /* So far this is module number one. */
+ bootstrap_map.l_tls_modid = 1;
+
+ /* There can only be one PT_TLS entry. */
+ break;
+ }
+#endif /* USE___THREAD */
+
+#ifdef ELF_MACHINE_BEFORE_RTLD_RELOC
+ ELF_MACHINE_BEFORE_RTLD_RELOC (bootstrap_map.l_info);
+#endif
+
+ if (bootstrap_map.l_addr || ! bootstrap_map.l_info[VALIDX(DT_GNU_PRELINKED)])
+ {
+ /* Relocate ourselves so we can do normal function calls and
+ data access using the global offset table. */
+
+ ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, 0);
+ }
+ bootstrap_map.l_relocated = 1;
+
+ /* Please note that we don't allow profiling of this object and
+ therefore need not test whether we have to allocate the array
+ for the relocation results (as done in dl-reloc.c). */
+
+ /* Now life is sane; we can call functions and access global data.
+ Set up to use the operating system facilities, and find out from
+ the operating system's program loader where to find the program
+ header table in core. Put the rest of _dl_start into a separate
+ function, that way the compiler cannot put accesses to the GOT
+ before ELF_DYNAMIC_RELOCATE. */
+ {
+#ifdef DONT_USE_BOOTSTRAP_MAP
+ ElfW(Addr) entry = _dl_start_final (arg);
+#else
+ ElfW(Addr) entry = _dl_start_final (arg, &info);
+#endif
+
+#ifndef ELF_MACHINE_START_ADDRESS
+# define ELF_MACHINE_START_ADDRESS(map, start) (start)
+#endif
+
+ return ELF_MACHINE_START_ADDRESS (GL(dl_ns)[LM_ID_BASE]._ns_loaded, entry);
+ }
+}
+
+
+
+/* Now life is peachy; we can do all normal operations.
+ On to the real work. */
+
+/* Some helper functions. */
+
+/* Arguments to relocate_doit. */
+struct relocate_args
+{
+ struct link_map *l;
+ int lazy;
+};
+
+struct map_args
+{
+ /* Argument to map_doit. */
+ char *str;
+ struct link_map *loader;
+ int is_preloaded;
+ int mode;
+ /* Return value of map_doit. */
+ struct link_map *map;
+};
+
+struct dlmopen_args
+{
+ const char *fname;
+ struct link_map *map;
+};
+
+struct lookup_args
+{
+ const char *name;
+ struct link_map *map;
+ void *result;
+};
+
+/* Arguments to version_check_doit. */
+struct version_check_args
+{
+ int doexit;
+ int dotrace;
+};
+
+static void
+relocate_doit (void *a)
+{
+ struct relocate_args *args = (struct relocate_args *) a;
+
+ _dl_relocate_object (args->l, args->l->l_scope, args->lazy, 0);
+}
+
+static void
+map_doit (void *a)
+{
+ struct map_args *args = (struct map_args *) a;
+ args->map = _dl_map_object (args->loader, args->str,
+ args->is_preloaded, lt_library, 0, args->mode,
+ LM_ID_BASE);
+}
+
+static void
+dlmopen_doit (void *a)
+{
+ struct dlmopen_args *args = (struct dlmopen_args *) a;
+ args->map = _dl_open (args->fname, RTLD_LAZY | __RTLD_DLOPEN | __RTLD_AUDIT,
+ dl_main, LM_ID_NEWLM, _dl_argc, INTUSE(_dl_argv),
+ __environ);
+}
+
+static void
+lookup_doit (void *a)
+{
+ struct lookup_args *args = (struct lookup_args *) a;
+ const ElfW(Sym) *ref = NULL;
+ args->result = NULL;
+ lookup_t l = _dl_lookup_symbol_x (args->name, args->map, &ref,
+ args->map->l_local_scope, NULL, 0,
+ DL_LOOKUP_RETURN_NEWEST, NULL);
+ if (ref != NULL)
+ args->result = DL_SYMBOL_ADDRESS (l, ref);
+}
+
+static void
+version_check_doit (void *a)
+{
+ struct version_check_args *args = (struct version_check_args *) a;
+ if (_dl_check_all_versions (GL(dl_ns)[LM_ID_BASE]._ns_loaded, 1,
+ args->dotrace) && args->doexit)
+ /* We cannot start the application. Abort now. */
+ _exit (1);
+}
+
+
+static inline struct link_map *
+find_needed (const char *name)
+{
+ struct r_scope_elem *scope = &GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_searchlist;
+ unsigned int n = scope->r_nlist;
+
+ while (n-- > 0)
+ if (_dl_name_match_p (name, scope->r_list[n]))
+ return scope->r_list[n];
+
+ /* Should never happen. */
+ return NULL;
+}
+
+static int
+match_version (const char *string, struct link_map *map)
+{
+ const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+ ElfW(Verdef) *def;
+
+#define VERDEFTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (DT_VERDEF))
+ if (map->l_info[VERDEFTAG] == NULL)
+ /* The file has no symbol versioning. */
+ return 0;
+
+ def = (ElfW(Verdef) *) ((char *) map->l_addr
+ + map->l_info[VERDEFTAG]->d_un.d_ptr);
+ while (1)
+ {
+ ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) def + def->vd_aux);
+
+ /* Compare the version strings. */
+ if (strcmp (string, strtab + aux->vda_name) == 0)
+ /* Bingo! */
+ return 1;
+
+ /* If no more definitions we failed to find what we want. */
+ if (def->vd_next == 0)
+ break;
+
+ /* Next definition. */
+ def = (ElfW(Verdef) *) ((char *) def + def->vd_next);
+ }
+
+ return 0;
+}
+
+#ifdef USE_TLS
+static bool tls_init_tp_called;
+
+static void *
+init_tls (void)
+{
+ /* Number of elements in the static TLS block. */
+ GL(dl_tls_static_nelem) = GL(dl_tls_max_dtv_idx);
+
+ /* Do not do this twice. The audit interface might have required
+ the DTV interfaces to be set up early. */
+ if (GL(dl_initial_dtv) != NULL)
+ return NULL;
+
+ /* Allocate the array which contains the information about the
+ dtv slots. We allocate a few entries more than needed to
+ avoid the need for reallocation. */
+ size_t nelem = GL(dl_tls_max_dtv_idx) + 1 + TLS_SLOTINFO_SURPLUS;
+
+ /* Allocate. */
+ GL(dl_tls_dtv_slotinfo_list) = (struct dtv_slotinfo_list *)
+ calloc (sizeof (struct dtv_slotinfo_list)
+ + nelem * sizeof (struct dtv_slotinfo), 1);
+ /* No need to check the return value. If memory allocation failed
+ the program would have been terminated. */
+
+ struct dtv_slotinfo *slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo;
+ GL(dl_tls_dtv_slotinfo_list)->len = nelem;
+ GL(dl_tls_dtv_slotinfo_list)->next = NULL;
+
+ /* Fill in the information from the loaded modules. No namespace
+ but the base one can be filled at this time. */
+ assert (GL(dl_ns)[LM_ID_BASE + 1]._ns_loaded == NULL);
+ int i = 0;
+ for (struct link_map *l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l != NULL;
+ l = l->l_next)
+ if (l->l_tls_blocksize != 0)
+ {
+ /* This is a module with TLS data. Store the map reference.
+ The generation counter is zero. */
+ slotinfo[i].map = l;
+ /* slotinfo[i].gen = 0; */
+ ++i;
+ }
+ assert (i == GL(dl_tls_max_dtv_idx));
+
+ /* Compute the TLS offsets for the various blocks. */
+ _dl_determine_tlsoffset ();
+
+ /* Construct the static TLS block and the dtv for the initial
+ thread. For some platforms this will include allocating memory
+ for the thread descriptor. The memory for the TLS block will
+ never be freed. It should be allocated accordingly. The dtv
+ array can be changed if dynamic loading requires it. */
+ void *tcbp = _dl_allocate_tls_storage ();
+ if (tcbp == NULL)
+ _dl_fatal_printf ("\
+cannot allocate TLS data structures for initial thread");
+
+ /* Store for detection of the special case by __tls_get_addr
+ so it knows not to pass this dtv to the normal realloc. */
+ GL(dl_initial_dtv) = GET_DTV (tcbp);
+
+ /* And finally install it for the main thread. If ld.so itself uses
+ TLS we know the thread pointer was initialized earlier. */
+ const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD);
+ if (__builtin_expect (lossage != NULL, 0))
+ _dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage);
+ tls_init_tp_called = true;
+
+ return tcbp;
+}
+#endif
+
+#ifdef _LIBC_REENTRANT
+/* _dl_error_catch_tsd points to this for the single-threaded case.
+ It's reset by the thread library for multithreaded programs. */
+void ** __attribute__ ((const))
+_dl_initial_error_catch_tsd (void)
+{
+ static void *data;
+ return &data;
+}
+#endif
+
+
+static unsigned int
+do_preload (char *fname, struct link_map *main_map, const char *where)
+{
+ const char *objname;
+ const char *err_str = NULL;
+ struct map_args args;
+ bool malloced;
+
+ args.str = fname;
+ args.loader = main_map;
+ args.is_preloaded = 1;
+ args.mode = 0;
+
+ unsigned int old_nloaded = GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
+
+ (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit, &args);
+ if (__builtin_expect (err_str != NULL, 0))
+ {
+ _dl_error_printf ("\
+ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n",
+ fname, where);
+ /* No need to call free, this is still before
+ the libc's malloc is used. */
+ }
+ else if (GL(dl_ns)[LM_ID_BASE]._ns_nloaded != old_nloaded)
+ /* It is no duplicate. */
+ return 1;
+
+ /* Nothing loaded. */
+ return 0;
+}
+
+#if defined SHARED && defined _LIBC_REENTRANT \
+ && defined __rtld_lock_default_lock_recursive
+static void
+rtld_lock_default_lock_recursive (void *lock)
+{
+ __rtld_lock_default_lock_recursive (lock);
+}
+
+static void
+rtld_lock_default_unlock_recursive (void *lock)
+{
+ __rtld_lock_default_unlock_recursive (lock);
+}
+#endif
+
+
+/* The library search path. */
+static const char *library_path attribute_relro;
+/* The list preloaded objects. */
+static const char *preloadlist attribute_relro;
+/* Nonzero if information about versions has to be printed. */
+static int version_info attribute_relro;
+
+static void
+dl_main (const ElfW(Phdr) *phdr,
+ ElfW(Word) phnum,
+ ElfW(Addr) *user_entry)
+{
+ const ElfW(Phdr) *ph;
+ enum mode mode;
+ struct link_map *main_map;
+ size_t file_size;
+ char *file;
+ bool has_interp = false;
+ unsigned int i;
+ bool prelinked = false;
+ bool rtld_is_main = false;
+#ifndef HP_TIMING_NONAVAIL
+ hp_timing_t start;
+ hp_timing_t stop;
+ hp_timing_t diff;
+#endif
+#ifdef USE_TLS
+ void *tcbp = NULL;
+#endif
+
+#ifdef _LIBC_REENTRANT
+ /* Explicit initialization since the reloc would just be more work. */
+ GL(dl_error_catch_tsd) = &_dl_initial_error_catch_tsd;
+#endif
+
+#ifdef USE_TLS
+ GL(dl_init_static_tls) = &_dl_nothread_init_static_tls;
+#endif
+
+#if defined SHARED && defined _LIBC_REENTRANT \
+ && defined __rtld_lock_default_lock_recursive
+ GL(dl_rtld_lock_recursive) = rtld_lock_default_lock_recursive;
+ GL(dl_rtld_unlock_recursive) = rtld_lock_default_unlock_recursive;
+#endif
+
+ /* The explicit initialization here is cheaper than processing the reloc
+ in the _rtld_local definition's initializer. */
+ GL(dl_make_stack_executable_hook) = &_dl_make_stack_executable;
+
+ /* Process the environment variable which control the behaviour. */
+ process_envvars (&mode);
+
+#ifndef HAVE_INLINED_SYSCALLS
+ /* Set up a flag which tells we are just starting. */
+ INTUSE(_dl_starting_up) = 1;
+#endif
+
+ if (*user_entry == (ElfW(Addr)) ENTRY_POINT)
+ {
+ /* Ho ho. We are not the program interpreter! We are the program
+ itself! This means someone ran ld.so as a command. Well, that
+ might be convenient to do sometimes. We support it by
+ interpreting the args like this:
+
+ ld.so PROGRAM ARGS...
+
+ The first argument is the name of a file containing an ELF
+ executable we will load and run with the following arguments.
+ To simplify life here, PROGRAM is searched for using the
+ normal rules for shared objects, rather than $PATH or anything
+ like that. We just load it and use its entry point; we don't
+ pay attention to its PT_INTERP command (we are the interpreter
+ ourselves). This is an easy way to test a new ld.so before
+ installing it. */
+ rtld_is_main = true;
+
+ /* Note the place where the dynamic linker actually came from. */
+ GL(dl_rtld_map).l_name = rtld_progname;
+
+ while (_dl_argc > 1)
+ if (! strcmp (INTUSE(_dl_argv)[1], "--list"))
+ {
+ mode = list;
+ GLRO(dl_lazy) = -1; /* This means do no dependency analysis. */
+
+ ++_dl_skip_args;
+ --_dl_argc;
+ ++INTUSE(_dl_argv);
+ }
+ else if (! strcmp (INTUSE(_dl_argv)[1], "--verify"))
+ {
+ mode = verify;
+
+ ++_dl_skip_args;
+ --_dl_argc;
+ ++INTUSE(_dl_argv);
+ }
+ else if (! strcmp (INTUSE(_dl_argv)[1], "--library-path")
+ && _dl_argc > 2)
+ {
+ library_path = INTUSE(_dl_argv)[2];
+
+ _dl_skip_args += 2;
+ _dl_argc -= 2;
+ INTUSE(_dl_argv) += 2;
+ }
+ else if (! strcmp (INTUSE(_dl_argv)[1], "--inhibit-rpath")
+ && _dl_argc > 2)
+ {
+ GLRO(dl_inhibit_rpath) = INTUSE(_dl_argv)[2];
+
+ _dl_skip_args += 2;
+ _dl_argc -= 2;
+ INTUSE(_dl_argv) += 2;
+ }
+ else if (! strcmp (INTUSE(_dl_argv)[1], "--audit") && _dl_argc > 2)
+ {
+ process_dl_audit (INTUSE(_dl_argv)[2]);
+
+ _dl_skip_args += 2;
+ _dl_argc -= 2;
+ INTUSE(_dl_argv) += 2;
+ }
+ else
+ break;
+
+ /* If we have no further argument the program was called incorrectly.
+ Grant the user some education. */
+ if (_dl_argc < 2)
+ _dl_fatal_printf ("\
+Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
+You have invoked `ld.so', the helper program for shared library executables.\n\
+This program usually lives in the file `/lib/ld.so', and special directives\n\
+in executable files using ELF shared libraries tell the system's program\n\
+loader to load the helper program from this file. This helper program loads\n\
+the shared libraries needed by the program executable, prepares the program\n\
+to run, and runs it. You may invoke this helper program directly from the\n\
+command line to load and run an ELF executable file; this is like executing\n\
+that file itself, but always uses this helper program from the file you\n\
+specified, instead of the helper program file specified in the executable\n\
+file you run. This is mostly of use for maintainers to test new versions\n\
+of this helper program; chances are you did not intend to run this program.\n\
+\n\
+ --list list all dependencies and how they are resolved\n\
+ --verify verify that given object really is a dynamically linked\n\
+ object we can handle\n\
+ --library-path PATH use given PATH instead of content of the environment\n\
+ variable LD_LIBRARY_PATH\n\
+ --inhibit-rpath LIST ignore RUNPATH and RPATH information in object names\n\
+ in LIST\n");
+
+ ++_dl_skip_args;
+ --_dl_argc;
+ ++INTUSE(_dl_argv);
+
+ /* The initialization of _dl_stack_flags done below assumes the
+ executable's PT_GNU_STACK may have been honored by the kernel, and
+ so a PT_GNU_STACK with PF_X set means the stack started out with
+ execute permission. However, this is not really true if the
+ dynamic linker is the executable the kernel loaded. For this
+ case, we must reinitialize _dl_stack_flags to match the dynamic
+ linker itself. If the dynamic linker was built with a
+ PT_GNU_STACK, then the kernel may have loaded us with a
+ nonexecutable stack that we will have to make executable when we
+ load the program below unless it has a PT_GNU_STACK indicating
+ nonexecutable stack is ok. */
+
+ for (ph = phdr; ph < &phdr[phnum]; ++ph)
+ if (ph->p_type == PT_GNU_STACK)
+ {
+ GL(dl_stack_flags) = ph->p_flags;
+ break;
+ }
+
+ if (__builtin_expect (mode, normal) == verify)
+ {
+ const char *objname;
+ const char *err_str = NULL;
+ struct map_args args;
+ bool malloced;
+
+ args.str = rtld_progname;
+ args.loader = NULL;
+ args.is_preloaded = 0;
+ args.mode = __RTLD_OPENEXEC;
+ (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit,
+ &args);
+ if (__builtin_expect (err_str != NULL, 0))
+ /* We don't free the returned string, the programs stops
+ anyway. */
+ _exit (EXIT_FAILURE);
+ }
+ else
+ {
+ HP_TIMING_NOW (start);
+ _dl_map_object (NULL, rtld_progname, 0, lt_library, 0,
+ __RTLD_OPENEXEC, LM_ID_BASE);
+ HP_TIMING_NOW (stop);
+
+ HP_TIMING_DIFF (load_time, start, stop);
+ }
+
+ /* Now the map for the main executable is available. */
+ main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+
+ phdr = main_map->l_phdr;
+ phnum = main_map->l_phnum;
+ /* We overwrite here a pointer to a malloc()ed string. But since
+ the malloc() implementation used at this point is the dummy
+ implementations which has no real free() function it does not
+ makes sense to free the old string first. */
+ main_map->l_name = (char *) "";
+ *user_entry = main_map->l_entry;
+ }
+ else
+ {
+ /* Create a link_map for the executable itself.
+ This will be what dlopen on "" returns. */
+ main_map = _dl_new_object ((char *) "", "", lt_executable, NULL,
+ __RTLD_OPENEXEC, LM_ID_BASE);
+ assert (main_map != NULL);
+ assert (main_map == GL(dl_ns)[LM_ID_BASE]._ns_loaded);
+ main_map->l_phdr = phdr;
+ main_map->l_phnum = phnum;
+ main_map->l_entry = *user_entry;
+
+ /* At this point we are in a bit of trouble. We would have to
+ fill in the values for l_dev and l_ino. But in general we
+ do not know where the file is. We also do not handle AT_EXECFD
+ even if it would be passed up.
+
+ We leave the values here defined to 0. This is normally no
+ problem as the program code itself is normally no shared
+ object and therefore cannot be loaded dynamically. Nothing
+ prevent the use of dynamic binaries and in these situations
+ we might get problems. We might not be able to find out
+ whether the object is already loaded. But since there is no
+ easy way out and because the dynamic binary must also not
+ have an SONAME we ignore this program for now. If it becomes
+ a problem we can force people using SONAMEs. */
+
+ /* We delay initializing the path structure until we got the dynamic
+ information for the program. */
+ }
+
+ main_map->l_map_end = 0;
+ main_map->l_text_end = 0;
+ /* Perhaps the executable has no PT_LOAD header entries at all. */
+ main_map->l_map_start = ~0;
+ /* And it was opened directly. */
+ ++main_map->l_direct_opencount;
+
+ /* Scan the program header table for the dynamic section. */
+ for (ph = phdr; ph < &phdr[phnum]; ++ph)
+ switch (ph->p_type)
+ {
+ case PT_PHDR:
+ /* Find out the load address. */
+ main_map->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr;
+ break;
+ case PT_DYNAMIC:
+ /* This tells us where to find the dynamic section,
+ which tells us everything we need to do. */
+ main_map->l_ld = (void *) main_map->l_addr + ph->p_vaddr;
+ break;
+ case PT_INTERP:
+ /* This "interpreter segment" was used by the program loader to
+ find the program interpreter, which is this program itself, the
+ dynamic linker. We note what name finds us, so that a future
+ dlopen call or DT_NEEDED entry, for something that wants to link
+ against the dynamic linker as a shared library, will know that
+ the shared object is already loaded. */
+ _dl_rtld_libname.name = ((const char *) main_map->l_addr
+ + ph->p_vaddr);
+ /* _dl_rtld_libname.next = NULL; Already zero. */
+ GL(dl_rtld_map).l_libname = &_dl_rtld_libname;
+
+ /* Ordinarilly, we would get additional names for the loader from
+ our DT_SONAME. This can't happen if we were actually linked as
+ a static executable (detect this case when we have no DYNAMIC).
+ If so, assume the filename component of the interpreter path to
+ be our SONAME, and add it to our name list. */
+ if (GL(dl_rtld_map).l_ld == NULL)
+ {
+ const char *p = NULL;
+ const char *cp = _dl_rtld_libname.name;
+
+ /* Find the filename part of the path. */
+ while (*cp != '\0')
+ if (*cp++ == '/')
+ p = cp;
+
+ if (p != NULL)
+ {
+ _dl_rtld_libname2.name = p;
+ /* _dl_rtld_libname2.next = NULL; Already zero. */
+ _dl_rtld_libname.next = &_dl_rtld_libname2;
+ }
+ }
+
+ has_interp = true;
+ break;
+ case PT_LOAD:
+ {
+ ElfW(Addr) mapstart;
+ ElfW(Addr) allocend;
+
+ /* Remember where the main program starts in memory. */
+ mapstart = (main_map->l_addr + (ph->p_vaddr & ~(ph->p_align - 1)));
+ if (main_map->l_map_start > mapstart)
+ main_map->l_map_start = mapstart;
+
+ /* Also where it ends. */
+ allocend = main_map->l_addr + ph->p_vaddr + ph->p_memsz;
+ if (main_map->l_map_end < allocend)
+ main_map->l_map_end = allocend;
+ if ((ph->p_flags & PF_X) && allocend > main_map->l_text_end)
+ main_map->l_text_end = allocend;
+ }
+ break;
+
+ case PT_TLS:
+#ifdef USE_TLS
+ if (ph->p_memsz > 0)
+ {
+ /* Note that in the case the dynamic linker we duplicate work
+ here since we read the PT_TLS entry already in
+ _dl_start_final. But the result is repeatable so do not
+ check for this special but unimportant case. */
+ main_map->l_tls_blocksize = ph->p_memsz;
+ main_map->l_tls_align = ph->p_align;
+ if (ph->p_align == 0)
+ main_map->l_tls_firstbyte_offset = 0;
+ else
+ main_map->l_tls_firstbyte_offset = (ph->p_vaddr
+ & (ph->p_align - 1));
+ main_map->l_tls_initimage_size = ph->p_filesz;
+ main_map->l_tls_initimage = (void *) ph->p_vaddr;
+
+ /* This image gets the ID one. */
+ GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1;
+ }
+#else
+ _dl_fatal_printf ("\
+ld.so does not support TLS, but program uses it!\n");
+#endif
+ break;
+
+ case PT_GNU_STACK:
+ GL(dl_stack_flags) = ph->p_flags;
+ break;
+
+ case PT_GNU_RELRO:
+ main_map->l_relro_addr = ph->p_vaddr;
+ main_map->l_relro_size = ph->p_memsz;
+ break;
+ }
+#ifdef USE_TLS
+ /* Adjust the address of the TLS initialization image in case
+ the executable is actually an ET_DYN object. */
+ if (main_map->l_tls_initimage != NULL)
+ main_map->l_tls_initimage
+ = (char *) main_map->l_tls_initimage + main_map->l_addr;
+#endif
+ if (! main_map->l_map_end)
+ main_map->l_map_end = ~0;
+ if (! main_map->l_text_end)
+ main_map->l_text_end = ~0;
+ if (! GL(dl_rtld_map).l_libname && GL(dl_rtld_map).l_name)
+ {
+ /* We were invoked directly, so the program might not have a
+ PT_INTERP. */
+ _dl_rtld_libname.name = GL(dl_rtld_map).l_name;
+ /* _dl_rtld_libname.next = NULL; Already zero. */
+ GL(dl_rtld_map).l_libname = &_dl_rtld_libname;
+ }
+ else
+ assert (GL(dl_rtld_map).l_libname); /* How else did we get here? */
+
+ /* If the current libname is different from the SONAME, add the
+ latter as well. */
+ if (GL(dl_rtld_map).l_info[DT_SONAME] != NULL
+ && strcmp (GL(dl_rtld_map).l_libname->name,
+ (const char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB])
+ + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_val) != 0)
+ {
+ static struct libname_list newname;
+ newname.name = ((char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB])
+ + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_ptr);
+ newname.next = NULL;
+ newname.dont_free = 1;
+
+ assert (GL(dl_rtld_map).l_libname->next == NULL);
+ GL(dl_rtld_map).l_libname->next = &newname;
+ }
+ /* The ld.so must be relocated since otherwise loading audit modules
+ will fail since they reuse the very same ld.so. */
+ assert (GL(dl_rtld_map).l_relocated);
+
+ if (! rtld_is_main)
+ {
+ /* Extract the contents of the dynamic section for easy access. */
+ elf_get_dynamic_info (main_map, NULL);
+ /* Set up our cache of pointers into the hash table. */
+ _dl_setup_hash (main_map);
+ }
+
+ if (__builtin_expect (mode, normal) == verify)
+ {
+ /* We were called just to verify that this is a dynamic
+ executable using us as the program interpreter. Exit with an
+ error if we were not able to load the binary or no interpreter
+ is specified (i.e., this is no dynamically linked binary. */
+ if (main_map->l_ld == NULL)
+ _exit (1);
+
+ /* We allow here some platform specific code. */
+#ifdef DISTINGUISH_LIB_VERSIONS
+ DISTINGUISH_LIB_VERSIONS;
+#endif
+ _exit (has_interp ? 0 : 2);
+ }
+
+ struct link_map **first_preload = &GL(dl_rtld_map).l_next;
+#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO
+ /* Set up the data structures for the system-supplied DSO early,
+ so they can influence _dl_init_paths. */
+ if (GLRO(dl_sysinfo_dso) != NULL)
+ {
+ /* Do an abridged version of the work _dl_map_object_from_fd would do
+ to map in the object. It's already mapped and prelinked (and
+ better be, since it's read-only and so we couldn't relocate it).
+ We just want our data structures to describe it as if we had just
+ mapped and relocated it normally. */
+ struct link_map *l = _dl_new_object ((char *) "", "", lt_library, NULL,
+ 0, LM_ID_BASE);
+ if (__builtin_expect (l != NULL, 1))
+ {
+ static ElfW(Dyn) dyn_temp[DL_RO_DYN_TEMP_CNT] attribute_relro;
+
+ l->l_phdr = ((const void *) GLRO(dl_sysinfo_dso)
+ + GLRO(dl_sysinfo_dso)->e_phoff);
+ l->l_phnum = GLRO(dl_sysinfo_dso)->e_phnum;
+ for (uint_fast16_t i = 0; i < l->l_phnum; ++i)
+ {
+ const ElfW(Phdr) *const ph = &l->l_phdr[i];
+ if (ph->p_type == PT_DYNAMIC)
+ {
+ l->l_ld = (void *) ph->p_vaddr;
+ l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
+ }
+ else if (ph->p_type == PT_LOAD)
+ {
+ if (! l->l_addr)
+ l->l_addr = ph->p_vaddr;
+ if (ph->p_vaddr + ph->p_memsz >= l->l_map_end)
+ l->l_map_end = ph->p_vaddr + ph->p_memsz;
+ if ((ph->p_flags & PF_X)
+ && ph->p_vaddr + ph->p_memsz >= l->l_text_end)
+ l->l_text_end = ph->p_vaddr + ph->p_memsz;
+ }
+ else
+ /* There must be no TLS segment. */
+ assert (ph->p_type != PT_TLS);
+ }
+ l->l_map_start = (ElfW(Addr)) GLRO(dl_sysinfo_dso);
+ l->l_addr = l->l_map_start - l->l_addr;
+ l->l_map_end += l->l_addr;
+ l->l_text_end += l->l_addr;
+ l->l_ld = (void *) ((ElfW(Addr)) l->l_ld + l->l_addr);
+ elf_get_dynamic_info (l, dyn_temp);
+ _dl_setup_hash (l);
+ l->l_relocated = 1;
+
+ /* Initialize l_local_scope to contain just this map. This allows
+ the use of dl_lookup_symbol_x to resolve symbols within the vdso.
+ So we create a single entry list pointing to l_real as its only
+ element */
+ l->l_local_scope[0]->r_nlist = 1;
+ l->l_local_scope[0]->r_list = &l->l_real;
+
+ /* Now that we have the info handy, use the DSO image's soname
+ so this object can be looked up by name. Note that we do not
+ set l_name here. That field gives the file name of the DSO,
+ and this DSO is not associated with any file. */
+ if (l->l_info[DT_SONAME] != NULL)
+ {
+ /* Work around a kernel problem. The kernel cannot handle
+ addresses in the vsyscall DSO pages in writev() calls. */
+ const char *dsoname = ((char *) D_PTR (l, l_info[DT_STRTAB])
+ + l->l_info[DT_SONAME]->d_un.d_val);
+ size_t len = strlen (dsoname);
+ char *copy = malloc (len);
+ if (copy == NULL)
+ _dl_fatal_printf ("out of memory\n");
+ l->l_libname->name = memcpy (copy, dsoname, len);
+ }
+
+ /* Rearrange the list so this DSO appears after rtld_map. */
+ assert (l->l_next == NULL);
+ assert (l->l_prev == main_map);
+ GL(dl_rtld_map).l_next = l;
+ l->l_prev = &GL(dl_rtld_map);
+ first_preload = &l->l_next;
+
+ /* We have a prelinked DSO preloaded by the system. */
+ GLRO(dl_sysinfo_map) = l;
+# ifdef NEED_DL_SYSINFO
+ if (GLRO(dl_sysinfo) == DL_SYSINFO_DEFAULT)
+ GLRO(dl_sysinfo) = GLRO(dl_sysinfo_dso)->e_entry + l->l_addr;
+# endif
+ }
+ }
+#endif
+
+#ifdef DL_SYSDEP_OSCHECK
+ DL_SYSDEP_OSCHECK (dl_fatal);
+#endif
+
+ /* Initialize the data structures for the search paths for shared
+ objects. */
+ _dl_init_paths (library_path);
+
+ /* Initialize _r_debug. */
+ struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr,
+ LM_ID_BASE);
+ r->r_state = RT_CONSISTENT;
+
+ /* Put the link_map for ourselves on the chain so it can be found by
+ name. Note that at this point the global chain of link maps contains
+ exactly one element, which is pointed to by dl_loaded. */
+ if (! GL(dl_rtld_map).l_name)
+ /* If not invoked directly, the dynamic linker shared object file was
+ found by the PT_INTERP name. */
+ GL(dl_rtld_map).l_name = (char *) GL(dl_rtld_map).l_libname->name;
+ GL(dl_rtld_map).l_type = lt_library;
+ main_map->l_next = &GL(dl_rtld_map);
+ GL(dl_rtld_map).l_prev = main_map;
+ ++GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
+ ++GL(dl_load_adds);
+
+ /* If LD_USE_LOAD_BIAS env variable has not been seen, default
+ to not using bias for non-prelinked PIEs and libraries
+ and using it for executables or prelinked PIEs or libraries. */
+ if (GLRO(dl_use_load_bias) == (ElfW(Addr)) -2)
+ GLRO(dl_use_load_bias) = main_map->l_addr == 0 ? -1 : 0;
+
+ /* Set up the program header information for the dynamic linker
+ itself. It is needed in the dl_iterate_phdr() callbacks. */
+ ElfW(Ehdr) *rtld_ehdr = (ElfW(Ehdr) *) GL(dl_rtld_map).l_map_start;
+ ElfW(Phdr) *rtld_phdr = (ElfW(Phdr) *) (GL(dl_rtld_map).l_map_start
+ + rtld_ehdr->e_phoff);
+ GL(dl_rtld_map).l_phdr = rtld_phdr;
+ GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum;
+
+
+ /* PT_GNU_RELRO is usually the last phdr. */
+ size_t cnt = rtld_ehdr->e_phnum;
+ while (cnt-- > 0)
+ if (rtld_phdr[cnt].p_type == PT_GNU_RELRO)
+ {
+ GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr;
+ GL(dl_rtld_map).l_relro_size = rtld_phdr[cnt].p_memsz;
+ break;
+ }
+
+#ifdef USE_TLS
+ /* Add the dynamic linker to the TLS list if it also uses TLS. */
+ if (GL(dl_rtld_map).l_tls_blocksize != 0)
+ /* Assign a module ID. Do this before loading any audit modules. */
+ GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid ();
+#endif
+
+ /* If we have auditing DSOs to load, do it now. */
+ if (__builtin_expect (audit_list != NULL, 0))
+ {
+ /* Iterate over all entries in the list. The order is important. */
+ struct audit_ifaces *last_audit = NULL;
+ struct audit_list *al = audit_list->next;
+ do
+ {
+#ifdef USE_TLS
+ int tls_idx = GL(dl_tls_max_dtv_idx);
+
+ /* Now it is time to determine the layout of the static TLS
+ block and allocate it for the initial thread. Note that we
+ always allocate the static block, we never defer it even if
+ no DF_STATIC_TLS bit is set. The reason is that we know
+ glibc will use the static model. */
+
+ /* Since we start using the auditing DSOs right away we need to
+ initialize the data structures now. */
+ tcbp = init_tls ();
+#endif
+ struct dlmopen_args dlmargs;
+ dlmargs.fname = al->name;
+ dlmargs.map = NULL;
+
+ const char *objname;
+ const char *err_str = NULL;
+ bool malloced;
+ (void) _dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit,
+ &dlmargs);
+ if (__builtin_expect (err_str != NULL, 0))
+ {
+ not_loaded:
+ _dl_error_printf ("\
+ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
+ al->name, err_str);
+ if (malloced)
+ free ((char *) err_str);
+ }
+ else
+ {
+ struct lookup_args largs;
+ largs.name = "la_version";
+ largs.map = dlmargs.map;
+
+ /* Check whether the interface version matches. */
+ (void) _dl_catch_error (&objname, &err_str, &malloced,
+ lookup_doit, &largs);
+
+ unsigned int (*laversion) (unsigned int);
+ unsigned int lav;
+ if (err_str == NULL
+ && (laversion = largs.result) != NULL
+ && (lav = laversion (LAV_CURRENT)) > 0
+ && lav <= LAV_CURRENT)
+ {
+ /* Allocate structure for the callback function pointers.
+ This call can never fail. */
+ union
+ {
+ struct audit_ifaces ifaces;
+#define naudit_ifaces 8
+ void (*fptr[naudit_ifaces]) (void);
+ } *newp = malloc (sizeof (*newp));
+
+ /* Names of the auditing interfaces. All in one
+ long string. */
+ static const char audit_iface_names[] =
+ "la_activity\0"
+ "la_objsearch\0"
+ "la_objopen\0"
+ "la_preinit\0"
+#if __ELF_NATIVE_CLASS == 32
+ "la_symbind32\0"
+#elif __ELF_NATIVE_CLASS == 64
+ "la_symbind64\0"
+#else
+# error "__ELF_NATIVE_CLASS must be defined"
+#endif
+#define STRING(s) __STRING (s)
+ "la_" STRING (ARCH_LA_PLTENTER) "\0"
+ "la_" STRING (ARCH_LA_PLTEXIT) "\0"
+ "la_objclose\0";
+ unsigned int cnt = 0;
+ const char *cp = audit_iface_names;
+ do
+ {
+ largs.name = cp;
+ (void) _dl_catch_error (&objname, &err_str, &malloced,
+ lookup_doit, &largs);
+
+ /* Store the pointer. */
+ if (err_str == NULL && largs.result != NULL)
+ {
+ newp->fptr[cnt] = largs.result;
+
+ /* The dynamic linker link map is statically
+ allocated, initialize the data now. */
+ GL(dl_rtld_map).l_audit[cnt].cookie
+ = (intptr_t) &GL(dl_rtld_map);
+ }
+ else
+ newp->fptr[cnt] = NULL;
+ ++cnt;
+
+ cp = (char *) rawmemchr (cp, '\0') + 1;
+ }
+ while (*cp != '\0');
+ assert (cnt == naudit_ifaces);
+
+ /* Now append the new auditing interface to the list. */
+ newp->ifaces.next = NULL;
+ if (last_audit == NULL)
+ last_audit = GLRO(dl_audit) = &newp->ifaces;
+ else
+ last_audit = last_audit->next = &newp->ifaces;
+ ++GLRO(dl_naudit);
+
+ /* Mark the DSO as being used for auditing. */
+ dlmargs.map->l_auditing = 1;
+ }
+ else
+ {
+ /* We cannot use the DSO, it does not have the
+ appropriate interfaces or it expects something
+ more recent. */
+#ifndef NDEBUG
+ Lmid_t ns = dlmargs.map->l_ns;
+#endif
+ _dl_close (dlmargs.map);
+
+ /* Make sure the namespace has been cleared entirely. */
+ assert (GL(dl_ns)[ns]._ns_loaded == NULL);
+ assert (GL(dl_ns)[ns]._ns_nloaded == 0);
+
+#ifdef USE_TLS
+ GL(dl_tls_max_dtv_idx) = tls_idx;
+#endif
+ goto not_loaded;
+ }
+ }
+
+ al = al->next;
+ }
+ while (al != audit_list->next);
+
+ /* If we have any auditing modules, announce that we already
+ have two objects loaded. */
+ if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
+ {
+ struct link_map *ls[2] = { main_map, &GL(dl_rtld_map) };
+
+ for (unsigned int outer = 0; outer < 2; ++outer)
+ {
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->objopen != NULL)
+ {
+ ls[outer]->l_audit[cnt].bindflags
+ = afct->objopen (ls[outer], LM_ID_BASE,
+ &ls[outer]->l_audit[cnt].cookie);
+
+ ls[outer]->l_audit_any_plt
+ |= ls[outer]->l_audit[cnt].bindflags != 0;
+ }
+
+ afct = afct->next;
+ }
+ }
+ }
+ }
+
+ /* Set up debugging before the debugger is notified for the first time. */
+#ifdef ELF_MACHINE_DEBUG_SETUP
+ /* Some machines (e.g. MIPS) don't use DT_DEBUG in this way. */
+ ELF_MACHINE_DEBUG_SETUP (main_map, r);
+ ELF_MACHINE_DEBUG_SETUP (&GL(dl_rtld_map), r);
+#else
+ if (main_map->l_info[DT_DEBUG] != NULL)
+ /* There is a DT_DEBUG entry in the dynamic section. Fill it in
+ with the run-time address of the r_debug structure */
+ main_map->l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
+
+ /* Fill in the pointer in the dynamic linker's own dynamic section, in
+ case you run gdb on the dynamic linker directly. */
+ if (GL(dl_rtld_map).l_info[DT_DEBUG] != NULL)
+ GL(dl_rtld_map).l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
+#endif
+
+ /* We start adding objects. */
+ r->r_state = RT_ADD;
+ _dl_debug_state ();
+
+ /* Auditing checkpoint: we are ready to signal that the initial map
+ is being constructed. */
+ if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
+ {
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->activity != NULL)
+ afct->activity (&main_map->l_audit[cnt].cookie, LA_ACT_ADD);
+
+ afct = afct->next;
+ }
+ }
+
+ /* We have two ways to specify objects to preload: via environment
+ variable and via the file /etc/ld.so.preload. The latter can also
+ be used when security is enabled. */
+ assert (*first_preload == NULL);
+ struct link_map **preloads = NULL;
+ unsigned int npreloads = 0;
+
+ if (__builtin_expect (preloadlist != NULL, 0))
+ {
+ /* The LD_PRELOAD environment variable gives list of libraries
+ separated by white space or colons that are loaded before the
+ executable's dependencies and prepended to the global scope
+ list. If the binary is running setuid all elements
+ containing a '/' are ignored since it is insecure. */
+ char *list = strdupa (preloadlist);
+ char *p;
+
+ HP_TIMING_NOW (start);
+
+ /* Prevent optimizing strsep. Speed is not important here. */
+ while ((p = (strsep) (&list, " :")) != NULL)
+ if (p[0] != '\0'
+ && (__builtin_expect (! INTUSE(__libc_enable_secure), 1)
+ || strchr (p, '/') == NULL))
+ npreloads += do_preload (p, main_map, "LD_PRELOAD");
+
+ HP_TIMING_NOW (stop);
+ HP_TIMING_DIFF (diff, start, stop);
+ HP_TIMING_ACCUM_NT (load_time, diff);
+ }
+
+ /* There usually is no ld.so.preload file, it should only be used
+ for emergencies and testing. So the open call etc should usually
+ fail. Using access() on a non-existing file is faster than using
+ open(). So we do this first. If it succeeds we do almost twice
+ the work but this does not matter, since it is not for production
+ use. */
+ static const char preload_file[] = "/etc/ld.so.preload";
+ if (__builtin_expect (__access (preload_file, R_OK) == 0, 0))
+ {
+ /* Read the contents of the file. */
+ file = _dl_sysdep_read_whole_file (preload_file, &file_size,
+ PROT_READ | PROT_WRITE);
+ if (__builtin_expect (file != MAP_FAILED, 0))
+ {
+ /* Parse the file. It contains names of libraries to be loaded,
+ separated by white spaces or `:'. It may also contain
+ comments introduced by `#'. */
+ char *problem;
+ char *runp;
+ size_t rest;
+
+ /* Eliminate comments. */
+ runp = file;
+ rest = file_size;
+ while (rest > 0)
+ {
+ char *comment = memchr (runp, '#', rest);
+ if (comment == NULL)
+ break;
+
+ rest -= comment - runp;
+ do
+ *comment = ' ';
+ while (--rest > 0 && *++comment != '\n');
+ }
+
+ /* We have one problematic case: if we have a name at the end of
+ the file without a trailing terminating characters, we cannot
+ place the \0. Handle the case separately. */
+ if (file[file_size - 1] != ' ' && file[file_size - 1] != '\t'
+ && file[file_size - 1] != '\n' && file[file_size - 1] != ':')
+ {
+ problem = &file[file_size];
+ while (problem > file && problem[-1] != ' '
+ && problem[-1] != '\t'
+ && problem[-1] != '\n' && problem[-1] != ':')
+ --problem;
+
+ if (problem > file)
+ problem[-1] = '\0';
+ }
+ else
+ {
+ problem = NULL;
+ file[file_size - 1] = '\0';
+ }
+
+ HP_TIMING_NOW (start);
+
+ if (file != problem)
+ {
+ char *p;
+ runp = file;
+ while ((p = strsep (&runp, ": \t\n")) != NULL)
+ if (p[0] != '\0')
+ npreloads += do_preload (p, main_map, preload_file);
+ }
+
+ if (problem != NULL)
+ {
+ char *p = strndupa (problem, file_size - (problem - file));
+
+ npreloads += do_preload (p, main_map, preload_file);
+ }
+
+ HP_TIMING_NOW (stop);
+ HP_TIMING_DIFF (diff, start, stop);
+ HP_TIMING_ACCUM_NT (load_time, diff);
+
+ /* We don't need the file anymore. */
+ __munmap (file, file_size);
+ }
+ }
+
+ if (__builtin_expect (*first_preload != NULL, 0))
+ {
+ /* Set up PRELOADS with a vector of the preloaded libraries. */
+ struct link_map *l = *first_preload;
+ preloads = __alloca (npreloads * sizeof preloads[0]);
+ i = 0;
+ do
+ {
+ preloads[i++] = l;
+ l = l->l_next;
+ } while (l);
+ assert (i == npreloads);
+ }
+
+ /* Load all the libraries specified by DT_NEEDED entries. If LD_PRELOAD
+ specified some libraries to load, these are inserted before the actual
+ dependencies in the executable's searchlist for symbol resolution. */
+ HP_TIMING_NOW (start);
+ _dl_map_object_deps (main_map, preloads, npreloads, mode == trace, 0);
+ HP_TIMING_NOW (stop);
+ HP_TIMING_DIFF (diff, start, stop);
+ HP_TIMING_ACCUM_NT (load_time, diff);
+
+ /* Mark all objects as being in the global scope. */
+ for (i = main_map->l_searchlist.r_nlist; i > 0; )
+ main_map->l_searchlist.r_list[--i]->l_global = 1;
+
+#ifndef MAP_ANON
+ /* We are done mapping things, so close the zero-fill descriptor. */
+ __close (_dl_zerofd);
+ _dl_zerofd = -1;
+#endif
+
+ /* Remove _dl_rtld_map from the chain. */
+ GL(dl_rtld_map).l_prev->l_next = GL(dl_rtld_map).l_next;
+ if (GL(dl_rtld_map).l_next != NULL)
+ GL(dl_rtld_map).l_next->l_prev = GL(dl_rtld_map).l_prev;
+
+ for (i = 1; i < main_map->l_searchlist.r_nlist; ++i)
+ if (main_map->l_searchlist.r_list[i] == &GL(dl_rtld_map))
+ break;
+
+ bool rtld_multiple_ref = false;
+ if (__builtin_expect (i < main_map->l_searchlist.r_nlist, 1))
+ {
+ /* Some DT_NEEDED entry referred to the interpreter object itself, so
+ put it back in the list of visible objects. We insert it into the
+ chain in symbol search order because gdb uses the chain's order as
+ its symbol search order. */
+ rtld_multiple_ref = true;
+
+ GL(dl_rtld_map).l_prev = main_map->l_searchlist.r_list[i - 1];
+ if (__builtin_expect (mode, normal) == normal)
+ {
+ GL(dl_rtld_map).l_next = (i + 1 < main_map->l_searchlist.r_nlist
+ ? main_map->l_searchlist.r_list[i + 1]
+ : NULL);
+#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO
+ if (GLRO(dl_sysinfo_map) != NULL
+ && GL(dl_rtld_map).l_prev->l_next == GLRO(dl_sysinfo_map)
+ && GL(dl_rtld_map).l_next != GLRO(dl_sysinfo_map))
+ GL(dl_rtld_map).l_prev = GLRO(dl_sysinfo_map);
+#endif
+ }
+ else
+ /* In trace mode there might be an invisible object (which we
+ could not find) after the previous one in the search list.
+ In this case it doesn't matter much where we put the
+ interpreter object, so we just initialize the list pointer so
+ that the assertion below holds. */
+ GL(dl_rtld_map).l_next = GL(dl_rtld_map).l_prev->l_next;
+
+ assert (GL(dl_rtld_map).l_prev->l_next == GL(dl_rtld_map).l_next);
+ GL(dl_rtld_map).l_prev->l_next = &GL(dl_rtld_map);
+ if (GL(dl_rtld_map).l_next != NULL)
+ {
+ assert (GL(dl_rtld_map).l_next->l_prev == GL(dl_rtld_map).l_prev);
+ GL(dl_rtld_map).l_next->l_prev = &GL(dl_rtld_map);
+ }
+ }
+
+ /* Now let us see whether all libraries are available in the
+ versions we need. */
+ {
+ struct version_check_args args;
+ args.doexit = mode == normal;
+ args.dotrace = mode == trace;
+ _dl_receive_error (print_missing_version, version_check_doit, &args);
+ }
+
+#ifdef USE_TLS
+ /* We do not initialize any of the TLS functionality unless any of the
+ initial modules uses TLS. This makes dynamic loading of modules with
+ TLS impossible, but to support it requires either eagerly doing setup
+ now or lazily doing it later. Doing it now makes us incompatible with
+ an old kernel that can't perform TLS_INIT_TP, even if no TLS is ever
+ used. Trying to do it lazily is too hairy to try when there could be
+ multiple threads (from a non-TLS-using libpthread). */
+ bool was_tls_init_tp_called = tls_init_tp_called;
+ if (tcbp == NULL)
+ tcbp = init_tls ();
+#endif
+
+ /* Set up the stack checker's canary. */
+ uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard ();
+#ifdef THREAD_SET_STACK_GUARD
+ THREAD_SET_STACK_GUARD (stack_chk_guard);
+#else
+ __stack_chk_guard = stack_chk_guard;
+#endif
+
+ /* Set up the pointer guard as well, if necessary. */
+ if (GLRO(dl_pointer_guard))
+ {
+ // XXX If it is cheap, we should use a separate value.
+ uintptr_t pointer_chk_guard = stack_chk_guard;
+#ifndef HP_TIMING_NONAVAIL
+ hp_timing_t now;
+ HP_TIMING_NOW (now);
+ pointer_chk_guard ^= now;
+#endif
+#ifdef THREAD_SET_POINTER_GUARD
+ THREAD_SET_POINTER_GUARD (pointer_chk_guard);
+#endif
+ __pointer_chk_guard_local = pointer_chk_guard;
+ }
+
+ if (__builtin_expect (mode, normal) != normal)
+ {
+ /* We were run just to list the shared libraries. It is
+ important that we do this before real relocation, because the
+ functions we call below for output may no longer work properly
+ after relocation. */
+ struct link_map *l;
+
+ if (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK)
+ {
+ struct r_scope_elem *scope = &main_map->l_searchlist;
+
+ for (i = 0; i < scope->r_nlist; i++)
+ {
+ l = scope->r_list [i];
+ if (l->l_faked)
+ {
+ _dl_printf ("\t%s => not found\n", l->l_libname->name);
+ continue;
+ }
+ if (_dl_name_match_p (GLRO(dl_trace_prelink), l))
+ GLRO(dl_trace_prelink_map) = l;
+ _dl_printf ("\t%s => %s (0x%0*Zx, 0x%0*Zx)",
+ l->l_libname->name[0] ? l->l_libname->name
+ : rtld_progname ?: "<main program>",
+ l->l_name[0] ? l->l_name
+ : rtld_progname ?: "<main program>",
+ (int) sizeof l->l_map_start * 2,
+ (size_t) l->l_map_start,
+ (int) sizeof l->l_addr * 2,
+ (size_t) l->l_addr);
+#ifdef USE_TLS
+ if (l->l_tls_modid)
+ _dl_printf (" TLS(0x%Zx, 0x%0*Zx)\n", l->l_tls_modid,
+ (int) sizeof l->l_tls_offset * 2,
+ (size_t) l->l_tls_offset);
+ else
+#endif
+ _dl_printf ("\n");
+ }
+ }
+ else if (GLRO(dl_debug_mask) & DL_DEBUG_UNUSED)
+ {
+ /* Look through the dependencies of the main executable
+ and determine which of them is not actually
+ required. */
+ struct link_map *l = main_map;
+
+ /* Relocate the main executable. */
+ struct relocate_args args = { .l = l, .lazy = GLRO(dl_lazy) };
+ _dl_receive_error (print_unresolved, relocate_doit, &args);
+
+ /* This loop depends on the dependencies of the executable to
+ correspond in number and order to the DT_NEEDED entries. */
+ ElfW(Dyn) *dyn = main_map->l_ld;
+ bool first = true;
+ while (dyn->d_tag != DT_NULL)
+ {
+ if (dyn->d_tag == DT_NEEDED)
+ {
+ l = l->l_next;
+
+ if (!l->l_used)
+ {
+ if (first)
+ {
+ _dl_printf ("Unused direct dependencies:\n");
+ first = false;
+ }
+
+ _dl_printf ("\t%s\n", l->l_name);
+ }
+ }
+
+ ++dyn;
+ }
+
+ _exit (first != true);
+ }
+ else if (! main_map->l_info[DT_NEEDED])
+ _dl_printf ("\tstatically linked\n");
+ else
+ {
+ for (l = main_map->l_next; l; l = l->l_next)
+ if (l->l_faked)
+ /* The library was not found. */
+ _dl_printf ("\t%s => not found\n", l->l_libname->name);
+ else if (strcmp (l->l_libname->name, l->l_name) == 0)
+ _dl_printf ("\t%s (0x%0*Zx)\n", l->l_libname->name,
+ (int) sizeof l->l_map_start * 2,
+ (size_t) l->l_map_start);
+ else
+ _dl_printf ("\t%s => %s (0x%0*Zx)\n", l->l_libname->name,
+ l->l_name, (int) sizeof l->l_map_start * 2,
+ (size_t) l->l_map_start);
+ }
+
+ if (__builtin_expect (mode, trace) != trace)
+ for (i = 1; i < (unsigned int) _dl_argc; ++i)
+ {
+ const ElfW(Sym) *ref = NULL;
+ ElfW(Addr) loadbase;
+ lookup_t result;
+
+ result = _dl_lookup_symbol_x (INTUSE(_dl_argv)[i], main_map,
+ &ref, main_map->l_scope, NULL,
+ ELF_RTYPE_CLASS_PLT,
+ DL_LOOKUP_ADD_DEPENDENCY, NULL);
+
+ loadbase = LOOKUP_VALUE_ADDRESS (result);
+
+ _dl_printf ("%s found at 0x%0*Zd in object at 0x%0*Zd\n",
+ INTUSE(_dl_argv)[i],
+ (int) sizeof ref->st_value * 2,
+ (size_t) ref->st_value,
+ (int) sizeof loadbase * 2, (size_t) loadbase);
+ }
+ else
+ {
+ /* If LD_WARN is set, warn about undefined symbols. */
+ if (GLRO(dl_lazy) >= 0 && GLRO(dl_verbose))
+ {
+ /* We have to do symbol dependency testing. */
+ struct relocate_args args;
+ struct link_map *l;
+
+ args.lazy = GLRO(dl_lazy);
+
+ l = main_map;
+ while (l->l_next != NULL)
+ l = l->l_next;
+ do
+ {
+ if (l != &GL(dl_rtld_map) && ! l->l_faked)
+ {
+ args.l = l;
+ _dl_receive_error (print_unresolved, relocate_doit,
+ &args);
+ }
+ l = l->l_prev;
+ }
+ while (l != NULL);
+
+ if ((GLRO(dl_debug_mask) & DL_DEBUG_PRELINK)
+ && rtld_multiple_ref)
+ {
+ /* Mark the link map as not yet relocated again. */
+ GL(dl_rtld_map).l_relocated = 0;
+ _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope,
+ 0, 0);
+ }
+ }
+#define VERNEEDTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (DT_VERNEED))
+ if (version_info)
+ {
+ /* Print more information. This means here, print information
+ about the versions needed. */
+ int first = 1;
+ struct link_map *map;
+
+ for (map = main_map; map != NULL; map = map->l_next)
+ {
+ const char *strtab;
+ ElfW(Dyn) *dyn = map->l_info[VERNEEDTAG];
+ ElfW(Verneed) *ent;
+
+ if (dyn == NULL)
+ continue;
+
+ strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+ ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
+
+ if (first)
+ {
+ _dl_printf ("\n\tVersion information:\n");
+ first = 0;
+ }
+
+ _dl_printf ("\t%s:\n",
+ map->l_name[0] ? map->l_name : rtld_progname);
+
+ while (1)
+ {
+ ElfW(Vernaux) *aux;
+ struct link_map *needed;
+
+ needed = find_needed (strtab + ent->vn_file);
+ aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
+
+ while (1)
+ {
+ const char *fname = NULL;
+
+ if (needed != NULL
+ && match_version (strtab + aux->vna_name,
+ needed))
+ fname = needed->l_name;
+
+ _dl_printf ("\t\t%s (%s) %s=> %s\n",
+ strtab + ent->vn_file,
+ strtab + aux->vna_name,
+ aux->vna_flags & VER_FLG_WEAK
+ ? "[WEAK] " : "",
+ fname ?: "not found");
+
+ if (aux->vna_next == 0)
+ /* No more symbols. */
+ break;
+
+ /* Next symbol. */
+ aux = (ElfW(Vernaux) *) ((char *) aux
+ + aux->vna_next);
+ }
+
+ if (ent->vn_next == 0)
+ /* No more dependencies. */
+ break;
+
+ /* Next dependency. */
+ ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
+ }
+ }
+ }
+ }
+
+ _exit (0);
+ }
+
+ if (main_map->l_info[ADDRIDX (DT_GNU_LIBLIST)]
+ && ! __builtin_expect (GLRO(dl_profile) != NULL, 0))
+ {
+ ElfW(Lib) *liblist, *liblistend;
+ struct link_map **r_list, **r_listend, *l;
+ const char *strtab = (const void *) D_PTR (main_map, l_info[DT_STRTAB]);
+
+ assert (main_map->l_info[VALIDX (DT_GNU_LIBLISTSZ)] != NULL);
+ liblist = (ElfW(Lib) *)
+ main_map->l_info[ADDRIDX (DT_GNU_LIBLIST)]->d_un.d_ptr;
+ liblistend = (ElfW(Lib) *)
+ ((char *) liblist +
+ main_map->l_info[VALIDX (DT_GNU_LIBLISTSZ)]->d_un.d_val);
+ r_list = main_map->l_searchlist.r_list;
+ r_listend = r_list + main_map->l_searchlist.r_nlist;
+
+ for (; r_list < r_listend && liblist < liblistend; r_list++)
+ {
+ l = *r_list;
+
+ if (l == main_map)
+ continue;
+
+ /* If the library is not mapped where it should, fail. */
+ if (l->l_addr)
+ break;
+
+ /* Next, check if checksum matches. */
+ if (l->l_info [VALIDX(DT_CHECKSUM)] == NULL
+ || l->l_info [VALIDX(DT_CHECKSUM)]->d_un.d_val
+ != liblist->l_checksum)
+ break;
+
+ if (l->l_info [VALIDX(DT_GNU_PRELINKED)] == NULL
+ || l->l_info [VALIDX(DT_GNU_PRELINKED)]->d_un.d_val
+ != liblist->l_time_stamp)
+ break;
+
+ if (! _dl_name_match_p (strtab + liblist->l_name, l))
+ break;
+
+ ++liblist;
+ }
+
+
+ if (r_list == r_listend && liblist == liblistend)
+ prelinked = true;
+
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
+ _dl_debug_printf ("\nprelink checking: %s\n",
+ prelinked ? "ok" : "failed");
+ }
+
+
+ /* Now set up the variable which helps the assembler startup code. */
+ GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist = &main_map->l_searchlist;
+ GL(dl_ns)[LM_ID_BASE]._ns_global_scope[0] = &main_map->l_searchlist;
+
+ /* Save the information about the original global scope list since
+ we need it in the memory handling later. */
+ GLRO(dl_initial_searchlist) = *GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist;
+
+ if (prelinked)
+ {
+ if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL)
+ {
+ ElfW(Rela) *conflict, *conflictend;
+#ifndef HP_TIMING_NONAVAIL
+ hp_timing_t start;
+ hp_timing_t stop;
+#endif
+
+ HP_TIMING_NOW (start);
+ assert (main_map->l_info [VALIDX (DT_GNU_CONFLICTSZ)] != NULL);
+ conflict = (ElfW(Rela) *)
+ main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)]->d_un.d_ptr;
+ conflictend = (ElfW(Rela) *)
+ ((char *) conflict
+ + main_map->l_info [VALIDX (DT_GNU_CONFLICTSZ)]->d_un.d_val);
+ _dl_resolve_conflicts (main_map, conflict, conflictend);
+ HP_TIMING_NOW (stop);
+ HP_TIMING_DIFF (relocate_time, start, stop);
+ }
+
+
+ /* Mark all the objects so we know they have been already relocated. */
+ for (struct link_map *l = main_map; l != NULL; l = l->l_next)
+ {
+ l->l_relocated = 1;
+ if (l->l_relro_size)
+ _dl_protect_relro (l);
+
+#ifdef USE_TLS
+ /* Add object to slot information data if necessasy. */
+ if (l->l_tls_blocksize != 0 && tls_init_tp_called)
+ _dl_add_to_slotinfo (l);
+#endif
+ }
+
+ _dl_sysdep_start_cleanup ();
+ }
+ else
+ {
+ /* Now we have all the objects loaded. Relocate them all except for
+ the dynamic linker itself. We do this in reverse order so that copy
+ relocs of earlier objects overwrite the data written by later
+ objects. We do not re-relocate the dynamic linker itself in this
+ loop because that could result in the GOT entries for functions we
+ call being changed, and that would break us. It is safe to relocate
+ the dynamic linker out of order because it has no copy relocs (we
+ know that because it is self-contained). */
+
+ int consider_profiling = GLRO(dl_profile) != NULL;
+#ifndef HP_TIMING_NONAVAIL
+ hp_timing_t start;
+ hp_timing_t stop;
+#endif
+
+ /* If we are profiling we also must do lazy reloaction. */
+ GLRO(dl_lazy) |= consider_profiling;
+
+ struct link_map *l = main_map;
+ while (l->l_next)
+ l = l->l_next;
+
+ HP_TIMING_NOW (start);
+ do
+ {
+ /* While we are at it, help the memory handling a bit. We have to
+ mark some data structures as allocated with the fake malloc()
+ implementation in ld.so. */
+ struct libname_list *lnp = l->l_libname->next;
+
+ while (__builtin_expect (lnp != NULL, 0))
+ {
+ lnp->dont_free = 1;
+ lnp = lnp->next;
+ }
+
+ if (l != &GL(dl_rtld_map))
+ _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy),
+ consider_profiling);
+
+#ifdef USE_TLS
+ /* Add object to slot information data if necessasy. */
+ if (l->l_tls_blocksize != 0 && tls_init_tp_called)
+ _dl_add_to_slotinfo (l);
+#endif
+
+ l = l->l_prev;
+ }
+ while (l);
+ HP_TIMING_NOW (stop);
+
+ HP_TIMING_DIFF (relocate_time, start, stop);
+
+ /* Do any necessary cleanups for the startup OS interface code.
+ We do these now so that no calls are made after rtld re-relocation
+ which might be resolved to different functions than we expect.
+ We cannot do this before relocating the other objects because
+ _dl_relocate_object might need to call `mprotect' for DT_TEXTREL. */
+ _dl_sysdep_start_cleanup ();
+
+ /* Now enable profiling if needed. Like the previous call,
+ this has to go here because the calls it makes should use the
+ rtld versions of the functions (particularly calloc()), but it
+ needs to have _dl_profile_map set up by the relocator. */
+ if (__builtin_expect (GL(dl_profile_map) != NULL, 0))
+ /* We must prepare the profiling. */
+ _dl_start_profile ();
+ }
+
+#ifndef NONTLS_INIT_TP
+# define NONTLS_INIT_TP do { } while (0)
+#endif
+
+#ifdef USE_TLS
+ if (!was_tls_init_tp_called && GL(dl_tls_max_dtv_idx) > 0)
+ ++GL(dl_tls_generation);
+
+ /* Now that we have completed relocation, the initializer data
+ for the TLS blocks has its final values and we can copy them
+ into the main thread's TLS area, which we allocated above. */
+ _dl_allocate_tls_init (tcbp);
+
+ /* And finally install it for the main thread. If ld.so itself uses
+ TLS we know the thread pointer was initialized earlier. */
+ if (! tls_init_tp_called)
+ {
+ const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD);
+ if (__builtin_expect (lossage != NULL, 0))
+ _dl_fatal_printf ("cannot set up thread-local storage: %s\n",
+ lossage);
+ }
+#else
+ NONTLS_INIT_TP;
+#endif
+
+ if (! prelinked && rtld_multiple_ref)
+ {
+ /* There was an explicit ref to the dynamic linker as a shared lib.
+ Re-relocate ourselves with user-controlled symbol definitions.
+
+ We must do this after TLS initialization in case after this
+ re-relocation, we might call a user-supplied function
+ (e.g. calloc from _dl_relocate_object) that uses TLS data. */
+
+#ifndef HP_TIMING_NONAVAIL
+ hp_timing_t start;
+ hp_timing_t stop;
+ hp_timing_t add;
+#endif
+
+ HP_TIMING_NOW (start);
+ /* Mark the link map as not yet relocated again. */
+ GL(dl_rtld_map).l_relocated = 0;
+ _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0);
+ HP_TIMING_NOW (stop);
+ HP_TIMING_DIFF (add, start, stop);
+ HP_TIMING_ACCUM_NT (relocate_time, add);
+ }
+
+#ifdef SHARED
+ /* Auditing checkpoint: we have added all objects. */
+ if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
+ {
+ struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+ /* Do not call the functions for any auditing object. */
+ if (head->l_auditing == 0)
+ {
+ struct audit_ifaces *afct = GLRO(dl_audit);
+ for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+ {
+ if (afct->activity != NULL)
+ afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT);
+
+ afct = afct->next;
+ }
+ }
+ }
+#endif
+
+ /* Notify the debugger all new objects are now ready to go. We must re-get
+ the address since by now the variable might be in another object. */
+ r = _dl_debug_initialize (0, LM_ID_BASE);
+ r->r_state = RT_CONSISTENT;
+ _dl_debug_state ();
+
+#ifndef MAP_COPY
+ /* We must munmap() the cache file. */
+ _dl_unload_cache ();
+#endif
+
+ /* Once we return, _dl_sysdep_start will invoke
+ the DT_INIT functions and then *USER_ENTRY. */
+}
+
+/* This is a little helper function for resolving symbols while
+ tracing the binary. */
+static void
+print_unresolved (int errcode __attribute__ ((unused)), const char *objname,
+ const char *errstring)
+{
+ if (objname[0] == '\0')
+ objname = rtld_progname ?: "<main program>";
+ _dl_error_printf ("%s (%s)\n", errstring, objname);
+}
+
+/* This is a little helper function for resolving symbols while
+ tracing the binary. */
+static void
+print_missing_version (int errcode __attribute__ ((unused)),
+ const char *objname, const char *errstring)
+{
+ _dl_error_printf ("%s: %s: %s\n", rtld_progname ?: "<program name unknown>",
+ objname, errstring);
+}
+
+/* Nonzero if any of the debugging options is enabled. */
+static int any_debug attribute_relro;
+
+/* Process the string given as the parameter which explains which debugging
+ options are enabled. */
+static void
+process_dl_debug (const char *dl_debug)
+{
+ /* When adding new entries make sure that the maximal length of a name
+ is correctly handled in the LD_DEBUG_HELP code below. */
+ static const struct
+ {
+ unsigned char len;
+ const char name[10];
+ const char helptext[41];
+ unsigned short int mask;
+ } debopts[] =
+ {
+#define LEN_AND_STR(str) sizeof (str) - 1, str
+ { LEN_AND_STR ("libs"), "display library search paths",
+ DL_DEBUG_LIBS | DL_DEBUG_IMPCALLS },
+ { LEN_AND_STR ("reloc"), "display relocation processing",
+ DL_DEBUG_RELOC | DL_DEBUG_IMPCALLS },
+ { LEN_AND_STR ("files"), "display progress for input file",
+ DL_DEBUG_FILES | DL_DEBUG_IMPCALLS },
+ { LEN_AND_STR ("symbols"), "display symbol table processing",
+ DL_DEBUG_SYMBOLS | DL_DEBUG_IMPCALLS },
+ { LEN_AND_STR ("bindings"), "display information about symbol binding",
+ DL_DEBUG_BINDINGS | DL_DEBUG_IMPCALLS },
+ { LEN_AND_STR ("versions"), "display version dependencies",
+ DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS },
+ { LEN_AND_STR ("all"), "all previous options combined",
+ DL_DEBUG_LIBS | DL_DEBUG_RELOC | DL_DEBUG_FILES | DL_DEBUG_SYMBOLS
+ | DL_DEBUG_BINDINGS | DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS },
+ { LEN_AND_STR ("statistics"), "display relocation statistics",
+ DL_DEBUG_STATISTICS },
+ { LEN_AND_STR ("unused"), "determined unused DSOs",
+ DL_DEBUG_UNUSED },
+ { LEN_AND_STR ("help"), "display this help message and exit",
+ DL_DEBUG_HELP },
+ };
+#define ndebopts (sizeof (debopts) / sizeof (debopts[0]))
+
+ /* Skip separating white spaces and commas. */
+ while (*dl_debug != '\0')
+ {
+ if (*dl_debug != ' ' && *dl_debug != ',' && *dl_debug != ':')
+ {
+ size_t cnt;
+ size_t len = 1;
+
+ while (dl_debug[len] != '\0' && dl_debug[len] != ' '
+ && dl_debug[len] != ',' && dl_debug[len] != ':')
+ ++len;
+
+ for (cnt = 0; cnt < ndebopts; ++cnt)
+ if (debopts[cnt].len == len
+ && memcmp (dl_debug, debopts[cnt].name, len) == 0)
+ {
+ GLRO(dl_debug_mask) |= debopts[cnt].mask;
+ any_debug = 1;
+ break;
+ }
+
+ if (cnt == ndebopts)
+ {
+ /* Display a warning and skip everything until next
+ separator. */
+ char *copy = strndupa (dl_debug, len);
+ _dl_error_printf ("\
+warning: debug option `%s' unknown; try LD_DEBUG=help\n", copy);
+ }
+
+ dl_debug += len;
+ continue;
+ }
+
+ ++dl_debug;
+ }
+
+ if (GLRO(dl_debug_mask) & DL_DEBUG_HELP)
+ {
+ size_t cnt;
+
+ _dl_printf ("\
+Valid options for the LD_DEBUG environment variable are:\n\n");
+
+ for (cnt = 0; cnt < ndebopts; ++cnt)
+ _dl_printf (" %.*s%s%s\n", debopts[cnt].len, debopts[cnt].name,
+ " " + debopts[cnt].len - 3,
+ debopts[cnt].helptext);
+
+ _dl_printf ("\n\
+To direct the debugging output into a file instead of standard output\n\
+a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n");
+ _exit (0);
+ }
+}
+
+static void
+process_dl_audit (char *str)
+{
+ /* The parameter is a colon separated list of DSO names. */
+ char *p;
+
+ while ((p = (strsep) (&str, ":")) != NULL)
+ if (p[0] != '\0'
+ && (__builtin_expect (! INTUSE(__libc_enable_secure), 1)
+ || strchr (p, '/') == NULL))
+ {
+ /* This is using the local malloc, not the system malloc. The
+ memory can never be freed. */
+ struct audit_list *newp = malloc (sizeof (*newp));
+ newp->name = p;
+
+ if (audit_list == NULL)
+ audit_list = newp->next = newp;
+ else
+ {
+ newp->next = audit_list->next;
+ audit_list = audit_list->next = newp;
+ }
+ }
+}
+
+/* Process all environments variables the dynamic linker must recognize.
+ Since all of them start with `LD_' we are a bit smarter while finding
+ all the entries. */
+extern char **_environ attribute_hidden;
+
+
+static void
+process_envvars (enum mode *modep)
+{
+ char **runp = _environ;
+ char *envline;
+ enum mode mode = normal;
+ char *debug_output = NULL;
+
+ /* This is the default place for profiling data file. */
+ GLRO(dl_profile_output)
+ = &"/var/tmp\0/var/profile"[INTUSE(__libc_enable_secure) ? 9 : 0];
+
+ while ((envline = _dl_next_ld_env_entry (&runp)) != NULL)
+ {
+ size_t len = 0;
+
+ while (envline[len] != '\0' && envline[len] != '=')
+ ++len;
+
+ if (envline[len] != '=')
+ /* This is a "LD_" variable at the end of the string without
+ a '=' character. Ignore it since otherwise we will access
+ invalid memory below. */
+ continue;
+
+ switch (len)
+ {
+ case 4:
+ /* Warning level, verbose or not. */
+ if (memcmp (envline, "WARN", 4) == 0)
+ GLRO(dl_verbose) = envline[5] != '\0';
+ break;
+
+ case 5:
+ /* Debugging of the dynamic linker? */
+ if (memcmp (envline, "DEBUG", 5) == 0)
+ {
+ process_dl_debug (&envline[6]);
+ break;
+ }
+ if (memcmp (envline, "AUDIT", 5) == 0)
+ process_dl_audit (&envline[6]);
+ break;
+
+ case 7:
+ /* Print information about versions. */
+ if (memcmp (envline, "VERBOSE", 7) == 0)
+ {
+ version_info = envline[8] != '\0';
+ break;
+ }
+
+ /* List of objects to be preloaded. */
+ if (memcmp (envline, "PRELOAD", 7) == 0)
+ {
+ preloadlist = &envline[8];
+ break;
+ }
+
+ /* Which shared object shall be profiled. */
+ if (memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0')
+ GLRO(dl_profile) = &envline[8];
+ break;
+
+ case 8:
+ /* Do we bind early? */
+ if (memcmp (envline, "BIND_NOW", 8) == 0)
+ {
+ GLRO(dl_lazy) = envline[9] == '\0';
+ break;
+ }
+ if (memcmp (envline, "BIND_NOT", 8) == 0)
+ GLRO(dl_bind_not) = envline[9] != '\0';
+ break;
+
+ case 9:
+ /* Test whether we want to see the content of the auxiliary
+ array passed up from the kernel. */
+ if (!INTUSE(__libc_enable_secure)
+ && memcmp (envline, "SHOW_AUXV", 9) == 0)
+ _dl_show_auxv ();
+ break;
+
+ case 10:
+ /* Mask for the important hardware capabilities. */
+ if (memcmp (envline, "HWCAP_MASK", 10) == 0)
+ GLRO(dl_hwcap_mask) = __strtoul_internal (&envline[11], NULL,
+ 0, 0);
+ break;
+
+ case 11:
+ /* Path where the binary is found. */
+ if (!INTUSE(__libc_enable_secure)
+ && memcmp (envline, "ORIGIN_PATH", 11) == 0)
+ GLRO(dl_origin_path) = &envline[12];
+ break;
+
+ case 12:
+ /* The library search path. */
+ if (memcmp (envline, "LIBRARY_PATH", 12) == 0)
+ {
+ library_path = &envline[13];
+ break;
+ }
+
+ /* Where to place the profiling data file. */
+ if (memcmp (envline, "DEBUG_OUTPUT", 12) == 0)
+ {
+ debug_output = &envline[13];
+ break;
+ }
+
+ if (!INTUSE(__libc_enable_secure)
+ && memcmp (envline, "DYNAMIC_WEAK", 12) == 0)
+ GLRO(dl_dynamic_weak) = 1;
+ break;
+
+ case 13:
+ /* We might have some extra environment variable with length 13
+ to handle. */
+#ifdef EXTRA_LD_ENVVARS_13
+ EXTRA_LD_ENVVARS_13
+#endif
+ if (!INTUSE(__libc_enable_secure)
+ && memcmp (envline, "USE_LOAD_BIAS", 13) == 0)
+ {
+ GLRO(dl_use_load_bias) = envline[14] == '1' ? -1 : 0;
+ break;
+ }
+
+ if (memcmp (envline, "POINTER_GUARD", 13) == 0)
+ GLRO(dl_pointer_guard) = envline[14] != '0';
+ break;
+
+ case 14:
+ /* Where to place the profiling data file. */
+ if (!INTUSE(__libc_enable_secure)
+ && memcmp (envline, "PROFILE_OUTPUT", 14) == 0
+ && envline[15] != '\0')
+ GLRO(dl_profile_output) = &envline[15];
+ break;
+
+ case 16:
+ /* The mode of the dynamic linker can be set. */
+ if (memcmp (envline, "TRACE_PRELINKING", 16) == 0)
+ {
+ mode = trace;
+ GLRO(dl_verbose) = 1;
+ GLRO(dl_debug_mask) |= DL_DEBUG_PRELINK;
+ GLRO(dl_trace_prelink) = &envline[17];
+ }
+ break;
+
+ case 20:
+ /* The mode of the dynamic linker can be set. */
+ if (memcmp (envline, "TRACE_LOADED_OBJECTS", 20) == 0)
+ mode = trace;
+ break;
+
+ /* We might have some extra environment variable to handle. This
+ is tricky due to the pre-processing of the length of the name
+ in the switch statement here. The code here assumes that added
+ environment variables have a different length. */
+#ifdef EXTRA_LD_ENVVARS
+ EXTRA_LD_ENVVARS
+#endif
+ }
+ }
+
+ /* The caller wants this information. */
+ *modep = mode;
+
+ /* Extra security for SUID binaries. Remove all dangerous environment
+ variables. */
+ if (__builtin_expect (INTUSE(__libc_enable_secure), 0))
+ {
+ static const char unsecure_envvars[] =
+#ifdef EXTRA_UNSECURE_ENVVARS
+ EXTRA_UNSECURE_ENVVARS
+#endif
+ UNSECURE_ENVVARS;
+ const char *nextp;
+
+ nextp = unsecure_envvars;
+ do
+ {
+ unsetenv (nextp);
+ /* We could use rawmemchr but this need not be fast. */
+ nextp = (char *) (strchr) (nextp, '\0') + 1;
+ }
+ while (*nextp != '\0');
+
+ if (__access ("/etc/suid-debug", F_OK) != 0)
+ {
+ unsetenv ("MALLOC_CHECK_");
+ GLRO(dl_debug_mask) = 0;
+ }
+
+ if (mode != normal)
+ _exit (5);
+ }
+ /* If we have to run the dynamic linker in debugging mode and the
+ LD_DEBUG_OUTPUT environment variable is given, we write the debug
+ messages to this file. */
+ else if (any_debug && debug_output != NULL)
+ {
+#ifdef O_NOFOLLOW
+ const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW;
+#else
+ const int flags = O_WRONLY | O_APPEND | O_CREAT;
+#endif
+ size_t name_len = strlen (debug_output);
+ char buf[name_len + 12];
+ char *startp;
+
+ buf[name_len + 11] = '\0';
+ startp = _itoa (__getpid (), &buf[name_len + 11], 10, 0);
+ *--startp = '.';
+ startp = memcpy (startp - name_len, debug_output, name_len);
+
+ GLRO(dl_debug_fd) = __open (startp, flags, DEFFILEMODE);
+ if (GLRO(dl_debug_fd) == -1)
+ /* We use standard output if opening the file failed. */
+ GLRO(dl_debug_fd) = STDOUT_FILENO;
+ }
+}
+
+
+/* Print the various times we collected. */
+static void
+__attribute ((noinline))
+print_statistics (hp_timing_t *rtld_total_timep)
+{
+#ifndef HP_TIMING_NONAVAIL
+ char buf[200];
+ char *cp;
+ char *wp;
+
+ /* Total time rtld used. */
+ if (HP_TIMING_AVAIL)
+ {
+ HP_TIMING_PRINT (buf, sizeof (buf), *rtld_total_timep);
+ _dl_debug_printf ("\nruntime linker statistics:\n"
+ " total startup time in dynamic loader: %s\n", buf);
+
+ /* Print relocation statistics. */
+ char pbuf[30];
+ HP_TIMING_PRINT (buf, sizeof (buf), relocate_time);
+ cp = _itoa ((1000ULL * relocate_time) / *rtld_total_timep,
+ pbuf + sizeof (pbuf), 10, 0);
+ wp = pbuf;
+ switch (pbuf + sizeof (pbuf) - cp)
+ {
+ case 3:
+ *wp++ = *cp++;
+ case 2:
+ *wp++ = *cp++;
+ case 1:
+ *wp++ = '.';
+ *wp++ = *cp++;
+ }
+ *wp = '\0';
+ _dl_debug_printf ("\
+ time needed for relocation: %s (%s%%)\n", buf, pbuf);
+ }
+#endif
+
+ unsigned long int num_relative_relocations = 0;
+ for (Lmid_t ns = 0; ns < DL_NNS; ++ns)
+ {
+ if (GL(dl_ns)[ns]._ns_loaded == NULL)
+ continue;
+
+ struct r_scope_elem *scope = &GL(dl_ns)[ns]._ns_loaded->l_searchlist;
+
+ for (unsigned int i = 0; i < scope->r_nlist; i++)
+ {
+ struct link_map *l = scope->r_list [i];
+
+ if (l->l_addr != 0 && l->l_info[VERSYMIDX (DT_RELCOUNT)])
+ num_relative_relocations
+ += l->l_info[VERSYMIDX (DT_RELCOUNT)]->d_un.d_val;
+#ifndef ELF_MACHINE_REL_RELATIVE
+ /* Relative relocations are processed on these architectures if
+ library is loaded to different address than p_vaddr or
+ if not prelinked. */
+ if ((l->l_addr != 0 || !l->l_info[VALIDX(DT_GNU_PRELINKED)])
+ && l->l_info[VERSYMIDX (DT_RELACOUNT)])
+#else
+ /* On e.g. IA-64 or Alpha, relative relocations are processed
+ only if library is loaded to different address than p_vaddr. */
+ if (l->l_addr != 0 && l->l_info[VERSYMIDX (DT_RELACOUNT)])
+#endif
+ num_relative_relocations
+ += l->l_info[VERSYMIDX (DT_RELACOUNT)]->d_un.d_val;
+ }
+ }
+
+ _dl_debug_printf (" number of relocations: %lu\n"
+ " number of relocations from cache: %lu\n"
+ " number of relative relocations: %lu\n",
+ GL(dl_num_relocations),
+ GL(dl_num_cache_relocations),
+ num_relative_relocations);
+
+#ifndef HP_TIMING_NONAVAIL
+ /* Time spend while loading the object and the dependencies. */
+ if (HP_TIMING_AVAIL)
+ {
+ char pbuf[30];
+ HP_TIMING_PRINT (buf, sizeof (buf), load_time);
+ cp = _itoa ((1000ULL * load_time) / *rtld_total_timep,
+ pbuf + sizeof (pbuf), 10, 0);
+ wp = pbuf;
+ switch (pbuf + sizeof (pbuf) - cp)
+ {
+ case 3:
+ *wp++ = *cp++;
+ case 2:
+ *wp++ = *cp++;
+ case 1:
+ *wp++ = '.';
+ *wp++ = *cp++;
+ }
+ *wp = '\0';
+ _dl_debug_printf ("\
+ time needed to load objects: %s (%s%%)\n",
+ buf, pbuf);
+ }
+#endif
+}
diff --git a/libc/elf/sln.c b/libc/elf/sln.c
new file mode 100644
index 000000000..8e66510bb
--- /dev/null
+++ b/libc/elf/sln.c
@@ -0,0 +1,189 @@
+/* `sln' program to create symbolic links between files.
+ Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <error.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#if !defined S_ISDIR && defined S_IFDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+static int makesymlink (const char *src, const char *dest);
+static int makesymlinks (const char *file);
+
+int
+main (int argc, char **argv)
+{
+ switch (argc)
+ {
+ case 2:
+ return makesymlinks (argv [1]);
+ break;
+
+ case 3:
+ return makesymlink (argv [1], argv [2]);
+ break;
+
+ default:
+ printf ("Usage: %s src dest|file\n", argv [0]);
+ return 1;
+ break;
+ }
+}
+
+static int
+makesymlinks (file)
+ const char *file;
+{
+#ifndef PATH_MAX
+#define PATH_MAX 4095
+#endif
+ char *buffer = NULL;
+ size_t bufferlen = 0;
+ int ret;
+ int lineno;
+ FILE *fp;
+
+ if (strcmp (file, "-") == 0)
+ fp = stdin;
+ else
+ {
+ fp = fopen (file, "r");
+ if (fp == NULL)
+ {
+ fprintf (stderr, "%s: file open error: %m\n", file);
+ return 1;
+ }
+ }
+
+ ret = 0;
+ lineno = 0;
+ while (!feof_unlocked (fp))
+ {
+ ssize_t n = getline (&buffer, &bufferlen, fp);
+ char *src;
+ char *dest;
+ char *cp = buffer;
+
+ if (n < 0)
+ break;
+ if (buffer[n - 1] == '\n')
+ buffer[n - 1] = '\0';
+
+ ++lineno;
+ while (isspace (*cp))
+ ++cp;
+ if (*cp == '\0')
+ /* Ignore empty lines. */
+ continue;
+ src = cp;
+
+ do
+ ++cp;
+ while (*cp != '\0' && ! isspace (*cp));
+ if (*cp != '\0')
+ *cp++ = '\0';
+
+ while (isspace (*cp))
+ ++cp;
+ if (*cp == '\0')
+ {
+ fprintf (stderr, "No target in line %d\n", lineno);
+ ret = 1;
+ continue;
+ }
+ dest = cp;
+
+ do
+ ++cp;
+ while (*cp != '\0' && ! isspace (*cp));
+ if (*cp != '\0')
+ *cp++ = '\0';
+
+ ret |= makesymlink (src, dest);
+ }
+ fclose (fp);
+
+ return ret;
+}
+
+static int
+makesymlink (src, dest)
+ const char *src;
+ const char *dest;
+{
+ struct stat stats;
+ const char *error;
+
+ /* Destination must not be a directory. */
+ if (lstat (dest, &stats) == 0)
+ {
+ if (S_ISDIR (stats.st_mode))
+ {
+ fprintf (stderr, "%s: destination must not be a directory\n",
+ dest);
+ return 1;
+ }
+ else if (unlink (dest) && errno != ENOENT)
+ {
+ fprintf (stderr, "%s: failed to remove the old destination\n",
+ dest);
+ return 1;
+ }
+ }
+ else if (errno != ENOENT)
+ {
+ error = strerror (errno);
+ fprintf (stderr, "%s: invalid destination: %s\n", dest, error);
+ return -1;
+ }
+
+#ifdef S_ISLNK
+ if (symlink (src, dest) == 0)
+#else
+ if (link (src, dest) == 0)
+#endif
+ {
+ /* Destination must exist by now. */
+ if (access (dest, F_OK))
+ {
+ error = strerror (errno);
+ unlink (dest);
+ fprintf (stderr, "Invalid link from \"%s\" to \"%s\": %s\n",
+ src, dest, error);
+ return 1;
+ }
+ return 0;
+ }
+ else
+ {
+ error = strerror (errno);
+ fprintf (stderr, "Invalid link from \"%s\" to \"%s\": %s\n",
+ src, dest, error);
+ return 1;
+ }
+}
diff --git a/libc/elf/sofini.c b/libc/elf/sofini.c
new file mode 100644
index 000000000..5e06f0ca9
--- /dev/null
+++ b/libc/elf/sofini.c
@@ -0,0 +1,17 @@
+/* Finalizer module for ELF shared C library. This provides terminating
+ null pointer words in the `.ctors' and `.dtors' sections. */
+
+static void (*const __CTOR_END__[1]) (void)
+ __attribute__ ((used, section (".ctors")))
+ = { 0 };
+static void (*const __DTOR_END__[1]) (void)
+ __attribute__ ((used, section (".dtors")))
+ = { 0 };
+
+/* Terminate the frame unwind info section with a 4byte 0 as a sentinel;
+ this would be the 'length' field in a real FDE. */
+
+typedef unsigned int ui32 __attribute__ ((mode (SI)));
+static const ui32 __FRAME_END__[1]
+ __attribute__ ((used, section (".eh_frame")))
+ = { 0 };
diff --git a/libc/elf/soinit.c b/libc/elf/soinit.c
new file mode 100644
index 000000000..c0a881ef5
--- /dev/null
+++ b/libc/elf/soinit.c
@@ -0,0 +1,46 @@
+/* Initializer module for building the ELF shared C library. This file and
+ sofini.c do the work normally done by crtbeginS.o and crtendS.o, to wrap
+ the `.ctors' and `.dtors' sections so the lists are terminated, and
+ calling those lists of functions. */
+
+#include <libc-internal.h>
+#include <stdlib.h>
+
+static void (*const __CTOR_LIST__[1]) (void)
+ __attribute__ ((section (".ctors")))
+ = { (void (*) (void)) -1 };
+static void (*const __DTOR_LIST__[1]) (void)
+ __attribute__ ((section (".dtors")))
+ = { (void (*) (void)) -1 };
+
+static inline void
+run_hooks (void (*const list[]) (void))
+{
+ while (*++list)
+ (**list) ();
+}
+
+static const char __EH_FRAME_BEGIN__[]
+ __attribute__ ((used, section (".eh_frame")))
+ = { };
+
+/* This function will be called from _init in init-first.c. */
+void
+__libc_global_ctors (void)
+{
+ /* Call constructor functions. */
+ run_hooks (__CTOR_LIST__);
+}
+
+
+/* This function becomes the DT_FINI termination function
+ for the C library. */
+void
+__libc_fini (void)
+{
+ /* Call destructor functions. */
+ run_hooks (__DTOR_LIST__);
+}
+
+void (*_fini_ptr) (void) __attribute__ ((section (".fini_array")))
+ = &__libc_fini;
diff --git a/libc/elf/sprof.c b/libc/elf/sprof.c
new file mode 100644
index 000000000..e53a7ba7a
--- /dev/null
+++ b/libc/elf/sprof.c
@@ -0,0 +1,1383 @@
+/* Read and display shared object profiling data.
+ Copyright (C) 1997-2004, 2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <argp.h>
+#include <dlfcn.h>
+#include <elf.h>
+#include <error.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <locale.h>
+#include <obstack.h>
+#include <search.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <sys/gmon.h>
+#include <sys/gmon_out.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+/* Get libc version number. */
+#include "../version.h"
+
+#define PACKAGE _libc_intl_domainname
+
+
+#include <endian.h>
+#if BYTE_ORDER == BIG_ENDIAN
+# define byteorder ELFDATA2MSB
+# define byteorder_name "big-endian"
+#elif BYTE_ORDER == LITTLE_ENDIAN
+# define byteorder ELFDATA2LSB
+# define byteorder_name "little-endian"
+#else
+# error "Unknown BYTE_ORDER " BYTE_ORDER
+# define byteorder ELFDATANONE
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+
+extern int __profile_frequency (void);
+
+/* Name and version of program. */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
+
+#define OPT_TEST 1
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Output selection:") },
+ { "call-pairs", 'c', NULL, 0,
+ N_("print list of count paths and their number of use") },
+ { "flat-profile", 'p', NULL, 0,
+ N_("generate flat profile with counts and ticks") },
+ { "graph", 'q', NULL, 0, N_("generate call graph") },
+
+ { "test", OPT_TEST, NULL, OPTION_HIDDEN, NULL },
+ { NULL, 0, NULL, 0, NULL }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("Read and display shared object profiling data.\v\
+For bug reporting instructions, please see:\n\
+<http://www.gnu.org/software/libc/bugs.html>.\n");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("SHOBJ [PROFDATA]");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc
+};
+
+
+/* Operation modes. */
+static enum
+{
+ NONE = 0,
+ FLAT_MODE = 1 << 0,
+ CALL_GRAPH_MODE = 1 << 1,
+ CALL_PAIRS = 1 << 2,
+
+ DEFAULT_MODE = FLAT_MODE | CALL_GRAPH_MODE
+} mode;
+
+/* Nozero for testing. */
+static int do_test;
+
+/* Strcuture describing calls. */
+struct here_fromstruct
+{
+ struct here_cg_arc_record volatile *here;
+ uint16_t link;
+};
+
+/* We define a special type to address the elements of the arc table.
+ This is basically the `gmon_cg_arc_record' format but it includes
+ the room for the tag and it uses real types. */
+struct here_cg_arc_record
+{
+ uintptr_t from_pc;
+ uintptr_t self_pc;
+ uint32_t count;
+} __attribute__ ((packed));
+
+
+struct known_symbol;
+struct arc_list
+{
+ size_t idx;
+ uintmax_t count;
+
+ struct arc_list *next;
+};
+
+static struct obstack ob_list;
+
+
+struct known_symbol
+{
+ const char *name;
+ uintptr_t addr;
+ size_t size;
+ bool weak;
+ bool hidden;
+
+ uintmax_t ticks;
+ uintmax_t calls;
+
+ struct arc_list *froms;
+ struct arc_list *tos;
+};
+
+
+struct shobj
+{
+ const char *name; /* User-provided name. */
+
+ struct link_map *map;
+ const char *dynstrtab; /* Dynamic string table of shared object. */
+ const char *soname; /* Soname of shared object. */
+
+ uintptr_t lowpc;
+ uintptr_t highpc;
+ unsigned long int kcountsize;
+ size_t expected_size; /* Expected size of profiling file. */
+ size_t tossize;
+ size_t fromssize;
+ size_t fromlimit;
+ unsigned int hashfraction;
+ int s_scale;
+
+ void *symbol_map;
+ size_t symbol_mapsize;
+ const ElfW(Sym) *symtab;
+ size_t symtab_size;
+ const char *strtab;
+
+ struct obstack ob_str;
+ struct obstack ob_sym;
+};
+
+
+struct profdata
+{
+ void *addr;
+ off_t size;
+
+ char *hist;
+ struct gmon_hist_hdr *hist_hdr;
+ uint16_t *kcount;
+ uint32_t narcs; /* Number of arcs in toset. */
+ struct here_cg_arc_record *data;
+ uint16_t *tos;
+ struct here_fromstruct *froms;
+};
+
+/* Search tree for symbols. */
+static void *symroot;
+static struct known_symbol **sortsym;
+static size_t symidx;
+static uintmax_t total_ticks;
+
+/* Prototypes for local functions. */
+static struct shobj *load_shobj (const char *name);
+static void unload_shobj (struct shobj *shobj);
+static struct profdata *load_profdata (const char *name, struct shobj *shobj);
+static void unload_profdata (struct profdata *profdata);
+static void count_total_ticks (struct shobj *shobj, struct profdata *profdata);
+static void count_calls (struct shobj *shobj, struct profdata *profdata);
+static void read_symbols (struct shobj *shobj);
+static void add_arcs (struct profdata *profdata);
+static void generate_flat_profile (struct profdata *profdata);
+static void generate_call_graph (struct profdata *profdata);
+static void generate_call_pair_list (struct profdata *profdata);
+
+
+int
+main (int argc, char *argv[])
+{
+ const char *shobj;
+ const char *profdata;
+ struct shobj *shobj_handle;
+ struct profdata *profdata_handle;
+ int remaining;
+
+ setlocale (LC_ALL, "");
+
+ /* Initialize the message catalog. */
+ textdomain (_libc_intl_domainname);
+
+ /* Parse and process arguments. */
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ if (argc - remaining == 0 || argc - remaining > 2)
+ {
+ /* We need exactly two non-option parameter. */
+ argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
+ program_invocation_short_name);
+ exit (1);
+ }
+
+ /* Get parameters. */
+ shobj = argv[remaining];
+ if (argc - remaining == 2)
+ profdata = argv[remaining + 1];
+ else
+ /* No filename for the profiling data given. We will determine it
+ from the soname of the shobj, later. */
+ profdata = NULL;
+
+ /* First see whether we can load the shared object. */
+ shobj_handle = load_shobj (shobj);
+ if (shobj_handle == NULL)
+ exit (1);
+
+ /* We can now determine the filename for the profiling data, if
+ nececessary. */
+ if (profdata == NULL)
+ {
+ char *newp;
+ const char *soname;
+ size_t soname_len;
+
+ soname = shobj_handle->soname ?: basename (shobj);
+ soname_len = strlen (soname);
+ newp = (char *) alloca (soname_len + sizeof ".profile");
+ stpcpy (mempcpy (newp, soname, soname_len), ".profile");
+ profdata = newp;
+ }
+
+ /* Now see whether the profiling data file matches the given object. */
+ profdata_handle = load_profdata (profdata, shobj_handle);
+ if (profdata_handle == NULL)
+ {
+ unload_shobj (shobj_handle);
+
+ exit (1);
+ }
+
+ read_symbols (shobj_handle);
+
+ /* Count the ticks. */
+ count_total_ticks (shobj_handle, profdata_handle);
+
+ /* Count the calls. */
+ count_calls (shobj_handle, profdata_handle);
+
+ /* Add the arc information. */
+ add_arcs (profdata_handle);
+
+ /* If no mode is specified fall back to the default mode. */
+ if (mode == NONE)
+ mode = DEFAULT_MODE;
+
+ /* Do some work. */
+ if (mode & FLAT_MODE)
+ generate_flat_profile (profdata_handle);
+
+ if (mode & CALL_GRAPH_MODE)
+ generate_call_graph (profdata_handle);
+
+ if (mode & CALL_PAIRS)
+ generate_call_pair_list (profdata_handle);
+
+ /* Free the resources. */
+ unload_shobj (shobj_handle);
+ unload_profdata (profdata_handle);
+
+ return 0;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'c':
+ mode |= CALL_PAIRS;
+ break;
+ case 'p':
+ mode |= FLAT_MODE;
+ break;
+ case 'q':
+ mode |= CALL_GRAPH_MODE;
+ break;
+ case OPT_TEST:
+ do_test = 1;
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+/* Print the version information. */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+ fprintf (stream, "sprof (GNU %s) %s\n", PACKAGE, VERSION);
+ fprintf (stream, gettext ("\
+Copyright (C) %s Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions. There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"),
+ "2006");
+ fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
+}
+
+
+/* Note that we must not use `dlopen' etc. The shobj object must not
+ be loaded for use. */
+static struct shobj *
+load_shobj (const char *name)
+{
+ struct link_map *map = NULL;
+ struct shobj *result;
+ ElfW(Addr) mapstart = ~((ElfW(Addr)) 0);
+ ElfW(Addr) mapend = 0;
+ const ElfW(Phdr) *ph;
+ size_t textsize;
+ unsigned int log_hashfraction;
+ ElfW(Ehdr) *ehdr;
+ int fd;
+ ElfW(Shdr) *shdr;
+ size_t pagesize = getpagesize ();
+
+ /* Since we use dlopen() we must be prepared to work around the sometimes
+ strange lookup rules for the shared objects. If we have a file foo.so
+ in the current directory and the user specfies foo.so on the command
+ line (without specifying a directory) we should load the file in the
+ current directory even if a normal dlopen() call would read the other
+ file. We do this by adding a directory portion to the name. */
+ if (strchr (name, '/') == NULL)
+ {
+ char *load_name = (char *) alloca (strlen (name) + 3);
+ stpcpy (stpcpy (load_name, "./"), name);
+
+ map = (struct link_map *) dlopen (load_name, RTLD_LAZY | __RTLD_SPROF);
+ }
+ if (map == NULL)
+ {
+ map = (struct link_map *) dlopen (name, RTLD_LAZY | __RTLD_SPROF);
+ if (map == NULL)
+ {
+ error (0, errno, _("failed to load shared object `%s'"), name);
+ return NULL;
+ }
+ }
+
+ /* Prepare the result. */
+ result = (struct shobj *) calloc (1, sizeof (struct shobj));
+ if (result == NULL)
+ {
+ error (0, errno, _("cannot create internal descriptors"));
+ dlclose (map);
+ return NULL;
+ }
+ result->name = name;
+ result->map = map;
+
+ /* Compute the size of the sections which contain program code.
+ This must match the code in dl-profile.c (_dl_start_profile). */
+ for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
+ if (ph->p_type == PT_LOAD && (ph->p_flags & PF_X))
+ {
+ ElfW(Addr) start = (ph->p_vaddr & ~(pagesize - 1));
+ ElfW(Addr) end = ((ph->p_vaddr + ph->p_memsz + pagesize - 1)
+ & ~(pagesize - 1));
+
+ if (start < mapstart)
+ mapstart = start;
+ if (end > mapend)
+ mapend = end;
+ }
+
+ result->lowpc = ROUNDDOWN ((uintptr_t) (mapstart + map->l_addr),
+ HISTFRACTION * sizeof (HISTCOUNTER));
+ result->highpc = ROUNDUP ((uintptr_t) (mapend + map->l_addr),
+ HISTFRACTION * sizeof (HISTCOUNTER));
+ if (do_test)
+ printf ("load addr: %0#*" PRIxPTR "\n"
+ "lower bound PC: %0#*" PRIxPTR "\n"
+ "upper bound PC: %0#*" PRIxPTR "\n",
+ __ELF_NATIVE_CLASS == 32 ? 10 : 18, map->l_addr,
+ __ELF_NATIVE_CLASS == 32 ? 10 : 18, result->lowpc,
+ __ELF_NATIVE_CLASS == 32 ? 10 : 18, result->highpc);
+
+ textsize = result->highpc - result->lowpc;
+ result->kcountsize = textsize / HISTFRACTION;
+ result->hashfraction = HASHFRACTION;
+ if ((HASHFRACTION & (HASHFRACTION - 1)) == 0)
+ /* If HASHFRACTION is a power of two, mcount can use shifting
+ instead of integer division. Precompute shift amount. */
+ log_hashfraction = __builtin_ffs (result->hashfraction
+ * sizeof (struct here_fromstruct)) - 1;
+ else
+ log_hashfraction = -1;
+ if (do_test)
+ printf ("hashfraction = %d\ndivider = %Zu\n",
+ result->hashfraction,
+ result->hashfraction * sizeof (struct here_fromstruct));
+ result->tossize = textsize / HASHFRACTION;
+ result->fromlimit = textsize * ARCDENSITY / 100;
+ if (result->fromlimit < MINARCS)
+ result->fromlimit = MINARCS;
+ if (result->fromlimit > MAXARCS)
+ result->fromlimit = MAXARCS;
+ result->fromssize = result->fromlimit * sizeof (struct here_fromstruct);
+
+ result->expected_size = (sizeof (struct gmon_hdr)
+ + 4 + sizeof (struct gmon_hist_hdr)
+ + result->kcountsize
+ + 4 + 4
+ + (result->fromssize
+ * sizeof (struct here_cg_arc_record)));
+
+ if (do_test)
+ printf ("expected size: %Zd\n", result->expected_size);
+
+#define SCALE_1_TO_1 0x10000L
+
+ if (result->kcountsize < result->highpc - result->lowpc)
+ {
+ size_t range = result->highpc - result->lowpc;
+ size_t quot = range / result->kcountsize;
+
+ if (quot >= SCALE_1_TO_1)
+ result->s_scale = 1;
+ else if (quot >= SCALE_1_TO_1 / 256)
+ result->s_scale = SCALE_1_TO_1 / quot;
+ else if (range > ULONG_MAX / 256)
+ result->s_scale = ((SCALE_1_TO_1 * 256)
+ / (range / (result->kcountsize / 256)));
+ else
+ result->s_scale = ((SCALE_1_TO_1 * 256)
+ / ((range * 256) / result->kcountsize));
+ }
+ else
+ result->s_scale = SCALE_1_TO_1;
+
+ if (do_test)
+ printf ("s_scale: %d\n", result->s_scale);
+
+ /* Determine the dynamic string table. */
+ if (map->l_info[DT_STRTAB] == NULL)
+ result->dynstrtab = NULL;
+ else
+ result->dynstrtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
+ if (do_test)
+ printf ("string table: %p\n", result->dynstrtab);
+
+ /* Determine the soname. */
+ if (map->l_info[DT_SONAME] == NULL)
+ result->soname = NULL;
+ else
+ result->soname = result->dynstrtab + map->l_info[DT_SONAME]->d_un.d_val;
+ if (do_test && result->soname != NULL)
+ printf ("soname: %s\n", result->soname);
+
+ /* Now we have to load the symbol table.
+
+ First load the section header table. */
+ ehdr = (ElfW(Ehdr) *) map->l_map_start;
+
+ /* Make sure we are on the right party. */
+ if (ehdr->e_shentsize != sizeof (ElfW(Shdr)))
+ abort ();
+
+ /* And we need the shared object file descriptor again. */
+ fd = open (map->l_name, O_RDONLY);
+ if (fd == -1)
+ /* Dooh, this really shouldn't happen. We know the file is available. */
+ error (EXIT_FAILURE, errno, _("Reopening shared object `%s' failed"),
+ map->l_name);
+
+ /* Map the section header. */
+ size_t size = ehdr->e_shnum * sizeof (ElfW(Shdr));
+ shdr = (ElfW(Shdr) *) alloca (size);
+ if (pread (fd, shdr, size, ehdr->e_shoff) != size)
+ error (EXIT_FAILURE, errno, _("reading of section headers failed"));
+
+ /* Get the section header string table. */
+ char *shstrtab = (char *) alloca (shdr[ehdr->e_shstrndx].sh_size);
+ if (pread (fd, shstrtab, shdr[ehdr->e_shstrndx].sh_size,
+ shdr[ehdr->e_shstrndx].sh_offset)
+ != shdr[ehdr->e_shstrndx].sh_size)
+ error (EXIT_FAILURE, errno,
+ _("reading of section header string table failed"));
+
+ /* Search for the ".symtab" section. */
+ ElfW(Shdr) *symtab_entry = NULL;
+ ElfW(Shdr) *debuglink_entry = NULL;
+ for (int idx = 0; idx < ehdr->e_shnum; ++idx)
+ if (shdr[idx].sh_type == SHT_SYMTAB
+ && strcmp (shstrtab + shdr[idx].sh_name, ".symtab") == 0)
+ {
+ symtab_entry = &shdr[idx];
+ break;
+ }
+ else if (shdr[idx].sh_type == SHT_PROGBITS
+ && strcmp (shstrtab + shdr[idx].sh_name, ".gnu_debuglink") == 0)
+ debuglink_entry = &shdr[idx];
+
+ /* Get the file name of the debuginfo file if necessary. */
+ int symfd = fd;
+ if (symtab_entry == NULL && debuglink_entry != NULL)
+ {
+ size_t size = debuglink_entry->sh_size;
+ char *debuginfo_fname = (char *) alloca (size + 1);
+ debuginfo_fname[size] = '\0';
+ if (pread (fd, debuginfo_fname, size, debuglink_entry->sh_offset)
+ != size)
+ {
+ fprintf (stderr, _("*** Cannot read debuginfo file name: %m\n"));
+ goto no_debuginfo;
+ }
+
+ static const char procpath[] = "/proc/self/fd/%d";
+ char origprocname[sizeof (procpath) + sizeof (int) * 3];
+ snprintf (origprocname, sizeof (origprocname), procpath, fd);
+ char *origlink = (char *) alloca (PATH_MAX + 1);
+ origlink[PATH_MAX] = '\0';
+ if (readlink (origprocname, origlink, PATH_MAX) == -1)
+ goto no_debuginfo;
+
+ /* Try to find the actual file. There are three places:
+ 1. the same directory the DSO is in
+ 2. in a subdir named .debug of the directory the DSO is in
+ 3. in /usr/lib/debug/PATH-OF-DSO
+ */
+ char *realname = canonicalize_file_name (origlink);
+ char *cp = NULL;
+ if (realname == NULL || (cp = strrchr (realname, '/')) == NULL)
+ error (EXIT_FAILURE, errno, _("cannot determine file name"));
+
+ /* Leave the last slash in place. */
+ *++cp = '\0';
+
+ /* First add the debuginfo file name only. */
+ static const char usrlibdebug[]= "/usr/lib/debug/";
+ char *workbuf = (char *) alloca (sizeof (usrlibdebug)
+ + (cp - realname)
+ + strlen (debuginfo_fname));
+ strcpy (stpcpy (workbuf, realname), debuginfo_fname);
+
+ int fd2 = open (workbuf, O_RDONLY);
+ if (fd2 == -1)
+ {
+ strcpy (stpcpy (stpcpy (workbuf, realname), ".debug/"),
+ debuginfo_fname);
+ fd2 = open (workbuf, O_RDONLY);
+ if (fd2 == -1)
+ {
+ strcpy (stpcpy (stpcpy (workbuf, usrlibdebug), realname),
+ debuginfo_fname);
+ fd2 = open (workbuf, O_RDONLY);
+ }
+ }
+
+ if (fd2 != -1)
+ {
+ ElfW(Ehdr) ehdr2;
+
+ /* Read the ELF header. */
+ if (pread (fd2, &ehdr2, sizeof (ehdr2), 0) != sizeof (ehdr2))
+ error (EXIT_FAILURE, errno,
+ _("reading of ELF header failed"));
+
+ /* Map the section header. */
+ size_t size = ehdr2.e_shnum * sizeof (ElfW(Shdr));
+ ElfW(Shdr) *shdr2 = (ElfW(Shdr) *) alloca (size);
+ if (pread (fd2, shdr2, size, ehdr2.e_shoff) != size)
+ error (EXIT_FAILURE, errno,
+ _("reading of section headers failed"));
+
+ /* Get the section header string table. */
+ shstrtab = (char *) alloca (shdr2[ehdr2.e_shstrndx].sh_size);
+ if (pread (fd2, shstrtab, shdr2[ehdr2.e_shstrndx].sh_size,
+ shdr2[ehdr2.e_shstrndx].sh_offset)
+ != shdr2[ehdr2.e_shstrndx].sh_size)
+ error (EXIT_FAILURE, errno,
+ _("reading of section header string table failed"));
+
+ /* Search for the ".symtab" section. */
+ for (int idx = 0; idx < ehdr2.e_shnum; ++idx)
+ if (shdr2[idx].sh_type == SHT_SYMTAB
+ && strcmp (shstrtab + shdr2[idx].sh_name, ".symtab") == 0)
+ {
+ symtab_entry = &shdr2[idx];
+ shdr = shdr2;
+ symfd = fd2;
+ break;
+ }
+
+ if (fd2 != symfd)
+ close (fd2);
+ }
+ }
+
+ no_debuginfo:
+ if (symtab_entry == NULL)
+ {
+ fprintf (stderr, _("\
+*** The file `%s' is stripped: no detailed analysis possible\n"),
+ name);
+ result->symtab = NULL;
+ result->strtab = NULL;
+ }
+ else
+ {
+ ElfW(Off) min_offset, max_offset;
+ ElfW(Shdr) *strtab_entry;
+
+ strtab_entry = &shdr[symtab_entry->sh_link];
+
+ /* Find the minimum and maximum offsets that include both the symbol
+ table and the string table. */
+ if (symtab_entry->sh_offset < strtab_entry->sh_offset)
+ {
+ min_offset = symtab_entry->sh_offset & ~(pagesize - 1);
+ max_offset = strtab_entry->sh_offset + strtab_entry->sh_size;
+ }
+ else
+ {
+ min_offset = strtab_entry->sh_offset & ~(pagesize - 1);
+ max_offset = symtab_entry->sh_offset + symtab_entry->sh_size;
+ }
+
+ result->symbol_map = mmap (NULL, max_offset - min_offset,
+ PROT_READ, MAP_SHARED|MAP_FILE, symfd,
+ min_offset);
+ if (result->symbol_map == MAP_FAILED)
+ error (EXIT_FAILURE, errno, _("failed to load symbol data"));
+
+ result->symtab
+ = (const ElfW(Sym) *) ((const char *) result->symbol_map
+ + (symtab_entry->sh_offset - min_offset));
+ result->symtab_size = symtab_entry->sh_size;
+ result->strtab = ((const char *) result->symbol_map
+ + (strtab_entry->sh_offset - min_offset));
+ result->symbol_mapsize = max_offset - min_offset;
+ }
+
+ /* Free the descriptor for the shared object. */
+ close (fd);
+ if (symfd != fd)
+ close (symfd);
+
+ return result;
+}
+
+
+static void
+unload_shobj (struct shobj *shobj)
+{
+ munmap (shobj->symbol_map, shobj->symbol_mapsize);
+ dlclose (shobj->map);
+}
+
+
+static struct profdata *
+load_profdata (const char *name, struct shobj *shobj)
+{
+ struct profdata *result;
+ int fd;
+ struct stat st;
+ void *addr;
+ struct gmon_hdr gmon_hdr;
+ struct gmon_hist_hdr hist_hdr;
+ uint32_t *narcsp;
+ size_t fromlimit;
+ struct here_cg_arc_record *data;
+ struct here_fromstruct *froms;
+ uint16_t *tos;
+ size_t fromidx;
+ size_t idx;
+
+ fd = open (name, O_RDONLY);
+ if (fd == -1)
+ {
+ char *ext_name;
+
+ if (errno != ENOENT || strchr (name, '/') != NULL)
+ /* The file exists but we are not allowed to read it or the
+ file does not exist and the name includes a path
+ specification.. */
+ return NULL;
+
+ /* A file with the given name does not exist in the current
+ directory, try it in the default location where the profiling
+ files are created. */
+ ext_name = (char *) alloca (strlen (name) + sizeof "/var/tmp/");
+ stpcpy (stpcpy (ext_name, "/var/tmp/"), name);
+ name = ext_name;
+
+ fd = open (ext_name, O_RDONLY);
+ if (fd == -1)
+ {
+ /* Even this file does not exist. */
+ error (0, errno, _("cannot load profiling data"));
+ return NULL;
+ }
+ }
+
+ /* We have found the file, now make sure it is the right one for the
+ data file. */
+ if (fstat (fd, &st) < 0)
+ {
+ error (0, errno, _("while stat'ing profiling data file"));
+ close (fd);
+ return NULL;
+ }
+
+ if ((size_t) st.st_size != shobj->expected_size)
+ {
+ error (0, 0,
+ _("profiling data file `%s' does not match shared object `%s'"),
+ name, shobj->name);
+ close (fd);
+ return NULL;
+ }
+
+ /* The data file is most probably the right one for our shared
+ object. Map it now. */
+ addr = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
+ if (addr == MAP_FAILED)
+ {
+ error (0, errno, _("failed to mmap the profiling data file"));
+ close (fd);
+ return NULL;
+ }
+
+ /* We don't need the file desriptor anymore. */
+ if (close (fd) < 0)
+ {
+ error (0, errno, _("error while closing the profiling data file"));
+ munmap (addr, st.st_size);
+ return NULL;
+ }
+
+ /* Prepare the result. */
+ result = (struct profdata *) calloc (1, sizeof (struct profdata));
+ if (result == NULL)
+ {
+ error (0, errno, _("cannot create internal descriptor"));
+ munmap (addr, st.st_size);
+ return NULL;
+ }
+
+ /* Store the address and size so that we can later free the resources. */
+ result->addr = addr;
+ result->size = st.st_size;
+
+ /* Pointer to data after the header. */
+ result->hist = (char *) ((struct gmon_hdr *) addr + 1);
+ result->hist_hdr = (struct gmon_hist_hdr *) ((char *) result->hist
+ + sizeof (uint32_t));
+ result->kcount = (uint16_t *) ((char *) result->hist + sizeof (uint32_t)
+ + sizeof (struct gmon_hist_hdr));
+
+ /* Compute pointer to array of the arc information. */
+ narcsp = (uint32_t *) ((char *) result->kcount + shobj->kcountsize
+ + sizeof (uint32_t));
+ result->narcs = *narcsp;
+ result->data = (struct here_cg_arc_record *) ((char *) narcsp
+ + sizeof (uint32_t));
+
+ /* Create the gmon_hdr we expect or write. */
+ memset (&gmon_hdr, '\0', sizeof (struct gmon_hdr));
+ memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie));
+ *(int32_t *) gmon_hdr.version = GMON_SHOBJ_VERSION;
+
+ /* Create the hist_hdr we expect or write. */
+ *(char **) hist_hdr.low_pc = (char *) shobj->lowpc - shobj->map->l_addr;
+ *(char **) hist_hdr.high_pc = (char *) shobj->highpc - shobj->map->l_addr;
+ if (do_test)
+ printf ("low_pc = %p\nhigh_pc = %p\n",
+ *(char **) hist_hdr.low_pc, *(char **) hist_hdr.high_pc);
+ *(int32_t *) hist_hdr.hist_size = shobj->kcountsize / sizeof (HISTCOUNTER);
+ *(int32_t *) hist_hdr.prof_rate = __profile_frequency ();
+ strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen));
+ hist_hdr.dimen_abbrev = 's';
+
+ /* Test whether the header of the profiling data is ok. */
+ if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0
+ || *(uint32_t *) result->hist != GMON_TAG_TIME_HIST
+ || memcmp (result->hist_hdr, &hist_hdr,
+ sizeof (struct gmon_hist_hdr)) != 0
+ || narcsp[-1] != GMON_TAG_CG_ARC)
+ {
+ error (0, 0, _("`%s' is no correct profile data file for `%s'"),
+ name, shobj->name);
+ if (do_test)
+ {
+ if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0)
+ puts ("gmon_hdr differs");
+ if (*(uint32_t *) result->hist != GMON_TAG_TIME_HIST)
+ puts ("result->hist differs");
+ if (memcmp (result->hist_hdr, &hist_hdr,
+ sizeof (struct gmon_hist_hdr)) != 0)
+ puts ("hist_hdr differs");
+ if (narcsp[-1] != GMON_TAG_CG_ARC)
+ puts ("narcsp[-1] differs");
+ }
+ free (result);
+ munmap (addr, st.st_size);
+ return NULL;
+ }
+
+ /* We are pretty sure now that this is a correct input file. Set up
+ the remaining information in the result structure and return. */
+ result->tos = (uint16_t *) calloc (shobj->tossize + shobj->fromssize, 1);
+ if (result->tos == NULL)
+ {
+ error (0, errno, _("cannot create internal descriptor"));
+ munmap (addr, st.st_size);
+ free (result);
+ return NULL;
+ }
+
+ result->froms = (struct here_fromstruct *) ((char *) result->tos
+ + shobj->tossize);
+ fromidx = 0;
+
+ /* Now we have to process all the arc count entries. */
+ fromlimit = shobj->fromlimit;
+ data = result->data;
+ froms = result->froms;
+ tos = result->tos;
+ for (idx = 0; idx < MIN (*narcsp, fromlimit); ++idx)
+ {
+ size_t to_index;
+ size_t newfromidx;
+ to_index = (data[idx].self_pc / (shobj->hashfraction * sizeof (*tos)));
+ newfromidx = fromidx++;
+ froms[newfromidx].here = &data[idx];
+ froms[newfromidx].link = tos[to_index];
+ tos[to_index] = newfromidx;
+ }
+
+ return result;
+}
+
+
+static void
+unload_profdata (struct profdata *profdata)
+{
+ free (profdata->tos);
+ munmap (profdata->addr, profdata->size);
+ free (profdata);
+}
+
+
+static void
+count_total_ticks (struct shobj *shobj, struct profdata *profdata)
+{
+ volatile uint16_t *kcount = profdata->kcount;
+ size_t maxkidx = shobj->kcountsize;
+ size_t factor = 2 * (65536 / shobj->s_scale);
+ size_t kidx = 0;
+ size_t sidx = 0;
+
+ while (sidx < symidx)
+ {
+ uintptr_t start = sortsym[sidx]->addr;
+ uintptr_t end = start + sortsym[sidx]->size;
+
+ while (kidx < maxkidx && factor * kidx < start)
+ ++kidx;
+ if (kidx == maxkidx)
+ break;
+
+ while (kidx < maxkidx && factor * kidx < end)
+ sortsym[sidx]->ticks += kcount[kidx++];
+ if (kidx == maxkidx)
+ break;
+
+ total_ticks += sortsym[sidx++]->ticks;
+ }
+}
+
+
+static size_t
+find_symbol (uintptr_t addr)
+{
+ size_t sidx = 0;
+
+ while (sidx < symidx)
+ {
+ uintptr_t start = sortsym[sidx]->addr;
+ uintptr_t end = start + sortsym[sidx]->size;
+
+ if (addr >= start && addr < end)
+ return sidx;
+
+ if (addr < start)
+ break;
+
+ ++sidx;
+ }
+
+ return (size_t) -1l;
+}
+
+
+static void
+count_calls (struct shobj *shobj, struct profdata *profdata)
+{
+ struct here_cg_arc_record *data = profdata->data;
+ uint32_t narcs = profdata->narcs;
+ uint32_t cnt;
+
+ for (cnt = 0; cnt < narcs; ++cnt)
+ {
+ uintptr_t here = data[cnt].self_pc;
+ size_t symbol_idx;
+
+ /* Find the symbol for this address. */
+ symbol_idx = find_symbol (here);
+ if (symbol_idx != (size_t) -1l)
+ sortsym[symbol_idx]->calls += data[cnt].count;
+ }
+}
+
+
+static int
+symorder (const void *o1, const void *o2)
+{
+ const struct known_symbol *p1 = (const struct known_symbol *) o1;
+ const struct known_symbol *p2 = (const struct known_symbol *) o2;
+
+ return p1->addr - p2->addr;
+}
+
+
+static void
+printsym (const void *node, VISIT value, int level)
+{
+ if (value == leaf || value == postorder)
+ sortsym[symidx++] = *(struct known_symbol **) node;
+}
+
+
+static void
+read_symbols (struct shobj *shobj)
+{
+ int n = 0;
+
+ /* Initialize the obstacks. */
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+ obstack_init (&shobj->ob_str);
+ obstack_init (&shobj->ob_sym);
+ obstack_init (&ob_list);
+
+ /* Process the symbols. */
+ if (shobj->symtab != NULL)
+ {
+ const ElfW(Sym) *sym = shobj->symtab;
+ const ElfW(Sym) *sym_end
+ = (const ElfW(Sym) *) ((const char *) sym + shobj->symtab_size);
+ for (; sym < sym_end; sym++)
+ if ((ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
+ || ELFW(ST_TYPE) (sym->st_info) == STT_NOTYPE)
+ && sym->st_size != 0)
+ {
+ struct known_symbol **existp;
+ struct known_symbol *newsym
+ = (struct known_symbol *) obstack_alloc (&shobj->ob_sym,
+ sizeof (*newsym));
+ if (newsym == NULL)
+ error (EXIT_FAILURE, errno, _("cannot allocate symbol data"));
+
+ newsym->name = &shobj->strtab[sym->st_name];
+ newsym->addr = sym->st_value;
+ newsym->size = sym->st_size;
+ newsym->weak = ELFW(ST_BIND) (sym->st_info) == STB_WEAK;
+ newsym->hidden = (ELFW(ST_VISIBILITY) (sym->st_other)
+ != STV_DEFAULT);
+ newsym->ticks = 0;
+ newsym->calls = 0;
+
+ existp = tfind (newsym, &symroot, symorder);
+ if (existp == NULL)
+ {
+ /* New function. */
+ tsearch (newsym, &symroot, symorder);
+ ++n;
+ }
+ else
+ {
+ /* The function is already defined. See whether we have
+ a better name here. */
+ if (((*existp)->hidden && !newsym->hidden)
+ || ((*existp)->name[0] == '_' && newsym->name[0] != '_')
+ || ((*existp)->name[0] != '_' && newsym->name[0] != '_'
+ && ((*existp)->weak && !newsym->weak)))
+ *existp = newsym;
+ else
+ /* We don't need the allocated memory. */
+ obstack_free (&shobj->ob_sym, newsym);
+ }
+ }
+ }
+ else
+ {
+ /* Blarg, the binary is stripped. We have to rely on the
+ information contained in the dynamic section of the object. */
+ const ElfW(Sym) *symtab = (ElfW(Sym) *) D_PTR (shobj->map,
+ l_info[DT_SYMTAB]);
+ const char *strtab = (const char *) D_PTR (shobj->map,
+ l_info[DT_STRTAB]);
+
+ /* We assume that the string table follows the symbol table,
+ because there is no way in ELF to know the size of the
+ dynamic symbol table without looking at the section headers. */
+ while ((void *) symtab < (void *) strtab)
+ {
+ if ((ELFW(ST_TYPE)(symtab->st_info) == STT_FUNC
+ || ELFW(ST_TYPE)(symtab->st_info) == STT_NOTYPE)
+ && symtab->st_size != 0)
+ {
+ struct known_symbol *newsym;
+ struct known_symbol **existp;
+
+ newsym =
+ (struct known_symbol *) obstack_alloc (&shobj->ob_sym,
+ sizeof (*newsym));
+ if (newsym == NULL)
+ error (EXIT_FAILURE, errno, _("cannot allocate symbol data"));
+
+ newsym->name = &strtab[symtab->st_name];
+ newsym->addr = symtab->st_value;
+ newsym->size = symtab->st_size;
+ newsym->weak = ELFW(ST_BIND) (symtab->st_info) == STB_WEAK;
+ newsym->hidden = (ELFW(ST_VISIBILITY) (symtab->st_other)
+ != STV_DEFAULT);
+ newsym->ticks = 0;
+ newsym->froms = NULL;
+ newsym->tos = NULL;
+
+ existp = tfind (newsym, &symroot, symorder);
+ if (existp == NULL)
+ {
+ /* New function. */
+ tsearch (newsym, &symroot, symorder);
+ ++n;
+ }
+ else
+ {
+ /* The function is already defined. See whether we have
+ a better name here. */
+ if (((*existp)->hidden && !newsym->hidden)
+ || ((*existp)->name[0] == '_' && newsym->name[0] != '_')
+ || ((*existp)->name[0] != '_' && newsym->name[0] != '_'
+ && ((*existp)->weak && !newsym->weak)))
+ *existp = newsym;
+ else
+ /* We don't need the allocated memory. */
+ obstack_free (&shobj->ob_sym, newsym);
+ }
+ }
+
+ ++symtab;
+ }
+ }
+
+ sortsym = malloc (n * sizeof (struct known_symbol *));
+ if (sortsym == NULL)
+ abort ();
+
+ twalk (symroot, printsym);
+}
+
+
+static void
+add_arcs (struct profdata *profdata)
+{
+ uint32_t narcs = profdata->narcs;
+ struct here_cg_arc_record *data = profdata->data;
+ uint32_t cnt;
+
+ for (cnt = 0; cnt < narcs; ++cnt)
+ {
+ /* First add the incoming arc. */
+ size_t sym_idx = find_symbol (data[cnt].self_pc);
+
+ if (sym_idx != (size_t) -1l)
+ {
+ struct known_symbol *sym = sortsym[sym_idx];
+ struct arc_list *runp = sym->froms;
+
+ while (runp != NULL
+ && ((data[cnt].from_pc == 0 && runp->idx != (size_t) -1l)
+ || (data[cnt].from_pc != 0
+ && (runp->idx == (size_t) -1l
+ || data[cnt].from_pc < sortsym[runp->idx]->addr
+ || (data[cnt].from_pc
+ >= (sortsym[runp->idx]->addr
+ + sortsym[runp->idx]->size))))))
+ runp = runp->next;
+
+ if (runp == NULL)
+ {
+ /* We need a new entry. */
+ struct arc_list *newp = (struct arc_list *)
+ obstack_alloc (&ob_list, sizeof (struct arc_list));
+
+ if (data[cnt].from_pc == 0)
+ newp->idx = (size_t) -1l;
+ else
+ newp->idx = find_symbol (data[cnt].from_pc);
+ newp->count = data[cnt].count;
+ newp->next = sym->froms;
+ sym->froms = newp;
+ }
+ else
+ /* Increment the counter for the found entry. */
+ runp->count += data[cnt].count;
+ }
+
+ /* Now add it to the appropriate outgoing list. */
+ sym_idx = find_symbol (data[cnt].from_pc);
+ if (sym_idx != (size_t) -1l)
+ {
+ struct known_symbol *sym = sortsym[sym_idx];
+ struct arc_list *runp = sym->tos;
+
+ while (runp != NULL
+ && (runp->idx == (size_t) -1l
+ || data[cnt].self_pc < sortsym[runp->idx]->addr
+ || data[cnt].self_pc >= (sortsym[runp->idx]->addr
+ + sortsym[runp->idx]->size)))
+ runp = runp->next;
+
+ if (runp == NULL)
+ {
+ /* We need a new entry. */
+ struct arc_list *newp = (struct arc_list *)
+ obstack_alloc (&ob_list, sizeof (struct arc_list));
+
+ newp->idx = find_symbol (data[cnt].self_pc);
+ newp->count = data[cnt].count;
+ newp->next = sym->tos;
+ sym->tos = newp;
+ }
+ else
+ /* Increment the counter for the found entry. */
+ runp->count += data[cnt].count;
+ }
+ }
+}
+
+
+static int
+countorder (const void *p1, const void *p2)
+{
+ struct known_symbol *s1 = (struct known_symbol *) p1;
+ struct known_symbol *s2 = (struct known_symbol *) p2;
+
+ if (s1->ticks != s2->ticks)
+ return (int) (s2->ticks - s1->ticks);
+
+ if (s1->calls != s2->calls)
+ return (int) (s2->calls - s1->calls);
+
+ return strcmp (s1->name, s2->name);
+}
+
+
+static double tick_unit;
+static uintmax_t cumu_ticks;
+
+static void
+printflat (const void *node, VISIT value, int level)
+{
+ if (value == leaf || value == postorder)
+ {
+ struct known_symbol *s = *(struct known_symbol **) node;
+
+ cumu_ticks += s->ticks;
+
+ printf ("%6.2f%10.2f%9.2f%9" PRIdMAX "%9.2f %s\n",
+ total_ticks ? (100.0 * s->ticks) / total_ticks : 0.0,
+ tick_unit * cumu_ticks,
+ tick_unit * s->ticks,
+ s->calls,
+ s->calls ? (s->ticks * 1000000) * tick_unit / s->calls : 0,
+ /* FIXME: don't know about called functions. */
+ s->name);
+ }
+}
+
+
+/* ARGUSED */
+static void
+freenoop (void *p)
+{
+}
+
+
+static void
+generate_flat_profile (struct profdata *profdata)
+{
+ size_t n;
+ void *data = NULL;
+
+ tick_unit = 1.0 / *(uint32_t *) profdata->hist_hdr->prof_rate;
+
+ printf ("Flat profile:\n\n"
+ "Each sample counts as %g %s.\n",
+ tick_unit, profdata->hist_hdr->dimen);
+ fputs (" % cumulative self self total\n"
+ " time seconds seconds calls us/call us/call name\n",
+ stdout);
+
+ for (n = 0; n < symidx; ++n)
+ if (sortsym[n]->calls != 0 || sortsym[n]->ticks != 0)
+ tsearch (sortsym[n], &data, countorder);
+
+ twalk (data, printflat);
+
+ tdestroy (data, freenoop);
+}
+
+
+static void
+generate_call_graph (struct profdata *profdata)
+{
+ size_t cnt;
+
+ puts ("\nindex % time self children called name\n");
+
+ for (cnt = 0; cnt < symidx; ++cnt)
+ if (sortsym[cnt]->froms != NULL || sortsym[cnt]->tos != NULL)
+ {
+ struct arc_list *runp;
+ size_t n;
+
+ /* First print the from-information. */
+ runp = sortsym[cnt]->froms;
+ while (runp != NULL)
+ {
+ printf (" %8.2f%8.2f%9" PRIdMAX "/%-9" PRIdMAX " %s",
+ (runp->idx != (size_t) -1l
+ ? sortsym[runp->idx]->ticks * tick_unit : 0.0),
+ 0.0, /* FIXME: what's time for the children, recursive */
+ runp->count, sortsym[cnt]->calls,
+ (runp->idx != (size_t) -1l ?
+ sortsym[runp->idx]->name : "<UNKNOWN>"));
+
+ if (runp->idx != (size_t) -1l)
+ printf (" [%Zd]", runp->idx);
+ putchar_unlocked ('\n');
+
+ runp = runp->next;
+ }
+
+ /* Info abount the function itself. */
+ n = printf ("[%Zu]", cnt);
+ printf ("%*s%5.1f%8.2f%8.2f%9" PRIdMAX " %s [%Zd]\n",
+ (int) (7 - n), " ",
+ total_ticks ? (100.0 * sortsym[cnt]->ticks) / total_ticks : 0,
+ sortsym[cnt]->ticks * tick_unit,
+ 0.0, /* FIXME: what's time for the children, recursive */
+ sortsym[cnt]->calls,
+ sortsym[cnt]->name, cnt);
+
+ /* Info about the functions this function calls. */
+ runp = sortsym[cnt]->tos;
+ while (runp != NULL)
+ {
+ printf (" %8.2f%8.2f%9" PRIdMAX "/",
+ (runp->idx != (size_t) -1l
+ ? sortsym[runp->idx]->ticks * tick_unit : 0.0),
+ 0.0, /* FIXME: what's time for the children, recursive */
+ runp->count);
+
+ if (runp->idx != (size_t) -1l)
+ printf ("%-9" PRIdMAX " %s [%Zd]\n",
+ sortsym[runp->idx]->calls,
+ sortsym[runp->idx]->name,
+ runp->idx);
+ else
+ fputs ("??? <UNKNOWN>\n\n", stdout);
+
+ runp = runp->next;
+ }
+
+ fputs ("-----------------------------------------------\n", stdout);
+ }
+}
+
+
+static void
+generate_call_pair_list (struct profdata *profdata)
+{
+ size_t cnt;
+
+ for (cnt = 0; cnt < symidx; ++cnt)
+ if (sortsym[cnt]->froms != NULL || sortsym[cnt]->tos != NULL)
+ {
+ struct arc_list *runp;
+
+ /* First print the incoming arcs. */
+ runp = sortsym[cnt]->froms;
+ while (runp != NULL)
+ {
+ if (runp->idx == (size_t) -1l)
+ printf ("\
+<UNKNOWN> %-34s %9" PRIdMAX "\n",
+ sortsym[cnt]->name, runp->count);
+ runp = runp->next;
+ }
+
+ /* Next the outgoing arcs. */
+ runp = sortsym[cnt]->tos;
+ while (runp != NULL)
+ {
+ printf ("%-34s %-34s %9" PRIdMAX "\n",
+ sortsym[cnt]->name,
+ (runp->idx != (size_t) -1l
+ ? sortsym[runp->idx]->name : "<UNKNOWN>"),
+ runp->count);
+ runp = runp->next;
+ }
+ }
+}
diff --git a/libc/elf/stackguard-macros.h b/libc/elf/stackguard-macros.h
new file mode 100644
index 000000000..97db8bc22
--- /dev/null
+++ b/libc/elf/stackguard-macros.h
@@ -0,0 +1,33 @@
+#include <stdint.h>
+
+#ifdef __i386__
+# define STACK_CHK_GUARD \
+ ({ uintptr_t x; asm ("movl %%gs:0x14, %0" : "=r" (x)); x; })
+#elif defined __x86_64__
+# define STACK_CHK_GUARD \
+ ({ uintptr_t x; asm ("movq %%fs:0x28, %0" : "=r" (x)); x; })
+#elif defined __powerpc64__
+# define STACK_CHK_GUARD \
+ ({ uintptr_t x; asm ("ld %0,-28688(13)" : "=r" (x)); x; })
+#elif defined __powerpc__
+# define STACK_CHK_GUARD \
+ ({ uintptr_t x; asm ("lwz %0,-28680(2)" : "=r" (x)); x; })
+#elif defined __sparc__ && defined __arch64__
+# define STACK_CHK_GUARD \
+ ({ uintptr_t x; asm ("ldx [%%g7+0x28], %0" : "=r" (x)); x; })
+#elif defined __sparc__
+# define STACK_CHK_GUARD \
+ ({ uintptr_t x; asm ("ld [%%g7+0x14], %0" : "=r" (x)); x; })
+#elif defined __s390x__
+# define STACK_CHK_GUARD \
+ ({ uintptr_t x; asm ("ear %0,%%a0; sllg %0,%0,32; ear %0,%%a1; lg %0,0x28(%0)" : "=a" (x)); x; })
+#elif defined __s390__
+# define STACK_CHK_GUARD \
+ ({ uintptr_t x; asm ("ear %0,%%a0; l %0,0x14(%0)" : "=a" (x)); x; })
+#elif defined __ia64__
+# define STACK_CHK_GUARD \
+ ({ uintptr_t x; asm ("adds %0 = -8, r13;; ld8 %0 = [%0]" : "=r" (x)); x; })
+#else
+extern uintptr_t __stack_chk_guard;
+# define STACK_CHK_GUARD __stack_chk_guard
+#endif
diff --git a/libc/elf/testobj.h b/libc/elf/testobj.h
new file mode 100644
index 000000000..12f18effc
--- /dev/null
+++ b/libc/elf/testobj.h
@@ -0,0 +1,28 @@
+extern int preload (int a);
+
+extern int foo (int);
+
+extern int obj1func1 (int);
+
+extern int obj1func2 (int);
+
+extern int obj2func1 (int);
+
+extern int obj2func2 (int);
+
+extern int obj3func1 (int);
+
+extern int obj3func2 (int);
+
+extern int obj4func1 (int);
+
+extern int obj4func2 (int);
+
+extern int obj5func1 (int);
+
+extern int obj5func2 (int);
+
+extern int obj6func1 (int);
+
+extern int obj6func2 (int);
+
diff --git a/libc/elf/testobj1.c b/libc/elf/testobj1.c
new file mode 100644
index 000000000..5ab20efd6
--- /dev/null
+++ b/libc/elf/testobj1.c
@@ -0,0 +1,25 @@
+#include <dlfcn.h>
+#include <stdlib.h>
+
+#include "testobj.h"
+
+int
+obj1func1 (int a __attribute__ ((unused)))
+{
+ return 42;
+}
+
+int
+obj1func2 (int a)
+{
+ return foo (a) + 10;
+}
+
+int
+preload (int a)
+{
+ int (*fp) (int) = dlsym (RTLD_NEXT, "preload");
+ if (fp != NULL)
+ return fp (a) + 10;
+ return 10;
+}
diff --git a/libc/elf/testobj1_1.c b/libc/elf/testobj1_1.c
new file mode 100644
index 000000000..2541a5ad1
--- /dev/null
+++ b/libc/elf/testobj1_1.c
@@ -0,0 +1,7 @@
+#include "testobj.h"
+
+int
+obj1func1 (int a)
+{
+ return 42 + obj1func2 (a);
+}
diff --git a/libc/elf/testobj2.c b/libc/elf/testobj2.c
new file mode 100644
index 000000000..7e4b61098
--- /dev/null
+++ b/libc/elf/testobj2.c
@@ -0,0 +1,32 @@
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "testobj.h"
+
+int
+obj2func1 (int a __attribute__ ((unused)))
+{
+ return 43;
+}
+
+int
+obj2func2 (int a)
+{
+ return obj1func1 (a) + 10;
+}
+
+int
+preload (int a)
+{
+ int (*fp) (int) = dlsym (RTLD_NEXT, "preload");
+ if (fp != NULL)
+ return fp (a) + 10;
+ return 10;
+}
+
+void
+p (void)
+{
+ puts ("hello world");
+}
diff --git a/libc/elf/testobj3.c b/libc/elf/testobj3.c
new file mode 100644
index 000000000..c025ff631
--- /dev/null
+++ b/libc/elf/testobj3.c
@@ -0,0 +1,26 @@
+#include <dlfcn.h>
+#include <stdlib.h>
+
+#include "testobj.h"
+
+
+int
+obj3func1 (int a __attribute__ ((unused)))
+{
+ return 44;
+}
+
+int
+obj3func2 (int a)
+{
+ return foo (a) + 42;
+}
+
+int
+preload (int a)
+{
+ int (*fp) (int) = dlsym (RTLD_NEXT, "preload");
+ if (fp != NULL)
+ return fp (a) + 10;
+ return 10;
+}
diff --git a/libc/elf/testobj4.c b/libc/elf/testobj4.c
new file mode 100644
index 000000000..2729ba32b
--- /dev/null
+++ b/libc/elf/testobj4.c
@@ -0,0 +1,25 @@
+#include <dlfcn.h>
+#include <stdlib.h>
+
+#include "testobj.h"
+
+int
+obj4func1 (int a __attribute__ ((unused)))
+{
+ return 55;
+}
+
+int
+obj4func2 (int a)
+{
+ return foo (a) + 43;
+}
+
+int
+preload (int a)
+{
+ int (*fp) (int) = dlsym (RTLD_NEXT, "preload");
+ if (fp != NULL)
+ return fp (a) + 10;
+ return 10;
+}
diff --git a/libc/elf/testobj5.c b/libc/elf/testobj5.c
new file mode 100644
index 000000000..9675cad88
--- /dev/null
+++ b/libc/elf/testobj5.c
@@ -0,0 +1,26 @@
+#include <dlfcn.h>
+#include <stdlib.h>
+
+#include "testobj.h"
+
+
+int
+obj5func1 (int a __attribute__ ((unused)))
+{
+ return 66;
+}
+
+int
+obj5func2 (int a)
+{
+ return foo (a) + 44;
+}
+
+int
+preload (int a)
+{
+ int (*fp) (int) = dlsym (RTLD_NEXT, "preload");
+ if (fp != NULL)
+ return fp (a) + 10;
+ return 10;
+}
diff --git a/libc/elf/testobj6.c b/libc/elf/testobj6.c
new file mode 100644
index 000000000..fcba01631
--- /dev/null
+++ b/libc/elf/testobj6.c
@@ -0,0 +1,19 @@
+#include "testobj.h"
+
+int
+obj6func1 (int a __attribute__ ((unused)))
+{
+ return 77;
+}
+
+int
+obj6func2 (int a)
+{
+ return foo (a) + 46;
+}
+
+int
+preload (int a)
+{
+ return a;
+}
diff --git a/libc/elf/tls-macros.h b/libc/elf/tls-macros.h
new file mode 100644
index 000000000..37cbe7514
--- /dev/null
+++ b/libc/elf/tls-macros.h
@@ -0,0 +1,851 @@
+/* Macros to support TLS testing in times of missing compiler support. */
+
+#define COMMON_INT_DEF(x) \
+ asm (".tls_common " #x ",4,4")
+/* XXX Until we get compiler support we don't need declarations. */
+#define COMMON_INT_DECL(x)
+
+/* XXX This definition will probably be machine specific, too. */
+#define VAR_INT_DEF(x) \
+ asm (".section .tdata\n\t" \
+ ".globl " #x "\n" \
+ ".balign 4\n" \
+ #x ":\t.long 0\n\t" \
+ ".size " #x ",4\n\t" \
+ ".previous")
+/* XXX Until we get compiler support we don't need declarations. */
+#define VAR_INT_DECL(x)
+
+#include_next <tls-macros.h>
+
+ /* XXX Each architecture must have its own asm for now. */
+#ifdef __i386__
+# define TLS_LE(x) \
+ ({ int *__l; \
+ asm ("movl %%gs:0,%0\n\t" \
+ "subl $" #x "@tpoff,%0" \
+ : "=r" (__l)); \
+ __l; })
+
+# ifdef PIC
+# define TLS_IE(x) \
+ ({ int *__l; \
+ asm ("movl %%gs:0,%0\n\t" \
+ "subl " #x "@gottpoff(%%ebx),%0" \
+ : "=r" (__l)); \
+ __l; })
+# else
+# define TLS_IE(x) \
+ ({ int *__l, __b; \
+ asm ("call 1f\n\t" \
+ ".subsection 1\n" \
+ "1:\tmovl (%%esp), %%ebx\n\t" \
+ "ret\n\t" \
+ ".previous\n\t" \
+ "addl $_GLOBAL_OFFSET_TABLE_, %%ebx\n\t" \
+ "movl %%gs:0,%0\n\t" \
+ "subl " #x "@gottpoff(%%ebx),%0" \
+ : "=r" (__l), "=&b" (__b)); \
+ __l; })
+# endif
+
+# ifdef PIC
+# define TLS_LD(x) \
+ ({ int *__l, __c, __d; \
+ asm ("leal " #x "@tlsldm(%%ebx),%%eax\n\t" \
+ "call ___tls_get_addr@plt\n\t" \
+ "leal " #x "@dtpoff(%%eax), %%eax" \
+ : "=a" (__l), "=&c" (__c), "=&d" (__d)); \
+ __l; })
+# else
+# define TLS_LD(x) \
+ ({ int *__l, __b, __c, __d; \
+ asm ("call 1f\n\t" \
+ ".subsection 1\n" \
+ "1:\tmovl (%%esp), %%ebx\n\t" \
+ "ret\n\t" \
+ ".previous\n\t" \
+ "addl $_GLOBAL_OFFSET_TABLE_, %%ebx\n\t" \
+ "leal " #x "@tlsldm(%%ebx),%%eax\n\t" \
+ "call ___tls_get_addr@plt\n\t" \
+ "leal " #x "@dtpoff(%%eax), %%eax" \
+ : "=a" (__l), "=&b" (__b), "=&c" (__c), "=&d" (__d)); \
+ __l; })
+# endif
+
+# ifdef PIC
+# define TLS_GD(x) \
+ ({ int *__l, __c, __d; \
+ asm ("leal " #x "@tlsgd(%%ebx),%%eax\n\t" \
+ "call ___tls_get_addr@plt\n\t" \
+ "nop" \
+ : "=a" (__l), "=&c" (__c), "=&d" (__d)); \
+ __l; })
+# else
+# define TLS_GD(x) \
+ ({ int *__l, __b, __c, __d; \
+ asm ("call 1f\n\t" \
+ ".subsection 1\n" \
+ "1:\tmovl (%%esp), %%ebx\n\t" \
+ "ret\n\t" \
+ ".previous\n\t" \
+ "addl $_GLOBAL_OFFSET_TABLE_, %%ebx\n\t" \
+ "leal " #x "@tlsgd(%%ebx),%%eax\n\t" \
+ "call ___tls_get_addr@plt\n\t" \
+ "nop" \
+ : "=a" (__l), "=&b" (__b), "=&c" (__c), "=&d" (__d)); \
+ __l; })
+# endif
+
+#elif defined __x86_64__
+
+# define TLS_LE(x) \
+ ({ int *__l; \
+ asm ("movq %%fs:0,%0\n\t" \
+ "leaq " #x "@tpoff(%0), %0" \
+ : "=r" (__l)); \
+ __l; })
+
+# define TLS_IE(x) \
+ ({ int *__l; \
+ asm ("movq %%fs:0,%0\n\t" \
+ "addq " #x "@gottpoff(%%rip),%0" \
+ : "=r" (__l)); \
+ __l; })
+
+# define TLS_LD(x) \
+ ({ int *__l, __c, __d; \
+ asm ("leaq " #x "@tlsld(%%rip),%%rdi\n\t" \
+ "call __tls_get_addr@plt\n\t" \
+ "leaq " #x "@dtpoff(%%rax), %%rax" \
+ : "=a" (__l), "=&c" (__c), "=&d" (__d) \
+ : : "rdi", "rsi", "r8", "r9", "r10", "r11"); \
+ __l; })
+
+# define TLS_GD(x) \
+ ({ int *__l, __c, __d; \
+ asm (".byte 0x66\n\t" \
+ "leaq " #x "@tlsgd(%%rip),%%rdi\n\t" \
+ ".word 0x6666\n\t" \
+ "rex64\n\t" \
+ "call __tls_get_addr@plt" \
+ : "=a" (__l), "=&c" (__c), "=&d" (__d) \
+ : : "rdi", "rsi", "r8", "r9", "r10", "r11"); \
+ __l; })
+
+#elif defined __sh__
+
+# define TLS_LE(x) \
+ ({ int *__l; void *__tp; \
+ asm ("stc gbr,%1\n\t" \
+ "mov.l 1f,%0\n\t" \
+ "bra 2f\n\t" \
+ " add %1,%0\n\t" \
+ ".align 2\n\t" \
+ "1: .long " #x "@tpoff\n\t" \
+ "2:" \
+ : "=r" (__l), "=r" (__tp)); \
+ __l; })
+
+# ifdef PIC
+# define TLS_IE(x) \
+ ({ int *__l; void *__tp; \
+ register void *__gp __asm__("r12"); \
+ asm ("mov.l 1f,r0\n\t" \
+ "stc gbr,%1\n\t" \
+ "mov.l @(r0,r12),%0\n\t" \
+ "bra 2f\n\t" \
+ " add %1,%0\n\t" \
+ ".align 2\n\t" \
+ "1: .long " #x "@gottpoff\n\t" \
+ "2:" \
+ : "=r" (__l), "=r" (__tp) : "r" (__gp) : "r0"); \
+ __l; })
+# else
+# define TLS_IE(x) \
+ ({ int *__l; void *__tp; \
+ asm ("mov.l r12,@-r15\n\t" \
+ "mova 0f,r0\n\t" \
+ "mov.l 0f,r12\n\t" \
+ "add r0,r12\n\t" \
+ "mov.l 1f,r0\n\t" \
+ "stc gbr,%1\n\t" \
+ "mov.l @(r0,r12),%0\n\t" \
+ "bra 2f\n\t" \
+ " add %1,%0\n\t" \
+ ".align 2\n\t" \
+ "1: .long " #x "@gottpoff\n\t" \
+ "0: .long _GLOBAL_OFFSET_TABLE_\n\t" \
+ "2: mov.l @r15+,r12" \
+ : "=r" (__l), "=r" (__tp) : : "r0"); \
+ __l; })
+#endif
+
+# ifdef PIC
+# define TLS_LD(x) \
+ ({ int *__l; \
+ register void *__gp __asm__("r12"); \
+ asm ("mov.l 1f,r4\n\t" \
+ "mova 2f,r0\n\t" \
+ "mov.l 2f,r1\n\t" \
+ "add r0,r1\n\t" \
+ "jsr @r1\n\t" \
+ " add r12,r4\n\t" \
+ "bra 4f\n\t" \
+ " nop\n\t" \
+ ".align 2\n\t" \
+ "1: .long " #x "@tlsldm\n\t" \
+ "2: .long __tls_get_addr@plt\n\t" \
+ "4: mov.l 3f,%0\n\t" \
+ "bra 5f\n\t" \
+ " add r0,%0\n\t" \
+ ".align 2\n\t" \
+ "3: .long " #x "@dtpoff\n\t" \
+ "5:" \
+ : "=r" (__l) : "r" (__gp) : "r0", "r1", "r2", "r3", "r4", "r5", \
+ "r6", "r7", "pr", "t"); \
+ __l; })
+# else
+# define TLS_LD(x) \
+ ({ int *__l; \
+ asm ("mov.l r12,@-r15\n\t" \
+ "mova 0f,r0\n\t" \
+ "mov.l 0f,r12\n\t" \
+ "add r0,r12\n\t" \
+ "mov.l 1f,r4\n\t" \
+ "mova 2f,r0\n\t" \
+ "mov.l 2f,r1\n\t" \
+ "add r0,r1\n\t" \
+ "jsr @r1\n\t" \
+ " add r12,r4\n\t" \
+ "bra 4f\n\t" \
+ " nop\n\t" \
+ ".align 2\n\t" \
+ "1: .long " #x "@tlsldm\n\t" \
+ "2: .long __tls_get_addr@plt\n\t" \
+ "0: .long _GLOBAL_OFFSET_TABLE_\n\t" \
+ "4: mov.l 3f,%0\n\t" \
+ "bra 5f\n\t" \
+ " add r0,%0\n\t" \
+ ".align 2\n\t" \
+ "3: .long " #x "@dtpoff\n\t" \
+ "5: mov.l @r15+,r12" \
+ : "=r" (__l) : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
+ "pr", "t"); \
+ __l; })
+#endif
+
+# ifdef PIC
+# define TLS_GD(x) \
+ ({ int *__l; \
+ register void *__gp __asm__("r12"); \
+ asm ("mov.l 1f,r4\n\t" \
+ "mova 2f,r0\n\t" \
+ "mov.l 2f,r1\n\t" \
+ "add r0,r1\n\t" \
+ "jsr @r1\n\t" \
+ " add r12,r4\n\t" \
+ "bra 3f\n\t" \
+ " mov r0,%0\n\t" \
+ ".align 2\n\t" \
+ "1: .long " #x "@tlsgd\n\t" \
+ "2: .long __tls_get_addr@plt\n\t" \
+ "3:" \
+ : "=r" (__l) : "r" (__gp) : "r0", "r1", "r2", "r3", "r4", "r5", \
+ "r6", "r7", "pr", "t"); \
+ __l; })
+# else
+# define TLS_GD(x) \
+ ({ int *__l; \
+ asm ("mov.l r12,@-r15\n\t" \
+ "mova 0f,r0\n\t" \
+ "mov.l 0f,r12\n\t" \
+ "add r0,r12\n\t" \
+ "mov.l 1f,r4\n\t" \
+ "mova 2f,r0\n\t" \
+ "mov.l 2f,r1\n\t" \
+ "add r0,r1\n\t" \
+ "jsr @r1\n\t" \
+ " add r12,r4\n\t" \
+ "bra 3f\n\t" \
+ " mov r0,%0\n\t" \
+ ".align 2\n\t" \
+ "1: .long " #x "@tlsgd\n\t" \
+ "2: .long __tls_get_addr@plt\n\t" \
+ "0: .long _GLOBAL_OFFSET_TABLE_\n\t" \
+ "3: mov.l @r15+,r12" \
+ : "=r" (__l) : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
+ "pr", "t"); \
+ __l; })
+#endif
+
+#elif defined __alpha__
+
+register void *__gp __asm__("$29");
+
+# define TLS_LE(x) \
+ ({ int *__l; \
+ asm ("call_pal 158\n\tlda $0," #x "($0)\t\t!tprel" : "=v"(__l)); \
+ __l; })
+
+# define TLS_IE(x) \
+ ({ char *__tp; unsigned long __o; \
+ asm ("call_pal 158\n\tldq %1," #x "($gp)\t\t!gottprel" \
+ : "=v"(__tp), "=r"(__o) : "r"(__gp)); \
+ (int *)(__tp + __o); })
+
+# define TLS_LD(x) \
+ ({ extern void *__tls_get_addr(void *); int *__l; void *__i; \
+ asm ("lda %0," #x "($gp)\t\t!tlsldm" : "=r" (__i) : "r"(__gp)); \
+ __i = __tls_get_addr(__i); \
+ asm ("lda %0, " #x "(%1)\t\t!dtprel" : "=r"(__l) : "r"(__i)); \
+ __l; })
+
+# define TLS_GD(x) \
+ ({ extern void *__tls_get_addr(void *); void *__i; \
+ asm ("lda %0," #x "($gp)\t\t!tlsgd" : "=r" (__i) : "r"(__gp)); \
+ (int *) __tls_get_addr(__i); })
+
+
+#elif defined __ia64__
+
+# define TLS_LE(x) \
+ ({ void *__l; \
+ asm ("mov r2=r13\n\t" \
+ ";;\n\t" \
+ "addl %0=@tprel(" #x "),r2\n\t" \
+ : "=r" (__l) : : "r2" ); __l; })
+
+# define TLS_IE(x) \
+ ({ void *__l; \
+ register long __gp asm ("gp"); \
+ asm (";;\n\t" \
+ "addl r16=@ltoff(@tprel(" #x ")),gp\n\t" \
+ ";;\n\t" \
+ "ld8 r17=[r16]\n\t" \
+ ";;\n\t" \
+ "add %0=r13,r17\n\t" \
+ ";;\n\t" \
+ : "=r" (__l) : "r" (__gp) : "r16", "r17" ); __l; })
+
+# define __TLS_CALL_CLOBBERS \
+ "r2", "r3", "r8", "r9", "r10", "r11", "r14", "r15", "r16", "r17", \
+ "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", \
+ "r27", "r28", "r29", "r30", "r31", \
+ "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15", \
+ "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", \
+ "b6", "b7", \
+ "out0", "out1", "out2", "out3", "out4", "out5", "out6", "out7"
+
+# define TLS_LD(x) \
+ ({ void *__l; \
+ register long __gp asm ("gp"); \
+ asm (";;\n\t" \
+ "mov loc0=gp\n\t" \
+ "addl r16=@ltoff(@dtpmod(" #x ")),gp\n\t" \
+ "addl out1=@dtprel(" #x "),r0\n\t" \
+ ";;\n\t" \
+ "ld8 out0=[r16]\n\t" \
+ "br.call.sptk.many b0=__tls_get_addr" \
+ ";;\n\t" \
+ "mov gp=loc0\n\t" \
+ "mov %0=r8\n\t" \
+ ";;\n\t" \
+ : "=r" (__l) : "r" (__gp) : "loc0", __TLS_CALL_CLOBBERS); \
+ __l; })
+
+# define TLS_GD(x) \
+ ({ void *__l; \
+ register long __gp asm ("gp"); \
+ asm (";;\n\t" \
+ "mov loc0=gp\n\t" \
+ "addl r16=@ltoff(@dtpmod(" #x ")),gp\n\t" \
+ "addl r17=@ltoff(@dtprel(" #x ")),gp\n\t" \
+ ";;\n\t" \
+ "ld8 out0=[r16]\n\t" \
+ "ld8 out1=[r17]\n\t" \
+ "br.call.sptk.many b0=__tls_get_addr" \
+ ";;\n\t" \
+ "mov gp=loc0\n\t" \
+ "mov %0=r8\n\t" \
+ ";;\n\t" \
+ : "=r" (__l) : "r" (__gp) : "loc0", __TLS_CALL_CLOBBERS); \
+ __l; })
+
+#elif defined __sparc__ && !defined __arch64__
+
+# define TLS_LE(x) \
+ ({ int *__l; \
+ asm ("sethi %%tle_hix22(" #x "), %0" : "=r" (__l)); \
+ asm ("xor %1, %%tle_lox10(" #x "), %0" : "=r" (__l) : "r" (__l)); \
+ asm ("add %%g7, %1, %0" : "=r" (__l) : "r" (__l)); \
+ __l; })
+
+# ifdef __PIC__
+# define TLS_LOAD_PIC \
+ ({ register long pc __asm__ ("%o7"); \
+ long got; \
+ asm ("sethi %%hi(_GLOBAL_OFFSET_TABLE_-4), %1\n\t" \
+ "call .+8\n\t" \
+ "add %1, %%lo(_GLOBAL_OFFSET_TABLE_+4), %1\n\t" \
+ "add %1, %0, %1\n\t" \
+ : "=r" (pc), "=r" (got)); \
+ got; })
+# else
+# define TLS_LOAD_PIC \
+ ({ long got; \
+ asm (".hidden _GLOBAL_OFFSET_TABLE_\n\t" \
+ "sethi %%hi(_GLOBAL_OFFSET_TABLE_), %0\n\t" \
+ "or %0, %%lo(_GLOBAL_OFFSET_TABLE_), %0" \
+ : "=r" (got)); \
+ got; })
+# endif
+
+# define TLS_IE(x) \
+ ({ int *__l; \
+ asm ("sethi %%tie_hi22(" #x "), %0" : "=r" (__l)); \
+ asm ("add %1, %%tie_lo10(" #x "), %0" : "=r" (__l) : "r" (__l)); \
+ asm ("ld [%1 + %2], %0, %%tie_ld(" #x ")" \
+ : "=r" (__l) : "r" (TLS_LOAD_PIC), "r" (__l)); \
+ asm ("add %%g7, %1, %0, %%tie_add(" #x ")" : "=r" (__l) : "r" (__l)); \
+ __l; })
+
+# define TLS_LD(x) \
+ ({ int *__l; register void *__o0 asm ("%o0"); \
+ long __o; \
+ asm ("sethi %%tldm_hi22(" #x "), %0" : "=r" (__l)); \
+ asm ("add %1, %%tldm_lo10(" #x "), %0" : "=r" (__l) : "r" (__l)); \
+ asm ("add %1, %2, %0, %%tldm_add(" #x ")" \
+ : "=r" (__o0) : "r" (TLS_LOAD_PIC), "r" (__l)); \
+ asm ("call __tls_get_addr, %%tgd_call(" #x ")\n\t" \
+ " nop" \
+ : "=r" (__o0) : "0" (__o0) \
+ : "g1", "g2", "g3", "g4", "g5", "g6", "o1", "o2", "o3", "o4", \
+ "o5", "o7", "cc"); \
+ asm ("sethi %%tldo_hix22(" #x "), %0" : "=r" (__o)); \
+ asm ("xor %1, %%tldo_lox10(" #x "), %0" : "=r" (__o) : "r" (__o)); \
+ asm ("add %1, %2, %0, %%tldo_add(" #x ")" : "=r" (__l) \
+ : "r" (__o0), "r" (__o)); \
+ __l; })
+
+# define TLS_GD(x) \
+ ({ int *__l; register void *__o0 asm ("%o0"); \
+ asm ("sethi %%tgd_hi22(" #x "), %0" : "=r" (__l)); \
+ asm ("add %1, %%tgd_lo10(" #x "), %0" : "=r" (__l) : "r" (__l)); \
+ asm ("add %1, %2, %0, %%tgd_add(" #x ")" \
+ : "=r" (__o0) : "r" (TLS_LOAD_PIC), "r" (__l)); \
+ asm ("call __tls_get_addr, %%tgd_call(" #x ")\n\t" \
+ " nop" \
+ : "=r" (__o0) : "0" (__o0) \
+ : "g1", "g2", "g3", "g4", "g5", "g6", "o1", "o2", "o3", "o4", \
+ "o5", "o7", "cc"); \
+ __o0; })
+
+#elif defined __sparc__ && defined __arch64__
+
+# define TLS_LE(x) \
+ ({ int *__l; \
+ asm ("sethi %%tle_hix22(" #x "), %0" : "=r" (__l)); \
+ asm ("xor %1, %%tle_lox10(" #x "), %0" : "=r" (__l) : "r" (__l)); \
+ asm ("add %%g7, %1, %0" : "=r" (__l) : "r" (__l)); \
+ __l; })
+
+# ifdef __PIC__
+# define TLS_LOAD_PIC \
+ ({ long pc, got; \
+ asm ("sethi %%hi(_GLOBAL_OFFSET_TABLE_-4), %1\n\t" \
+ "rd %%pc, %0\n\t" \
+ "add %1, %%lo(_GLOBAL_OFFSET_TABLE_+4), %1\n\t" \
+ "add %1, %0, %1\n\t" \
+ : "=r" (pc), "=r" (got)); \
+ got; })
+# else
+# define TLS_LOAD_PIC \
+ ({ long got; \
+ asm (".hidden _GLOBAL_OFFSET_TABLE_\n\t" \
+ "sethi %%hi(_GLOBAL_OFFSET_TABLE_), %0\n\t" \
+ "or %0, %%lo(_GLOBAL_OFFSET_TABLE_), %0" \
+ : "=r" (got)); \
+ got; })
+# endif
+
+# define TLS_IE(x) \
+ ({ int *__l; \
+ asm ("sethi %%tie_hi22(" #x "), %0" : "=r" (__l)); \
+ asm ("add %1, %%tie_lo10(" #x "), %0" : "=r" (__l) : "r" (__l)); \
+ asm ("ldx [%1 + %2], %0, %%tie_ldx(" #x ")" \
+ : "=r" (__l) : "r" (TLS_LOAD_PIC), "r" (__l)); \
+ asm ("add %%g7, %1, %0, %%tie_add(" #x ")" : "=r" (__l) : "r" (__l)); \
+ __l; })
+
+# define TLS_LD(x) \
+ ({ int *__l; register void *__o0 asm ("%o0"); \
+ long __o; \
+ asm ("sethi %%tldm_hi22(" #x "), %0" : "=r" (__l)); \
+ asm ("add %1, %%tldm_lo10(" #x "), %0" : "=r" (__l) : "r" (__l)); \
+ asm ("add %1, %2, %0, %%tldm_add(" #x ")" \
+ : "=r" (__o0) : "r" (TLS_LOAD_PIC), "r" (__l)); \
+ asm ("call __tls_get_addr, %%tgd_call(" #x ")\n\t" \
+ " nop" \
+ : "=r" (__o0) : "0" (__o0) \
+ : "g1", "g2", "g3", "g4", "g5", "g6", "o1", "o2", "o3", "o4", \
+ "o5", "o7", "cc"); \
+ asm ("sethi %%tldo_hix22(" #x "), %0" : "=r" (__o)); \
+ asm ("xor %1, %%tldo_lox10(" #x "), %0" : "=r" (__o) : "r" (__o)); \
+ asm ("add %1, %2, %0, %%tldo_add(" #x ")" : "=r" (__l) \
+ : "r" (__o0), "r" (__o)); \
+ __l; })
+
+# define TLS_GD(x) \
+ ({ int *__l; register void *__o0 asm ("%o0"); \
+ asm ("sethi %%tgd_hi22(" #x "), %0" : "=r" (__l)); \
+ asm ("add %1, %%tgd_lo10(" #x "), %0" : "=r" (__l) : "r" (__l)); \
+ asm ("add %1, %2, %0, %%tgd_add(" #x ")" \
+ : "=r" (__o0) : "r" (TLS_LOAD_PIC), "r" (__l)); \
+ asm ("call __tls_get_addr, %%tgd_call(" #x ")\n\t" \
+ " nop" \
+ : "=r" (__o0) : "0" (__o0) \
+ : "g1", "g2", "g3", "g4", "g5", "g6", "o1", "o2", "o3", "o4", \
+ "o5", "o7", "cc"); \
+ __o0; })
+
+#elif defined __s390x__
+
+# define TLS_LE(x) \
+ ({ unsigned long __offset; \
+ asm ("bras %0,1f\n" \
+ "0:\t.quad " #x "@ntpoff\n" \
+ "1:\tlg %0,0(%0)" \
+ : "=a" (__offset) : : "cc" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+
+# ifdef PIC
+# define TLS_IE(x) \
+ ({ unsigned long __offset; \
+ asm ("bras %0,1f\n" \
+ "0:\t.quad " #x "@gotntpoff\n" \
+ "1:\tlg %0,0(%0)\n\t" \
+ "lg %0,0(%0,%%r12):tls_load:" #x \
+ : "=&a" (__offset) : : "cc" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# else
+# define TLS_IE(x) \
+ ({ unsigned long __offset; \
+ asm ("bras %0,1f\n" \
+ "0:\t.quad " #x "@indntpoff\n" \
+ "1:\t lg %0,0(%0)\n\t" \
+ "lg %0,0(%0):tls_load:" #x \
+ : "=&a" (__offset) : : "cc" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
+# ifdef PIC
+# define TLS_LD(x) \
+ ({ unsigned long __offset, __save12; \
+ asm ("bras %0,1f\n" \
+ "0:\t.quad " #x "@tlsldm\n\t" \
+ ".quad " #x "@dtpoff\n" \
+ "1:\tlgr %1,%%r12\n\t" \
+ "larl %%r12,_GLOBAL_OFFSET_TABLE_\n\t" \
+ "lg %%r2,0(%0)\n\t" \
+ "brasl %%r14,__tls_get_offset@plt:tls_ldcall:" #x "\n\t" \
+ "lg %0,8(%0)\n\t" \
+ "algr %0,%%r2\n\t" \
+ "lgr %%r12,%1" \
+ : "=&a" (__offset), "=&a" (__save12) \
+ : : "cc", "0", "1", "2", "3", "4", "5", "14" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# else
+# define TLS_LD(x) \
+ ({ unsigned long __offset; \
+ asm ("bras %0,1f\n" \
+ "0:\t.quad " #x "@tlsldm\n\t" \
+ ".quad " #x "@dtpoff\n" \
+ "1:\tlarl %%r12,_GLOBAL_OFFSET_TABLE_\n\t" \
+ "lg %%r2,0(%0)\n\t" \
+ "brasl %%r14,__tls_get_offset@plt:tls_ldcall:" #x "\n\t" \
+ "lg %0,8(%0)\n\t" \
+ "algr %0,%%r2" \
+ : "=&a" (__offset) \
+ : : "cc", "0", "1", "2", "3", "4", "5", "12", "14" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
+# ifdef PIC
+# define TLS_GD(x) \
+ ({ unsigned long __offset, __save12; \
+ asm ("bras %0,1f\n" \
+ "0:\t.quad " #x "@tlsgd\n" \
+ "1:\tlgr %1,%%r12\n\t" \
+ "larl %%r12,_GLOBAL_OFFSET_TABLE_\n\t" \
+ "lg %%r2,0(%0)\n\t" \
+ "brasl %%r14,__tls_get_offset@plt:tls_gdcall:" #x "\n\t" \
+ "lgr %0,%%r2\n\t" \
+ "lgr %%r12,%1" \
+ : "=&a" (__offset), "=&a" (__save12) \
+ : : "cc", "0", "1", "2", "3", "4", "5", "14" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# else
+# define TLS_GD(x) \
+ ({ unsigned long __offset; \
+ asm ("bras %0,1f\n" \
+ "0:\t.quad " #x "@tlsgd\n" \
+ "1:\tlarl %%r12,_GLOBAL_OFFSET_TABLE_\n\t" \
+ "lg %%r2,0(%0)\n\t" \
+ "brasl %%r14,__tls_get_offset@plt:tls_gdcall:" #x "\n\t" \
+ "lgr %0,%%r2" \
+ : "=&a" (__offset) \
+ : : "cc", "0", "1", "2", "3", "4", "5", "12", "14" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
+#elif defined __s390__
+
+# define TLS_LE(x) \
+ ({ unsigned long __offset; \
+ asm ("bras %0,1f\n" \
+ "0:\t.long " #x "@ntpoff\n" \
+ "1:\tl %0,0(%0)" \
+ : "=a" (__offset) : : "cc" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+
+# ifdef PIC
+# define TLS_IE(x) \
+ ({ unsigned long __offset; \
+ asm ("bras %0,1f\n" \
+ "0:\t.long " #x "@gotntpoff\n" \
+ "1:\tl %0,0(%0)\n\t" \
+ "l %0,0(%0,%%r12):tls_load:" #x \
+ : "=&a" (__offset) : : "cc" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# else
+# define TLS_IE(x) \
+ ({ unsigned long __offset; \
+ asm ("bras %0,1f\n" \
+ "0:\t.long " #x "@indntpoff\n" \
+ "1:\t l %0,0(%0)\n\t" \
+ "l %0,0(%0):tls_load:" #x \
+ : "=&a" (__offset) : : "cc" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
+# ifdef PIC
+# define TLS_LD(x) \
+ ({ unsigned long __offset, __save12; \
+ asm ("bras %0,1f\n" \
+ "0:\t.long _GLOBAL_OFFSET_TABLE_-0b\n\t" \
+ ".long __tls_get_offset@plt-0b\n\t" \
+ ".long " #x "@tlsldm\n\t" \
+ ".long " #x "@dtpoff\n" \
+ "1:\tlr %1,%%r12\n\t" \
+ "l %%r12,0(%0)\n\t" \
+ "la %%r12,0(%%r12,%0)\n\t" \
+ "l %%r1,4(%0)\n\t" \
+ "l %%r2,8(%0)\n\t" \
+ "bas %%r14,0(%%r1,%0):tls_ldcall:" #x "\n\t" \
+ "l %0,12(%0)\n\t" \
+ "alr %0,%%r2\n\t" \
+ "lr %%r12,%1" \
+ : "=&a" (__offset), "=&a" (__save12) \
+ : : "cc", "0", "1", "2", "3", "4", "5" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# else
+# define TLS_LD(x) \
+ ({ unsigned long __offset; \
+ asm ("bras %0,1f\n" \
+ "0:\t.long _GLOBAL_OFFSET_TABLE_\n\t" \
+ ".long __tls_get_offset@plt\n\t" \
+ ".long " #x "@tlsldm\n\t" \
+ ".long " #x "@dtpoff\n" \
+ "1:\tl %%r12,0(%0)\n\t" \
+ "l %%r1,4(%0)\n\t" \
+ "l %%r2,8(%0)\n\t" \
+ "bas %%r14,0(%%r1):tls_ldcall:" #x "\n\t" \
+ "l %0,12(%0)\n\t" \
+ "alr %0,%%r2" \
+ : "=&a" (__offset) : : "cc", "0", "1", "2", "3", "4", "5", "12" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
+# ifdef PIC
+# define TLS_GD(x) \
+ ({ unsigned long __offset, __save12; \
+ asm ("bras %0,1f\n" \
+ "0:\t.long _GLOBAL_OFFSET_TABLE_-0b\n\t" \
+ ".long __tls_get_offset@plt-0b\n\t" \
+ ".long " #x "@tlsgd\n" \
+ "1:\tlr %1,%%r12\n\t" \
+ "l %%r12,0(%0)\n\t" \
+ "la %%r12,0(%%r12,%0)\n\t" \
+ "l %%r1,4(%0)\n\t" \
+ "l %%r2,8(%0)\n\t" \
+ "bas %%r14,0(%%r1,%0):tls_gdcall:" #x "\n\t" \
+ "lr %0,%%r2\n\t" \
+ "lr %%r12,%1" \
+ : "=&a" (__offset), "=&a" (__save12) \
+ : : "cc", "0", "1", "2", "3", "4", "5" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# else
+# define TLS_GD(x) \
+ ({ unsigned long __offset; \
+ asm ("bras %0,1f\n" \
+ "0:\t.long _GLOBAL_OFFSET_TABLE_\n\t" \
+ ".long __tls_get_offset@plt\n\t" \
+ ".long " #x "@tlsgd\n" \
+ "1:\tl %%r12,0(%0)\n\t" \
+ "l %%r1,4(%0)\n\t" \
+ "l %%r2,8(%0)\n\t" \
+ "bas %%r14,0(%%r1):tls_gdcall:" #x "\n\t" \
+ "lr %0,%%r2" \
+ : "=&a" (__offset) : : "cc", "0", "1", "2", "3", "4", "5", "12" ); \
+ (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
+#elif defined __powerpc__ && !defined __powerpc64__
+
+#include "config.h"
+
+# define __TLS_CALL_CLOBBERS \
+ "0", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", \
+ "lr", "ctr", "cr0", "cr1", "cr5", "cr6", "cr7"
+
+/* PowerPC32 Local Exec TLS access. */
+# define TLS_LE(x) \
+ ({ int *__result; \
+ asm ("addi %0,2," #x "@tprel" \
+ : "=r" (__result)); \
+ __result; })
+
+/* PowerPC32 Initial Exec TLS access. */
+# ifdef HAVE_ASM_PPC_REL16
+# define TLS_IE(x) \
+ ({ int *__result; \
+ asm ("bcl 20,31,1f\n1:\t" \
+ "mflr %0\n\t" \
+ "addis %0,%0,_GLOBAL_OFFSET_TABLE_-1b@ha\n\t" \
+ "addi %0,%0,_GLOBAL_OFFSET_TABLE_-1b@l\n\t" \
+ "lwz %0," #x "@got@tprel(%0)\n\t" \
+ "add %0,%0," #x "@tls" \
+ : "=b" (__result) : \
+ : "lr"); \
+ __result; })
+# else
+# define TLS_IE(x) \
+ ({ int *__result; \
+ asm ("bl _GLOBAL_OFFSET_TABLE_@local-4\n\t" \
+ "mflr %0\n\t" \
+ "lwz %0," #x "@got@tprel(%0)\n\t" \
+ "add %0,%0," #x "@tls" \
+ : "=b" (__result) : \
+ : "lr"); \
+ __result; })
+# endif
+
+/* PowerPC32 Local Dynamic TLS access. */
+# ifdef HAVE_ASM_PPC_REL16
+# define TLS_LD(x) \
+ ({ int *__result; \
+ asm ("bcl 20,31,1f\n1:\t" \
+ "mflr 3\n\t" \
+ "addis 3,3,_GLOBAL_OFFSET_TABLE_-1b@ha\n\t" \
+ "addi 3,3,_GLOBAL_OFFSET_TABLE_-1b@l\n\t" \
+ "addi 3,3," #x "@got@tlsld\n\t" \
+ "bl __tls_get_addr@plt\n\t" \
+ "addi %0,3," #x "@dtprel" \
+ : "=r" (__result) : \
+ : __TLS_CALL_CLOBBERS); \
+ __result; })
+# else
+# define TLS_LD(x) \
+ ({ int *__result; \
+ asm ("bl _GLOBAL_OFFSET_TABLE_@local-4\n\t" \
+ "mflr 3\n\t" \
+ "addi 3,3," #x "@got@tlsld\n\t" \
+ "bl __tls_get_addr@plt\n\t" \
+ "addi %0,3," #x "@dtprel" \
+ : "=r" (__result) : \
+ : __TLS_CALL_CLOBBERS); \
+ __result; })
+# endif
+
+/* PowerPC32 General Dynamic TLS access. */
+# ifdef HAVE_ASM_PPC_REL16
+# define TLS_GD(x) \
+ ({ register int *__result __asm__ ("r3"); \
+ asm ("bcl 20,31,1f\n1:\t" \
+ "mflr 3\n\t" \
+ "addis 3,3,_GLOBAL_OFFSET_TABLE_-1b@ha\n\t" \
+ "addi 3,3,_GLOBAL_OFFSET_TABLE_-1b@l\n\t" \
+ "addi 3,3," #x "@got@tlsgd\n\t" \
+ "bl __tls_get_addr@plt" \
+ : : \
+ : __TLS_CALL_CLOBBERS); \
+ __result; })
+# else
+# define TLS_GD(x) \
+ ({ register int *__result __asm__ ("r3"); \
+ asm ("bl _GLOBAL_OFFSET_TABLE_@local-4\n\t" \
+ "mflr 3\n\t" \
+ "addi 3,3," #x "@got@tlsgd\n\t" \
+ "bl __tls_get_addr@plt" \
+ : : \
+ : __TLS_CALL_CLOBBERS); \
+ __result; })
+# endif
+
+#elif defined __powerpc__ && defined __powerpc64__
+
+/* PowerPC64 Local Exec TLS access. */
+# define TLS_LE(x) \
+ ({ int * __result; \
+ asm ( \
+ " addis %0,13," #x "@tprel@ha\n" \
+ " addi %0,%0," #x "@tprel@l\n" \
+ : "=b" (__result) ); \
+ __result; \
+ })
+/* PowerPC64 Initial Exec TLS access. */
+# define TLS_IE(x) \
+ ({ int * __result; \
+ asm ( \
+ " ld %0," #x "@got@tprel(2)\n" \
+ " add %0,%0," #x "@tls\n" \
+ : "=b" (__result) ); \
+ __result; \
+ })
+/* PowerPC64 Local Dynamic TLS access. */
+# define TLS_LD(x) \
+ ({ int * __result; \
+ asm ( \
+ " addi 3,2," #x "@got@tlsld\n" \
+ " bl .__tls_get_addr\n" \
+ " nop \n" \
+ " addis %0,3," #x "@dtprel@ha\n" \
+ " addi %0,%0," #x "@dtprel@l\n" \
+ : "=b" (__result) : \
+ : "0", "3", "4", "5", "6", "7", \
+ "8", "9", "10", "11", "12", \
+ "lr", "ctr", \
+ "cr0", "cr1", "cr5", "cr6", "cr7"); \
+ __result; \
+ })
+/* PowerPC64 General Dynamic TLS access. */
+# define TLS_GD(x) \
+ ({ int * __result; \
+ asm ( \
+ " addi 3,2," #x "@got@tlsgd\n" \
+ " bl .__tls_get_addr\n" \
+ " nop \n" \
+ " mr %0,3\n" \
+ : "=b" (__result) : \
+ : "0", "3", "4", "5", "6", "7", \
+ "8", "9", "10", "11", "12", \
+ "lr", "ctr", \
+ "cr0", "cr1", "cr5", "cr6", "cr7"); \
+ __result; \
+ })
+
+#elif !defined TLS_LE || !defined TLS_IE \
+ || !defined TLS_LD || !defined TLS_GD
+# error "No support for this architecture so far."
+#endif
diff --git a/libc/elf/tst-align.c b/libc/elf/tst-align.c
new file mode 100644
index 000000000..e4d77763c
--- /dev/null
+++ b/libc/elf/tst-align.c
@@ -0,0 +1,54 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static int
+do_test (void)
+{
+ static const char modname[] = "tst-alignmod.so";
+ int result = 0;
+ void (*fp) (int *);
+ void *h;
+
+ h = dlopen (modname, RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname, dlerror ());
+ exit (1);
+ }
+
+ fp = dlsym (h, "in_dso");
+ if (fp == NULL)
+ {
+ printf ("cannot get symbol 'in_dso': %s\n", dlerror ());
+ exit (1);
+ }
+
+ fp (&result);
+
+ dlclose (h);
+
+ return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-align2.c b/libc/elf/tst-align2.c
new file mode 100644
index 000000000..4fc0330e3
--- /dev/null
+++ b/libc/elf/tst-align2.c
@@ -0,0 +1,157 @@
+/* Copyright (C) 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Jakub Jelinek <jakub@redhat.com>, 2005.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <tst-stack-align.h>
+#include <unistd.h>
+
+static int res, fds[2], result;
+static bool test_destructors;
+
+extern void in_dso (int *, bool *, int *);
+
+static void __attribute__ ((constructor)) con (void)
+{
+ res = TEST_STACK_ALIGN () ? -1 : 1;
+}
+
+static void __attribute__ ((destructor)) des (void)
+{
+ if (!test_destructors)
+ return;
+
+ char c = TEST_STACK_ALIGN () ? 'B' : 'A';
+ write (fds[1], &c, 1);
+}
+
+static int
+do_test (void)
+{
+ if (!res)
+ {
+ puts ("binary's constructor has not been run");
+ result = 1;
+ }
+ else if (res != 1)
+ {
+ puts ("binary's constructor has been run without sufficient alignment");
+ result = 1;
+ }
+
+ if (TEST_STACK_ALIGN ())
+ {
+ puts ("insufficient stack alignment in do_test");
+ result = 1;
+ }
+
+ in_dso (&result, &test_destructors, &fds[1]);
+
+ if (pipe (fds) < 0)
+ {
+ printf ("couldn't create pipe: %m\n");
+ return 1;
+ }
+
+ pid_t pid = fork ();
+ if (pid < 0)
+ {
+ printf ("fork failed: %m\n");
+ return 1;
+ }
+
+ if (!pid)
+ {
+ close (fds[0]);
+ test_destructors = true;
+ exit (0);
+ }
+
+ close (fds[1]);
+
+ unsigned char c;
+ ssize_t len;
+ int des_seen = 0, dso_des_seen = 0;
+ while ((len = TEMP_FAILURE_RETRY (read (fds[0], &c, 1))) > 0)
+ {
+ switch (c)
+ {
+ case 'B':
+ puts ("insufficient alignment in binary's destructor");
+ result = 1;
+ /* FALLTHROUGH */
+ case 'A':
+ des_seen++;
+ break;
+ case 'D':
+ puts ("insufficient alignment in DSO destructor");
+ result = 1;
+ /* FALLTHROUGH */
+ case 'C':
+ dso_des_seen++;
+ break;
+ default:
+ printf ("unexpected character %x read from pipe", c);
+ result = 1;
+ break;
+ }
+ }
+
+ close (fds[0]);
+
+ if (des_seen != 1)
+ {
+ printf ("binary destructor run %d times instead of once\n", des_seen);
+ result = 1;
+ }
+
+ if (dso_des_seen != 1)
+ {
+ printf ("DSO destructor run %d times instead of once\n", dso_des_seen);
+ result = 1;
+ }
+
+ int status;
+ pid_t termpid;
+ termpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
+ if (termpid == -1)
+ {
+ printf ("waitpid failed: %m\n");
+ result = 1;
+ }
+ else if (termpid != pid)
+ {
+ printf ("waitpid returned %ld != %ld\n",
+ (long int) termpid, (long int) pid);
+ result = 1;
+ }
+ else if (!WIFEXITED (status) || WEXITSTATUS (status))
+ {
+ puts ("child hasn't exited with exit status 0");
+ result = 1;
+ }
+
+ return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-alignmod.c b/libc/elf/tst-alignmod.c
new file mode 100644
index 000000000..d1305c1a6
--- /dev/null
+++ b/libc/elf/tst-alignmod.c
@@ -0,0 +1,53 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <stdio.h>
+#include <tst-stack-align.h>
+
+static int res, *resp;
+
+static void __attribute__((constructor))
+con (void)
+{
+ res = TEST_STACK_ALIGN () ? -1 : 1;
+}
+
+void
+in_dso (int *result)
+{
+ if (!res)
+ {
+ puts ("constructor has not been run");
+ *result = 1;
+ }
+ else if (res != 1)
+ {
+ puts ("constructor has been run without sufficient alignment");
+ *result = 1;
+ }
+
+ resp = result;
+}
+
+static void __attribute__((destructor))
+des (void)
+{
+ if (TEST_STACK_ALIGN ())
+ *resp = 1;
+}
diff --git a/libc/elf/tst-alignmod2.c b/libc/elf/tst-alignmod2.c
new file mode 100644
index 000000000..21dcc535c
--- /dev/null
+++ b/libc/elf/tst-alignmod2.c
@@ -0,0 +1,60 @@
+/* Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <tst-stack-align.h>
+#include <unistd.h>
+
+static int res, *fdp;
+static bool *test_destructorsp;
+
+static void __attribute__((constructor))
+con (void)
+{
+ res = TEST_STACK_ALIGN () ? -1 : 1;
+}
+
+void
+in_dso (int *result, bool *test_destructors, int *fd)
+{
+ if (!res)
+ {
+ puts ("constructor has not been run");
+ *result = 1;
+ }
+ else if (res != 1)
+ {
+ puts ("constructor has been run without sufficient alignment");
+ *result = 1;
+ }
+
+ test_destructorsp = test_destructors;
+ fdp = fd;
+}
+
+static void __attribute__((destructor))
+des (void)
+{
+ if (!test_destructorsp || !*test_destructorsp)
+ return;
+
+ char c = TEST_STACK_ALIGN () ? 'D' : 'C';
+ write (*fdp, &c, 1);
+}
diff --git a/libc/elf/tst-array1-static.c b/libc/elf/tst-array1-static.c
new file mode 100644
index 000000000..21539a421
--- /dev/null
+++ b/libc/elf/tst-array1-static.c
@@ -0,0 +1 @@
+#include "tst-array1.c"
diff --git a/libc/elf/tst-array1.c b/libc/elf/tst-array1.c
new file mode 100644
index 000000000..4d78db616
--- /dev/null
+++ b/libc/elf/tst-array1.c
@@ -0,0 +1,101 @@
+#include <unistd.h>
+
+static void init (void) __attribute__ ((constructor));
+
+static void
+init (void)
+{
+ write (STDOUT_FILENO, "init\n", 5);
+}
+
+static void fini (void) __attribute__ ((destructor));
+
+static void
+fini (void)
+{
+ write (STDOUT_FILENO, "fini\n", 5);
+}
+
+static void
+preinit_0 (void)
+{
+ write (STDOUT_FILENO, "preinit array 0\n", 16);
+}
+
+static void
+preinit_1 (void)
+{
+ write (STDOUT_FILENO, "preinit array 1\n", 16);
+}
+
+static void
+preinit_2 (void)
+{
+ write (STDOUT_FILENO, "preinit array 2\n", 16);
+}
+
+void (*const preinit_array []) (void)
+ __attribute__ ((section (".preinit_array"), aligned (sizeof (void *)))) =
+{
+ &preinit_0,
+ &preinit_1,
+ &preinit_2
+};
+
+static void
+init_0 (void)
+{
+ write (STDOUT_FILENO, "init array 0\n", 13);
+}
+
+static void
+init_1 (void)
+{
+ write (STDOUT_FILENO, "init array 1\n", 13);
+}
+
+static void
+init_2 (void)
+{
+ write (STDOUT_FILENO, "init array 2\n", 13);
+}
+
+void (*const init_array []) (void)
+ __attribute__ ((section (".init_array"), aligned (sizeof (void *)))) =
+{
+ &init_0,
+ &init_1,
+ &init_2
+};
+
+static void
+fini_0 (void)
+{
+ write (STDOUT_FILENO, "fini array 0\n", 13);
+}
+
+static void
+fini_1 (void)
+{
+ write (STDOUT_FILENO, "fini array 1\n", 13);
+}
+
+static void
+fini_2 (void)
+{
+ write (STDOUT_FILENO, "fini array 2\n", 13);
+}
+
+void (*const fini_array []) (void)
+ __attribute__ ((section (".fini_array"), aligned (sizeof (void *)))) =
+{
+ &fini_0,
+ &fini_1,
+ &fini_2
+};
+
+int
+main (void)
+{
+ return 0;
+}
diff --git a/libc/elf/tst-array1.exp b/libc/elf/tst-array1.exp
new file mode 100644
index 000000000..cfcec9de0
--- /dev/null
+++ b/libc/elf/tst-array1.exp
@@ -0,0 +1,11 @@
+preinit array 0
+preinit array 1
+preinit array 2
+init
+init array 0
+init array 1
+init array 2
+fini array 2
+fini array 1
+fini array 0
+fini
diff --git a/libc/elf/tst-array2.c b/libc/elf/tst-array2.c
new file mode 100644
index 000000000..21539a421
--- /dev/null
+++ b/libc/elf/tst-array2.c
@@ -0,0 +1 @@
+#include "tst-array1.c"
diff --git a/libc/elf/tst-array2.exp b/libc/elf/tst-array2.exp
new file mode 100644
index 000000000..ed203525b
--- /dev/null
+++ b/libc/elf/tst-array2.exp
@@ -0,0 +1,19 @@
+preinit array 0
+preinit array 1
+preinit array 2
+DSO init
+DSO init array 0
+DSO init array 1
+DSO init array 2
+init
+init array 0
+init array 1
+init array 2
+fini array 2
+fini array 1
+fini array 0
+fini
+DSO fini array 2
+DSO fini array 1
+DSO fini array 0
+DSO fini
diff --git a/libc/elf/tst-array2dep.c b/libc/elf/tst-array2dep.c
new file mode 100644
index 000000000..e1596b5b3
--- /dev/null
+++ b/libc/elf/tst-array2dep.c
@@ -0,0 +1,69 @@
+#include <unistd.h>
+
+static void init (void) __attribute__ ((constructor));
+
+static void
+init (void)
+{
+ write (STDOUT_FILENO, "DSO init\n", 9);
+}
+
+static void fini (void) __attribute__ ((destructor));
+
+static void
+fini (void)
+{
+ write (STDOUT_FILENO, "DSO fini\n", 9);
+}
+
+static void
+init_0 (void)
+{
+ write (STDOUT_FILENO, "DSO init array 0\n", 17);
+}
+
+static void
+init_1 (void)
+{
+ write (STDOUT_FILENO, "DSO init array 1\n", 17);
+}
+
+static void
+init_2 (void)
+{
+ write (STDOUT_FILENO, "DSO init array 2\n", 17);
+}
+
+void (*const init_array []) (void)
+ __attribute__ ((section (".init_array"), aligned (sizeof (void *)))) =
+{
+ &init_0,
+ &init_1,
+ &init_2
+};
+
+static void
+fini_0 (void)
+{
+ write (STDOUT_FILENO, "DSO fini array 0\n", 17);
+}
+
+static void
+fini_1 (void)
+{
+ write (STDOUT_FILENO, "DSO fini array 1\n", 17);
+}
+
+static void
+fini_2 (void)
+{
+ write (STDOUT_FILENO, "DSO fini array 2\n", 17);
+}
+
+void (*const fini_array []) (void)
+ __attribute__ ((section (".fini_array"), aligned (sizeof (void *)))) =
+{
+ &fini_0,
+ &fini_1,
+ &fini_2
+};
diff --git a/libc/elf/tst-array3.c b/libc/elf/tst-array3.c
new file mode 100644
index 000000000..21539a421
--- /dev/null
+++ b/libc/elf/tst-array3.c
@@ -0,0 +1 @@
+#include "tst-array1.c"
diff --git a/libc/elf/tst-array4.c b/libc/elf/tst-array4.c
new file mode 100644
index 000000000..ac3d4eb71
--- /dev/null
+++ b/libc/elf/tst-array4.c
@@ -0,0 +1,18 @@
+#include <dlfcn.h>
+
+#define main array1_main
+#include "tst-array1.c"
+#undef main
+
+int
+main (void)
+{
+ void *handle = dlopen ("tst-array2dep.so", RTLD_LAZY);
+
+ array1_main ();
+
+ if (handle != NULL)
+ dlclose (handle);
+
+ return 0;
+}
diff --git a/libc/elf/tst-array4.exp b/libc/elf/tst-array4.exp
new file mode 100644
index 000000000..560444d2e
--- /dev/null
+++ b/libc/elf/tst-array4.exp
@@ -0,0 +1,19 @@
+preinit array 0
+preinit array 1
+preinit array 2
+init
+init array 0
+init array 1
+init array 2
+DSO init
+DSO init array 0
+DSO init array 1
+DSO init array 2
+DSO fini array 2
+DSO fini array 1
+DSO fini array 0
+DSO fini
+fini array 2
+fini array 1
+fini array 0
+fini
diff --git a/libc/elf/tst-array5-static.c b/libc/elf/tst-array5-static.c
new file mode 100644
index 000000000..4ef2aba3f
--- /dev/null
+++ b/libc/elf/tst-array5-static.c
@@ -0,0 +1 @@
+#include "tst-array5.c"
diff --git a/libc/elf/tst-array5-static.exp b/libc/elf/tst-array5-static.exp
new file mode 100644
index 000000000..b1dc9e467
--- /dev/null
+++ b/libc/elf/tst-array5-static.exp
@@ -0,0 +1,2 @@
+preinit array in executable: tst-array5-static
+init array in executable: tst-array5-static
diff --git a/libc/elf/tst-array5.c b/libc/elf/tst-array5.c
new file mode 100644
index 000000000..03a566832
--- /dev/null
+++ b/libc/elf/tst-array5.c
@@ -0,0 +1,50 @@
+#include <string.h>
+#include <unistd.h>
+
+static void
+preinit_0 (int argc __attribute__ ((unused)), char **argv)
+{
+ char *p = strrchr (argv [0], '/');
+
+ if (p == NULL)
+ return;
+
+ p++;
+ size_t len = strlen (p);
+ write (STDOUT_FILENO, "preinit array in executable: ", 29);
+ write (STDOUT_FILENO, p, len);
+ write (STDOUT_FILENO, "\n", 1);
+}
+
+void (*const preinit_array []) (int, char **)
+ __attribute__ ((section (".preinit_array"), aligned (sizeof (void *)))) =
+{
+ &preinit_0,
+};
+
+static void
+init_0 (int argc __attribute__ ((unused)), char **argv)
+{
+ char *p = strrchr (argv [0], '/');
+
+ if (p == NULL)
+ return;
+
+ p++;
+ size_t len = strlen (p);
+ write (STDOUT_FILENO, "init array in executable: ", 26);
+ write (STDOUT_FILENO, p, len);
+ write (STDOUT_FILENO, "\n", 1);
+}
+
+void (*const init_array []) (int, char **)
+ __attribute__ ((section (".init_array"), aligned (sizeof (void *)))) =
+{
+ &init_0,
+};
+
+int
+main (void)
+{
+ return 0;
+}
diff --git a/libc/elf/tst-array5.exp b/libc/elf/tst-array5.exp
new file mode 100644
index 000000000..28b490983
--- /dev/null
+++ b/libc/elf/tst-array5.exp
@@ -0,0 +1,3 @@
+preinit array in executable: tst-array5
+init array in DSO: tst-array5
+init array in executable: tst-array5
diff --git a/libc/elf/tst-array5dep.c b/libc/elf/tst-array5dep.c
new file mode 100644
index 000000000..570d282af
--- /dev/null
+++ b/libc/elf/tst-array5dep.c
@@ -0,0 +1,23 @@
+#include <string.h>
+#include <unistd.h>
+
+static void
+init_0 (int argc __attribute__ ((unused)), char **argv)
+{
+ char *p = strrchr (argv [0], '/');
+
+ if (p == NULL)
+ return;
+
+ p++;
+ size_t len = strlen (p);
+ write (STDOUT_FILENO, "init array in DSO: ", 19);
+ write (STDOUT_FILENO, p, len);
+ write (STDOUT_FILENO, "\n", 1);
+}
+
+void (*const init_array []) (int, char **)
+ __attribute__ ((section (".init_array"), aligned (sizeof (void *)))) =
+{
+ &init_0,
+};
diff --git a/libc/elf/tst-audit1.c b/libc/elf/tst-audit1.c
new file mode 100644
index 000000000..63656b4ee
--- /dev/null
+++ b/libc/elf/tst-audit1.c
@@ -0,0 +1 @@
+#include "../io/pwd.c"
diff --git a/libc/elf/tst-audit2.c b/libc/elf/tst-audit2.c
new file mode 100644
index 000000000..fd089b6f6
--- /dev/null
+++ b/libc/elf/tst-audit2.c
@@ -0,0 +1,50 @@
+/* Test case for early TLS initialization in dynamic linker. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE___THREAD
+# define MAGIC1 0xabcdef72
+# define MAGIC2 0xd8675309
+static __thread unsigned int magic[] = { MAGIC1, MAGIC2 };
+#endif
+
+#undef calloc
+
+/* This calloc definition will be called by the dynamic linker itself.
+ We test that it has initialized our TLS block by the time it does so. */
+
+void *
+calloc (size_t n, size_t m)
+{
+#if HAVE___THREAD
+ if (magic[0] != MAGIC1 || magic[1] != MAGIC2)
+ {
+ printf ("{%x, %x} != {%x, %x}\n", magic[0], magic[1], MAGIC1, MAGIC2);
+ abort ();
+ }
+ magic[0] = MAGIC2;
+ magic[1] = MAGIC1;
+#endif
+
+ n *= m;
+ void *ptr = malloc (n);
+ if (ptr != NULL)
+ memset (ptr, '\0', n);
+ return ptr;
+}
+
+int
+main (void)
+{
+#if HAVE___THREAD
+ if (magic[1] != MAGIC1 || magic[0] != MAGIC2)
+ {
+ printf ("{%x, %x} != {%x, %x}\n", magic[0], magic[1], MAGIC2, MAGIC1);
+ return 1;
+ }
+#endif
+
+ return 0;
+}
diff --git a/libc/elf/tst-auditmod1.c b/libc/elf/tst-auditmod1.c
new file mode 100644
index 000000000..2d39df21e
--- /dev/null
+++ b/libc/elf/tst-auditmod1.c
@@ -0,0 +1,200 @@
+#include <dlfcn.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <bits/wordsize.h>
+#include <gnu/lib-names.h>
+
+
+unsigned int
+la_version (unsigned int v)
+{
+ setlinebuf (stdout);
+
+ printf ("version: %u\n", v);
+
+ char buf[20];
+ sprintf (buf, "%u", v);
+
+ return v;
+}
+
+void
+la_activity (uintptr_t *cookie, unsigned int flag)
+{
+ if (flag == LA_ACT_CONSISTENT)
+ printf ("activity: consistent\n");
+ else if (flag == LA_ACT_ADD)
+ printf ("activity: add\n");
+ else if (flag == LA_ACT_DELETE)
+ printf ("activity: delete\n");
+ else
+ printf ("activity: unknown activity %u\n", flag);
+}
+
+char *
+la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag)
+{
+ char buf[100];
+ const char *flagstr;
+ if (flag == LA_SER_ORIG)
+ flagstr = "LA_SET_ORIG";
+ else if (flag == LA_SER_LIBPATH)
+ flagstr = "LA_SER_LIBPATH";
+ else if (flag == LA_SER_RUNPATH)
+ flagstr = "LA_SER_RUNPATH";
+ else if (flag == LA_SER_CONFIG)
+ flagstr = "LA_SER_CONFIG";
+ else if (flag == LA_SER_DEFAULT)
+ flagstr = "LA_SER_DEFAULT";
+ else if (flag == LA_SER_SECURE)
+ flagstr = "LA_SER_SECURE";
+ else
+ {
+ sprintf (buf, "unknown flag %d", flag);
+ flagstr = buf;
+ }
+ printf ("objsearch: %s, %s\n", name, flagstr);
+
+ return (char *) name;
+}
+
+unsigned int
+la_objopen (struct link_map *l, Lmid_t lmid, uintptr_t *cookie)
+{
+ printf ("objopen: %ld, %s\n", lmid, l->l_name);
+
+ return 3;
+}
+
+void
+la_preinit (uintptr_t *cookie)
+{
+ printf ("preinit\n");
+}
+
+unsigned int
+la_objclose (uintptr_t *cookie)
+{
+ printf ("objclose\n");
+ return 0;
+}
+
+uintptr_t
+la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, unsigned int *flags, const char *symname)
+{
+ printf ("symbind32: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n",
+ symname, (long int) sym->st_value, ndx, *flags);
+
+ return sym->st_value;
+}
+
+uintptr_t
+la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, unsigned int *flags, const char *symname)
+{
+ printf ("symbind64: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n",
+ symname, (long int) sym->st_value, ndx, *flags);
+
+ return sym->st_value;
+}
+
+#ifdef __i386__
+# define pltenter la_i86_gnu_pltenter
+# define pltexit la_i86_gnu_pltexit
+# define La_regs La_i86_regs
+# define La_retval La_i86_retval
+# define int_retval lrv_eax
+#elif defined __x86_64__
+# define pltenter la_x86_64_gnu_pltenter
+# define pltexit la_x86_64_gnu_pltexit
+# define La_regs La_x86_64_regs
+# define La_retval La_x86_64_retval
+# define int_retval lrv_rax
+#elif defined __powerpc__ && __WORDSIZE == 32
+# define pltenter la_ppc32_gnu_pltenter
+# define pltexit la_ppc32_gnu_pltexit
+# define La_regs La_ppc32_regs
+# define La_retval La_ppc32_retval
+# define int_retval lrv_r3
+#elif defined __powerpc__ && __WORDSIZE == 64
+# define pltenter la_ppc64_gnu_pltenter
+# define pltexit la_ppc64_gnu_pltexit
+# define La_regs La_ppc64_regs
+# define La_retval La_ppc64_retval
+# define int_retval lrv_r3
+#elif defined __sh__
+# define pltenter la_sh_gnu_pltenter
+# define pltexit la_sh_gnu_pltexit
+# define La_regs La_sh_regs
+# define La_retval La_sh_retval
+# define int_retval lrv_r0
+#elif defined __alpha__
+# define pltenter la_alpha_gnu_pltenter
+# define pltexit la_alpha_gnu_pltexit
+# define La_regs La_alpha_regs
+# define La_retval La_alpha_retval
+# define int_retval lrv_r0
+#elif defined __s390__ && __WORDSIZE == 32
+# define pltenter la_s390_32_gnu_pltenter
+# define pltexit la_s390_32_gnu_pltexit
+# define La_regs La_s390_32_regs
+# define La_retval La_s390_32_retval
+# define int_retval lrv_r2
+#elif defined __s390__ && __WORDSIZE == 64
+# define pltenter la_s390_64_gnu_pltenter
+# define pltexit la_s390_64_gnu_pltexit
+# define La_regs La_s390_64_regs
+# define La_retval La_s390_64_retval
+# define int_retval lrv_r2
+#elif defined __ia64__
+# define pltenter la_ia64_gnu_pltenter
+# define pltexit la_ia64_gnu_pltexit
+# define La_regs La_ia64_regs
+# define La_retval La_ia64_retval
+# define int_retval lrv_r8
+#elif defined __sparc__ && __WORDSIZE == 32
+# define pltenter la_sparc32_gnu_pltenter
+# define pltexit la_sparc32_gnu_pltexit
+# define La_regs La_sparc32_regs
+# define La_retval La_sparc32_retval
+# define int_retval lrv_reg[0]
+#elif defined __sparc__ && __WORDSIZE == 64
+# define pltenter la_sparc64_gnu_pltenter
+# define pltexit la_sparc64_gnu_pltexit
+# define La_regs La_sparc64_regs
+# define La_retval La_sparc64_retval
+# define int_retval lrv_reg[0]
+#endif
+
+#include <tst-audit.h>
+#if (!defined (pltenter) || !defined (pltexit) || !defined (La_regs) \
+ || !defined (La_retval) || !defined (int_retval))
+# error "architecture specific code needed in sysdeps/CPU/tst-audit.h or here"
+#endif
+
+
+ElfW(Addr)
+pltenter (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, La_regs *regs, unsigned int *flags,
+ const char *symname, long int *framesizep)
+{
+ printf ("pltenter: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n",
+ symname, (long int) sym->st_value, ndx, *flags);
+
+ return sym->st_value;
+}
+
+unsigned int
+pltexit (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, const La_regs *inregs, La_retval *outregs,
+ const char *symname)
+{
+ printf ("pltexit: symname=%s, st_value=%#lx, ndx=%u, retval=%tu\n",
+ symname, (long int) sym->st_value, ndx, outregs->int_retval);
+
+ return 0;
+}
diff --git a/libc/elf/tst-deep1.c b/libc/elf/tst-deep1.c
new file mode 100644
index 000000000..5428d13de
--- /dev/null
+++ b/libc/elf/tst-deep1.c
@@ -0,0 +1,36 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+int
+xyzzy (void)
+{
+ printf ("%s:%s\n", __FILE__, __func__);
+ return 21;
+}
+
+int
+back (void)
+{
+ printf ("%s:%s\n", __FILE__, __func__);
+ return 1;
+}
+
+extern int foo (void);
+
+static int
+do_test (void)
+{
+ void *p = dlopen ("$ORIGIN/tst-deep1mod2.so", RTLD_LAZY|RTLD_DEEPBIND);
+
+ int (*f) (void) = dlsym (p, "bar");
+ if (f == NULL)
+ {
+ puts (dlerror ());
+ return 1;
+ }
+
+ return foo () + f ();
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-deep1mod1.c b/libc/elf/tst-deep1mod1.c
new file mode 100644
index 000000000..cc922e6ea
--- /dev/null
+++ b/libc/elf/tst-deep1mod1.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+int
+foo (void)
+{
+ printf ("%s:%s\n", __FILE__, __func__);
+ return 1;
+}
+
+int
+baz (void)
+{
+ printf ("%s:%s\n", __FILE__, __func__);
+ return 20;
+}
diff --git a/libc/elf/tst-deep1mod2.c b/libc/elf/tst-deep1mod2.c
new file mode 100644
index 000000000..b99caf032
--- /dev/null
+++ b/libc/elf/tst-deep1mod2.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+extern int baz (void);
+extern int xyzzy (void);
+int
+bar (void)
+{
+ printf ("%s:%s\n", __FILE__, __func__);
+ return baz () + xyzzy ();;
+}
+
+int
+back (void)
+{
+ printf ("%s:%s\n", __FILE__, __func__);
+ return -1;
+}
diff --git a/libc/elf/tst-deep1mod3.c b/libc/elf/tst-deep1mod3.c
new file mode 100644
index 000000000..eee7d5c97
--- /dev/null
+++ b/libc/elf/tst-deep1mod3.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+
+extern int back (void);
+
+int
+baz (void)
+{
+ printf ("%s:%s\n", __FILE__, __func__);
+ return back ();
+}
+
+int
+xyzzy (void)
+{
+ printf ("%s:%s\n", __FILE__, __func__);
+ return 0;
+}
diff --git a/libc/elf/tst-dlmodcount.c b/libc/elf/tst-dlmodcount.c
new file mode 100644
index 000000000..781aad51d
--- /dev/null
+++ b/libc/elf/tst-dlmodcount.c
@@ -0,0 +1,107 @@
+/* Copyright (C) 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by David Mosberger <davidm@hpl.hp.com>, 2004.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <link.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define SET 0
+#define ADD 1
+#define REMOVE 2
+
+#define leq(l,r) (((r) - (l)) <= ~0ULL / 2)
+
+static int
+callback (struct dl_phdr_info *info, size_t size, void *ptr)
+{
+ static int last_adds = 0, last_subs = 0;
+ intptr_t cmd = (intptr_t) ptr;
+
+ printf (" size = %Zu\n", size);
+ if (size < (offsetof (struct dl_phdr_info, dlpi_subs)
+ + sizeof (info->dlpi_subs)))
+ {
+ fprintf (stderr, "dl_iterate_phdr failed to pass dlpi_adds/dlpi_subs\n");
+ exit (5);
+ }
+
+ printf (" dlpi_adds = %Lu dlpi_subs = %Lu\n",
+ info->dlpi_adds, info->dlpi_subs);
+
+ switch (cmd)
+ {
+ case SET:
+ break;
+
+ case ADD:
+ if (leq (info->dlpi_adds, last_adds))
+ {
+ fprintf (stderr, "dlpi_adds failed to get incremented!\n");
+ exit (3);
+ }
+ break;
+
+ case REMOVE:
+ if (leq (info->dlpi_subs, last_subs))
+ {
+ fprintf (stderr, "dlpi_subs failed to get incremented!\n");
+ exit (4);
+ }
+ break;
+ }
+ last_adds = info->dlpi_adds;
+ last_subs = info->dlpi_subs;
+ return -1;
+}
+
+static void *
+load (const char *path)
+{
+ void *handle;
+
+ printf ("loading `%s'\n", path);
+ handle = dlopen (path, RTLD_LAZY);
+ if (!handle)
+ exit (1);
+ dl_iterate_phdr (callback, (void *)(intptr_t) ADD);
+ return handle;
+}
+
+static void
+unload (const char *path, void *handle)
+{
+ printf ("unloading `%s'\n", path);
+ if (dlclose (handle) < 0)
+ exit (2);
+ dl_iterate_phdr (callback, (void *)(intptr_t) REMOVE);
+}
+
+int
+main (int argc, char **argv)
+{
+ void *handle1, *handle2;
+
+ dl_iterate_phdr (callback, (void *)(intptr_t) SET);
+ handle1 = load ("firstobj.so");
+ handle2 = load ("globalmod1.so");
+ unload ("firstobj.so", handle1);
+ unload ("globalmod1.so", handle2);
+ return 0;
+}
diff --git a/libc/elf/tst-dlmopen1.c b/libc/elf/tst-dlmopen1.c
new file mode 100644
index 000000000..9839267d8
--- /dev/null
+++ b/libc/elf/tst-dlmopen1.c
@@ -0,0 +1,80 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <gnu/lib-names.h>
+
+
+static int
+do_test (void)
+{
+ void *h = dlopen (LIBC_SO, RTLD_LAZY|RTLD_NOLOAD);
+ if (h == NULL)
+ {
+ printf ("cannot get handle for %s: %s\n", LIBC_SO, dlerror ());
+ return 1;
+ }
+
+ Lmid_t ns = -10;
+ if (dlinfo (h, RTLD_DI_LMID, &ns) != 0)
+ {
+ printf ("dlinfo for %s in %s failed: %s\n",
+ LIBC_SO, __func__, dlerror ());
+ return 1;
+ }
+
+ if (ns != LM_ID_BASE)
+ {
+ printf ("namespace for %s not LM_ID_BASE\n", LIBC_SO);
+ return 1;
+ }
+
+ if (dlclose (h) != 0)
+ {
+ printf ("dlclose for %s in %s failed: %s\n",
+ LIBC_SO, __func__, dlerror ());
+ return 1;
+ }
+
+ h = dlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot get handle for %s: %s\n",
+ "tst-dlmopen1mod.so", dlerror ());
+ return 1;
+ }
+
+ ns = -10;
+ if (dlinfo (h, RTLD_DI_LMID, &ns) != 0)
+ {
+ printf ("dlinfo for %s in %s failed: %s\n",
+ "tst-dlmopen1mod.so", __func__, dlerror ());
+ return 1;
+ }
+
+ if (ns == LM_ID_BASE)
+ {
+ printf ("namespace for %s is LM_ID_BASE\n", LIBC_SO);
+ return 1;
+ }
+
+ int (*fct) (Lmid_t) = dlsym (h, "foo");
+ if (fct == NULL)
+ {
+ printf ("could not find %s: %s\n", "foo", dlerror ());
+ return 1;
+ }
+
+ if (fct (ns) != 0)
+ return 1;
+
+ if (dlclose (h) != 0)
+ {
+ printf ("dlclose for %s in %s failed: %s\n",
+ LIBC_SO, __func__, dlerror ());
+ return 1;
+ }
+
+ return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-dlmopen1mod.c b/libc/elf/tst-dlmopen1mod.c
new file mode 100644
index 000000000..142488098
--- /dev/null
+++ b/libc/elf/tst-dlmopen1mod.c
@@ -0,0 +1,59 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <gnu/lib-names.h>
+
+
+static int cnt;
+
+static void
+__attribute ((constructor))
+constr (void)
+{
+ ++cnt;
+}
+
+
+int
+foo (Lmid_t ns2)
+{
+ void *h = dlopen (LIBC_SO, RTLD_LAZY|RTLD_NOLOAD);
+ if (h == NULL)
+ {
+ printf ("cannot get handle for %s: %s\n", LIBC_SO, dlerror ());
+ return 1;
+ }
+
+ Lmid_t ns = -10;
+ if (dlinfo (h, RTLD_DI_LMID, &ns) != 0)
+ {
+ printf ("dlinfo for %s in %s failed: %s\n",
+ LIBC_SO, __func__, dlerror ());
+ return 1;
+ }
+
+ if (ns != ns2)
+ {
+ printf ("namespace for %s not LM_ID_BASE\n", LIBC_SO);
+ return 1;
+ }
+
+ if (dlclose (h) != 0)
+ {
+ printf ("dlclose for %s in %s failed: %s\n",
+ LIBC_SO, __func__, dlerror ());
+ return 1;
+ }
+
+ if (cnt == 0)
+ {
+ puts ("constructor did not run");
+ return 1;
+ }
+ else if (cnt != 1)
+ {
+ puts ("constructor did not run exactly once");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/libc/elf/tst-dlmopen2.c b/libc/elf/tst-dlmopen2.c
new file mode 100644
index 000000000..056999725
--- /dev/null
+++ b/libc/elf/tst-dlmopen2.c
@@ -0,0 +1,70 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+#include <gnu/lib-names.h>
+#include <ldsodefs.h>
+
+
+static int
+do_test (void)
+{
+ int result = 0;
+
+ for (int i = 1; i <= 10; ++i)
+ {
+ void *h[DL_NNS - 1];
+ char used[DL_NNS];
+
+ printf ("round %d\n", i);
+
+ memset (used, '\0', sizeof (used));
+ used[LM_ID_BASE] = 1;
+
+ for (int j = 0; j < DL_NNS - 1; ++j)
+ {
+ h[j] = dlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so",
+ RTLD_LAZY);
+ if (h[j] == NULL)
+ {
+ printf ("round %d, namespace %d: load failed: %s\n",
+ i, j, dlerror ());
+ return 1;
+ }
+ Lmid_t ns;
+ if (dlinfo (h[j], RTLD_DI_LMID, &ns) != 0)
+ {
+ printf ("round %d, namespace %d: dlinfo failed: %s\n",
+ i, j, dlerror ());
+ return 1;
+ }
+ if (ns < 0 || ns >= DL_NNS)
+ {
+ printf ("round %d, namespace %d: invalid namespace %ld",
+ i, j, (long int) ns);
+ result = 1;
+ }
+ else if (used[ns] != 0)
+ {
+ printf ("\
+round %d, namespace %d: duplicate allocate of namespace %ld",
+ i, j, (long int) ns);
+ result = 1;
+ }
+ else
+ used[ns] = 1;
+ }
+
+ for (int j = 0; j < DL_NNS - 1; ++j)
+ if (dlclose (h[j]) != 0)
+ {
+ printf ("round %d, namespace %d: close failed: %s\n",
+ i, j, dlerror ());
+ return 1;
+ }
+ }
+
+ return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-dlmopen3.c b/libc/elf/tst-dlmopen3.c
new file mode 100644
index 000000000..26c86b2dc
--- /dev/null
+++ b/libc/elf/tst-dlmopen3.c
@@ -0,0 +1,22 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+
+static int
+do_test (void)
+{
+ void *h = dlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot get handle for %s: %s\n",
+ "tst-dlmopen1mod.so", dlerror ());
+ return 1;
+ }
+
+ /* Do not unload. */
+
+ return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-dlopenrpath.c b/libc/elf/tst-dlopenrpath.c
new file mode 100644
index 000000000..bd4d888f3
--- /dev/null
+++ b/libc/elf/tst-dlopenrpath.c
@@ -0,0 +1,72 @@
+/* Copyright (C) 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+
+extern int foo (void);
+
+static const char testsubdir[] = PFX "test-subdir";
+
+
+static int
+do_test (void)
+{
+ struct stat64 st;
+ int result = 1;
+
+ if (mkdir (testsubdir, 0777) != 0
+ && (errno != EEXIST
+ || stat64 (testsubdir, &st) != 0
+ || !S_ISDIR (st.st_mode)))
+ {
+ printf ("cannot create directory %s\n", testsubdir);
+ return 1;
+ }
+
+ if (system ("cp " PFX "firstobj.so " PFX "test-subdir/in-subdir.so") != 0)
+ {
+ puts ("cannot copy DSO");
+ return 1;
+ }
+
+ void *p = dlopen ("in-subdir.so", RTLD_LAZY|RTLD_LOCAL);
+ if (p != NULL)
+ {
+ puts ("succeeded in opening in-subdir.so from do_test");
+ dlclose (p);
+ goto out;
+ }
+
+ result = foo ();
+
+ out:
+ unlink (PFX "test-subdir/in-subdir.so");
+ rmdir (testsubdir);
+
+ return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-dlopenrpathmod.c b/libc/elf/tst-dlopenrpathmod.c
new file mode 100644
index 000000000..8fe7873c9
--- /dev/null
+++ b/libc/elf/tst-dlopenrpathmod.c
@@ -0,0 +1,36 @@
+/* Copyright (C) 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <dlfcn.h>
+#include <stdio.h>
+
+
+int
+foo (void)
+{
+ void *p = dlopen ("in-subdir.so", RTLD_LAZY|RTLD_LOCAL);
+ if (p != NULL)
+ {
+ dlclose (p);
+ return 0;
+ }
+
+ puts ("couldn't open in-subdir.so from foo");
+ return 1;
+}
diff --git a/libc/elf/tst-execstack-mod.c b/libc/elf/tst-execstack-mod.c
new file mode 100644
index 000000000..038e6550b
--- /dev/null
+++ b/libc/elf/tst-execstack-mod.c
@@ -0,0 +1,30 @@
+/* Test module for making nonexecutable stacks executable
+ on load of a DSO that requires executable stacks. */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void callme (void (*callback) (void));
+
+/* This is a function that makes use of executable stack by
+ using a local function trampoline. */
+void
+tryme (void)
+{
+ bool ok = false;
+ void callback (void) { ok = true; }
+
+ callme (&callback);
+
+ if (ok)
+ printf ("DSO called ok (local %p, trampoline %p)\n", &ok, &callback);
+ else
+ abort ();
+}
+
+void
+callme (void (*callback) (void))
+{
+ (*callback) ();
+}
diff --git a/libc/elf/tst-execstack-needed.c b/libc/elf/tst-execstack-needed.c
new file mode 100644
index 000000000..03090f7dd
--- /dev/null
+++ b/libc/elf/tst-execstack-needed.c
@@ -0,0 +1,36 @@
+/* Test program for making nonexecutable stacks executable
+ on DT_NEEDED load of a DSO that requires executable stacks. */
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <error.h>
+
+extern void tryme (void); /* from tst-execstack-mod.so */
+
+static void deeper (void (*f) (void));
+
+static int
+do_test (void)
+{
+ tryme ();
+
+ /* Test that growing the stack region gets new executable pages too. */
+ deeper (&tryme);
+
+ return 0;
+}
+
+static void
+deeper (void (*f) (void))
+{
+ char stack[1100 * 1024];
+ memfrob (stack, sizeof stack);
+ (*f) ();
+ memfrob (stack, sizeof stack);
+}
+
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-execstack-prog.c b/libc/elf/tst-execstack-prog.c
new file mode 100644
index 000000000..5a66d63ca
--- /dev/null
+++ b/libc/elf/tst-execstack-prog.c
@@ -0,0 +1,35 @@
+/* Test program for executable stacks in an executable itself. */
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <error.h>
+
+#include "tst-execstack-mod.c" /* This defines the `tryme' test function. */
+
+static void deeper (void (*f) (void));
+
+static int
+do_test (void)
+{
+ tryme ();
+
+ /* Test that growing the stack region gets new executable pages too. */
+ deeper (&tryme);
+
+ return 0;
+}
+
+static void
+deeper (void (*f) (void))
+{
+ char stack[1100 * 1024];
+ memfrob (stack, sizeof stack);
+ (*f) ();
+ memfrob (stack, sizeof stack);
+}
+
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-execstack.c b/libc/elf/tst-execstack.c
new file mode 100644
index 000000000..4b0661545
--- /dev/null
+++ b/libc/elf/tst-execstack.c
@@ -0,0 +1,133 @@
+/* Test program for making nonexecutable stacks executable
+ on load of a DSO that requires executable stacks. */
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <error.h>
+
+static void
+print_maps (void)
+{
+#if 0
+ char *cmd = NULL;
+ asprintf (&cmd, "cat /proc/%d/maps", getpid ());
+ system (cmd);
+ free (cmd);
+#endif
+}
+
+static void deeper (void (*f) (void));
+
+#if USE_PTHREADS
+# include <pthread.h>
+
+static void *
+tryme_thread (void *f)
+{
+ (*((void (*) (void)) f)) ();
+
+ return 0;
+}
+
+static pthread_barrier_t startup_barrier, go_barrier;
+static void *
+waiter_thread (void *arg)
+{
+ void **f = arg;
+ pthread_barrier_wait (&startup_barrier);
+ pthread_barrier_wait (&go_barrier);
+
+ (*((void (*) (void)) *f)) ();
+
+ return 0;
+}
+#endif
+
+static int
+do_test (void)
+{
+ static void *f; /* Address of this is used in other threads. */
+
+#if USE_PTHREADS
+ /* Create some threads while stacks are nonexecutable. */
+ #define N 5
+ pthread_t thr[N];
+
+ pthread_barrier_init (&startup_barrier, NULL, N + 1);
+ pthread_barrier_init (&go_barrier, NULL, N + 1);
+
+ for (int i = 0; i < N; ++i)
+ {
+ int rc = pthread_create (&thr[i], NULL, &waiter_thread, &f);
+ if (rc)
+ error (1, rc, "pthread_create");
+ }
+
+ /* Make sure they are all there using their stacks. */
+ pthread_barrier_wait (&startup_barrier);
+ puts ("threads waiting");
+#endif
+
+ print_maps ();
+
+ /* Loading this module should force stacks to become executable. */
+ void *h = dlopen ("tst-execstack-mod.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot load: %s\n", dlerror ());
+ return 1;
+ }
+
+ f = dlsym (h, "tryme");
+ if (f == NULL)
+ {
+ printf ("symbol not found: %s\n", dlerror ());
+ return 1;
+ }
+
+ /* Test if that really made our stack executable.
+ The `tryme' function should crash if not. */
+
+ (*((void (*) (void)) f)) ();
+
+ print_maps ();
+
+ /* Test that growing the stack region gets new executable pages too. */
+ deeper ((void (*) (void)) f);
+
+ print_maps ();
+
+#if USE_PTHREADS
+ /* Test that a fresh thread now gets an executable stack. */
+ {
+ pthread_t th;
+ int rc = pthread_create (&th, NULL, &tryme_thread, f);
+ if (rc)
+ error (1, rc, "pthread_create");
+ }
+
+ puts ("threads go");
+ /* The existing threads' stacks should have been changed.
+ Let them run to test it. */
+ pthread_barrier_wait (&go_barrier);
+
+ pthread_exit (0);
+#endif
+
+ return 0;
+}
+
+static void
+deeper (void (*f) (void))
+{
+ char stack[1100 * 1024];
+ memfrob (stack, sizeof stack);
+ (*f) ();
+ memfrob (stack, sizeof stack);
+}
+
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-global1.c b/libc/elf/tst-global1.c
new file mode 100644
index 000000000..1611b51b6
--- /dev/null
+++ b/libc/elf/tst-global1.c
@@ -0,0 +1,36 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+int
+main (void)
+{
+ void *h1 = dlopen ("$ORIGIN/testobj6.so", RTLD_GLOBAL|RTLD_LAZY);
+ if (h1 == NULL)
+ {
+ puts ("cannot open testobj6");
+ return 1;
+ }
+
+ void *h2 = dlopen ("$ORIGIN/testobj2.so",
+ RTLD_GLOBAL|RTLD_DEEPBIND|RTLD_LAZY);
+ if (h2 == NULL)
+ {
+ puts ("cannot open testobj2");
+ return 1;
+ }
+
+ dlclose (h1);
+
+ void (*f) (void) = dlsym (h2, "p");
+ if (f == NULL)
+ {
+ puts ("cannot find p");
+ return 1;
+ }
+
+ f ();
+
+ dlclose (h2);
+
+ return 0;
+}
diff --git a/libc/elf/tst-leaks1.c b/libc/elf/tst-leaks1.c
new file mode 100644
index 000000000..36e4aee9c
--- /dev/null
+++ b/libc/elf/tst-leaks1.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mcheck.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ mtrace ();
+
+ int ret = 0;
+ for (int i = 0; i < 10; i++)
+ {
+ void *h = dlopen (i < 5 ? "./tst-leaks1.c"
+ : "$ORIGIN/tst-leaks1.o", RTLD_LAZY);
+ if (h != NULL)
+ {
+ puts ("dlopen unexpectedly succeeded");
+ ret = 1;
+ dlclose (h);
+ }
+ }
+
+ return ret;
+}
diff --git a/libc/elf/tst-pathopt.c b/libc/elf/tst-pathopt.c
new file mode 100644
index 000000000..1f7aac2a4
--- /dev/null
+++ b/libc/elf/tst-pathopt.c
@@ -0,0 +1,39 @@
+#include <dlfcn.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+int
+main (void)
+{
+ void *h;
+ int (*fp) (int);
+ int result;
+
+ mtrace ();
+
+ h = dlopen ("renamed.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("failed to load \"%s\": %s\n", "renamed.so", dlerror ());
+ exit (1);
+ }
+
+ fp = dlsym (h, "in_renamed");
+ if (fp == NULL)
+ {
+ printf ("lookup of \"%s\" failed: %s\n", "in_renamed", dlerror ());
+ exit (1);
+ }
+
+ result = fp (10);
+
+ if (dlclose (h) != 0)
+ {
+ printf ("failed to close \"%s\": %s\n", "renamed.so", dlerror ());
+ exit (1);
+ }
+
+ return result;
+}
diff --git a/libc/elf/tst-pathopt.sh b/libc/elf/tst-pathopt.sh
new file mode 100755
index 000000000..0fe504e1a
--- /dev/null
+++ b/libc/elf/tst-pathopt.sh
@@ -0,0 +1,37 @@
+#! /bin/sh
+# Test lookup path optimization.
+# Copyright (C) 2000 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, write to the Free
+# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+# 02111-1307 USA.
+
+common_objpfx=$1
+run_program_prefix=$2
+
+test -e ${common_objpfx}elf/will-be-empty &&
+ rm -fr ${common_objpfx}elf/will-be-empty
+test -d ${common_objpfx}elf/for-renamed ||
+ mkdir ${common_objpfx}elf/for-renamed
+
+cp ${common_objpfx}elf/pathoptobj.so ${common_objpfx}elf/for-renamed/renamed.so
+
+LOCPATH=${common_objpfx}localedata GCONV_PATH=${common_objpfx}iconvdata \
+LC_ALL=C LD_LIBRARY_PATH=${common_objpfx}elf/will-be-empty:${common_objpfx}elf/for-renamed:${common_objpfx}.:${common_objpfx}dlfcn \
+ ${common_objpfx}elf/ld.so ${common_objpfx}elf/tst-pathopt \
+ > ${common_objpfx}elf/tst-pathopt.out
+
+exit $?
diff --git a/libc/elf/tst-pie1.c b/libc/elf/tst-pie1.c
new file mode 100644
index 000000000..75d941f21
--- /dev/null
+++ b/libc/elf/tst-pie1.c
@@ -0,0 +1,5 @@
+int
+foo (void)
+{
+ return 34;
+}
diff --git a/libc/elf/tst-piemod1.c b/libc/elf/tst-piemod1.c
new file mode 100644
index 000000000..ad439da80
--- /dev/null
+++ b/libc/elf/tst-piemod1.c
@@ -0,0 +1,20 @@
+#include <stdio.h>
+
+int
+foo (void)
+{
+ return 21;
+}
+
+int
+main (void)
+{
+ int val = foo ();
+ if (val != 34)
+ {
+ printf ("foo () returned %d\n", val);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/libc/elf/tst-stackguard1-static.c b/libc/elf/tst-stackguard1-static.c
new file mode 100644
index 000000000..db1e21554
--- /dev/null
+++ b/libc/elf/tst-stackguard1-static.c
@@ -0,0 +1 @@
+#include "tst-stackguard1.c"
diff --git a/libc/elf/tst-stackguard1.c b/libc/elf/tst-stackguard1.c
new file mode 100644
index 000000000..480f9297d
--- /dev/null
+++ b/libc/elf/tst-stackguard1.c
@@ -0,0 +1,196 @@
+/* Copyright (C) 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Jakub Jelinek <jakub@redhat.com>, 2005.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <stackguard-macros.h>
+#include <unistd.h>
+
+static const char *command;
+static bool child;
+static uintptr_t stack_chk_guard_copy;
+static bool stack_chk_guard_copy_set;
+static int fds[2];
+
+static void __attribute__ ((constructor))
+con (void)
+{
+ stack_chk_guard_copy = STACK_CHK_GUARD;
+ stack_chk_guard_copy_set = true;
+}
+
+static int
+uintptr_t_cmp (const void *a, const void *b)
+{
+ if (*(uintptr_t *) a < *(uintptr_t *) b)
+ return 1;
+ if (*(uintptr_t *) a > *(uintptr_t *) b)
+ return -1;
+ return 0;
+}
+
+static int
+do_test (void)
+{
+ if (!stack_chk_guard_copy_set)
+ {
+ puts ("constructor has not been run");
+ return 1;
+ }
+
+ if (stack_chk_guard_copy != STACK_CHK_GUARD)
+ {
+ puts ("STACK_CHK_GUARD changed between constructor and do_test");
+ return 1;
+ }
+
+ if (child)
+ {
+ write (2, &stack_chk_guard_copy, sizeof (stack_chk_guard_copy));
+ return 0;
+ }
+
+ if (command == NULL)
+ {
+ puts ("missing --command or --child argument");
+ return 1;
+ }
+
+#define N 16
+ uintptr_t child_stack_chk_guards[N + 1];
+ child_stack_chk_guards[N] = stack_chk_guard_copy;
+ int i;
+ for (i = 0; i < N; ++i)
+ {
+ if (pipe (fds) < 0)
+ {
+ printf ("couldn't create pipe: %m\n");
+ return 1;
+ }
+
+ pid_t pid = fork ();
+ if (pid < 0)
+ {
+ printf ("fork failed: %m\n");
+ return 1;
+ }
+
+ if (!pid)
+ {
+ if (stack_chk_guard_copy != STACK_CHK_GUARD)
+ {
+ puts ("STACK_CHK_GUARD changed after fork");
+ exit (1);
+ }
+
+ close (fds[0]);
+ close (2);
+ dup2 (fds[1], 2);
+ close (fds[1]);
+
+ system (command);
+ exit (0);
+ }
+
+ close (fds[1]);
+
+ if (TEMP_FAILURE_RETRY (read (fds[0], &child_stack_chk_guards[i],
+ sizeof (uintptr_t))) != sizeof (uintptr_t))
+ {
+ puts ("could not read stack_chk_guard value from child");
+ return 1;
+ }
+
+ close (fds[0]);
+
+ pid_t termpid;
+ int status;
+ termpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
+ if (termpid == -1)
+ {
+ printf ("waitpid failed: %m\n");
+ return 1;
+ }
+ else if (termpid != pid)
+ {
+ printf ("waitpid returned %ld != %ld\n",
+ (long int) termpid, (long int) pid);
+ return 1;
+ }
+ else if (!WIFEXITED (status) || WEXITSTATUS (status))
+ {
+ puts ("child hasn't exited with exit status 0");
+ return 1;
+ }
+ }
+
+ qsort (child_stack_chk_guards, N + 1, sizeof (uintptr_t), uintptr_t_cmp);
+
+ uintptr_t default_guard = 0;
+ unsigned char *p = (unsigned char *) &default_guard;
+ p[sizeof (uintptr_t) - 1] = 255;
+ p[sizeof (uintptr_t) - 2] = '\n';
+ p[0] = 0;
+
+ /* Test if the stack guard canaries are either randomized,
+ or equal to the default stack guard canary value.
+ Even with randomized stack guards it might happen
+ that the random number generator generates the same
+ values, but if that happens in more than half from
+ the 16 runs, something is very wrong. */
+ int ndifferences = 0;
+ int ndefaults = 0;
+ for (i = 0; i < N; ++i)
+ {
+ if (child_stack_chk_guards[i] != child_stack_chk_guards[i+1])
+ ndifferences++;
+ else if (child_stack_chk_guards[i] == default_guard)
+ ndefaults++;
+ }
+
+ printf ("differences %d defaults %d\n", ndifferences, ndefaults);
+
+ if (ndifferences < N / 2 && ndefaults < N / 2)
+ {
+ puts ("stack guard canaries are not randomized enough");
+ puts ("nor equal to the default canary value");
+ return 1;
+ }
+
+ return 0;
+}
+
+#define OPT_COMMAND 10000
+#define OPT_CHILD 10001
+#define CMDLINE_OPTIONS \
+ { "command", required_argument, NULL, OPT_COMMAND }, \
+ { "child", no_argument, NULL, OPT_CHILD },
+#define CMDLINE_PROCESS \
+ case OPT_COMMAND: \
+ command = optarg; \
+ break; \
+ case OPT_CHILD: \
+ child = true; \
+ break;
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls-dlinfo.c b/libc/elf/tst-tls-dlinfo.c
new file mode 100644
index 000000000..e97b5081f
--- /dev/null
+++ b/libc/elf/tst-tls-dlinfo.c
@@ -0,0 +1,92 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <tls.h>
+
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+#ifdef USE_TLS
+ static const char modname[] = "tst-tlsmod2.so";
+ int result = 0;
+ int *foop;
+ int (*fp) (int, int *);
+ void *h;
+
+ h = dlopen (modname, RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname, dlerror ());
+ exit (1);
+ }
+
+ fp = dlsym (h, "in_dso");
+ if (fp == NULL)
+ {
+ printf ("cannot get symbol 'in_dso': %s\n", dlerror ());
+ exit (1);
+ }
+
+ size_t modid = -1;
+ if (dlinfo (h, RTLD_DI_TLS_MODID, &modid))
+ {
+ printf ("dlinfo RTLD_DI_TLS_MODID failed: %s\n", dlerror ());
+ result = 1;
+ }
+ else
+ printf ("dlinfo says TLS module ID %Zu\n", modid);
+
+ void *block;
+ if (dlinfo (h, RTLD_DI_TLS_DATA, &block))
+ {
+ printf ("dlinfo RTLD_DI_TLS_DATA failed: %s\n", dlerror ());
+ result = 1;
+ }
+ else if (block != NULL)
+ {
+ printf ("dlinfo RTLD_DI_TLS_DATA says %p but should be unallocated\n",
+ block);
+ result = 1;
+ }
+
+ result |= fp (0, NULL);
+
+ foop = dlsym (h, "foo");
+ if (foop == NULL)
+ {
+ printf ("cannot get symbol 'foo' the second time: %s\n", dlerror ());
+ exit (1);
+ }
+ if (*foop != 16)
+ {
+ puts ("foo != 16");
+ result = 1;
+ }
+
+ /* Now the module's TLS block has been used and should appear. */
+ if (dlinfo (h, RTLD_DI_TLS_DATA, &block))
+ {
+ printf ("dlinfo RTLD_DI_TLS_DATA failed the second time: %s\n",
+ dlerror ());
+ result = 1;
+ }
+ else if (block != foop)
+ {
+ printf ("dlinfo RTLD_DI_TLS_DATA says %p but should be %p\n",
+ block, foop);
+ result = 1;
+ }
+
+ dlclose (h);
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls1-static.c b/libc/elf/tst-tls1-static.c
new file mode 100644
index 000000000..a01008073
--- /dev/null
+++ b/libc/elf/tst-tls1-static.c
@@ -0,0 +1 @@
+#include "tst-tls1.c"
diff --git a/libc/elf/tst-tls1.c b/libc/elf/tst-tls1.c
new file mode 100644
index 000000000..478f5bbdc
--- /dev/null
+++ b/libc/elf/tst-tls1.c
@@ -0,0 +1,91 @@
+/* glibc test for TLS in ld.so. */
+#include <stdio.h>
+
+#include <tls.h>
+
+#ifdef USE_TLS
+# include "tls-macros.h"
+
+
+/* Two common 'int' variables in TLS. */
+COMMON_INT_DEF(foo);
+COMMON_INT_DEF(bar);
+#endif
+
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+#ifdef USE_TLS
+ int result = 0;
+ int *ap, *bp;
+
+
+ /* Set the variable using the local exec model. */
+ puts ("set bar to 1 (LE)");
+ ap = TLS_LE (bar);
+ *ap = 1;
+
+
+ /* Get variables using initial exec model. */
+ fputs ("get sum of foo and bar (IE)", stdout);
+ ap = TLS_IE (foo);
+ bp = TLS_IE (bar);
+ printf (" = %d\n", *ap + *bp);
+ result |= *ap + *bp != 1;
+ if (*ap != 0)
+ {
+ printf ("foo = %d\n", *ap);
+ result = 1;
+ }
+ if (*bp != 1)
+ {
+ printf ("bar = %d\n", *bp);
+ result = 1;
+ }
+
+
+ /* Get variables using local dynamic model. */
+ fputs ("get sum of foo and bar (LD)", stdout);
+ ap = TLS_LD (foo);
+ bp = TLS_LD (bar);
+ printf (" = %d\n", *ap + *bp);
+ result |= *ap + *bp != 1;
+ if (*ap != 0)
+ {
+ printf ("foo = %d\n", *ap);
+ result = 1;
+ }
+ if (*bp != 1)
+ {
+ printf ("bar = %d\n", *bp);
+ result = 1;
+ }
+
+
+ /* Get variables using generic dynamic model. */
+ fputs ("get sum of foo and bar (GD)", stdout);
+ ap = TLS_GD (foo);
+ bp = TLS_GD (bar);
+ printf (" = %d\n", *ap + *bp);
+ result |= *ap + *bp != 1;
+ if (*ap != 0)
+ {
+ printf ("foo = %d\n", *ap);
+ result = 1;
+ }
+ if (*bp != 1)
+ {
+ printf ("bar = %d\n", *bp);
+ result = 1;
+ }
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls10.c b/libc/elf/tst-tls10.c
new file mode 100644
index 000000000..dbcc69727
--- /dev/null
+++ b/libc/elf/tst-tls10.c
@@ -0,0 +1,40 @@
+#include "tst-tls10.h"
+
+#ifdef USE_TLS__THREAD
+__thread int dummy __attribute__((visibility ("hidden"))) = 12;
+__thread struct A local = { 1, 2, 3 };
+#endif
+
+#define CHECK(N, S) \
+ p = f##N##a (); \
+ if (p->a != S || p->b != S + 1 || p->c != S + 2) \
+ abort ()
+
+int
+main (void)
+{
+#ifdef USE_TLS__THREAD
+ struct A *p;
+ if (local.a != 1 || local.b != 2 || local.c != 3)
+ abort ();
+ if (a1.a != 4 || a1.b != 5 || a1.c != 6)
+ abort ();
+ if (a2.a != 22 || a2.b != 23 || a2.c != 24)
+ abort ();
+ if (a3.a != 10 || a3.b != 11 || a3.c != 12)
+ abort ();
+ if (a4.a != 25 || a4.b != 26 || a4.c != 27)
+ abort ();
+ check1 ();
+ check2 ();
+ if (f1a () != &a1 || f2a () != &a2 || f3a () != &a3 || f4a () != &a4)
+ abort ();
+ CHECK (5, 16);
+ CHECK (6, 19);
+ if (f7a () != &a2 || f8a () != &a4)
+ abort ();
+ CHECK (9, 28);
+ CHECK (10, 31);
+#endif
+ exit (0);
+}
diff --git a/libc/elf/tst-tls10.h b/libc/elf/tst-tls10.h
new file mode 100644
index 000000000..1be6adc29
--- /dev/null
+++ b/libc/elf/tst-tls10.h
@@ -0,0 +1,38 @@
+#include <tls.h>
+#include <stdlib.h>
+
+#if defined USE_TLS && defined HAVE___THREAD \
+ && defined HAVE_TLS_MODEL_ATTRIBUTE
+# define USE_TLS__THREAD
+
+struct A
+{
+ char a;
+ int b;
+ long long c;
+};
+
+extern __thread struct A a1, a2, a3, a4;
+extern struct A *f1a (void);
+extern struct A *f2a (void);
+extern struct A *f3a (void);
+extern struct A *f4a (void);
+extern struct A *f5a (void);
+extern struct A *f6a (void);
+extern struct A *f7a (void);
+extern struct A *f8a (void);
+extern struct A *f9a (void);
+extern struct A *f10a (void);
+extern int f1b (void);
+extern int f2b (void);
+extern int f3b (void);
+extern int f4b (void);
+extern int f5b (void);
+extern int f6b (void);
+extern int f7b (void);
+extern int f8b (void);
+extern int f9b (void);
+extern int f10b (void);
+extern void check1 (void);
+extern void check2 (void);
+#endif
diff --git a/libc/elf/tst-tls11.c b/libc/elf/tst-tls11.c
new file mode 100644
index 000000000..816cf5cc2
--- /dev/null
+++ b/libc/elf/tst-tls11.c
@@ -0,0 +1,27 @@
+#include "tst-tls10.h"
+
+#define CHECK(N, S) \
+ p = f##N##a (); \
+ if (p->a != S || p->b != S + 1 || p->c != S + 2) \
+ abort ()
+
+int
+main (void)
+{
+#ifdef USE_TLS__THREAD
+ struct A *p;
+ check1 ();
+ check2 ();
+ CHECK (1, 4);
+ CHECK (2, 22);
+ CHECK (3, 10);
+ CHECK (4, 25);
+ CHECK (5, 16);
+ CHECK (6, 19);
+ CHECK (7, 22);
+ CHECK (8, 25);
+ CHECK (9, 28);
+ CHECK (10, 31);
+#endif
+ exit (0);
+}
diff --git a/libc/elf/tst-tls12.c b/libc/elf/tst-tls12.c
new file mode 100644
index 000000000..84aa7d35c
--- /dev/null
+++ b/libc/elf/tst-tls12.c
@@ -0,0 +1,18 @@
+#include "tst-tls10.h"
+
+#define CHECK(N, S) \
+ p = &a##N; \
+ if (p->a != S || p->b != S + 1 || p->c != S + 2) \
+ abort ()
+
+int
+main (void)
+{
+#ifdef USE_TLS__THREAD
+ struct A *p;
+ check1 ();
+ CHECK (1, 4);
+ CHECK (2, 7);
+#endif
+ exit (0);
+}
diff --git a/libc/elf/tst-tls13.c b/libc/elf/tst-tls13.c
new file mode 100644
index 000000000..06bfbacb5
--- /dev/null
+++ b/libc/elf/tst-tls13.c
@@ -0,0 +1,30 @@
+/* Check unloading modules with data in static TLS block. */
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+
+static int
+do_test (void)
+{
+ for (int i = 0; i < 1000;)
+ {
+ printf ("round %d\n",++i);
+
+ void *h = dlopen ("$ORIGIN/tst-tlsmod13a.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot load: %s\n", dlerror ());
+ exit (1);
+ }
+
+ dlclose (h);
+ }
+
+ return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#define TIMEOUT 3
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls14.c b/libc/elf/tst-tls14.c
new file mode 100644
index 000000000..428fd5293
--- /dev/null
+++ b/libc/elf/tst-tls14.c
@@ -0,0 +1,66 @@
+/* Check alignment of TLS variable. */
+#include <dlfcn.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <tls.h>
+
+#if USE_TLS && HAVE___THREAD
+
+#define AL 4096
+struct foo
+{
+ int i;
+} __attribute ((aligned (AL)));
+
+static __thread struct foo f;
+static struct foo g;
+
+
+extern int in_dso1 (void);
+
+
+static int
+do_test (void)
+{
+ int result = 0;
+
+ int fail = (((uintptr_t) &f) & (AL - 1)) != 0;
+ printf ("&f = %p %s\n", &f, fail ? "FAIL" : "OK");
+ result |= fail;
+
+ fail = (((uintptr_t) &g) & (AL - 1)) != 0;
+ printf ("&g = %p %s\n", &g, fail ? "FAIL" : "OK");
+ result |= fail;
+
+ result |= in_dso1 ();
+
+ void *h = dlopen ("tst-tlsmod14b.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot open tst-tlsmod14b.so: %m\n");
+ exit (1);
+ }
+
+ int (*fp) (void) = (int (*) (void)) dlsym (h, "in_dso2");
+ if (fp == NULL)
+ {
+ puts ("cannot find in_dso2");
+ exit (1);
+ }
+
+ result |= fp ();
+
+ return result;
+}
+
+#define TEST_FUNCTION do_test ()
+
+#else
+
+#define TEST_FUNCTION 0
+
+#endif
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls15.c b/libc/elf/tst-tls15.c
new file mode 100644
index 000000000..7ac963aa2
--- /dev/null
+++ b/libc/elf/tst-tls15.c
@@ -0,0 +1,32 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+static int
+do_test (void)
+{
+ void *h = dlopen ("tst-tlsmod15a.so", RTLD_NOW);
+ if (h != NULL)
+ {
+ puts ("unexpectedly succeeded to open tst-tlsmod15a.so");
+ exit (1);
+ }
+
+ h = dlopen ("tst-tlsmod15b.so", RTLD_NOW);
+ if (h == NULL)
+ {
+ puts ("failed to open tst-tlsmod15b.so");
+ exit (1);
+ }
+
+ int (*fp) (void) = (int (*) (void)) dlsym (h, "in_dso");
+ if (fp == NULL)
+ {
+ puts ("cannot find in_dso");
+ exit (1);
+ }
+
+ return fp ();
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls2-static.c b/libc/elf/tst-tls2-static.c
new file mode 100644
index 000000000..55ffa5744
--- /dev/null
+++ b/libc/elf/tst-tls2-static.c
@@ -0,0 +1 @@
+#include "tst-tls2.c"
diff --git a/libc/elf/tst-tls2.c b/libc/elf/tst-tls2.c
new file mode 100644
index 000000000..417489968
--- /dev/null
+++ b/libc/elf/tst-tls2.c
@@ -0,0 +1,91 @@
+/* glibc test for TLS in ld.so. */
+#include <stdio.h>
+
+#include <tls.h>
+
+#ifdef USE_TLS
+# include "tls-macros.h"
+
+
+/* Two 'int' variables in TLS. */
+VAR_INT_DEF(foo);
+VAR_INT_DEF(bar);
+#endif
+
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+#ifdef USE_TLS
+ int result = 0;
+ int *ap, *bp;
+
+
+ /* Set the variable using the local exec model. */
+ puts ("set bar to 1 (LE)");
+ ap = TLS_LE (bar);
+ *ap = 1;
+
+
+ /* Get variables using initial exec model. */
+ fputs ("get sum of foo and bar (IE)", stdout);
+ ap = TLS_IE (foo);
+ bp = TLS_IE (bar);
+ printf (" = %d\n", *ap + *bp);
+ result |= *ap + *bp != 1;
+ if (*ap != 0)
+ {
+ printf ("foo = %d\n", *ap);
+ result = 1;
+ }
+ if (*bp != 1)
+ {
+ printf ("bar = %d\n", *bp);
+ result = 1;
+ }
+
+
+ /* Get variables using local dynamic model. */
+ fputs ("get sum of foo and bar (LD)", stdout);
+ ap = TLS_LD (foo);
+ bp = TLS_LD (bar);
+ printf (" = %d\n", *ap + *bp);
+ result |= *ap + *bp != 1;
+ if (*ap != 0)
+ {
+ printf ("foo = %d\n", *ap);
+ result = 1;
+ }
+ if (*bp != 1)
+ {
+ printf ("bar = %d\n", *bp);
+ result = 1;
+ }
+
+
+ /* Get variables using generic dynamic model. */
+ fputs ("get sum of foo and bar (GD)", stdout);
+ ap = TLS_GD (foo);
+ bp = TLS_GD (bar);
+ printf (" = %d\n", *ap + *bp);
+ result |= *ap + *bp != 1;
+ if (*ap != 0)
+ {
+ printf ("foo = %d\n", *ap);
+ result = 1;
+ }
+ if (*bp != 1)
+ {
+ printf ("bar = %d\n", *bp);
+ result = 1;
+ }
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls3.c b/libc/elf/tst-tls3.c
new file mode 100644
index 000000000..84be43575
--- /dev/null
+++ b/libc/elf/tst-tls3.c
@@ -0,0 +1,76 @@
+/* glibc test for TLS in ld.so. */
+#include <stdio.h>
+
+#include <tls.h>
+
+#ifdef USE_TLS
+# include "tls-macros.h"
+
+
+/* One define int variable, two externs. */
+COMMON_INT_DECL(foo);
+VAR_INT_DECL(bar);
+VAR_INT_DEF(baz);
+#endif
+
+
+extern int in_dso (void);
+
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+#ifdef USE_TLS
+ int result = 0;
+ int *ap, *bp, *cp;
+
+
+ /* Set the variable using the local exec model. */
+ puts ("set baz to 3 (LE)");
+ ap = TLS_LE (baz);
+ *ap = 3;
+
+
+ /* Get variables using initial exec model. */
+ puts ("set variables foo and bar (IE)");
+ ap = TLS_IE (foo);
+ *ap = 1;
+ bp = TLS_IE (bar);
+ *bp = 2;
+
+
+ /* Get variables using local dynamic model. */
+ fputs ("get sum of foo, bar (GD) and baz (LD)", stdout);
+ ap = TLS_GD (foo);
+ bp = TLS_GD (bar);
+ cp = TLS_LD (baz);
+ printf (" = %d\n", *ap + *bp + *cp);
+ result |= *ap + *bp + *cp != 6;
+ if (*ap != 1)
+ {
+ printf ("foo = %d\n", *ap);
+ result = 1;
+ }
+ if (*bp != 2)
+ {
+ printf ("bar = %d\n", *bp);
+ result = 1;
+ }
+ if (*cp != 3)
+ {
+ printf ("baz = %d\n", *cp);
+ result = 1;
+ }
+
+
+ result |= in_dso ();
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls4.c b/libc/elf/tst-tls4.c
new file mode 100644
index 000000000..f92ee53ce
--- /dev/null
+++ b/libc/elf/tst-tls4.c
@@ -0,0 +1,56 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <tls.h>
+
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+#ifdef USE_TLS
+ static const char modname[] = "tst-tlsmod2.so";
+ int result = 0;
+ int *foop;
+ int (*fp) (int, int *);
+ void *h;
+
+ h = dlopen (modname, RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname, dlerror ());
+ exit (1);
+ }
+
+ fp = dlsym (h, "in_dso");
+ if (fp == NULL)
+ {
+ printf ("cannot get symbol 'in_dso': %s\n", dlerror ());
+ exit (1);
+ }
+
+ result |= fp (0, NULL);
+
+ foop = dlsym (h, "foo");
+ if (foop == NULL)
+ {
+ printf ("cannot get symbol 'foo' the second time: %s\n", dlerror ());
+ exit (1);
+ }
+ if (*foop != 16)
+ {
+ puts ("foo != 16");
+ result = 1;
+ }
+
+ dlclose (h);
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls5.c b/libc/elf/tst-tls5.c
new file mode 100644
index 000000000..a571d2cd3
--- /dev/null
+++ b/libc/elf/tst-tls5.c
@@ -0,0 +1,72 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <tls.h>
+
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+#ifdef USE_TLS
+ static const char modname[] = "tst-tlsmod2.so";
+ int result = 0;
+ int *foop;
+ int *foop2;
+ int (*fp) (int, int *);
+ void *h;
+
+ h = dlopen (modname, RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname, dlerror ());
+ exit (1);
+ }
+
+ foop = dlsym (h, "foo");
+ if (foop == NULL)
+ {
+ printf ("cannot get symbol 'foo': %s\n", dlerror ());
+ exit (1);
+ }
+
+ *foop = 42;
+
+ fp = dlsym (h, "in_dso");
+ if (fp == NULL)
+ {
+ printf ("cannot get symbol 'in_dso': %s\n", dlerror ());
+ exit (1);
+ }
+
+ result |= fp (42, foop);
+
+ foop2 = dlsym (h, "foo");
+ if (foop2 == NULL)
+ {
+ printf ("cannot get symbol 'foo' the second time: %s\n", dlerror ());
+ exit (1);
+ }
+
+ if (foop != foop2)
+ {
+ puts ("address of 'foo' different the second time");
+ result = 1;
+ }
+ else if (*foop != 16)
+ {
+ puts ("foo != 16");
+ result = 1;
+ }
+
+ dlclose (h);
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls6.c b/libc/elf/tst-tls6.c
new file mode 100644
index 000000000..68d706538
--- /dev/null
+++ b/libc/elf/tst-tls6.c
@@ -0,0 +1,90 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <link.h>
+#include <tls.h>
+
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+#ifdef USE_TLS
+ static const char modname[] = "tst-tlsmod2.so";
+ int result = 0;
+ int *foop;
+ int *foop2;
+ int (*fp) (int, int *);
+ void *h;
+ int i;
+ int modid = -1;
+
+ for (i = 0; i < 10; ++i)
+ {
+ h = dlopen (modname, RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname, dlerror ());
+ exit (1);
+ }
+
+ /* Dirty test code here: we peek into a private data structure.
+ We make sure that the module gets assigned the same ID every
+ time. The value of the first round is used. */
+ if (modid == -1)
+ modid = ((struct link_map *) h)->l_tls_modid;
+ else if (((struct link_map *) h)->l_tls_modid != modid)
+ {
+ printf ("round %d: modid now %zd, initially %d\n",
+ i, ((struct link_map *) h)->l_tls_modid, modid);
+ result = 1;
+ }
+
+ foop = dlsym (h, "foo");
+ if (foop == NULL)
+ {
+ printf ("cannot get symbol 'foo': %s\n", dlerror ());
+ exit (1);
+ }
+
+ *foop = 42 + i;
+
+ fp = dlsym (h, "in_dso");
+ if (fp == NULL)
+ {
+ printf ("cannot get symbol 'in_dso': %s\n", dlerror ());
+ exit (1);
+ }
+
+ result |= fp (42 + i, foop);
+
+ foop2 = dlsym (h, "foo");
+ if (foop2 == NULL)
+ {
+ printf ("cannot get symbol 'foo' the second time: %s\n", dlerror ());
+ exit (1);
+ }
+
+ if (foop != foop2)
+ {
+ puts ("address of 'foo' different the second time");
+ result = 1;
+ }
+ else if (*foop != 16)
+ {
+ puts ("foo != 16");
+ result = 1;
+ }
+
+ dlclose (h);
+ }
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls7.c b/libc/elf/tst-tls7.c
new file mode 100644
index 000000000..37f1a63e1
--- /dev/null
+++ b/libc/elf/tst-tls7.c
@@ -0,0 +1,61 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <link.h>
+#include <tls.h>
+
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+#ifdef USE_TLS
+ static const char modname[] = "tst-tlsmod3.so";
+ int result = 0;
+ int (*fp) (void);
+ void *h;
+ int i;
+ int modid = -1;
+
+ for (i = 0; i < 10; ++i)
+ {
+ h = dlopen (modname, RTLD_LAZY);
+ if (h == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname, dlerror ());
+ exit (1);
+ }
+
+ /* Dirty test code here: we peek into a private data structure.
+ We make sure that the module gets assigned the same ID every
+ time. The value of the first round is used. */
+ if (modid == -1)
+ modid = ((struct link_map *) h)->l_tls_modid;
+ else if (((struct link_map *) h)->l_tls_modid != (size_t) modid)
+ {
+ printf ("round %d: modid now %zu, initially %d\n",
+ i, ((struct link_map *) h)->l_tls_modid, modid);
+ result = 1;
+ }
+
+ fp = dlsym (h, "in_dso2");
+ if (fp == NULL)
+ {
+ printf ("cannot get symbol 'in_dso2': %s\n", dlerror ());
+ exit (1);
+ }
+
+ result |= fp ();
+
+ dlclose (h);
+ }
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls8.c b/libc/elf/tst-tls8.c
new file mode 100644
index 000000000..ccc4e9f6f
--- /dev/null
+++ b/libc/elf/tst-tls8.c
@@ -0,0 +1,174 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <link.h>
+#include <tls.h>
+
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+#ifdef USE_TLS
+ static const char modname1[] = "$ORIGIN/tst-tlsmod3.so";
+ static const char modname2[] = "$ORIGIN/tst-tlsmod4.so";
+ int result = 0;
+ int (*fp1) (void);
+ int (*fp2) (int, int *);
+ void *h1;
+ void *h2;
+ int i;
+ size_t modid1 = (size_t) -1;
+ size_t modid2 = (size_t) -1;
+ int *bazp;
+
+ for (i = 0; i < 10; ++i)
+ {
+ h1 = dlopen (modname1, RTLD_LAZY);
+ if (h1 == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname1, dlerror ());
+ exit (1);
+ }
+
+ /* Dirty test code here: we peek into a private data structure.
+ We make sure that the module gets assigned the same ID every
+ time. The value of the first round is used. */
+ if (modid1 == (size_t) -1)
+ modid1 = ((struct link_map *) h1)->l_tls_modid;
+ else if (((struct link_map *) h1)->l_tls_modid != modid1)
+ {
+ printf ("round %d: modid now %zd, initially %zd\n",
+ i, ((struct link_map *) h1)->l_tls_modid, modid1);
+ result = 1;
+ }
+
+ fp1 = dlsym (h1, "in_dso2");
+ if (fp1 == NULL)
+ {
+ printf ("cannot get symbol 'in_dso2' in %s\n", modname1);
+ exit (1);
+ }
+
+ result |= fp1 ();
+
+
+
+ h2 = dlopen (modname2, RTLD_LAZY);
+ if (h2 == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname2, dlerror ());
+ exit (1);
+ }
+
+ /* Dirty test code here: we peek into a private data structure.
+ We make sure that the module gets assigned the same ID every
+ time. The value of the first round is used. */
+ if (modid2 == (size_t) -1)
+ modid2 = ((struct link_map *) h1)->l_tls_modid;
+ else if (((struct link_map *) h1)->l_tls_modid != modid2)
+ {
+ printf ("round %d: modid now %zd, initially %zd\n",
+ i, ((struct link_map *) h1)->l_tls_modid, modid2);
+ result = 1;
+ }
+
+ bazp = dlsym (h2, "baz");
+ if (bazp == NULL)
+ {
+ printf ("cannot get symbol 'baz' in %s\n", modname2);
+ exit (1);
+ }
+
+ *bazp = 42 + i;
+
+ fp2 = dlsym (h2, "in_dso");
+ if (fp2 == NULL)
+ {
+ printf ("cannot get symbol 'in_dso' in %s\n", modname2);
+ exit (1);
+ }
+
+ result |= fp2 (42 + i, bazp);
+
+ dlclose (h1);
+ dlclose (h2);
+
+
+ h1 = dlopen (modname1, RTLD_LAZY);
+ if (h1 == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname1, dlerror ());
+ exit (1);
+ }
+
+ /* Dirty test code here: we peek into a private data structure.
+ We make sure that the module gets assigned the same ID every
+ time. The value of the first round is used. */
+ if (((struct link_map *) h1)->l_tls_modid != modid1)
+ {
+ printf ("round %d: modid now %zd, initially %zd\n",
+ i, ((struct link_map *) h1)->l_tls_modid, modid1);
+ result = 1;
+ }
+
+ fp1 = dlsym (h1, "in_dso2");
+ if (fp1 == NULL)
+ {
+ printf ("cannot get symbol 'in_dso2' in %s\n", modname1);
+ exit (1);
+ }
+
+ result |= fp1 ();
+
+
+
+ h2 = dlopen (modname2, RTLD_LAZY);
+ if (h2 == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname2, dlerror ());
+ exit (1);
+ }
+
+ /* Dirty test code here: we peek into a private data structure.
+ We make sure that the module gets assigned the same ID every
+ time. The value of the first round is used. */
+ if (((struct link_map *) h1)->l_tls_modid != modid2)
+ {
+ printf ("round %d: modid now %zd, initially %zd\n",
+ i, ((struct link_map *) h1)->l_tls_modid, modid2);
+ result = 1;
+ }
+
+ bazp = dlsym (h2, "baz");
+ if (bazp == NULL)
+ {
+ printf ("cannot get symbol 'baz' in %s\n", modname2);
+ exit (1);
+ }
+
+ *bazp = 62 + i;
+
+ fp2 = dlsym (h2, "in_dso");
+ if (fp2 == NULL)
+ {
+ printf ("cannot get symbol 'in_dso' in %s\n", modname2);
+ exit (1);
+ }
+
+ result |= fp2 (62 + i, bazp);
+
+ /* This time the dlclose calls are in reverse order. */
+ dlclose (h2);
+ dlclose (h1);
+ }
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tls9-static.c b/libc/elf/tst-tls9-static.c
new file mode 100644
index 000000000..51812ccc7
--- /dev/null
+++ b/libc/elf/tst-tls9-static.c
@@ -0,0 +1 @@
+#include "tst-tls9.c"
diff --git a/libc/elf/tst-tls9.c b/libc/elf/tst-tls9.c
new file mode 100644
index 000000000..e317696df
--- /dev/null
+++ b/libc/elf/tst-tls9.c
@@ -0,0 +1,42 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <link.h>
+#include <tls.h>
+
+#define TEST_FUNCTION do_test ()
+static int
+do_test (void)
+{
+#ifdef USE_TLS
+ static const char modname1[] = "tst-tlsmod5.so";
+ static const char modname2[] = "tst-tlsmod6.so";
+ int result = 0;
+
+ void *h1 = dlopen (modname1, RTLD_LAZY);
+ if (h1 == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname1, dlerror ());
+ result = 1;
+ }
+ void *h2 = dlopen (modname2, RTLD_LAZY);
+ if (h2 == NULL)
+ {
+ printf ("cannot open '%s': %s\n", modname2, dlerror ());
+ result = 1;
+ }
+
+ if (h1 != NULL)
+ dlclose (h1);
+ if (h2 != NULL)
+ dlclose (h2);
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+
+#include "../test-skeleton.c"
diff --git a/libc/elf/tst-tlsmod1.c b/libc/elf/tst-tlsmod1.c
new file mode 100644
index 000000000..c74a617b7
--- /dev/null
+++ b/libc/elf/tst-tlsmod1.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+
+#include <tls.h>
+
+#ifdef USE_TLS
+#include "tls-macros.h"
+
+
+/* One define int variable, two externs. */
+COMMON_INT_DEF(foo);
+VAR_INT_DEF(bar);
+VAR_INT_DECL(baz);
+#endif
+
+extern int in_dso (void);
+
+int
+in_dso (void)
+{
+ int result = 0;
+#ifdef USE_TLS
+ int *ap, *bp, *cp;
+
+ /* Get variables using initial exec model. */
+ fputs ("get sum of foo and bar (IE)", stdout);
+ asm ("" ::: "memory");
+ ap = TLS_IE (foo);
+ bp = TLS_IE (bar);
+ printf (" = %d\n", *ap + *bp);
+ result |= *ap + *bp != 3;
+ if (*ap != 1)
+ {
+ printf ("foo = %d\n", *ap);
+ result = 1;
+ }
+ if (*bp != 2)
+ {
+ printf ("bar = %d\n", *bp);
+ result = 1;
+ }
+
+
+ /* Get variables using generic dynamic model. */
+ fputs ("get sum of foo and bar and baz (GD)", stdout);
+ ap = TLS_GD (foo);
+ bp = TLS_GD (bar);
+ cp = TLS_GD (baz);
+ printf (" = %d\n", *ap + *bp + *cp);
+ result |= *ap + *bp + *cp != 6;
+ if (*ap != 1)
+ {
+ printf ("foo = %d\n", *ap);
+ result = 1;
+ }
+ if (*bp != 2)
+ {
+ printf ("bar = %d\n", *bp);
+ result = 1;
+ }
+ if (*cp != 3)
+ {
+ printf ("baz = %d\n", *cp);
+ result = 1;
+ }
+#endif
+
+ return result;
+}
diff --git a/libc/elf/tst-tlsmod10.c b/libc/elf/tst-tlsmod10.c
new file mode 100644
index 000000000..32e54f3c0
--- /dev/null
+++ b/libc/elf/tst-tlsmod10.c
@@ -0,0 +1 @@
+#include "tst-tlsmod8.c"
diff --git a/libc/elf/tst-tlsmod11.c b/libc/elf/tst-tlsmod11.c
new file mode 100644
index 000000000..9938b5753
--- /dev/null
+++ b/libc/elf/tst-tlsmod11.c
@@ -0,0 +1,6 @@
+#include "tst-tls10.h"
+
+#ifdef USE_TLS__THREAD
+__thread struct A a1 = { 4, 5, 6 };
+__thread struct A a2 = { 7, 8, 9 };
+#endif
diff --git a/libc/elf/tst-tlsmod12.c b/libc/elf/tst-tlsmod12.c
new file mode 100644
index 000000000..4602709a1
--- /dev/null
+++ b/libc/elf/tst-tlsmod12.c
@@ -0,0 +1,14 @@
+#include "tst-tls10.h"
+
+#ifdef USE_TLS__THREAD
+extern __thread struct A a2 __attribute__((tls_model("initial-exec")));
+
+void
+check1 (void)
+{
+ if (a1.a != 4 || a1.b != 5 || a1.c != 6)
+ abort ();
+ if (a2.a != 7 || a2.b != 8 || a2.c != 9)
+ abort ();
+}
+#endif
diff --git a/libc/elf/tst-tlsmod13.c b/libc/elf/tst-tlsmod13.c
new file mode 100644
index 000000000..beca89f6f
--- /dev/null
+++ b/libc/elf/tst-tlsmod13.c
@@ -0,0 +1,14 @@
+#include <tls.h>
+
+#if defined USE_TLS && defined HAVE___THREAD \
+ && defined HAVE_TLS_MODEL_ATTRIBUTE
+__thread int a[2] __attribute__ ((tls_model ("initial-exec")));
+#else
+int a[2];
+#endif
+
+int
+foo (void)
+{
+ return a[0];
+}
diff --git a/libc/elf/tst-tlsmod13a.c b/libc/elf/tst-tlsmod13a.c
new file mode 100644
index 000000000..14b12b032
--- /dev/null
+++ b/libc/elf/tst-tlsmod13a.c
@@ -0,0 +1,16 @@
+#include <tls.h>
+
+#if defined USE_TLS && defined HAVE___THREAD \
+ && defined HAVE_TLS_MODEL_ATTRIBUTE
+__thread int b[2] __attribute__ ((tls_model ("initial-exec")));
+#else
+int b[2];
+#endif
+
+extern int foo (void);
+
+int
+bar (void)
+{
+ return foo () + b[0];
+}
diff --git a/libc/elf/tst-tlsmod14a.c b/libc/elf/tst-tlsmod14a.c
new file mode 100644
index 000000000..0bb393d9c
--- /dev/null
+++ b/libc/elf/tst-tlsmod14a.c
@@ -0,0 +1,41 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#include <tls.h>
+
+#if USE_TLS && HAVE___THREAD
+
+#define AL 4096
+struct foo
+{
+ int i;
+} __attribute ((aligned (AL)));
+
+static __thread struct foo f;
+static struct foo g;
+
+
+#ifndef FCT
+# define FCT in_dso1
+#endif
+
+
+int
+FCT (void)
+{
+ puts (__func__);
+
+ int result = 0;
+
+ int fail = (((uintptr_t) &f) & (AL - 1)) != 0;
+ printf ("&f = %p %s\n", &f, fail ? "FAIL" : "OK");
+ result |= fail;
+
+ fail = (((uintptr_t) &g) & (AL - 1)) != 0;
+ printf ("&g = %p %s\n", &g, fail ? "FAIL" : "OK");
+ result |= fail;
+
+ return result;
+}
+
+#endif
diff --git a/libc/elf/tst-tlsmod14b.c b/libc/elf/tst-tlsmod14b.c
new file mode 100644
index 000000000..24d9ceaf7
--- /dev/null
+++ b/libc/elf/tst-tlsmod14b.c
@@ -0,0 +1,2 @@
+#define FCT in_dso2
+#include "tst-tlsmod14a.c"
diff --git a/libc/elf/tst-tlsmod15a.c b/libc/elf/tst-tlsmod15a.c
new file mode 100644
index 000000000..66c707129
--- /dev/null
+++ b/libc/elf/tst-tlsmod15a.c
@@ -0,0 +1,6 @@
+extern int nonexistent_dummy_var;
+int *
+foo (void)
+{
+ return &nonexistent_dummy_var;
+}
diff --git a/libc/elf/tst-tlsmod15b.c b/libc/elf/tst-tlsmod15b.c
new file mode 100644
index 000000000..4f63eab14
--- /dev/null
+++ b/libc/elf/tst-tlsmod15b.c
@@ -0,0 +1,17 @@
+#include "tst-tls10.h"
+
+#ifdef USE_TLS__THREAD
+__thread int mod15b_var __attribute__((tls_model("initial-exec")));
+
+int
+in_dso (void)
+{
+ return mod15b_var;
+}
+#else
+int
+in_dso (void)
+{
+ return 0;
+}
+#endif
diff --git a/libc/elf/tst-tlsmod2.c b/libc/elf/tst-tlsmod2.c
new file mode 100644
index 000000000..98d9d3e51
--- /dev/null
+++ b/libc/elf/tst-tlsmod2.c
@@ -0,0 +1,38 @@
+#include <stdio.h>
+
+#include <tls.h>
+
+#ifdef USE_TLS
+#include "tls-macros.h"
+
+
+COMMON_INT_DEF(foo);
+
+
+int
+in_dso (int n, int *caller_foop)
+{
+ int *foop;
+ int result = 0;
+
+ puts ("foo"); /* Make sure PLT is used before macros. */
+ asm ("" ::: "memory");
+
+ foop = TLS_GD (foo);
+
+ if (caller_foop != NULL && foop != caller_foop)
+ {
+ printf ("callers address of foo differs: %p vs %p\n", caller_foop, foop);
+ result = 1;
+ }
+ else if (*foop != n)
+ {
+ printf ("foo != %d\n", n);
+ result = 1;
+ }
+
+ *foop = 16;
+
+ return result;
+}
+#endif
diff --git a/libc/elf/tst-tlsmod3.c b/libc/elf/tst-tlsmod3.c
new file mode 100644
index 000000000..4a8aad659
--- /dev/null
+++ b/libc/elf/tst-tlsmod3.c
@@ -0,0 +1,41 @@
+#include <stdio.h>
+
+#include <tls.h>
+
+#ifdef USE_TLS
+# include "tls-macros.h"
+
+extern int in_dso (int n, int *caller_foop);
+
+COMMON_INT_DEF(comm_n);
+
+
+
+
+int
+in_dso2 (void)
+{
+ int *foop;
+ int result = 0;
+ static int n;
+ int *np;
+
+ puts ("foo"); /* Make sure PLT is used before macros. */
+ asm ("" ::: "memory");
+
+ foop = TLS_GD (foo);
+ np = TLS_GD (comm_n);
+
+ if (n != *np)
+ {
+ printf ("n = %d != comm_n = %d\n", n, *np);
+ result = 1;
+ }
+
+ result |= in_dso (*foop = 42 + n++, foop);
+
+ *foop = 16;
+
+ return result;
+}
+#endif
diff --git a/libc/elf/tst-tlsmod4.c b/libc/elf/tst-tlsmod4.c
new file mode 100644
index 000000000..5285e821b
--- /dev/null
+++ b/libc/elf/tst-tlsmod4.c
@@ -0,0 +1,38 @@
+#include <stdio.h>
+
+#include <tls.h>
+
+#ifdef USE_TLS
+# include "tls-macros.h"
+
+
+COMMON_INT_DEF(baz);
+
+
+int
+in_dso (int n, int *caller_bazp)
+{
+ int *bazp;
+ int result = 0;
+
+ puts ("foo"); /* Make sure PLT is used before macros. */
+ asm ("" ::: "memory");
+
+ bazp = TLS_GD (baz);
+
+ if (caller_bazp != NULL && bazp != caller_bazp)
+ {
+ printf ("callers address of baz differs: %p vs %p\n", caller_bazp, bazp);
+ result = 1;
+ }
+ else if (*bazp != n)
+ {
+ printf ("baz != %d\n", n);
+ result = 1;
+ }
+
+ *bazp = 16;
+
+ return result;
+}
+#endif
diff --git a/libc/elf/tst-tlsmod5.c b/libc/elf/tst-tlsmod5.c
new file mode 100644
index 000000000..2ec69e13b
--- /dev/null
+++ b/libc/elf/tst-tlsmod5.c
@@ -0,0 +1,7 @@
+#include <tls.h>
+
+#ifdef USE_TLS
+#include "tls-macros.h"
+
+COMMON_INT_DEF(foo);
+#endif
diff --git a/libc/elf/tst-tlsmod6.c b/libc/elf/tst-tlsmod6.c
new file mode 100644
index 000000000..0fda51b22
--- /dev/null
+++ b/libc/elf/tst-tlsmod6.c
@@ -0,0 +1,7 @@
+#include <tls.h>
+
+#ifdef USE_TLS
+#include "tls-macros.h"
+
+COMMON_INT_DEF(bar);
+#endif
diff --git a/libc/elf/tst-tlsmod7.c b/libc/elf/tst-tlsmod7.c
new file mode 100644
index 000000000..944b97f9c
--- /dev/null
+++ b/libc/elf/tst-tlsmod7.c
@@ -0,0 +1,103 @@
+#include "tst-tls10.h"
+
+#ifdef USE_TLS__THREAD
+__thread int dummy __attribute__((visibility ("hidden"))) = 12;
+__thread struct A a1 = { 4, 5, 6 };
+__thread struct A a2 = { 7, 8, 9 };
+__thread struct A a3 __attribute__((tls_model("initial-exec")))
+ = { 10, 11, 12 };
+__thread struct A a4 __attribute__((tls_model("initial-exec")))
+ = { 13, 14, 15 };
+static __thread struct A local1 = { 16, 17, 18 };
+static __thread struct A local2 __attribute__((tls_model("initial-exec")))
+ = { 19, 20, 21 };
+
+void
+check1 (void)
+{
+ if (a1.a != 4 || a1.b != 5 || a1.c != 6)
+ abort ();
+ if (a2.a != 22 || a2.b != 23 || a2.c != 24)
+ abort ();
+ if (a3.a != 10 || a3.b != 11 || a3.c != 12)
+ abort ();
+ if (a4.a != 25 || a4.b != 26 || a4.c != 27)
+ abort ();
+ if (local1.a != 16 || local1.b != 17 || local1.c != 18)
+ abort ();
+ if (local2.a != 19 || local2.b != 20 || local2.c != 21)
+ abort ();
+}
+
+struct A *
+f1a (void)
+{
+ return &a1;
+}
+
+struct A *
+f2a (void)
+{
+ return &a2;
+}
+
+struct A *
+f3a (void)
+{
+ return &a3;
+}
+
+struct A *
+f4a (void)
+{
+ return &a4;
+}
+
+struct A *
+f5a (void)
+{
+ return &local1;
+}
+
+struct A *
+f6a (void)
+{
+ return &local2;
+}
+
+int
+f1b (void)
+{
+ return a1.a;
+}
+
+int
+f2b (void)
+{
+ return a2.b;
+}
+
+int
+f3b (void)
+{
+ return a3.c;
+}
+
+int
+f4b (void)
+{
+ return a4.a;
+}
+
+int
+f5b (void)
+{
+ return local1.b;
+}
+
+int
+f6b (void)
+{
+ return local2.c;
+}
+#endif
diff --git a/libc/elf/tst-tlsmod8.c b/libc/elf/tst-tlsmod8.c
new file mode 100644
index 000000000..c1822fc0c
--- /dev/null
+++ b/libc/elf/tst-tlsmod8.c
@@ -0,0 +1,72 @@
+#include "tst-tls10.h"
+
+#ifdef USE_TLS__THREAD
+__thread long long dummy __attribute__((visibility ("hidden"))) = 12;
+__thread struct A a2 = { 22, 23, 24 };
+__thread struct A a4 __attribute__((tls_model("initial-exec")))
+ = { 25, 26, 27 };
+static __thread struct A local1 = { 28, 29, 30 };
+static __thread struct A local2 __attribute__((tls_model("initial-exec")))
+ = { 31, 32, 33 };
+
+void
+check2 (void)
+{
+ if (a2.a != 22 || a2.b != 23 || a2.c != 24)
+ abort ();
+ if (a4.a != 25 || a4.b != 26 || a4.c != 27)
+ abort ();
+ if (local1.a != 28 || local1.b != 29 || local1.c != 30)
+ abort ();
+ if (local2.a != 31 || local2.b != 32 || local2.c != 33)
+ abort ();
+}
+
+struct A *
+f7a (void)
+{
+ return &a2;
+}
+
+struct A *
+f8a (void)
+{
+ return &a4;
+}
+
+struct A *
+f9a (void)
+{
+ return &local1;
+}
+
+struct A *
+f10a (void)
+{
+ return &local2;
+}
+
+int
+f7b (void)
+{
+ return a2.b;
+}
+
+int
+f8b (void)
+{
+ return a4.a;
+}
+
+int
+f9b (void)
+{
+ return local1.b;
+}
+
+int
+f10b (void)
+{
+ return local2.c;
+}
+#endif
diff --git a/libc/elf/tst-tlsmod9.c b/libc/elf/tst-tlsmod9.c
new file mode 100644
index 000000000..e124144e4
--- /dev/null
+++ b/libc/elf/tst-tlsmod9.c
@@ -0,0 +1,101 @@
+#include "tst-tls10.h"
+
+#ifdef USE_TLS__THREAD
+__thread int dummy __attribute__((visibility ("hidden"))) = 12;
+__thread struct A a1 = { 4, 5, 6 };
+__thread struct A a3 __attribute__((tls_model("initial-exec")))
+ = { 10, 11, 12 };
+extern __thread struct A a4 __attribute__((tls_model("initial-exec")));
+static __thread struct A local1 = { 16, 17, 18 };
+static __thread struct A local2 __attribute__((tls_model("initial-exec")))
+ = { 19, 20, 21 };
+
+void
+check1 (void)
+{
+ if (a1.a != 4 || a1.b != 5 || a1.c != 6)
+ abort ();
+ if (a2.a != 22 || a2.b != 23 || a2.c != 24)
+ abort ();
+ if (a3.a != 10 || a3.b != 11 || a3.c != 12)
+ abort ();
+ if (a4.a != 25 || a4.b != 26 || a4.c != 27)
+ abort ();
+ if (local1.a != 16 || local1.b != 17 || local1.c != 18)
+ abort ();
+ if (local2.a != 19 || local2.b != 20 || local2.c != 21)
+ abort ();
+}
+
+struct A *
+f1a (void)
+{
+ return &a1;
+}
+
+struct A *
+f2a (void)
+{
+ return &a2;
+}
+
+struct A *
+f3a (void)
+{
+ return &a3;
+}
+
+struct A *
+f4a (void)
+{
+ return &a4;
+}
+
+struct A *
+f5a (void)
+{
+ return &local1;
+}
+
+struct A *
+f6a (void)
+{
+ return &local2;
+}
+
+int
+f1b (void)
+{
+ return a1.a;
+}
+
+int
+f2b (void)
+{
+ return a2.b;
+}
+
+int
+f3b (void)
+{
+ return a3.c;
+}
+
+int
+f4b (void)
+{
+ return a4.a;
+}
+
+int
+f5b (void)
+{
+ return local1.b;
+}
+
+int
+f6b (void)
+{
+ return local2.c;
+}
+#endif
diff --git a/libc/elf/unload.c b/libc/elf/unload.c
new file mode 100644
index 000000000..4566f226f
--- /dev/null
+++ b/libc/elf/unload.c
@@ -0,0 +1,91 @@
+/* Test for unloading (really unmapping) of objects. By Franz Sirl.
+ This test does not have to passed in all dlopen() et.al. implementation
+ since it is not required the unloading actually happens. But we
+ require it for glibc. */
+
+#include <dlfcn.h>
+#include <link.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define MAPS ((struct link_map *) _r_debug.r_map)
+
+#define OUT \
+ for (map = MAPS; map != NULL; map = map->l_next) \
+ if (map->l_type == lt_loaded) \
+ printf ("name = \"%s\", direct_opencount = %d\n", \
+ map->l_name, (int) map->l_direct_opencount); \
+ fflush (stdout)
+
+typedef struct
+{
+ void *next;
+} strct;
+
+int
+main (void)
+{
+ void *sohandle;
+ strct *testdat;
+ int ret;
+ int result = 0;
+ struct link_map *map;
+
+ mtrace ();
+
+ puts ("\nBefore");
+ OUT;
+
+ sohandle = dlopen ("unloadmod.so", RTLD_NOW | RTLD_GLOBAL);
+ if (sohandle == NULL)
+ {
+ printf ("*** first dlopen failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ puts ("\nAfter loading unloadmod.so");
+ OUT;
+
+ testdat = dlsym (sohandle, "testdat");
+ testdat->next = (void *) -1;
+
+ ret = dlclose (sohandle);
+ if (ret != 0)
+ {
+ puts ("*** first dlclose failed");
+ result = 1;
+ }
+
+ puts ("\nAfter closing unloadmod.so");
+ OUT;
+
+ sohandle = dlopen ("unloadmod.so", RTLD_NOW | RTLD_GLOBAL);
+ if (sohandle == NULL)
+ {
+ printf ("*** second dlopen failed: %s\n", dlerror ());
+ exit (1);
+ }
+
+ puts ("\nAfter loading unloadmod.so the second time");
+ OUT;
+
+ testdat = dlsym (sohandle, "testdat");
+ if (testdat->next == (void *) -1)
+ {
+ puts ("*** testdat->next == (void *) -1");
+ result = 1;
+ }
+
+ ret = dlclose (sohandle);
+ if (ret != 0)
+ {
+ puts ("*** second dlclose failed");
+ result = 1;
+ }
+
+ puts ("\nAfter closing unloadmod.so again");
+ OUT;
+
+ return result;
+}
diff --git a/libc/elf/unload2.c b/libc/elf/unload2.c
new file mode 100644
index 000000000..eef2bfd42
--- /dev/null
+++ b/libc/elf/unload2.c
@@ -0,0 +1,59 @@
+#include <dlfcn.h>
+#include <elf.h>
+#include <errno.h>
+#include <error.h>
+#include <link.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define MAPS ((struct link_map *) _r_debug.r_map)
+
+#define OUT \
+ for (map = MAPS; map != NULL; map = map->l_next) \
+ if (map->l_type == lt_loaded) \
+ printf ("name = \"%s\", direct_opencount = %d\n", \
+ map->l_name, (int) map->l_direct_opencount); \
+ fflush (stdout)
+
+int
+main (void)
+{
+ void *h[3];
+ struct link_map *map;
+ void (*fp) (void);
+
+ h[0] = dlopen ("unload2mod.so", RTLD_LAZY);
+ h[1] = dlopen ("unload2mod.so", RTLD_LAZY);
+ if (h[0] == NULL || h[1] == NULL)
+ error (EXIT_FAILURE, errno, "cannot load \"unload2mod.so\"");
+ h[2] = dlopen ("unload2dep.so", RTLD_LAZY);
+ if (h[2] == NULL)
+ error (EXIT_FAILURE, errno, "cannot load \"unload2dep.so\"");
+
+ puts ("\nAfter loading everything:");
+ OUT;
+
+ dlclose (h[0]);
+
+ puts ("\nAfter unloading \"unload2mod.so\" once:");
+ OUT;
+
+ dlclose (h[1]);
+
+ puts ("\nAfter unloading \"unload2mod.so\" twice:");
+ OUT;
+
+ fp = dlsym (h[2], "foo");
+ puts ("\nnow calling `foo'");
+ fflush (stdout);
+ fp ();
+ puts ("managed to call `foo'");
+ fflush (stdout);
+
+ dlclose (h[2]);
+
+ puts ("\nAfter unloading \"unload2dep.so\":");
+ OUT;
+
+ return 0;
+}
diff --git a/libc/elf/unload2dep.c b/libc/elf/unload2dep.c
new file mode 100644
index 000000000..0d319515d
--- /dev/null
+++ b/libc/elf/unload2dep.c
@@ -0,0 +1,6 @@
+extern void foo (void);
+
+void
+foo (void)
+{
+}
diff --git a/libc/elf/unload2mod.c b/libc/elf/unload2mod.c
new file mode 100644
index 000000000..9c2ea586b
--- /dev/null
+++ b/libc/elf/unload2mod.c
@@ -0,0 +1,8 @@
+extern void foo (void);
+extern void bar (void);
+
+void
+bar (void)
+{
+ foo ();
+}
diff --git a/libc/elf/unload3.c b/libc/elf/unload3.c
new file mode 100644
index 000000000..6f1af707e
--- /dev/null
+++ b/libc/elf/unload3.c
@@ -0,0 +1,41 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+int
+main (void)
+{
+ void *g = dlopen ("unload3mod1.so", RTLD_GLOBAL | RTLD_NOW);
+ void *h = dlopen ("unload3mod2.so", RTLD_GLOBAL | RTLD_NOW);
+ if (g == NULL || h == NULL)
+ {
+ printf ("dlopen unload3mod{1,2}.so failed: %p %p\n", g, h);
+ return 1;
+ }
+ dlclose (h);
+ dlclose (g);
+
+ g = dlopen ("unload3mod3.so", RTLD_GLOBAL | RTLD_NOW);
+ h = dlopen ("unload3mod4.so", RTLD_GLOBAL | RTLD_NOW);
+ if (g == NULL || h == NULL)
+ {
+ printf ("dlopen unload3mod{3,4}.so failed: %p %p\n", g, h);
+ return 1;
+ }
+
+ int (*fn) (int);
+ fn = dlsym (h, "bar");
+ if (fn == NULL)
+ {
+ puts ("dlsym failed");
+ return 1;
+ }
+
+ int val = fn (16);
+ if (val != 24)
+ {
+ printf ("bar returned %d != 24\n", val);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/libc/elf/unload3mod1.c b/libc/elf/unload3mod1.c
new file mode 100644
index 000000000..e886b11c1
--- /dev/null
+++ b/libc/elf/unload3mod1.c
@@ -0,0 +1 @@
+int dummy1;
diff --git a/libc/elf/unload3mod2.c b/libc/elf/unload3mod2.c
new file mode 100644
index 000000000..03252a523
--- /dev/null
+++ b/libc/elf/unload3mod2.c
@@ -0,0 +1 @@
+int dummy2;
diff --git a/libc/elf/unload3mod3.c b/libc/elf/unload3mod3.c
new file mode 100644
index 000000000..046022c55
--- /dev/null
+++ b/libc/elf/unload3mod3.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int
+foo (int x)
+{
+ puts ("foo");
+ return x * 2;
+}
diff --git a/libc/elf/unload3mod4.c b/libc/elf/unload3mod4.c
new file mode 100644
index 000000000..52f808e79
--- /dev/null
+++ b/libc/elf/unload3mod4.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+
+extern int foo (int x);
+
+int
+bar (int x)
+{
+ puts ("bar");
+ fflush (stdout);
+ x = foo (x - 4);
+ puts ("bar after foo");
+ return x;
+}
diff --git a/libc/elf/unload4.c b/libc/elf/unload4.c
new file mode 100644
index 000000000..6e171a22e
--- /dev/null
+++ b/libc/elf/unload4.c
@@ -0,0 +1,48 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <malloc.h>
+
+int
+main (void)
+{
+#ifdef M_PERTURB
+ mallopt (M_PERTURB, 0xaa);
+#endif
+
+ void *h;
+ int (*fn) (int);
+ h = dlopen ("unload4mod1.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ puts ("1st dlopen failed");
+ return 1;
+ }
+ fn = dlsym (h, "foo");
+ if (fn == NULL)
+ {
+ puts ("dlsym failed");
+ return 1;
+ }
+ int n = fn (10);
+ if (n != 28)
+ {
+ printf ("foo (10) returned %d != 28\n", n);
+ return 1;
+ }
+ dlclose (h);
+ h = dlopen ("unload4mod3.so", RTLD_LAZY);
+ fn = dlsym (h, "mod3fn2");
+ if (fn == NULL)
+ {
+ puts ("second dlsym failed");
+ return 1;
+ }
+ n = fn (10);
+ if (n != 22)
+ {
+ printf ("mod3fn2 (10) returned %d != 22\n", n);
+ return 1;
+ }
+ dlclose (h);
+ return 0;
+}
diff --git a/libc/elf/unload4mod1.c b/libc/elf/unload4mod1.c
new file mode 100644
index 000000000..38c5b0168
--- /dev/null
+++ b/libc/elf/unload4mod1.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+extern int bar (int);
+
+int
+foo (int x)
+{
+ puts ("in foo");
+ return bar (x / 2) + 2;
+}
diff --git a/libc/elf/unload4mod2.c b/libc/elf/unload4mod2.c
new file mode 100644
index 000000000..497ef5d93
--- /dev/null
+++ b/libc/elf/unload4mod2.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int
+baz (int x)
+{
+ puts ("in baz");
+ return x * 4;
+}
diff --git a/libc/elf/unload4mod3.c b/libc/elf/unload4mod3.c
new file mode 100644
index 000000000..4b280bc05
--- /dev/null
+++ b/libc/elf/unload4mod3.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+int
+__attribute__((noinline))
+mod3fn1 (int x)
+{
+ puts ("in mod3fn1");
+ return x + 6;
+}
+
+int
+mod3fn2 (int x)
+{
+ puts ("in mod3fn2");
+ return mod3fn1 (x / 2) * 2;
+}
diff --git a/libc/elf/unload4mod4.c b/libc/elf/unload4mod4.c
new file mode 100644
index 000000000..ba5a144d3
--- /dev/null
+++ b/libc/elf/unload4mod4.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+__attribute__((noinline))
+baz (int x)
+{
+ abort ();
+}
+
+int
+bar (int x)
+{
+ puts ("in bar");
+ return baz (x + 1) + 2;
+}
diff --git a/libc/elf/unload5.c b/libc/elf/unload5.c
new file mode 100644
index 000000000..0555052ce
--- /dev/null
+++ b/libc/elf/unload5.c
@@ -0,0 +1,42 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+int
+main (void)
+{
+ void *g = dlopen ("unload3mod1.so", RTLD_GLOBAL | RTLD_NOW);
+ void *h = dlopen ("unload3mod2.so", RTLD_GLOBAL | RTLD_NOW);
+ if (g == NULL || h == NULL)
+ {
+ printf ("dlopen unload3mod{1,2}.so failed: %p %p\n", g, h);
+ return 1;
+ }
+ dlopen ("unload3mod4.so", RTLD_GLOBAL | RTLD_NOW);
+ dlclose (h);
+ dlclose (g);
+
+ g = dlopen ("unload3mod3.so", RTLD_GLOBAL | RTLD_NOW);
+ h = dlopen ("unload3mod4.so", RTLD_GLOBAL | RTLD_NOW);
+ if (g == NULL || h == NULL)
+ {
+ printf ("dlopen unload3mod{3,4}.so failed: %p %p\n", g, h);
+ return 1;
+ }
+
+ int (*fn) (int);
+ fn = dlsym (h, "bar");
+ if (fn == NULL)
+ {
+ puts ("dlsym failed");
+ return 1;
+ }
+
+ int val = fn (16);
+ if (val != 24)
+ {
+ printf ("bar returned %d != 24\n", val);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/libc/elf/unload6.c b/libc/elf/unload6.c
new file mode 100644
index 000000000..1efc7eb84
--- /dev/null
+++ b/libc/elf/unload6.c
@@ -0,0 +1,30 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+int
+main (void)
+{
+ void *h = dlopen ("unload6mod1.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ puts ("dlopen unload6mod1.so failed");
+ return 1;
+ }
+
+ int (*fn) (int);
+ fn = dlsym (h, "foo");
+ if (fn == NULL)
+ {
+ puts ("dlsym failed");
+ return 1;
+ }
+
+ int val = fn (16);
+ if (val != 24)
+ {
+ printf ("foo returned %d != 24\n", val);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/libc/elf/unload6mod1.c b/libc/elf/unload6mod1.c
new file mode 100644
index 000000000..24f2e5a19
--- /dev/null
+++ b/libc/elf/unload6mod1.c
@@ -0,0 +1,16 @@
+#include <dlfcn.h>
+#include <stdio.h>
+
+int
+foo (int i)
+{
+ void *h = dlopen ("unload6mod2.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ puts ("dlopen unload6mod2.so failed");
+ return 1;
+ }
+
+ dlclose (h);
+ return i + 8;
+}
diff --git a/libc/elf/unload6mod2.c b/libc/elf/unload6mod2.c
new file mode 100644
index 000000000..980efa4b0
--- /dev/null
+++ b/libc/elf/unload6mod2.c
@@ -0,0 +1,23 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <unistd.h>
+
+static void *h;
+
+static void __attribute__((constructor))
+mod2init (void)
+{
+ h = dlopen ("unload6mod3.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ puts ("dlopen unload6mod3.so failed");
+ fflush (stdout);
+ _exit (1);
+ }
+}
+
+static void __attribute__((destructor))
+mod2fini (void)
+{
+ dlclose (h);
+}
diff --git a/libc/elf/unload6mod3.c b/libc/elf/unload6mod3.c
new file mode 100644
index 000000000..7b29e1d62
--- /dev/null
+++ b/libc/elf/unload6mod3.c
@@ -0,0 +1,23 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <unistd.h>
+
+static void *h;
+
+static void __attribute__((constructor))
+mod3init (void)
+{
+ h = dlopen ("unload6mod1.so", RTLD_LAZY);
+ if (h == NULL)
+ {
+ puts ("dlopen unload6mod1.so failed");
+ fflush (stdout);
+ _exit (1);
+ }
+}
+
+static void __attribute__((destructor))
+mod3fini (void)
+{
+ dlclose (h);
+}
diff --git a/libc/elf/unloadmod.c b/libc/elf/unloadmod.c
new file mode 100644
index 000000000..3aa5403ed
--- /dev/null
+++ b/libc/elf/unloadmod.c
@@ -0,0 +1,4 @@
+struct testdat
+{
+ void *next;
+} testdat;
diff --git a/libc/elf/vismain.c b/libc/elf/vismain.c
new file mode 100644
index 000000000..ebf9b65c3
--- /dev/null
+++ b/libc/elf/vismain.c
@@ -0,0 +1,257 @@
+/* Copyright (C) 2000, 2003 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vismod.h"
+
+/* Prototype for our test function. */
+extern int do_test (void);
+
+#define TEST_FUNCTION do_test ()
+
+/* This defines the `main' function and some more. */
+#include <test-skeleton.c>
+
+
+/* Prototypes for local functions. */
+extern int protlocal (void);
+
+const char *protvarlocal = __FILE__;
+extern const char *protvarinmod;
+extern const char *protvaritcpt;
+
+int
+do_test (void)
+{
+ int res = 0;
+ int val;
+
+ /* First test: check whether .protected is handled correctly by the
+ assembler/linker. The uses of `protlocal' in the DSOs and in the
+ main program should all be resolved with the local definitions. */
+ val = protlocal () + calllocal1 () + calllocal2 ();
+ if (val != 0x155)
+ {
+ puts ("\
+The handling of `.protected' seems to be implemented incorrectly: giving up");
+ abort ();
+ }
+ puts ("`.protected' seems to be handled correctly, good!");
+
+ /* Function pointers: for functions which are marked local and for
+ which definitions are available all function pointers must be
+ distinct. */
+ if (protlocal == getlocal1 ())
+ {
+ puts ("`protlocal' in main and mod1 have same address");
+ res = 1;
+ }
+ if (protlocal == getlocal2 ())
+ {
+ puts ("`protlocal' in main and mod2 have same address");
+ res = 1;
+ }
+ if (getlocal1 () == getlocal2 ())
+ {
+ puts ("`protlocal' in mod1 and mod2 have same address");
+ res = 1;
+ }
+ if (getlocal1 () () + getlocal2 () () != 0x44)
+ {
+ puts ("pointers to `protlocal' in mod1 or mod2 incorrect");
+ res = 1;
+ }
+
+ /* Next test. This is similar to the last one but the function we
+ are calling is not defined in the main object. This means that
+ the invocation in the main object uses the definition in the
+ first DSO. */
+ if (protinmod != getinmod1 ())
+ {
+ printf ("&protinmod in main (%p) != &protinmod in mod1 (%p)\n",
+ protinmod, getinmod1 ());
+ res = 1;
+ }
+ if (protinmod == getinmod2 ())
+ {
+ puts ("`protinmod' in main and mod2 have same address");
+ res = 1;
+ }
+ if (getinmod1 () == getinmod2 ())
+ {
+ puts ("`protinmod' in mod1 and mod2 have same address");
+ res = 1;
+ }
+ if (protinmod () + getinmod1 () () + getinmod2 () () != 0x4800)
+ {
+ puts ("pointers to `protinmod' in mod1 or mod2 incorrect");
+ res = 1;
+ }
+ val = protinmod () + callinmod1 () + callinmod2 ();
+ if (val != 0x15800)
+ {
+ printf ("calling of `protinmod' leads to wrong result (%#x)\n", val);
+ res = 1;
+ }
+
+ /* A very similar text. Same setup for the main object and the modules
+ but this time we have another definition in a preloaded module. This
+ one intercepts the references from the main object. */
+ if (protitcpt != getitcpt3 ())
+ {
+ printf ("&protitcpt in main (%p) != &protitcpt in mod3 (%p)\n",
+ &protitcpt, getitcpt3 ());
+ res = 1;
+ }
+ if (protitcpt == getitcpt1 ())
+ {
+ puts ("`protitcpt' in main and mod1 have same address");
+ res = 1;
+ }
+ if (protitcpt == getitcpt2 ())
+ {
+ puts ("`protitcpt' in main and mod2 have same address");
+ res = 1;
+ }
+ if (getitcpt1 () == getitcpt2 ())
+ {
+ puts ("`protitcpt' in mod1 and mod2 have same address");
+ res = 1;
+ }
+ val = protitcpt () + getitcpt1 () () + getitcpt2 () () + getitcpt3 () ();
+ if (val != 0x8440000)
+ {
+ printf ("\
+pointers to `protitcpt' in mod1 or mod2 or mod3 incorrect (%#x)\n", val);
+ res = 1;
+ }
+ val = protitcpt () + callitcpt1 () + callitcpt2 () + callitcpt3 ();
+ if (val != 0x19540000)
+ {
+ printf ("calling of `protitcpt' leads to wrong result (%#x)\n", val);
+ res = 1;
+ }
+
+ /* Now look at variables. First a variable which is available
+ everywhere. We must have three different addresses. */
+ if (&protvarlocal == getvarlocal1 ())
+ {
+ puts ("`protvarlocal' in main and mod1 have same address");
+ res = 1;
+ }
+ if (&protvarlocal == getvarlocal2 ())
+ {
+ puts ("`protvarlocal' in main and mod2 have same address");
+ res = 1;
+ }
+ if (getvarlocal1 () == getvarlocal2 ())
+ {
+ puts ("`protvarlocal' in mod1 and mod2 have same address");
+ res = 1;
+ }
+ if (strcmp (protvarlocal, __FILE__) != 0)
+ {
+ puts ("`protvarlocal in main has wrong value");
+ res = 1;
+ }
+ if (strcmp (*getvarlocal1 (), "vismod1.c") != 0)
+ {
+ puts ("`getvarlocal1' returns wrong value");
+ res = 1;
+ }
+ if (strcmp (*getvarlocal2 (), "vismod2.c") != 0)
+ {
+ puts ("`getvarlocal2' returns wrong value");
+ res = 1;
+ }
+
+ /* Now the case where there is no local definition. */
+ if (&protvarinmod != getvarinmod1 ())
+ {
+ printf ("&protvarinmod in main (%p) != &protitcpt in mod1 (%p)\n",
+ &protvarinmod, getvarinmod1 ());
+ // XXX Possibly enable once fixed.
+ // res = 1;
+ }
+ if (&protvarinmod == getvarinmod2 ())
+ {
+ puts ("`protvarinmod' in main and mod2 have same address");
+ res = 1;
+ }
+ if (strcmp (*getvarinmod1 (), "vismod1.c") != 0)
+ {
+ puts ("`getvarinmod1' returns wrong value");
+ res = 1;
+ }
+ if (strcmp (*getvarinmod2 (), "vismod2.c") != 0)
+ {
+ puts ("`getvarinmod2' returns wrong value");
+ res = 1;
+ }
+
+ /* And a test where a variable definition is intercepted. */
+ if (&protvaritcpt == getvaritcpt1 ())
+ {
+ puts ("`protvaritcpt' in main and mod1 have same address");
+ res = 1;
+ }
+ if (&protvaritcpt == getvaritcpt2 ())
+ {
+ puts ("`protvaritcpt' in main and mod2 have same address");
+ res = 1;
+ }
+ if (&protvaritcpt != getvaritcpt3 ())
+ {
+ printf ("&protvaritcpt in main (%p) != &protvaritcpt in mod3 (%p)\n",
+ &protvaritcpt, getvaritcpt3 ());
+ // XXX Possibly enable once fixed.
+ // res = 1;
+ }
+ if (getvaritcpt1 () == getvaritcpt2 ())
+ {
+ puts ("`protvaritcpt' in mod1 and mod2 have same address");
+ res = 1;
+ }
+ if (strcmp (protvaritcpt, "vismod3.c") != 0)
+ {
+ puts ("`protvaritcpt in main has wrong value");
+ res = 1;
+ }
+ if (strcmp (*getvaritcpt1 (), "vismod1.c") != 0)
+ {
+ puts ("`getvaritcpt1' returns wrong value");
+ res = 1;
+ }
+ if (strcmp (*getvaritcpt2 (), "vismod2.c") != 0)
+ {
+ puts ("`getvaritcpt2' returns wrong value");
+ res = 1;
+ }
+
+ return res;
+}
+
+
+int
+protlocal (void)
+{
+ return 0x1;
+}
diff --git a/libc/elf/vismod.h b/libc/elf/vismod.h
new file mode 100644
index 000000000..ef05ffd5e
--- /dev/null
+++ b/libc/elf/vismod.h
@@ -0,0 +1,27 @@
+/* Prototypes for the functions in the DSOs. */
+extern int calllocal1 (void);
+extern int (*getlocal1 (void)) (void);
+extern int callinmod1 (void);
+extern int (*getinmod1 (void)) (void);
+extern int callitcpt1 (void);
+extern int (*getitcpt1 (void)) (void);
+extern const char **getvarlocal1 (void);
+extern const char **getvarinmod1 (void);
+extern const char **getvaritcpt1 (void);
+extern int calllocal2 (void);
+extern int (*getlocal2 (void)) (void);
+extern int callinmod2 (void);
+extern int (*getinmod2 (void)) (void);
+extern int callitcpt2 (void);
+extern int (*getitcpt2 (void)) (void);
+extern const char **getvarlocal2 (void);
+extern const char **getvarinmod2 (void);
+extern const char **getvaritcpt2 (void);
+extern int callitcpt3 (void);
+extern int (*getitcpt3 (void)) (void);
+extern const char **getvaritcpt3 (void);
+
+extern int protinmod (void);
+extern int protitcpt (void);
+extern int protlocal (void);
+
diff --git a/libc/elf/vismod1.c b/libc/elf/vismod1.c
new file mode 100644
index 000000000..6f9c1a59f
--- /dev/null
+++ b/libc/elf/vismod1.c
@@ -0,0 +1,104 @@
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include "vismod.h"
+
+int
+protlocal (void)
+{
+ return 0x4;
+}
+asm (".protected protlocal");
+
+
+int
+calllocal1 (void)
+{
+ return protlocal () + 0x10;
+}
+
+int
+(*getlocal1 (void)) (void)
+{
+ return protlocal;
+}
+
+int
+protinmod (void)
+{
+ return 0x400;
+}
+asm (".protected protinmod");
+
+int
+callinmod1 (void)
+{
+ return protinmod () + 0x1000;
+}
+
+int
+(*getinmod1 (void)) (void)
+{
+ return protinmod;
+}
+
+int
+protitcpt (void)
+{
+ return 0x40000;
+}
+asm (".protected protitcpt");
+
+int
+callitcpt1 (void)
+{
+ return protitcpt () + 0x100000;
+}
+
+int
+(*getitcpt1 (void)) (void)
+{
+ return protitcpt;
+}
+
+const char *protvarlocal = __FILE__;
+asm (".protected protvarlocal");
+
+const char **
+getvarlocal1 (void)
+{
+ return &protvarlocal;
+}
+
+const char *protvarinmod = __FILE__;
+asm (".protected protvarinmod");
+
+const char **
+getvarinmod1 (void)
+{
+ return &protvarinmod;
+}
+
+const char *protvaritcpt = __FILE__;
+asm (".protected protvaritcpt");
+
+const char **
+getvaritcpt1 (void)
+{
+ return &protvaritcpt;
+}
diff --git a/libc/elf/vismod2.c b/libc/elf/vismod2.c
new file mode 100644
index 000000000..5443e7c8a
--- /dev/null
+++ b/libc/elf/vismod2.c
@@ -0,0 +1,124 @@
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <stdlib.h>
+#include "vismod.h"
+
+int
+protlocal (void)
+{
+ return 0x40;
+}
+asm (".protected protlocal");
+
+
+int
+calllocal2 (void)
+{
+ return protlocal () + 0x100;
+}
+
+int
+(*getlocal2 (void)) (void)
+{
+ return protlocal;
+}
+
+int
+protinmod (void)
+{
+ return 0x4000;
+}
+asm (".protected protinmod");
+
+int
+callinmod2 (void)
+{
+ return protinmod () + 0x10000;
+}
+
+int
+(*getinmod2 (void)) (void)
+{
+ return protinmod;
+}
+
+int
+protitcpt (void)
+{
+ return 0x400000;
+}
+asm (".protected protitcpt");
+
+int
+callitcpt2 (void)
+{
+ return protitcpt () + 0x1000000;
+}
+
+int
+(*getitcpt2 (void)) (void)
+{
+ return protitcpt;
+}
+
+const char *protvarlocal = __FILE__;
+asm (".protected protvarlocal");
+
+const char **
+getvarlocal2 (void)
+{
+ return &protvarlocal;
+}
+
+const char *protvarinmod = __FILE__;
+asm (".protected protvarinmod");
+
+const char **
+getvarinmod2 (void)
+{
+ return &protvarinmod;
+}
+
+const char *protvaritcpt = __FILE__;
+asm (".protected protvaritcpt");
+
+const char **
+getvaritcpt2 (void)
+{
+ return &protvaritcpt;
+}
+
+/* We must never call these functions. */
+int
+callitcpt3 (void)
+{
+ abort ();
+}
+
+int
+(*getitcpt3 (void)) (void)
+{
+ abort ();
+}
+
+const char **
+getvaritcpt3 (void)
+{
+ abort ();
+}
diff --git a/libc/elf/vismod3.c b/libc/elf/vismod3.c
new file mode 100644
index 000000000..c9df95cb0
--- /dev/null
+++ b/libc/elf/vismod3.c
@@ -0,0 +1,47 @@
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include "vismod.h"
+
+int
+protitcpt (void)
+{
+ return 0x4000000;
+}
+asm (".protected protitcpt");
+
+int
+callitcpt3 (void)
+{
+ return protitcpt () + 0x10000000;
+}
+
+int
+(*getitcpt3 (void)) (void)
+{
+ return protitcpt;
+}
+
+const char *protvaritcpt = __FILE__;
+asm (".protected protvaritcpt");
+
+const char **
+getvaritcpt3 (void)
+{
+ return &protvaritcpt;
+}