diff options
author | Eric Andersen <andersen@codepoet.org> | 2001-04-23 17:43:54 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2001-04-23 17:43:54 +0000 |
commit | 66f269d2a51dae6a2cb10f1a9ae4aaeba815219b (patch) | |
tree | e2094832990caf6d849ba90e4b1a82a6264f86b3 /ldso | |
parent | c4a3f3f81ea90e3df93c352ac0e2161a4bfd3327 (diff) |
Initial checkin for ld.so. This is a combination of effort from Manuel Novoa
III and me. I've been working on stripping out arch dependant stuff and
replacing it with generic stuff whenever possible.
-Erik
Diffstat (limited to 'ldso')
58 files changed, 13958 insertions, 0 deletions
diff --git a/ldso/COPYRIGHT b/ldso/COPYRIGHT new file mode 100644 index 000000000..fb34248ec --- /dev/null +++ b/ldso/COPYRIGHT @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald, David Engel, + * Hongjiu Lu and Mitch D'Souza + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the above contributors may not be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Notice of general intent: + * + * The linux operating system generally contains large amounts of code + * that fall under the GNU General Public License, or GPL for short. + * This file contains source code that by it's very nature would always + * be linked with an application program, and because of this a GPL + * type of copyright on this file would place restrictions upon the + * distribution of binary-only commercial software. Since the goal of + * the Linux project as a whole is not to discourage the development and + * distribution of commercial software for Linux, this file has been + * placed under a more relaxed BSD-style of copyright. + * + * It is the general understanding of the above contributors that a + * program executable linked to a library containing code that falls + * under the GPL or GLPL style of license is not subject to the terms of + * the GPL or GLPL license if the program executable(s) that are supplied + * are linked to a shared library form of the GPL or GLPL library, and as + * long as the form of the shared library is such that it is possible for + * the end user to modify and rebuild the library and use it in + * conjunction with the program executable. + */ diff --git a/ldso/Makefile b/ldso/Makefile new file mode 100644 index 000000000..23626f5e3 --- /dev/null +++ b/ldso/Makefile @@ -0,0 +1,13 @@ +include Config.mk + +SUBDIRS = util d-link # man + +all: + set -e ; for d in $(SUBDIRS) ; do $(MAKE) -C $$d ; done + +install: all + sh instldso.sh + +clean: + set -e ; for d in $(SUBDIRS) ; do $(MAKE) -C $$d $@ ; done + -find . -name '*~' | xargs rm -f diff --git a/ldso/README b/ldso/README new file mode 100644 index 000000000..025694ee3 --- /dev/null +++ b/ldso/README @@ -0,0 +1,860 @@ + +Apr 20, 2001 -- Manuel Novoa III + +Inital port for uClibc from debian ld.so_1.9.11-9.tar.gz. + +Removed a.out support. + +"make" generates the ld-linux-uclibc.so.1, libdl.so.1, ldd, ldconfig + suitable for the target platform. + +"make DEVEL=true" generates the same files, but ld-linux-uclibc.so.1 and + ldconfig are modified to not conflict with the devel platform system + libs. (This is only of use if TARGET_ARCH == NATIVE_ARCH.) These + modified versions ignore /lib and /usr/lib and look for shared libs + only in $(INSTALL_DIR)/lib. The modified ldconfig writes ld.so.cache + in $(INSTALL_DIR)/etc. + +The above assumes you've set the DYNAMIC_LINKER to /lib/ld-linux-uclibc.so.1 +in extra/gcc-uClibc/Makefile. + +Todo: + +Remove unneeded code in util/ldd.c. +Link against static uClibc instead of using custom routines. ??? +Lots more cleanup... especially the arch-dependent Makefiles. + +****************** original ld.so.lsm file ************************** +Begin3 +Title: Linux shared, dynamic linker and utilities. +Version: 1.9.11 +Entered-date: 01MAY99 +Description: This package contains ld.so, ld-linux.so, ldconfig, + ldd and libdl. +Keywords: dynamic linker, shared library, ld.so, ld-linux.so, + ldconfig, ldd, libdl +Author: david@ods.com (David Engel) +Maintained-by: david@ods.com (David Engel) +Primary-site: tsx-11.mit.edu /pub/linux/packages/GCC + ld.so-1.9.11.tar.gz +Alternate-site: sunsite.unc.edu /pub/Linux/GCC + ld.so-1.9.11.tar.gz +Platform: Linux 2.0.0 or later. +Copying-policy: Copyrighted but freely distributable. +End +********************************************************************* + Original README starts here +********************************************************************* + +This package contains my ELF dynamic linkers (ld-linux.so.1), dynamic +linker library (libdl.so.1) and utilities (ldconfig and ldd) for Linux. + +You need Linux kernel 2.0.0 or later with ELF support compiled in +(i.e. not loaded as a module) to use this package. + +The dynamic linker is used to bootstrap programs and load shared +libraries at startup. The dynamic linker library is used to +dynamically load shared libraries after a program is running. +Ldconfig is used to automatically update the symbolic links to shared +libraries and build the cache file used by the dynamic linker. Ldd is +used to list the shared libraries used by a program. + +Please see the included manual pages for further details. + +To install, simply run "sh instldso.sh" as root. Ready-to-go versions +of all end-products are provided so nothing should need to be compiled +or linked. If you are still using libc5 as your primary development +library, you should use the "--devfiles" option when running +instldso.sh to install the file needed to compile with libdl. + +ELF versions of gcc, binutils and libc are now required to compile +everything, including the old, unsupported, a.out dynamic linker. +Finally, an optimization level of O2 or higher must be used to compile +ld-linux.so and libdl.so due the use of inline functions. + +Notable contributors to this package include Eric Youngdale, Peter +MacDonald, Hongjiu Lu, Linus Torvalds, Lars Wirzenius, Mitch D'Souza, +Rik Faith, Andreas Schwab and Adam Richter (not necessarily in that +order). + +###################### IMPORTANT NOTICES ############################# + +A.OUT SUPPORT: + +As of ld.so-1.9.0, the old, a.out dynamic loader is no longer +officially supported. The code is still included and built, but I +make no promises that it will work. I will accept patches for it, +but they will not be tested by me. + +GLIBC (AKA LIBC6) SUPPORT: + +As of ld.so-1.9.0, the main focus of this package is to ease the +transition to libc6. No significant, new features are expected to be +added. If you need new features, switch to libc6. + +Except for libpthread.so, the sonames of the core libraries provided +with libc6 have been chosen so they do not conflict with those +provided by libc5 and ld.so. However, the current plan is not use +new, nonconflicting sonames for other libraries such as ncurses and +X11. This presents two problems. First, libraries using the same +soname for both libc5 and libc6 can not be placed in the same +directory. Second, the dynamic linkers need to make sure not to load +a library for the wrong version of libc. + +The first problem is easy. Just move the old, libc5-based libraries +to new directories (e.g. /lib/libc5-compat, /usr/lib/libc5-compat, +etc.) and add those directories to /etc/ld.so.conf. Then install the +new, libc6-based versions in the standard places. + +The second problem is more difficult. Ideally, the dynamic linkers +would be changed to perform a complete dependency analysis on every +library to be loaded to make sure the wrong versions aren't used. +This approach doesn't seem worth the added complexity, especially +since we now have symbol versioning for ELF libraries. Instead a +simpler approach will be used, at least initially. + +Ldconfig has been modified to perform a (currently simple) dependency +analysis on libraries and to store an indication in /etc/ld.so.cache +of whether a library is for libc5, libc6 or an unknown libc. The +dynamic linkers then only need to make a simple check at run-time to +make sure they don't load the wrong version of a library. + +The dynamic linker for libc5 provided in this package, has already +been modified to use the new information in /etc/ld.so.cache. For +glibc versions 2.0.1 and earlier, the dynamic linker for libc6 needs +the patch contained in glibc.patch. You should apply the patch and +rebuild glibc before using the new ldconfig. + +As stated above, the dependency analysis currently done by ldconfig is +rather simple. Basically, it looks for the sonames used by the +various versions of libc, libm and libdl. For any approach using a +dependency analysis such as this to work, it is very important that +shared libraries be built with complete dependency information. This +can be done by using the appropriate -l options when running 'gcc +-shared'. For example, when building libfoo.so which depends on libc +and libbar, you should add -lbar and -lc gcc command line. + +###################################################################### + +Changes in version 1.9.11: + + Fixed a bug in ld-linux.so where a reference to an + undefined symbol could cause a segfault. + + Added a clarification for LD_PRELOAD to the ld.so manual + page and added a symlink for ld-linux.so (Bug#33123). + + Don't install ldd for Debian except for the m68k arch + because glibc 2.1 now includes it (Bug#35458). + +Changes in version 1.9.10: + + Changed ldconfig to issue a warning and not overwrite a + regular file with a symlink (Bug#30859). + + Changed Debian packaging to conflict with and replace the + ldconfig package (Bug#29398). + +Changes in version 1.9.9: + + Changed ld-linux.so and libdl.so to match glibc by not + allowing user preloads of system libraries into setu/gid + binaries unless the library itself is setuid. + + Fixed problems in ld-linux.so on the sparc architecture + (Juan Cespedes). + +Changes in version 1.9.8: + + Changed ldconfig to allow the expected type for all + libraries in a directory to be optionally specified + (Mark Phillips). See the ldconfig man page. + + Changed ldconfig to use the same type names used in the + change above when the -p option is used. + +Changes in version 1.9.7: + + Changed ldd for m68k to use /lib/ld.so.1 instead of + /lib/ld-linux.so.2. + + Added support for dladdr to libdl.so (Eduard Gode). + + Fixed a small memory leak in libdl.so (Richard Garnish). + + Fixed a bug in ldconfig when the -l option was used on a + filename without a '/' in it. + + Updated the man pages (Bug#6404, Bug#9721, Bug#10652, + Bug#13494 and Bug#14127). They could still use some work. + + No longer install the info page since it's way out of date. + + Fixed minor Debian packaging problems (Bug#13160, + Bug#15577 and Bug#19345). + +Changes in version 1.9.6: + + Changed ldd to not use the glibc dynamic linker when run + on a libc5-based shared library. + + Added a -q option to ldconfig which causes warnings not + to be printed (Bob Tinsley). + + Dropped support for the Debian libdl1-dev package. + + Changed ld-linux.so to be compilable with gcc 2.8.0 (Sven + Verdoolaege) + +Changes in version 1.9.5: + + Fixed a bug in ldd where ld-linux.so.2 was not called + correctly when run on shared libraries. + + Fixed a problem in the previous version where some + Makefiles were not architecture independent. + +Changes in version 1.9.4: + + Fixed a bug in ld.so introduced in the previous version + which broke preloads. + + Turned a.out support back on by default, at least for the + time being. There are no promises to keep it. + +Changes in version 1.9.3: + + Fixed buffer overflow bugs in ld-linux.so and ld.so. + + Changed the README file a little to clarify a couple of + things. + + Changed ldconfig to chroot to the specified directory when + the new -r option is used (Bob Tinsley). + +Changes in version 1.9.2: + + Removed /usr/local/lib from the default /etc/ld.so.conf + for Debian (Bug#8181). + + Changed ldconfig to be 64-bit clean (H.J. Lu). + +Changes in version 1.9.1: + + Changed ldconfig to try to determine which libc a + library is for even if it doesn't have an soname. + + Fixed a bug in ldconfig where an older library using + the glibc naming convention would be used instead of + a newer library. + + Changed to ld-linux.so and libdl.so to not require the + libc5 headers in order to compile. + + Changed ldconfig and ldd to be compilable with either + libc5 or libc6. + +Changes in version 1.9.0: + + Changed to not build the old, a.out dynamic loader by + default. + + Changed instldso.sh to require the --force option to + make sure users read the README file. + + Changed instldso.sh to not install the libdl.so + development files unless the --devfiles option is used. + + Changed instldso.sh to not strip binaries and libraries + if the --no-strip option is used. + + Changed the Debian packaging to put the development files + which conflict with glibc in a new libdl1-dev package. + + Changed ldd to use the glibc dynamic linker, if it is + available, when run on a shared library. + + Changed ld-linux.so to print the load addresses of + libraries, ala glibc, when run by ldd. + + Changed ld-linux.so to allow the libraries listed in + LD_PRELOAD to be separated by white space in addition to + colons. + + Changed ld-linux.so to load the libraries listed in + LD_PRELOAD for setu/gid programs as long as they can be + loaded securely. + + Changed ldconfig to update the symlinks for the dynamic + linkers. + + Changed ldconfig to try to determine if an ELF library is + intended for libc5 or libc6 and save the infomation in the + cache. The mechanism used is rather simplistic and may + need to be enhanced. + + Changed ldconfig to print the type of ELF library when + printing the cache. + + Changed ld-linux.so to only load ELF shared libraries for + use with libc5 or an unknown libc. + +Changes in version 1.8.10: + + Fixed a bug in ldconfig where a symlink could be used + instead of a regular file. + + Fixed a Debian packaging problem for the sparc + architecture. + +Changes in version 1.8.9: + + Changed ldconfig to only cache the symlinks it creates. + This make the behavior of the dynamic linkers consistent + with how they would behave if a cache was not used. + + Changed ldconfig to cache the symlinks that it finds but + use the name of the symlink as the soname instead of the + actual soname. + +Changes in version 1.8.8: + + Minor documentation updates to reflect recent changes. + + Changed ld.so and ld-linux.so to perform more complete + validation on ld.so.cache before using it. + + Changed ldconfig to accept libraries with inconsistent + sonames since glibc is going to use them. A warning is + still printed in debug mode. + + Changed the install script to not strip _dl_debug_state + from ld-linux.so since gdb needs it. + + More sparc fixes (Derrick Brashear). + + Changed ldconfig to not issue a warning when a linker + script disguised as a shared library is found. + + Fixed a bug in ld-linux.so where some registers were + not preserved on the first call to a function causing + problems for non-C-like languages (Tim Renouf). + + Fixed a bug in ld-linux.so where global variables were + not always mapped correctly across dynamically loaded + libraries (Mikihiko Nakao). + + Converted to new Debian source packaging format (Shaya + Potter). + +Changes in version 1.8.6/7: + + Never released as some unofficial patches used these + version numbers. + +Changes in version 1.8.5: + + Fixed a bug in ld.so introduced in the previous changes. + +Changes in version 1.8.4: + + Changed ldconfig to completely ignore symbolic links. + + Changed ldconfig to issue the warning concerning an + inconsistent soname in non-verbose mode. + + Changed ld-linux.so back to not keep ld.so.cache mapped + at all times. + + Changed Debian packaging to compress man pages, strip all + binaries (Bug#5125) and include a shlibs file. + +Changes in version 1.8.3: + + Changed ld-linux.so to process LD_PRELOAD before + /etc/ld.so.preload. + + Fixed a Debian packaging problem where libdl might not + be available if other packages were upgraded at the same + time (Debian Bug#4728). + + Changed ldd to always exit with status 1 if any errors + occur (Debian Bug#4188). + + Fixed some minor problems in instldso.sh (Mike Castle and + Wolfgang Franke). + + Changed ldconfig to issue a warning in verbose mode when + skipping a library because the soname doesn't match. + + More sparc fixes (Miguel de Icaza). + + Don't link with -N when building ld.so (Alan Modra). + + Changed ld-linux.so to better support position-dependant + libraries (NIIBE Yutaka). + +Changes in version 1.8.2: + + Added a texinfo file for ld.so and libdl (Michael + Deutschmann). + + Minor sparc and installation changes (Elliot Lee). + + Added multiple architecture support for Debian (Leland + Lucius). + + Changed libdl to better support RTLD_NEXT (Eric + Youngdale). Note: the exact meaning of ETLD_NEXT is + still not clear in all cases. + + Removed some libc dependencies from libdl. Still need + to remove malloc and free. + +Changes in version 1.8.1: + + Changed ld.so to be compiled as ELF. This also means + that ELF support is now required. A.out support is + still optional. + + Changed ld-linux.so and libdl.so to use the rpath in the + executable instead of in the invoking shared library. + + More m68k fixes (Andreas Schwab). + + Various sparc fixes (Miguel de Icaza). + + Changed ldcnnfig to ignore libraries ending in '~'. + + Changed ldconfig to allow alternative conf and cache + files to be specified on the command-line. + + Changed libdl.so to work when dlsym is passed a NULL + handle pointer. + +Changes in version 1.8.0: + + Changed ld-linux.so to be more liberal when checking to + see if a library is already loaded. This should avoid + the duplicate loading problem for programs linkeed with + the -rpath option. + + Various m68k fixes (Andreas Schwab). + + Changed ld.so to only use LD_AOUT_LIBRARY_PATH and + LD_AOUT_PRELOAD and ld-linux.so to only use + LD_LIBRARY_PATH and LD_PRELOAD. LD_ELF_LIBRARY_PATH + and LD_ELF_PRELOAD are no longer supported. + + Changed ld-linux.so to allow debugging of shared and + dynamically loaded libraries (H.J. Lu, Andreas Schwab). + + Changed ld-linux.so to preload ELF shared libraries + listed in /etc/ld.so.preload. This allows secure + preloads, even for setuid/setgid programs. + + Changed ld-linux.so to keep ld.so.cache mapped at all + times. + + Changed ldconfig to allow #-style comments in ld.so.conf. + + Removed various compiler warnings (Richard Sladkey and + David Engel). + + Changed ldd to work on ELF shared libraries. This may + need a little more work. + +Changes in version 1.7.14: + + Changed ldconfig to recognize ELF shared libraries + generated by post-2.6 versions of ld (Andreas Schwab). + + Changed ldconfig to not remove stale links that do not + have a version number since they may be needed by ld. + +Changes in version 1.7.13: + + Fixed a problem in ld-linux.so where a program linked + with a shared library that was not used could result in + a segmentation fault (H.J. Lu). + +Changes in version 1.7.12: + + Fixed a problem in libdl.so where the wrong library + could be marked as global when RTLD_GLOBAL was used + (Lars Heete). + + Installed dlfcn.h with libdl.so instead of requiring + it to be supplied with libc. + + Removed support for libldso.a since it was nearly + impossible to use anyway. + + Changed ldd to detect when the program being checked + exited abnormally. + +Changes in version 1.7.11: + + Changed ld.so and ld-linux.so to delete all variations + of LD_PRELOAD and LD_LIBRARY_PATH for set[ug]id programs, + This makes it harder for broken set[ug]id programs to be + compromised. + + Fixed a problem in libdl.so where dlsym would not accept + the handle returned from dlopen(0, *). + +Changes in version 1.7.10: + + Changed ld-linux.so and libdl.so to support RTLD_GLOBAL + (Eric Youngdale). + +Changes in version 1.7.9: + + Fixed a problem in ld-linux.so in detecting when the + new user/group information is provided by the kernel. + + Fixed a problem in ld-linux.so where a buffer could be + overflowed if a large number of libraries were loaded + (Thomas Moore). + +Changes in version 1.7.8: + + Changed the Makefiles and install scripts to support + a.out- and ELF-only configurations. + + Changed ld-linux.so to use the user/group information + provided by linux 1.3.23+ instead of making syscalls + to get it. + + Changed libdl.so to support RTLD_NEXT (Glenn Fowler). + + Changed libdl.so to only execute the fini sections + instead of completely closing libraries at exit (Glenn + Fowler). + + Changed ld.so and ld-linux.so to print the required + cache version when a mismatch is detected. + + Changed ld-linux.so to not require on /dev/zero (Ralph + Loader). + + Minor m68k cleanups (Andreas Schwab). + +Changes in version 1.7.7: + + Fixed problems compiling with recent 1.3.x kernels. + + Changed ld-linux.so to not use MAP_DENYWRITE until the + permission issue regarding it is resolved. + +Changes in version 1.7.6: + + Fixed a bug in ld-linux.so dealing with a zero-length + LD_{ELF_}PRELOAD. + + Changed ld.so and ld-linux.so to truncate all variations + of LD_PRELOAD and LD_LIBRARY_PATH for set[ug]id programs. + +Changes in version 1.7.5: + + Changed ldconfig to recognize libraries without any + version number (eg. libXYZ.so). + + Changed ldconfig to not generate a corrupt cache when + the disk is full or other write errors occur. + + Changed ld-linux.so to map files with MAP_DENYWRITE to + keep them from being changed while the file is in use + (Rick Sladkey). + + Changed libdl to not overwrite the scope pointer of a + library if it was already loaded (H.J. Lu). + + Changed ld-linux.so so gdb can be used on constructors + (Eric Youngdale). + + Changed ldconfig to ignore ELF libraries where the soname + does not match the file name on the assumption that it is + a used at compile-time (eg. libcurses.so -> libncruses.so). + +Changes in version 1.7.4: + + Changed ld-linux.so and libdl to use the appropriate + rpaths when searching for shared libraries (Eric + Youngdale). + + Changed ld-linux.so to search rpath before using the + cache. This more closely conforms to the IBCS standard. + +Changes in version 1.7.3: + + Changed ld-linux.so to only print a library name the + first time it is loaded when run from ldd. + + Fixed a bug in ldconfig where an invalid cache could be + generated if a directory was specified multiple times in + ld.so.conf. + + Changed ld-linux.so so it will return the address of a + weak symbol when called from dlsym in libdl (Eric + Youngdale. + +Changes in version 1.7.2: + + Changed libdl.so again to fix the undefined foobar + problem. + +Changes in version 1.7.1: + + Changed libdl so it will compile at optimization level + O3 or higher. + + Changed ldconfig to always create the cache file with + mode 644. + + Changed ldconfig to not ingore valid symlinks. + + Changed ldconfig to use the library name as the soname + for ELF libraries that do not have an soname entry. + + Changed ld-linux.so to print the actual, requested library + name at the time it is loaded instead of trying to figure + it out after the fact. + +Changes in version 1.7.0: + + Changed ldconfig to read the actual soname from the image + for ELF libraries and make it available to ld-linux.so. + The soname for DLL libraries is still determined by + truncating the minor numbers from the image file name. + + Changed ldconfig to no longer support the undocumented + sort options. + + Changed ld.so to require a valid cache to find libraries + in directories specified in ld.so.conf. /usr/lib and /lib + are still searched as a last resort. Ld-linux.so already + operated this way. + + Fixed a bug in libldso.a where the arguments to + shared_loader were not parsed correctly (Wolfram Gloger). + + Added support for RELA-style relocations under Linux/68k + (Andreas Schwab). + + Changed ld-linux.so to only map the cache once for all + libraries instead of individually for each library. + + Changed ld-linux.so continue searching the cache instead of + giving up when failing to load the first entry found. + + Changed ld-linux.so to produce output similar to ld.so when + run from ldd or when errors occur. + +Changes in version 1.6.7: + + Changed the install scripts to make sure that ld.so and + ld-linux.so are always usable. + + Added support for Linux/Sparc (Eric Youngdale). + + Added support for Linux/68k (Andreas Schwab). + + Fixed various bugs in ld-linux.so dealing with closing + files, unmapping memory, dereferencing NULL pointers and + printing library names (David Engel, Eric Youngdale and + Andreas Schwab). + + Replaced the manual page for libdl with a freely + distributable one (Adam Richter). + + Fixed a bug in ld-linux.so where LD_LIBRARY_PATH and + LD_PRELOAD were not cleared for setuid/setgid programs. + + Fixed a bug in libdl where dlsym would not return the + correct address of a symbol if it was redefined in another + library (Oleg Kibirev). + + Changed ld-linux.so to use the following order to search + for libraries: LD_{ELF_}LIBRARY_PATH, ld.so.cache, rpath, + /usr/lib and /lib. + + Changed ld-linux.so to not needlessly allocate memory when + using ld.so.cache. + +Changes in version 1.6.6: + + Changed ldconfig to not warn about removing stale links + unless the -v option is specified. + + Added manual pages for libdl (from FreeBSD/Sun) + + Fixed a bug in ld.so dealing with preloading of objects + generated by recent versions of ld (Mitch D'Souza). + + Fixed bugs in ldd where some errors were either not + detected or not printed. + + Fixed a bug in ld-linux.so where the trailing nul in a + library name was not being copied (Owen Taylor). + +Changes in version 1.6.5: + + Changed ldconfig to remove stale symbolic links. + + Added debug hooks in ld-linux.so and libdl.so to be used + by a future version of gdb (Eric Youngdale). + +Changes in version 1.6.4: + + Change ld-linux.so to print on stdout instead of stderr + when run from ldd. + + Added support for Debian GNU/Linux packaging. + +Changes in version 1.6.3: + + Fixed a bug in libdl when closing a library (H.J. Lu). + +Changes in version 1.6.2: + + Changed the error message printed by ldd when a file is + not a.out or ELF. It used to only list a.out formats. + + Changed ldconfig to no longer cache and set up links for + ld-linux.so. + + Changed ld-linux.so and libdl to not conflict with upcoming + changes in kernel header files. + + Changed ld-linux.so to not print preloaded libraries. + +Changes in version 1.6.1: + + Updated the installation script. + + Changed ld.so and ld-linux.so to look for LD_AOUT_PRELOAD + and LD_ELF_PRELOAD, respectively, before LD_PRELOAD. + + Changed ld.so and ld-linux.so to use LD_AOUT_LIBRARY_PATH + and LD_ELF_LIBRARY_PATH, respectively, instead of + AOUT_LD_LIBRARY_PATH and ELF_LD_LIBRARY_PATH. + +Changes in version 1.6.0: + + Changed ldconfig to process libraries which do not have + a minor version or patch level number. + + Incorporated ld-linux.so and libdl.so. + + Changed ld.so and ld-linux.so to not miss entries in the + cache when the fully qualified library is requested. + + Changed ldconfig to use stdout instead of stderr when + printing the cache. + +Changes in version 1.5.3: + + LD_PRELOAD enhancements (Tristan Gigold). + + LD_PRELOAD patch for linux-68k (Andreas Schwab). + +Changes in version 1.5.2: + + More ELF changes (Mitch D'Souza). + + Changed ldconfig to also update the link for ld-linux.so. + +Changes in version 1.5.1: + + More ELF and LD_PRELOAD changes (Mitch D'Souza). + +Changes in version 1.5.0: + + Chnaged all executables to QMAGIC (Mitch D'Souza and Rick + Sladkey). + + Added preliminary support for ELF to ldd and ldconfig (Eric + Youndale and H.J. Lu). + + Added support for LD_PRELOAD to ld.so (Mitch D'Souza). + + Removed the "advertising" clause from the copyright notices + in all source files. + +Changes in version 1.4.4: + + Changed ldconfig to support QMAGIC libraries. + + Fixed a bug in ld.so where some of the error messages had + transposed arguments. + +Changes in version 1.4.3: + + Fixed an obscure bug in ld.so where an index was not being + incremented when a library was not found using the cache. + +Changes in version 1.4.2: + + Changed ldconfig to issue a warning and continue instead + of an error and exiting when a link can't be updated. + This is useful when some libraries are imported on read- + only file systems, such as an NFS mounted /usr. + + Changed ld.so to be more robust in searching for libraries. + A library is not considered found unless it can actually be + loaded. If a library is not found using the cache, the + standard directories are searched as in pre-cache versions. + +Changes in version 1.4.1: + + Fixed minor Makefile problems. + + Added support for linux-68k. + + Fixed a bug in ld.so where libraries with absolute paths + were not handled correctly. + + Changed ld.so to ignore the directory in the names of + shared libraries by default. This allows older libraries + with absolute paths, such as the XView libraries, to take + advantage of the cache support. + + Added a minimal usage message to ldconfig. + +Changes in version 1.4: + + Fixed bug in ld.so where minor version numbers were not + reported correctly when a minor version incompatibility + was found. + + Fixed bug in ldconfig where libraries with subversion + numbers greater than 9 were not compared correctly. + + Added Mitch D'Souza's support for suppressing warning + messages from ld.so about minor version incompatibilities. + + Added Mitch D'Souza's support for using a cache to speed + up searching for libraries in the standard directories. + + Added Mitch D'Souza's support for a debugging version of + ld.so. Link with -lldso if you think you are experiencing + dynamic linker problems. + +Changes in version 1.3: + + Added support for libraries using absolute pathnames. If I + had known that the XView libraries used them, I would have + added this earlier. + + Fixed a bug handling old libraries using a pathname beginning + with '/' or '/lib/'. + +Changes in version 1.2a: + + Fixed a minor bug in ldd which caused all files, specifically + scripts, to be recognized as binaries. Thanks to Olaf Flebbe + for reporting it. + +David Engel +david@sw.ods.com diff --git a/ldso/config.h b/ldso/config.h new file mode 100644 index 000000000..1d1429dc1 --- /dev/null +++ b/ldso/config.h @@ -0,0 +1,59 @@ +#ifdef DEBUG +# define LDSO_IMAGE "../ld-so/ld.so" +# define LDSO_CONF "../util/ld.so.conf" +# define LDSO_CACHE "../util/ld.so.cache" +# define LDSO_PRELOAD "../util/ld.so.preload" +# define LDDSTUB "../util/lddstub" +#elif UCLIBC_DEVEL +# define LDSO_IMAGE UCLIBC_INSTALL_DIR"/lib/ld.so" +# define LDSO_CONF UCLIBC_INSTALL_DIR"/etc/ld.so.conf" +# define LDSO_CACHE UCLIBC_INSTALL_DIR"/etc/ld.so.cache" +# define LDSO_PRELOAD UCLIBC_INSTALL_DIR"/etc/ld.so.preload" +# define LDDSTUB UCLIBC_INSTALL_DIR"/lib/lddstub" +#else +# define LDSO_IMAGE "/lib/ld.so" +# define LDSO_CONF "/etc/ld.so.conf" +# define LDSO_CACHE "/etc/ld.so.cache" +# define LDSO_PRELOAD "/etc/ld.so.preload" +# define LDDSTUB "/usr/lib/lddstub" +#endif + +#define LDD_ARGV0 "__LDD_ARGV0" +#define DIR_SEP ":, \t\n" +#define MAX_DIRS 32 + +typedef void (*loadptr)(int func, ...); +typedef void (*callbackptr)(int ver, int nlibs, char **libs, + int nmods, char **mods); + +#define CALLBACK_VER 1 + +#define LIB_ANY -1 +#define LIB_DLL 0 +#define LIB_ELF 1 +#define LIB_ELF_LIBC5 2 +#define LIB_ELF_LIBC6 3 +#define LIB_ELF64 0x80 + +#define FUNC_VERS 0 +#define FUNC_LDD 1 +#define FUNC_LINK 2 +#define FUNC_LINK_AND_CALLBACK 3 + +#define LDSO_CACHE_MAGIC "ld.so-" +#define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1) +#define LDSO_CACHE_VER "1.7.0" +#define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1) + +typedef struct { + char magic [LDSO_CACHE_MAGIC_LEN]; + char version [LDSO_CACHE_VER_LEN]; + int nlibs; +} header_t; + +typedef struct { + int flags; + int sooffset; + int liboffset; +} libentry_t; + diff --git a/ldso/ldso/Makefile b/ldso/ldso/Makefile new file mode 100644 index 000000000..80f0643ea --- /dev/null +++ b/ldso/ldso/Makefile @@ -0,0 +1,39 @@ +TOPDIR=../../ +include $(TOPDIR)Rules.mak +include $(TOPDIR)/ld.so-1/Config.mk + +DIRS = $(TARGET_ARCH) libdl + +CFLAGS += -DNO_UNDERSCORE -DVERBOSE_DLINKER +CFLAGS += -DUSE_CACHE #-fPIC -D__PIC__ #-funroll-loops + +CSRC= boot1.c hash.c readelflib1.c vsprintf.c +COBJS=$(patsubst %.c,%.o, $(CSRC)) +OBJS=$(COBJS) + +DLINKER = ld-linux-uclibc.so + +ELF_LDFLAGS=--shared # using GNU ld + +ifneq ($(DIRS),) +lib realclean clean:: + @set -e; for i in $(DIRS); do \ + echo making $@ in $$i; \ + $(MAKE) -C $$i $@; \ + done; +endif + +lib:: $(OBJS) + $(LD) -e _dl_boot $(ELF_LDFLAGS) -o $(DLINKER).$(LDSO_VMAJOR) \ + -soname $(DLINKER).$(LDSO_VMAJOR) *.o + +$(COBJS): %.o : %.c + $(CC) -I. -I./$(TARGET_ARCH) $(CFLAGS) -c $< -o $@ + $(STRIPTOOL) -x -R .note -R .comment $*.o + +realclean:: + $(RM) -f .depend $(DLINKER) core *.o *.a *.s *.i tmp_make foo *~ + +clean:: + $(RM) -f $(DLINKER) core *.o *.a *.s *.i tmp_make foo *~ + diff --git a/ldso/ldso/boot1.c b/ldso/ldso/boot1.c new file mode 100644 index 000000000..6b0b864cb --- /dev/null +++ b/ldso/ldso/boot1.c @@ -0,0 +1,1005 @@ +/* Run an ELF binary on a linux system. + + Copyright (C) 1993-1996, Eric Youngdale. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +/* Program to load an ELF binary on a linux system, and run it. + * References to symbols in sharable libraries can be resolved by + * an ELF sharable library. */ + +/* Disclaimer: I have never seen any AT&T source code for SVr4, nor have + I ever taken any courses on internals. This program was developed using + information available through the book "UNIX SYSTEM V RELEASE 4, + Programmers guide: Ansi C and Programming Support Tools", which did + a more than adequate job of explaining everything required to get this + working. */ + +/* + * The main trick with this program is that initially, we ourselves are not + * dynamicly linked. This means that we cannot access any global variables + * since the GOT is initialized by the linker assuming a virtual address of 0, + * and we cannot call any functions since the PLT is not initialized at all + * (it will tend to want to call the dynamic linker + * + * There are further restrictions - we cannot use large switch statements, + * since the compiler generates tables of addresses and jumps through them. + * We can use inline functions, because these do not transfer control to + * a new address, but they must be static so that they are not exported + * from the modules. We cannot use normal syscall stubs, because these + * all reference the errno global variable which is not yet initialized. + * We can use all of the local stack variables that we want, since these + * are all referenced to %ebp or %esp. + * + * Life is further complicated by the fact that initially we do not want + * to do a complete dynamic linking. We want to allow the user to supply + * new functions replacing some of the library versions, and until we have + * the list of modules that we should search set up, we do not want to do + * any of this. Thus I have chosen to only perform the relocations for + * variables that start with "_dl_" since ANSI specifies that the user is + * not supposed to redefine any of these variables. + * + * Fortunately, the linker itself leaves a few clues lying around, and + * when the kernel starts the image, there are a few further clues. + * First of all, there is information buried on the stack that the kernel + * leaves, which includes information about the load address that the + * program interpreter was loaded at, the number of sections, the address + * the application was loaded at and so forth. Here this information + * is stored in the array dl_info, and the indicies are taken from the + * file /usr/include/sys/auxv.h on any SVr4 system. + * + * The linker itself leaves a pointer to the .dynamic section in the first + * slot of the GOT, and as it turns out, %ebx points to ghe GOT when + * you are using PIC code, so we just dereference this to get the address + * of the dynamic sections. + * + * Typically you must load all text pages as writable so that dynamic linking + * can succeed. The kernel under SVr4 loads these as R/O, so we must call + * mprotect to change the protections. Once we are done, we should set these + * back again, so the desired behavior is achieved. Under linux there is + * currently no mprotect function in the distribution kernel (although + * someone has alpha patches), so for now everything is loaded writable. + * + * We do not have access to malloc and friends at the initial stages of dynamic + * linking, and it would be handy to have some scratchpad memory available + * for use as we set things up. It is a bit of a kluge, but we mmap /dev/zero + * to get one page of scratchpad. A simpleminded _dl_malloc is provided so + * that we have some memory that can be used for this purpose. Typically + * we would not want to use the same memory pool as malloc anyway - the user + * might want to redefine malloc for example. + * + * Our first task is to perform a minimal linking so that we can call other + * portions of the dynamic linker. Once we have done this, we then build + * the list of modules that the application requires, using LD_LIBRARY_PATH + * if this is not a suid program (/usr/lib otherwise). Once this is done, + * we can do the dynamic linking as required (and we must omit the things + * we did to get the dynamic linker up and running in the first place. + * After we have done this, we just have a few housekeeping chores and we + * can transfer control to the user's application. + */ + +#include <stdarg.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/unistd.h> +#include <linux/elf.h> +#include <linux/mman.h> +#include "link.h" + +#include "sysdep.h" +#include "hash.h" +#include "linuxelf.h" +#include "syscall.h" +#include "string.h" + +#include "../config.h" + +#define ALLOW_ZERO_PLTGOT + +static char * _dl_malloc_addr, *_dl_mmap_zero; +char * _dl_library_path = 0; /* Where we look for libraries */ +char *_dl_preload = 0; /* Things to be loaded before the libs. */ +char *_dl_progname = "/lib/ld-linux-uclibc.so.1"; +static char * _dl_not_lazy = 0; +static char * _dl_warn = 0; /* Used by ldd */ +static char * _dl_trace_loaded_objects = 0; +static int (*_dl_elf_main)(int, char **, char**); + +static int (*_dl_elf_init)(void); + +void * (*_dl_malloc_function)(int size) = NULL; + +struct r_debug * _dl_debug_addr = NULL; + +unsigned int * _dl_brkp; + +unsigned int * _dl_envp; + +#define DL_MALLOC(SIZE) ((void *) (malloc_buffer += SIZE, malloc_buffer - SIZE)) +/* + * Make sure that the malloc buffer is aligned on 4 byte boundary. For 64 bit + * platforms we may need to increase this to 8, but this is good enough for + * now. This is typically called after DL_MALLOC. + */ +#define REALIGN() malloc_buffer = (char *) (((unsigned int) malloc_buffer + 3) & ~(3)) + + + +#define ELF_HASH(RESULT,NAME) { \ + unsigned long hash = 0; \ + unsigned long tmp; \ + char * name = NAME; \ + while (*name){ \ + hash = (hash << 4) + *name++; \ + if((tmp = hash & 0xf0000000)) hash ^= tmp >> 24; \ + hash &= ~tmp; \ + } \ + RESULT = hash; \ +} +extern int _dl_linux_resolve(void); +extern int _dl_interpreter_exit(int); +extern char * _dl_strdup(const char *); +extern char * _dl_getenv(char * symbol, char ** envp); +extern void _dl_unsetenv(char * symbol, char ** envp); +extern int _dl_fixup(struct elf_resolve * tpnt); + +/* + * Datatype of a relocation on this platform + */ +#ifdef ELF_USES_RELOCA +typedef struct elf32_rela ELF_RELOC; +#else +typedef struct elf32_rel ELF_RELOC; +#endif + +/* + * This stub function is used by some debuggers. The idea is that they + * can set an internal breakpoint on it, so that we are notified when the + * address mapping is changed in some way. + */ +void _dl_debug_state() +{ + return; +} + +void _dl_boot(int args); + +void _dl_boot(int args){ + unsigned int argc; + char ** argv, ** envp; + int status; + + unsigned int load_addr; + unsigned int * got; + unsigned int * aux_dat; + int goof = 0; + struct elfhdr * header; + struct elf_resolve * tpnt; + struct dyn_elf * rpnt; + struct elf_resolve * app_tpnt; + unsigned int brk_addr; + unsigned int dl_data[AT_EGID+1]; + unsigned char * malloc_buffer, *mmap_zero; + int (*_dl_atexit)(void *); + int * lpnt; + struct dynamic * dpnt; + unsigned int *hash_addr; + struct r_debug * debug_addr; + unsigned int *chains; + int indx; + int _dl_secure; + + /* First obtain the information on the stack that tells us more about + what binary is loaded, where it is loaded, etc, etc */ + + GET_ARGV(aux_dat, args); + argc = *(aux_dat - 1); + argv = (char **) aux_dat; + aux_dat += argc; /* Skip over the argv pointers */ + aux_dat++; /* Skip over NULL at end of argv */ + envp = (char **) aux_dat; + while(*aux_dat) aux_dat++; /* Skip over the envp pointers */ + aux_dat++; /* Skip over NULL at end of envp */ + dl_data[AT_UID] = -1; /* check later to see if it is changed */ + while(*aux_dat) + { + unsigned int * ad1; + ad1 = aux_dat + 1; + if( *aux_dat <= AT_EGID ) dl_data[*aux_dat] = *ad1; + aux_dat += 2; + } + + /* Next, locate the GOT */ + + load_addr = dl_data[AT_BASE]; + + GET_GOT(got); + dpnt = (struct dynamic *) (*got + load_addr); + + /* OK, time for another hack. Now call mmap to get a page of writable + memory that can be used for a temporary malloc. We do not know brk + yet, so we cannot use real malloc. */ + + { + /* This hack is to work around a suspected asm bug in gcc-2.7.0 */ + int zfileno; +#define ZFILENO ((-1 & (~zfileno)) | zfileno) +/*#define ZFILENO -1*/ + +#ifndef MAP_ANONYMOUS +#ifdef __sparc__ +#define MAP_ANONYMOUS 0x20 +#else +#error MAP_ANONYMOUS not defined and suplementary value not known +#endif +#endif + + /* See if we need to relocate this address */ + mmap_zero = malloc_buffer = (unsigned char *) _dl_mmap((void*) 0, 4096, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, ZFILENO, 0); + if(_dl_mmap_check_error(mmap_zero)) { + SEND_STDERR("dl_boot: mmap of /dev/zero failed!\n"); + _dl_exit(13); + } + } + + tpnt = DL_MALLOC(sizeof(struct elf_resolve)); + REALIGN(); + _dl_memset (tpnt, 0, sizeof (*tpnt)); + app_tpnt = DL_MALLOC(sizeof(struct elf_resolve)); + REALIGN(); + _dl_memset (app_tpnt, 0, sizeof (*app_tpnt)); + + /* + * This is used by gdb to locate the chain of shared libraries that are currently loaded. + */ + debug_addr = DL_MALLOC(sizeof(struct r_debug)); + REALIGN(); + _dl_memset (debug_addr, 0, sizeof (*debug_addr)); + + /* OK, that was easy. Next scan the DYNAMIC section of the image. + We are only doing ourself right now - we will have to do the rest later */ + + while(dpnt->d_tag) + { + tpnt->dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val; + if(dpnt->d_tag == DT_TEXTREL || + SVR4_BUGCOMPAT) tpnt->dynamic_info[DT_TEXTREL] = 1; + dpnt++; + } + + { + struct elf_phdr * ppnt; + int i; + + ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + for(i=0; i<dl_data[AT_PHNUM]; i++, ppnt++) + if(ppnt->p_type == PT_DYNAMIC) { + dpnt = (struct dynamic *) ppnt->p_vaddr; + while(dpnt->d_tag) + { + if(dpnt->d_tag > DT_JMPREL) {dpnt++; continue; } + app_tpnt->dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val; + if(dpnt->d_tag == DT_DEBUG) dpnt->d_un.d_val = (int) debug_addr; + if(dpnt->d_tag == DT_TEXTREL || + SVR4_BUGCOMPAT) app_tpnt->dynamic_info[DT_TEXTREL] = 1; + dpnt++; + } + } + } + + /* Get some more of the information that we will need to dynamicly link + this module to itself */ + + hash_addr = (unsigned int *) (tpnt->dynamic_info[DT_HASH]+load_addr); + tpnt->nbucket = *hash_addr++; + tpnt->nchain = *hash_addr++; + tpnt->elf_buckets = hash_addr; + hash_addr += tpnt->nbucket; + chains = hash_addr; + + /* Ugly, ugly. We need to call mprotect to change the protection of + the text pages so that we can do the dynamic linking. We can set the + protection back again once we are done */ + + { + struct elf_phdr * ppnt; + int i; + + /* First cover the shared library/dynamic linker. */ + if(tpnt->dynamic_info[DT_TEXTREL]) { + header = (struct elfhdr *) dl_data[AT_BASE]; + ppnt = (struct elf_phdr *) (dl_data[AT_BASE] + header->e_phoff); + for(i=0; i<header->e_phnum ; i++, ppnt++) { + if(ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W)) + _dl_mprotect((void *) (load_addr + (ppnt->p_vaddr & 0xfffff000)), + (ppnt->p_vaddr & 0xfff) + (unsigned int) ppnt->p_filesz, + PROT_READ | PROT_WRITE | PROT_EXEC); + } + } + + /* Now cover the application program. */ + if(app_tpnt->dynamic_info[DT_TEXTREL]) { + ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + for(i=0; i<dl_data[AT_PHNUM]; i++, ppnt++) { + if(ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W)) + _dl_mprotect((void *) (ppnt->p_vaddr & 0xfffff000), + (ppnt->p_vaddr & 0xfff) + (unsigned int) ppnt->p_filesz, + PROT_READ | PROT_WRITE | PROT_EXEC); + } + } + } + + /* OK, now do the relocations. We do not do a lazy binding here, so + that once we are done, we have considerably more flexibility. */ + + goof = 0; + for(indx=0; indx < 2; indx++) + { + int i; + ELF_RELOC * rpnt; + unsigned int * reloc_addr; + unsigned int symbol_addr; + int symtab_index; + unsigned int rel_addr, rel_size; + + +#ifdef ELF_USES_RELOCA + rel_addr = (indx ? tpnt->dynamic_info[DT_JMPREL] : tpnt->dynamic_info[DT_RELA]); + rel_size = (indx ? tpnt->dynamic_info[DT_PLTRELSZ] : tpnt->dynamic_info[DT_RELASZ]); +#else + rel_addr = (indx ? tpnt->dynamic_info[DT_JMPREL] : tpnt->dynamic_info[DT_REL]); + rel_size = (indx ? tpnt->dynamic_info[DT_PLTRELSZ] : tpnt->dynamic_info[DT_RELSZ]); +#endif + + + if(!rel_addr) continue; + + /* Now parse the relocation information */ + rpnt = (ELF_RELOC *) (rel_addr + load_addr); + for(i=0; i< rel_size; i+=sizeof(ELF_RELOC), rpnt++){ + reloc_addr = (int *) (load_addr + (int)rpnt->r_offset); + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + if(symtab_index) { + char * strtab; + struct elf32_sym * symtab; + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB]+load_addr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]+load_addr); + + /* We only do a partial dynamic linking right now. The user + is not supposed to redefine any symbols that start with + a '_', so we can do this with confidence. */ + + if (!_dl_symbol(strtab + symtab[symtab_index].st_name)) continue; + + symbol_addr = load_addr + symtab[symtab_index].st_value; + + if(!symbol_addr) { + /* + * This will segfault - you cannot call a function until + * we have finished the relocations. + */ + SEND_STDERR("ELF dynamic loader - unable to self-bootstrap - symbol "); + SEND_STDERR(strtab + symtab[symtab_index].st_name); + SEND_STDERR(" undefined.\n"); + goof++; + } + } + /* + * Use this machine-specific macro to perform the actual relocation. + */ + PERFORM_BOOTSTRAP_RELOC(rpnt, reloc_addr, symbol_addr, load_addr); + } + } + + if (goof) _dl_exit(14); + + /* OK, at this point we have a crude malloc capability. Start to build + the tables of the modules that are required for this beast to run. + We start with the basic executable, and then go from there. Eventually + we will run across ourself, and we will need to properly deal with that + as well. */ + + _dl_malloc_addr = malloc_buffer; + + _dl_mmap_zero = mmap_zero; +/* tpnt = _dl_malloc(sizeof(struct elf_resolve)); */ + +/* Now we have done the mandatory linking of some things. We are now + free to start using global variables, since these things have all been + fixed up by now. Still no function calls outside of this library , + since the dynamic resolver is not yet ready. */ + + lpnt = (int *) (tpnt->dynamic_info[DT_PLTGOT] + load_addr); + INIT_GOT(lpnt, tpnt); + + /* OK, this was a big step, now we need to scan all of the user images + and load them properly. */ + + tpnt->next = 0; + tpnt->libname = 0; + tpnt->libtype = program_interpreter; + + { struct elfhdr * epnt; + struct elf_phdr * ppnt; + int i; + + epnt = (struct elfhdr *) dl_data[AT_BASE]; + tpnt->n_phent = epnt->e_phnum; + tpnt->ppnt = ppnt = (struct elf_phdr *) (load_addr + epnt->e_phoff); + for(i=0;i < epnt->e_phnum; i++, ppnt++){ + if(ppnt->p_type == PT_DYNAMIC) { + tpnt->dynamic_addr = ppnt->p_vaddr + load_addr; + tpnt->dynamic_size = ppnt->p_filesz; + } + } + } + + tpnt->chains = chains; + tpnt->loadaddr = (char *) load_addr; + + brk_addr = 0; + rpnt = NULL; + + /* At this point we are now free to examine the user application, + and figure out which libraries are supposed to be called. Until + we have this list, we will not be completely ready for dynamic linking */ + + { + struct elf_phdr * ppnt; + int i; + + ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + for(i=0; i<dl_data[AT_PHNUM]; i++, ppnt++) { + if(ppnt->p_type == PT_LOAD) { + if(ppnt->p_vaddr + ppnt->p_memsz > brk_addr) + brk_addr = ppnt->p_vaddr + ppnt->p_memsz; + } + if(ppnt->p_type == PT_DYNAMIC) { +#ifndef ALLOW_ZERO_PLTGOT + /* make sure it's really there. */ + if (app_tpnt->dynamic_info[DT_PLTGOT] == 0) continue; +#endif + /* OK, we have what we need - slip this one into the list. */ + app_tpnt = _dl_add_elf_hash_table("", 0, + app_tpnt->dynamic_info, ppnt->p_vaddr, ppnt->p_filesz); + _dl_loaded_modules->libtype = elf_executable; + _dl_loaded_modules->ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + _dl_loaded_modules->n_phent = dl_data[AT_PHNUM]; + _dl_symbol_tables = rpnt = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt, 0, sizeof (*rpnt)); + rpnt->dyn = _dl_loaded_modules; + app_tpnt->usage_count++; + app_tpnt->symbol_scope = _dl_symbol_tables; + lpnt = (int *) (app_tpnt->dynamic_info[DT_PLTGOT]); +#ifdef ALLOW_ZERO_PLTGOT + if (lpnt) +#endif + INIT_GOT(lpnt, _dl_loaded_modules); + } + if(ppnt->p_type == PT_INTERP) { /* OK, fill this in - we did not have + this before */ + tpnt->libname = _dl_strdup((char *) ppnt->p_offset +(dl_data[AT_PHDR] & 0xfffff000)); + } + } + } + + if (argv[0]) + _dl_progname = argv[0]; + + /* Now we need to figure out what kind of options are selected. + Note that for SUID programs we ignore the settings in LD_LIBRARY_PATH */ + { + _dl_not_lazy = _dl_getenv("LD_BIND_NOW",envp); + + if ( (dl_data[AT_UID] == -1 && _dl_suid_ok()) || + (dl_data[AT_UID] != -1 && dl_data[AT_UID] == dl_data[AT_EUID] && + dl_data[AT_GID] == dl_data[AT_EGID])) + { + _dl_secure = 0; + _dl_preload = _dl_getenv("LD_PRELOAD", envp); + _dl_library_path = _dl_getenv("LD_LIBRARY_PATH",envp); + } + else + { + _dl_secure = 1; + _dl_preload = _dl_getenv("LD_PRELOAD", envp); + _dl_unsetenv("LD_AOUT_PRELOAD", envp); + _dl_unsetenv("LD_LIBRARY_PATH", envp); + _dl_unsetenv("LD_AOUT_LIBRARY_PATH", envp); + _dl_library_path = NULL; + } + } + + _dl_trace_loaded_objects = _dl_getenv("LD_TRACE_LOADED_OBJECTS", envp); + + /* OK, we now have the application in the list, and we have some + basic stuff in place. Now search through the list for other shared + libraries that should be loaded, and insert them on the list in the + correct order. */ + +#ifdef USE_CACHE + _dl_map_cache(); +#endif + + { + struct elf_resolve *tcurr; + struct elf_resolve *tpnt1; + char *lpnt; + + if (_dl_preload) { + char c, *str, *str2; + + str = _dl_preload; + while (*str == ':' || *str == ' ' || *str == '\t') + str++; + while (*str) { + str2 = str; + while (*str2 && *str2 != ':' && *str2 != ' ' && *str2 != '\t') + str2++; + c = *str2; + *str2 = '\0'; + if (!_dl_secure || _dl_strchr(str, '/') == NULL) { + tpnt1 = _dl_load_shared_library(_dl_secure, NULL, str); + if (!tpnt1) { + if (_dl_trace_loaded_objects) + _dl_fdprintf(1, "\t%s => not found\n", str); + else { + _dl_fdprintf(2, "%s: can't load library '%s'\n", + _dl_progname, str); + _dl_exit(15); + } + } else { + if (_dl_trace_loaded_objects && !tpnt1->usage_count) { + /* this is a real hack to make ldd not print the + library itself when run on a library. */ + if (_dl_strcmp(_dl_progname, str) != 0) + _dl_fdprintf(1, "\t%s => %s (0x%x)\n", str, tpnt1->libname, + (unsigned)tpnt1->loadaddr); + } + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + tpnt1->usage_count++; + tpnt1->symbol_scope = _dl_symbol_tables; + tpnt1->libtype = elf_lib; + rpnt->dyn = tpnt1; + } + } + *str2 = c; + str = str2; + while (*str == ':' || *str == ' ' || *str == '\t') + str++; + } + } + + { + int fd; + struct kernel_stat st; + char *preload; + + if (!_dl_stat(LDSO_PRELOAD, &st)) { + if ((fd = _dl_open(LDSO_PRELOAD, O_RDONLY)) < 0) { + _dl_fdprintf(2, "%s: can't open file '%s'\n", _dl_progname, + LDSO_PRELOAD); + } else { + preload = (caddr_t)_dl_mmap(0, st.st_size+1, PROT_READ|PROT_WRITE, + MAP_PRIVATE, fd, 0); + _dl_close (fd); + if (preload == (caddr_t)-1) { + _dl_fdprintf(2, "%s: can't map file '%s'\n", _dl_progname, + LDSO_PRELOAD); + } else { + char c, *cp, *cp2; + + /* convert all separators and comments to spaces */ + for (cp = preload; *cp; /*nada*/) { + if (*cp == ':' || *cp == '\t' || *cp == '\n') { + *cp++ = ' '; + } else if (*cp == '#') { + do + *cp++ = ' '; + while (*cp != '\n' && *cp != '\0'); + } else { + cp++; + } + } + + /* find start of first library */ + for (cp = preload; *cp && *cp == ' '; cp++) + /*nada*/; + + while (*cp) { + /* find end of library */ + for (cp2 = cp; *cp && *cp != ' '; cp++) + /*nada*/; + c = *cp; + *cp = '\0'; + + tpnt1 = _dl_load_shared_library(0, NULL, cp2); + if (!tpnt1) { + if (_dl_trace_loaded_objects) + _dl_fdprintf(1, "\t%s => not found\n", cp2); + else { + _dl_fdprintf(2, "%s: can't load library '%s'\n", + _dl_progname, cp2); + _dl_exit(15); + } + } else { + if (_dl_trace_loaded_objects && !tpnt1->usage_count) + _dl_fdprintf(1, "\t%s => %s (0x%x)\n", cp2, tpnt1->libname, + (unsigned)tpnt1->loadaddr); + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + tpnt1->usage_count++; + tpnt1->symbol_scope = _dl_symbol_tables; + tpnt1->libtype = elf_lib; + rpnt->dyn = tpnt1; + } + + /* find start of next library */ + *cp = c; + for (/*nada*/; *cp && *cp == ' '; cp++) + /*nada*/; + } + + _dl_munmap(preload, st.st_size+1); + } + } + } + } + + for (tcurr = _dl_loaded_modules; tcurr; tcurr = tcurr->next) + { + for (dpnt = (struct dynamic *)tcurr->dynamic_addr; dpnt->d_tag; dpnt++) + { + if(dpnt->d_tag == DT_NEEDED) + { + lpnt = tcurr->loadaddr + tcurr->dynamic_info[DT_STRTAB] + + dpnt->d_un.d_val; + if (tpnt && _dl_strcmp(lpnt, tpnt->libname) == 0) + { + struct elf_resolve * ttmp; + ttmp = _dl_loaded_modules; + while (ttmp->next) + ttmp = ttmp->next; + ttmp->next = tpnt; + tpnt->prev = ttmp; + tpnt->next = NULL; + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + rpnt->dyn = tpnt; + tpnt->usage_count++; + tpnt->symbol_scope = _dl_symbol_tables; + tpnt = NULL; + continue; + } + if (!(tpnt1 = _dl_load_shared_library(0, tcurr, lpnt))) + { + if (_dl_trace_loaded_objects) + _dl_fdprintf(1, "\t%s => not found\n", lpnt); + else + { + _dl_fdprintf(2, "%s: can't load library '%s'\n", + _dl_progname, lpnt); + _dl_exit(16); + } + } + else + { + if (_dl_trace_loaded_objects && !tpnt1->usage_count) + _dl_fdprintf(1, "\t%s => %s (0x%x)\n", lpnt, tpnt1->libname, + (unsigned)tpnt1->loadaddr); + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + tpnt1->usage_count++; + tpnt1->symbol_scope = _dl_symbol_tables; + tpnt1->libtype = elf_lib; + rpnt->dyn = tpnt1; + } + } + } + } + } + +#ifdef USE_CACHE + _dl_unmap_cache(); +#endif + + /* ldd uses uses this. I am not sure how you pick up the other flags */ + if(_dl_trace_loaded_objects) + { + _dl_warn = _dl_getenv("LD_WARN", envp); + if (!_dl_warn) _dl_exit(0); + } + + /* + * If the program interpreter is not in the module chain, add it. This will + * be required for dlopen to be able to access the internal functions in the + * dynamic linker. + */ + if(tpnt) { + struct elf_resolve * tcurr; + + tcurr = _dl_loaded_modules; + if (tcurr) + while(tcurr->next) tcurr = tcurr->next; + tpnt->next = NULL; + tpnt->usage_count++; + + if (tcurr) { + tcurr->next = tpnt; + tpnt->prev = tcurr; + } + else { + _dl_loaded_modules = tpnt; + tpnt->prev = NULL; + } + if (rpnt) { + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + } else { + rpnt = (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt, 0, sizeof (*(rpnt->next))); + } + rpnt->dyn = tpnt; + tpnt = NULL; + } + + /* + * OK, now all of the kids are tucked into bed in their proper addresses. + * Now we go through and look for REL and RELA records that indicate fixups + * to the GOT tables. We need to do this in reverse order so that COPY + * directives work correctly */ + + + goof = _dl_loaded_modules ? _dl_fixup(_dl_loaded_modules) : 0; + + + /* Some flavors of SVr4 do not generate the R_*_COPY directive, + and we have to manually search for entries that require fixups. + Solaris gets this one right, from what I understand. */ + + + if (_dl_symbol_tables) + goof += _dl_copy_fixups(_dl_symbol_tables); + + if(goof || _dl_trace_loaded_objects) _dl_exit(0); + + /* OK, at this point things are pretty much ready to run. Now we + need to touch up a few items that are required, and then + we can let the user application have at it. Note that + the dynamic linker itself is not guaranteed to be fully + dynamicly linked if we are using ld.so.1, so we have to look + up each symbol individually. */ + + + _dl_brkp = (unsigned int *) _dl_find_hash("___brk_addr", NULL, 1, NULL, 0); + if (_dl_brkp) *_dl_brkp = brk_addr; + _dl_envp = (unsigned int *) _dl_find_hash("__environ", NULL, 1, NULL, 0); + + if (_dl_envp) *_dl_envp = (unsigned int) envp; + + { + int i; + struct elf_phdr * ppnt; + + /* We had to set the protections of all pages to R/W for dynamic linking. + Set text pages back to R/O */ + for(tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) + for(ppnt = tpnt->ppnt, i=0; i < tpnt->n_phent; i++, ppnt++) + if(ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W) && + tpnt->dynamic_info[DT_TEXTREL]) + _dl_mprotect((void *) (tpnt->loadaddr + (ppnt->p_vaddr & 0xfffff000)), + (ppnt->p_vaddr & 0xfff) + (unsigned int) ppnt->p_filesz, + LXFLAGS(ppnt->p_flags)); + + } + + _dl_atexit = (int (*)(void *)) _dl_find_hash("atexit", NULL, 1, NULL, 0); + + /* + * OK, fix one more thing - set up the debug_addr structure to point + * to our chain. Later we may need to fill in more fields, but this + * should be enough for now. + */ + debug_addr->r_map = (struct link_map *) _dl_loaded_modules; + debug_addr->r_version = 1; + debug_addr->r_ldbase = load_addr; + debug_addr->r_brk = (unsigned long) &_dl_debug_state; + _dl_debug_addr = debug_addr; + debug_addr->r_state = RT_CONSISTENT; + /* This is written in this funny way to keep gcc from inlining the + function call. */ + ((void (*)(void))debug_addr->r_brk)(); + + for(tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) + { + /* Apparently crt1 for the application is responsible for handling this. + * We only need to run the init/fini for shared libraries + */ + if (tpnt->libtype == program_interpreter || + tpnt->libtype == elf_executable) continue; + if (tpnt->init_flag & INIT_FUNCS_CALLED) continue; + tpnt->init_flag |= INIT_FUNCS_CALLED; + + if(tpnt->dynamic_info[DT_INIT]) { + _dl_elf_init = (int (*)(void)) (tpnt->loadaddr + + tpnt->dynamic_info[DT_INIT]); + (*_dl_elf_init)(); + } + if(_dl_atexit && tpnt->dynamic_info[DT_FINI]) + { + (*_dl_atexit)(tpnt->loadaddr + tpnt->dynamic_info[DT_FINI]); + } +#undef DL_DEBUG +#ifdef DL_DEBUG + else + { + _dl_fdprintf(2, tpnt->libname); + _dl_fdprintf(2, ": "); + if (!_dl_atexit) + _dl_fdprintf(2, "The address is atexit () is 0x0."); + if (!tpnt->dynamic_info[DT_FINI]) + _dl_fdprintf(2, "Invalid .fini section."); + _dl_fdprintf(2, "\n"); + } +#endif +#undef DL_DEBUG + } + + /* OK we are done here. Turn out the lights, and lock up. */ + _dl_elf_main = (int (*)(int, char**, char**)) dl_data[AT_ENTRY]; + + + /* + * Transfer control to the application. + */ + START(); +} + +int _dl_fixup(struct elf_resolve * tpnt) +{ + int goof = 0; + if(tpnt->next) goof += _dl_fixup(tpnt->next); + + if(tpnt->dynamic_info[DT_REL]) { +#ifdef ELF_USES_RELOCA + _dl_fdprintf(2, "%s: can't handle REL relocation records\n", _dl_progname); + _dl_exit(17); +#else + if (tpnt->init_flag & RELOCS_DONE) return goof; + tpnt->init_flag |= RELOCS_DONE; + + goof += _dl_parse_relocation_information(tpnt, tpnt->dynamic_info[DT_REL], + tpnt->dynamic_info[DT_RELSZ], 0); +#endif + } + if(tpnt->dynamic_info[DT_RELA]) { +#ifdef ELF_USES_RELOCA + if (tpnt->init_flag & RELOCS_DONE) return goof; + tpnt->init_flag |= RELOCS_DONE; + + goof += _dl_parse_relocation_information(tpnt, tpnt->dynamic_info[DT_RELA], + tpnt->dynamic_info[DT_RELASZ], 0); +#else + _dl_fdprintf(2, "%s: can't handle RELA relocation records\n", _dl_progname); + _dl_exit(18); +#endif + } + if(tpnt->dynamic_info[DT_JMPREL]) + { + if (tpnt->init_flag & JMP_RELOCS_DONE) return goof; + tpnt->init_flag |= JMP_RELOCS_DONE; + + if(! _dl_not_lazy || *_dl_not_lazy == 0) + _dl_parse_lazy_relocation_information(tpnt, tpnt->dynamic_info[DT_JMPREL], + tpnt->dynamic_info[DT_PLTRELSZ], 0); + else + goof += _dl_parse_relocation_information(tpnt, + tpnt->dynamic_info[DT_JMPREL], + tpnt->dynamic_info[DT_PLTRELSZ], 0); + } + return goof; +} + +void * _dl_malloc(int size) { + void * retval; + + if(_dl_malloc_function) + return (*_dl_malloc_function)(size); + + if(_dl_malloc_addr-_dl_mmap_zero+size>4096) { + _dl_mmap_zero = _dl_malloc_addr = (unsigned char *) _dl_mmap((void*) 0, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if(_dl_mmap_check_error(_dl_mmap_zero)) { + _dl_fdprintf(2, "%s: can't map '/dev/zero'\n", _dl_progname); + _dl_exit(20); + } + } + retval = _dl_malloc_addr; + _dl_malloc_addr += size; + + /* + * Align memory to 4 byte boundary. Some platforms require this, others + * simply get better performance. + */ + _dl_malloc_addr = (char *) (((unsigned int) _dl_malloc_addr + 3) & ~(3)); + return retval; +} + +char * _dl_getenv(char *symbol, char **envp) +{ + char *pnt; + char *pnt1; + while ((pnt = *envp++)) { + pnt1 = symbol; + while (*pnt && *pnt == *pnt1) + pnt1++, pnt++; + if (!*pnt || *pnt != '=' || *pnt1) + continue; + return pnt+1; + } + return 0; +} + +void _dl_unsetenv(char *symbol, char **envp) +{ + char *pnt; + char *pnt1; + char **newenvp = envp; + for (pnt = *envp; pnt; pnt = *++envp) { + pnt1 = symbol; + while (*pnt && *pnt == *pnt1) + pnt1++, pnt++; + if(!*pnt || *pnt != '=' || *pnt1) + *newenvp++ = *envp; + } + *newenvp++ = *envp; + return; +} + +char * _dl_strdup(const char * string){ + char * retval; + int len; + + len = _dl_strlen(string); + retval = _dl_malloc(len + 1); + _dl_strcpy(retval, string); + return retval; +} + +/* In principle we could do the .fini stuff here, but we already + registered this stuff with atexit */ +int _dl_interpreter_exit(int exitcode){ +/* _dl_fdprintf(2, "Hey, look where I am!\n"); */ + return 0; +} diff --git a/ldso/ldso/dl-elf.c b/ldso/ldso/dl-elf.c new file mode 100644 index 000000000..9d1cd0ff5 --- /dev/null +++ b/ldso/ldso/dl-elf.c @@ -0,0 +1,588 @@ +/* Load an ELF sharable library into memory. + + Copyright (C) 1993-1996, Eric Youngdale. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +/* This file contains the helper routines to load an ELF sharable + library into memory and add the symbol table info to the chain. */ + +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/errno.h> +#include "string.h" +/*#include <stdlib.h>*/ +#include <linux/mman.h> +#include <linux/stat.h> +#include "hash.h" +#include "linuxelf.h" +#include "sysdep.h" +#include <linux/unistd.h> +#include "syscall.h" +#ifdef USE_CACHE +#include "../config.h" +#endif + +extern char *_dl_progname; + +#ifdef USE_CACHE + +static caddr_t _dl_cache_addr = NULL; +static size_t _dl_cache_size = 0; + +int _dl_map_cache(void) +{ + int fd; + struct kernel_stat st; + header_t *header; + libentry_t *libent; + int i, strtabsize; + + if (_dl_cache_addr == (caddr_t)-1) + return -1; + else if (_dl_cache_addr != NULL) + return 0; + + if (_dl_stat(LDSO_CACHE, &st) || (fd = _dl_open(LDSO_CACHE, O_RDONLY)) < 0) + { + _dl_fdprintf(2, "%s: can't open cache '%s'\n", _dl_progname, LDSO_CACHE); + _dl_cache_addr = (caddr_t)-1; /* so we won't try again */ + return -1; + } + + _dl_cache_size = st.st_size; + _dl_cache_addr = (caddr_t)_dl_mmap(0, _dl_cache_size, PROT_READ, + MAP_SHARED, fd, 0); + _dl_close (fd); + if (_dl_cache_addr == (caddr_t)-1) + { + _dl_fdprintf(2, "%s: can't map cache '%s'\n", _dl_progname, LDSO_CACHE); + return -1; + } + + header = (header_t *)_dl_cache_addr; + + if (_dl_cache_size < sizeof (header_t) || + _dl_memcmp(header->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN) || + _dl_memcmp(header->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN) || + _dl_cache_size < + (sizeof (header_t) + header->nlibs * sizeof (libentry_t)) || + _dl_cache_addr[_dl_cache_size-1] != '\0') + { + _dl_fdprintf(2, "%s: cache '%s' is corrupt\n", _dl_progname, LDSO_CACHE); + goto fail; + } + + strtabsize = _dl_cache_size - sizeof (header_t) - + header->nlibs * sizeof (libentry_t); + libent = (libentry_t *)&header[1]; + + for (i = 0; i < header->nlibs; i++) + { + if (libent[i].sooffset >= strtabsize || + libent[i].liboffset >= strtabsize) + { + _dl_fdprintf(2, "%s: cache '%s' is corrupt\n", _dl_progname, LDSO_CACHE); + goto fail; + } + } + + return 0; + +fail: + _dl_munmap(_dl_cache_addr, _dl_cache_size); + _dl_cache_addr = (caddr_t)-1; + return -1; +} + +int _dl_unmap_cache(void) +{ + if (_dl_cache_addr == NULL || _dl_cache_addr == (caddr_t)-1) + return -1; + +#if 1 + _dl_munmap (_dl_cache_addr, _dl_cache_size); + _dl_cache_addr = NULL; +#endif + + return 0; +} + +#endif + +/* + * Used to return error codes back to dlopen et. al. + */ + +unsigned int _dl_error_number; +unsigned int _dl_internal_error_number; + +struct elf_resolve * _dl_load_shared_library(int secure, + struct elf_resolve * tpnt, char * full_libname) { + char * pnt, *pnt1, *pnt2; + struct elf_resolve *tpnt1 = NULL; + char mylibname[2050]; + char * libname; + + _dl_internal_error_number = 0; + + /* quick hack to ensure mylibname buffer doesn't overflow. don't + allow full_libname or any directory to be longer than 1024. */ + if (_dl_strlen(full_libname) > 1024) + goto goof; + + pnt = libname = full_libname; + while (*pnt) { + if(*pnt == '/') libname = pnt+1; + pnt++; + } + + /* If the filename has any '/', try it straight and leave it at that. + For IBCS2 compatibility under linux, we substitute the string + /usr/i486-sysv4/lib for /usr/lib in library names. */ + + if (libname != full_libname) { + tpnt1 = _dl_load_elf_shared_library(secure, full_libname, 0); + if (tpnt1) + return tpnt1; + goto goof; + } + + /* + * The ABI specifies that RPATH is searched before LD_*_PATH or + * the default path of /usr/lib. + * Check in rpath directories + */ + for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) { + if (tpnt->libtype == elf_executable) { + pnt1 = (char *)tpnt->dynamic_info[DT_RPATH]; + if(pnt1) { + pnt1 += (unsigned int) tpnt->loadaddr + tpnt->dynamic_info[DT_STRTAB]; + while(*pnt1){ + pnt2 = mylibname; + while(*pnt1 && *pnt1 != ':') { + if (pnt2 - mylibname < 1024) + *pnt2++ = *pnt1++; + else + pnt1++; + } + if (pnt2 - mylibname >= 1024) + break; + if(pnt2[-1] != '/') *pnt2++ = '/'; + pnt = libname; + while(*pnt) *pnt2++ = *pnt++; + *pnt2++ = 0; + tpnt1 = _dl_load_elf_shared_library(secure, mylibname, 0); + if(tpnt1) return tpnt1; + if(*pnt1 == ':') pnt1++; + } + } + } + } + + + /* Check in LD_{ELF_}LIBRARY_PATH, if specified and allowed */ + pnt1 = _dl_library_path; + if (pnt1 && *pnt1) { + while (*pnt1) { + pnt2 = mylibname; + while(*pnt1 && *pnt1 != ':' && *pnt1 != ';') { + if (pnt2 - mylibname < 1024) + *pnt2++ = *pnt1++; + else + pnt1++; + } + if (pnt2 - mylibname >= 1024) + break; + if(pnt2[-1] != '/') *pnt2++ = '/'; + pnt = libname; + while(*pnt) *pnt2++ = *pnt++; + *pnt2++ = 0; + tpnt1 = _dl_load_elf_shared_library(secure, mylibname, 0); + if(tpnt1) return tpnt1; + if(*pnt1 == ':' || *pnt1 == ';') pnt1++; + } + } + + + /* + * Where should the cache be searched? There is no such concept in the + * ABI, so we have some flexibility here. For now, search it before + * the default path of /usr/lib. + */ +#ifdef USE_CACHE + if (_dl_cache_addr != NULL && _dl_cache_addr != (caddr_t)-1) + { + int i; + header_t *header = (header_t *)_dl_cache_addr; + libentry_t *libent = (libentry_t *)&header[1]; + char *strs = (char *)&libent[header->nlibs]; + + for (i = 0; i < header->nlibs; i++) + { + if ((libent[i].flags == LIB_ELF || + libent[i].flags == LIB_ELF_LIBC5) && + _dl_strcmp(libname, strs+libent[i].sooffset) == 0 && + (tpnt1 = _dl_load_elf_shared_library(secure, strs+libent[i].liboffset, 0))) + return tpnt1; + } + } +#endif + + +#ifdef UCLIBC_DEVEL + + /* Check in /usr/<arch>-linux-uclibc/lib */ + pnt1 = UCLIBC_INSTALL_DIR"/lib"; + pnt = mylibname; + while(*pnt1) *pnt++ = *pnt1++; + pnt1 = libname; + while(*pnt1) *pnt++ = *pnt1++; + *pnt++ = 0; + tpnt1 = _dl_load_elf_shared_library(secure, mylibname, 0); + if (tpnt1) return tpnt1; + +#else /* UCLIBC_DEVEL */ + + /* Check in /usr/lib */ + pnt1 = "/usr/lib/"; + pnt = mylibname; + while(*pnt1) *pnt++ = *pnt1++; + pnt1 = libname; + while(*pnt1) *pnt++ = *pnt1++; + *pnt++ = 0; + tpnt1 = _dl_load_elf_shared_library(secure, mylibname, 0); + if (tpnt1) return tpnt1; + + /* Check in /lib */ + /* try "/lib/". */ + pnt1 = "/lib/"; + pnt = mylibname; + while(*pnt1) *pnt++ = *pnt1++; + pnt1 = libname; + while(*pnt1) *pnt++ = *pnt1++; + *pnt++ = 0; + tpnt1 = _dl_load_elf_shared_library(secure, mylibname, 0); + if (tpnt1) return tpnt1; +#endif /* UCLIBC_DEVEL */ + +goof: + /* Well, we shot our wad on that one. All we can do now is punt */ + if (_dl_internal_error_number) _dl_error_number = _dl_internal_error_number; + else _dl_error_number = DL_ERROR_NOFILE; + return NULL; +} + +/* + * Read one ELF library into memory, mmap it into the correct locations and + * add the symbol info to the symbol chain. Perform any relocations that + * are required. + */ + +//extern _elf_rtbndr(void); + +struct elf_resolve * _dl_load_elf_shared_library(int secure, + char * libname, int flag) { + struct elfhdr * epnt; + unsigned int dynamic_addr = 0; + unsigned int dynamic_size = 0; + struct dynamic * dpnt; + struct elf_resolve * tpnt; + struct elf_phdr * ppnt; + int piclib; + char * status; + int flags; + char header[4096]; + int dynamic_info[24]; + int * lpnt; + unsigned int libaddr; + unsigned int minvma=0xffffffff, maxvma=0; + + int i; + int infile; + + /* If this file is already loaded, skip this step */ + tpnt = _dl_check_hashed_files(libname); + if(tpnt) return tpnt; + + /* If we are in secure mode (i.e. a setu/gid binary using LD_PRELOAD), + we don't load the library if it isn't setuid. */ + + if (secure) { + struct kernel_stat st; + if (_dl_stat(libname, &st) || !(st.st_mode & S_ISUID)) + return NULL; + } + + libaddr = 0; + infile = _dl_open(libname, O_RDONLY); + if(infile < 0) + { +#if 0 + /* + * NO! When we open shared libraries we may search several paths. + * it is inappropriate to generate an error here. + */ + _dl_fdprintf(2, "%s: can't open '%s'\n", _dl_progname, libname); +#endif + _dl_internal_error_number = DL_ERROR_NOFILE; + return NULL; + } + + _dl_read(infile, header, sizeof(header)); + epnt = (struct elfhdr *) header; + if (epnt->e_ident[0] != 0x7f || + epnt->e_ident[1] != 'E' || + epnt->e_ident[2] != 'L' || + epnt->e_ident[3] != 'F') { + _dl_fdprintf(2, "%s: '%s' is not an ELF file\n", _dl_progname, libname); + _dl_internal_error_number = DL_ERROR_NOTELF; + _dl_close(infile); + return NULL; + }; + + if((epnt->e_type != ET_DYN) || + (epnt->e_machine != MAGIC1 +#ifdef MAGIC2 + && epnt->e_machine != MAGIC2 +#endif + )){ + _dl_internal_error_number = (epnt->e_type != ET_DYN ? DL_ERROR_NOTDYN : DL_ERROR_NOTMAGIC); + _dl_fdprintf(2, "%s: '%s' is not an ELF executable for " ELF_TARGET "\n", + _dl_progname, libname); + _dl_close(infile); + return NULL; + }; + + ppnt = (struct elf_phdr *) &header[epnt->e_phoff]; + + piclib = 1; + for(i=0;i < epnt->e_phnum; i++){ + + if(ppnt->p_type == PT_DYNAMIC) { + if (dynamic_addr) + _dl_fdprintf(2, "%s: '%s' has more than one dynamic section\n", + _dl_progname, libname); + dynamic_addr = ppnt->p_vaddr; + dynamic_size = ppnt->p_filesz; + }; + + if(ppnt->p_type == PT_LOAD) { + /* See if this is a PIC library. */ + if(i == 0 && ppnt->p_vaddr > 0x1000000) { + piclib = 0; + minvma=ppnt->p_vaddr; + } + if(piclib && ppnt->p_vaddr < minvma) { + minvma = ppnt->p_vaddr; + } + if(((unsigned int)ppnt->p_vaddr + ppnt->p_memsz) > maxvma) { + maxvma = ppnt->p_vaddr + ppnt->p_memsz; + } + } + ppnt++; + }; + + maxvma=(maxvma+0xfffU)&~0xfffU; + minvma=minvma&~0xffffU; + + flags = MAP_PRIVATE /*| MAP_DENYWRITE*/; + if(!piclib) flags |= MAP_FIXED; + + status = (char *) _dl_mmap((char *) (piclib?0:minvma), + maxvma-minvma, + PROT_NONE, + flags | MAP_ANONYMOUS, -1, + 0); + if(_dl_mmap_check_error(status)) { + _dl_fdprintf(2, "%s: can't map '/dev/zero'\n", _dl_progname); + _dl_internal_error_number = DL_ERROR_MMAP_FAILED; + _dl_close(infile); + return NULL; + }; + libaddr=(unsigned int)status; + flags|=MAP_FIXED; + + /* Get the memory to store the library */ + ppnt = (struct elf_phdr *) &header[epnt->e_phoff]; + + for(i=0;i < epnt->e_phnum; i++){ + if(ppnt->p_type == PT_LOAD) { + + /* See if this is a PIC library. */ + if(i == 0 && ppnt->p_vaddr > 0x1000000) { + piclib = 0; + /* flags |= MAP_FIXED; */ + } + + + + if(ppnt->p_flags & PF_W) { + unsigned int map_size; + char * cpnt; + + status = (char *) _dl_mmap((char *) ((piclib?libaddr:0) + + (ppnt->p_vaddr & 0xfffff000)), + (ppnt->p_vaddr & 0xfff) + ppnt->p_filesz, + LXFLAGS(ppnt->p_flags), + flags, infile, + ppnt->p_offset & 0x7ffff000); + + if(_dl_mmap_check_error(status)) { + _dl_fdprintf(2, "%s: can't map '%s'\n", _dl_progname, libname); + _dl_internal_error_number = DL_ERROR_MMAP_FAILED; + _dl_munmap((char *)libaddr, maxvma-minvma); + _dl_close(infile); + return NULL; + }; + + /* Pad the last page with zeroes. */ + cpnt =(char *) (status + (ppnt->p_vaddr & 0xfff) + ppnt->p_filesz); + while(((unsigned int) cpnt) & 0xfff) *cpnt++ = 0; + +/* I am not quite sure if this is completely correct to do or not, but + the basic way that we handle bss segments is that we mmap /dev/zero if + there are any pages left over that are not mapped as part of the file */ + + map_size = (ppnt->p_vaddr + ppnt->p_filesz + 0xfff) & 0xfffff000; + if(map_size < ppnt->p_vaddr + ppnt->p_memsz) + status = (char *) _dl_mmap((char *) map_size + (piclib?libaddr:0), + ppnt->p_vaddr + ppnt->p_memsz - map_size, + LXFLAGS(ppnt->p_flags), + flags | MAP_ANONYMOUS, -1, 0); + } else + status = (char *) _dl_mmap((char *) (ppnt->p_vaddr & 0xfffff000) + + (piclib?libaddr:0), + (ppnt->p_vaddr & 0xfff) + ppnt->p_filesz, + LXFLAGS(ppnt->p_flags), + flags, infile, + ppnt->p_offset & 0x7ffff000); + if(_dl_mmap_check_error(status)) { + _dl_fdprintf(2, "%s: can't map '%s'\n", _dl_progname, libname); + _dl_internal_error_number = DL_ERROR_MMAP_FAILED; + _dl_munmap((char *)libaddr, maxvma-minvma); + _dl_close(infile); + return NULL; + }; + + /* if(libaddr == 0 && piclib) { + libaddr = (unsigned int) status; + flags |= MAP_FIXED; + }; */ + }; + ppnt++; + }; + _dl_close(infile); + + /* For a non-PIC library, the addresses are all absolute */ + if(piclib) { + dynamic_addr += (unsigned int) libaddr; + } + + /* + * OK, the ELF library is now loaded into VM in the correct locations + * The next step is to go through and do the dynamic linking (if needed). + */ + + /* Start by scanning the dynamic section to get all of the pointers */ + + if(!dynamic_addr) { + _dl_internal_error_number = DL_ERROR_NODYNAMIC; + _dl_fdprintf(2, "%s: '%s' is missing a dynamic section\n", _dl_progname, libname); + return NULL; + } + + dpnt = (struct dynamic *) dynamic_addr; + + dynamic_size = dynamic_size / sizeof(struct dynamic); + _dl_memset(dynamic_info, 0, sizeof(dynamic_info)); + for(i=0; i< dynamic_size; i++){ + if( dpnt->d_tag > DT_JMPREL ) {dpnt++; continue; } + dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val; + if(dpnt->d_tag == DT_TEXTREL || + SVR4_BUGCOMPAT) dynamic_info[DT_TEXTREL] = 1; + dpnt++; + }; + + /* If the TEXTREL is set, this means that we need to make the pages + writable before we perform relocations. Do this now. They get set back + again later. */ + + if (dynamic_info[DT_TEXTREL]) { + ppnt = (struct elf_phdr *) &header[epnt->e_phoff]; + for(i=0;i < epnt->e_phnum; i++, ppnt++){ + if(ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W)) + _dl_mprotect((void *) ((piclib?libaddr:0) + (ppnt->p_vaddr & 0xfffff000)), + (ppnt->p_vaddr & 0xfff) + (unsigned int) ppnt->p_filesz, + PROT_READ | PROT_WRITE | PROT_EXEC); + } + } + + + tpnt = _dl_add_elf_hash_table(libname, (char *) libaddr, dynamic_info, dynamic_addr, + dynamic_size); + + tpnt->ppnt = (struct elf_phdr *) (tpnt->loadaddr + epnt->e_phoff); + tpnt->n_phent = epnt->e_phnum; + + /* + * OK, the next thing we need to do is to insert the dynamic linker into + * the proper entry in the GOT so that the PLT symbols can be properly + * resolved. + */ + + lpnt = (int *) dynamic_info[DT_PLTGOT]; + + if(lpnt) { + lpnt = (int *) (dynamic_info[DT_PLTGOT] + ((int) libaddr)); + INIT_GOT(lpnt, tpnt); + }; + + return tpnt; +} + +/* Ugly, ugly. Some versions of the SVr4 linker fail to generate COPY + relocations for global variables that are present both in the image and + the shared library. Go through and do it manually. If the images + are guaranteed to be generated by a trustworthy linker, then this + step can be skipped. */ + +int _dl_copy_fixups(struct dyn_elf * rpnt) +{ + int goof = 0; + struct elf_resolve * tpnt; + + if(rpnt->next) goof += _dl_copy_fixups(rpnt->next); + else return 0; + + tpnt = rpnt->dyn; + + if (tpnt->init_flag & COPY_RELOCS_DONE) return goof; + tpnt->init_flag |= COPY_RELOCS_DONE; + +#ifdef ELF_USES_RELOCA + goof += _dl_parse_copy_information(rpnt, tpnt->dynamic_info[DT_RELA], + tpnt->dynamic_info[DT_RELASZ], 0); + +#else + goof += _dl_parse_copy_information(rpnt, tpnt->dynamic_info[DT_REL], + tpnt->dynamic_info[DT_RELSZ], 0); + +#endif + return goof; +} + diff --git a/ldso/ldso/dl-hash.c b/ldso/ldso/dl-hash.c new file mode 100644 index 000000000..4eab974a8 --- /dev/null +++ b/ldso/ldso/dl-hash.c @@ -0,0 +1,284 @@ +/* Run an ELF binary on a linux system. + + Copyright (C) 1993-1996, Eric Youngdale. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +/* Various symbol table handling functions, including symbol lookup */ + +/*#include <stdlib.h>*/ +#include "string.h" +#include <linux/unistd.h> +#include <linux/elf.h> + +#include "libdl/dlfcn.h" +#include "hash.h" +#include "linuxelf.h" +#include "syscall.h" +#include "string.h" +#include "sysdep.h" + +/* + * This is the start of the linked list that describes all of the files present + * in the system with pointers to all of the symbol, string, and hash tables, + * as well as all of the other good stuff in the binary. + */ + +struct elf_resolve * _dl_loaded_modules = NULL; + +/* + * This is the list of modules that are loaded when the image is first + * started. As we add more via dlopen, they get added into other + * chains. + */ +struct dyn_elf * _dl_symbol_tables = NULL; + +/* + * This is the list of modules that are loaded via dlopen. We may need + * to search these for RTLD_GLOBAL files. + */ +struct dyn_elf * _dl_handles = NULL; + + +/* + * This is the hash function that is used by the ELF linker to generate + * the hash table that each executable and library is required to + * have. We need it to decode the hash table. + */ + +unsigned long _dl_elf_hash(const char * name){ + unsigned long hash = 0; + unsigned long tmp; + + while (*name){ + hash = (hash << 4) + *name++; + if((tmp = hash & 0xf0000000)) hash ^= tmp >> 24; + hash &= ~tmp; + }; + return hash; +} + +/* + * Check to see if a library has already been added to the hash chain. + */ +struct elf_resolve * _dl_check_hashed_files(char * libname){ + struct elf_resolve * tpnt; + int len = _dl_strlen(libname); + + for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) { + if (_dl_strncmp(tpnt->libname, libname, len) == 0 && + (tpnt->libname[len] == '\0' || tpnt->libname[len] == '.')) + return tpnt; + } + + return NULL; +} + +/* + * We call this function when we have just read an ELF library or executable. + * We add the relevant info to the symbol chain, so that we can resolve all + * externals properly. + */ + +struct elf_resolve * _dl_add_elf_hash_table(char * libname, + char * loadaddr, + unsigned int * dynamic_info, + unsigned int dynamic_addr, + unsigned int dynamic_size){ + unsigned int * hash_addr; + struct elf_resolve * tpnt; + int i; + + if (!_dl_loaded_modules) { + tpnt = _dl_loaded_modules = + (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve)); + _dl_memset (tpnt, 0, sizeof (*tpnt)); + } + else { + tpnt = _dl_loaded_modules; + while(tpnt->next) tpnt = tpnt->next; + tpnt->next = (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve)); + _dl_memset (tpnt->next, 0, sizeof (*(tpnt->next))); + tpnt->next->prev = tpnt; + tpnt = tpnt->next; + }; + + tpnt->next = NULL; + tpnt->init_flag = 0; + tpnt->libname = _dl_strdup(libname); + tpnt->dynamic_addr = dynamic_addr; + tpnt->dynamic_size = dynamic_size; + tpnt->libtype = loaded_file; + + if( dynamic_info[DT_HASH] != 0 ) + { + hash_addr = (unsigned int *) (dynamic_info[DT_HASH] + loadaddr); + tpnt->nbucket = *hash_addr++; + tpnt->nchain = *hash_addr++; + tpnt->elf_buckets = hash_addr; + hash_addr += tpnt->nbucket; + tpnt->chains = hash_addr; + } + tpnt->loadaddr = loadaddr; + for(i=0; i<24; i++) tpnt->dynamic_info[i] = dynamic_info[i]; + return tpnt; +} + + +/* + * This function resolves externals, and this is either called when we process + * relocations or when we call an entry in the PLT table for the first time. + */ + +char * _dl_find_hash(char * name, struct dyn_elf * rpnt1, + unsigned int instr_addr, struct elf_resolve * f_tpnt, + int copyrel){ + struct elf_resolve * tpnt; + int si; + char * pnt; + int pass; + char * strtab; + struct elf32_sym * symtab; + unsigned int elf_hash_number, hn; + char * weak_result; + struct elf_resolve * first_def; + struct dyn_elf * rpnt, first; + char * data_result = 0; /* nakao */ + + weak_result = 0; + elf_hash_number = _dl_elf_hash(name); + + /* A quick little hack to make sure that any symbol in the executable + will be preferred to one in a shared library. This is necessary so + that any shared library data symbols referenced in the executable + will be seen at the same address by the executable, shared libraries + and dynamically loaded code. -Rob Ryan (robr@cmu.edu) */ + if(!copyrel && rpnt1) { + first=(*_dl_symbol_tables); + first.next=rpnt1; + rpnt1=(&first); + } + + /* + * The passes are so that we can first search the regular symbols + * for whatever module was specified, and then search anything + * loaded with RTLD_GLOBAL. When pass is 1, it means we are just + * starting the first dlopened module, and anything above that + * is just the next one in the chain. + */ + for(pass = 0; (1==1); pass++) + { + + /* + * If we are just starting to search for RTLD_GLOBAL, setup + * the pointer for the start of the search. + */ + if( pass == 1) { + rpnt1 = _dl_handles; + } + + /* + * Anything after this, we need to skip to the next module. + */ + else if( pass >= 2) { + rpnt1 = rpnt1->next_handle; + } + + /* + * Make sure we still have a module, and make sure that this + * module was loaded with RTLD_GLOBAL. + */ + if( pass != 0 ) + { + if( rpnt1 == NULL ) break; + if( (rpnt1->flags & RTLD_GLOBAL) == 0) continue; + } + + for(rpnt = (rpnt1 ? rpnt1 : _dl_symbol_tables); + rpnt; rpnt = rpnt->next) { + tpnt = rpnt->dyn; + + /* + * The idea here is that if we are using dlsym, we want to + * first search the entire chain loaded from dlopen, and + * return a result from that if we found anything. If this + * fails, then we continue the search into the stuff loaded + * when the image was activated. For normal lookups, we start + * with rpnt == NULL, so we should never hit this. + */ + if( tpnt->libtype == elf_executable + && weak_result != 0 ) + { + break; + } + + /* + * Avoid calling .urem here. + */ + do_rem(hn, elf_hash_number, tpnt->nbucket); + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + /* + * This crap is required because the first instance of a + * symbol on the chain will be used for all symbol references. + * Thus this instance must be resolved to an address that + * contains the actual function, + */ + + first_def = NULL; + + for(si = tpnt->elf_buckets[hn]; si; si = tpnt->chains[si]){ + pnt = strtab + symtab[si].st_name; + + if(_dl_strcmp(pnt, name) == 0 && + (ELF32_ST_TYPE(symtab[si].st_info) == STT_FUNC || + ELF32_ST_TYPE(symtab[si].st_info) == STT_NOTYPE || + ELF32_ST_TYPE(symtab[si].st_info) == STT_OBJECT) && + symtab[si].st_value != 0) { + + /* Here we make sure that we find a module where the symbol is + * actually defined. + */ + + if(f_tpnt) { + if(!first_def) first_def = tpnt; + if(first_def == f_tpnt && symtab[si].st_shndx == 0) + continue; + } + + switch(ELF32_ST_BIND(symtab[si].st_info)){ + case STB_GLOBAL: + if ( tpnt->libtype != elf_executable + && ELF32_ST_TYPE(symtab[si].st_info) == STT_NOTYPE) { /* nakao */ + data_result = tpnt->loadaddr + symtab[si].st_value; /* nakao */ + break; /* nakao */ + } else /* nakao */ + return tpnt->loadaddr + symtab[si].st_value; + case STB_WEAK: + if (!weak_result) weak_result = tpnt->loadaddr + symtab[si].st_value; + break; + default: /* Do local symbols need to be examined? */ + break; + } + } + } + } + } + if (data_result) return data_result; /* nakao */ + return weak_result; +} diff --git a/ldso/ldso/hash.c b/ldso/ldso/hash.c new file mode 100644 index 000000000..4eab974a8 --- /dev/null +++ b/ldso/ldso/hash.c @@ -0,0 +1,284 @@ +/* Run an ELF binary on a linux system. + + Copyright (C) 1993-1996, Eric Youngdale. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +/* Various symbol table handling functions, including symbol lookup */ + +/*#include <stdlib.h>*/ +#include "string.h" +#include <linux/unistd.h> +#include <linux/elf.h> + +#include "libdl/dlfcn.h" +#include "hash.h" +#include "linuxelf.h" +#include "syscall.h" +#include "string.h" +#include "sysdep.h" + +/* + * This is the start of the linked list that describes all of the files present + * in the system with pointers to all of the symbol, string, and hash tables, + * as well as all of the other good stuff in the binary. + */ + +struct elf_resolve * _dl_loaded_modules = NULL; + +/* + * This is the list of modules that are loaded when the image is first + * started. As we add more via dlopen, they get added into other + * chains. + */ +struct dyn_elf * _dl_symbol_tables = NULL; + +/* + * This is the list of modules that are loaded via dlopen. We may need + * to search these for RTLD_GLOBAL files. + */ +struct dyn_elf * _dl_handles = NULL; + + +/* + * This is the hash function that is used by the ELF linker to generate + * the hash table that each executable and library is required to + * have. We need it to decode the hash table. + */ + +unsigned long _dl_elf_hash(const char * name){ + unsigned long hash = 0; + unsigned long tmp; + + while (*name){ + hash = (hash << 4) + *name++; + if((tmp = hash & 0xf0000000)) hash ^= tmp >> 24; + hash &= ~tmp; + }; + return hash; +} + +/* + * Check to see if a library has already been added to the hash chain. + */ +struct elf_resolve * _dl_check_hashed_files(char * libname){ + struct elf_resolve * tpnt; + int len = _dl_strlen(libname); + + for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) { + if (_dl_strncmp(tpnt->libname, libname, len) == 0 && + (tpnt->libname[len] == '\0' || tpnt->libname[len] == '.')) + return tpnt; + } + + return NULL; +} + +/* + * We call this function when we have just read an ELF library or executable. + * We add the relevant info to the symbol chain, so that we can resolve all + * externals properly. + */ + +struct elf_resolve * _dl_add_elf_hash_table(char * libname, + char * loadaddr, + unsigned int * dynamic_info, + unsigned int dynamic_addr, + unsigned int dynamic_size){ + unsigned int * hash_addr; + struct elf_resolve * tpnt; + int i; + + if (!_dl_loaded_modules) { + tpnt = _dl_loaded_modules = + (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve)); + _dl_memset (tpnt, 0, sizeof (*tpnt)); + } + else { + tpnt = _dl_loaded_modules; + while(tpnt->next) tpnt = tpnt->next; + tpnt->next = (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve)); + _dl_memset (tpnt->next, 0, sizeof (*(tpnt->next))); + tpnt->next->prev = tpnt; + tpnt = tpnt->next; + }; + + tpnt->next = NULL; + tpnt->init_flag = 0; + tpnt->libname = _dl_strdup(libname); + tpnt->dynamic_addr = dynamic_addr; + tpnt->dynamic_size = dynamic_size; + tpnt->libtype = loaded_file; + + if( dynamic_info[DT_HASH] != 0 ) + { + hash_addr = (unsigned int *) (dynamic_info[DT_HASH] + loadaddr); + tpnt->nbucket = *hash_addr++; + tpnt->nchain = *hash_addr++; + tpnt->elf_buckets = hash_addr; + hash_addr += tpnt->nbucket; + tpnt->chains = hash_addr; + } + tpnt->loadaddr = loadaddr; + for(i=0; i<24; i++) tpnt->dynamic_info[i] = dynamic_info[i]; + return tpnt; +} + + +/* + * This function resolves externals, and this is either called when we process + * relocations or when we call an entry in the PLT table for the first time. + */ + +char * _dl_find_hash(char * name, struct dyn_elf * rpnt1, + unsigned int instr_addr, struct elf_resolve * f_tpnt, + int copyrel){ + struct elf_resolve * tpnt; + int si; + char * pnt; + int pass; + char * strtab; + struct elf32_sym * symtab; + unsigned int elf_hash_number, hn; + char * weak_result; + struct elf_resolve * first_def; + struct dyn_elf * rpnt, first; + char * data_result = 0; /* nakao */ + + weak_result = 0; + elf_hash_number = _dl_elf_hash(name); + + /* A quick little hack to make sure that any symbol in the executable + will be preferred to one in a shared library. This is necessary so + that any shared library data symbols referenced in the executable + will be seen at the same address by the executable, shared libraries + and dynamically loaded code. -Rob Ryan (robr@cmu.edu) */ + if(!copyrel && rpnt1) { + first=(*_dl_symbol_tables); + first.next=rpnt1; + rpnt1=(&first); + } + + /* + * The passes are so that we can first search the regular symbols + * for whatever module was specified, and then search anything + * loaded with RTLD_GLOBAL. When pass is 1, it means we are just + * starting the first dlopened module, and anything above that + * is just the next one in the chain. + */ + for(pass = 0; (1==1); pass++) + { + + /* + * If we are just starting to search for RTLD_GLOBAL, setup + * the pointer for the start of the search. + */ + if( pass == 1) { + rpnt1 = _dl_handles; + } + + /* + * Anything after this, we need to skip to the next module. + */ + else if( pass >= 2) { + rpnt1 = rpnt1->next_handle; + } + + /* + * Make sure we still have a module, and make sure that this + * module was loaded with RTLD_GLOBAL. + */ + if( pass != 0 ) + { + if( rpnt1 == NULL ) break; + if( (rpnt1->flags & RTLD_GLOBAL) == 0) continue; + } + + for(rpnt = (rpnt1 ? rpnt1 : _dl_symbol_tables); + rpnt; rpnt = rpnt->next) { + tpnt = rpnt->dyn; + + /* + * The idea here is that if we are using dlsym, we want to + * first search the entire chain loaded from dlopen, and + * return a result from that if we found anything. If this + * fails, then we continue the search into the stuff loaded + * when the image was activated. For normal lookups, we start + * with rpnt == NULL, so we should never hit this. + */ + if( tpnt->libtype == elf_executable + && weak_result != 0 ) + { + break; + } + + /* + * Avoid calling .urem here. + */ + do_rem(hn, elf_hash_number, tpnt->nbucket); + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + /* + * This crap is required because the first instance of a + * symbol on the chain will be used for all symbol references. + * Thus this instance must be resolved to an address that + * contains the actual function, + */ + + first_def = NULL; + + for(si = tpnt->elf_buckets[hn]; si; si = tpnt->chains[si]){ + pnt = strtab + symtab[si].st_name; + + if(_dl_strcmp(pnt, name) == 0 && + (ELF32_ST_TYPE(symtab[si].st_info) == STT_FUNC || + ELF32_ST_TYPE(symtab[si].st_info) == STT_NOTYPE || + ELF32_ST_TYPE(symtab[si].st_info) == STT_OBJECT) && + symtab[si].st_value != 0) { + + /* Here we make sure that we find a module where the symbol is + * actually defined. + */ + + if(f_tpnt) { + if(!first_def) first_def = tpnt; + if(first_def == f_tpnt && symtab[si].st_shndx == 0) + continue; + } + + switch(ELF32_ST_BIND(symtab[si].st_info)){ + case STB_GLOBAL: + if ( tpnt->libtype != elf_executable + && ELF32_ST_TYPE(symtab[si].st_info) == STT_NOTYPE) { /* nakao */ + data_result = tpnt->loadaddr + symtab[si].st_value; /* nakao */ + break; /* nakao */ + } else /* nakao */ + return tpnt->loadaddr + symtab[si].st_value; + case STB_WEAK: + if (!weak_result) weak_result = tpnt->loadaddr + symtab[si].st_value; + break; + default: /* Do local symbols need to be examined? */ + break; + } + } + } + } + } + if (data_result) return data_result; /* nakao */ + return weak_result; +} diff --git a/ldso/ldso/hash.h b/ldso/ldso/hash.h new file mode 100644 index 000000000..2eeda2d46 --- /dev/null +++ b/ldso/ldso/hash.h @@ -0,0 +1,113 @@ +#include "link.h" + +#ifndef RTLD_NEXT +#define RTLD_NEXT ((void*)-1) +#endif + +struct dyn_elf{ + unsigned int flags; + struct elf_resolve * dyn; + struct dyn_elf * next_handle; /* Used by dlopen et al. */ + struct dyn_elf * next; +}; + +struct elf_resolve{ + /* These entries must be in this order to be compatible with the interface used + by gdb to obtain the list of symbols. */ + char * loadaddr; + char * libname; + unsigned int dynamic_addr; + struct elf_resolve * next; + struct elf_resolve * prev; + /* Nothing after this address is used by gdb. */ + enum {elf_lib, elf_executable,program_interpreter, loaded_file} libtype; + struct dyn_elf * symbol_scope; + unsigned short usage_count; + unsigned short int init_flag; + unsigned int nbucket; + unsigned int * elf_buckets; + /* + * These are only used with ELF style shared libraries + */ + unsigned int nchain; + unsigned int * chains; + unsigned int dynamic_info[24]; + + unsigned int dynamic_size; + unsigned int n_phent; + struct elf_phdr * ppnt; +}; + +#if 0 +/* + * The DT_DEBUG entry in the .dynamic section is given the address of this structure. + * gdb can pick this up to obtain the correct list of loaded modules. + */ + +struct r_debug{ + int r_version; + struct elf_resolve * link_map; + unsigned long brk_fun; + enum {RT_CONSISTENT, RT_ADD, RT_DELETE}; + unsigned long ldbase; +}; +#endif + +#define COPY_RELOCS_DONE 1 +#define RELOCS_DONE 2 +#define JMP_RELOCS_DONE 4 +#define INIT_FUNCS_CALLED 8 + +extern struct dyn_elf * _dl_symbol_tables; +extern struct elf_resolve * _dl_loaded_modules; +extern struct dyn_elf * _dl_handles; + +extern struct elf_resolve * _dl_check_hashed_files(char * libname); +extern struct elf_resolve * _dl_add_elf_hash_table(char * libname, + char * loadaddr, + unsigned int * dynamic_info, + unsigned int dynamic_addr, + unsigned int dynamic_size); +extern char * _dl_find_hash(char * name, struct dyn_elf * rpnt1, + unsigned int instr_addr, + struct elf_resolve * f_tpnt, + int copyrel); + +extern int _dl_linux_dynamic_link(void); + +#ifdef __mc68000__ +/* On m68k constant strings are referenced through the GOT. */ +/* XXX Requires load_addr to be defined. */ +#define SEND_STDERR(X) \ + { const char *__s = (X); \ + if (__s < (const char *) load_addr) __s += load_addr; \ + _dl_write (2, __s, _dl_strlen (__s)); \ + } +#else +#define SEND_STDERR(X) _dl_write(2, X, _dl_strlen(X)); +#endif +extern int _dl_fdprintf(int, const char *, ...); +extern char * _dl_library_path; +extern char * _dl_not_lazy; +extern char * _dl_strdup(const char *); +extern inline int _dl_symbol(char * name); +unsigned long _dl_elf_hash(const char * name); + +extern inline int _dl_symbol(char * name) +{ + if(name[0] != '_' || name[1] != 'd' || name[2] != 'l' || name[3] != '_') + return 0; + return 1; +} + +#define DL_ERROR_NOFILE 1 +#define DL_ERROR_NOZERO 2 +#define DL_ERROR_NOTELF 3 +#define DL_ERROR_NOTMAGIC 4 +#define DL_ERROR_NOTDYN 5 +#define DL_ERROR_MMAP_FAILED 6 +#define DL_ERROR_NODYNAMIC 7 +#define DL_WRONG_RELOCS 8 +#define DL_BAD_HANDLE 9 +#define DL_NO_SYMBOL 10 + diff --git a/ldso/ldso/i386/dl-sysdep.h b/ldso/ldso/i386/dl-sysdep.h new file mode 100644 index 000000000..9bbeef1fa --- /dev/null +++ b/ldso/ldso/i386/dl-sysdep.h @@ -0,0 +1,82 @@ + +/* + * Various assmbly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + */ + +/* + * Define this if the system uses RELOCA. + */ +#undef ELF_USES_RELOCA + +/* + * Get a pointer to the argv array. On many platforms this can be just + * the address if the first argument, on other platforms we need to + * do something a little more subtle here. + */ +#define GET_ARGV(ARGVP, ARGS) ARGVP = ((unsigned int*) & ARGS) +/* + * Get the address of the Global offset table. This must be absolute, not + * relative. + */ +#define GET_GOT(X) __asm__("\tmovl %%ebx,%0\n\t" : "=a" (X)) + +/* + * Initialization sequence for a GOT. + */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[2] = (int) _dl_linux_resolve; \ + GOT_BASE[1] = (int) MODULE; \ +} + +/* + * Here is a macro to perform a relocation. This is only used when + * bootstrapping the dynamic loader. RELP is the relocation that we + * are performing, REL is the pointer to the address we are relocating. + * SYMBOL is the symbol involved in the relocation, and LOAD is the + * load address. + */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch(ELF32_R_TYPE((RELP)->r_info)){ \ + case R_386_32: \ + *REL += SYMBOL; \ + break; \ + case R_386_PC32: \ + *REL += SYMBOL - (unsigned int) REL; \ + break; \ + case R_386_GLOB_DAT: \ + case R_386_JMP_SLOT: \ + *REL = SYMBOL; \ + break; \ + case R_386_RELATIVE: \ + *REL += (unsigned int) LOAD; \ + break; \ + default: \ + _dl_exit(1); \ + } + + +/* + * Transfer control to the user's application, once the dynamic loader + * is done. + */ + +#define START() \ + __asm__ volatile ("leave\n\t" \ + "jmp *%%eax\n\t" \ + : "=a" (status) : \ + "d" (_dl_interpreter_exit), "a" (_dl_elf_main)) + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_386 +#define MAGIC2 EM_486 +/* Used for error messages */ +#define ELF_TARGET "386/486" + +extern unsigned int _dl_linux_resolver(int dummy, int i); + +#define do_rem(result, n, base) result = (n % base) diff --git a/ldso/ldso/i386/elfinterp.c b/ldso/ldso/i386/elfinterp.c new file mode 100644 index 000000000..3347882d6 --- /dev/null +++ b/ldso/ldso/i386/elfinterp.c @@ -0,0 +1,321 @@ +/* Run an ELF binary on a linux system. + + Copyright (C) 1993, Eric Youngdale. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef VERBOSE_DLINKER +#define VERBOSE_DLINKER +#endif +#ifdef VERBOSE_DLINKER +static char * _dl_reltypes[] = {"R_386_NONE","R_386_32","R_386_PC32","R_386_GOT32", + "R_386_PLT32","R_386_COPY","R_386_GLOB_DAT", + "R_386_JMP_SLOT","R_386_RELATIVE","R_386_GOTOFF", + "R_386_GOTPC","R_386_NUM"}; +#endif + +/* Program to load an ELF binary on a linux system, and run it. +References to symbols in sharable libraries can be resolved by either +an ELF sharable library or a linux style of shared library. */ + +/* Disclaimer: I have never seen any AT&T source code for SVr4, nor have + I ever taken any courses on internals. This program was developed using + information available through the book "UNIX SYSTEM V RELEASE 4, + Programmers guide: Ansi C and Programming Support Tools", which did + a more than adequate job of explaining everything required to get this + working. */ + +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/elf.h> + +#include "hash.h" +#include "linuxelf.h" +#include "../string.h" +#include "../syscall.h" + +#define SVR4_COMPATIBILITY + +extern char *_dl_progname; + +extern int _dl_linux_resolve(void); + +unsigned int _dl_linux_resolver(int dummy, int i) +{ + unsigned int * sp; + int reloc_entry; + int reloc_type; + struct elf32_rel * this_reloc; + char * strtab; + struct elf32_sym * symtab; + struct elf32_rel * rel_addr; + struct elf_resolve * tpnt; + int symtab_index; + char * new_addr; + char ** got_addr; + unsigned int instr_addr; + sp = &i; + reloc_entry = sp[1]; + tpnt = (struct elf_resolve *) sp[0]; + + rel_addr = (struct elf32_rel *) (tpnt->dynamic_info[DT_JMPREL] + + tpnt->loadaddr); + + this_reloc = rel_addr + (reloc_entry >> 3); + reloc_type = ELF32_R_TYPE(this_reloc->r_info); + symtab_index = ELF32_R_SYM(this_reloc->r_info); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + + if (reloc_type != R_386_JMP_SLOT) { + _dl_fdprintf(2, "%s: Incorrect relocation type in jump relocations\n", + _dl_progname); + _dl_exit(1); + }; + + /* Address of jump instruction to fix up */ + instr_addr = ((int)this_reloc->r_offset + (int)tpnt->loadaddr); + got_addr = (char **) instr_addr; + +#ifdef DEBUG + _dl_fdprintf(2, "Resolving symbol %s\n", + strtab + symtab[symtab_index].st_name); +#endif + + /* Get the address of the GOT entry */ + new_addr = _dl_find_hash(strtab + symtab[symtab_index].st_name, + tpnt->symbol_scope, (int) got_addr, tpnt, 0); + if(!new_addr) { + _dl_fdprintf(2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + _dl_exit(1); + }; +/* #define DEBUG_LIBRARY */ +#ifdef DEBUG_LIBRARY + if((unsigned int) got_addr < 0x40000000) { + _dl_fdprintf(2, "Calling library function: %s\n", + strtab + symtab[symtab_index].st_name); + } else { + *got_addr = new_addr; + } +#else + *got_addr = new_addr; +#endif + return (unsigned int) new_addr; +} + +void _dl_parse_lazy_relocation_information(struct elf_resolve * tpnt, int rel_addr, + int rel_size, int type){ + int i; + char * strtab; + int reloc_type; + int symtab_index; + struct elf32_sym * symtab; + struct elf32_rel * rpnt; + unsigned int * reloc_addr; + + /* Now parse the relocation information */ + rpnt = (struct elf32_rel *) (rel_addr + tpnt->loadaddr); + rel_size = rel_size / sizeof(struct elf32_rel); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = ( char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for(i=0; i< rel_size; i++, rpnt++){ + reloc_addr = (int *) (tpnt->loadaddr + (int)rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + symtab_index = ELF32_R_SYM(rpnt->r_info); + + /* When the dynamic linker bootstrapped itself, it resolved some symbols. + Make sure we do not do them again */ + if(!symtab_index && tpnt->libtype == program_interpreter) continue; + if(symtab_index && tpnt->libtype == program_interpreter && + _dl_symbol(strtab + symtab[symtab_index].st_name)) + continue; + + switch(reloc_type){ + case R_386_NONE: break; + case R_386_JMP_SLOT: + *reloc_addr += (unsigned int) tpnt->loadaddr; + break; + default: + _dl_fdprintf(2, "%s: (LAZY) can't handle reloc type ", _dl_progname); +#ifdef VERBOSE_DLINKER + _dl_fdprintf(2, "%s ", _dl_reltypes[reloc_type]); +#endif + if(symtab_index) _dl_fdprintf(2, "'%s'\n", + strtab + symtab[symtab_index].st_name); + _dl_exit(1); + }; + }; +} + +int _dl_parse_relocation_information(struct elf_resolve * tpnt, int rel_addr, + int rel_size, int type){ + int i; + char * strtab; + int reloc_type; + int goof = 0; + struct elf32_sym * symtab; + struct elf32_rel * rpnt; + unsigned int * reloc_addr; + unsigned int symbol_addr; + int symtab_index; + /* Now parse the relocation information */ + + rpnt = (struct elf32_rel *) (rel_addr + tpnt->loadaddr); + rel_size = rel_size / sizeof(struct elf32_rel); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = ( char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for(i=0; i< rel_size; i++, rpnt++){ + reloc_addr = (int *) (tpnt->loadaddr + (int)rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + + if(!symtab_index && tpnt->libtype == program_interpreter) continue; + + if(symtab_index) { + + if(tpnt->libtype == program_interpreter && + _dl_symbol(strtab + symtab[symtab_index].st_name)) + continue; + + symbol_addr = (unsigned int) + _dl_find_hash(strtab + symtab[symtab_index].st_name, + tpnt->symbol_scope, (int) reloc_addr, + (reloc_type == R_386_JMP_SLOT ? tpnt : NULL), 0); + + /* + * We want to allow undefined references to weak symbols - this might + * have been intentional. We should not be linking local symbols + * here, so all bases should be covered. + */ + if(!symbol_addr && + ELF32_ST_BIND(symtab[symtab_index].st_info) == STB_GLOBAL) { + _dl_fdprintf(2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + goof++; + } + } + switch(reloc_type){ + case R_386_NONE: + break; + case R_386_32: + *reloc_addr += symbol_addr; + break; + case R_386_PC32: + *reloc_addr += symbol_addr - (unsigned int) reloc_addr; + break; + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + *reloc_addr = symbol_addr; + break; + case R_386_RELATIVE: + *reloc_addr += (unsigned int) tpnt->loadaddr; + break; + case R_386_COPY: +#if 0 /* Do this later */ + _dl_fdprintf(2, "Doing copy for symbol "); + if(symtab_index) _dl_fdprintf(2, strtab + symtab[symtab_index].st_name); + _dl_fdprintf(2, "\n"); + _dl_memcpy((void *) symtab[symtab_index].st_value, + (void *) symbol_addr, + symtab[symtab_index].st_size); +#endif + break; + default: + _dl_fdprintf(2, "%s: can't handle reloc type ", _dl_progname); +#ifdef VERBOSE_DLINKER + _dl_fdprintf(2, "%s ", _dl_reltypes[reloc_type]); +#endif + if (symtab_index) + _dl_fdprintf(2, "'%s'\n", strtab + symtab[symtab_index].st_name); + _dl_exit(1); + }; + + }; + return goof; +} + + +/* This is done as a separate step, because there are cases where + information is first copied and later initialized. This results in + the wrong information being copied. Someone at Sun was complaining about + a bug in the handling of _COPY by SVr4, and this may in fact be what he + was talking about. Sigh. */ + +/* No, there are cases where the SVr4 linker fails to emit COPY relocs + at all */ + +int _dl_parse_copy_information(struct dyn_elf * xpnt, int rel_addr, + int rel_size, int type) +{ + int i; + char * strtab; + int reloc_type; + int goof = 0; + struct elf32_sym * symtab; + struct elf32_rel * rpnt; + unsigned int * reloc_addr; + unsigned int symbol_addr; + struct elf_resolve *tpnt; + int symtab_index; + /* Now parse the relocation information */ + + tpnt = xpnt->dyn; + + rpnt = (struct elf32_rel *) (rel_addr + tpnt->loadaddr); + rel_size = rel_size / sizeof(struct elf32_rel); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = ( char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for(i=0; i< rel_size; i++, rpnt++){ + reloc_addr = (int *) (tpnt->loadaddr + (int)rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + if(reloc_type != R_386_COPY) continue; + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + if(!symtab_index && tpnt->libtype == program_interpreter) continue; + if(symtab_index) { + + if(tpnt->libtype == program_interpreter && + _dl_symbol(strtab + symtab[symtab_index].st_name)) + continue; + + symbol_addr = (unsigned int) + _dl_find_hash(strtab + symtab[symtab_index].st_name, + xpnt->next, (int) reloc_addr, NULL, 1); + if(!symbol_addr) { + _dl_fdprintf(2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + goof++; + }; + }; + if (!goof) + _dl_memcpy((char *) symtab[symtab_index].st_value, + (char *) symbol_addr, + symtab[symtab_index].st_size); + }; + return goof; +} + + diff --git a/ldso/ldso/i386/ld_sysdep.h b/ldso/ldso/i386/ld_sysdep.h new file mode 100644 index 000000000..9bbeef1fa --- /dev/null +++ b/ldso/ldso/i386/ld_sysdep.h @@ -0,0 +1,82 @@ + +/* + * Various assmbly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + */ + +/* + * Define this if the system uses RELOCA. + */ +#undef ELF_USES_RELOCA + +/* + * Get a pointer to the argv array. On many platforms this can be just + * the address if the first argument, on other platforms we need to + * do something a little more subtle here. + */ +#define GET_ARGV(ARGVP, ARGS) ARGVP = ((unsigned int*) & ARGS) +/* + * Get the address of the Global offset table. This must be absolute, not + * relative. + */ +#define GET_GOT(X) __asm__("\tmovl %%ebx,%0\n\t" : "=a" (X)) + +/* + * Initialization sequence for a GOT. + */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[2] = (int) _dl_linux_resolve; \ + GOT_BASE[1] = (int) MODULE; \ +} + +/* + * Here is a macro to perform a relocation. This is only used when + * bootstrapping the dynamic loader. RELP is the relocation that we + * are performing, REL is the pointer to the address we are relocating. + * SYMBOL is the symbol involved in the relocation, and LOAD is the + * load address. + */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch(ELF32_R_TYPE((RELP)->r_info)){ \ + case R_386_32: \ + *REL += SYMBOL; \ + break; \ + case R_386_PC32: \ + *REL += SYMBOL - (unsigned int) REL; \ + break; \ + case R_386_GLOB_DAT: \ + case R_386_JMP_SLOT: \ + *REL = SYMBOL; \ + break; \ + case R_386_RELATIVE: \ + *REL += (unsigned int) LOAD; \ + break; \ + default: \ + _dl_exit(1); \ + } + + +/* + * Transfer control to the user's application, once the dynamic loader + * is done. + */ + +#define START() \ + __asm__ volatile ("leave\n\t" \ + "jmp *%%eax\n\t" \ + : "=a" (status) : \ + "d" (_dl_interpreter_exit), "a" (_dl_elf_main)) + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_386 +#define MAGIC2 EM_486 +/* Used for error messages */ +#define ELF_TARGET "386/486" + +extern unsigned int _dl_linux_resolver(int dummy, int i); + +#define do_rem(result, n, base) result = (n % base) diff --git a/ldso/ldso/i386/resolve.S b/ldso/ldso/i386/resolve.S new file mode 100644 index 000000000..db22e7b82 --- /dev/null +++ b/ldso/ldso/i386/resolve.S @@ -0,0 +1,55 @@ +#if 0 +#include <sysdep.h> +#endif +/* + * These are various helper routines that are needed to run an ELF image. + */ +#ifndef ALIGN +#define ALIGN 4 +#endif + +#ifndef NO_UNDERSCORE +#define RUN _linux_run +#define RESOLVE __dl_linux_resolve +#define EXIT __interpreter_exit +#define RESOLVER __dl_linux_resolver +#define INIT ___loader_bootstrap +#else +#define RUN linux_run +#define RESOLVE _dl_linux_resolve +#define RESOLVER _dl_linux_resolver +#define EXIT _interpreter_exit +#define INIT __loader_bootstrap +#endif + +.text +.align ALIGN + .align 16 + +.globl RESOLVE + .type RESOLVE,@function +RESOLVE: + pusha /* preserve all regs */ + lea 0x20(%esp),%eax /* eax = tpnt and reloc_entry params */ + pushl 4(%eax) /* push copy of reloc_entry param */ + pushl (%eax) /* push copy of tpnt param */ + pushl %eax /* _dl_linux_resolver expects a dummy + * param - this could be removed */ +#ifdef __PIC__ + call .L24 +.L24: + popl %ebx + addl $_GLOBAL_OFFSET_TABLE_+[.-.L24],%ebx + movl RESOLVER@GOT(%ebx),%ebx /* eax = resolved func */ + call *%ebx +#else + call RESOLVER +#endif + movl %eax,0x2C(%esp) /* store func addr over original + * tpnt param */ + addl $0xC,%esp /* remove copy parameters */ + popa /* restore regs */ + ret $4 /* jump to func removing original + * reloc_entry param from stack */ +.LFE2: + .size RESOLVE,.LFE2-RESOLVE diff --git a/ldso/ldso/i386/sysdep.h b/ldso/ldso/i386/sysdep.h new file mode 100644 index 000000000..9bbeef1fa --- /dev/null +++ b/ldso/ldso/i386/sysdep.h @@ -0,0 +1,82 @@ + +/* + * Various assmbly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + */ + +/* + * Define this if the system uses RELOCA. + */ +#undef ELF_USES_RELOCA + +/* + * Get a pointer to the argv array. On many platforms this can be just + * the address if the first argument, on other platforms we need to + * do something a little more subtle here. + */ +#define GET_ARGV(ARGVP, ARGS) ARGVP = ((unsigned int*) & ARGS) +/* + * Get the address of the Global offset table. This must be absolute, not + * relative. + */ +#define GET_GOT(X) __asm__("\tmovl %%ebx,%0\n\t" : "=a" (X)) + +/* + * Initialization sequence for a GOT. + */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[2] = (int) _dl_linux_resolve; \ + GOT_BASE[1] = (int) MODULE; \ +} + +/* + * Here is a macro to perform a relocation. This is only used when + * bootstrapping the dynamic loader. RELP is the relocation that we + * are performing, REL is the pointer to the address we are relocating. + * SYMBOL is the symbol involved in the relocation, and LOAD is the + * load address. + */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch(ELF32_R_TYPE((RELP)->r_info)){ \ + case R_386_32: \ + *REL += SYMBOL; \ + break; \ + case R_386_PC32: \ + *REL += SYMBOL - (unsigned int) REL; \ + break; \ + case R_386_GLOB_DAT: \ + case R_386_JMP_SLOT: \ + *REL = SYMBOL; \ + break; \ + case R_386_RELATIVE: \ + *REL += (unsigned int) LOAD; \ + break; \ + default: \ + _dl_exit(1); \ + } + + +/* + * Transfer control to the user's application, once the dynamic loader + * is done. + */ + +#define START() \ + __asm__ volatile ("leave\n\t" \ + "jmp *%%eax\n\t" \ + : "=a" (status) : \ + "d" (_dl_interpreter_exit), "a" (_dl_elf_main)) + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_386 +#define MAGIC2 EM_486 +/* Used for error messages */ +#define ELF_TARGET "386/486" + +extern unsigned int _dl_linux_resolver(int dummy, int i); + +#define do_rem(result, n, base) result = (n % base) diff --git a/ldso/ldso/ld-uClibc.c b/ldso/ldso/ld-uClibc.c new file mode 100644 index 000000000..6b0b864cb --- /dev/null +++ b/ldso/ldso/ld-uClibc.c @@ -0,0 +1,1005 @@ +/* Run an ELF binary on a linux system. + + Copyright (C) 1993-1996, Eric Youngdale. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +/* Program to load an ELF binary on a linux system, and run it. + * References to symbols in sharable libraries can be resolved by + * an ELF sharable library. */ + +/* Disclaimer: I have never seen any AT&T source code for SVr4, nor have + I ever taken any courses on internals. This program was developed using + information available through the book "UNIX SYSTEM V RELEASE 4, + Programmers guide: Ansi C and Programming Support Tools", which did + a more than adequate job of explaining everything required to get this + working. */ + +/* + * The main trick with this program is that initially, we ourselves are not + * dynamicly linked. This means that we cannot access any global variables + * since the GOT is initialized by the linker assuming a virtual address of 0, + * and we cannot call any functions since the PLT is not initialized at all + * (it will tend to want to call the dynamic linker + * + * There are further restrictions - we cannot use large switch statements, + * since the compiler generates tables of addresses and jumps through them. + * We can use inline functions, because these do not transfer control to + * a new address, but they must be static so that they are not exported + * from the modules. We cannot use normal syscall stubs, because these + * all reference the errno global variable which is not yet initialized. + * We can use all of the local stack variables that we want, since these + * are all referenced to %ebp or %esp. + * + * Life is further complicated by the fact that initially we do not want + * to do a complete dynamic linking. We want to allow the user to supply + * new functions replacing some of the library versions, and until we have + * the list of modules that we should search set up, we do not want to do + * any of this. Thus I have chosen to only perform the relocations for + * variables that start with "_dl_" since ANSI specifies that the user is + * not supposed to redefine any of these variables. + * + * Fortunately, the linker itself leaves a few clues lying around, and + * when the kernel starts the image, there are a few further clues. + * First of all, there is information buried on the stack that the kernel + * leaves, which includes information about the load address that the + * program interpreter was loaded at, the number of sections, the address + * the application was loaded at and so forth. Here this information + * is stored in the array dl_info, and the indicies are taken from the + * file /usr/include/sys/auxv.h on any SVr4 system. + * + * The linker itself leaves a pointer to the .dynamic section in the first + * slot of the GOT, and as it turns out, %ebx points to ghe GOT when + * you are using PIC code, so we just dereference this to get the address + * of the dynamic sections. + * + * Typically you must load all text pages as writable so that dynamic linking + * can succeed. The kernel under SVr4 loads these as R/O, so we must call + * mprotect to change the protections. Once we are done, we should set these + * back again, so the desired behavior is achieved. Under linux there is + * currently no mprotect function in the distribution kernel (although + * someone has alpha patches), so for now everything is loaded writable. + * + * We do not have access to malloc and friends at the initial stages of dynamic + * linking, and it would be handy to have some scratchpad memory available + * for use as we set things up. It is a bit of a kluge, but we mmap /dev/zero + * to get one page of scratchpad. A simpleminded _dl_malloc is provided so + * that we have some memory that can be used for this purpose. Typically + * we would not want to use the same memory pool as malloc anyway - the user + * might want to redefine malloc for example. + * + * Our first task is to perform a minimal linking so that we can call other + * portions of the dynamic linker. Once we have done this, we then build + * the list of modules that the application requires, using LD_LIBRARY_PATH + * if this is not a suid program (/usr/lib otherwise). Once this is done, + * we can do the dynamic linking as required (and we must omit the things + * we did to get the dynamic linker up and running in the first place. + * After we have done this, we just have a few housekeeping chores and we + * can transfer control to the user's application. + */ + +#include <stdarg.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/unistd.h> +#include <linux/elf.h> +#include <linux/mman.h> +#include "link.h" + +#include "sysdep.h" +#include "hash.h" +#include "linuxelf.h" +#include "syscall.h" +#include "string.h" + +#include "../config.h" + +#define ALLOW_ZERO_PLTGOT + +static char * _dl_malloc_addr, *_dl_mmap_zero; +char * _dl_library_path = 0; /* Where we look for libraries */ +char *_dl_preload = 0; /* Things to be loaded before the libs. */ +char *_dl_progname = "/lib/ld-linux-uclibc.so.1"; +static char * _dl_not_lazy = 0; +static char * _dl_warn = 0; /* Used by ldd */ +static char * _dl_trace_loaded_objects = 0; +static int (*_dl_elf_main)(int, char **, char**); + +static int (*_dl_elf_init)(void); + +void * (*_dl_malloc_function)(int size) = NULL; + +struct r_debug * _dl_debug_addr = NULL; + +unsigned int * _dl_brkp; + +unsigned int * _dl_envp; + +#define DL_MALLOC(SIZE) ((void *) (malloc_buffer += SIZE, malloc_buffer - SIZE)) +/* + * Make sure that the malloc buffer is aligned on 4 byte boundary. For 64 bit + * platforms we may need to increase this to 8, but this is good enough for + * now. This is typically called after DL_MALLOC. + */ +#define REALIGN() malloc_buffer = (char *) (((unsigned int) malloc_buffer + 3) & ~(3)) + + + +#define ELF_HASH(RESULT,NAME) { \ + unsigned long hash = 0; \ + unsigned long tmp; \ + char * name = NAME; \ + while (*name){ \ + hash = (hash << 4) + *name++; \ + if((tmp = hash & 0xf0000000)) hash ^= tmp >> 24; \ + hash &= ~tmp; \ + } \ + RESULT = hash; \ +} +extern int _dl_linux_resolve(void); +extern int _dl_interpreter_exit(int); +extern char * _dl_strdup(const char *); +extern char * _dl_getenv(char * symbol, char ** envp); +extern void _dl_unsetenv(char * symbol, char ** envp); +extern int _dl_fixup(struct elf_resolve * tpnt); + +/* + * Datatype of a relocation on this platform + */ +#ifdef ELF_USES_RELOCA +typedef struct elf32_rela ELF_RELOC; +#else +typedef struct elf32_rel ELF_RELOC; +#endif + +/* + * This stub function is used by some debuggers. The idea is that they + * can set an internal breakpoint on it, so that we are notified when the + * address mapping is changed in some way. + */ +void _dl_debug_state() +{ + return; +} + +void _dl_boot(int args); + +void _dl_boot(int args){ + unsigned int argc; + char ** argv, ** envp; + int status; + + unsigned int load_addr; + unsigned int * got; + unsigned int * aux_dat; + int goof = 0; + struct elfhdr * header; + struct elf_resolve * tpnt; + struct dyn_elf * rpnt; + struct elf_resolve * app_tpnt; + unsigned int brk_addr; + unsigned int dl_data[AT_EGID+1]; + unsigned char * malloc_buffer, *mmap_zero; + int (*_dl_atexit)(void *); + int * lpnt; + struct dynamic * dpnt; + unsigned int *hash_addr; + struct r_debug * debug_addr; + unsigned int *chains; + int indx; + int _dl_secure; + + /* First obtain the information on the stack that tells us more about + what binary is loaded, where it is loaded, etc, etc */ + + GET_ARGV(aux_dat, args); + argc = *(aux_dat - 1); + argv = (char **) aux_dat; + aux_dat += argc; /* Skip over the argv pointers */ + aux_dat++; /* Skip over NULL at end of argv */ + envp = (char **) aux_dat; + while(*aux_dat) aux_dat++; /* Skip over the envp pointers */ + aux_dat++; /* Skip over NULL at end of envp */ + dl_data[AT_UID] = -1; /* check later to see if it is changed */ + while(*aux_dat) + { + unsigned int * ad1; + ad1 = aux_dat + 1; + if( *aux_dat <= AT_EGID ) dl_data[*aux_dat] = *ad1; + aux_dat += 2; + } + + /* Next, locate the GOT */ + + load_addr = dl_data[AT_BASE]; + + GET_GOT(got); + dpnt = (struct dynamic *) (*got + load_addr); + + /* OK, time for another hack. Now call mmap to get a page of writable + memory that can be used for a temporary malloc. We do not know brk + yet, so we cannot use real malloc. */ + + { + /* This hack is to work around a suspected asm bug in gcc-2.7.0 */ + int zfileno; +#define ZFILENO ((-1 & (~zfileno)) | zfileno) +/*#define ZFILENO -1*/ + +#ifndef MAP_ANONYMOUS +#ifdef __sparc__ +#define MAP_ANONYMOUS 0x20 +#else +#error MAP_ANONYMOUS not defined and suplementary value not known +#endif +#endif + + /* See if we need to relocate this address */ + mmap_zero = malloc_buffer = (unsigned char *) _dl_mmap((void*) 0, 4096, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, ZFILENO, 0); + if(_dl_mmap_check_error(mmap_zero)) { + SEND_STDERR("dl_boot: mmap of /dev/zero failed!\n"); + _dl_exit(13); + } + } + + tpnt = DL_MALLOC(sizeof(struct elf_resolve)); + REALIGN(); + _dl_memset (tpnt, 0, sizeof (*tpnt)); + app_tpnt = DL_MALLOC(sizeof(struct elf_resolve)); + REALIGN(); + _dl_memset (app_tpnt, 0, sizeof (*app_tpnt)); + + /* + * This is used by gdb to locate the chain of shared libraries that are currently loaded. + */ + debug_addr = DL_MALLOC(sizeof(struct r_debug)); + REALIGN(); + _dl_memset (debug_addr, 0, sizeof (*debug_addr)); + + /* OK, that was easy. Next scan the DYNAMIC section of the image. + We are only doing ourself right now - we will have to do the rest later */ + + while(dpnt->d_tag) + { + tpnt->dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val; + if(dpnt->d_tag == DT_TEXTREL || + SVR4_BUGCOMPAT) tpnt->dynamic_info[DT_TEXTREL] = 1; + dpnt++; + } + + { + struct elf_phdr * ppnt; + int i; + + ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + for(i=0; i<dl_data[AT_PHNUM]; i++, ppnt++) + if(ppnt->p_type == PT_DYNAMIC) { + dpnt = (struct dynamic *) ppnt->p_vaddr; + while(dpnt->d_tag) + { + if(dpnt->d_tag > DT_JMPREL) {dpnt++; continue; } + app_tpnt->dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val; + if(dpnt->d_tag == DT_DEBUG) dpnt->d_un.d_val = (int) debug_addr; + if(dpnt->d_tag == DT_TEXTREL || + SVR4_BUGCOMPAT) app_tpnt->dynamic_info[DT_TEXTREL] = 1; + dpnt++; + } + } + } + + /* Get some more of the information that we will need to dynamicly link + this module to itself */ + + hash_addr = (unsigned int *) (tpnt->dynamic_info[DT_HASH]+load_addr); + tpnt->nbucket = *hash_addr++; + tpnt->nchain = *hash_addr++; + tpnt->elf_buckets = hash_addr; + hash_addr += tpnt->nbucket; + chains = hash_addr; + + /* Ugly, ugly. We need to call mprotect to change the protection of + the text pages so that we can do the dynamic linking. We can set the + protection back again once we are done */ + + { + struct elf_phdr * ppnt; + int i; + + /* First cover the shared library/dynamic linker. */ + if(tpnt->dynamic_info[DT_TEXTREL]) { + header = (struct elfhdr *) dl_data[AT_BASE]; + ppnt = (struct elf_phdr *) (dl_data[AT_BASE] + header->e_phoff); + for(i=0; i<header->e_phnum ; i++, ppnt++) { + if(ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W)) + _dl_mprotect((void *) (load_addr + (ppnt->p_vaddr & 0xfffff000)), + (ppnt->p_vaddr & 0xfff) + (unsigned int) ppnt->p_filesz, + PROT_READ | PROT_WRITE | PROT_EXEC); + } + } + + /* Now cover the application program. */ + if(app_tpnt->dynamic_info[DT_TEXTREL]) { + ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + for(i=0; i<dl_data[AT_PHNUM]; i++, ppnt++) { + if(ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W)) + _dl_mprotect((void *) (ppnt->p_vaddr & 0xfffff000), + (ppnt->p_vaddr & 0xfff) + (unsigned int) ppnt->p_filesz, + PROT_READ | PROT_WRITE | PROT_EXEC); + } + } + } + + /* OK, now do the relocations. We do not do a lazy binding here, so + that once we are done, we have considerably more flexibility. */ + + goof = 0; + for(indx=0; indx < 2; indx++) + { + int i; + ELF_RELOC * rpnt; + unsigned int * reloc_addr; + unsigned int symbol_addr; + int symtab_index; + unsigned int rel_addr, rel_size; + + +#ifdef ELF_USES_RELOCA + rel_addr = (indx ? tpnt->dynamic_info[DT_JMPREL] : tpnt->dynamic_info[DT_RELA]); + rel_size = (indx ? tpnt->dynamic_info[DT_PLTRELSZ] : tpnt->dynamic_info[DT_RELASZ]); +#else + rel_addr = (indx ? tpnt->dynamic_info[DT_JMPREL] : tpnt->dynamic_info[DT_REL]); + rel_size = (indx ? tpnt->dynamic_info[DT_PLTRELSZ] : tpnt->dynamic_info[DT_RELSZ]); +#endif + + + if(!rel_addr) continue; + + /* Now parse the relocation information */ + rpnt = (ELF_RELOC *) (rel_addr + load_addr); + for(i=0; i< rel_size; i+=sizeof(ELF_RELOC), rpnt++){ + reloc_addr = (int *) (load_addr + (int)rpnt->r_offset); + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + if(symtab_index) { + char * strtab; + struct elf32_sym * symtab; + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB]+load_addr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]+load_addr); + + /* We only do a partial dynamic linking right now. The user + is not supposed to redefine any symbols that start with + a '_', so we can do this with confidence. */ + + if (!_dl_symbol(strtab + symtab[symtab_index].st_name)) continue; + + symbol_addr = load_addr + symtab[symtab_index].st_value; + + if(!symbol_addr) { + /* + * This will segfault - you cannot call a function until + * we have finished the relocations. + */ + SEND_STDERR("ELF dynamic loader - unable to self-bootstrap - symbol "); + SEND_STDERR(strtab + symtab[symtab_index].st_name); + SEND_STDERR(" undefined.\n"); + goof++; + } + } + /* + * Use this machine-specific macro to perform the actual relocation. + */ + PERFORM_BOOTSTRAP_RELOC(rpnt, reloc_addr, symbol_addr, load_addr); + } + } + + if (goof) _dl_exit(14); + + /* OK, at this point we have a crude malloc capability. Start to build + the tables of the modules that are required for this beast to run. + We start with the basic executable, and then go from there. Eventually + we will run across ourself, and we will need to properly deal with that + as well. */ + + _dl_malloc_addr = malloc_buffer; + + _dl_mmap_zero = mmap_zero; +/* tpnt = _dl_malloc(sizeof(struct elf_resolve)); */ + +/* Now we have done the mandatory linking of some things. We are now + free to start using global variables, since these things have all been + fixed up by now. Still no function calls outside of this library , + since the dynamic resolver is not yet ready. */ + + lpnt = (int *) (tpnt->dynamic_info[DT_PLTGOT] + load_addr); + INIT_GOT(lpnt, tpnt); + + /* OK, this was a big step, now we need to scan all of the user images + and load them properly. */ + + tpnt->next = 0; + tpnt->libname = 0; + tpnt->libtype = program_interpreter; + + { struct elfhdr * epnt; + struct elf_phdr * ppnt; + int i; + + epnt = (struct elfhdr *) dl_data[AT_BASE]; + tpnt->n_phent = epnt->e_phnum; + tpnt->ppnt = ppnt = (struct elf_phdr *) (load_addr + epnt->e_phoff); + for(i=0;i < epnt->e_phnum; i++, ppnt++){ + if(ppnt->p_type == PT_DYNAMIC) { + tpnt->dynamic_addr = ppnt->p_vaddr + load_addr; + tpnt->dynamic_size = ppnt->p_filesz; + } + } + } + + tpnt->chains = chains; + tpnt->loadaddr = (char *) load_addr; + + brk_addr = 0; + rpnt = NULL; + + /* At this point we are now free to examine the user application, + and figure out which libraries are supposed to be called. Until + we have this list, we will not be completely ready for dynamic linking */ + + { + struct elf_phdr * ppnt; + int i; + + ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + for(i=0; i<dl_data[AT_PHNUM]; i++, ppnt++) { + if(ppnt->p_type == PT_LOAD) { + if(ppnt->p_vaddr + ppnt->p_memsz > brk_addr) + brk_addr = ppnt->p_vaddr + ppnt->p_memsz; + } + if(ppnt->p_type == PT_DYNAMIC) { +#ifndef ALLOW_ZERO_PLTGOT + /* make sure it's really there. */ + if (app_tpnt->dynamic_info[DT_PLTGOT] == 0) continue; +#endif + /* OK, we have what we need - slip this one into the list. */ + app_tpnt = _dl_add_elf_hash_table("", 0, + app_tpnt->dynamic_info, ppnt->p_vaddr, ppnt->p_filesz); + _dl_loaded_modules->libtype = elf_executable; + _dl_loaded_modules->ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + _dl_loaded_modules->n_phent = dl_data[AT_PHNUM]; + _dl_symbol_tables = rpnt = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt, 0, sizeof (*rpnt)); + rpnt->dyn = _dl_loaded_modules; + app_tpnt->usage_count++; + app_tpnt->symbol_scope = _dl_symbol_tables; + lpnt = (int *) (app_tpnt->dynamic_info[DT_PLTGOT]); +#ifdef ALLOW_ZERO_PLTGOT + if (lpnt) +#endif + INIT_GOT(lpnt, _dl_loaded_modules); + } + if(ppnt->p_type == PT_INTERP) { /* OK, fill this in - we did not have + this before */ + tpnt->libname = _dl_strdup((char *) ppnt->p_offset +(dl_data[AT_PHDR] & 0xfffff000)); + } + } + } + + if (argv[0]) + _dl_progname = argv[0]; + + /* Now we need to figure out what kind of options are selected. + Note that for SUID programs we ignore the settings in LD_LIBRARY_PATH */ + { + _dl_not_lazy = _dl_getenv("LD_BIND_NOW",envp); + + if ( (dl_data[AT_UID] == -1 && _dl_suid_ok()) || + (dl_data[AT_UID] != -1 && dl_data[AT_UID] == dl_data[AT_EUID] && + dl_data[AT_GID] == dl_data[AT_EGID])) + { + _dl_secure = 0; + _dl_preload = _dl_getenv("LD_PRELOAD", envp); + _dl_library_path = _dl_getenv("LD_LIBRARY_PATH",envp); + } + else + { + _dl_secure = 1; + _dl_preload = _dl_getenv("LD_PRELOAD", envp); + _dl_unsetenv("LD_AOUT_PRELOAD", envp); + _dl_unsetenv("LD_LIBRARY_PATH", envp); + _dl_unsetenv("LD_AOUT_LIBRARY_PATH", envp); + _dl_library_path = NULL; + } + } + + _dl_trace_loaded_objects = _dl_getenv("LD_TRACE_LOADED_OBJECTS", envp); + + /* OK, we now have the application in the list, and we have some + basic stuff in place. Now search through the list for other shared + libraries that should be loaded, and insert them on the list in the + correct order. */ + +#ifdef USE_CACHE + _dl_map_cache(); +#endif + + { + struct elf_resolve *tcurr; + struct elf_resolve *tpnt1; + char *lpnt; + + if (_dl_preload) { + char c, *str, *str2; + + str = _dl_preload; + while (*str == ':' || *str == ' ' || *str == '\t') + str++; + while (*str) { + str2 = str; + while (*str2 && *str2 != ':' && *str2 != ' ' && *str2 != '\t') + str2++; + c = *str2; + *str2 = '\0'; + if (!_dl_secure || _dl_strchr(str, '/') == NULL) { + tpnt1 = _dl_load_shared_library(_dl_secure, NULL, str); + if (!tpnt1) { + if (_dl_trace_loaded_objects) + _dl_fdprintf(1, "\t%s => not found\n", str); + else { + _dl_fdprintf(2, "%s: can't load library '%s'\n", + _dl_progname, str); + _dl_exit(15); + } + } else { + if (_dl_trace_loaded_objects && !tpnt1->usage_count) { + /* this is a real hack to make ldd not print the + library itself when run on a library. */ + if (_dl_strcmp(_dl_progname, str) != 0) + _dl_fdprintf(1, "\t%s => %s (0x%x)\n", str, tpnt1->libname, + (unsigned)tpnt1->loadaddr); + } + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + tpnt1->usage_count++; + tpnt1->symbol_scope = _dl_symbol_tables; + tpnt1->libtype = elf_lib; + rpnt->dyn = tpnt1; + } + } + *str2 = c; + str = str2; + while (*str == ':' || *str == ' ' || *str == '\t') + str++; + } + } + + { + int fd; + struct kernel_stat st; + char *preload; + + if (!_dl_stat(LDSO_PRELOAD, &st)) { + if ((fd = _dl_open(LDSO_PRELOAD, O_RDONLY)) < 0) { + _dl_fdprintf(2, "%s: can't open file '%s'\n", _dl_progname, + LDSO_PRELOAD); + } else { + preload = (caddr_t)_dl_mmap(0, st.st_size+1, PROT_READ|PROT_WRITE, + MAP_PRIVATE, fd, 0); + _dl_close (fd); + if (preload == (caddr_t)-1) { + _dl_fdprintf(2, "%s: can't map file '%s'\n", _dl_progname, + LDSO_PRELOAD); + } else { + char c, *cp, *cp2; + + /* convert all separators and comments to spaces */ + for (cp = preload; *cp; /*nada*/) { + if (*cp == ':' || *cp == '\t' || *cp == '\n') { + *cp++ = ' '; + } else if (*cp == '#') { + do + *cp++ = ' '; + while (*cp != '\n' && *cp != '\0'); + } else { + cp++; + } + } + + /* find start of first library */ + for (cp = preload; *cp && *cp == ' '; cp++) + /*nada*/; + + while (*cp) { + /* find end of library */ + for (cp2 = cp; *cp && *cp != ' '; cp++) + /*nada*/; + c = *cp; + *cp = '\0'; + + tpnt1 = _dl_load_shared_library(0, NULL, cp2); + if (!tpnt1) { + if (_dl_trace_loaded_objects) + _dl_fdprintf(1, "\t%s => not found\n", cp2); + else { + _dl_fdprintf(2, "%s: can't load library '%s'\n", + _dl_progname, cp2); + _dl_exit(15); + } + } else { + if (_dl_trace_loaded_objects && !tpnt1->usage_count) + _dl_fdprintf(1, "\t%s => %s (0x%x)\n", cp2, tpnt1->libname, + (unsigned)tpnt1->loadaddr); + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + tpnt1->usage_count++; + tpnt1->symbol_scope = _dl_symbol_tables; + tpnt1->libtype = elf_lib; + rpnt->dyn = tpnt1; + } + + /* find start of next library */ + *cp = c; + for (/*nada*/; *cp && *cp == ' '; cp++) + /*nada*/; + } + + _dl_munmap(preload, st.st_size+1); + } + } + } + } + + for (tcurr = _dl_loaded_modules; tcurr; tcurr = tcurr->next) + { + for (dpnt = (struct dynamic *)tcurr->dynamic_addr; dpnt->d_tag; dpnt++) + { + if(dpnt->d_tag == DT_NEEDED) + { + lpnt = tcurr->loadaddr + tcurr->dynamic_info[DT_STRTAB] + + dpnt->d_un.d_val; + if (tpnt && _dl_strcmp(lpnt, tpnt->libname) == 0) + { + struct elf_resolve * ttmp; + ttmp = _dl_loaded_modules; + while (ttmp->next) + ttmp = ttmp->next; + ttmp->next = tpnt; + tpnt->prev = ttmp; + tpnt->next = NULL; + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + rpnt->dyn = tpnt; + tpnt->usage_count++; + tpnt->symbol_scope = _dl_symbol_tables; + tpnt = NULL; + continue; + } + if (!(tpnt1 = _dl_load_shared_library(0, tcurr, lpnt))) + { + if (_dl_trace_loaded_objects) + _dl_fdprintf(1, "\t%s => not found\n", lpnt); + else + { + _dl_fdprintf(2, "%s: can't load library '%s'\n", + _dl_progname, lpnt); + _dl_exit(16); + } + } + else + { + if (_dl_trace_loaded_objects && !tpnt1->usage_count) + _dl_fdprintf(1, "\t%s => %s (0x%x)\n", lpnt, tpnt1->libname, + (unsigned)tpnt1->loadaddr); + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + tpnt1->usage_count++; + tpnt1->symbol_scope = _dl_symbol_tables; + tpnt1->libtype = elf_lib; + rpnt->dyn = tpnt1; + } + } + } + } + } + +#ifdef USE_CACHE + _dl_unmap_cache(); +#endif + + /* ldd uses uses this. I am not sure how you pick up the other flags */ + if(_dl_trace_loaded_objects) + { + _dl_warn = _dl_getenv("LD_WARN", envp); + if (!_dl_warn) _dl_exit(0); + } + + /* + * If the program interpreter is not in the module chain, add it. This will + * be required for dlopen to be able to access the internal functions in the + * dynamic linker. + */ + if(tpnt) { + struct elf_resolve * tcurr; + + tcurr = _dl_loaded_modules; + if (tcurr) + while(tcurr->next) tcurr = tcurr->next; + tpnt->next = NULL; + tpnt->usage_count++; + + if (tcurr) { + tcurr->next = tpnt; + tpnt->prev = tcurr; + } + else { + _dl_loaded_modules = tpnt; + tpnt->prev = NULL; + } + if (rpnt) { + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + } else { + rpnt = (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt, 0, sizeof (*(rpnt->next))); + } + rpnt->dyn = tpnt; + tpnt = NULL; + } + + /* + * OK, now all of the kids are tucked into bed in their proper addresses. + * Now we go through and look for REL and RELA records that indicate fixups + * to the GOT tables. We need to do this in reverse order so that COPY + * directives work correctly */ + + + goof = _dl_loaded_modules ? _dl_fixup(_dl_loaded_modules) : 0; + + + /* Some flavors of SVr4 do not generate the R_*_COPY directive, + and we have to manually search for entries that require fixups. + Solaris gets this one right, from what I understand. */ + + + if (_dl_symbol_tables) + goof += _dl_copy_fixups(_dl_symbol_tables); + + if(goof || _dl_trace_loaded_objects) _dl_exit(0); + + /* OK, at this point things are pretty much ready to run. Now we + need to touch up a few items that are required, and then + we can let the user application have at it. Note that + the dynamic linker itself is not guaranteed to be fully + dynamicly linked if we are using ld.so.1, so we have to look + up each symbol individually. */ + + + _dl_brkp = (unsigned int *) _dl_find_hash("___brk_addr", NULL, 1, NULL, 0); + if (_dl_brkp) *_dl_brkp = brk_addr; + _dl_envp = (unsigned int *) _dl_find_hash("__environ", NULL, 1, NULL, 0); + + if (_dl_envp) *_dl_envp = (unsigned int) envp; + + { + int i; + struct elf_phdr * ppnt; + + /* We had to set the protections of all pages to R/W for dynamic linking. + Set text pages back to R/O */ + for(tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) + for(ppnt = tpnt->ppnt, i=0; i < tpnt->n_phent; i++, ppnt++) + if(ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W) && + tpnt->dynamic_info[DT_TEXTREL]) + _dl_mprotect((void *) (tpnt->loadaddr + (ppnt->p_vaddr & 0xfffff000)), + (ppnt->p_vaddr & 0xfff) + (unsigned int) ppnt->p_filesz, + LXFLAGS(ppnt->p_flags)); + + } + + _dl_atexit = (int (*)(void *)) _dl_find_hash("atexit", NULL, 1, NULL, 0); + + /* + * OK, fix one more thing - set up the debug_addr structure to point + * to our chain. Later we may need to fill in more fields, but this + * should be enough for now. + */ + debug_addr->r_map = (struct link_map *) _dl_loaded_modules; + debug_addr->r_version = 1; + debug_addr->r_ldbase = load_addr; + debug_addr->r_brk = (unsigned long) &_dl_debug_state; + _dl_debug_addr = debug_addr; + debug_addr->r_state = RT_CONSISTENT; + /* This is written in this funny way to keep gcc from inlining the + function call. */ + ((void (*)(void))debug_addr->r_brk)(); + + for(tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) + { + /* Apparently crt1 for the application is responsible for handling this. + * We only need to run the init/fini for shared libraries + */ + if (tpnt->libtype == program_interpreter || + tpnt->libtype == elf_executable) continue; + if (tpnt->init_flag & INIT_FUNCS_CALLED) continue; + tpnt->init_flag |= INIT_FUNCS_CALLED; + + if(tpnt->dynamic_info[DT_INIT]) { + _dl_elf_init = (int (*)(void)) (tpnt->loadaddr + + tpnt->dynamic_info[DT_INIT]); + (*_dl_elf_init)(); + } + if(_dl_atexit && tpnt->dynamic_info[DT_FINI]) + { + (*_dl_atexit)(tpnt->loadaddr + tpnt->dynamic_info[DT_FINI]); + } +#undef DL_DEBUG +#ifdef DL_DEBUG + else + { + _dl_fdprintf(2, tpnt->libname); + _dl_fdprintf(2, ": "); + if (!_dl_atexit) + _dl_fdprintf(2, "The address is atexit () is 0x0."); + if (!tpnt->dynamic_info[DT_FINI]) + _dl_fdprintf(2, "Invalid .fini section."); + _dl_fdprintf(2, "\n"); + } +#endif +#undef DL_DEBUG + } + + /* OK we are done here. Turn out the lights, and lock up. */ + _dl_elf_main = (int (*)(int, char**, char**)) dl_data[AT_ENTRY]; + + + /* + * Transfer control to the application. + */ + START(); +} + +int _dl_fixup(struct elf_resolve * tpnt) +{ + int goof = 0; + if(tpnt->next) goof += _dl_fixup(tpnt->next); + + if(tpnt->dynamic_info[DT_REL]) { +#ifdef ELF_USES_RELOCA + _dl_fdprintf(2, "%s: can't handle REL relocation records\n", _dl_progname); + _dl_exit(17); +#else + if (tpnt->init_flag & RELOCS_DONE) return goof; + tpnt->init_flag |= RELOCS_DONE; + + goof += _dl_parse_relocation_information(tpnt, tpnt->dynamic_info[DT_REL], + tpnt->dynamic_info[DT_RELSZ], 0); +#endif + } + if(tpnt->dynamic_info[DT_RELA]) { +#ifdef ELF_USES_RELOCA + if (tpnt->init_flag & RELOCS_DONE) return goof; + tpnt->init_flag |= RELOCS_DONE; + + goof += _dl_parse_relocation_information(tpnt, tpnt->dynamic_info[DT_RELA], + tpnt->dynamic_info[DT_RELASZ], 0); +#else + _dl_fdprintf(2, "%s: can't handle RELA relocation records\n", _dl_progname); + _dl_exit(18); +#endif + } + if(tpnt->dynamic_info[DT_JMPREL]) + { + if (tpnt->init_flag & JMP_RELOCS_DONE) return goof; + tpnt->init_flag |= JMP_RELOCS_DONE; + + if(! _dl_not_lazy || *_dl_not_lazy == 0) + _dl_parse_lazy_relocation_information(tpnt, tpnt->dynamic_info[DT_JMPREL], + tpnt->dynamic_info[DT_PLTRELSZ], 0); + else + goof += _dl_parse_relocation_information(tpnt, + tpnt->dynamic_info[DT_JMPREL], + tpnt->dynamic_info[DT_PLTRELSZ], 0); + } + return goof; +} + +void * _dl_malloc(int size) { + void * retval; + + if(_dl_malloc_function) + return (*_dl_malloc_function)(size); + + if(_dl_malloc_addr-_dl_mmap_zero+size>4096) { + _dl_mmap_zero = _dl_malloc_addr = (unsigned char *) _dl_mmap((void*) 0, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if(_dl_mmap_check_error(_dl_mmap_zero)) { + _dl_fdprintf(2, "%s: can't map '/dev/zero'\n", _dl_progname); + _dl_exit(20); + } + } + retval = _dl_malloc_addr; + _dl_malloc_addr += size; + + /* + * Align memory to 4 byte boundary. Some platforms require this, others + * simply get better performance. + */ + _dl_malloc_addr = (char *) (((unsigned int) _dl_malloc_addr + 3) & ~(3)); + return retval; +} + +char * _dl_getenv(char *symbol, char **envp) +{ + char *pnt; + char *pnt1; + while ((pnt = *envp++)) { + pnt1 = symbol; + while (*pnt && *pnt == *pnt1) + pnt1++, pnt++; + if (!*pnt || *pnt != '=' || *pnt1) + continue; + return pnt+1; + } + return 0; +} + +void _dl_unsetenv(char *symbol, char **envp) +{ + char *pnt; + char *pnt1; + char **newenvp = envp; + for (pnt = *envp; pnt; pnt = *++envp) { + pnt1 = symbol; + while (*pnt && *pnt == *pnt1) + pnt1++, pnt++; + if(!*pnt || *pnt != '=' || *pnt1) + *newenvp++ = *envp; + } + *newenvp++ = *envp; + return; +} + +char * _dl_strdup(const char * string){ + char * retval; + int len; + + len = _dl_strlen(string); + retval = _dl_malloc(len + 1); + _dl_strcpy(retval, string); + return retval; +} + +/* In principle we could do the .fini stuff here, but we already + registered this stuff with atexit */ +int _dl_interpreter_exit(int exitcode){ +/* _dl_fdprintf(2, "Hey, look where I am!\n"); */ + return 0; +} diff --git a/ldso/ldso/ld_hash.h b/ldso/ldso/ld_hash.h new file mode 100644 index 000000000..2eeda2d46 --- /dev/null +++ b/ldso/ldso/ld_hash.h @@ -0,0 +1,113 @@ +#include "link.h" + +#ifndef RTLD_NEXT +#define RTLD_NEXT ((void*)-1) +#endif + +struct dyn_elf{ + unsigned int flags; + struct elf_resolve * dyn; + struct dyn_elf * next_handle; /* Used by dlopen et al. */ + struct dyn_elf * next; +}; + +struct elf_resolve{ + /* These entries must be in this order to be compatible with the interface used + by gdb to obtain the list of symbols. */ + char * loadaddr; + char * libname; + unsigned int dynamic_addr; + struct elf_resolve * next; + struct elf_resolve * prev; + /* Nothing after this address is used by gdb. */ + enum {elf_lib, elf_executable,program_interpreter, loaded_file} libtype; + struct dyn_elf * symbol_scope; + unsigned short usage_count; + unsigned short int init_flag; + unsigned int nbucket; + unsigned int * elf_buckets; + /* + * These are only used with ELF style shared libraries + */ + unsigned int nchain; + unsigned int * chains; + unsigned int dynamic_info[24]; + + unsigned int dynamic_size; + unsigned int n_phent; + struct elf_phdr * ppnt; +}; + +#if 0 +/* + * The DT_DEBUG entry in the .dynamic section is given the address of this structure. + * gdb can pick this up to obtain the correct list of loaded modules. + */ + +struct r_debug{ + int r_version; + struct elf_resolve * link_map; + unsigned long brk_fun; + enum {RT_CONSISTENT, RT_ADD, RT_DELETE}; + unsigned long ldbase; +}; +#endif + +#define COPY_RELOCS_DONE 1 +#define RELOCS_DONE 2 +#define JMP_RELOCS_DONE 4 +#define INIT_FUNCS_CALLED 8 + +extern struct dyn_elf * _dl_symbol_tables; +extern struct elf_resolve * _dl_loaded_modules; +extern struct dyn_elf * _dl_handles; + +extern struct elf_resolve * _dl_check_hashed_files(char * libname); +extern struct elf_resolve * _dl_add_elf_hash_table(char * libname, + char * loadaddr, + unsigned int * dynamic_info, + unsigned int dynamic_addr, + unsigned int dynamic_size); +extern char * _dl_find_hash(char * name, struct dyn_elf * rpnt1, + unsigned int instr_addr, + struct elf_resolve * f_tpnt, + int copyrel); + +extern int _dl_linux_dynamic_link(void); + +#ifdef __mc68000__ +/* On m68k constant strings are referenced through the GOT. */ +/* XXX Requires load_addr to be defined. */ +#define SEND_STDERR(X) \ + { const char *__s = (X); \ + if (__s < (const char *) load_addr) __s += load_addr; \ + _dl_write (2, __s, _dl_strlen (__s)); \ + } +#else +#define SEND_STDERR(X) _dl_write(2, X, _dl_strlen(X)); +#endif +extern int _dl_fdprintf(int, const char *, ...); +extern char * _dl_library_path; +extern char * _dl_not_lazy; +extern char * _dl_strdup(const char *); +extern inline int _dl_symbol(char * name); +unsigned long _dl_elf_hash(const char * name); + +extern inline int _dl_symbol(char * name) +{ + if(name[0] != '_' || name[1] != 'd' || name[2] != 'l' || name[3] != '_') + return 0; + return 1; +} + +#define DL_ERROR_NOFILE 1 +#define DL_ERROR_NOZERO 2 +#define DL_ERROR_NOTELF 3 +#define DL_ERROR_NOTMAGIC 4 +#define DL_ERROR_NOTDYN 5 +#define DL_ERROR_MMAP_FAILED 6 +#define DL_ERROR_NODYNAMIC 7 +#define DL_WRONG_RELOCS 8 +#define DL_BAD_HANDLE 9 +#define DL_NO_SYMBOL 10 + diff --git a/ldso/ldso/ld_string.h b/ldso/ldso/ld_string.h new file mode 100644 index 000000000..1ea8fd7ae --- /dev/null +++ b/ldso/ldso/ld_string.h @@ -0,0 +1,112 @@ +#ifndef _LINUX_STRING_H_ +#define _LINUX_STRING_H_ + +#include <linux/types.h> /* for size_t */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +extern inline char * _dl_strcpy(char * dst,const char *src) +{ + register char *ptr = dst; + + while (*src) + *dst++ = *src++; + *dst = '\0'; + + return ptr; +} + +extern inline int _dl_strcmp(const char * s1,const char * s2) +{ + unsigned register char c1, c2; + + do { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0') + return c1 - c2; + } + while (c1 == c2); + + return c1 - c2; +} + +extern inline int _dl_strncmp(const char * s1,const char * s2,size_t len) +{ + unsigned register char c1 = '\0'; + unsigned register char c2 = '\0'; + + while (len > 0) { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + len--; + } + + return c1 - c2; +} + +extern inline char * _dl_strchr(const char * str,int c) +{ + register char ch; + + do { + if ((ch = *str) == c) + return (char *) str; + str++; + } + while (ch); + + return 0; +} + + +extern inline size_t _dl_strlen(const char * str) +{ + register char *ptr = (char *) str; + + while (*ptr) + ptr++; + return (ptr - str); +} + +extern inline void * _dl_memcpy(void * dst, const void * src, size_t len) +{ + register char *a = dst; + register const char *b = src; + + while (len--) + *a++ = *b++; + + return dst; +} + + +extern inline int _dl_memcmp(const void * s1,const void * s2,size_t len) +{ + unsigned char *c1 = (unsigned char *)s1; + unsigned char *c2 = (unsigned char *)s2; + + while (len--) { + if (*c1 != *c2) + return *c1 - *c2; + c1++; + c2++; + } + return 0; +} + +extern inline void * _dl_memset(void * str,int c,size_t len) +{ + register char *a = str; + + while (len--) + *a++ = c; + + return str; +} + +#endif diff --git a/ldso/ldso/ld_syscall.h b/ldso/ldso/ld_syscall.h new file mode 100644 index 000000000..3cf244338 --- /dev/null +++ b/ldso/ldso/ld_syscall.h @@ -0,0 +1,108 @@ +#include <linux/types.h> +#include <asm/unistd.h> + +#ifndef _dl_MAX_ERRNO +#define _dl_MAX_ERRNO 4096 +#endif + +#define _dl_mmap_check_error(__res) \ + (((int)__res) < 0 && ((int)__res) >= -_dl_MAX_ERRNO) + + +/* Here are the definitions for some syscalls that are used + by the dynamic linker. The idea is that we want to be able + to call these before the errno symbol is dynamicly linked, so + we use our own version here. Note that we cannot assume any + dynamic linking at all, so we cannot return any error codes. + We just punt if there is an error. */ + +/* Do not include unistd.h, so gcc doesn't whine about + * _exit returning. It really doesn't return... */ +#define __NR__dl_exit __NR_exit +static inline _syscall1(void, _dl_exit, int, status); + + +#define __NR__dl_close __NR_close +static inline _syscall1(int, _dl_close, int, fd); + + +#define __NR__dl_mmap_real __NR_mmap +static inline _syscall1(void *, _dl_mmap_real, unsigned long *, buffer); + +static inline void * _dl_mmap(void * addr, unsigned long size, int prot, + int flags, int fd, unsigned long offset) +{ + unsigned long buffer[6]; + + buffer[0] = (unsigned long) addr; + buffer[1] = (unsigned long) size; + buffer[2] = (unsigned long) prot; + buffer[3] = (unsigned long) flags; + buffer[4] = (unsigned long) fd; + buffer[5] = (unsigned long) offset; + return (void *) _dl_mmap_real(buffer); +} + +#define __NR__dl_open __NR_open +static inline _syscall2(int, _dl_open, const char *, fn, int, flags); + +#define __NR__dl_write __NR_write +static inline _syscall3(unsigned long, _dl_write, int, fd, + const void *, buf, unsigned long, count); + + +#define __NR__dl_read __NR_read +static inline _syscall3(unsigned long, _dl_read, int, fd, + const void *, buf, unsigned long, count); + +#define __NR__dl_mprotect __NR_mprotect +static inline _syscall3(int, _dl_mprotect, const void *, addr, unsigned long, len, int, prot); + + + +/* Pull in whatever this particular arch's kernel thinks the kernel version of + * struct stat should look like. It turns out that each arch has a different + * opinion on the subject, and different kernel revs use different names... */ +#define __NR__dl_stat __NR_stat +#define stat kernel_stat +#define new_stat kernel_stat +#include <asm/stat.h> +#undef new_stat +#undef stat +static inline _syscall2(int, _dl_stat, const char *, file_name, struct kernel_stat *, buf); + + +#define __NR__dl_munmap __NR_munmap +static inline _syscall2(int, _dl_munmap, void *, start, unsigned long, length); + +#define __NR__dl_getuid __NR_getuid +static inline _syscall0(gid_t, _dl_getuid); + +#define __NR__dl_geteuid __NR_geteuid +static inline _syscall0(uid_t, _dl_geteuid); + +#define __NR__dl_getgid __NR_getgid +static inline _syscall0(gid_t, _dl_getgid); + +#define __NR__dl_getegid __NR_getegid +static inline _syscall0(gid_t, _dl_getegid); + +/* + * Not an actual syscall, but we need something in assembly to say whether + * this is OK or not. + */ +extern inline int _dl_suid_ok(void) +{ + uid_t uid, euid, gid, egid; + + uid = _dl_getuid(); + euid = _dl_geteuid(); + gid = _dl_getgid(); + egid = _dl_getegid(); + + if(uid == euid && gid == egid) + return 1; + else + return 0; +} + diff --git a/ldso/ldso/ldso.c b/ldso/ldso/ldso.c new file mode 100644 index 000000000..6b0b864cb --- /dev/null +++ b/ldso/ldso/ldso.c @@ -0,0 +1,1005 @@ +/* Run an ELF binary on a linux system. + + Copyright (C) 1993-1996, Eric Youngdale. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +/* Program to load an ELF binary on a linux system, and run it. + * References to symbols in sharable libraries can be resolved by + * an ELF sharable library. */ + +/* Disclaimer: I have never seen any AT&T source code for SVr4, nor have + I ever taken any courses on internals. This program was developed using + information available through the book "UNIX SYSTEM V RELEASE 4, + Programmers guide: Ansi C and Programming Support Tools", which did + a more than adequate job of explaining everything required to get this + working. */ + +/* + * The main trick with this program is that initially, we ourselves are not + * dynamicly linked. This means that we cannot access any global variables + * since the GOT is initialized by the linker assuming a virtual address of 0, + * and we cannot call any functions since the PLT is not initialized at all + * (it will tend to want to call the dynamic linker + * + * There are further restrictions - we cannot use large switch statements, + * since the compiler generates tables of addresses and jumps through them. + * We can use inline functions, because these do not transfer control to + * a new address, but they must be static so that they are not exported + * from the modules. We cannot use normal syscall stubs, because these + * all reference the errno global variable which is not yet initialized. + * We can use all of the local stack variables that we want, since these + * are all referenced to %ebp or %esp. + * + * Life is further complicated by the fact that initially we do not want + * to do a complete dynamic linking. We want to allow the user to supply + * new functions replacing some of the library versions, and until we have + * the list of modules that we should search set up, we do not want to do + * any of this. Thus I have chosen to only perform the relocations for + * variables that start with "_dl_" since ANSI specifies that the user is + * not supposed to redefine any of these variables. + * + * Fortunately, the linker itself leaves a few clues lying around, and + * when the kernel starts the image, there are a few further clues. + * First of all, there is information buried on the stack that the kernel + * leaves, which includes information about the load address that the + * program interpreter was loaded at, the number of sections, the address + * the application was loaded at and so forth. Here this information + * is stored in the array dl_info, and the indicies are taken from the + * file /usr/include/sys/auxv.h on any SVr4 system. + * + * The linker itself leaves a pointer to the .dynamic section in the first + * slot of the GOT, and as it turns out, %ebx points to ghe GOT when + * you are using PIC code, so we just dereference this to get the address + * of the dynamic sections. + * + * Typically you must load all text pages as writable so that dynamic linking + * can succeed. The kernel under SVr4 loads these as R/O, so we must call + * mprotect to change the protections. Once we are done, we should set these + * back again, so the desired behavior is achieved. Under linux there is + * currently no mprotect function in the distribution kernel (although + * someone has alpha patches), so for now everything is loaded writable. + * + * We do not have access to malloc and friends at the initial stages of dynamic + * linking, and it would be handy to have some scratchpad memory available + * for use as we set things up. It is a bit of a kluge, but we mmap /dev/zero + * to get one page of scratchpad. A simpleminded _dl_malloc is provided so + * that we have some memory that can be used for this purpose. Typically + * we would not want to use the same memory pool as malloc anyway - the user + * might want to redefine malloc for example. + * + * Our first task is to perform a minimal linking so that we can call other + * portions of the dynamic linker. Once we have done this, we then build + * the list of modules that the application requires, using LD_LIBRARY_PATH + * if this is not a suid program (/usr/lib otherwise). Once this is done, + * we can do the dynamic linking as required (and we must omit the things + * we did to get the dynamic linker up and running in the first place. + * After we have done this, we just have a few housekeeping chores and we + * can transfer control to the user's application. + */ + +#include <stdarg.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/unistd.h> +#include <linux/elf.h> +#include <linux/mman.h> +#include "link.h" + +#include "sysdep.h" +#include "hash.h" +#include "linuxelf.h" +#include "syscall.h" +#include "string.h" + +#include "../config.h" + +#define ALLOW_ZERO_PLTGOT + +static char * _dl_malloc_addr, *_dl_mmap_zero; +char * _dl_library_path = 0; /* Where we look for libraries */ +char *_dl_preload = 0; /* Things to be loaded before the libs. */ +char *_dl_progname = "/lib/ld-linux-uclibc.so.1"; +static char * _dl_not_lazy = 0; +static char * _dl_warn = 0; /* Used by ldd */ +static char * _dl_trace_loaded_objects = 0; +static int (*_dl_elf_main)(int, char **, char**); + +static int (*_dl_elf_init)(void); + +void * (*_dl_malloc_function)(int size) = NULL; + +struct r_debug * _dl_debug_addr = NULL; + +unsigned int * _dl_brkp; + +unsigned int * _dl_envp; + +#define DL_MALLOC(SIZE) ((void *) (malloc_buffer += SIZE, malloc_buffer - SIZE)) +/* + * Make sure that the malloc buffer is aligned on 4 byte boundary. For 64 bit + * platforms we may need to increase this to 8, but this is good enough for + * now. This is typically called after DL_MALLOC. + */ +#define REALIGN() malloc_buffer = (char *) (((unsigned int) malloc_buffer + 3) & ~(3)) + + + +#define ELF_HASH(RESULT,NAME) { \ + unsigned long hash = 0; \ + unsigned long tmp; \ + char * name = NAME; \ + while (*name){ \ + hash = (hash << 4) + *name++; \ + if((tmp = hash & 0xf0000000)) hash ^= tmp >> 24; \ + hash &= ~tmp; \ + } \ + RESULT = hash; \ +} +extern int _dl_linux_resolve(void); +extern int _dl_interpreter_exit(int); +extern char * _dl_strdup(const char *); +extern char * _dl_getenv(char * symbol, char ** envp); +extern void _dl_unsetenv(char * symbol, char ** envp); +extern int _dl_fixup(struct elf_resolve * tpnt); + +/* + * Datatype of a relocation on this platform + */ +#ifdef ELF_USES_RELOCA +typedef struct elf32_rela ELF_RELOC; +#else +typedef struct elf32_rel ELF_RELOC; +#endif + +/* + * This stub function is used by some debuggers. The idea is that they + * can set an internal breakpoint on it, so that we are notified when the + * address mapping is changed in some way. + */ +void _dl_debug_state() +{ + return; +} + +void _dl_boot(int args); + +void _dl_boot(int args){ + unsigned int argc; + char ** argv, ** envp; + int status; + + unsigned int load_addr; + unsigned int * got; + unsigned int * aux_dat; + int goof = 0; + struct elfhdr * header; + struct elf_resolve * tpnt; + struct dyn_elf * rpnt; + struct elf_resolve * app_tpnt; + unsigned int brk_addr; + unsigned int dl_data[AT_EGID+1]; + unsigned char * malloc_buffer, *mmap_zero; + int (*_dl_atexit)(void *); + int * lpnt; + struct dynamic * dpnt; + unsigned int *hash_addr; + struct r_debug * debug_addr; + unsigned int *chains; + int indx; + int _dl_secure; + + /* First obtain the information on the stack that tells us more about + what binary is loaded, where it is loaded, etc, etc */ + + GET_ARGV(aux_dat, args); + argc = *(aux_dat - 1); + argv = (char **) aux_dat; + aux_dat += argc; /* Skip over the argv pointers */ + aux_dat++; /* Skip over NULL at end of argv */ + envp = (char **) aux_dat; + while(*aux_dat) aux_dat++; /* Skip over the envp pointers */ + aux_dat++; /* Skip over NULL at end of envp */ + dl_data[AT_UID] = -1; /* check later to see if it is changed */ + while(*aux_dat) + { + unsigned int * ad1; + ad1 = aux_dat + 1; + if( *aux_dat <= AT_EGID ) dl_data[*aux_dat] = *ad1; + aux_dat += 2; + } + + /* Next, locate the GOT */ + + load_addr = dl_data[AT_BASE]; + + GET_GOT(got); + dpnt = (struct dynamic *) (*got + load_addr); + + /* OK, time for another hack. Now call mmap to get a page of writable + memory that can be used for a temporary malloc. We do not know brk + yet, so we cannot use real malloc. */ + + { + /* This hack is to work around a suspected asm bug in gcc-2.7.0 */ + int zfileno; +#define ZFILENO ((-1 & (~zfileno)) | zfileno) +/*#define ZFILENO -1*/ + +#ifndef MAP_ANONYMOUS +#ifdef __sparc__ +#define MAP_ANONYMOUS 0x20 +#else +#error MAP_ANONYMOUS not defined and suplementary value not known +#endif +#endif + + /* See if we need to relocate this address */ + mmap_zero = malloc_buffer = (unsigned char *) _dl_mmap((void*) 0, 4096, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, ZFILENO, 0); + if(_dl_mmap_check_error(mmap_zero)) { + SEND_STDERR("dl_boot: mmap of /dev/zero failed!\n"); + _dl_exit(13); + } + } + + tpnt = DL_MALLOC(sizeof(struct elf_resolve)); + REALIGN(); + _dl_memset (tpnt, 0, sizeof (*tpnt)); + app_tpnt = DL_MALLOC(sizeof(struct elf_resolve)); + REALIGN(); + _dl_memset (app_tpnt, 0, sizeof (*app_tpnt)); + + /* + * This is used by gdb to locate the chain of shared libraries that are currently loaded. + */ + debug_addr = DL_MALLOC(sizeof(struct r_debug)); + REALIGN(); + _dl_memset (debug_addr, 0, sizeof (*debug_addr)); + + /* OK, that was easy. Next scan the DYNAMIC section of the image. + We are only doing ourself right now - we will have to do the rest later */ + + while(dpnt->d_tag) + { + tpnt->dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val; + if(dpnt->d_tag == DT_TEXTREL || + SVR4_BUGCOMPAT) tpnt->dynamic_info[DT_TEXTREL] = 1; + dpnt++; + } + + { + struct elf_phdr * ppnt; + int i; + + ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + for(i=0; i<dl_data[AT_PHNUM]; i++, ppnt++) + if(ppnt->p_type == PT_DYNAMIC) { + dpnt = (struct dynamic *) ppnt->p_vaddr; + while(dpnt->d_tag) + { + if(dpnt->d_tag > DT_JMPREL) {dpnt++; continue; } + app_tpnt->dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val; + if(dpnt->d_tag == DT_DEBUG) dpnt->d_un.d_val = (int) debug_addr; + if(dpnt->d_tag == DT_TEXTREL || + SVR4_BUGCOMPAT) app_tpnt->dynamic_info[DT_TEXTREL] = 1; + dpnt++; + } + } + } + + /* Get some more of the information that we will need to dynamicly link + this module to itself */ + + hash_addr = (unsigned int *) (tpnt->dynamic_info[DT_HASH]+load_addr); + tpnt->nbucket = *hash_addr++; + tpnt->nchain = *hash_addr++; + tpnt->elf_buckets = hash_addr; + hash_addr += tpnt->nbucket; + chains = hash_addr; + + /* Ugly, ugly. We need to call mprotect to change the protection of + the text pages so that we can do the dynamic linking. We can set the + protection back again once we are done */ + + { + struct elf_phdr * ppnt; + int i; + + /* First cover the shared library/dynamic linker. */ + if(tpnt->dynamic_info[DT_TEXTREL]) { + header = (struct elfhdr *) dl_data[AT_BASE]; + ppnt = (struct elf_phdr *) (dl_data[AT_BASE] + header->e_phoff); + for(i=0; i<header->e_phnum ; i++, ppnt++) { + if(ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W)) + _dl_mprotect((void *) (load_addr + (ppnt->p_vaddr & 0xfffff000)), + (ppnt->p_vaddr & 0xfff) + (unsigned int) ppnt->p_filesz, + PROT_READ | PROT_WRITE | PROT_EXEC); + } + } + + /* Now cover the application program. */ + if(app_tpnt->dynamic_info[DT_TEXTREL]) { + ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + for(i=0; i<dl_data[AT_PHNUM]; i++, ppnt++) { + if(ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W)) + _dl_mprotect((void *) (ppnt->p_vaddr & 0xfffff000), + (ppnt->p_vaddr & 0xfff) + (unsigned int) ppnt->p_filesz, + PROT_READ | PROT_WRITE | PROT_EXEC); + } + } + } + + /* OK, now do the relocations. We do not do a lazy binding here, so + that once we are done, we have considerably more flexibility. */ + + goof = 0; + for(indx=0; indx < 2; indx++) + { + int i; + ELF_RELOC * rpnt; + unsigned int * reloc_addr; + unsigned int symbol_addr; + int symtab_index; + unsigned int rel_addr, rel_size; + + +#ifdef ELF_USES_RELOCA + rel_addr = (indx ? tpnt->dynamic_info[DT_JMPREL] : tpnt->dynamic_info[DT_RELA]); + rel_size = (indx ? tpnt->dynamic_info[DT_PLTRELSZ] : tpnt->dynamic_info[DT_RELASZ]); +#else + rel_addr = (indx ? tpnt->dynamic_info[DT_JMPREL] : tpnt->dynamic_info[DT_REL]); + rel_size = (indx ? tpnt->dynamic_info[DT_PLTRELSZ] : tpnt->dynamic_info[DT_RELSZ]); +#endif + + + if(!rel_addr) continue; + + /* Now parse the relocation information */ + rpnt = (ELF_RELOC *) (rel_addr + load_addr); + for(i=0; i< rel_size; i+=sizeof(ELF_RELOC), rpnt++){ + reloc_addr = (int *) (load_addr + (int)rpnt->r_offset); + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + if(symtab_index) { + char * strtab; + struct elf32_sym * symtab; + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB]+load_addr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]+load_addr); + + /* We only do a partial dynamic linking right now. The user + is not supposed to redefine any symbols that start with + a '_', so we can do this with confidence. */ + + if (!_dl_symbol(strtab + symtab[symtab_index].st_name)) continue; + + symbol_addr = load_addr + symtab[symtab_index].st_value; + + if(!symbol_addr) { + /* + * This will segfault - you cannot call a function until + * we have finished the relocations. + */ + SEND_STDERR("ELF dynamic loader - unable to self-bootstrap - symbol "); + SEND_STDERR(strtab + symtab[symtab_index].st_name); + SEND_STDERR(" undefined.\n"); + goof++; + } + } + /* + * Use this machine-specific macro to perform the actual relocation. + */ + PERFORM_BOOTSTRAP_RELOC(rpnt, reloc_addr, symbol_addr, load_addr); + } + } + + if (goof) _dl_exit(14); + + /* OK, at this point we have a crude malloc capability. Start to build + the tables of the modules that are required for this beast to run. + We start with the basic executable, and then go from there. Eventually + we will run across ourself, and we will need to properly deal with that + as well. */ + + _dl_malloc_addr = malloc_buffer; + + _dl_mmap_zero = mmap_zero; +/* tpnt = _dl_malloc(sizeof(struct elf_resolve)); */ + +/* Now we have done the mandatory linking of some things. We are now + free to start using global variables, since these things have all been + fixed up by now. Still no function calls outside of this library , + since the dynamic resolver is not yet ready. */ + + lpnt = (int *) (tpnt->dynamic_info[DT_PLTGOT] + load_addr); + INIT_GOT(lpnt, tpnt); + + /* OK, this was a big step, now we need to scan all of the user images + and load them properly. */ + + tpnt->next = 0; + tpnt->libname = 0; + tpnt->libtype = program_interpreter; + + { struct elfhdr * epnt; + struct elf_phdr * ppnt; + int i; + + epnt = (struct elfhdr *) dl_data[AT_BASE]; + tpnt->n_phent = epnt->e_phnum; + tpnt->ppnt = ppnt = (struct elf_phdr *) (load_addr + epnt->e_phoff); + for(i=0;i < epnt->e_phnum; i++, ppnt++){ + if(ppnt->p_type == PT_DYNAMIC) { + tpnt->dynamic_addr = ppnt->p_vaddr + load_addr; + tpnt->dynamic_size = ppnt->p_filesz; + } + } + } + + tpnt->chains = chains; + tpnt->loadaddr = (char *) load_addr; + + brk_addr = 0; + rpnt = NULL; + + /* At this point we are now free to examine the user application, + and figure out which libraries are supposed to be called. Until + we have this list, we will not be completely ready for dynamic linking */ + + { + struct elf_phdr * ppnt; + int i; + + ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + for(i=0; i<dl_data[AT_PHNUM]; i++, ppnt++) { + if(ppnt->p_type == PT_LOAD) { + if(ppnt->p_vaddr + ppnt->p_memsz > brk_addr) + brk_addr = ppnt->p_vaddr + ppnt->p_memsz; + } + if(ppnt->p_type == PT_DYNAMIC) { +#ifndef ALLOW_ZERO_PLTGOT + /* make sure it's really there. */ + if (app_tpnt->dynamic_info[DT_PLTGOT] == 0) continue; +#endif + /* OK, we have what we need - slip this one into the list. */ + app_tpnt = _dl_add_elf_hash_table("", 0, + app_tpnt->dynamic_info, ppnt->p_vaddr, ppnt->p_filesz); + _dl_loaded_modules->libtype = elf_executable; + _dl_loaded_modules->ppnt = (struct elf_phdr *) dl_data[AT_PHDR]; + _dl_loaded_modules->n_phent = dl_data[AT_PHNUM]; + _dl_symbol_tables = rpnt = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt, 0, sizeof (*rpnt)); + rpnt->dyn = _dl_loaded_modules; + app_tpnt->usage_count++; + app_tpnt->symbol_scope = _dl_symbol_tables; + lpnt = (int *) (app_tpnt->dynamic_info[DT_PLTGOT]); +#ifdef ALLOW_ZERO_PLTGOT + if (lpnt) +#endif + INIT_GOT(lpnt, _dl_loaded_modules); + } + if(ppnt->p_type == PT_INTERP) { /* OK, fill this in - we did not have + this before */ + tpnt->libname = _dl_strdup((char *) ppnt->p_offset +(dl_data[AT_PHDR] & 0xfffff000)); + } + } + } + + if (argv[0]) + _dl_progname = argv[0]; + + /* Now we need to figure out what kind of options are selected. + Note that for SUID programs we ignore the settings in LD_LIBRARY_PATH */ + { + _dl_not_lazy = _dl_getenv("LD_BIND_NOW",envp); + + if ( (dl_data[AT_UID] == -1 && _dl_suid_ok()) || + (dl_data[AT_UID] != -1 && dl_data[AT_UID] == dl_data[AT_EUID] && + dl_data[AT_GID] == dl_data[AT_EGID])) + { + _dl_secure = 0; + _dl_preload = _dl_getenv("LD_PRELOAD", envp); + _dl_library_path = _dl_getenv("LD_LIBRARY_PATH",envp); + } + else + { + _dl_secure = 1; + _dl_preload = _dl_getenv("LD_PRELOAD", envp); + _dl_unsetenv("LD_AOUT_PRELOAD", envp); + _dl_unsetenv("LD_LIBRARY_PATH", envp); + _dl_unsetenv("LD_AOUT_LIBRARY_PATH", envp); + _dl_library_path = NULL; + } + } + + _dl_trace_loaded_objects = _dl_getenv("LD_TRACE_LOADED_OBJECTS", envp); + + /* OK, we now have the application in the list, and we have some + basic stuff in place. Now search through the list for other shared + libraries that should be loaded, and insert them on the list in the + correct order. */ + +#ifdef USE_CACHE + _dl_map_cache(); +#endif + + { + struct elf_resolve *tcurr; + struct elf_resolve *tpnt1; + char *lpnt; + + if (_dl_preload) { + char c, *str, *str2; + + str = _dl_preload; + while (*str == ':' || *str == ' ' || *str == '\t') + str++; + while (*str) { + str2 = str; + while (*str2 && *str2 != ':' && *str2 != ' ' && *str2 != '\t') + str2++; + c = *str2; + *str2 = '\0'; + if (!_dl_secure || _dl_strchr(str, '/') == NULL) { + tpnt1 = _dl_load_shared_library(_dl_secure, NULL, str); + if (!tpnt1) { + if (_dl_trace_loaded_objects) + _dl_fdprintf(1, "\t%s => not found\n", str); + else { + _dl_fdprintf(2, "%s: can't load library '%s'\n", + _dl_progname, str); + _dl_exit(15); + } + } else { + if (_dl_trace_loaded_objects && !tpnt1->usage_count) { + /* this is a real hack to make ldd not print the + library itself when run on a library. */ + if (_dl_strcmp(_dl_progname, str) != 0) + _dl_fdprintf(1, "\t%s => %s (0x%x)\n", str, tpnt1->libname, + (unsigned)tpnt1->loadaddr); + } + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + tpnt1->usage_count++; + tpnt1->symbol_scope = _dl_symbol_tables; + tpnt1->libtype = elf_lib; + rpnt->dyn = tpnt1; + } + } + *str2 = c; + str = str2; + while (*str == ':' || *str == ' ' || *str == '\t') + str++; + } + } + + { + int fd; + struct kernel_stat st; + char *preload; + + if (!_dl_stat(LDSO_PRELOAD, &st)) { + if ((fd = _dl_open(LDSO_PRELOAD, O_RDONLY)) < 0) { + _dl_fdprintf(2, "%s: can't open file '%s'\n", _dl_progname, + LDSO_PRELOAD); + } else { + preload = (caddr_t)_dl_mmap(0, st.st_size+1, PROT_READ|PROT_WRITE, + MAP_PRIVATE, fd, 0); + _dl_close (fd); + if (preload == (caddr_t)-1) { + _dl_fdprintf(2, "%s: can't map file '%s'\n", _dl_progname, + LDSO_PRELOAD); + } else { + char c, *cp, *cp2; + + /* convert all separators and comments to spaces */ + for (cp = preload; *cp; /*nada*/) { + if (*cp == ':' || *cp == '\t' || *cp == '\n') { + *cp++ = ' '; + } else if (*cp == '#') { + do + *cp++ = ' '; + while (*cp != '\n' && *cp != '\0'); + } else { + cp++; + } + } + + /* find start of first library */ + for (cp = preload; *cp && *cp == ' '; cp++) + /*nada*/; + + while (*cp) { + /* find end of library */ + for (cp2 = cp; *cp && *cp != ' '; cp++) + /*nada*/; + c = *cp; + *cp = '\0'; + + tpnt1 = _dl_load_shared_library(0, NULL, cp2); + if (!tpnt1) { + if (_dl_trace_loaded_objects) + _dl_fdprintf(1, "\t%s => not found\n", cp2); + else { + _dl_fdprintf(2, "%s: can't load library '%s'\n", + _dl_progname, cp2); + _dl_exit(15); + } + } else { + if (_dl_trace_loaded_objects && !tpnt1->usage_count) + _dl_fdprintf(1, "\t%s => %s (0x%x)\n", cp2, tpnt1->libname, + (unsigned)tpnt1->loadaddr); + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + tpnt1->usage_count++; + tpnt1->symbol_scope = _dl_symbol_tables; + tpnt1->libtype = elf_lib; + rpnt->dyn = tpnt1; + } + + /* find start of next library */ + *cp = c; + for (/*nada*/; *cp && *cp == ' '; cp++) + /*nada*/; + } + + _dl_munmap(preload, st.st_size+1); + } + } + } + } + + for (tcurr = _dl_loaded_modules; tcurr; tcurr = tcurr->next) + { + for (dpnt = (struct dynamic *)tcurr->dynamic_addr; dpnt->d_tag; dpnt++) + { + if(dpnt->d_tag == DT_NEEDED) + { + lpnt = tcurr->loadaddr + tcurr->dynamic_info[DT_STRTAB] + + dpnt->d_un.d_val; + if (tpnt && _dl_strcmp(lpnt, tpnt->libname) == 0) + { + struct elf_resolve * ttmp; + ttmp = _dl_loaded_modules; + while (ttmp->next) + ttmp = ttmp->next; + ttmp->next = tpnt; + tpnt->prev = ttmp; + tpnt->next = NULL; + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + rpnt->dyn = tpnt; + tpnt->usage_count++; + tpnt->symbol_scope = _dl_symbol_tables; + tpnt = NULL; + continue; + } + if (!(tpnt1 = _dl_load_shared_library(0, tcurr, lpnt))) + { + if (_dl_trace_loaded_objects) + _dl_fdprintf(1, "\t%s => not found\n", lpnt); + else + { + _dl_fdprintf(2, "%s: can't load library '%s'\n", + _dl_progname, lpnt); + _dl_exit(16); + } + } + else + { + if (_dl_trace_loaded_objects && !tpnt1->usage_count) + _dl_fdprintf(1, "\t%s => %s (0x%x)\n", lpnt, tpnt1->libname, + (unsigned)tpnt1->loadaddr); + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + tpnt1->usage_count++; + tpnt1->symbol_scope = _dl_symbol_tables; + tpnt1->libtype = elf_lib; + rpnt->dyn = tpnt1; + } + } + } + } + } + +#ifdef USE_CACHE + _dl_unmap_cache(); +#endif + + /* ldd uses uses this. I am not sure how you pick up the other flags */ + if(_dl_trace_loaded_objects) + { + _dl_warn = _dl_getenv("LD_WARN", envp); + if (!_dl_warn) _dl_exit(0); + } + + /* + * If the program interpreter is not in the module chain, add it. This will + * be required for dlopen to be able to access the internal functions in the + * dynamic linker. + */ + if(tpnt) { + struct elf_resolve * tcurr; + + tcurr = _dl_loaded_modules; + if (tcurr) + while(tcurr->next) tcurr = tcurr->next; + tpnt->next = NULL; + tpnt->usage_count++; + + if (tcurr) { + tcurr->next = tpnt; + tpnt->prev = tcurr; + } + else { + _dl_loaded_modules = tpnt; + tpnt->prev = NULL; + } + if (rpnt) { + rpnt->next = + (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + } else { + rpnt = (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt, 0, sizeof (*(rpnt->next))); + } + rpnt->dyn = tpnt; + tpnt = NULL; + } + + /* + * OK, now all of the kids are tucked into bed in their proper addresses. + * Now we go through and look for REL and RELA records that indicate fixups + * to the GOT tables. We need to do this in reverse order so that COPY + * directives work correctly */ + + + goof = _dl_loaded_modules ? _dl_fixup(_dl_loaded_modules) : 0; + + + /* Some flavors of SVr4 do not generate the R_*_COPY directive, + and we have to manually search for entries that require fixups. + Solaris gets this one right, from what I understand. */ + + + if (_dl_symbol_tables) + goof += _dl_copy_fixups(_dl_symbol_tables); + + if(goof || _dl_trace_loaded_objects) _dl_exit(0); + + /* OK, at this point things are pretty much ready to run. Now we + need to touch up a few items that are required, and then + we can let the user application have at it. Note that + the dynamic linker itself is not guaranteed to be fully + dynamicly linked if we are using ld.so.1, so we have to look + up each symbol individually. */ + + + _dl_brkp = (unsigned int *) _dl_find_hash("___brk_addr", NULL, 1, NULL, 0); + if (_dl_brkp) *_dl_brkp = brk_addr; + _dl_envp = (unsigned int *) _dl_find_hash("__environ", NULL, 1, NULL, 0); + + if (_dl_envp) *_dl_envp = (unsigned int) envp; + + { + int i; + struct elf_phdr * ppnt; + + /* We had to set the protections of all pages to R/W for dynamic linking. + Set text pages back to R/O */ + for(tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) + for(ppnt = tpnt->ppnt, i=0; i < tpnt->n_phent; i++, ppnt++) + if(ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W) && + tpnt->dynamic_info[DT_TEXTREL]) + _dl_mprotect((void *) (tpnt->loadaddr + (ppnt->p_vaddr & 0xfffff000)), + (ppnt->p_vaddr & 0xfff) + (unsigned int) ppnt->p_filesz, + LXFLAGS(ppnt->p_flags)); + + } + + _dl_atexit = (int (*)(void *)) _dl_find_hash("atexit", NULL, 1, NULL, 0); + + /* + * OK, fix one more thing - set up the debug_addr structure to point + * to our chain. Later we may need to fill in more fields, but this + * should be enough for now. + */ + debug_addr->r_map = (struct link_map *) _dl_loaded_modules; + debug_addr->r_version = 1; + debug_addr->r_ldbase = load_addr; + debug_addr->r_brk = (unsigned long) &_dl_debug_state; + _dl_debug_addr = debug_addr; + debug_addr->r_state = RT_CONSISTENT; + /* This is written in this funny way to keep gcc from inlining the + function call. */ + ((void (*)(void))debug_addr->r_brk)(); + + for(tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) + { + /* Apparently crt1 for the application is responsible for handling this. + * We only need to run the init/fini for shared libraries + */ + if (tpnt->libtype == program_interpreter || + tpnt->libtype == elf_executable) continue; + if (tpnt->init_flag & INIT_FUNCS_CALLED) continue; + tpnt->init_flag |= INIT_FUNCS_CALLED; + + if(tpnt->dynamic_info[DT_INIT]) { + _dl_elf_init = (int (*)(void)) (tpnt->loadaddr + + tpnt->dynamic_info[DT_INIT]); + (*_dl_elf_init)(); + } + if(_dl_atexit && tpnt->dynamic_info[DT_FINI]) + { + (*_dl_atexit)(tpnt->loadaddr + tpnt->dynamic_info[DT_FINI]); + } +#undef DL_DEBUG +#ifdef DL_DEBUG + else + { + _dl_fdprintf(2, tpnt->libname); + _dl_fdprintf(2, ": "); + if (!_dl_atexit) + _dl_fdprintf(2, "The address is atexit () is 0x0."); + if (!tpnt->dynamic_info[DT_FINI]) + _dl_fdprintf(2, "Invalid .fini section."); + _dl_fdprintf(2, "\n"); + } +#endif +#undef DL_DEBUG + } + + /* OK we are done here. Turn out the lights, and lock up. */ + _dl_elf_main = (int (*)(int, char**, char**)) dl_data[AT_ENTRY]; + + + /* + * Transfer control to the application. + */ + START(); +} + +int _dl_fixup(struct elf_resolve * tpnt) +{ + int goof = 0; + if(tpnt->next) goof += _dl_fixup(tpnt->next); + + if(tpnt->dynamic_info[DT_REL]) { +#ifdef ELF_USES_RELOCA + _dl_fdprintf(2, "%s: can't handle REL relocation records\n", _dl_progname); + _dl_exit(17); +#else + if (tpnt->init_flag & RELOCS_DONE) return goof; + tpnt->init_flag |= RELOCS_DONE; + + goof += _dl_parse_relocation_information(tpnt, tpnt->dynamic_info[DT_REL], + tpnt->dynamic_info[DT_RELSZ], 0); +#endif + } + if(tpnt->dynamic_info[DT_RELA]) { +#ifdef ELF_USES_RELOCA + if (tpnt->init_flag & RELOCS_DONE) return goof; + tpnt->init_flag |= RELOCS_DONE; + + goof += _dl_parse_relocation_information(tpnt, tpnt->dynamic_info[DT_RELA], + tpnt->dynamic_info[DT_RELASZ], 0); +#else + _dl_fdprintf(2, "%s: can't handle RELA relocation records\n", _dl_progname); + _dl_exit(18); +#endif + } + if(tpnt->dynamic_info[DT_JMPREL]) + { + if (tpnt->init_flag & JMP_RELOCS_DONE) return goof; + tpnt->init_flag |= JMP_RELOCS_DONE; + + if(! _dl_not_lazy || *_dl_not_lazy == 0) + _dl_parse_lazy_relocation_information(tpnt, tpnt->dynamic_info[DT_JMPREL], + tpnt->dynamic_info[DT_PLTRELSZ], 0); + else + goof += _dl_parse_relocation_information(tpnt, + tpnt->dynamic_info[DT_JMPREL], + tpnt->dynamic_info[DT_PLTRELSZ], 0); + } + return goof; +} + +void * _dl_malloc(int size) { + void * retval; + + if(_dl_malloc_function) + return (*_dl_malloc_function)(size); + + if(_dl_malloc_addr-_dl_mmap_zero+size>4096) { + _dl_mmap_zero = _dl_malloc_addr = (unsigned char *) _dl_mmap((void*) 0, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if(_dl_mmap_check_error(_dl_mmap_zero)) { + _dl_fdprintf(2, "%s: can't map '/dev/zero'\n", _dl_progname); + _dl_exit(20); + } + } + retval = _dl_malloc_addr; + _dl_malloc_addr += size; + + /* + * Align memory to 4 byte boundary. Some platforms require this, others + * simply get better performance. + */ + _dl_malloc_addr = (char *) (((unsigned int) _dl_malloc_addr + 3) & ~(3)); + return retval; +} + +char * _dl_getenv(char *symbol, char **envp) +{ + char *pnt; + char *pnt1; + while ((pnt = *envp++)) { + pnt1 = symbol; + while (*pnt && *pnt == *pnt1) + pnt1++, pnt++; + if (!*pnt || *pnt != '=' || *pnt1) + continue; + return pnt+1; + } + return 0; +} + +void _dl_unsetenv(char *symbol, char **envp) +{ + char *pnt; + char *pnt1; + char **newenvp = envp; + for (pnt = *envp; pnt; pnt = *++envp) { + pnt1 = symbol; + while (*pnt && *pnt == *pnt1) + pnt1++, pnt++; + if(!*pnt || *pnt != '=' || *pnt1) + *newenvp++ = *envp; + } + *newenvp++ = *envp; + return; +} + +char * _dl_strdup(const char * string){ + char * retval; + int len; + + len = _dl_strlen(string); + retval = _dl_malloc(len + 1); + _dl_strcpy(retval, string); + return retval; +} + +/* In principle we could do the .fini stuff here, but we already + registered this stuff with atexit */ +int _dl_interpreter_exit(int exitcode){ +/* _dl_fdprintf(2, "Hey, look where I am!\n"); */ + return 0; +} diff --git a/ldso/ldso/link.h b/ldso/ldso/link.h new file mode 100644 index 000000000..adaa20e4a --- /dev/null +++ b/ldso/ldso/link.h @@ -0,0 +1,37 @@ +#ifndef _LINK_H +#define _LINK_H + +#include "elf.h" + +/* Header file that describes the internal data structures used by the + * ELF dynamic linker. */ + +struct link_map +{ + /* These entries must be in this order to be compatible with the + * interface used by gdb to obtain the list of symbols. */ + unsigned long l_addr; /* address at which object is mapped */ + char *l_name; /* full name of loaded object */ + Elf32_Dyn *l_ld; /* dynamic structure of object */ + struct link_map *l_next; + struct link_map *l_prev; +}; + +/* The DT_DEBUG entry in the .dynamic section is given the address of + * this structure. gdb can pick this up to obtain the correct list of + * loaded modules. */ +struct r_debug +{ + int r_version; /* debugging info version no */ + struct link_map *r_map; /* address of link_map */ + unsigned long r_brk; /* address of update routine */ + enum + { + RT_CONSISTENT, + RT_ADD, + RT_DELETE + } r_state; + unsigned long r_ldbase; /* base addr of ld.so */ +}; + +#endif /* _LINK_H */ diff --git a/ldso/ldso/linuxelf.h b/ldso/ldso/linuxelf.h new file mode 100644 index 000000000..cc016f6b7 --- /dev/null +++ b/ldso/ldso/linuxelf.h @@ -0,0 +1,137 @@ +/* This should eventually appear in the distribution version of linux/elf.h */ +#ifndef R_SPARC_NONE +#define R_SPARC_NONE 0 +#define R_SPARC_8 1 +#define R_SPARC_16 2 +#define R_SPARC_32 3 +#define R_SPARC_DISP8 4 +#define R_SPARC_DISP16 5 +#define R_SPARC_DISP32 6 +#define R_SPARC_WDISP30 7 +#define R_SPARC_WDISP22 8 +#define R_SPARC_HI22 9 +#define R_SPARC_22 10 +#define R_SPARC_13 11 +#define R_SPARC_LO10 12 +#define R_SPARC_GOT10 13 +#define R_SPARC_GOT13 14 +#define R_SPARC_GOT22 15 +#define R_SPARC_PC10 16 +#define R_SPARC_PC22 17 +#define R_SPARC_WPLT30 18 +#define R_SPARC_COPY 19 +#define R_SPARC_GLOB_DAT 20 +#define R_SPARC_JMP_SLOT 21 +#define R_SPARC_RELATIVE 22 +#define R_SPARC_UA32 23 +#endif + +#ifndef R_68K_NONE +#define R_68K_NONE 0 +#define R_68K_32 1 +#define R_68K_16 2 +#define R_68K_8 3 +#define R_68K_PC32 4 +#define R_68K_PC16 5 +#define R_68K_PC8 6 +#define R_68K_GOT32 7 +#define R_68K_GOT16 8 +#define R_68K_GOT8 9 +#define R_68K_GOT32O 10 +#define R_68K_GOT16O 11 +#define R_68K_GOT8O 12 +#define R_68K_PLT32 13 +#define R_68K_PLT16 14 +#define R_68K_PLT8 15 +#define R_68K_PLT32O 16 +#define R_68K_PLT16O 17 +#define R_68K_PLT8O 18 +#define R_68K_COPY 19 +#define R_68K_GLOB_DAT 20 +#define R_68K_JMP_SLOT 21 +#define R_68K_RELATIVE 22 +#define R_68K_NUM 23 +#endif + +/* + * These constants define the elements of the auxiliary vector used to + * pass additional information from the kernel to an ELF application. + */ + +#ifndef AT_NULL +typedef struct +{ + int a_type; + union{ + long a_val; + void *p_ptr; + void (*a_fcn)(); + } a_un; +} auxv_t; + +/* + * Values of a_type... These often appear in the file /usr/include/sys/auxv.h + * on SVr4 systems. + */ +#define AT_NULL 0 +#define AT_IGNORE 1 +#define AT_EXECFD 2 +#define AT_PHDR 3 +#define AT_PHENT 4 +#define AT_PHNUM 5 +#define AT_PAGESZ 6 +#define AT_BASE 7 +#define AT_FLAGS 8 +#define AT_ENTRY 9 +#endif +#ifndef AT_NOTELF +#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 */ +#endif + +extern int _dl_linux_resolve(void); +extern struct elf_resolve * _dl_load_shared_library(int secure, + struct elf_resolve *, char * libname); +extern void * _dl_malloc(int size); +extern int _dl_map_cache(void); +extern int _dl_unmap_cache(void); + +extern struct elf_resolve * _dl_load_elf_shared_library(int secure, + char * libname, int); +int _dl_copy_fixups(struct dyn_elf * tpnt); + +extern int linux_run(int argc, char * argv[]); + +extern void _dl_parse_lazy_relocation_information(struct elf_resolve * tpnt, int rel_addr, + int rel_size, int type); + +extern int _dl_parse_relocation_information(struct elf_resolve * tpnt, int rel_addr, + int rel_size, int type); +extern int _dl_parse_copy_information(struct dyn_elf * rpnt, int rel_addr, + int rel_size, int type); + + +/* This means that we may be forced to manually search for copy fixups + which were omitted by the linker. We cannot depend upon the DT_TEXTREL + to tell us whether there are fixups in a text section or not. */ + +#ifndef SVR4_BUGCOMPAT +#define SVR4_BUGCOMPAT 1 +#endif + +#ifndef PF_R +#define PF_R 4 +#define PF_W 2 +#define PF_X 1 +#endif + +/* Convert between the Linux flags for page protections and the + ones specified in the ELF standard. */ + +#define LXFLAGS(X) ( (((X) & PF_R) ? PROT_READ : 0) | \ + (((X) & PF_W) ? PROT_WRITE : 0) | \ + (((X) & PF_X) ? PROT_EXEC : 0)) + diff --git a/ldso/ldso/m68k/dl-sysdep.h b/ldso/ldso/m68k/dl-sysdep.h new file mode 100644 index 000000000..24af47ca0 --- /dev/null +++ b/ldso/ldso/m68k/dl-sysdep.h @@ -0,0 +1,87 @@ + +/* Various assmbly language/system dependent hacks that are required + so that we can minimize the amount of platform specific code. */ + +/* Define this if the system uses RELOCA. */ +#define ELF_USES_RELOCA + +/* Get a pointer to the argv array. On many platforms this can be + just the address if the first argument, on other platforms we need + to do something a little more subtle here. */ +#define GET_ARGV(ARGVP, ARGS) ((ARGVP) = ((unsigned int *) &(ARGS))) + +/* Get the address of the Global offset table. This must be absolute, + not relative. */ +#define GET_GOT(X) __asm__ ("movel %%a5,%0" : "=g" (X)) + +/* Initialization sequence for a GOT. */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[2] = (int) _dl_linux_resolve; \ + GOT_BASE[1] = (int) (MODULE); \ +} + +/* Here is a macro to perform a relocation. This is only used when + bootstrapping the dynamic loader. RELP is the relocation that we + are performing, REL is the pointer to the address we are + relocating. SYMBOL is the symbol involved in the relocation, and + LOAD is the load address. */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch (ELF32_R_TYPE ((RELP)->r_info)) \ + { \ + case R_68K_8: \ + *(char *) (REL) = (SYMBOL) + (RELP)->r_addend; \ + break; \ + case R_68K_16: \ + *(short *) (REL) = (SYMBOL) + (RELP)->r_addend; \ + break; \ + case R_68K_32: \ + *(REL) = (SYMBOL) + (RELP)->r_addend; \ + break; \ + case R_68K_PC8: \ + *(char *) (REL) = ((SYMBOL) + (RELP)->r_addend \ + - (unsigned int) (REL)); \ + break; \ + case R_68K_PC16: \ + *(short *) (REL) = ((SYMBOL) + (RELP)->r_addend \ + - (unsigned int) (REL)); \ + break; \ + case R_68K_PC32: \ + *(REL) = ((SYMBOL) + (RELP)->r_addend \ + - (unsigned int) (REL)); \ + break; \ + case R_68K_GLOB_DAT: \ + case R_68K_JMP_SLOT: \ + *(REL) = (SYMBOL); \ + break; \ + case R_68K_RELATIVE: /* Compatibility kludge */ \ + *(REL) = ((unsigned int) (LOAD) + ((RELP)->r_addend ? : *(REL))); \ + break; \ + default: \ + _dl_exit (1); \ + } + + +/* Transfer control to the user's application, once the dynamic loader + is done. */ + +#define START() \ + __asm__ volatile ("unlk %%a6\n\t" \ + "jmp %0@" \ + : : "a" (_dl_elf_main)); + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_68K +#undef MAGIC2 +/* Used for error messages */ +#define ELF_TARGET "m68k" + +struct elf_resolve; +extern unsigned int _dl_linux_resolver (int, int, struct elf_resolve *, int); + +/* Define this because we do not want to call .udiv in the library. + Not needed for m68k. */ +#define do_rem(result, n, base) ((result) = (n) % (base)) diff --git a/ldso/ldso/m68k/elfinterp.c b/ldso/ldso/m68k/elfinterp.c new file mode 100644 index 000000000..9b73efd3f --- /dev/null +++ b/ldso/ldso/m68k/elfinterp.c @@ -0,0 +1,358 @@ +/* Run an ELF binary on a linux system. + + Copyright (C) 1993, Eric Youngdale. + Copyright (C) 1995, Andreas Schwab. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Adapted to ELF/68k by Andreas Schwab. */ + +#ifndef VERBOSE_DLINKER +#define VERBOSE_DLINKER +#endif +#ifdef VERBOSE_DLINKER +static char *_dl_reltypes[] = +{ + "R_68K_NONE", + "R_68K_32", "R_68K_16", "R_68K_8", + "R_68K_PC32", "R_68K_PC16", "R_68K_PC8", + "R_68K_GOT32", "R_68K_GOT16", "R_68K_GOT8", + "R_68K_GOT32O", "R_68K_GOT16O", "R_68K_GOT8O", + "R_68K_PLT32", "R_68K_PLT16", "R_68K_PLT8", + "R_68K_PLT32O", "R_68K_PLT16O", "R_68K_PLT8O", + "R_68K_COPY", "R_68K_GLOB_DAT", "R_68K_JMP_SLOT", "R_68K_RELATIVE", + "R_68K_NUM" +}; +#endif + +/* Program to load an ELF binary on a linux system, and run it. + References to symbols in sharable libraries can be resolved by either + an ELF sharable library or a linux style of shared library. */ + +/* Disclaimer: I have never seen any AT&T source code for SVr4, nor have + I ever taken any courses on internals. This program was developed using + information available through the book "UNIX SYSTEM V RELEASE 4, + Programmers guide: Ansi C and Programming Support Tools", which did + a more than adequate job of explaining everything required to get this + working. */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/unistd.h> +/*#include <stdlib.h>*/ +#include "string.h" +#include <linux/unistd.h> +#include <linux/fcntl.h> +#include <linux/elf.h> + +#include "hash.h" +#include "linuxelf.h" +#include "sysdep.h" +#include "../syscall.h" +#include "../string.h" + +extern char *_dl_progname; + +unsigned int +_dl_linux_resolver (int dummy1, int dummy2, + struct elf_resolve *tpnt, int reloc_entry) +{ + int reloc_type; + struct elf32_rela *this_reloc; + char *strtab; + struct elf32_sym *symtab; + char *rel_addr; + int symtab_index; + char *new_addr; + char **got_addr; + unsigned int instr_addr; + + rel_addr = tpnt->loadaddr + tpnt->dynamic_info[DT_JMPREL]; + this_reloc = (struct elf32_rela *) (rel_addr + reloc_entry); + reloc_type = ELF32_R_TYPE (this_reloc->r_info); + symtab_index = ELF32_R_SYM (this_reloc->r_info); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + + if (reloc_type != R_68K_JMP_SLOT) + { + _dl_fdprintf (2, "%s: incorrect relocation type in jump relocations\n", + _dl_progname); + _dl_exit (1); + } + + /* Address of jump instruction to fix up. */ + instr_addr = (int) this_reloc->r_offset + (int) tpnt->loadaddr; + got_addr = (char **) instr_addr; + +#ifdef DEBUG + _dl_fdprintf (2, "Resolving symbol %s\n", + strtab + symtab[symtab_index].st_name); +#endif + + /* Get the address of the GOT entry. */ + new_addr = _dl_find_hash (strtab + symtab[symtab_index].st_name, + tpnt->symbol_scope, (int) got_addr, tpnt, 0); + if (!new_addr) + { + _dl_fdprintf (2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + _dl_exit (1); + } +/* #define DEBUG_LIBRARY */ +#ifdef DEBUG_LIBRARY + if ((unsigned int) got_addr < 0x40000000) + _dl_fdprintf (2, "Calling library function: %s\n", + strtab + symtab[symtab_index].st_name); + else +#endif + *got_addr = new_addr; + return (unsigned int) new_addr; +} + +void +_dl_parse_lazy_relocation_information (struct elf_resolve *tpnt, int rel_addr, + int rel_size, int type) +{ + int i; + char *strtab; + int reloc_type; + int symtab_index; + struct elf32_sym *symtab; + struct elf32_rela *rpnt; + unsigned int *reloc_addr; + + /* Now parse the relocation information. */ + rpnt = (struct elf32_rela *) (rel_addr + tpnt->loadaddr); + rel_size = rel_size / sizeof (struct elf32_rela); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for (i = 0; i < rel_size; i++, rpnt++) + { + reloc_addr = (int *) (tpnt->loadaddr + (int) rpnt->r_offset); + reloc_type = ELF32_R_TYPE (rpnt->r_info); + symtab_index = ELF32_R_SYM (rpnt->r_info); + + /* When the dynamic linker bootstrapped itself, it resolved some symbols. + Make sure we do not do them again. */ + if (tpnt->libtype == program_interpreter + && (!symtab_index + || _dl_symbol (strtab + symtab[symtab_index].st_name))) + continue; + + switch (reloc_type) + { + case R_68K_NONE: + break; + case R_68K_JMP_SLOT: + *reloc_addr += (unsigned int) tpnt->loadaddr; + break; + default: + _dl_fdprintf (2, "%s: (LAZY) can't handle reloc type ", _dl_progname); +#ifdef VERBOSE_DLINKER + _dl_fdprintf (2, "%s ", _dl_reltypes[reloc_type]); +#endif + if (symtab_index) + _dl_fdprintf (2, "'%s'", strtab + symtab[symtab_index].st_name); + _dl_fdprintf (2, "\n"); + _dl_exit (1); + } + } +} + +int +_dl_parse_relocation_information (struct elf_resolve *tpnt, int rel_addr, + int rel_size, int type) +{ + int i; + char *strtab; + int reloc_type; + int goof = 0; + struct elf32_sym *symtab; + struct elf32_rela *rpnt; + unsigned int *reloc_addr; + unsigned int symbol_addr; + int symtab_index; + /* Now parse the relocation information */ + + rpnt = (struct elf32_rela *) (rel_addr + tpnt->loadaddr); + rel_size = rel_size / sizeof (struct elf32_rela); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for (i = 0; i < rel_size; i++, rpnt++) + { + reloc_addr = (int *) (tpnt->loadaddr + (int) rpnt->r_offset); + reloc_type = ELF32_R_TYPE (rpnt->r_info); + symtab_index = ELF32_R_SYM (rpnt->r_info); + symbol_addr = 0; + + if (tpnt->libtype == program_interpreter + && (!symtab_index + || _dl_symbol (strtab + symtab[symtab_index].st_name))) + continue; + + if (symtab_index) + { + symbol_addr = (unsigned int) + _dl_find_hash (strtab + symtab[symtab_index].st_name, + tpnt->symbol_scope, (int) reloc_addr, + reloc_type == R_68K_JMP_SLOT ? tpnt : NULL, 0); + + /* We want to allow undefined references to weak symbols - + this might have been intentional. We should not be + linking local symbols here, so all bases should be + covered. */ + if (!symbol_addr + && ELF32_ST_BIND (symtab[symtab_index].st_info) == STB_GLOBAL) + { + _dl_fdprintf (2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + goof++; + } + } + switch (reloc_type) + { + case R_68K_NONE: + break; + case R_68K_8: + *(char *) reloc_addr = symbol_addr + rpnt->r_addend; + break; + case R_68K_16: + *(short *) reloc_addr = symbol_addr + rpnt->r_addend; + break; + case R_68K_32: + *reloc_addr = symbol_addr + rpnt->r_addend; + break; + case R_68K_PC8: + *(char *) reloc_addr = (symbol_addr + rpnt->r_addend + - (unsigned int) reloc_addr); + break; + case R_68K_PC16: + *(short *) reloc_addr = (symbol_addr + rpnt->r_addend + - (unsigned int) reloc_addr); + break; + case R_68K_PC32: + *reloc_addr = (symbol_addr + rpnt->r_addend + - (unsigned int) reloc_addr); + break; + case R_68K_GLOB_DAT: + case R_68K_JMP_SLOT: + *reloc_addr = symbol_addr; + break; + case R_68K_RELATIVE: + *reloc_addr = ((unsigned int) tpnt->loadaddr + /* Compatibility kludge. */ + + (rpnt->r_addend ? : *reloc_addr)); + break; + case R_68K_COPY: +#if 0 /* Do this later. */ + _dl_fdprintf (2, "Doing copy"); + if (symtab_index) + _dl_fdprintf (2, " for symbol %s", + strtab + symtab[symtab_index].st_name); + _dl_fdprintf (2, "\n"); + _dl_memcpy ((void *) symtab[symtab_index].st_value, + (void *) symbol_addr, + symtab[symtab_index].st_size); +#endif + break; + default: + _dl_fdprintf (2, "%s: can't handle reloc type ", _dl_progname); +#ifdef VERBOSE_DLINKER + _dl_fdprintf (2, "%s ", _dl_reltypes[reloc_type]); +#endif + if (symtab_index) + _dl_fdprintf (2, "'%s'", strtab + symtab[symtab_index].st_name); + _dl_fdprintf (2, "\n"); + _dl_exit (1); + } + + } + return goof; +} + +/* This is done as a separate step, because there are cases where + information is first copied and later initialized. This results in + the wrong information being copied. Someone at Sun was complaining about + a bug in the handling of _COPY by SVr4, and this may in fact be what he + was talking about. Sigh. */ + +/* No, there are cases where the SVr4 linker fails to emit COPY relocs + at all. */ + +int +_dl_parse_copy_information (struct dyn_elf *xpnt, int rel_addr, + int rel_size, int type) +{ + int i; + char *strtab; + int reloc_type; + int goof = 0; + struct elf32_sym *symtab; + struct elf32_rela *rpnt; + unsigned int *reloc_addr; + unsigned int symbol_addr; + struct elf_resolve *tpnt; + int symtab_index; + /* Now parse the relocation information */ + + tpnt = xpnt->dyn; + + rpnt = (struct elf32_rela *) (rel_addr + tpnt->loadaddr); + rel_size = rel_size / sizeof (struct elf32_rela); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for (i = 0; i < rel_size; i++, rpnt++) + { + reloc_addr = (int *) (tpnt->loadaddr + (int) rpnt->r_offset); + reloc_type = ELF32_R_TYPE (rpnt->r_info); + if (reloc_type != R_68K_COPY) + continue; + symtab_index = ELF32_R_SYM (rpnt->r_info); + symbol_addr = 0; + if (tpnt->libtype == program_interpreter + && (!symtab_index + || _dl_symbol (strtab + symtab[symtab_index].st_name))) + continue; + if (symtab_index) + { + symbol_addr = (unsigned int) + _dl_find_hash (strtab + symtab[symtab_index].st_name, + xpnt->next, (int) reloc_addr, NULL, 1); + if (!symbol_addr) + { + _dl_fdprintf (2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + goof++; + } + } + if (!goof) + _dl_memcpy ((void *) symtab[symtab_index].st_value, (void *) symbol_addr, + symtab[symtab_index].st_size); + } + return goof; +} diff --git a/ldso/ldso/m68k/ld_sysdep.h b/ldso/ldso/m68k/ld_sysdep.h new file mode 100644 index 000000000..24af47ca0 --- /dev/null +++ b/ldso/ldso/m68k/ld_sysdep.h @@ -0,0 +1,87 @@ + +/* Various assmbly language/system dependent hacks that are required + so that we can minimize the amount of platform specific code. */ + +/* Define this if the system uses RELOCA. */ +#define ELF_USES_RELOCA + +/* Get a pointer to the argv array. On many platforms this can be + just the address if the first argument, on other platforms we need + to do something a little more subtle here. */ +#define GET_ARGV(ARGVP, ARGS) ((ARGVP) = ((unsigned int *) &(ARGS))) + +/* Get the address of the Global offset table. This must be absolute, + not relative. */ +#define GET_GOT(X) __asm__ ("movel %%a5,%0" : "=g" (X)) + +/* Initialization sequence for a GOT. */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[2] = (int) _dl_linux_resolve; \ + GOT_BASE[1] = (int) (MODULE); \ +} + +/* Here is a macro to perform a relocation. This is only used when + bootstrapping the dynamic loader. RELP is the relocation that we + are performing, REL is the pointer to the address we are + relocating. SYMBOL is the symbol involved in the relocation, and + LOAD is the load address. */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch (ELF32_R_TYPE ((RELP)->r_info)) \ + { \ + case R_68K_8: \ + *(char *) (REL) = (SYMBOL) + (RELP)->r_addend; \ + break; \ + case R_68K_16: \ + *(short *) (REL) = (SYMBOL) + (RELP)->r_addend; \ + break; \ + case R_68K_32: \ + *(REL) = (SYMBOL) + (RELP)->r_addend; \ + break; \ + case R_68K_PC8: \ + *(char *) (REL) = ((SYMBOL) + (RELP)->r_addend \ + - (unsigned int) (REL)); \ + break; \ + case R_68K_PC16: \ + *(short *) (REL) = ((SYMBOL) + (RELP)->r_addend \ + - (unsigned int) (REL)); \ + break; \ + case R_68K_PC32: \ + *(REL) = ((SYMBOL) + (RELP)->r_addend \ + - (unsigned int) (REL)); \ + break; \ + case R_68K_GLOB_DAT: \ + case R_68K_JMP_SLOT: \ + *(REL) = (SYMBOL); \ + break; \ + case R_68K_RELATIVE: /* Compatibility kludge */ \ + *(REL) = ((unsigned int) (LOAD) + ((RELP)->r_addend ? : *(REL))); \ + break; \ + default: \ + _dl_exit (1); \ + } + + +/* Transfer control to the user's application, once the dynamic loader + is done. */ + +#define START() \ + __asm__ volatile ("unlk %%a6\n\t" \ + "jmp %0@" \ + : : "a" (_dl_elf_main)); + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_68K +#undef MAGIC2 +/* Used for error messages */ +#define ELF_TARGET "m68k" + +struct elf_resolve; +extern unsigned int _dl_linux_resolver (int, int, struct elf_resolve *, int); + +/* Define this because we do not want to call .udiv in the library. + Not needed for m68k. */ +#define do_rem(result, n, base) ((result) = (n) % (base)) diff --git a/ldso/ldso/m68k/resolve.S b/ldso/ldso/m68k/resolve.S new file mode 100644 index 000000000..9b1a24c68 --- /dev/null +++ b/ldso/ldso/m68k/resolve.S @@ -0,0 +1,29 @@ +#if 0 +#include <sysdep.h> +#endif +/* + * These are various helper routines that are needed to run an ELF image. + */ + +#ifdef NO_UNDERSCORE +#define __dl_linux_resolve _dl_linux_resolve +#define __dl_linux_resolver _dl_linux_resolver +#endif + +.text +.even + +.globl __dl_linux_resolve + .type __dl_linux_resolve,@function +__dl_linux_resolve: + moveml %a0/%a1,%sp@- +#ifdef __PIC__ + bsrl __dl_linux_resolver@PLTPC +#else + jbsr __dl_linux_resolver +#endif + moveml %sp@+,%a0/%a1 + addql #8,%sp + jmp @(%d0) +.LFE2: + .size __dl_linux_resolve,.LFE2-__dl_linux_resolve diff --git a/ldso/ldso/m68k/sysdep.h b/ldso/ldso/m68k/sysdep.h new file mode 100644 index 000000000..24af47ca0 --- /dev/null +++ b/ldso/ldso/m68k/sysdep.h @@ -0,0 +1,87 @@ + +/* Various assmbly language/system dependent hacks that are required + so that we can minimize the amount of platform specific code. */ + +/* Define this if the system uses RELOCA. */ +#define ELF_USES_RELOCA + +/* Get a pointer to the argv array. On many platforms this can be + just the address if the first argument, on other platforms we need + to do something a little more subtle here. */ +#define GET_ARGV(ARGVP, ARGS) ((ARGVP) = ((unsigned int *) &(ARGS))) + +/* Get the address of the Global offset table. This must be absolute, + not relative. */ +#define GET_GOT(X) __asm__ ("movel %%a5,%0" : "=g" (X)) + +/* Initialization sequence for a GOT. */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[2] = (int) _dl_linux_resolve; \ + GOT_BASE[1] = (int) (MODULE); \ +} + +/* Here is a macro to perform a relocation. This is only used when + bootstrapping the dynamic loader. RELP is the relocation that we + are performing, REL is the pointer to the address we are + relocating. SYMBOL is the symbol involved in the relocation, and + LOAD is the load address. */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch (ELF32_R_TYPE ((RELP)->r_info)) \ + { \ + case R_68K_8: \ + *(char *) (REL) = (SYMBOL) + (RELP)->r_addend; \ + break; \ + case R_68K_16: \ + *(short *) (REL) = (SYMBOL) + (RELP)->r_addend; \ + break; \ + case R_68K_32: \ + *(REL) = (SYMBOL) + (RELP)->r_addend; \ + break; \ + case R_68K_PC8: \ + *(char *) (REL) = ((SYMBOL) + (RELP)->r_addend \ + - (unsigned int) (REL)); \ + break; \ + case R_68K_PC16: \ + *(short *) (REL) = ((SYMBOL) + (RELP)->r_addend \ + - (unsigned int) (REL)); \ + break; \ + case R_68K_PC32: \ + *(REL) = ((SYMBOL) + (RELP)->r_addend \ + - (unsigned int) (REL)); \ + break; \ + case R_68K_GLOB_DAT: \ + case R_68K_JMP_SLOT: \ + *(REL) = (SYMBOL); \ + break; \ + case R_68K_RELATIVE: /* Compatibility kludge */ \ + *(REL) = ((unsigned int) (LOAD) + ((RELP)->r_addend ? : *(REL))); \ + break; \ + default: \ + _dl_exit (1); \ + } + + +/* Transfer control to the user's application, once the dynamic loader + is done. */ + +#define START() \ + __asm__ volatile ("unlk %%a6\n\t" \ + "jmp %0@" \ + : : "a" (_dl_elf_main)); + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_68K +#undef MAGIC2 +/* Used for error messages */ +#define ELF_TARGET "m68k" + +struct elf_resolve; +extern unsigned int _dl_linux_resolver (int, int, struct elf_resolve *, int); + +/* Define this because we do not want to call .udiv in the library. + Not needed for m68k. */ +#define do_rem(result, n, base) ((result) = (n) % (base)) diff --git a/ldso/ldso/readelflib1.c b/ldso/ldso/readelflib1.c new file mode 100644 index 000000000..9d1cd0ff5 --- /dev/null +++ b/ldso/ldso/readelflib1.c @@ -0,0 +1,588 @@ +/* Load an ELF sharable library into memory. + + Copyright (C) 1993-1996, Eric Youngdale. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +/* This file contains the helper routines to load an ELF sharable + library into memory and add the symbol table info to the chain. */ + +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/errno.h> +#include "string.h" +/*#include <stdlib.h>*/ +#include <linux/mman.h> +#include <linux/stat.h> +#include "hash.h" +#include "linuxelf.h" +#include "sysdep.h" +#include <linux/unistd.h> +#include "syscall.h" +#ifdef USE_CACHE +#include "../config.h" +#endif + +extern char *_dl_progname; + +#ifdef USE_CACHE + +static caddr_t _dl_cache_addr = NULL; +static size_t _dl_cache_size = 0; + +int _dl_map_cache(void) +{ + int fd; + struct kernel_stat st; + header_t *header; + libentry_t *libent; + int i, strtabsize; + + if (_dl_cache_addr == (caddr_t)-1) + return -1; + else if (_dl_cache_addr != NULL) + return 0; + + if (_dl_stat(LDSO_CACHE, &st) || (fd = _dl_open(LDSO_CACHE, O_RDONLY)) < 0) + { + _dl_fdprintf(2, "%s: can't open cache '%s'\n", _dl_progname, LDSO_CACHE); + _dl_cache_addr = (caddr_t)-1; /* so we won't try again */ + return -1; + } + + _dl_cache_size = st.st_size; + _dl_cache_addr = (caddr_t)_dl_mmap(0, _dl_cache_size, PROT_READ, + MAP_SHARED, fd, 0); + _dl_close (fd); + if (_dl_cache_addr == (caddr_t)-1) + { + _dl_fdprintf(2, "%s: can't map cache '%s'\n", _dl_progname, LDSO_CACHE); + return -1; + } + + header = (header_t *)_dl_cache_addr; + + if (_dl_cache_size < sizeof (header_t) || + _dl_memcmp(header->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN) || + _dl_memcmp(header->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN) || + _dl_cache_size < + (sizeof (header_t) + header->nlibs * sizeof (libentry_t)) || + _dl_cache_addr[_dl_cache_size-1] != '\0') + { + _dl_fdprintf(2, "%s: cache '%s' is corrupt\n", _dl_progname, LDSO_CACHE); + goto fail; + } + + strtabsize = _dl_cache_size - sizeof (header_t) - + header->nlibs * sizeof (libentry_t); + libent = (libentry_t *)&header[1]; + + for (i = 0; i < header->nlibs; i++) + { + if (libent[i].sooffset >= strtabsize || + libent[i].liboffset >= strtabsize) + { + _dl_fdprintf(2, "%s: cache '%s' is corrupt\n", _dl_progname, LDSO_CACHE); + goto fail; + } + } + + return 0; + +fail: + _dl_munmap(_dl_cache_addr, _dl_cache_size); + _dl_cache_addr = (caddr_t)-1; + return -1; +} + +int _dl_unmap_cache(void) +{ + if (_dl_cache_addr == NULL || _dl_cache_addr == (caddr_t)-1) + return -1; + +#if 1 + _dl_munmap (_dl_cache_addr, _dl_cache_size); + _dl_cache_addr = NULL; +#endif + + return 0; +} + +#endif + +/* + * Used to return error codes back to dlopen et. al. + */ + +unsigned int _dl_error_number; +unsigned int _dl_internal_error_number; + +struct elf_resolve * _dl_load_shared_library(int secure, + struct elf_resolve * tpnt, char * full_libname) { + char * pnt, *pnt1, *pnt2; + struct elf_resolve *tpnt1 = NULL; + char mylibname[2050]; + char * libname; + + _dl_internal_error_number = 0; + + /* quick hack to ensure mylibname buffer doesn't overflow. don't + allow full_libname or any directory to be longer than 1024. */ + if (_dl_strlen(full_libname) > 1024) + goto goof; + + pnt = libname = full_libname; + while (*pnt) { + if(*pnt == '/') libname = pnt+1; + pnt++; + } + + /* If the filename has any '/', try it straight and leave it at that. + For IBCS2 compatibility under linux, we substitute the string + /usr/i486-sysv4/lib for /usr/lib in library names. */ + + if (libname != full_libname) { + tpnt1 = _dl_load_elf_shared_library(secure, full_libname, 0); + if (tpnt1) + return tpnt1; + goto goof; + } + + /* + * The ABI specifies that RPATH is searched before LD_*_PATH or + * the default path of /usr/lib. + * Check in rpath directories + */ + for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) { + if (tpnt->libtype == elf_executable) { + pnt1 = (char *)tpnt->dynamic_info[DT_RPATH]; + if(pnt1) { + pnt1 += (unsigned int) tpnt->loadaddr + tpnt->dynamic_info[DT_STRTAB]; + while(*pnt1){ + pnt2 = mylibname; + while(*pnt1 && *pnt1 != ':') { + if (pnt2 - mylibname < 1024) + *pnt2++ = *pnt1++; + else + pnt1++; + } + if (pnt2 - mylibname >= 1024) + break; + if(pnt2[-1] != '/') *pnt2++ = '/'; + pnt = libname; + while(*pnt) *pnt2++ = *pnt++; + *pnt2++ = 0; + tpnt1 = _dl_load_elf_shared_library(secure, mylibname, 0); + if(tpnt1) return tpnt1; + if(*pnt1 == ':') pnt1++; + } + } + } + } + + + /* Check in LD_{ELF_}LIBRARY_PATH, if specified and allowed */ + pnt1 = _dl_library_path; + if (pnt1 && *pnt1) { + while (*pnt1) { + pnt2 = mylibname; + while(*pnt1 && *pnt1 != ':' && *pnt1 != ';') { + if (pnt2 - mylibname < 1024) + *pnt2++ = *pnt1++; + else + pnt1++; + } + if (pnt2 - mylibname >= 1024) + break; + if(pnt2[-1] != '/') *pnt2++ = '/'; + pnt = libname; + while(*pnt) *pnt2++ = *pnt++; + *pnt2++ = 0; + tpnt1 = _dl_load_elf_shared_library(secure, mylibname, 0); + if(tpnt1) return tpnt1; + if(*pnt1 == ':' || *pnt1 == ';') pnt1++; + } + } + + + /* + * Where should the cache be searched? There is no such concept in the + * ABI, so we have some flexibility here. For now, search it before + * the default path of /usr/lib. + */ +#ifdef USE_CACHE + if (_dl_cache_addr != NULL && _dl_cache_addr != (caddr_t)-1) + { + int i; + header_t *header = (header_t *)_dl_cache_addr; + libentry_t *libent = (libentry_t *)&header[1]; + char *strs = (char *)&libent[header->nlibs]; + + for (i = 0; i < header->nlibs; i++) + { + if ((libent[i].flags == LIB_ELF || + libent[i].flags == LIB_ELF_LIBC5) && + _dl_strcmp(libname, strs+libent[i].sooffset) == 0 && + (tpnt1 = _dl_load_elf_shared_library(secure, strs+libent[i].liboffset, 0))) + return tpnt1; + } + } +#endif + + +#ifdef UCLIBC_DEVEL + + /* Check in /usr/<arch>-linux-uclibc/lib */ + pnt1 = UCLIBC_INSTALL_DIR"/lib"; + pnt = mylibname; + while(*pnt1) *pnt++ = *pnt1++; + pnt1 = libname; + while(*pnt1) *pnt++ = *pnt1++; + *pnt++ = 0; + tpnt1 = _dl_load_elf_shared_library(secure, mylibname, 0); + if (tpnt1) return tpnt1; + +#else /* UCLIBC_DEVEL */ + + /* Check in /usr/lib */ + pnt1 = "/usr/lib/"; + pnt = mylibname; + while(*pnt1) *pnt++ = *pnt1++; + pnt1 = libname; + while(*pnt1) *pnt++ = *pnt1++; + *pnt++ = 0; + tpnt1 = _dl_load_elf_shared_library(secure, mylibname, 0); + if (tpnt1) return tpnt1; + + /* Check in /lib */ + /* try "/lib/". */ + pnt1 = "/lib/"; + pnt = mylibname; + while(*pnt1) *pnt++ = *pnt1++; + pnt1 = libname; + while(*pnt1) *pnt++ = *pnt1++; + *pnt++ = 0; + tpnt1 = _dl_load_elf_shared_library(secure, mylibname, 0); + if (tpnt1) return tpnt1; +#endif /* UCLIBC_DEVEL */ + +goof: + /* Well, we shot our wad on that one. All we can do now is punt */ + if (_dl_internal_error_number) _dl_error_number = _dl_internal_error_number; + else _dl_error_number = DL_ERROR_NOFILE; + return NULL; +} + +/* + * Read one ELF library into memory, mmap it into the correct locations and + * add the symbol info to the symbol chain. Perform any relocations that + * are required. + */ + +//extern _elf_rtbndr(void); + +struct elf_resolve * _dl_load_elf_shared_library(int secure, + char * libname, int flag) { + struct elfhdr * epnt; + unsigned int dynamic_addr = 0; + unsigned int dynamic_size = 0; + struct dynamic * dpnt; + struct elf_resolve * tpnt; + struct elf_phdr * ppnt; + int piclib; + char * status; + int flags; + char header[4096]; + int dynamic_info[24]; + int * lpnt; + unsigned int libaddr; + unsigned int minvma=0xffffffff, maxvma=0; + + int i; + int infile; + + /* If this file is already loaded, skip this step */ + tpnt = _dl_check_hashed_files(libname); + if(tpnt) return tpnt; + + /* If we are in secure mode (i.e. a setu/gid binary using LD_PRELOAD), + we don't load the library if it isn't setuid. */ + + if (secure) { + struct kernel_stat st; + if (_dl_stat(libname, &st) || !(st.st_mode & S_ISUID)) + return NULL; + } + + libaddr = 0; + infile = _dl_open(libname, O_RDONLY); + if(infile < 0) + { +#if 0 + /* + * NO! When we open shared libraries we may search several paths. + * it is inappropriate to generate an error here. + */ + _dl_fdprintf(2, "%s: can't open '%s'\n", _dl_progname, libname); +#endif + _dl_internal_error_number = DL_ERROR_NOFILE; + return NULL; + } + + _dl_read(infile, header, sizeof(header)); + epnt = (struct elfhdr *) header; + if (epnt->e_ident[0] != 0x7f || + epnt->e_ident[1] != 'E' || + epnt->e_ident[2] != 'L' || + epnt->e_ident[3] != 'F') { + _dl_fdprintf(2, "%s: '%s' is not an ELF file\n", _dl_progname, libname); + _dl_internal_error_number = DL_ERROR_NOTELF; + _dl_close(infile); + return NULL; + }; + + if((epnt->e_type != ET_DYN) || + (epnt->e_machine != MAGIC1 +#ifdef MAGIC2 + && epnt->e_machine != MAGIC2 +#endif + )){ + _dl_internal_error_number = (epnt->e_type != ET_DYN ? DL_ERROR_NOTDYN : DL_ERROR_NOTMAGIC); + _dl_fdprintf(2, "%s: '%s' is not an ELF executable for " ELF_TARGET "\n", + _dl_progname, libname); + _dl_close(infile); + return NULL; + }; + + ppnt = (struct elf_phdr *) &header[epnt->e_phoff]; + + piclib = 1; + for(i=0;i < epnt->e_phnum; i++){ + + if(ppnt->p_type == PT_DYNAMIC) { + if (dynamic_addr) + _dl_fdprintf(2, "%s: '%s' has more than one dynamic section\n", + _dl_progname, libname); + dynamic_addr = ppnt->p_vaddr; + dynamic_size = ppnt->p_filesz; + }; + + if(ppnt->p_type == PT_LOAD) { + /* See if this is a PIC library. */ + if(i == 0 && ppnt->p_vaddr > 0x1000000) { + piclib = 0; + minvma=ppnt->p_vaddr; + } + if(piclib && ppnt->p_vaddr < minvma) { + minvma = ppnt->p_vaddr; + } + if(((unsigned int)ppnt->p_vaddr + ppnt->p_memsz) > maxvma) { + maxvma = ppnt->p_vaddr + ppnt->p_memsz; + } + } + ppnt++; + }; + + maxvma=(maxvma+0xfffU)&~0xfffU; + minvma=minvma&~0xffffU; + + flags = MAP_PRIVATE /*| MAP_DENYWRITE*/; + if(!piclib) flags |= MAP_FIXED; + + status = (char *) _dl_mmap((char *) (piclib?0:minvma), + maxvma-minvma, + PROT_NONE, + flags | MAP_ANONYMOUS, -1, + 0); + if(_dl_mmap_check_error(status)) { + _dl_fdprintf(2, "%s: can't map '/dev/zero'\n", _dl_progname); + _dl_internal_error_number = DL_ERROR_MMAP_FAILED; + _dl_close(infile); + return NULL; + }; + libaddr=(unsigned int)status; + flags|=MAP_FIXED; + + /* Get the memory to store the library */ + ppnt = (struct elf_phdr *) &header[epnt->e_phoff]; + + for(i=0;i < epnt->e_phnum; i++){ + if(ppnt->p_type == PT_LOAD) { + + /* See if this is a PIC library. */ + if(i == 0 && ppnt->p_vaddr > 0x1000000) { + piclib = 0; + /* flags |= MAP_FIXED; */ + } + + + + if(ppnt->p_flags & PF_W) { + unsigned int map_size; + char * cpnt; + + status = (char *) _dl_mmap((char *) ((piclib?libaddr:0) + + (ppnt->p_vaddr & 0xfffff000)), + (ppnt->p_vaddr & 0xfff) + ppnt->p_filesz, + LXFLAGS(ppnt->p_flags), + flags, infile, + ppnt->p_offset & 0x7ffff000); + + if(_dl_mmap_check_error(status)) { + _dl_fdprintf(2, "%s: can't map '%s'\n", _dl_progname, libname); + _dl_internal_error_number = DL_ERROR_MMAP_FAILED; + _dl_munmap((char *)libaddr, maxvma-minvma); + _dl_close(infile); + return NULL; + }; + + /* Pad the last page with zeroes. */ + cpnt =(char *) (status + (ppnt->p_vaddr & 0xfff) + ppnt->p_filesz); + while(((unsigned int) cpnt) & 0xfff) *cpnt++ = 0; + +/* I am not quite sure if this is completely correct to do or not, but + the basic way that we handle bss segments is that we mmap /dev/zero if + there are any pages left over that are not mapped as part of the file */ + + map_size = (ppnt->p_vaddr + ppnt->p_filesz + 0xfff) & 0xfffff000; + if(map_size < ppnt->p_vaddr + ppnt->p_memsz) + status = (char *) _dl_mmap((char *) map_size + (piclib?libaddr:0), + ppnt->p_vaddr + ppnt->p_memsz - map_size, + LXFLAGS(ppnt->p_flags), + flags | MAP_ANONYMOUS, -1, 0); + } else + status = (char *) _dl_mmap((char *) (ppnt->p_vaddr & 0xfffff000) + + (piclib?libaddr:0), + (ppnt->p_vaddr & 0xfff) + ppnt->p_filesz, + LXFLAGS(ppnt->p_flags), + flags, infile, + ppnt->p_offset & 0x7ffff000); + if(_dl_mmap_check_error(status)) { + _dl_fdprintf(2, "%s: can't map '%s'\n", _dl_progname, libname); + _dl_internal_error_number = DL_ERROR_MMAP_FAILED; + _dl_munmap((char *)libaddr, maxvma-minvma); + _dl_close(infile); + return NULL; + }; + + /* if(libaddr == 0 && piclib) { + libaddr = (unsigned int) status; + flags |= MAP_FIXED; + }; */ + }; + ppnt++; + }; + _dl_close(infile); + + /* For a non-PIC library, the addresses are all absolute */ + if(piclib) { + dynamic_addr += (unsigned int) libaddr; + } + + /* + * OK, the ELF library is now loaded into VM in the correct locations + * The next step is to go through and do the dynamic linking (if needed). + */ + + /* Start by scanning the dynamic section to get all of the pointers */ + + if(!dynamic_addr) { + _dl_internal_error_number = DL_ERROR_NODYNAMIC; + _dl_fdprintf(2, "%s: '%s' is missing a dynamic section\n", _dl_progname, libname); + return NULL; + } + + dpnt = (struct dynamic *) dynamic_addr; + + dynamic_size = dynamic_size / sizeof(struct dynamic); + _dl_memset(dynamic_info, 0, sizeof(dynamic_info)); + for(i=0; i< dynamic_size; i++){ + if( dpnt->d_tag > DT_JMPREL ) {dpnt++; continue; } + dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val; + if(dpnt->d_tag == DT_TEXTREL || + SVR4_BUGCOMPAT) dynamic_info[DT_TEXTREL] = 1; + dpnt++; + }; + + /* If the TEXTREL is set, this means that we need to make the pages + writable before we perform relocations. Do this now. They get set back + again later. */ + + if (dynamic_info[DT_TEXTREL]) { + ppnt = (struct elf_phdr *) &header[epnt->e_phoff]; + for(i=0;i < epnt->e_phnum; i++, ppnt++){ + if(ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W)) + _dl_mprotect((void *) ((piclib?libaddr:0) + (ppnt->p_vaddr & 0xfffff000)), + (ppnt->p_vaddr & 0xfff) + (unsigned int) ppnt->p_filesz, + PROT_READ | PROT_WRITE | PROT_EXEC); + } + } + + + tpnt = _dl_add_elf_hash_table(libname, (char *) libaddr, dynamic_info, dynamic_addr, + dynamic_size); + + tpnt->ppnt = (struct elf_phdr *) (tpnt->loadaddr + epnt->e_phoff); + tpnt->n_phent = epnt->e_phnum; + + /* + * OK, the next thing we need to do is to insert the dynamic linker into + * the proper entry in the GOT so that the PLT symbols can be properly + * resolved. + */ + + lpnt = (int *) dynamic_info[DT_PLTGOT]; + + if(lpnt) { + lpnt = (int *) (dynamic_info[DT_PLTGOT] + ((int) libaddr)); + INIT_GOT(lpnt, tpnt); + }; + + return tpnt; +} + +/* Ugly, ugly. Some versions of the SVr4 linker fail to generate COPY + relocations for global variables that are present both in the image and + the shared library. Go through and do it manually. If the images + are guaranteed to be generated by a trustworthy linker, then this + step can be skipped. */ + +int _dl_copy_fixups(struct dyn_elf * rpnt) +{ + int goof = 0; + struct elf_resolve * tpnt; + + if(rpnt->next) goof += _dl_copy_fixups(rpnt->next); + else return 0; + + tpnt = rpnt->dyn; + + if (tpnt->init_flag & COPY_RELOCS_DONE) return goof; + tpnt->init_flag |= COPY_RELOCS_DONE; + +#ifdef ELF_USES_RELOCA + goof += _dl_parse_copy_information(rpnt, tpnt->dynamic_info[DT_RELA], + tpnt->dynamic_info[DT_RELASZ], 0); + +#else + goof += _dl_parse_copy_information(rpnt, tpnt->dynamic_info[DT_REL], + tpnt->dynamic_info[DT_RELSZ], 0); + +#endif + return goof; +} + diff --git a/ldso/ldso/sparc/DEFS.h b/ldso/ldso/sparc/DEFS.h new file mode 100644 index 000000000..4b9abccfd --- /dev/null +++ b/ldso/ldso/sparc/DEFS.h @@ -0,0 +1,5 @@ +#define FUNC(name) \ + .global name; \ + .type name,@function; \ + .align 4; \ + name: diff --git a/ldso/ldso/sparc/dl-sysdep.h b/ldso/ldso/sparc/dl-sysdep.h new file mode 100644 index 000000000..1d4c0354f --- /dev/null +++ b/ldso/ldso/sparc/dl-sysdep.h @@ -0,0 +1,131 @@ + +/* + * Various assmbly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + */ +#define LINUXBIN + +/* + * Define this if the system uses RELOCA. + */ +#define ELF_USES_RELOCA + +/* + * Get the address of the Global offset table. This must be absolute, not + * relative. + */ +#define GET_GOT(X) __asm__("\tmov %%l7,%0\n\t" : "=r" (X)) + +/* + * Get a pointer to the argv array. On many platforms this can be just + * the address if the first argument, on other platforms we need to + * do something a little more subtle here. We assume that argc is stored + * at the word just below the argvp that we return here. + */ +#define GET_ARGV(ARGVP, ARGS) __asm__("\tadd %%fp,68,%0\n" : "=r" (ARGVP)); + +/* + * Initialization sequence for a GOT. For the Sparc, this points to the + * PLT, and we need to initialize a couple of the slots. The PLT should + * look like: + * + * save %sp, -64, %sp + * call _dl_linux_resolve + * nop + * .word implementation_dependent + */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[0] = 0x9de3bfc0; /* save %sp, -64, %sp */ \ + GOT_BASE[1] = 0x40000000 | (((unsigned int) _dl_linux_resolve - (unsigned int) GOT_BASE - 4) >> 2); \ + GOT_BASE[2] = 0x01000000; /* nop */ \ + GOT_BASE[3] = (int) MODULE; \ +} + +/* + * Here is a macro to perform a relocation. This is only used when + * bootstrapping the dynamic loader. + */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch(ELF32_R_TYPE((RELP)->r_info)) { \ + case R_SPARC_32: \ + *REL = SYMBOL + (RELP)->r_addend; \ + break; \ + case R_SPARC_GLOB_DAT: \ + *REL = SYMBOL + (RELP)->r_addend; \ + break; \ + case R_SPARC_JMP_SLOT: \ + REL[1] = 0x03000000 | ((SYMBOL >> 10) & 0x3fffff); \ + REL[2] = 0x81c06000 | (SYMBOL & 0x3ff); \ + break; \ + case R_SPARC_NONE: \ + break; \ + case R_SPARC_WDISP30: \ + break; \ + case R_SPARC_RELATIVE: \ + *REL += (unsigned int) LOAD + (RELP)->r_addend; \ + break; \ + default: \ + _dl_exit(1); \ + } + + +/* + * Transfer control to the user's application, once the dynamic loader + * is done. The crt calls atexit with $g1 if not null, so we need to + * ensure that it contains NULL. + */ + +#define START() \ + __asm__ volatile ( \ + "add %%g0,%%g0,%%g1\n\t" \ + "jmpl %0, %%o7\n\t" \ + "restore %%g0,%%g0,%%g0\n\t" \ + : /*"=r" (status) */ : \ + "r" (_dl_elf_main): "g1", "o0", "o1") + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_SPARC +#undef MAGIC2 +/* Used for error messages */ +#define ELF_TARGET "Sparc" + +#ifndef COMPILE_ASM +extern unsigned int _dl_linux_resolver(unsigned int reloc_entry, + unsigned int * i); +#endif + +/* + * Define this if you want a dynamic loader that works on Solaris. + */ +#define SOLARIS_COMPATIBLE + +/* + * Define this because we do not want to call .udiv in the library. + * Change on the plans -miguel: + * We just statically link against .udiv. This is required + * if we want to be able to run on Sun4c machines. + */ + +/* We now link .urem against this one */ +#ifdef USE_V8 +#define do_rem(result,n,base) ({ \ +volatile int __res; \ +__asm__("mov %%g0,%%Y\n\t" \ + "sdiv %2,%3,%%l6\n\t" \ + "smul %%l6,%3,%%l6\n\t" \ + "sub %2,%%l6,%0\n\t" \ + :"=r" (result),"=r" (__res):"r" (n),"r"(base) : "l6" ); __res; }) +#else +#define do_rem(a,b,c) a = _dl_urem (b,c); +#endif +/* + * dbx wants the binder to have a specific name. Mustn't disappoint it. + */ +#ifdef SOLARIS_COMPATIBLE +#define _dl_linux_resolve _elf_rtbndr +#endif + diff --git a/ldso/ldso/sparc/elfinterp.c b/ldso/ldso/sparc/elfinterp.c new file mode 100644 index 000000000..6f0d9f8fd --- /dev/null +++ b/ldso/ldso/sparc/elfinterp.c @@ -0,0 +1,355 @@ +/* Run an ELF binary on a linux system. + + Copyright (C) 1995, Eric Youngdale. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef VERBOSE_DLINKER +#define VERBOSE_DLINKER +#endif +#ifdef VERBOSE_DLINKER +static char * _dl_reltypes[] = { "R_SPARC_NONE", "R_SPARC_8", + "R_SPARC_16", "R_SPARC_32", "R_SPARC_DISP8", "R_SPARC_DISP16", + "R_SPARC_DISP32", "R_SPARC_WDISP30", "R_SPARC_WDISP22", + "R_SPARC_HI22", "R_SPARC_22", "R_SPARC_13", "R_SPARC_LO10", + "R_SPARC_GOT10", "R_SPARC_GOT13", "R_SPARC_GOT22", "R_SPARC_PC10", + "R_SPARC_PC22", "R_SPARC_WPLT30", "R_SPARC_COPY", + "R_SPARC_GLOB_DAT", "R_SPARC_JMP_SLOT", "R_SPARC_RELATIVE", + "R_SPARC_UA32"}; +#endif + +/* Program to load an ELF binary on a linux system, and run it. +References to symbols in sharable libraries can be resolved by either +an ELF sharable library or a linux style of shared library. */ + +/* Disclaimer: I have never seen any AT&T source code for SVr4, nor have + I ever taken any courses on internals. This program was developed using + information available through the book "UNIX SYSTEM V RELEASE 4, + Programmers guide: Ansi C and Programming Support Tools", which did + a more than adequate job of explaining everything required to get this + working. */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/unistd.h> +/*#include <stdlib.h>*/ +#include "string.h" +#include <linux/unistd.h> +#include <linux/fcntl.h> +#include "hash.h" +#include "linuxelf.h" +#include "sysdep.h" +#include "../syscall.h" +#include "../string.h" + +#define SVR4_COMPATIBILITY + +extern char *_dl_progname; + +extern _dl_linux_resolve(void); + +unsigned int _dl_linux_resolver(unsigned int reloc_entry, unsigned int * plt) +{ + int reloc_type; + struct elf32_rela * this_reloc; + char * strtab; + struct elf32_sym * symtab; + struct elf32_rela * rel_addr; + struct elf_resolve * tpnt; + int symtab_index; + char * new_addr; + char ** got_addr; + unsigned int instr_addr; + tpnt = (struct elf_resolve *) plt[2]; + + rel_addr = (struct elf32_rela *) (tpnt->dynamic_info[DT_JMPREL] + + tpnt->loadaddr); + + /* + * Generate the correct relocation index into the .rela.plt section. + */ + reloc_entry = (reloc_entry >> 12) - 0xc; + + this_reloc = (struct elf32_rela *) ((char *) rel_addr + reloc_entry); + + reloc_type = ELF32_R_TYPE(this_reloc->r_info); + symtab_index = ELF32_R_SYM(this_reloc->r_info); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + _dl_fdprintf(2, "tpnt = %x\n", tpnt); + _dl_fdprintf(2, "reloc = %x\n", this_reloc); + _dl_fdprintf(2, "symtab = %x\n", symtab); + _dl_fdprintf(2, "strtab = %x\n", strtab); + + + if (reloc_type != R_SPARC_JMP_SLOT) { + _dl_fdprintf(2, "%s: incorrect relocation type in jump relocations (%d)\n", + _dl_progname, reloc_type); + _dl_exit(30); + }; + + /* Address of jump instruction to fix up */ + instr_addr = ((int)this_reloc->r_offset + (int)tpnt->loadaddr); + got_addr = (char **) instr_addr; + + _dl_fdprintf(2, "symtab_index %d\n", symtab_index); + +#ifdef DEBUG + _dl_fdprintf(2, "Resolving symbol %s\n", + strtab + symtab[symtab_index].st_name); +#endif + + /* Get the address of the GOT entry */ + new_addr = _dl_find_hash(strtab + symtab[symtab_index].st_name, + tpnt->symbol_scope, (int) got_addr, tpnt, 0); + if(!new_addr) { + _dl_fdprintf(2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + _dl_exit(31); + }; +/* #define DEBUG_LIBRARY */ +#ifdef DEBUG_LIBRARY + if((unsigned int) got_addr < 0x40000000) { + _dl_fdprintf(2, "Calling library function: %s\n", + strtab + symtab[symtab_index].st_name); + } else { + got_addr[1] = (char *) (0x03000000 | (((unsigned int) new_addr >> 10) & 0x3fffff)); + got_addr[2] = (char *) (0x81c06000 | ((unsigned int) new_addr & 0x3ff)); + } +#else + got_addr[1] = (char *) (0x03000000 | (((unsigned int) new_addr >> 10) & 0x3fffff)); + got_addr[2] = (char *) (0x81c06000 | ((unsigned int) new_addr & 0x3ff)); +#endif + _dl_fdprintf(2, "Address = %x\n",new_addr); + _dl_exit(32); + + return (unsigned int) new_addr; +} + +void _dl_parse_lazy_relocation_information(struct elf_resolve * tpnt, int rel_addr, + int rel_size, int type){ + int i; + char * strtab; + int reloc_type; + int symtab_index; + struct elf32_sym * symtab; + struct elf32_rela * rpnt; + unsigned int * reloc_addr; + + /* Now parse the relocation information */ + rpnt = (struct elf32_rela *) (rel_addr + tpnt->loadaddr); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = ( char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for(i=0; i< rel_size; i += sizeof(struct elf32_rela), rpnt++){ + reloc_addr = (int *) (tpnt->loadaddr + (int)rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + symtab_index = ELF32_R_SYM(rpnt->r_info); + + /* When the dynamic linker bootstrapped itself, it resolved some symbols. + Make sure we do not do them again */ + if(!symtab_index && tpnt->libtype == program_interpreter) continue; + if(symtab_index && tpnt->libtype == program_interpreter && + _dl_symbol(strtab + symtab[symtab_index].st_name)) + continue; + + switch(reloc_type){ + case R_SPARC_NONE: + break; + case R_SPARC_JMP_SLOT: + break; + default: + _dl_fdprintf(2, "%s: (LAZY) can't handle reloc type ", _dl_progname); +#ifdef VERBOSE_DLINKER + _dl_fdprintf(2, "%s ", _dl_reltypes[reloc_type]); +#endif + if(symtab_index) _dl_fdprintf(2, "'%s'\n", + strtab + symtab[symtab_index].st_name); + _dl_exit(33); + }; + }; +} + +int _dl_parse_relocation_information(struct elf_resolve * tpnt, int rel_addr, + int rel_size, int type){ + int i; + char * strtab; + int reloc_type; + int goof = 0; + struct elf32_sym * symtab; + struct elf32_rela * rpnt; + unsigned int * reloc_addr; + unsigned int symbol_addr; + int symtab_index; + /* Now parse the relocation information */ + + rpnt = (struct elf32_rela *) (rel_addr + tpnt->loadaddr); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = ( char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for(i=0; i< rel_size; i+= sizeof(struct elf32_rela), rpnt++){ + reloc_addr = (int *) (tpnt->loadaddr + (int)rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + + if(!symtab_index && tpnt->libtype == program_interpreter) continue; + + if(symtab_index) { + + if(tpnt->libtype == program_interpreter && + _dl_symbol(strtab + symtab[symtab_index].st_name)) + continue; + + symbol_addr = (unsigned int) + _dl_find_hash(strtab + symtab[symtab_index].st_name, + tpnt->symbol_scope, (int) reloc_addr, + (reloc_type == R_SPARC_JMP_SLOT ? tpnt : NULL), 0); + + if(!symbol_addr && + ELF32_ST_BIND(symtab [symtab_index].st_info) == STB_GLOBAL) { + _dl_fdprintf(2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + goof++; + }; + }; + switch(reloc_type){ + case R_SPARC_NONE: + break; + case R_SPARC_32: + *reloc_addr = symbol_addr + rpnt->r_addend; + break; + case R_SPARC_DISP32: + *reloc_addr = symbol_addr + rpnt->r_addend - (unsigned int) reloc_addr; + break; + case R_SPARC_GLOB_DAT: + *reloc_addr = symbol_addr + rpnt->r_addend; + break; + case R_SPARC_JMP_SLOT: + reloc_addr[1] = 0x03000000 | ((symbol_addr >> 10) & 0x3fffff); + reloc_addr[2] = 0x81c06000 | (symbol_addr & 0x3ff); + break; + case R_SPARC_RELATIVE: + *reloc_addr += (unsigned int) tpnt->loadaddr + rpnt->r_addend; + break; + case R_SPARC_HI22: + if (!symbol_addr) + symbol_addr = tpnt->loadaddr + rpnt->r_addend; + else + symbol_addr += rpnt->r_addend; + *reloc_addr = (*reloc_addr & 0xffc00000)|(symbol_addr >> 10); + break; + case R_SPARC_LO10: + if (!symbol_addr) + symbol_addr = tpnt->loadaddr + rpnt->r_addend; + else + symbol_addr += rpnt->r_addend; + *reloc_addr = (*reloc_addr & ~0x3ff)|(symbol_addr & 0x3ff); + break; + case R_SPARC_WDISP30: + *reloc_addr = (*reloc_addr & 0xc0000000)| + ((symbol_addr - (unsigned int) reloc_addr) >> 2); + break; + case R_SPARC_COPY: +#if 0 /* This one is done later */ + _dl_fdprintf(2, "Doing copy for symbol "); + if(symtab_index) _dl_fdprintf(2, strtab + symtab[symtab_index].st_name); + _dl_fdprintf(2, "\n"); + _dl_memcpy((void *) symtab[symtab_index].st_value, + (void *) symbol_addr, + symtab[symtab_index].st_size); +#endif + break; + default: + _dl_fdprintf(2, "%s: can't handle reloc type ", _dl_progname); +#ifdef VERBOSE_DLINKER + _dl_fdprintf(2, "%s ", _dl_reltypes[reloc_type]); +#endif + if (symtab_index) + _dl_fdprintf(2, "'%s'\n", strtab + symtab[symtab_index].st_name); + _dl_exit(34); + }; + + }; + return goof; +} + + +/* This is done as a separate step, because there are cases where + information is first copied and later initialized. This results in + the wrong information being copied. Someone at Sun was complaining about + a bug in the handling of _COPY by SVr4, and this may in fact be what he + was talking about. Sigh. */ + +/* No, there are cases where the SVr4 linker fails to emit COPY relocs + at all */ + +int _dl_parse_copy_information(struct dyn_elf * xpnt, int rel_addr, + int rel_size, int type) +{ + int i; + char * strtab; + int reloc_type; + int goof = 0; + struct elf32_sym * symtab; + struct elf32_rela * rpnt; + unsigned int * reloc_addr; + unsigned int symbol_addr; + struct elf_resolve *tpnt; + int symtab_index; + /* Now parse the relocation information */ + + tpnt = xpnt->dyn; + + rpnt = (struct elf32_rela *) (rel_addr + tpnt->loadaddr); + + symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); + strtab = ( char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); + + for(i=0; i< rel_size; i+= sizeof(struct elf32_rela), rpnt++){ + reloc_addr = (int *) (tpnt->loadaddr + (int)rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + if(reloc_type != R_SPARC_COPY) continue; + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + if(!symtab_index && tpnt->libtype == program_interpreter) continue; + if(symtab_index) { + + if(tpnt->libtype == program_interpreter && + _dl_symbol(strtab + symtab[symtab_index].st_name)) + continue; + + symbol_addr = (unsigned int) + _dl_find_hash(strtab + symtab[symtab_index].st_name, + xpnt->next, (int) reloc_addr, NULL, 1); + if(!symbol_addr) { + _dl_fdprintf(2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + goof++; + }; + }; + if (!goof) + _dl_memcpy((char *) symtab[symtab_index].st_value, + (char *) symbol_addr, + symtab[symtab_index].st_size); + }; + return goof; +} + + diff --git a/ldso/ldso/sparc/ld_sysdep.h b/ldso/ldso/sparc/ld_sysdep.h new file mode 100644 index 000000000..1d4c0354f --- /dev/null +++ b/ldso/ldso/sparc/ld_sysdep.h @@ -0,0 +1,131 @@ + +/* + * Various assmbly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + */ +#define LINUXBIN + +/* + * Define this if the system uses RELOCA. + */ +#define ELF_USES_RELOCA + +/* + * Get the address of the Global offset table. This must be absolute, not + * relative. + */ +#define GET_GOT(X) __asm__("\tmov %%l7,%0\n\t" : "=r" (X)) + +/* + * Get a pointer to the argv array. On many platforms this can be just + * the address if the first argument, on other platforms we need to + * do something a little more subtle here. We assume that argc is stored + * at the word just below the argvp that we return here. + */ +#define GET_ARGV(ARGVP, ARGS) __asm__("\tadd %%fp,68,%0\n" : "=r" (ARGVP)); + +/* + * Initialization sequence for a GOT. For the Sparc, this points to the + * PLT, and we need to initialize a couple of the slots. The PLT should + * look like: + * + * save %sp, -64, %sp + * call _dl_linux_resolve + * nop + * .word implementation_dependent + */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[0] = 0x9de3bfc0; /* save %sp, -64, %sp */ \ + GOT_BASE[1] = 0x40000000 | (((unsigned int) _dl_linux_resolve - (unsigned int) GOT_BASE - 4) >> 2); \ + GOT_BASE[2] = 0x01000000; /* nop */ \ + GOT_BASE[3] = (int) MODULE; \ +} + +/* + * Here is a macro to perform a relocation. This is only used when + * bootstrapping the dynamic loader. + */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch(ELF32_R_TYPE((RELP)->r_info)) { \ + case R_SPARC_32: \ + *REL = SYMBOL + (RELP)->r_addend; \ + break; \ + case R_SPARC_GLOB_DAT: \ + *REL = SYMBOL + (RELP)->r_addend; \ + break; \ + case R_SPARC_JMP_SLOT: \ + REL[1] = 0x03000000 | ((SYMBOL >> 10) & 0x3fffff); \ + REL[2] = 0x81c06000 | (SYMBOL & 0x3ff); \ + break; \ + case R_SPARC_NONE: \ + break; \ + case R_SPARC_WDISP30: \ + break; \ + case R_SPARC_RELATIVE: \ + *REL += (unsigned int) LOAD + (RELP)->r_addend; \ + break; \ + default: \ + _dl_exit(1); \ + } + + +/* + * Transfer control to the user's application, once the dynamic loader + * is done. The crt calls atexit with $g1 if not null, so we need to + * ensure that it contains NULL. + */ + +#define START() \ + __asm__ volatile ( \ + "add %%g0,%%g0,%%g1\n\t" \ + "jmpl %0, %%o7\n\t" \ + "restore %%g0,%%g0,%%g0\n\t" \ + : /*"=r" (status) */ : \ + "r" (_dl_elf_main): "g1", "o0", "o1") + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_SPARC +#undef MAGIC2 +/* Used for error messages */ +#define ELF_TARGET "Sparc" + +#ifndef COMPILE_ASM +extern unsigned int _dl_linux_resolver(unsigned int reloc_entry, + unsigned int * i); +#endif + +/* + * Define this if you want a dynamic loader that works on Solaris. + */ +#define SOLARIS_COMPATIBLE + +/* + * Define this because we do not want to call .udiv in the library. + * Change on the plans -miguel: + * We just statically link against .udiv. This is required + * if we want to be able to run on Sun4c machines. + */ + +/* We now link .urem against this one */ +#ifdef USE_V8 +#define do_rem(result,n,base) ({ \ +volatile int __res; \ +__asm__("mov %%g0,%%Y\n\t" \ + "sdiv %2,%3,%%l6\n\t" \ + "smul %%l6,%3,%%l6\n\t" \ + "sub %2,%%l6,%0\n\t" \ + :"=r" (result),"=r" (__res):"r" (n),"r"(base) : "l6" ); __res; }) +#else +#define do_rem(a,b,c) a = _dl_urem (b,c); +#endif +/* + * dbx wants the binder to have a specific name. Mustn't disappoint it. + */ +#ifdef SOLARIS_COMPATIBLE +#define _dl_linux_resolve _elf_rtbndr +#endif + diff --git a/ldso/ldso/sparc/resolve.S b/ldso/ldso/sparc/resolve.S new file mode 100644 index 000000000..ea985b5c8 --- /dev/null +++ b/ldso/ldso/sparc/resolve.S @@ -0,0 +1,25 @@ +/* + * These are various helper routines that are needed to run an ELF image. + */ +#define COMPILE_ASM +#include "sysdep.h" + +.text + .align 16 + +.globl _dl_linux_resolve +_dl_linux_resolve: + /* + * Call the resolver - pass the address of the PLT so that we can + * figure out which module we are in. + */ + mov %o7,%o1 + call _dl_linux_resolver + mov %g1,%o0 + + jmpl %o0,%o7 + restore +.LFE2: + + .type _dl_linux_resolve,#function + .size _dl_linux_resolve,.LFE2-_dl_linux_resolve diff --git a/ldso/ldso/sparc/sdiv.S b/ldso/ldso/sparc/sdiv.S new file mode 100644 index 000000000..5e52e1959 --- /dev/null +++ b/ldso/ldso/sparc/sdiv.S @@ -0,0 +1,369 @@ + /* This file is generated from divrem.m4; DO NOT EDIT! */ +/* + * Division and remainder, from Appendix E of the Sparc Version 8 + * Architecture Manual, with fixes from Gordon Irlam. + */ + +/* + * Input: dividend and divisor in %o0 and %o1 respectively. + * + * m4 parameters: + * .div name of function to generate + * div div=div => %o0 / %o1; div=rem => %o0 % %o1 + * true true=true => signed; true=false => unsigned + * + * Algorithm parameters: + * N how many bits per iteration we try to get (4) + * WORDSIZE total number of bits (32) + * + * Derived constants: + * TOPBITS number of bits in the top decade of a number + * + * Important variables: + * Q the partial quotient under development (initially 0) + * R the remainder so far, initially the dividend + * ITER number of main division loop iterations required; + * equal to ceil(log2(quotient) / N). Note that this + * is the log base (2^N) of the quotient. + * V the current comparand, initially divisor*2^(ITER*N-1) + * + * Cost: + * Current estimate for non-large dividend is + * ceil(log2(quotient) / N) * (10 + 7N/2) + C + * A large dividend is one greater than 2^(31-TOPBITS) and takes a + * different path, as the upper bits of the quotient must be developed + * one bit at a time. + */ + + + +#include "DEFS.h" +#ifndef __linux__ +#ifdef __svr4__ +#include <sys/trap.h> +#else +#include "/usr/include/machine/trap.h" +#endif +#else +#include <asm/traps.h> +#endif + +FUNC(_dl_div) + ! compute sign of result; if neither is negative, no problem + orcc %o1, %o0, %g0 ! either negative? + bge 2f ! no, go do the divide + xor %o1, %o0, %g6 ! compute sign in any case + tst %o1 + bge 1f + tst %o0 + ! %o1 is definitely negative; %o0 might also be negative + bge 2f ! if %o0 not negative... + sub %g0, %o1, %o1 ! in any case, make %o1 nonneg +1: ! %o0 is negative, %o1 is nonnegative + sub %g0, %o0, %o0 ! make %o0 nonnegative +2: + + ! Ready to divide. Compute size of quotient; scale comparand. + orcc %o1, %g0, %o5 + bne 1f + mov %o0, %o3 + + ! Divide by zero trap. If it returns, return 0 (about as + ! wrong as possible, but that is what SunOS does...). + ta ST_DIV0 + retl + clr %o0 + +1: + cmp %o3, %o5 ! if %o1 exceeds %o0, done + blu Lgot_result ! (and algorithm fails otherwise) + clr %o2 + sethi %hi(1 << (32 - 4 - 1)), %g1 + cmp %o3, %g1 + blu Lnot_really_big + clr %o4 + + ! Here the dividend is >= 2**(31-N) or so. We must be careful here, + ! as our usual N-at-a-shot divide step will cause overflow and havoc. + ! The number of bits in the result here is N*ITER+SC, where SC <= N. + ! Compute ITER in an unorthodox manner: know we need to shift V into + ! the top decade: so do not even bother to compare to R. + 1: + cmp %o5, %g1 + bgeu 3f + mov 1, %g7 + sll %o5, 4, %o5 + b 1b + add %o4, 1, %o4 + + ! Now compute %g7. + 2: addcc %o5, %o5, %o5 + bcc Lnot_too_big + add %g7, 1, %g7 + + ! We get here if the %o1 overflowed while shifting. + ! This means that %o3 has the high-order bit set. + ! Restore %o5 and subtract from %o3. + sll %g1, 4, %g1 ! high order bit + srl %o5, 1, %o5 ! rest of %o5 + add %o5, %g1, %o5 + b Ldo_single_div + sub %g7, 1, %g7 + + Lnot_too_big: + 3: cmp %o5, %o3 + blu 2b + nop + be Ldo_single_div + nop + /* NB: these are commented out in the V8-Sparc manual as well */ + /* (I do not understand this) */ + ! %o5 > %o3: went too far: back up 1 step + ! srl %o5, 1, %o5 + ! dec %g7 + ! do single-bit divide steps + ! + ! We have to be careful here. We know that %o3 >= %o5, so we can do the + ! first divide step without thinking. BUT, the others are conditional, + ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high- + ! order bit set in the first step, just falling into the regular + ! division loop will mess up the first time around. + ! So we unroll slightly... + Ldo_single_div: + subcc %g7, 1, %g7 + bl Lend_regular_divide + nop + sub %o3, %o5, %o3 + mov 1, %o2 + b Lend_single_divloop + nop + Lsingle_divloop: + sll %o2, 1, %o2 + bl 1f + srl %o5, 1, %o5 + ! %o3 >= 0 + sub %o3, %o5, %o3 + b 2f + add %o2, 1, %o2 + 1: ! %o3 < 0 + add %o3, %o5, %o3 + sub %o2, 1, %o2 + 2: + Lend_single_divloop: + subcc %g7, 1, %g7 + bge Lsingle_divloop + tst %o3 + b,a Lend_regular_divide + +Lnot_really_big: +1: + sll %o5, 4, %o5 + cmp %o5, %o3 + bleu 1b + addcc %o4, 1, %o4 + be Lgot_result + sub %o4, 1, %o4 + + tst %o3 ! set up for initial iteration +Ldivloop: + sll %o2, 4, %o2 + ! depth 1, accumulated bits 0 + bl L.1.16 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 2, accumulated bits 1 + bl L.2.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits 3 + bl L.3.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 7 + bl L.4.23 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (7*2+1), %o2 + +L.4.23: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (7*2-1), %o2 + + +L.3.19: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 5 + bl L.4.21 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (5*2+1), %o2 + +L.4.21: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (5*2-1), %o2 + + + +L.2.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits 1 + bl L.3.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 3 + bl L.4.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (3*2+1), %o2 + +L.4.19: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (3*2-1), %o2 + + +L.3.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 1 + bl L.4.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (1*2+1), %o2 + +L.4.17: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (1*2-1), %o2 + + + + +L.1.16: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 2, accumulated bits -1 + bl L.2.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits -1 + bl L.3.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -1 + bl L.4.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2+1), %o2 + +L.4.15: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2-1), %o2 + + +L.3.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -3 + bl L.4.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2+1), %o2 + +L.4.13: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2-1), %o2 + + + +L.2.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits -3 + bl L.3.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -5 + bl L.4.11 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2+1), %o2 + +L.4.11: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2-1), %o2 + + +L.3.13: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -7 + bl L.4.9 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2+1), %o2 + +L.4.9: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2-1), %o2 + + + + + 9: +Lend_regular_divide: + subcc %o4, 1, %o4 + bge Ldivloop + tst %o3 + bl,a Lgot_result + ! non-restoring fixup here (one instruction only!) + sub %o2, 1, %o2 + + +Lgot_result: + ! check to see if answer should be < 0 + tst %g6 + bl,a 1f + sub %g0, %o2, %o2 +1: + retl + mov %o2, %o0 diff --git a/ldso/ldso/sparc/sysdep.h b/ldso/ldso/sparc/sysdep.h new file mode 100644 index 000000000..1d4c0354f --- /dev/null +++ b/ldso/ldso/sparc/sysdep.h @@ -0,0 +1,131 @@ + +/* + * Various assmbly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + */ +#define LINUXBIN + +/* + * Define this if the system uses RELOCA. + */ +#define ELF_USES_RELOCA + +/* + * Get the address of the Global offset table. This must be absolute, not + * relative. + */ +#define GET_GOT(X) __asm__("\tmov %%l7,%0\n\t" : "=r" (X)) + +/* + * Get a pointer to the argv array. On many platforms this can be just + * the address if the first argument, on other platforms we need to + * do something a little more subtle here. We assume that argc is stored + * at the word just below the argvp that we return here. + */ +#define GET_ARGV(ARGVP, ARGS) __asm__("\tadd %%fp,68,%0\n" : "=r" (ARGVP)); + +/* + * Initialization sequence for a GOT. For the Sparc, this points to the + * PLT, and we need to initialize a couple of the slots. The PLT should + * look like: + * + * save %sp, -64, %sp + * call _dl_linux_resolve + * nop + * .word implementation_dependent + */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[0] = 0x9de3bfc0; /* save %sp, -64, %sp */ \ + GOT_BASE[1] = 0x40000000 | (((unsigned int) _dl_linux_resolve - (unsigned int) GOT_BASE - 4) >> 2); \ + GOT_BASE[2] = 0x01000000; /* nop */ \ + GOT_BASE[3] = (int) MODULE; \ +} + +/* + * Here is a macro to perform a relocation. This is only used when + * bootstrapping the dynamic loader. + */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD) \ + switch(ELF32_R_TYPE((RELP)->r_info)) { \ + case R_SPARC_32: \ + *REL = SYMBOL + (RELP)->r_addend; \ + break; \ + case R_SPARC_GLOB_DAT: \ + *REL = SYMBOL + (RELP)->r_addend; \ + break; \ + case R_SPARC_JMP_SLOT: \ + REL[1] = 0x03000000 | ((SYMBOL >> 10) & 0x3fffff); \ + REL[2] = 0x81c06000 | (SYMBOL & 0x3ff); \ + break; \ + case R_SPARC_NONE: \ + break; \ + case R_SPARC_WDISP30: \ + break; \ + case R_SPARC_RELATIVE: \ + *REL += (unsigned int) LOAD + (RELP)->r_addend; \ + break; \ + default: \ + _dl_exit(1); \ + } + + +/* + * Transfer control to the user's application, once the dynamic loader + * is done. The crt calls atexit with $g1 if not null, so we need to + * ensure that it contains NULL. + */ + +#define START() \ + __asm__ volatile ( \ + "add %%g0,%%g0,%%g1\n\t" \ + "jmpl %0, %%o7\n\t" \ + "restore %%g0,%%g0,%%g0\n\t" \ + : /*"=r" (status) */ : \ + "r" (_dl_elf_main): "g1", "o0", "o1") + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_SPARC +#undef MAGIC2 +/* Used for error messages */ +#define ELF_TARGET "Sparc" + +#ifndef COMPILE_ASM +extern unsigned int _dl_linux_resolver(unsigned int reloc_entry, + unsigned int * i); +#endif + +/* + * Define this if you want a dynamic loader that works on Solaris. + */ +#define SOLARIS_COMPATIBLE + +/* + * Define this because we do not want to call .udiv in the library. + * Change on the plans -miguel: + * We just statically link against .udiv. This is required + * if we want to be able to run on Sun4c machines. + */ + +/* We now link .urem against this one */ +#ifdef USE_V8 +#define do_rem(result,n,base) ({ \ +volatile int __res; \ +__asm__("mov %%g0,%%Y\n\t" \ + "sdiv %2,%3,%%l6\n\t" \ + "smul %%l6,%3,%%l6\n\t" \ + "sub %2,%%l6,%0\n\t" \ + :"=r" (result),"=r" (__res):"r" (n),"r"(base) : "l6" ); __res; }) +#else +#define do_rem(a,b,c) a = _dl_urem (b,c); +#endif +/* + * dbx wants the binder to have a specific name. Mustn't disappoint it. + */ +#ifdef SOLARIS_COMPATIBLE +#define _dl_linux_resolve _elf_rtbndr +#endif + diff --git a/ldso/ldso/sparc/udiv.S b/ldso/ldso/sparc/udiv.S new file mode 100644 index 000000000..df4e5385e --- /dev/null +++ b/ldso/ldso/sparc/udiv.S @@ -0,0 +1,351 @@ + /* This file is generated from divrem.m4; DO NOT EDIT! */ +/* + * Division and remainder, from Appendix E of the Sparc Version 8 + * Architecture Manual, with fixes from Gordon Irlam. + */ + +/* + * Input: dividend and divisor in %o0 and %o1 respectively. + * + * m4 parameters: + * .udiv name of function to generate + * div div=div => %o0 / %o1; div=rem => %o0 % %o1 + * false false=true => signed; false=false => unsigned + * + * Algorithm parameters: + * N how many bits per iteration we try to get (4) + * WORDSIZE total number of bits (32) + * + * Derived constants: + * TOPBITS number of bits in the top decade of a number + * + * Important variables: + * Q the partial quotient under development (initially 0) + * R the remainder so far, initially the dividend + * ITER number of main division loop iterations required; + * equal to ceil(log2(quotient) / N). Note that this + * is the log base (2^N) of the quotient. + * V the current comparand, initially divisor*2^(ITER*N-1) + * + * Cost: + * Current estimate for non-large dividend is + * ceil(log2(quotient) / N) * (10 + 7N/2) + C + * A large dividend is one greater than 2^(31-TOPBITS) and takes a + * different path, as the upper bits of the quotient must be developed + * one bit at a time. + */ + + +#include "DEFS.h" +#ifndef __linux__ +#ifdef __svr4__ +#include <sys/trap.h> +#else +#include "/usr/include/machine/trap.h" +#endif +#else +#include <asm/traps.h> +#endif + +FUNC(_dl_udiv) + + ! Ready to divide. Compute size of quotient; scale comparand. + orcc %o1, %g0, %o5 + bne 1f + mov %o0, %o3 + + ! Divide by zero trap. If it returns, return 0 (about as + ! wrong as possible, but that is what SunOS does...). + ta ST_DIV0 + retl + clr %o0 + +1: + cmp %o3, %o5 ! if %o1 exceeds %o0, done + blu Lgot_result ! (and algorithm fails otherwise) + clr %o2 + sethi %hi(1 << (32 - 4 - 1)), %g1 + cmp %o3, %g1 + blu Lnot_really_big + clr %o4 + + ! Here the dividend is >= 2**(31-N) or so. We must be careful here, + ! as our usual N-at-a-shot divide step will cause overflow and havoc. + ! The number of bits in the result here is N*ITER+SC, where SC <= N. + ! Compute ITER in an unorthodox manner: know we need to shift V into + ! the top decade: so do not even bother to compare to R. + 1: + cmp %o5, %g1 + bgeu 3f + mov 1, %g7 + sll %o5, 4, %o5 + b 1b + add %o4, 1, %o4 + + ! Now compute %g7. + 2: addcc %o5, %o5, %o5 + bcc Lnot_too_big + add %g7, 1, %g7 + + ! We get here if the %o1 overflowed while shifting. + ! This means that %o3 has the high-order bit set. + ! Restore %o5 and subtract from %o3. + sll %g1, 4, %g1 ! high order bit + srl %o5, 1, %o5 ! rest of %o5 + add %o5, %g1, %o5 + b Ldo_single_div + sub %g7, 1, %g7 + + Lnot_too_big: + 3: cmp %o5, %o3 + blu 2b + nop + be Ldo_single_div + nop + /* NB: these are commented out in the V8-Sparc manual as well */ + /* (I do not understand this) */ + ! %o5 > %o3: went too far: back up 1 step + ! srl %o5, 1, %o5 + ! dec %g7 + ! do single-bit divide steps + ! + ! We have to be careful here. We know that %o3 >= %o5, so we can do the + ! first divide step without thinking. BUT, the others are conditional, + ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high- + ! order bit set in the first step, just falling into the regular + ! division loop will mess up the first time around. + ! So we unroll slightly... + Ldo_single_div: + subcc %g7, 1, %g7 + bl Lend_regular_divide + nop + sub %o3, %o5, %o3 + mov 1, %o2 + b Lend_single_divloop + nop + Lsingle_divloop: + sll %o2, 1, %o2 + bl 1f + srl %o5, 1, %o5 + ! %o3 >= 0 + sub %o3, %o5, %o3 + b 2f + add %o2, 1, %o2 + 1: ! %o3 < 0 + add %o3, %o5, %o3 + sub %o2, 1, %o2 + 2: + Lend_single_divloop: + subcc %g7, 1, %g7 + bge Lsingle_divloop + tst %o3 + b,a Lend_regular_divide + +Lnot_really_big: +1: + sll %o5, 4, %o5 + cmp %o5, %o3 + bleu 1b + addcc %o4, 1, %o4 + be Lgot_result + sub %o4, 1, %o4 + + tst %o3 ! set up for initial iteration +Ldivloop: + sll %o2, 4, %o2 + ! depth 1, accumulated bits 0 + bl L.1.16 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 2, accumulated bits 1 + bl L.2.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits 3 + bl L.3.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 7 + bl L.4.23 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (7*2+1), %o2 + +L.4.23: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (7*2-1), %o2 + + +L.3.19: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 5 + bl L.4.21 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (5*2+1), %o2 + +L.4.21: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (5*2-1), %o2 + + + +L.2.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits 1 + bl L.3.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 3 + bl L.4.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (3*2+1), %o2 + +L.4.19: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (3*2-1), %o2 + + +L.3.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 1 + bl L.4.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (1*2+1), %o2 + +L.4.17: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (1*2-1), %o2 + + + + +L.1.16: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 2, accumulated bits -1 + bl L.2.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits -1 + bl L.3.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -1 + bl L.4.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2+1), %o2 + +L.4.15: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2-1), %o2 + + +L.3.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -3 + bl L.4.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2+1), %o2 + +L.4.13: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2-1), %o2 + + + +L.2.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits -3 + bl L.3.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -5 + bl L.4.11 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2+1), %o2 + +L.4.11: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2-1), %o2 + + +L.3.13: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -7 + bl L.4.9 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2+1), %o2 + +L.4.9: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2-1), %o2 + + + + + 9: +Lend_regular_divide: + subcc %o4, 1, %o4 + bge Ldivloop + tst %o3 + bl,a Lgot_result + ! non-restoring fixup here (one instruction only!) + sub %o2, 1, %o2 + + +Lgot_result: + + retl + mov %o2, %o0 diff --git a/ldso/ldso/sparc/umul.S b/ldso/ldso/sparc/umul.S new file mode 100644 index 000000000..7a26c295c --- /dev/null +++ b/ldso/ldso/sparc/umul.S @@ -0,0 +1,153 @@ +/* + * Unsigned multiply. Returns %o0 * %o1 in %o1%o0 (i.e., %o1 holds the + * upper 32 bits of the 64-bit product). + * + * This code optimizes short (less than 13-bit) multiplies. Short + * multiplies require 25 instruction cycles, and long ones require + * 45 instruction cycles. + * + * On return, overflow has occurred (%o1 is not zero) if and only if + * the Z condition code is clear, allowing, e.g., the following: + * + * call .umul + * nop + * bnz overflow (or tnz) + */ + +#include "DEFS.h" +FUNC(.umul) + or %o0, %o1, %o4 + mov %o0, %y ! multiplier -> Y + andncc %o4, 0xfff, %g0 ! test bits 12..31 of *both* args + be Lmul_shortway ! if zero, can do it the short way + andcc %g0, %g0, %o4 ! zero the partial product and clear N and V + + /* + * Long multiply. 32 steps, followed by a final shift step. + */ + mulscc %o4, %o1, %o4 ! 1 + mulscc %o4, %o1, %o4 ! 2 + mulscc %o4, %o1, %o4 ! 3 + mulscc %o4, %o1, %o4 ! 4 + mulscc %o4, %o1, %o4 ! 5 + mulscc %o4, %o1, %o4 ! 6 + mulscc %o4, %o1, %o4 ! 7 + mulscc %o4, %o1, %o4 ! 8 + mulscc %o4, %o1, %o4 ! 9 + mulscc %o4, %o1, %o4 ! 10 + mulscc %o4, %o1, %o4 ! 11 + mulscc %o4, %o1, %o4 ! 12 + mulscc %o4, %o1, %o4 ! 13 + mulscc %o4, %o1, %o4 ! 14 + mulscc %o4, %o1, %o4 ! 15 + mulscc %o4, %o1, %o4 ! 16 + mulscc %o4, %o1, %o4 ! 17 + mulscc %o4, %o1, %o4 ! 18 + mulscc %o4, %o1, %o4 ! 19 + mulscc %o4, %o1, %o4 ! 20 + mulscc %o4, %o1, %o4 ! 21 + mulscc %o4, %o1, %o4 ! 22 + mulscc %o4, %o1, %o4 ! 23 + mulscc %o4, %o1, %o4 ! 24 + mulscc %o4, %o1, %o4 ! 25 + mulscc %o4, %o1, %o4 ! 26 + mulscc %o4, %o1, %o4 ! 27 + mulscc %o4, %o1, %o4 ! 28 + mulscc %o4, %o1, %o4 ! 29 + mulscc %o4, %o1, %o4 ! 30 + mulscc %o4, %o1, %o4 ! 31 + mulscc %o4, %o1, %o4 ! 32 + mulscc %o4, %g0, %o4 ! final shift + + + /* + * Normally, with the shift-and-add approach, if both numbers are + * positive you get the correct result. With 32-bit two's-complement + * numbers, -x is represented as + * + * x 32 + * ( 2 - ------ ) mod 2 * 2 + * 32 + * 2 + * + * (the `mod 2' subtracts 1 from 1.bbbb). To avoid lots of 2^32s, + * we can treat this as if the radix point were just to the left + * of the sign bit (multiply by 2^32), and get + * + * -x = (2 - x) mod 2 + * + * Then, ignoring the `mod 2's for convenience: + * + * x * y = xy + * -x * y = 2y - xy + * x * -y = 2x - xy + * -x * -y = 4 - 2x - 2y + xy + * + * For signed multiplies, we subtract (x << 32) from the partial + * product to fix this problem for negative multipliers (see mul.s). + * Because of the way the shift into the partial product is calculated + * (N xor V), this term is automatically removed for the multiplicand, + * so we don't have to adjust. + * + * But for unsigned multiplies, the high order bit wasn't a sign bit, + * and the correction is wrong. So for unsigned multiplies where the + * high order bit is one, we end up with xy - (y << 32). To fix it + * we add y << 32. + */ +#if 0 + tst %o1 + bl,a 1f ! if %o1 < 0 (high order bit = 1), + add %o4, %o0, %o4 ! %o4 += %o0 (add y to upper half) +1: rd %y, %o0 ! get lower half of product + retl + addcc %o4, %g0, %o1 ! put upper half in place and set Z for %o1==0 +#else + /* Faster code from tege@sics.se. */ + sra %o1, 31, %o2 ! make mask from sign bit + and %o0, %o2, %o2 ! %o2 = 0 or %o0, depending on sign of %o1 + rd %y, %o0 ! get lower half of product + retl + addcc %o4, %o2, %o1 ! add compensation and put upper half in place +#endif + +Lmul_shortway: + /* + * Short multiply. 12 steps, followed by a final shift step. + * The resulting bits are off by 12 and (32-12) = 20 bit positions, + * but there is no problem with %o0 being negative (unlike above), + * and overflow is impossible (the answer is at most 24 bits long). + */ + mulscc %o4, %o1, %o4 ! 1 + mulscc %o4, %o1, %o4 ! 2 + mulscc %o4, %o1, %o4 ! 3 + mulscc %o4, %o1, %o4 ! 4 + mulscc %o4, %o1, %o4 ! 5 + mulscc %o4, %o1, %o4 ! 6 + mulscc %o4, %o1, %o4 ! 7 + mulscc %o4, %o1, %o4 ! 8 + mulscc %o4, %o1, %o4 ! 9 + mulscc %o4, %o1, %o4 ! 10 + mulscc %o4, %o1, %o4 ! 11 + mulscc %o4, %o1, %o4 ! 12 + mulscc %o4, %g0, %o4 ! final shift + + /* + * %o4 has 20 of the bits that should be in the result; %y has + * the bottom 12 (as %y's top 12). That is: + * + * %o4 %y + * +----------------+----------------+ + * | -12- | -20- | -12- | -20- | + * +------(---------+------)---------+ + * -----result----- + * + * The 12 bits of %o4 left of the `result' area are all zero; + * in fact, all top 20 bits of %o4 are zero. + */ + + rd %y, %o5 + sll %o4, 12, %o0 ! shift middle bits left 12 + srl %o5, 20, %o5 ! shift low bits right 20 + or %o5, %o0, %o0 + retl + addcc %g0, %g0, %o1 ! %o1 = zero, and set Z diff --git a/ldso/ldso/sparc/urem.S b/ldso/ldso/sparc/urem.S new file mode 100644 index 000000000..8d304038b --- /dev/null +++ b/ldso/ldso/sparc/urem.S @@ -0,0 +1,352 @@ + /* This file is generated from divrem.m4; DO NOT EDIT! */ +/* + * Division and remainder, from Appendix E of the Sparc Version 8 + * Architecture Manual, with fixes from Gordon Irlam. + */ + +/* + * Input: dividend and divisor in %o0 and %o1 respectively. + * + * m4 parameters: + * .urem name of function to generate + * rem rem=div => %o0 / %o1; rem=rem => %o0 % %o1 + * false false=true => signed; false=false => unsigned + * + * Algorithm parameters: + * N how many bits per iteration we try to get (4) + * WORDSIZE total number of bits (32) + * + * Derived constants: + * TOPBITS number of bits in the top decade of a number + * + * Important variables: + * Q the partial quotient under development (initially 0) + * R the remainder so far, initially the dividend + * ITER number of main division loop iterations required; + * equal to ceil(log2(quotient) / N). Note that this + * is the log base (2^N) of the quotient. + * V the current comparand, initially divisor*2^(ITER*N-1) + * + * Cost: + * Current estimate for non-large dividend is + * ceil(log2(quotient) / N) * (10 + 7N/2) + C + * A large dividend is one greater than 2^(31-TOPBITS) and takes a + * different path, as the upper bits of the quotient must be developed + * one bit at a time. + */ + + + +#include "DEFS.h" +#ifdef __linux__ +#include <asm/traps.h> +#else +#ifdef __svr4__ +#include <sys/trap.h> +#else +#include "/usr/include/machine/trap.h" +#endif +#endif + +FUNC(_dl_urem) + + ! Ready to divide. Compute size of quotient; scale comparand. + orcc %o1, %g0, %o5 + bne 1f + mov %o0, %o3 + + ! Divide by zero trap. If it returns, return 0 (about as + ! wrong as possible, but that is what SunOS does...). + ta ST_DIV0 + retl + clr %o0 + +1: + cmp %o3, %o5 ! if %o1 exceeds %o0, done + blu Lgot_result ! (and algorithm fails otherwise) + clr %o2 + sethi %hi(1 << (32 - 4 - 1)), %g1 + cmp %o3, %g1 + blu Lnot_really_big + clr %o4 + + ! Here the dividend is >= 2**(31-N) or so. We must be careful here, + ! as our usual N-at-a-shot divide step will cause overflow and havoc. + ! The number of bits in the result here is N*ITER+SC, where SC <= N. + ! Compute ITER in an unorthodox manner: know we need to shift V into + ! the top decade: so do not even bother to compare to R. + 1: + cmp %o5, %g1 + bgeu 3f + mov 1, %g7 + sll %o5, 4, %o5 + b 1b + add %o4, 1, %o4 + + ! Now compute %g7. + 2: addcc %o5, %o5, %o5 + bcc Lnot_too_big + add %g7, 1, %g7 + + ! We get here if the %o1 overflowed while shifting. + ! This means that %o3 has the high-order bit set. + ! Restore %o5 and subtract from %o3. + sll %g1, 4, %g1 ! high order bit + srl %o5, 1, %o5 ! rest of %o5 + add %o5, %g1, %o5 + b Ldo_single_div + sub %g7, 1, %g7 + + Lnot_too_big: + 3: cmp %o5, %o3 + blu 2b + nop + be Ldo_single_div + nop + /* NB: these are commented out in the V8-Sparc manual as well */ + /* (I do not understand this) */ + ! %o5 > %o3: went too far: back up 1 step + ! srl %o5, 1, %o5 + ! dec %g7 + ! do single-bit divide steps + ! + ! We have to be careful here. We know that %o3 >= %o5, so we can do the + ! first divide step without thinking. BUT, the others are conditional, + ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high- + ! order bit set in the first step, just falling into the regular + ! division loop will mess up the first time around. + ! So we unroll slightly... + Ldo_single_div: + subcc %g7, 1, %g7 + bl Lend_regular_divide + nop + sub %o3, %o5, %o3 + mov 1, %o2 + b Lend_single_divloop + nop + Lsingle_divloop: + sll %o2, 1, %o2 + bl 1f + srl %o5, 1, %o5 + ! %o3 >= 0 + sub %o3, %o5, %o3 + b 2f + add %o2, 1, %o2 + 1: ! %o3 < 0 + add %o3, %o5, %o3 + sub %o2, 1, %o2 + 2: + Lend_single_divloop: + subcc %g7, 1, %g7 + bge Lsingle_divloop + tst %o3 + b,a Lend_regular_divide + +Lnot_really_big: +1: + sll %o5, 4, %o5 + cmp %o5, %o3 + bleu 1b + addcc %o4, 1, %o4 + be Lgot_result + sub %o4, 1, %o4 + + tst %o3 ! set up for initial iteration +Ldivloop: + sll %o2, 4, %o2 + ! depth 1, accumulated bits 0 + bl L.1.16 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 2, accumulated bits 1 + bl L.2.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits 3 + bl L.3.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 7 + bl L.4.23 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (7*2+1), %o2 + +L.4.23: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (7*2-1), %o2 + + +L.3.19: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 5 + bl L.4.21 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (5*2+1), %o2 + +L.4.21: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (5*2-1), %o2 + + + +L.2.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits 1 + bl L.3.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits 3 + bl L.4.19 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (3*2+1), %o2 + +L.4.19: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (3*2-1), %o2 + + +L.3.17: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits 1 + bl L.4.17 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (1*2+1), %o2 + +L.4.17: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (1*2-1), %o2 + + + + +L.1.16: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 2, accumulated bits -1 + bl L.2.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 3, accumulated bits -1 + bl L.3.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -1 + bl L.4.15 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2+1), %o2 + +L.4.15: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-1*2-1), %o2 + + +L.3.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -3 + bl L.4.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2+1), %o2 + +L.4.13: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-3*2-1), %o2 + + + +L.2.15: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 3, accumulated bits -3 + bl L.3.13 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + ! depth 4, accumulated bits -5 + bl L.4.11 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2+1), %o2 + +L.4.11: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-5*2-1), %o2 + + +L.3.13: + ! remainder is negative + addcc %o3,%o5,%o3 + ! depth 4, accumulated bits -7 + bl L.4.9 + srl %o5,1,%o5 + ! remainder is positive + subcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2+1), %o2 + +L.4.9: + ! remainder is negative + addcc %o3,%o5,%o3 + b 9f + add %o2, (-7*2-1), %o2 + + + + + 9: +Lend_regular_divide: + subcc %o4, 1, %o4 + bge Ldivloop + tst %o3 + bl,a Lgot_result + ! non-restoring fixup here (one instruction only!) + add %o3, %o1, %o3 + + +Lgot_result: + + retl + mov %o3, %o0 diff --git a/ldso/ldso/string.h b/ldso/ldso/string.h new file mode 100644 index 000000000..1ea8fd7ae --- /dev/null +++ b/ldso/ldso/string.h @@ -0,0 +1,112 @@ +#ifndef _LINUX_STRING_H_ +#define _LINUX_STRING_H_ + +#include <linux/types.h> /* for size_t */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +extern inline char * _dl_strcpy(char * dst,const char *src) +{ + register char *ptr = dst; + + while (*src) + *dst++ = *src++; + *dst = '\0'; + + return ptr; +} + +extern inline int _dl_strcmp(const char * s1,const char * s2) +{ + unsigned register char c1, c2; + + do { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0') + return c1 - c2; + } + while (c1 == c2); + + return c1 - c2; +} + +extern inline int _dl_strncmp(const char * s1,const char * s2,size_t len) +{ + unsigned register char c1 = '\0'; + unsigned register char c2 = '\0'; + + while (len > 0) { + c1 = (unsigned char) *s1++; + c2 = (unsigned char) *s2++; + if (c1 == '\0' || c1 != c2) + return c1 - c2; + len--; + } + + return c1 - c2; +} + +extern inline char * _dl_strchr(const char * str,int c) +{ + register char ch; + + do { + if ((ch = *str) == c) + return (char *) str; + str++; + } + while (ch); + + return 0; +} + + +extern inline size_t _dl_strlen(const char * str) +{ + register char *ptr = (char *) str; + + while (*ptr) + ptr++; + return (ptr - str); +} + +extern inline void * _dl_memcpy(void * dst, const void * src, size_t len) +{ + register char *a = dst; + register const char *b = src; + + while (len--) + *a++ = *b++; + + return dst; +} + + +extern inline int _dl_memcmp(const void * s1,const void * s2,size_t len) +{ + unsigned char *c1 = (unsigned char *)s1; + unsigned char *c2 = (unsigned char *)s2; + + while (len--) { + if (*c1 != *c2) + return *c1 - *c2; + c1++; + c2++; + } + return 0; +} + +extern inline void * _dl_memset(void * str,int c,size_t len) +{ + register char *a = str; + + while (len--) + *a++ = c; + + return str; +} + +#endif diff --git a/ldso/ldso/syscall.h b/ldso/ldso/syscall.h new file mode 100644 index 000000000..3cf244338 --- /dev/null +++ b/ldso/ldso/syscall.h @@ -0,0 +1,108 @@ +#include <linux/types.h> +#include <asm/unistd.h> + +#ifndef _dl_MAX_ERRNO +#define _dl_MAX_ERRNO 4096 +#endif + +#define _dl_mmap_check_error(__res) \ + (((int)__res) < 0 && ((int)__res) >= -_dl_MAX_ERRNO) + + +/* Here are the definitions for some syscalls that are used + by the dynamic linker. The idea is that we want to be able + to call these before the errno symbol is dynamicly linked, so + we use our own version here. Note that we cannot assume any + dynamic linking at all, so we cannot return any error codes. + We just punt if there is an error. */ + +/* Do not include unistd.h, so gcc doesn't whine about + * _exit returning. It really doesn't return... */ +#define __NR__dl_exit __NR_exit +static inline _syscall1(void, _dl_exit, int, status); + + +#define __NR__dl_close __NR_close +static inline _syscall1(int, _dl_close, int, fd); + + +#define __NR__dl_mmap_real __NR_mmap +static inline _syscall1(void *, _dl_mmap_real, unsigned long *, buffer); + +static inline void * _dl_mmap(void * addr, unsigned long size, int prot, + int flags, int fd, unsigned long offset) +{ + unsigned long buffer[6]; + + buffer[0] = (unsigned long) addr; + buffer[1] = (unsigned long) size; + buffer[2] = (unsigned long) prot; + buffer[3] = (unsigned long) flags; + buffer[4] = (unsigned long) fd; + buffer[5] = (unsigned long) offset; + return (void *) _dl_mmap_real(buffer); +} + +#define __NR__dl_open __NR_open +static inline _syscall2(int, _dl_open, const char *, fn, int, flags); + +#define __NR__dl_write __NR_write +static inline _syscall3(unsigned long, _dl_write, int, fd, + const void *, buf, unsigned long, count); + + +#define __NR__dl_read __NR_read +static inline _syscall3(unsigned long, _dl_read, int, fd, + const void *, buf, unsigned long, count); + +#define __NR__dl_mprotect __NR_mprotect +static inline _syscall3(int, _dl_mprotect, const void *, addr, unsigned long, len, int, prot); + + + +/* Pull in whatever this particular arch's kernel thinks the kernel version of + * struct stat should look like. It turns out that each arch has a different + * opinion on the subject, and different kernel revs use different names... */ +#define __NR__dl_stat __NR_stat +#define stat kernel_stat +#define new_stat kernel_stat +#include <asm/stat.h> +#undef new_stat +#undef stat +static inline _syscall2(int, _dl_stat, const char *, file_name, struct kernel_stat *, buf); + + +#define __NR__dl_munmap __NR_munmap +static inline _syscall2(int, _dl_munmap, void *, start, unsigned long, length); + +#define __NR__dl_getuid __NR_getuid +static inline _syscall0(gid_t, _dl_getuid); + +#define __NR__dl_geteuid __NR_geteuid +static inline _syscall0(uid_t, _dl_geteuid); + +#define __NR__dl_getgid __NR_getgid +static inline _syscall0(gid_t, _dl_getgid); + +#define __NR__dl_getegid __NR_getegid +static inline _syscall0(gid_t, _dl_getegid); + +/* + * Not an actual syscall, but we need something in assembly to say whether + * this is OK or not. + */ +extern inline int _dl_suid_ok(void) +{ + uid_t uid, euid, gid, egid; + + uid = _dl_getuid(); + euid = _dl_geteuid(); + gid = _dl_getgid(); + egid = _dl_getegid(); + + if(uid == euid && gid == egid) + return 1; + else + return 0; +} + diff --git a/ldso/ldso/vsprintf.c b/ldso/ldso/vsprintf.c new file mode 100644 index 000000000..48c44e38f --- /dev/null +++ b/ldso/ldso/vsprintf.c @@ -0,0 +1,290 @@ +/* + * vsprintf.c + * + * Copyright (C) 1991-1996 Linus Torvalds + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +#include <stdarg.h> +#include "string.h" +#include "hash.h" +#include <linux/unistd.h> +#include "syscall.h" + +/* we use this so that we can do without the ctype library */ +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +static int skip_atoi(const char **s) +{ + int i=0; + + while (is_digit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +#ifndef __sparc__ +#define do_div(n,base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) +#else +#define do_div(n,base) _dl_div ((n)/(base)) +#define do_div(n,base) ({ \ +int __res; \ +__res = _dl_urem(((unsigned long) n),(unsigned) base); \ +n = _dl_udiv(((unsigned long) n),(unsigned) base); \ +__res; }) +#endif + +#define ADD_CHAR(s,n,c) ( ((n) > 1) ? *(s)++ = (c), (n)-- : (c) ) + +static char * number(char * str, int *bufsize, long num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + ADD_CHAR(str, *bufsize, ' '); + if (sign) + ADD_CHAR(str, *bufsize, sign); + if (type & SPECIAL) { + if (base==8) + ADD_CHAR(str, *bufsize, '0'); + else if (base==16) { + ADD_CHAR(str, *bufsize, '0'); + ADD_CHAR(str, *bufsize, digits[33]); + } + } + if (!(type & LEFT)) + while (size-- > 0) + ADD_CHAR(str, *bufsize, c); + while (i < precision--) + ADD_CHAR(str, *bufsize, '0'); + while (i-- > 0) + ADD_CHAR(str, *bufsize, tmp[i]); + while (size-- > 0) + ADD_CHAR(str, *bufsize, ' '); + return str; +} + +int _dl_fdprintf(int fd, const char *fmt, ...) +{ + int len; + unsigned long num; + int i, base; + char * str; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + int bufsize; + char buf[2048]; + va_list(args); + + va_start(args, fmt); + + for (str=buf, bufsize=sizeof buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + ADD_CHAR(str, bufsize, *fmt); + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (is_digit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (is_digit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + ADD_CHAR(str, bufsize, ' '); + ADD_CHAR(str, bufsize, (unsigned char) va_arg(args, int)); + while (--field_width > 0) + ADD_CHAR(str, bufsize, ' '); + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = "<NULL>"; + + len = _dl_strlen(s); + + if (!(flags & LEFT)) + while (len < field_width--) + ADD_CHAR(str, bufsize, ' '); + for (i = 0; i < len; ++i) + ADD_CHAR(str, bufsize, *s++); + while (len < field_width--) + ADD_CHAR(str, bufsize, ' '); + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, &bufsize, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + if (*fmt != '%') + ADD_CHAR(str, bufsize, '%'); + if (*fmt) + ADD_CHAR(str, bufsize, *fmt); + else + --fmt; + continue; + } + if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (qualifier == 'h') + if (flags & SIGN) + num = va_arg(args, short); + else + num = va_arg(args, unsigned short); + else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + str = number(str, &bufsize, num, base, field_width, precision, flags); + } + *str = '\0'; + _dl_write(fd, buf, str-buf); + return str-buf; +} + diff --git a/ldso/libdl/Makefile b/ldso/libdl/Makefile new file mode 100644 index 000000000..8578357ab --- /dev/null +++ b/ldso/libdl/Makefile @@ -0,0 +1,31 @@ +TOPDIR=../../../ +include $(TOPDIR)Rules.mak +include $(TOPDIR)/ld.so-1/Config.mk + +CFLAGS += -DNO_UNDERSCORE -DVERBOSE_DLINKER -DUSE_CACHE +CFLAGS += #-fPIC -D__PIC__ #-funroll-loops + +LIBDL = libdl.so + +CSRC= dlib.c +COBJS=$(patsubst %.c,%.o, $(CSRC)) +OBJS=$(COBJS) + +$(COBJS): %.o : %.c + $(CC) -I.. -I../$(TARGET_ARCH) $(CFLAGS) -c $< -o $@ + $(STRIPTOOL) -x -R .note -R .comment $*.o + +ELF_LDFLAGS=--shared -nostartfiles -nostdlib # using GNU ld +#ELF_LDFLAGS=-G # with SVr4 ld + +lib:: $(OBJS) + $(CC) $(ELF_LDFLAGS) -o $(LIBDL).$(LDSO_VMAJOR) \ + -Wl,-soname -Wl,$(LIBDL).$(LDSO_VMAJOR) *.o -lc + +obj: $(OBJS) + +realclean:: + $(RM) -f .depend $(LIBDL) core *.o *.a *.s *.i tmp_make foo *~ + +clean:: + $(RM) -f $(LIBDL) core *.o *.a *.s *.i tmp_make foo *~ diff --git a/ldso/libdl/dlfcn.h b/ldso/libdl/dlfcn.h new file mode 100644 index 000000000..620181173 --- /dev/null +++ b/ldso/libdl/dlfcn.h @@ -0,0 +1,41 @@ +#ifndef DLFCN_H +#define DLFCN_H + +#include <features.h> + +/* + * Various defines and so forth for the dynamic linker + */ + +/* For dlopen () */ +#define RTLD_LAZY 1 +#define RTLD_NOW 2 +#define RTLD_GLOBAL 0x100 + +/* For dlsym */ +#define RTLD_NEXT ((void *)-1) + +__BEGIN_DECLS + +/* The usual prototypes. We use void * instead of the actual + * datatype - the user does not manipulate the handles at all. + */ + +extern void * dlopen __P((__const char * __filename, int __flag)); +extern __const char * dlerror __P((void)); +extern void * dlsym __P((void *, __const char *)); +extern int dlclose __P((void *)); + +typedef struct +{ + const char * dli_fname; /* filename */ + void * dli_fbase; /* base address of object */ + const char * dli_sname; /* nearest symbol name */ + void * dli_saddr; /* nearest symbol address */ +} Dl_info; + +extern int dladdr __P((void * __address, Dl_info * __dlip )); + +__END_DECLS + +#endif diff --git a/ldso/libdl/dlib.c b/ldso/libdl/dlib.c new file mode 100644 index 000000000..545b6745f --- /dev/null +++ b/ldso/libdl/dlib.c @@ -0,0 +1,648 @@ +/* + * libdl.c + * + * Functions required for dlopen et. al. + */ + +#include "dlfcn.h" +/* #include "link.h" */ +#include <stdlib.h> +#include <linux/mman.h> +#include <linux/unistd.h> +#include "sysdep.h" +#include "syscall.h" +#include "hash.h" +#include "string.h" +#include "linuxelf.h" + +extern int _dl_error_number; +extern struct r_debug * _dl_debug_addr; + +extern void * (*_dl_malloc_function)(size_t size); + +static int do_fixup(struct elf_resolve * tpnt, int flag); +static int do_dlclose(void *, int need_fini); + +void * _dlopen(char * filename, int flag); +const char * _dlerror(void); +void * _dlsym(void *, char *); +int _dlclose(void *); +int _dladdr(void * __address, Dl_info * __dlip ); + +static const char * dl_error_names[] = { + "", + "File not found", + "Unable to open /dev/zero", + "Not an ELF file", +#if defined (__i386__) + "Not i386 binary", +#elif defined (__sparc__) + "Not sparc binary", +#elif defined (__mc68000__) + "Not m68k binary", +#else + "Unrecognized binary type", +#endif + "Not an ELF shared library", + "Unable to mmap file", + "No dynamic section", +#ifdef ELF_USES_RELOCA + "Unable to process REL relocs", +#else + "Unable to process RELA relocs", +#endif + "Bad handle", + "Unable to resolve symbol" +}; + +static void dl_cleanup(void) __attribute__ ((destructor)); + +static void dl_cleanup(void) +{ + struct dyn_elf* d; + + for (d = _dl_handles; d; d = d->next_handle) + if (d->dyn->libtype == loaded_file && d->dyn->dynamic_info[DT_FINI]) + { + (*((int(*)(void))(d->dyn->loadaddr + d->dyn->dynamic_info[DT_FINI])))(); + d->dyn->dynamic_info[DT_FINI] = 0; + } +} + +void * _dlopen(char * libname, int flag) +{ + struct elf_resolve * tpnt, *tfrom; + struct dyn_elf * rpnt; + struct dyn_elf * dyn_chain; + struct dyn_elf * dpnt; + static int dl_init = 0; + char * from; + void (*dl_brk)(void); + int (*dl_elf_init)(void); + + from = __builtin_return_address(0); + + /* Have the dynamic linker use the regular malloc function now */ + if (!dl_init) { + dl_init++; + _dl_malloc_function = malloc; + } + + /* Cover the trivial case first */ + if (!libname) return _dl_symbol_tables; + +#ifdef USE_CACHE + _dl_map_cache(); +#endif + + /* + * Try and locate the module we were called from - we + * need this so that we get the correct RPATH. Note that + * this is the current behavior under Solaris, but the + * ABI+ specifies that we should only use the RPATH from + * the application. Thus this may go away at some time + * in the future. + */ + tfrom = NULL; + for(dpnt = _dl_symbol_tables; dpnt; dpnt = dpnt->next) + { + tpnt = dpnt->dyn; + if( tpnt->loadaddr < from + && ( tfrom == NULL + || tfrom->loadaddr < tpnt->loadaddr)) + tfrom = tpnt; + } + + if(!(tpnt = _dl_load_shared_library(0, tfrom, libname))) + { +#ifdef USE_CACHE + _dl_unmap_cache(); +#endif + return NULL; + } + + tpnt->usage_count++; + dyn_chain = rpnt = + (struct dyn_elf *) malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt, 0, sizeof(*rpnt)); + rpnt->dyn = tpnt; + rpnt->flags = flag; + if (!tpnt->symbol_scope) tpnt->symbol_scope = dyn_chain; + + rpnt->next_handle = _dl_handles; + _dl_handles = rpnt; + + /* + * OK, we have the requested file in memory. Now check for + * any other requested files that may also be required. + */ + { + struct elf_resolve *tcurr; + struct elf_resolve * tpnt1; + struct dynamic * dpnt; + char * lpnt; + + tcurr = tpnt; + do{ + for(dpnt = (struct dynamic *) tcurr->dynamic_addr; dpnt->d_tag; dpnt++) + { + + if(dpnt->d_tag == DT_NEEDED) + { + lpnt = tcurr->loadaddr + tcurr->dynamic_info[DT_STRTAB] + + dpnt->d_un.d_val; + if(!(tpnt1 = _dl_load_shared_library(0, tcurr, lpnt))) + goto oops; + + rpnt->next = + (struct dyn_elf *) malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + tpnt1->usage_count++; + if (!tpnt1->symbol_scope) tpnt1->symbol_scope = dyn_chain; + rpnt->dyn = tpnt1; + }; + } + + tcurr = tcurr->next; + } while(tcurr); + } + + /* + * OK, now attach the entire chain at the end + */ + + rpnt->next = _dl_symbol_tables; + + if (do_fixup(tpnt, flag)) { + _dl_error_number = DL_NO_SYMBOL; + goto oops; + } + + dl_brk = (void (*)()) _dl_debug_addr->r_brk; + if( dl_brk != NULL ) + { + _dl_debug_addr->r_state = RT_ADD; + (*dl_brk)(); + + _dl_debug_addr->r_state = RT_CONSISTENT; + (*dl_brk)(); + } + + for(rpnt = dyn_chain; rpnt; rpnt = rpnt->next) + { + tpnt = rpnt->dyn; + /* Apparently crt1 for the application is responsible for handling this. + * We only need to run the init/fini for shared libraries + */ + if (tpnt->libtype == elf_executable) continue; + if (tpnt->init_flag & INIT_FUNCS_CALLED) continue; + tpnt->init_flag |= INIT_FUNCS_CALLED; + + if(tpnt->dynamic_info[DT_INIT]) { + dl_elf_init = (int (*)(void)) (tpnt->loadaddr + + tpnt->dynamic_info[DT_INIT]); + (*dl_elf_init)(); + } + } + +#ifdef USE_CACHE + _dl_unmap_cache(); +#endif + return (void *) dyn_chain; + +oops: + /* Something went wrong. Clean up and return NULL. */ +#ifdef USE_CACHE + _dl_unmap_cache(); +#endif + do_dlclose (dyn_chain, 0); + return NULL; +} + +static int do_fixup(struct elf_resolve * tpnt, int flag) +{ + int goof = 0; + if(tpnt->next) goof += do_fixup(tpnt->next, flag); + + if(tpnt->dynamic_info[DT_REL]) { +#ifdef ELF_USES_RELOCA + goof++; +#else + if (tpnt->init_flag & RELOCS_DONE) return goof; + tpnt->init_flag |= RELOCS_DONE; + + goof += _dl_parse_relocation_information(tpnt, tpnt->dynamic_info[DT_REL], + tpnt->dynamic_info[DT_RELSZ], 0); +#endif + } + if(tpnt->dynamic_info[DT_RELA]) { +#ifdef ELF_USES_RELOCA + if (tpnt->init_flag & RELOCS_DONE) return goof; + tpnt->init_flag |= RELOCS_DONE; + + goof += _dl_parse_relocation_information(tpnt, tpnt->dynamic_info[DT_RELA], + tpnt->dynamic_info[DT_RELASZ], 0); +#else + goof++; +#endif + } + if(tpnt->dynamic_info[DT_JMPREL]) + { + if (tpnt->init_flag & JMP_RELOCS_DONE) return goof; + tpnt->init_flag |= JMP_RELOCS_DONE; + + if(flag == RTLD_LAZY) + _dl_parse_lazy_relocation_information(tpnt, tpnt->dynamic_info[DT_JMPREL], + tpnt->dynamic_info[DT_PLTRELSZ], 0); + else + goof += _dl_parse_relocation_information(tpnt, + tpnt->dynamic_info[DT_JMPREL], + tpnt->dynamic_info[DT_PLTRELSZ], 0); + }; + return goof; +} + +void * _dlsym(void * vhandle, char * name) +{ + struct elf_resolve * tpnt, *tfrom; + struct dyn_elf * handle; + char * from; + struct dyn_elf * rpnt; + void *ret; + + handle = (struct dyn_elf *) vhandle; + + /* First of all verify that we have a real handle + of some kind. Return NULL if not a valid handle. */ + + if (handle == NULL) + handle = _dl_symbol_tables; + else if (handle != RTLD_NEXT && handle != _dl_symbol_tables) { + for(rpnt = _dl_handles; rpnt; rpnt = rpnt->next_handle) + if (rpnt == handle) break; + if (!rpnt) { + _dl_error_number = DL_BAD_HANDLE; + return NULL; + } + } + else if (handle == RTLD_NEXT ) + { + /* + * Try and locate the module we were called from - we + * need this so that we know where to start searching + * from. We never pass RTLD_NEXT down into the actual + * dynamic loader itself, as it doesn't know + * how to properly treat it. + */ + from = __builtin_return_address(0); + + tfrom = NULL; + for(rpnt = _dl_symbol_tables; rpnt; rpnt = rpnt->next) + { + tpnt = rpnt->dyn; + if( tpnt->loadaddr < from + && ( tfrom == NULL + || tfrom->loadaddr < tpnt->loadaddr)) + { + tfrom = tpnt; + handle = rpnt->next; + } + } + } + + ret = _dl_find_hash(name, handle, 1, NULL, 1); + + /* + * Nothing found. + */ + if (!ret) + _dl_error_number = DL_NO_SYMBOL; + return ret; +} + +int _dlclose(void * vhandle) +{ + return do_dlclose(vhandle, 1); +} + +static int do_dlclose(void * vhandle, int need_fini) +{ + struct dyn_elf * rpnt, *rpnt1; + struct dyn_elf *spnt, *spnt1; + struct elf_phdr * ppnt; + struct elf_resolve * tpnt; + int (*dl_elf_fini)(void); + void (*dl_brk)(void); + struct dyn_elf * handle; + unsigned int end; + int i = 0; + + handle = (struct dyn_elf *) vhandle; + rpnt1 = NULL; + for(rpnt = _dl_handles; rpnt; rpnt = rpnt->next_handle) + { + if(rpnt == handle) { + break; + } + rpnt1 = rpnt; + } + + if (!rpnt) { + _dl_error_number = DL_BAD_HANDLE; + return 1; + } + + /* OK, this is a valid handle - now close out the file. + * We check if we need to call fini () on the handle. */ + spnt = need_fini ? handle : handle->next; + for(; spnt; spnt = spnt1) + { + spnt1 = spnt->next; + + /* We appended the module list to the end - when we get back here, + quit. The access counts were not adjusted to account for being here. */ + if (spnt == _dl_symbol_tables) break; + if(spnt->dyn->usage_count==1 && spnt->dyn->libtype == loaded_file) { + tpnt = spnt->dyn; + /* Apparently crt1 for the application is responsible for handling this. + * We only need to run the init/fini for shared libraries + */ + + if(tpnt->dynamic_info[DT_FINI]) { + dl_elf_fini = (int (*)(void)) (tpnt->loadaddr + + tpnt->dynamic_info[DT_FINI]); + (*dl_elf_fini)(); + } + } + } + if(rpnt1) + rpnt1->next_handle = rpnt->next_handle; + else + _dl_handles = rpnt->next_handle; + + /* OK, this is a valid handle - now close out the file */ + for(rpnt = handle; rpnt; rpnt = rpnt1) + { + rpnt1 = rpnt->next; + + /* We appended the module list to the end - when we get back here, + quit. The access counts were not adjusted to account for being here. */ + if (rpnt == _dl_symbol_tables) break; + + rpnt->dyn->usage_count--; + if(rpnt->dyn->usage_count == 0 && rpnt->dyn->libtype == loaded_file) + { + tpnt = rpnt->dyn; + /* Apparently crt1 for the application is responsible for handling this. + * We only need to run the init/fini for shared libraries + */ +#if 0 +/* We have to do this above, before we start closing objects. +Otherwise when the needed symbols for _fini handling are +resolved a coredump would occur. Rob Ryan (robr@cmu.edu)*/ + if(tpnt->dynamic_info[DT_FINI]) { + dl_elf_fini = (int (*)(void)) (tpnt->loadaddr + + tpnt->dynamic_info[DT_FINI]); + (*dl_elf_fini)(); + } +#endif + end = 0; + for(i = 0, ppnt = rpnt->dyn->ppnt; + i < rpnt->dyn->n_phent; ppnt++, i++) { + if (ppnt->p_type != PT_LOAD) continue; + if (end < ppnt->p_vaddr + ppnt->p_memsz) + end = ppnt->p_vaddr + ppnt->p_memsz; + } + _dl_munmap (rpnt->dyn->loadaddr, end); + /* Next, remove rpnt->dyn from the loaded_module list */ + if (_dl_loaded_modules == rpnt->dyn) + { + _dl_loaded_modules = rpnt->dyn->next; + if (_dl_loaded_modules) + _dl_loaded_modules->prev = 0; + } + else + for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) + if (tpnt->next == rpnt->dyn) { + tpnt->next = tpnt->next->next; + if (tpnt->next) + tpnt->next->prev = tpnt; + break; + } + free(rpnt->dyn->libname); + free(rpnt->dyn); + } + free(rpnt); + } + + + dl_brk = (void (*)()) _dl_debug_addr->r_brk; + if( dl_brk != NULL ) + { + _dl_debug_addr->r_state = RT_DELETE; + (*dl_brk)(); + + _dl_debug_addr->r_state = RT_CONSISTENT; + (*dl_brk)(); + } + + return 0; +} + +const char * _dlerror() +{ + const char * retval; + if(!_dl_error_number) return NULL; + retval = dl_error_names[_dl_error_number]; + _dl_error_number = 0; + return retval; +} + +/* Generate the correct symbols that we need. */ +#if 1 +#pragma weak dlopen = _dlopen +#pragma weak dlerror = _dlerror +#pragma weak dlclose = _dlclose +#pragma weak dlsym = _dlsym +#pragma weak dladdr = _dladdr +#else +__asm__(".weak dlopen;dlopen=_dlopen"); +__asm__(".weak dlerror;dlerror=_dlerror"); +__asm__(".weak dlclose;dlclose=_dlclose"); +__asm__(".weak dlsym;dlsym=_dlsym"); +__asm__(".weak dladdr;dladdr=_dladdr"); +#endif + +/* This is a real hack. We need access to the dynamic linker, but we +also need to make it possible to link against this library without any +unresolved externals. We provide these weak symbols to make the link +possible, but at run time the normal symbols are accessed. */ + +static void foobar() +{ + _dl_fdprintf(2,"libdl library not correctly linked\n"); + _dl_exit(1); +} + +static int foobar1 = (int)foobar; /* Use as pointer */ + +#if 1 +#pragma weak _dl_find_hash = foobar +#pragma weak _dl_symbol_tables = foobar1 +#pragma weak _dl_handles = foobar1 +#pragma weak _dl_loaded_modules = foobar1 +#pragma weak _dl_debug_addr = foobar1 +#pragma weak _dl_error_number = foobar1 +#pragma weak _dl_load_shared_library = foobar +#ifdef USE_CACHE +#pragma weak _dl_map_cache = foobar +#pragma weak _dl_unmap_cache = foobar +#endif +#pragma weak _dl_malloc_function = foobar1 +#pragma weak _dl_parse_relocation_information = foobar +#pragma weak _dl_parse_lazy_relocation_information = foobar +#pragma weak _dl_fdprintf = foobar +#else +__asm__(".weak _dl_find_hash; _dl_find_hash = foobar"); +__asm__(".weak _dl_symbol_tables; _dl_symbol_tables = foobar1"); +__asm__(".weak _dl_handles; _dl_handles = foobar1"); +__asm__(".weak _dl_loaded_modules; _dl_loaded_modules = foobar1"); +__asm__(".weak _dl_debug_addr; _dl_debug_addr = foobar1"); +__asm__(".weak _dl_error_number; _dl_error_number = foobar1"); +__asm__(".weak _dl_load_shared_library; _dl_load_shared_library = foobar"); +#ifdef USE_CACHE +__asm__(".weak _dl_map_cache; _dl_map_cache = foobar"); +__asm__(".weak _dl_unmap_cache; _dl_unmap_cache = foobar"); +#endif +__asm__(".weak _dl_malloc_function; _dl_malloc_function = foobar1"); +__asm__(".weak _dl_parse_relocation_information; _dl_parse_relocation_information = foobar"); +__asm__(".weak _dl_parse_lazy_relocation_information; _dl_parse_lazy_relocation_information = foobar"); +__asm__(".weak _dl_fdprintf; _dl_fdprintf = foobar"); +#endif + +/* + * Dump information to stderrr about the current loaded modules + */ +static char * type[] = {"Lib","Exe","Int","Mod"}; + +void _dlinfo() +{ + struct elf_resolve * tpnt; + struct dyn_elf * rpnt, *hpnt; + _dl_fdprintf(2, "List of loaded modules\n"); + /* First start with a complete list of all of the loaded files. */ + for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) + _dl_fdprintf(2, "\t%8.8x %8.8x %8.8x %s %d %s\n", + (unsigned)tpnt->loadaddr, (unsigned)tpnt, + (unsigned)tpnt->symbol_scope, + type[tpnt->libtype], + tpnt->usage_count, + tpnt->libname); + + /* Next dump the module list for the application itself */ + _dl_fdprintf(2, "\nModules for application (%x):\n", + (unsigned)_dl_symbol_tables); + for (rpnt = _dl_symbol_tables; rpnt; rpnt = rpnt->next) + _dl_fdprintf(2, "\t%8.8x %s\n", (unsigned)rpnt->dyn, + rpnt->dyn->libname); + + for (hpnt = _dl_handles; hpnt; hpnt = hpnt->next_handle) + { + _dl_fdprintf(2, "Modules for handle %x\n", (unsigned)hpnt); + for(rpnt = hpnt; rpnt; rpnt = rpnt->next) + _dl_fdprintf(2, "\t%8.8x %s\n", (unsigned)rpnt->dyn, + rpnt->dyn->libname); + } +} + +int _dladdr(void * __address, Dl_info * __dlip ) +{ + struct elf_resolve * pelf; + struct elf_resolve * rpnt; + +#ifdef USE_CACHE + _dl_map_cache(); +#endif + + /* + * Try and locate the module address is in + */ + pelf = NULL; + +#if 0 + _dl_fdprintf( 2, + "dladdr( 0x%p, 0x%p )\n", + __address, __dlip ); +#endif + + for(rpnt = _dl_loaded_modules; rpnt; rpnt = rpnt->next) + { + struct elf_resolve * tpnt; + tpnt = rpnt; +#if 0 + _dl_fdprintf( 2, + "Module \"%s\" at 0x%p\n", + tpnt->libname, + tpnt->loadaddr ); +#endif + if( tpnt->loadaddr < (char*)__address + && ( pelf == NULL + || pelf->loadaddr < tpnt->loadaddr)) + pelf = tpnt; + } + + if ( ! pelf ) + { + return 0; + } + + /* + * Try and locate the symbol of address + */ + + { + char * strtab; + struct elf32_sym * symtab; + int hn, si; + int sf; + int sn = 0; + void* sa = 0; + + symtab = (struct elf32_sym *) (pelf->dynamic_info[DT_SYMTAB] + + pelf->loadaddr); + strtab = (char *) (pelf->dynamic_info[DT_STRTAB] + pelf->loadaddr); + + sf = 0; + for(hn = 0; hn < pelf->nbucket; hn++) + { + for(si = pelf->elf_buckets[hn]; si; si = pelf->chains[si]) + { + void* symbol_addr; + symbol_addr = pelf->loadaddr + symtab[si].st_value; + if ( symbol_addr <= __address && + ( !sf || sa < symbol_addr ) ) + { + sa = symbol_addr; + sn = si; + sf = 1; + } +#if 0 + _dl_fdprintf( 2, + "Symbol \"%s\" at 0x%p\n", + strtab + symtab[si].st_name, + symbol_addr ); +#endif + } + } + + if ( sf ) + { + __dlip->dli_fname = pelf->libname; + __dlip->dli_fbase = pelf->loadaddr; + __dlip->dli_sname = strtab + symtab[sn].st_name; + __dlip->dli_saddr = sa; + } + return 1; + } +} diff --git a/ldso/libdl/libdl.c b/ldso/libdl/libdl.c new file mode 100644 index 000000000..545b6745f --- /dev/null +++ b/ldso/libdl/libdl.c @@ -0,0 +1,648 @@ +/* + * libdl.c + * + * Functions required for dlopen et. al. + */ + +#include "dlfcn.h" +/* #include "link.h" */ +#include <stdlib.h> +#include <linux/mman.h> +#include <linux/unistd.h> +#include "sysdep.h" +#include "syscall.h" +#include "hash.h" +#include "string.h" +#include "linuxelf.h" + +extern int _dl_error_number; +extern struct r_debug * _dl_debug_addr; + +extern void * (*_dl_malloc_function)(size_t size); + +static int do_fixup(struct elf_resolve * tpnt, int flag); +static int do_dlclose(void *, int need_fini); + +void * _dlopen(char * filename, int flag); +const char * _dlerror(void); +void * _dlsym(void *, char *); +int _dlclose(void *); +int _dladdr(void * __address, Dl_info * __dlip ); + +static const char * dl_error_names[] = { + "", + "File not found", + "Unable to open /dev/zero", + "Not an ELF file", +#if defined (__i386__) + "Not i386 binary", +#elif defined (__sparc__) + "Not sparc binary", +#elif defined (__mc68000__) + "Not m68k binary", +#else + "Unrecognized binary type", +#endif + "Not an ELF shared library", + "Unable to mmap file", + "No dynamic section", +#ifdef ELF_USES_RELOCA + "Unable to process REL relocs", +#else + "Unable to process RELA relocs", +#endif + "Bad handle", + "Unable to resolve symbol" +}; + +static void dl_cleanup(void) __attribute__ ((destructor)); + +static void dl_cleanup(void) +{ + struct dyn_elf* d; + + for (d = _dl_handles; d; d = d->next_handle) + if (d->dyn->libtype == loaded_file && d->dyn->dynamic_info[DT_FINI]) + { + (*((int(*)(void))(d->dyn->loadaddr + d->dyn->dynamic_info[DT_FINI])))(); + d->dyn->dynamic_info[DT_FINI] = 0; + } +} + +void * _dlopen(char * libname, int flag) +{ + struct elf_resolve * tpnt, *tfrom; + struct dyn_elf * rpnt; + struct dyn_elf * dyn_chain; + struct dyn_elf * dpnt; + static int dl_init = 0; + char * from; + void (*dl_brk)(void); + int (*dl_elf_init)(void); + + from = __builtin_return_address(0); + + /* Have the dynamic linker use the regular malloc function now */ + if (!dl_init) { + dl_init++; + _dl_malloc_function = malloc; + } + + /* Cover the trivial case first */ + if (!libname) return _dl_symbol_tables; + +#ifdef USE_CACHE + _dl_map_cache(); +#endif + + /* + * Try and locate the module we were called from - we + * need this so that we get the correct RPATH. Note that + * this is the current behavior under Solaris, but the + * ABI+ specifies that we should only use the RPATH from + * the application. Thus this may go away at some time + * in the future. + */ + tfrom = NULL; + for(dpnt = _dl_symbol_tables; dpnt; dpnt = dpnt->next) + { + tpnt = dpnt->dyn; + if( tpnt->loadaddr < from + && ( tfrom == NULL + || tfrom->loadaddr < tpnt->loadaddr)) + tfrom = tpnt; + } + + if(!(tpnt = _dl_load_shared_library(0, tfrom, libname))) + { +#ifdef USE_CACHE + _dl_unmap_cache(); +#endif + return NULL; + } + + tpnt->usage_count++; + dyn_chain = rpnt = + (struct dyn_elf *) malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt, 0, sizeof(*rpnt)); + rpnt->dyn = tpnt; + rpnt->flags = flag; + if (!tpnt->symbol_scope) tpnt->symbol_scope = dyn_chain; + + rpnt->next_handle = _dl_handles; + _dl_handles = rpnt; + + /* + * OK, we have the requested file in memory. Now check for + * any other requested files that may also be required. + */ + { + struct elf_resolve *tcurr; + struct elf_resolve * tpnt1; + struct dynamic * dpnt; + char * lpnt; + + tcurr = tpnt; + do{ + for(dpnt = (struct dynamic *) tcurr->dynamic_addr; dpnt->d_tag; dpnt++) + { + + if(dpnt->d_tag == DT_NEEDED) + { + lpnt = tcurr->loadaddr + tcurr->dynamic_info[DT_STRTAB] + + dpnt->d_un.d_val; + if(!(tpnt1 = _dl_load_shared_library(0, tcurr, lpnt))) + goto oops; + + rpnt->next = + (struct dyn_elf *) malloc(sizeof(struct dyn_elf)); + _dl_memset (rpnt->next, 0, sizeof (*(rpnt->next))); + rpnt = rpnt->next; + tpnt1->usage_count++; + if (!tpnt1->symbol_scope) tpnt1->symbol_scope = dyn_chain; + rpnt->dyn = tpnt1; + }; + } + + tcurr = tcurr->next; + } while(tcurr); + } + + /* + * OK, now attach the entire chain at the end + */ + + rpnt->next = _dl_symbol_tables; + + if (do_fixup(tpnt, flag)) { + _dl_error_number = DL_NO_SYMBOL; + goto oops; + } + + dl_brk = (void (*)()) _dl_debug_addr->r_brk; + if( dl_brk != NULL ) + { + _dl_debug_addr->r_state = RT_ADD; + (*dl_brk)(); + + _dl_debug_addr->r_state = RT_CONSISTENT; + (*dl_brk)(); + } + + for(rpnt = dyn_chain; rpnt; rpnt = rpnt->next) + { + tpnt = rpnt->dyn; + /* Apparently crt1 for the application is responsible for handling this. + * We only need to run the init/fini for shared libraries + */ + if (tpnt->libtype == elf_executable) continue; + if (tpnt->init_flag & INIT_FUNCS_CALLED) continue; + tpnt->init_flag |= INIT_FUNCS_CALLED; + + if(tpnt->dynamic_info[DT_INIT]) { + dl_elf_init = (int (*)(void)) (tpnt->loadaddr + + tpnt->dynamic_info[DT_INIT]); + (*dl_elf_init)(); + } + } + +#ifdef USE_CACHE + _dl_unmap_cache(); +#endif + return (void *) dyn_chain; + +oops: + /* Something went wrong. Clean up and return NULL. */ +#ifdef USE_CACHE + _dl_unmap_cache(); +#endif + do_dlclose (dyn_chain, 0); + return NULL; +} + +static int do_fixup(struct elf_resolve * tpnt, int flag) +{ + int goof = 0; + if(tpnt->next) goof += do_fixup(tpnt->next, flag); + + if(tpnt->dynamic_info[DT_REL]) { +#ifdef ELF_USES_RELOCA + goof++; +#else + if (tpnt->init_flag & RELOCS_DONE) return goof; + tpnt->init_flag |= RELOCS_DONE; + + goof += _dl_parse_relocation_information(tpnt, tpnt->dynamic_info[DT_REL], + tpnt->dynamic_info[DT_RELSZ], 0); +#endif + } + if(tpnt->dynamic_info[DT_RELA]) { +#ifdef ELF_USES_RELOCA + if (tpnt->init_flag & RELOCS_DONE) return goof; + tpnt->init_flag |= RELOCS_DONE; + + goof += _dl_parse_relocation_information(tpnt, tpnt->dynamic_info[DT_RELA], + tpnt->dynamic_info[DT_RELASZ], 0); +#else + goof++; +#endif + } + if(tpnt->dynamic_info[DT_JMPREL]) + { + if (tpnt->init_flag & JMP_RELOCS_DONE) return goof; + tpnt->init_flag |= JMP_RELOCS_DONE; + + if(flag == RTLD_LAZY) + _dl_parse_lazy_relocation_information(tpnt, tpnt->dynamic_info[DT_JMPREL], + tpnt->dynamic_info[DT_PLTRELSZ], 0); + else + goof += _dl_parse_relocation_information(tpnt, + tpnt->dynamic_info[DT_JMPREL], + tpnt->dynamic_info[DT_PLTRELSZ], 0); + }; + return goof; +} + +void * _dlsym(void * vhandle, char * name) +{ + struct elf_resolve * tpnt, *tfrom; + struct dyn_elf * handle; + char * from; + struct dyn_elf * rpnt; + void *ret; + + handle = (struct dyn_elf *) vhandle; + + /* First of all verify that we have a real handle + of some kind. Return NULL if not a valid handle. */ + + if (handle == NULL) + handle = _dl_symbol_tables; + else if (handle != RTLD_NEXT && handle != _dl_symbol_tables) { + for(rpnt = _dl_handles; rpnt; rpnt = rpnt->next_handle) + if (rpnt == handle) break; + if (!rpnt) { + _dl_error_number = DL_BAD_HANDLE; + return NULL; + } + } + else if (handle == RTLD_NEXT ) + { + /* + * Try and locate the module we were called from - we + * need this so that we know where to start searching + * from. We never pass RTLD_NEXT down into the actual + * dynamic loader itself, as it doesn't know + * how to properly treat it. + */ + from = __builtin_return_address(0); + + tfrom = NULL; + for(rpnt = _dl_symbol_tables; rpnt; rpnt = rpnt->next) + { + tpnt = rpnt->dyn; + if( tpnt->loadaddr < from + && ( tfrom == NULL + || tfrom->loadaddr < tpnt->loadaddr)) + { + tfrom = tpnt; + handle = rpnt->next; + } + } + } + + ret = _dl_find_hash(name, handle, 1, NULL, 1); + + /* + * Nothing found. + */ + if (!ret) + _dl_error_number = DL_NO_SYMBOL; + return ret; +} + +int _dlclose(void * vhandle) +{ + return do_dlclose(vhandle, 1); +} + +static int do_dlclose(void * vhandle, int need_fini) +{ + struct dyn_elf * rpnt, *rpnt1; + struct dyn_elf *spnt, *spnt1; + struct elf_phdr * ppnt; + struct elf_resolve * tpnt; + int (*dl_elf_fini)(void); + void (*dl_brk)(void); + struct dyn_elf * handle; + unsigned int end; + int i = 0; + + handle = (struct dyn_elf *) vhandle; + rpnt1 = NULL; + for(rpnt = _dl_handles; rpnt; rpnt = rpnt->next_handle) + { + if(rpnt == handle) { + break; + } + rpnt1 = rpnt; + } + + if (!rpnt) { + _dl_error_number = DL_BAD_HANDLE; + return 1; + } + + /* OK, this is a valid handle - now close out the file. + * We check if we need to call fini () on the handle. */ + spnt = need_fini ? handle : handle->next; + for(; spnt; spnt = spnt1) + { + spnt1 = spnt->next; + + /* We appended the module list to the end - when we get back here, + quit. The access counts were not adjusted to account for being here. */ + if (spnt == _dl_symbol_tables) break; + if(spnt->dyn->usage_count==1 && spnt->dyn->libtype == loaded_file) { + tpnt = spnt->dyn; + /* Apparently crt1 for the application is responsible for handling this. + * We only need to run the init/fini for shared libraries + */ + + if(tpnt->dynamic_info[DT_FINI]) { + dl_elf_fini = (int (*)(void)) (tpnt->loadaddr + + tpnt->dynamic_info[DT_FINI]); + (*dl_elf_fini)(); + } + } + } + if(rpnt1) + rpnt1->next_handle = rpnt->next_handle; + else + _dl_handles = rpnt->next_handle; + + /* OK, this is a valid handle - now close out the file */ + for(rpnt = handle; rpnt; rpnt = rpnt1) + { + rpnt1 = rpnt->next; + + /* We appended the module list to the end - when we get back here, + quit. The access counts were not adjusted to account for being here. */ + if (rpnt == _dl_symbol_tables) break; + + rpnt->dyn->usage_count--; + if(rpnt->dyn->usage_count == 0 && rpnt->dyn->libtype == loaded_file) + { + tpnt = rpnt->dyn; + /* Apparently crt1 for the application is responsible for handling this. + * We only need to run the init/fini for shared libraries + */ +#if 0 +/* We have to do this above, before we start closing objects. +Otherwise when the needed symbols for _fini handling are +resolved a coredump would occur. Rob Ryan (robr@cmu.edu)*/ + if(tpnt->dynamic_info[DT_FINI]) { + dl_elf_fini = (int (*)(void)) (tpnt->loadaddr + + tpnt->dynamic_info[DT_FINI]); + (*dl_elf_fini)(); + } +#endif + end = 0; + for(i = 0, ppnt = rpnt->dyn->ppnt; + i < rpnt->dyn->n_phent; ppnt++, i++) { + if (ppnt->p_type != PT_LOAD) continue; + if (end < ppnt->p_vaddr + ppnt->p_memsz) + end = ppnt->p_vaddr + ppnt->p_memsz; + } + _dl_munmap (rpnt->dyn->loadaddr, end); + /* Next, remove rpnt->dyn from the loaded_module list */ + if (_dl_loaded_modules == rpnt->dyn) + { + _dl_loaded_modules = rpnt->dyn->next; + if (_dl_loaded_modules) + _dl_loaded_modules->prev = 0; + } + else + for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) + if (tpnt->next == rpnt->dyn) { + tpnt->next = tpnt->next->next; + if (tpnt->next) + tpnt->next->prev = tpnt; + break; + } + free(rpnt->dyn->libname); + free(rpnt->dyn); + } + free(rpnt); + } + + + dl_brk = (void (*)()) _dl_debug_addr->r_brk; + if( dl_brk != NULL ) + { + _dl_debug_addr->r_state = RT_DELETE; + (*dl_brk)(); + + _dl_debug_addr->r_state = RT_CONSISTENT; + (*dl_brk)(); + } + + return 0; +} + +const char * _dlerror() +{ + const char * retval; + if(!_dl_error_number) return NULL; + retval = dl_error_names[_dl_error_number]; + _dl_error_number = 0; + return retval; +} + +/* Generate the correct symbols that we need. */ +#if 1 +#pragma weak dlopen = _dlopen +#pragma weak dlerror = _dlerror +#pragma weak dlclose = _dlclose +#pragma weak dlsym = _dlsym +#pragma weak dladdr = _dladdr +#else +__asm__(".weak dlopen;dlopen=_dlopen"); +__asm__(".weak dlerror;dlerror=_dlerror"); +__asm__(".weak dlclose;dlclose=_dlclose"); +__asm__(".weak dlsym;dlsym=_dlsym"); +__asm__(".weak dladdr;dladdr=_dladdr"); +#endif + +/* This is a real hack. We need access to the dynamic linker, but we +also need to make it possible to link against this library without any +unresolved externals. We provide these weak symbols to make the link +possible, but at run time the normal symbols are accessed. */ + +static void foobar() +{ + _dl_fdprintf(2,"libdl library not correctly linked\n"); + _dl_exit(1); +} + +static int foobar1 = (int)foobar; /* Use as pointer */ + +#if 1 +#pragma weak _dl_find_hash = foobar +#pragma weak _dl_symbol_tables = foobar1 +#pragma weak _dl_handles = foobar1 +#pragma weak _dl_loaded_modules = foobar1 +#pragma weak _dl_debug_addr = foobar1 +#pragma weak _dl_error_number = foobar1 +#pragma weak _dl_load_shared_library = foobar +#ifdef USE_CACHE +#pragma weak _dl_map_cache = foobar +#pragma weak _dl_unmap_cache = foobar +#endif +#pragma weak _dl_malloc_function = foobar1 +#pragma weak _dl_parse_relocation_information = foobar +#pragma weak _dl_parse_lazy_relocation_information = foobar +#pragma weak _dl_fdprintf = foobar +#else +__asm__(".weak _dl_find_hash; _dl_find_hash = foobar"); +__asm__(".weak _dl_symbol_tables; _dl_symbol_tables = foobar1"); +__asm__(".weak _dl_handles; _dl_handles = foobar1"); +__asm__(".weak _dl_loaded_modules; _dl_loaded_modules = foobar1"); +__asm__(".weak _dl_debug_addr; _dl_debug_addr = foobar1"); +__asm__(".weak _dl_error_number; _dl_error_number = foobar1"); +__asm__(".weak _dl_load_shared_library; _dl_load_shared_library = foobar"); +#ifdef USE_CACHE +__asm__(".weak _dl_map_cache; _dl_map_cache = foobar"); +__asm__(".weak _dl_unmap_cache; _dl_unmap_cache = foobar"); +#endif +__asm__(".weak _dl_malloc_function; _dl_malloc_function = foobar1"); +__asm__(".weak _dl_parse_relocation_information; _dl_parse_relocation_information = foobar"); +__asm__(".weak _dl_parse_lazy_relocation_information; _dl_parse_lazy_relocation_information = foobar"); +__asm__(".weak _dl_fdprintf; _dl_fdprintf = foobar"); +#endif + +/* + * Dump information to stderrr about the current loaded modules + */ +static char * type[] = {"Lib","Exe","Int","Mod"}; + +void _dlinfo() +{ + struct elf_resolve * tpnt; + struct dyn_elf * rpnt, *hpnt; + _dl_fdprintf(2, "List of loaded modules\n"); + /* First start with a complete list of all of the loaded files. */ + for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) + _dl_fdprintf(2, "\t%8.8x %8.8x %8.8x %s %d %s\n", + (unsigned)tpnt->loadaddr, (unsigned)tpnt, + (unsigned)tpnt->symbol_scope, + type[tpnt->libtype], + tpnt->usage_count, + tpnt->libname); + + /* Next dump the module list for the application itself */ + _dl_fdprintf(2, "\nModules for application (%x):\n", + (unsigned)_dl_symbol_tables); + for (rpnt = _dl_symbol_tables; rpnt; rpnt = rpnt->next) + _dl_fdprintf(2, "\t%8.8x %s\n", (unsigned)rpnt->dyn, + rpnt->dyn->libname); + + for (hpnt = _dl_handles; hpnt; hpnt = hpnt->next_handle) + { + _dl_fdprintf(2, "Modules for handle %x\n", (unsigned)hpnt); + for(rpnt = hpnt; rpnt; rpnt = rpnt->next) + _dl_fdprintf(2, "\t%8.8x %s\n", (unsigned)rpnt->dyn, + rpnt->dyn->libname); + } +} + +int _dladdr(void * __address, Dl_info * __dlip ) +{ + struct elf_resolve * pelf; + struct elf_resolve * rpnt; + +#ifdef USE_CACHE + _dl_map_cache(); +#endif + + /* + * Try and locate the module address is in + */ + pelf = NULL; + +#if 0 + _dl_fdprintf( 2, + "dladdr( 0x%p, 0x%p )\n", + __address, __dlip ); +#endif + + for(rpnt = _dl_loaded_modules; rpnt; rpnt = rpnt->next) + { + struct elf_resolve * tpnt; + tpnt = rpnt; +#if 0 + _dl_fdprintf( 2, + "Module \"%s\" at 0x%p\n", + tpnt->libname, + tpnt->loadaddr ); +#endif + if( tpnt->loadaddr < (char*)__address + && ( pelf == NULL + || pelf->loadaddr < tpnt->loadaddr)) + pelf = tpnt; + } + + if ( ! pelf ) + { + return 0; + } + + /* + * Try and locate the symbol of address + */ + + { + char * strtab; + struct elf32_sym * symtab; + int hn, si; + int sf; + int sn = 0; + void* sa = 0; + + symtab = (struct elf32_sym *) (pelf->dynamic_info[DT_SYMTAB] + + pelf->loadaddr); + strtab = (char *) (pelf->dynamic_info[DT_STRTAB] + pelf->loadaddr); + + sf = 0; + for(hn = 0; hn < pelf->nbucket; hn++) + { + for(si = pelf->elf_buckets[hn]; si; si = pelf->chains[si]) + { + void* symbol_addr; + symbol_addr = pelf->loadaddr + symtab[si].st_value; + if ( symbol_addr <= __address && + ( !sf || sa < symbol_addr ) ) + { + sa = symbol_addr; + sn = si; + sf = 1; + } +#if 0 + _dl_fdprintf( 2, + "Symbol \"%s\" at 0x%p\n", + strtab + symtab[si].st_name, + symbol_addr ); +#endif + } + } + + if ( sf ) + { + __dlip->dli_fname = pelf->libname; + __dlip->dli_fbase = pelf->loadaddr; + __dlip->dli_sname = strtab + symtab[sn].st_name; + __dlip->dli_saddr = sa; + } + return 1; + } +} diff --git a/ldso/man/Makefile b/ldso/man/Makefile new file mode 100644 index 000000000..a701e1513 --- /dev/null +++ b/ldso/man/Makefile @@ -0,0 +1,11 @@ +include ../Config.mk + +ALL = #ld.so.info + +all: $(ALL) + +ld.so.info: ld.so.texi + makeinfo $< + +clean: + rm -f $(ALL) *~ diff --git a/ldso/man/dlopen.3 b/ldso/man/dlopen.3 new file mode 100644 index 000000000..8d1e09e71 --- /dev/null +++ b/ldso/man/dlopen.3 @@ -0,0 +1,218 @@ +.\" -*- nroff -*- +.\" Copyright 1995 Yggdrasil Computing, Incorporated. +.\" written by Adam J. Richter (adam@yggdrasil.com), +.\" with typesetting help from Daniel Quinlan (quinlan@yggdrasil.com). +.\" +.\" This is free documentation; you can redistribute it and/or +.\" modify it under the terms of the GNU General Public License as +.\" published by the Free Software Foundation; either version 2 of +.\" the License, or (at your option) any later version. +.\" +.\" The GNU General Public License's references to "object code" +.\" and "executables" are to be interpreted as the output of any +.\" document formatting or typesetting system, including +.\" intermediate and printed output. +.\" +.\" This manual is distributed in the hope that 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 manual; if not, write to the Free +.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, +.\" USA. +.\" +.TH DLOPEN 3 "16 May 1995" "Linux" "Linux Programmer's Manual" +.SH NAME +dlclose, dlerror, dlopen, dlsym \- Programming interface to dynamic linking loader. +.SH SYNOPSIS +.B #include <dlfcn.h> +.sp +.BI "void *dlopen (const char *" "filename" ", int " flag "); +.br +.BI "const char *dlerror(void);" +.br +.BI "void *dlsym(void *"handle ", char *"symbol ");" +.br +.BI "int dladdr(void *"address ", Dl_info *"dlip ");" +.br +.BI "int dlclose (void *"handle "); +.sp +Special symbols: +.BR "_init" ", " "_fini" ". " +.SH DESCRIPTION +.B dlopen +loads a dynamic library from the file named by the null terminated +string +.I filename +and returns an opaque "handle" for the dynamic library. +If +.I filename +is not an absolute path (i.e., it does not begin with a "/"), then the +file is searched for in the following locations: +.RS +.PP +A colon-separated list of directories in the user's +\fBLD_LIBRARY\fP path environment variable. +.PP +The list of libraries specified in \fI/etc/ld.so.cache\fP. +.PP +\fI/usr/lib\fP, followed by \fI/lib\fP. +.RE +.PP +If +.I filename +is a NULL pointer, then the returned handle is for the main program. +.PP +External references in the library are resolved using the libraries +in that library's dependency list and any other libraries previously +opened with the +.B RTLD_GLOBAL +flag. +If the executable was linked +with the flag "-rdynamic", then the global symbols in the executable +will also be used to resolve references in a dynamically loaded +library. +.PP +.I flag +must be either +.BR RTLD_LAZY , +meaning resolve undefined symbols as code from the dynamic library is +executed, or +.BR RTLD_NOW , +meaning resolve all undefined symbols before +.B dlopen +returns, and fail if this cannot be done. +Optionally, +.B RTLD_GLOBAL +may be or'ed with +.IR flag, +in which case the external symbols defined in the library will be +made available to subsequently loaded libraries. +.PP +If the library exports a routine named +.BR _init , +then that code is executed before dlopen returns. +If the same library is loaded twice with +.BR dlopen() , +the same file handle is returned. The dl library maintains link +counts for dynamic file handles, so a dynamic library is not +deallocated until +.B dlclose +has been called on it as many times as +.B dlopen +has succeeded on it. +.PP +If +.B dlopen +fails for any reason, it returns NULL. +A human readable string describing the most recent error that occurred +from any of the dl routines (dlopen, dlsym or dlclose) can be +extracted with +.BR dlerror() . +.B dlerror +returns NULL if no errors have occurred since initialization or since +it was last called. (Calling +.B dlerror() +twice consecutively, will always result in the second call returning +NULL.) + +.B dlsym +takes a "handle" of a dynamic library returned by dlopen and the null +terminated symbol name, returning the address where that symbol is +loaded. If the symbol is not found, +.B dlsym +returns NULL; however, the correct way to test for an error from +.B dlsym +is to save the result of +.B dlerror +into a variable, and then check if saved value is not NULL. +This is because the value of the symbol could actually be NULL. +It is also necessary to save the results of +.B dlerror +into a variable because if +.B dlerror +is called again, it will return NULL. +.PP +.B dladdr +returns information about the shared library containing the memory +location specified by +.IR address . +.B dladdr +returns zero on success and non-zero on error. +.PP +.B dlclose +decrements the reference count on the dynamic library handle +.IR handle . +If the reference count drops to zero and no other loaded libraries use +symbols in it, then the dynamic library is unloaded. If the dynamic +library exports a routine named +.BR _fini , +then that routine is called just before the library is unloaded. +.SH EXAMPLES +.B Load the math library, and print the cosine of 2.0: +.RS +.nf +.if t .ft CW +#include <dlfcn.h> + +int main(int argc, char **argv) { + void *handle = dlopen ("/lib/libm.so", RTLD_LAZY); + double (*cosine)(double) = dlsym(handle, "cos"); + printf ("%f\\n", (*cosine)(2.0)); + dlclose(handle); +} +.if t .ft P +.fi +.PP +If this program were in a file named "foo.c", you would build the program +with the following command: +.RS +.LP +gcc -rdynamic -o foo foo.c -ldl +.RE +.RE +.LP +.B Do the same thing, but check for errors at every step: +.RS +.nf +.if t .ft CW +#include <stdio.h> +#include <dlfcn.h> + +int main(int argc, char **argv) { + void *handle; + double (*cosine)(double); + char *error; + + handle = dlopen ("/lib/libm.so", RTLD_LAZY); + if (!handle) { + fputs (dlerror(), stderr); + exit(1); + } + + cosine = dlsym(handle, "cos"); + if ((error = dlerror()) != NULL) { + fputs(error, stderr); + exit(1); + } + + printf ("%f\\n", (*cosine)(2.0)); + dlclose(handle); +} +.if t .ft P +.fi +.RE +.SH ACKNOWLEDGEMENTS +The dlopen interface standard comes from Solaris. +The Linux dlopen implementation was primarily written by +Eric Youngdale with help from Mitch D'Souza, David Engel, +Hongjiu Lu, Andreas Schwab and others. +The manual page was written by Adam Richter. +.SH SEE ALSO +.BR ld(1) , +.BR ld.so(8) , +.BR ldconfig(8) , +.BR ldd(1) , +.BR ld.so.info . diff --git a/ldso/man/ld.so.8 b/ldso/man/ld.so.8 new file mode 100644 index 000000000..59ec8530d --- /dev/null +++ b/ldso/man/ld.so.8 @@ -0,0 +1,113 @@ +.TH ld.so 8 "14 March 1998" +.SH NAME +ld.so/ld-linux.so \- dynamic linker/loader +.SH DESCRIPTION +.B ld.so +loads the shared libraries needed by a program, prepares the program +to run, and then runs it. +Unless explicitly specified via the +.B \-static +option to +.B ld +during compilation, all Linux programs are incomplete and require +further linking at run time. +.PP +The necessary shared libraries needed by the program are searched for +in the following order +.IP o +Using the environment variable +.B LD_LIBRARY_PATH +.RB ( LD_AOUT_LIBRARY_PATH +for a.out programs). +Except if the executable is a setuid/setgid binary, in which case it +is ignored. +.IP o +From the cache file +.BR /etc/ld.so.cache +which contains a compiled list of candidate libraries previously found +in the augmented library path. +.IP o +In the default path +.BR /usr/lib , +and then +.BR /lib . +.SH ENVIRONMENT +.TP +.B LD_LIBRARY_PATH +A colon-separated list of directories in which to search for +ELF libraries at execution-time. +Similar to the +.B PATH +environment variable. +.TP +.B LD_PRELOAD +A whitespace-separated list of additional, user-specified, ELF shared +libraries to be loaded before all others. +This can be used to selectively override functions in other shared libraries. +For setuid/setgid ELF binaries, only libraries in the standard search +directories that are also setgid will be loaded. +.TP +.B LD_TRACE_LOADED_OBJECTS +If present, causes the program to list its dynamic library dependencies, +as if run by ldd, instead of running normally. +.TP +.B LD_BIND_NOW +If present, causes the dynamic linker to resolve all symbols at program +startup instead of when they are first referenced. +.TP +.B LD_AOUT_LIBRARY_PATH +A colon-separated list of directories in which to search for +a.out libraries at execution-time. +Similar to the +.B PATH +environment variable. +.TP +.B LD_AOUT_PRELOAD +The name of an additional, user-specified, a.out shared library to be loaded +after all others. +This can be used to selectively override functions in other shared libraries. +.TP +.B LD_NOWARN +Suppress warnings about a.out libraries with incompatible minor +version numbers. +.TP +.B LD_KEEPDIR +Don't ignore the directory in the names of a.out libraries to be loaded. +Use of this option is strongly discouraged. +.SH FILES +.PD 0 +.TP 20 +.B /lib/ld.so +a.out dynamic linker/loader +.TP 20 +.B /lib/ld-linux.so.* +ELF dynamic linker/loader +.TP +.B /etc/ld.so.cache +File containing a compiled list of directories in which to search for +libraries and an ordered list of candidate libraries. +.TP +.B /etc/ld.so.preload +File containing a whitespace separated list of ELF shared libraries to +be loaded before the program. +libraries and an ordered list of candidate libraries. +.TP +.B lib*.so* +shared libraries +.PD +.SH SEE ALSO +.BR ldd (1), +.BR ldconfig (8). +.SH BUGS +.LP +Currently +.B ld.so +has no means of unloading and searching for compatible or newer version of +libraries. +.PP +.B ld.so +functionality is only available for executables compiled using libc version +4.4.3 or greater. +.SH AUTHORS +David Engel, Eric Youngdale, Peter MacDonald, Hongjiu Lu, Linus +Torvalds, Lars Wirzenius and Mitch D'Souza (not necessarily in that order). diff --git a/ldso/man/ld.so.texi b/ldso/man/ld.so.texi new file mode 100644 index 000000000..4e5fb841b --- /dev/null +++ b/ldso/man/ld.so.texi @@ -0,0 +1,411 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename ld.so.info +@settitle ld.so : Dynamic-Link Library support +@c %**end of header + +@ifinfo +This file documents the dynamic-link support libraries and utilities for the +Linux OS, version 1.8.1. + +Copyright 1996 Michael Deutschmann + +This document is subject to the GNU General Public License as published by +the Free Software foundation, version 2 or later (your choice). + +Note: The software described in this document is under a different copyright +and license. + +@end ifinfo + +@titlepage +@title ld.so +@subtitle Dynamic Link library support for the Linux OS. +@author David Engel +@author Eric Youngdale +@author Peter Macdonald +@author Hongjiu Lu +@author Mitch D'Souza +@author Michael Deutschmann (this documentation) + +@page +Copyright @copyright{} 1996 Michael Deutschmann + +This document is subject to the GNU General Public License as published by +the Free Software foundation, version 2 or later (your choice). + +Note: The software described in this document is under a different copyright +and license. +@end titlepage + +@ifinfo +@node Top +@top + +The @code{ld.so} module provides dynamic linked library support in Linux. +This file documents @code{ld.so} and its companion software. + +@menu +* intro:: Introduction + +* ld.so:: The dynamic linker core program +* ldd:: A utility to print out dependencies +* ldconfig:: A utility to maintain the cache and symlinks +* libdl:: Manual dynamic linking library +@end menu + +@end ifinfo + +@node intro +@unnumbered Introduction + +The @code{ld.so} suite contains special files and utilities needed for linux +to handle @dfn{dynamic libraries}. + +Ordinary static libraries (@file{lib*.a} files) are included into executables +that use their functions. A file that only uses static libraries needs less +intelligence to load, but takes up more space. If many executables use the +same library, there can be much wastage of storage space, since multiple +copies of the library functions are scattered across the executables. +However, static libraries are easier to make. + +Dynamic libraries (@file{lib*.so*} files) are not copied into executables --- +the executable is written in such a way that it will automatically load the +libraries. In linux, the executable will first load the special library +@code{ld.so} or @code{ld-linux.so}, which contains the intelligence +to load further dynamic libraries. Since multiple files end up getting +executable data from the same file, dynamic libraries are also known as +shared libraries. + +Linux executables come in two flavors, @sc{elf} and a.out. + +a.out is the original executable format used by Linux. It has somewhat less +overhead than @sc{elf}. However creating shared libraries for a.out is +@emph{very} involved, and each a.out shared library must be explicitly +registered. + +@sc{elf} is a more recent format, which supports a much simpler method of +creating libraries. @sc{elf} libraries may also be linked manually +(@pxref{libdl}). + +Since many library authors prefer @sc{elf} and no longer release shared a.out +libraries, a.out is moribund on Linux. This version of the @code{ld.so} can +be compiled to support only @sc{elf}, or to support both formats. (The last +release of ld.so to support a.out alone was 1.8.0.) + +@node ld.so +@chapter @code{ld.so}: Dynamic linker core + +@code{ld.so} works behind the scenes to handle dynamic libraries in Linux. +Users will almost never have to deal with it directly, but in special cases +one can send instructions to it through environment variables. Also, if +something is wrong with your libraries (usually an incorrect version) ld.so +will give error messages. + +Actually @code{ld.so} is the a.out linker. The new @sc{elf} executables are +handled by a related program @code{ld-linux.so}. + +@menu +* files:: Configuration files used by the suite +* environment:: Environment settings that tweak @code{ld.so} +* errors:: Complaints @code{ld.so} might make +@end menu + +@node files +@section Configuration Files + +@table @file +@item /etc/ld.so.cache +A file created by @code{ldconfig} and used to speed linking. It's structure +is private to the suite. + +@item /etc/ld.so.conf +A simple list of directories to scan for libraries, in addition to +@file{/usr/lib} and @file{/lib}, which are hardwired. It may contain +comments started with a @samp{#}. + +@item /etc/ld.so.preload +A list of libraries to preload. This allows preloading libraries for +setuid/setgid executables securely. It may contain comments. +@end table + +@node environment +@section Environment Variables + +@table @code +@item LD_AOUT_LIBRARY_PATH +@itemx LD_LIBRARY_PATH +These variables supply a library path for finding dynamic libraries, in the +standard colon seperated format. These variables are ignored when executing +setuid/setgid programs, because otherwise they would be a security hazard. +@code{ld.so} will use @code{LD_AOUT_LIBRARY_PATH} and @code{ld-linux.so} will +use @code{LD_LIBRARY_PATH}. + +@item LD_AOUT_PRELOAD +@itemx LD_PRELOAD +These variables allow an extra library not specified in the executable to be +loaded. Generally this is only useful if you want to override a function. +These are also ignored when running setuid/setgid executables. @code{ld.so} +will use @code{LD_AOUT_PRELOAD} and @code{ld-linux.so} will use +@code{LD_PRELOAD}. + +@item LD_NOWARN +If non-empty, errors about incompatible minor revisions are suppressed. + +@item LD_KEEPDIR +If non-empty, allow executables to specify absolute library names. This +option is deprecated. +@c FIXME: +@c The following are things I noticed in the ld-linux.so source. +@c I don't really understand 'em. Could someone help me? +@c +@c @item LD_BIND_NOW +@c This option is used by the @code{ld-linux.so} only. I don't know +@c what it does. (I suspect, looking at the code, that it specifies +@c "RTLD_NOW" rather than "RTLD_LAZY" mode for the shared libraries.) +@c +@c @item LD_TRACE_LOADED_OBJECTS +@c @itemx LD_WARN +@c These seem to have something to do with the communication between the +@c @code{ld-linux.so} and @code{ldd}. I don't know more. +@end table + +@node errors +@section Errors + +@table @samp +@item Can't find library @var{library} +The executable required a dynamically linked library that ld.so cannot find. +Your symbolic links may be not set right, or you may have not installed a +library needed by the program. + +@item Can't load library @var{library} +The library is corrupt. + +@item Incompatible library @var{library} +@itemx Require major version @var{x} and found @var{y} +Your version of the library is incompatible with the executable. Recompiling +the executable, or upgrading the library will fix the problem. + +@item using incompatible library @var{library} +@itemx Desire minor version >= @var{x} and found @var{y}. +Your version of the library is older than that expected by the executable, +but not so old that the library interface has radically changed, so the +linker will attempt to run anyway. There is a chance that it will work, but +you should upgrade the library or recompile the software. The environment +variable @code{LD_NOWARN} can be used to supress this message. + +@item too many directories in library path +The linker only supports up to 32 library directories. You have too many. + +@item dynamic linker error in @var{blah} +The linker is having trouble handling a binary - it is probably corrupt. + +@item can't map cache file @var{cache-file} +@itemx cache file @var{cache-file} @var{blah} +The linker cache file (generally @file{/etc/ld.so.cache}) is corrupt or +non-existent. These errors can be ignored, and can be prevented by +regenerating the cache file with @code{ldconfig}. +@end table + +@node ldd +@chapter @code{ldd}: Dependency scanner + +@code{ldd} is a utility that prints out the dynamic libraries that an +executable is linked to. + +Actually @code{ldd} works by signalling ld.so to print the dependencies. +For a.out executables this is done by starting the executable with +@code{argc} equal to 0. The linker detects this and prints the dependencies. +(This can cause problems with @emph{very} old binaries, which would run as +normal only with an inappropriate @code{argc}.) + +For @sc{elf} executables, special environment variables are used to tell the +linker to print the dependencies. + +@code{ldd} has a few options: + +@table @samp +@item -v +Print the version number of @code{ldd} itself + +@item -V +Print the version number of the dynamic linker + +@item -d +Report missing functions. This is only supported for @sc{elf} executables. + +@item -r +Report missing objects. This is also only available for @sc{elf} +executables. +@end table + +@node ldconfig +@chapter @code{ldconfig}: Setup program + +This utility is used by the system administrator to automatically set up +symbolic links needed by the libraries, and also to set up the cache file. + +@code{ldconfig} is run after new dynamic libraries are installed, and if the +cache file or links are damaged. It is also run when upgrading the +@code{ld.so} suite itself. + +The @file{/lib} and @file{/usr/lib} directories, and any listed in the file +@file{/etc/ld.so.conf} are scanned by default unless @samp{-n} is used. +Additional directories may be specified on the command line. + +It has the following options: + +@table @samp +@item -D +Enter debug mode. Implies @samp{-N} and @samp{-X}. + +@item -v +Verbose. Print out links created and directories scanned. + +@item -n +Check directories specified on the commandline @emph{only}. + +@item -N +Do not regenerate the cache. + +@item -X +Do not rebuild symbolic links. + +@item -l +Set up symbolic links for only libraries presented on the command line. + +@item -p +Print out the library pathnames in the cache file (@file{/etc/ld.so.cache}) +@end table + +@node libdl +@chapter User dynamic linking library + +The @code{ld.so} package includes a small library of functions +(@code{libdl}) to allow manual dynamic linking. Normally programs are linked +so that dynamic functions and objects are automagically available. These +functions allow one to manually load and access a symbol from a library. +They are only available for @sc{elf} executables. + +@menu +* using libdl:: General points +* functions:: How to use the functions +* example:: A sample program +@end menu + +@node using libdl +@section Overview + +To access this library, add the flag @samp{-ldl} to your compile command when +linking the executable. You also must include the header file +@code{dlfcn.h}. You may also need the flag @samp{-rdynamic}, which enables +resolving references in the loaded libraries against your executable. + +Generally, you will first use @code{dlopen} to open a library. Then you use +@code{dlsym} one or more times to access symbols. Finally you use +@code{dlclose} to close the library. + +These facilities are most useful for language interpreters that provide +access to external libraries. Without @code{libdl}, it would be neccessary +to link the interpreter executable with any and all external libraries +needed by the programs it runs. With @code{libdl}, the interpreter only +needs to be linked with the libraries it uses itself, and can dynamically +load in additional ones if programs need it. + +@node functions +@section Functions + +@deftypefun void *dlopen ( const char @var{filename}, int @var{flags} ) + +This function opens the dynamic library specified by @var{filename} +and returns an abstract handle, which can be used in subsequent calls to +@code{dlsym}. The function will respect the @code{LD_ELF_LIBRARY_PATH} and +@code{LD_LIBRARY_PATH} environment variables. + +@end deftypefun + +The following flags can be used with @code{dlopen}: + +@deftypevr Macro int RTLD_LAZY +Resolve symbols in the library as they are needed. +@end deftypevr + +@deftypevr Macro int RTLD_NOW +Resolve all symbols in the library before returning, and fail if not all can +be resolved. This is mutually exclusive with @code{RTLD_LAZY}. +@end deftypevr + +@deftypevr Macro int RTLD_GLOBAL +Make symbols in this library available for resolving symbols in other +libraries loaded with @code{dlopen}. +@end deftypevr + +@deftypefun int dlclose ( void *@var{handle} ) + +This function releases a library handle. + +Note that if a library opened twice, the handle will be the same. However, +a reference count is used, so you should still close the library as many +times as you open it. + +@end deftypefun + +@deftypefun void *dlsym (void *@var{handle},char *@var{symbol-name}) + +This function looks up the name @var{symbol-name} in the library and returns +it in the void pointer. + +If there is an error, a null pointer will be returned. However, it is +possible for a valid name in the library to have a null value, so +@code{dlerror} should be used to check if there was an error. + +@end deftypefun + +@deftypefun {libdl function} {const char} *dlerror( void ) + +This function is used to read the error state. It returns a human-readable +string describing the last error, or null, meaning no error. + +The function resets the error value each time it is called, so the result +should be copied into a variable. If the function is called more than once +after an error, the second and subsequent calls will return null. + +@end deftypefun + +@node example +@section Example program + +Here is an example program that prints the cosine of two by manually linking +to the math library: + +@example +@c The following was snarfed verbatim from the dlopen.3 man file. +#include <stdio.h> +#include <dlfcn.h> + +int main(int argc, char **argv) @{ + void *handle; + double (*cosine)(double); + char *error; + + handle = dlopen ("/lib/libm.so", RTLD_LAZY); + if (!handle) @{ + fputs (dlerror(), stderr); + exit(1); + @} + + cosine = dlsym(handle, "cos"); + if ((error = dlerror()) != NULL) @{ + fputs(error, stderr); + exit(1); + @} + + printf ("%f\\n", (*cosine)(2.0)); + dlclose(handle); +@} +@end example + +@contents + +@bye diff --git a/ldso/man/ldconfig.8 b/ldso/man/ldconfig.8 new file mode 100644 index 000000000..82285291f --- /dev/null +++ b/ldso/man/ldconfig.8 @@ -0,0 +1,189 @@ +.TH ldconfig 8 "14 March 1998" +.SH NAME +ldconfig \- determine run-time link bindings +.SH SYNOPSIS +ldconfig +.RB [ \-DvqnNX ] +.RB [ \-f\ conf ] +.RB [ \-C\ cache ] +.RB [ \-r\ root ] +.IR directory \ ... +.PD 0 +.PP +.PD +ldconfig +.B \-l +.RB [ \-Dvq ] +.IR library \ ... +.PD 0 +.PP +.PD +ldconfig +.B \-p +.SH DESCRIPTION +.B ldconfig +creates the necessary links and cache (for use by the run-time linker, +.IR ld.so ) +to the most recent shared libraries found in the directories specified +on the command line, in the file +.IR /etc/ld.so.conf , +and in the trusted directories +.RI ( /usr/lib +and +.IR /lib ). +.B ldconfig +checks the header and file names of the libraries it encounters when +determining which versions should have their links updated. +.B ldconfig +ignores symbolic links when scanning for libraries. +.PP +.B ldconfig +will attempt to deduce the type of ELF libs (ie. libc5 or libc6/glibc) +based on what C libs if any the library was linked against, therefore when +making dynamic libraries, it is wise to explicitly link against libc (use -lc). +.PP +Some existing libs do not contain enough information to allow the deduction of +their type, therefore the +.IR /etc/ld.so.conf +file format allows the specification of an expected type. This is +.B only +used for those ELF libs which we can not work out. The format +is like this "dirname=TYPE", where type can be libc4, libc5 or libc6. +(This syntax also works on the command line). Spaces are +.B not +allowed. Also see the +.B -p +option. +.PP +Directory names containing an +.B = are no longer legal +unless they also have an expected type specifier. +.PP +.B ldconfig +should normally be run by the super-user as it may require write +permission on some root owned directories and files. +It is normally run automatically at bootup, from /etc/rc, or manually +whenever new DLL's are installed. +.SH OPTIONS +.TP +.B \-D +Debug mode. +Implies +.B \-N +and +.BR \-X . +.TP +.B \-v +Verbose mode. +Print current version number, the name of each directory as it +is scanned and any links that are created. +Overrides quiet mode. +.TP +.B \-q +Quiet mode. +Don't print warnings. +.TP +.B \-n +Only process directories specified on the command line. +Don't process the trusted directories +.RI ( /usr/lib +and +.IR /lib ) +nor those specified in +.IR /etc/ld.so.conf . +Implies +.BR \-N . +.TP +.B \-N +Don't rebuild the cache. +Unless +.B \-X +is also specified, links are still updated. +.TP +.B \-X +Don't update links. +Unless +.B \-N +is also specified, the cache is still rebuilt. +.TP +.B \-f conf +Use +.B conf +instead of +.IR /etc/ld.so.conf . +.TP +.B \-C cache +Use +.B cache +instead of +.IR /etc/ld.so.cache . +.TP +.B \-r root +Change to and use +.B root +as the root directory. +.TP +.B \-l +Library mode. +Manually link individual libraries. +Intended for use by experts only. +.TP +.B \-p +Print the lists of directories and candidate libraries stored in +the current cache. +.SH EXAMPLES +In the bootup file +.I /etc/rc +having the line +.RS + +/sbin/ldconfig -v + +.RE +will set up the correct links for the shared binaries and rebuild +the cache. +.TP +On the command line +.RS + +# /sbin/ldconfig -n /lib + +.RE +as root after the installation of a new DLL, will properly update the +shared library symbolic links in /lib. + +.SH FILES +.PD 0 +.TP 20 +.B /lib/ld.so +execution time linker/loader +.TP 20 +.B /etc/ld.so.conf +File containing a list of colon, space, tab, newline, or comma spearated +directories in which to search for libraries. +.TP 20 +.B /etc/ld.so.cache +File containing an ordered list of libraries found in the directories +specified in +.BR /etc/ld.so.conf . +.TP +.B lib*.so.version +shared libraries +.PD +.SH SEE ALSO +.BR ldd (1), +.BR ld.so (8). +.SH BUGS +.LP +.BR ldconfig 's +functionality, in conjunction with +.BR ld.so , +is only available for executables compiled using libc version 4.4.3 or greater. +.PP +.BR ldconfig , +being a user process, must be run manually and has no means of dynamically +determining and relinking shared libraries for use by +.BR ld.so +when a new DLL is installed. +.SH AUTHORS +David Engel and Mitch D'Souza. diff --git a/ldso/man/ldd.1 b/ldso/man/ldd.1 new file mode 100644 index 000000000..20c557847 --- /dev/null +++ b/ldso/man/ldd.1 @@ -0,0 +1,59 @@ +.\" Copyright 1995-2000 David Engel (david@ods.com) +.\" Copyright 1995 Rickard E. Faith (faith@cs.unc.edu) +.\" Most of this was copied from the README file. Do not restrict distribution. +.\" May be distributed under the GNU General Public License +.TH LDD 1 "14 March 1998" +.SH NAME +ldd \- print shared library dependencies +.SH SYNOPSIS +.B ldd +.RB [ \-vVdr ] +program|library ... +.SH DESCRIPTION +.B ldd +prints the shared libraries required by each program or shared library +specified on the command line. +If a shared library name does not contain a '/', +.B ldd +attempts to locate the library in the standard locations. +To run +.B ldd +on a shared library in the current directory, a "./" must be prepended +to its name. +.SH OPTIONS +.TP +.B \-v +Print the version number of +.BR ldd . +.TP +.B \-V +Print the version number of the dynamic linker, +.BR ld.so . +.TP +.B \-d +Perform relocations and report any missing functions (ELF only). +.TP +.B \-r +Perform relocations for both data objects and functions, and +report any missing objects (ELF only). +.SH BUGS +.B ldd +does not work very well on libc.so.5 itself. +.PP +.B ldd +does not work on a.out shared libraries. +.PP +.B ldd +does not work with some extremely old a.out programs which were +built before +.B ldd +support was added to the compiler releases. +If you use +.B ldd +on one of these programs, the program will attempt to run with argc = 0 and +the results will be unpredictable. +.SH AUTHOR +David Engel. +.SH SEE ALSO +.BR ldconfig (8), +.BR ld.so (8). diff --git a/ldso/util/Makefile b/ldso/util/Makefile new file mode 100644 index 000000000..18e8794ba --- /dev/null +++ b/ldso/util/Makefile @@ -0,0 +1,38 @@ +TOPDIR=../../ +include $(TOPDIR)Rules.mak +include ../Config.mk + +CFLAGS += -DLDSO_ADDR="0x62f00020" # needed by ldd.o +CFLAGS += -I./ -I../../include/ +LDFLAGS += -nostdlib ../../libc.a ../../crt0.o $(GCCINCDIR)/../libgcc.a + +ALL = ldconfig ldd # lddstub + +all: $(ALL) + +CSRC= readelf.c ldconfig.c ldd.c +COBJS=$(patsubst %.c,%.o, $(CSRC)) +OBJS=$(COBJS) + +$(COBJS): %.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + $(STRIPTOOL) -x -R .note -R .comment $*.o + +readelf.o: readelf.c readelf2.c + +ldconfig: ldconfig.o readelf.o + $(CC) -static $(CFLAGS) $^ $(LDFLAGS) -o $@ + +ldd: ldd.o readelf.o + $(CC) -static $(CFLAGS) $^ $(LDFLAGS) -o $@ + +#ifeq ($(DEBUG),true) +#STUBFLAGS = -Wl,-dynamic-linker,../d-link/ld-linux.so +#endif + +#lddstub: lddstub.o +# $(CC) $(CFLAGS) $(LDFLAGS) $(STUBFLAGS) $^ -o $@ + +clean: + rm -f $(ALL) *.o *~ core + diff --git a/ldso/util/ldconfig.c b/ldso/util/ldconfig.c new file mode 100644 index 000000000..2b3d50c8e --- /dev/null +++ b/ldso/util/ldconfig.c @@ -0,0 +1,837 @@ +/* + * ldconfig - update shared library symlinks + * + * usage: ldconfig [-DvqnNX] [-f conf] [-C cache] [-r root] dir ... + * ldconfig -l [-Dv] lib ... + * ldconfig -p + * -D: debug mode, don't update links + * -v: verbose mode, print things as we go + * -q: quiet mode, don't print warnings + * -n: don't process standard directories + * -N: don't update the library cache + * -X: don't update the library links + * -l: library mode, manually link libraries + * -p: print the current library cache + * -f conf: use conf instead of /etc/ld.so.conf + * -C cache: use cache instead of /etc/ld.so.cache + * -r root: first, do a chroot to the indicated directory + * dir ...: directories to process + * lib ...: libraries to link + * + * Copyright 1994-2000 David Engel and Mitch D'Souza + * + * This program may be used for any purpose as long as this + * copyright notice is kept. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <getopt.h> +#include <dirent.h> +#include <unistd.h> +#include <a.out.h> +#include <link.h> +#include <elf.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <errno.h> + +#include "../config.h" +#include "readelf.h" + +#ifdef __GNUC__ +void warn(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void error(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +#endif + +char *___strtok = NULL; + +/* For SunOS */ +#ifndef PATH_MAX +#include <limits.h> +#define PATH_MAX _POSIX_PATH_MAX +#endif + +/* For SunOS */ +#ifndef N_MAGIC +#define N_MAGIC(exec) ((exec).a_magic & 0xffff) +#endif + +#define EXIT_OK 0 +#define EXIT_FATAL 128 + +char *prog = NULL; +int debug = 0; /* debug mode */ +int verbose = 0; /* verbose mode */ +int libmode = 0; /* library mode */ +int nocache = 0; /* don't build cache */ +int nolinks = 0; /* don't update links */ + +char *conffile = LDSO_CONF; /* default conf file */ +char *cachefile = LDSO_CACHE; /* default cache file */ +void cache_print(void); +void cache_dolib(const char *dir, const char *so, int libtype); +void cache_write(void); + +void warn(const char *fmt, ...) +{ + va_list ap; + + if (verbose < 0) + return; + + fflush(stdout); /* don't mix output and error messages */ + fprintf(stderr, "%s: warning: ", prog); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "\n"); + + return; +} + +void error(const char *fmt, ...) +{ + va_list ap; + + fflush(stdout); /* don't mix output and error messages */ + fprintf(stderr, "%s: ", prog); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "\n"); + + exit(EXIT_FATAL); +} + +void *xmalloc(size_t size) +{ + void *ptr; + if ((ptr = malloc(size)) == NULL) + error("out of memory"); + return ptr; +} + +char *xstrdup(const char *str) +{ + char *ptr; + if ((ptr = strdup(str)) == NULL) + error("out of memory"); + return ptr; +} + +/* If shared library, return a malloced copy of the soname and set the + type, else return NULL. + + expected_type should be either LIB_ANY or one of the following:- + LIB_DLL + LIB_ELF + LIB_ELF_LIBC5 + LIB_ELF_LIBC6 + + If the lib is ELF and we can not deduce the type the type will + be set based on expected_type. + + If the expected, actual/deduced types missmatch we display a warning + and use the actual/deduced type. +*/ +char *is_shlib(const char *dir, const char *name, int *type, + int *islink, int expected_type) +{ + char *good = NULL; + char *cp, *cp2; + FILE *file; + struct exec exec; + ElfW(Ehdr) *elf_hdr; + struct stat statbuf; + char buff[4096]; + + /* see if name is of the form libZ.so* */ + if ((strncmp(name, "lib", 3) == 0 || strncmp(name, "ld-", 3) == 0) && \ + name[strlen(name)-1] != '~' && (cp = strstr(name, ".so"))) + { + /* find the start of the Vminor part, if any */ + if (cp[3] == '.' && (cp2 = strchr(cp + 4, '.'))) + cp = cp2; + else + cp = cp + strlen(cp); + + /* construct the full path name */ + sprintf(buff, "%s%s%s", dir, (*dir && strcmp(dir, "/")) ? + "/" : "", name); + + /* first, make sure it's a regular file */ + if (lstat(buff, &statbuf)) + warn("can't lstat %s (%s), skipping", buff, strerror(errno)); + else if (!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) + warn("%s is not a regular file or symlink, skipping", buff); + else + { + /* is it a regular file or a symlink */ + *islink = S_ISLNK(statbuf.st_mode); + + /* then try opening it */ + if (!(file = fopen(buff, "rb"))) + warn("can't open %s (%s), skipping", buff, strerror(errno)); + else + { + /* now make sure it's a shared library */ + if (fread(&exec, sizeof exec, 1, file) < 1) + warn("can't read header from %s, skipping", buff); + else if (N_MAGIC(exec) != ZMAGIC && N_MAGIC(exec) != QMAGIC) + { + elf_hdr = (ElfW(Ehdr) *) &exec; + if (elf_hdr->e_ident[0] != 0x7f || + strncmp(&elf_hdr->e_ident[1], "ELF",3) != 0) + { + /* silently ignore linker scripts */ + if (strncmp((char *)&exec, "/* GNU ld", 9) != 0) + warn("%s is not a shared library, skipping", buff); + } + else + { + /* always call readsoname to update type */ + if(expected_type == LIB_DLL){ + warn("%s is not an a.out library, its ELF!\n", buff); + expected_type=LIB_ANY; + } + *type = LIB_ELF; + good = readsoname(buff, file, expected_type, type, + elf_hdr->e_ident[EI_CLASS]); + if (good == NULL || *islink) + { + if (good != NULL) + free(good); + good = xstrdup(name); + } + else + { + /* if the soname does not match the filename, + issue a warning, but only in debug mode. */ + int len = strlen(good); + if (debug && (strncmp(good, name, len) != 0 || + (name[len] != '\0' && name[len] != '.'))) + warn("%s has inconsistent soname (%s)", + buff, good); + } + } + } + else + { + if (*islink) + good = xstrdup(name); + else + { + good = xmalloc(cp - name + 1); + strncpy(good, name, cp - name); + good[cp - name] = '\0'; + } + if(expected_type != LIB_ANY && expected_type != LIB_DLL) + { + warn("%s is not an ELF library, its an a.out DLL!", buff); + expected_type=LIB_ANY; + } + + *type = LIB_DLL; + } + fclose(file); + } + } + } + + return good; +} + +/* update the symlink to new library */ +void link_shlib(const char *dir, const char *file, const char *so) +{ + int change = 1; + char libname[4096]; + char linkname[4096]; + struct stat libstat; + struct stat linkstat; + + /* construct the full path names */ + sprintf(libname, "%s/%s", dir, file); + sprintf(linkname, "%s/%s", dir, so); + + /* see if a link already exists */ + if (!stat(linkname, &linkstat)) + { + /* now see if it's the one we want */ + if (stat(libname, &libstat)) + warn("can't stat %s (%s)", libname, strerror(errno)); + else if (libstat.st_dev == linkstat.st_dev && + libstat.st_ino == linkstat.st_ino) + change = 0; + } + + /* then update the link, if required */ + if (change > 0 && !nolinks) + { + if (!lstat(linkname, &linkstat)) + { + if (!S_ISLNK(linkstat.st_mode)) + { + warn("%s is not a symlink", linkname); + change = -1; + } + else if (remove(linkname)) + { + warn("can't unlink %s (%s)", linkname, strerror(errno)); + change = -1; + } + } + if (change > 0) + { + if (symlink(file, linkname)) + { + warn("can't link %s to %s (%s)", linkname, file, strerror(errno)); + change = -1; + } + } + } + + /* some people like to know what we're doing */ + if (verbose > 0) + printf("\t%s => %s%s\n", so, file, + change < 0 ? " (SKIPPED)" : + (change > 0 ? " (changed)" : "")); + + return; +} + +/* figure out which library is greater */ +int libcmp(char *p1, char *p2) +{ + while (*p1) + { + if (isdigit(*p1) && isdigit(*p2)) + { + /* must compare this numerically */ + int v1, v2; + v1 = strtoul(p1, &p1, 10); + v2 = strtoul(p2, &p2, 10); + if (v1 != v2) + return v1 - v2; + } + else if (isdigit(*p1) && !isdigit(*p2)) + return 1; + else if (!isdigit(*p1) && isdigit(*p2)) + return -1; + else if (*p1 != *p2) + return *p1 - *p2; + else + p1++, p2++; + } + + return *p1 - *p2; +} + +struct lib +{ + char *so; /* soname of a library */ + char *name; /* name of a library */ + int libtype; /* type of a library */ + int islink; /* is it a symlink */ + struct lib *next; /* next library in list */ +}; + +/* update all shared library links in a directory */ +void scan_dir(const char *name) +{ + DIR *dir; + struct dirent *ent; + char *so; + struct lib *lp, *libs = NULL; + int libtype, islink; + int expected_type = LIB_ANY; + char *t; + + /* Check for an embedded expected type */ + t=strrchr(name, '='); + if( t ) + { + *t++ = '\0'; /* Skip = char */ + if(strcasecmp(t, "libc4") == 0) + { + expected_type = LIB_DLL; + } + else + { + if(strcasecmp(t, "libc5") == 0) + { + expected_type = LIB_ELF_LIBC5; + } + else + { + if(strcasecmp(t, "libc6") == 0) + { + expected_type = LIB_ELF_LIBC6; + } + else + { + warn("Unknown type field '%s' for dir '%s' - ignored\n", t, name); + expected_type = LIB_ANY; + } + } + } + } + + /* let 'em know what's going on */ + if (verbose > 0) + printf("%s:\n", name); + + /* if we can't open it, we can't do anything */ + if ((dir = opendir(name)) == NULL) + { + warn("can't open %s (%s), skipping", name, strerror(errno)); + return; + } + + /* yes, we have to look at every single file */ + while ((ent = readdir(dir)) != NULL) + { + /* if it's not a shared library, don't bother */ + if ((so = is_shlib(name, ent->d_name, &libtype, &islink, expected_type)) == NULL) + continue; + + /* have we already seen one with the same so name? */ + for (lp = libs; lp; lp = lp->next) + { + if (strcmp(so, lp->so) == 0) + { + /* we have, which one do we want to use? */ + if ((!islink && lp->islink) || + (islink == lp->islink && + libcmp(ent->d_name, lp->name) > 0)) + { + /* let's use the new one */ + free(lp->name); + lp->name = xstrdup(ent->d_name); + lp->libtype = libtype; + lp->islink = islink; + } + break; + } + } + + /* congratulations, you're the first one we've seen */ + if (!lp) + { + lp = xmalloc(sizeof *lp); + lp->so = xstrdup(so); + lp->name = xstrdup(ent->d_name); + lp->libtype = libtype; + lp->islink = islink; + lp->next = libs; + libs = lp; + } + + free(so); + } + + /* don't need this any more */ + closedir(dir); + + /* now we have all the latest libs, update the links */ + for (lp = libs; lp; lp = lp->next) + { + if (!lp->islink) + link_shlib(name, lp->name, lp->so); + if (!nocache) + cache_dolib(name, lp->so, lp->libtype); + } + + /* always try to clean up after ourselves */ + while (libs) + { + lp = libs->next; + free(libs->so); + free(libs->name); + free(libs); + libs = lp; + } + + return; +} + +/* return the list of system-specific directories */ +char *get_extpath(void) +{ + char *res = NULL, *cp; + FILE *file; + struct stat stat; + + if ((file = fopen(conffile, "r")) != NULL) + { + fstat(fileno(file), &stat); + res = xmalloc(stat.st_size + 1); + fread(res, 1, stat.st_size, file); + fclose(file); + res[stat.st_size] = '\0'; + + /* convert comments fo spaces */ + for (cp = res; *cp; /*nada*/) { + if (*cp == '#') { + do + *cp++ = ' '; + while (*cp && *cp != '\n'); + } else { + cp++; + } + } + } + + return res; +} + +void usage(void) +{ + fprintf(stderr, + "ldconfig - update shared library symlinks\n" + "\n" + "usage: ldconfig [-DvqnNX] [-f conf] [-C cache] [-r root] dir ...\n" + " ldconfig -l [-Dv] lib ...\n" + " ldconfig -p\n" + "\t-D:\t\tdebug mode, don't update links\n" + "\t-v:\t\tverbose mode, print things as we go\n" + "\t-q:\t\tquiet mode, don't print warnings\n" + "\t-n:\t\tdon't process standard directories\n" + "\t-N:\t\tdon't update the library cache\n" + "\t-X:\t\tdon't update the library links\n" + "\t-l:\t\tlibrary mode, manually link libraries\n" + "\t-p:\t\tprint the current library cache\n" + "\t-f conf :\tuse conf instead of %s\n" + "\t-C cache:\tuse cache instead of %s\n" + "\t-r root :\tfirst, do a chroot to the indicated directory\n" + "\tdir ... :\tdirectories to process\n" + "\tlib ... :\tlibraries to link\n" + "\n" + "Copyright 1994-2000 David Engel and Mitch D'Souza\n", + LDSO_CONF, LDSO_CACHE + ); + exit(EXIT_FATAL); +} + +int main(int argc, char **argv) +{ + int i, c; + int nodefault = 0; + int printcache = 0; + char *cp, *dir, *so; + char *extpath; + int libtype, islink; + char *chroot_dir = NULL; + + prog = argv[0]; + opterr = 0; + + while ((c = getopt(argc, argv, "DvqnNXlpf:C:r:")) != EOF) + switch (c) + { + case 'D': + debug = 1; /* debug mode */ + nocache = 1; + nolinks = 1; + verbose = 1; + break; + case 'v': + verbose = 1; /* verbose mode */ + break; + case 'q': + if (verbose <= 0) + verbose = -1; /* quiet mode */ + break; + case 'n': + nodefault = 1; /* no default dirs */ + nocache = 1; + break; + case 'N': + nocache = 1; /* don't build cache */ + break; + case 'X': + nolinks = 1; /* don't update links */ + break; + case 'l': + libmode = 1; /* library mode */ + break; + case 'p': + printcache = 1; /* print cache */ + break; + case 'f': + conffile = optarg; /* alternate conf file */ + break; + case 'C': + cachefile = optarg; /* alternate cache file */ + break; + case 'r': + chroot_dir = optarg; + break; + default: + usage(); + break; + + /* THE REST OF THESE ARE UNDOCUMENTED AND MAY BE REMOVED + IN FUTURE VERSIONS. */ + } + + if (chroot_dir && *chroot_dir) { + if (chroot(chroot_dir) < 0) + error("couldn't chroot to %s (%s)", chroot_dir, strerror(errno)); + if (chdir("/") < 0) + error("couldn't chdir to / (%s)", strerror(errno)); + } + + /* allow me to introduce myself, hi, my name is ... */ + if (verbose > 0) + printf("%s: version %s\n", argv[0], VERSION); + + if (printcache) + { + /* print the cache -- don't you trust me? */ + cache_print(); + exit(EXIT_OK); + } + else if (libmode) + { + /* so you want to do things manually, eh? */ + + /* ok, if you're so smart, which libraries do we link? */ + for (i = optind; i < argc; i++) + { + /* split into directory and file parts */ + if (!(cp = strrchr(argv[i], '/'))) + { + dir = "."; /* no dir, only a filename */ + cp = argv[i]; + } + else + { + if (cp == argv[i]) + dir = "/"; /* file in root directory */ + else + dir = argv[i]; + *cp++ = '\0'; /* neither of the above */ + } + + /* we'd better do a little bit of checking */ + if ((so = is_shlib(dir, cp, &libtype, &islink, LIB_ANY)) == NULL) + error("%s%s%s is not a shared library", dir, + (*dir && strcmp(dir, "/")) ? "/" : "", cp); + + /* so far, so good, maybe he knows what he's doing */ + link_shlib(dir, cp, so); + } + } + else + { + /* the lazy bum want's us to do all the work for him */ + + /* don't cache dirs on the command line */ + int nocache_save = nocache; + nocache = 1; + + /* OK, which directories should we do? */ + for (i = optind; i < argc; i++) + scan_dir(argv[i]); + + /* restore the desired caching state */ + nocache = nocache_save; + + /* look ma, no defaults */ + if (!nodefault) + { + /* I guess the defaults aren't good enough */ + if ((extpath = get_extpath())) + { + for (cp = strtok(extpath, DIR_SEP); cp; + cp = strtok(NULL, DIR_SEP)) + scan_dir(cp); + free(extpath); + } + +#ifdef UCLIBC_DEVEL + scan_dir(UCLIBC_INSTALL_DIR"/lib"); +#else + /* everybody needs these, don't they? */ + scan_dir("/usr/lib"); + scan_dir("/lib"); +#endif + } + + if (!nocache) + cache_write(); + } + + exit(EXIT_OK); +} + +typedef struct liblist +{ + int flags; + int sooffset; + int liboffset; + char *soname; + char *libname; + struct liblist *next; +} liblist_t; + +static header_t magic = { LDSO_CACHE_MAGIC, LDSO_CACHE_VER, 0 }; +static liblist_t *lib_head = NULL; + +static int liblistcomp(liblist_t *x, liblist_t *y) +{ + int res; + + if ((res = libcmp(x->soname, y->soname)) == 0) + { + res = libcmp(strrchr(x->libname, '/') + 1, + strrchr(y->libname, '/') + 1); + } + + return res; +} + +void cache_dolib(const char *dir, const char *so, int libtype) +{ + char fullpath[PATH_MAX]; + liblist_t *new_lib, *cur_lib; + + magic.nlibs++; + sprintf(fullpath, "%s/%s", dir, so); + new_lib = xmalloc(sizeof (liblist_t)); + new_lib->flags = libtype; + new_lib->soname = xstrdup(so); + new_lib->libname = xstrdup(fullpath); + + if (lib_head == NULL || liblistcomp(new_lib, lib_head) > 0) + { + new_lib->next = lib_head; + lib_head = new_lib; + } + else + { + for (cur_lib = lib_head; cur_lib->next != NULL && + liblistcomp(new_lib, cur_lib->next) <= 0; + cur_lib = cur_lib->next) + /* nothing */; + new_lib->next = cur_lib->next; + cur_lib->next = new_lib; + } +} + +void cache_write(void) +{ + int cachefd; + int stroffset = 0; + char tempfile[4096]; + liblist_t *cur_lib; + + if (!magic.nlibs) + return; + + sprintf(tempfile, "%s~", cachefile); + + if (unlink(tempfile) && errno != ENOENT) + error("can't unlink %s (%s)", tempfile, strerror(errno)); + + if ((cachefd = creat(tempfile, 0644)) < 0) + error("can't create %s (%s)", tempfile, strerror(errno)); + + if (write(cachefd, &magic, sizeof (header_t)) != sizeof (header_t)) + error("can't write %s (%s)", tempfile, strerror(errno)); + + for (cur_lib = lib_head; cur_lib != NULL; cur_lib = cur_lib->next) + { + cur_lib->sooffset = stroffset; + stroffset += strlen(cur_lib->soname) + 1; + cur_lib->liboffset = stroffset; + stroffset += strlen(cur_lib->libname) + 1; + if (write(cachefd, cur_lib, sizeof (libentry_t)) != + sizeof (libentry_t)) + error("can't write %s (%s)", tempfile, strerror(errno)); + } + + for (cur_lib = lib_head; cur_lib != NULL; cur_lib = cur_lib->next) + { + if (write(cachefd, cur_lib->soname, strlen(cur_lib->soname) + 1) + != strlen(cur_lib->soname) + 1) + error("can't write %s (%s)", tempfile, strerror(errno)); + if (write(cachefd, cur_lib->libname, strlen(cur_lib->libname) + 1) + != strlen(cur_lib->libname) + 1) + error("can't write %s (%s)", tempfile, strerror(errno)); + } + + if (close(cachefd)) + error("can't close %s (%s)", tempfile, strerror(errno)); + + if (chmod(tempfile, 0644)) + error("can't chmod %s (%s)", tempfile, strerror(errno)); + + if (rename(tempfile, cachefile)) + error("can't rename %s (%s)", tempfile, strerror(errno)); +} + +void cache_print(void) +{ + caddr_t c; + struct stat st; + int fd = 0; + char *strs; + header_t *header; + libentry_t *libent; + + if (stat(cachefile, &st) || (fd = open(cachefile, O_RDONLY))<0) + error("can't read %s (%s)", cachefile, strerror(errno)); + if ((c = mmap(0,st.st_size, PROT_READ, MAP_SHARED ,fd, 0)) == (caddr_t)-1) + error("can't map %s (%s)", cachefile, strerror(errno)); + close(fd); + + if (memcmp(((header_t *)c)->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN)) + error("%s cache corrupt", cachefile); + + if (memcmp(((header_t *)c)->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN)) + error("wrong cache version - expected %s", LDSO_CACHE_VER); + + header = (header_t *)c; + libent = (libentry_t *)(c + sizeof (header_t)); + strs = (char *)&libent[header->nlibs]; + + printf("%d libs found in cache `%s' (version %s)\n", + header->nlibs, cachefile, LDSO_CACHE_VER); + + for (fd = 0; fd < header->nlibs; fd++) + { + printf("\t%s ", strs + libent[fd].sooffset); + switch (libent[fd].flags & ~LIB_ELF64) + { + case LIB_DLL: + printf("(libc4)"); + break; + case LIB_ELF: + printf("(ELF%s)", libent[fd].flags & LIB_ELF64 ? "/64" : ""); + break; + case LIB_ELF_LIBC5: + case LIB_ELF_LIBC6: + printf("(libc%d%s)", (libent[fd].flags & ~LIB_ELF64) + 3, + libent[fd].flags & LIB_ELF64 ? "/64" : ""); + break; + default: + printf("(unknown)"); + break; + } + printf(" => %s\n", strs + libent[fd].liboffset); + } + + munmap (c,st.st_size); +} + diff --git a/ldso/util/ldd.c b/ldso/util/ldd.c new file mode 100644 index 000000000..62c479ba3 --- /dev/null +++ b/ldso/util/ldd.c @@ -0,0 +1,310 @@ +/* + * ldd - print shared library dependencies + * + * usage: ldd [-vVdr] prog ... + * -v: print ldd version + * -V: print ld.so version + * -d: Perform relocations and report any missing functions. (ELF only). + * -r: Perform relocations for both data objects and functions, and + * report any missing objects (ELF only). + * prog ...: programs to check + * + * Copyright 1993-2000, David Engel + * + * This program may be used for any purpose as long as this + * copyright notice is kept. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <a.out.h> +#include <errno.h> +#include <sys/wait.h> +#include <linux/elf.h> +#include "../config.h" +#include "readelf.h" + +#ifdef __GNUC__ +void warn(char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void error(char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +#endif + +char *prog = NULL; + +void warn(char *fmt, ...) +{ + va_list ap; + + fflush(stdout); /* don't mix output and error messages */ + fprintf(stderr, "%s: warning: ", prog); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "\n"); + + return; +} + +void error(char *fmt, ...) +{ + va_list ap; + + fflush(stdout); /* don't mix output and error messages */ + fprintf(stderr, "%s: ", prog); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "\n"); + + exit(1); +} + +void *xmalloc(size_t size) +{ + void *ptr; + if ((ptr = malloc(size)) == NULL) + error("out of memory"); + return ptr; +} + +char *xstrdup(char *str) +{ + char *ptr; + if ((ptr = strdup(str)) == NULL) + error("out of memory"); + return ptr; +} + +/* see if prog is a binary file */ +int is_bin(char *argv0, char *prog) +{ + int res = 0; + FILE *file; + struct exec exec; + char *cp; + int libtype; + + /* must be able to open it for reading */ + if ((file = fopen(prog, "rb")) == NULL) + fprintf(stderr, "%s: can't open %s (%s)\n", argv0, prog, + strerror(errno)); + else + { + /* then see if it's Z, Q or OMAGIC */ + if (fread(&exec, sizeof exec, 1, file) < 1) + fprintf(stderr, "%s: can't read header from %s\n", argv0, prog); + else if (N_MAGIC(exec) != ZMAGIC && N_MAGIC(exec) != QMAGIC && + N_MAGIC(exec) != OMAGIC) + { + struct elfhdr elf_hdr; + + rewind(file); + fread(&elf_hdr, sizeof elf_hdr, 1, file); + if (elf_hdr.e_ident[0] != 0x7f || + strncmp(elf_hdr.e_ident+1, "ELF", 3) != 0) + fprintf(stderr, "%s: %s is not a.out or ELF\n", argv0, prog); + else + { + struct elf_phdr phdr; + int i; + + /* Check its an exectuable, library or other */ + switch (elf_hdr.e_type) + { + case ET_EXEC: + res = 3; + /* then determine if it is dynamic ELF */ + for (i=0; i<elf_hdr.e_phnum; i++) + { + fread(&phdr, sizeof phdr, 1, file); + if (phdr.p_type == PT_DYNAMIC) + { + res = 2; + break; + } + } + break; + case ET_DYN: + if ((cp = readsoname(prog, file, LIB_ANY, &libtype, + elf_hdr.e_ident[EI_CLASS])) != NULL) + free(cp); + if (libtype == LIB_ELF_LIBC5) + res = 5; + else + res = 4; + break; + default: + res = 0; + break; + } + } + } + else + res = 1; /* looks good */ + + fclose(file); + } + return res; +} + +int main(int argc, char **argv, char **envp) +{ + int i; + int vprinted = 0; + int resolve = 0; + int bind = 0; + int bintype; + char *ld_preload; + int status = 0; + + /* this must be volatile to work with -O, GCC bug? */ + volatile loadptr loader = (loadptr)LDSO_ADDR; + + prog = argv[0]; + + while ((i = getopt(argc, argv, "drvV")) != EOF) + switch (i) + { + case 'v': + /* print our version number */ + printf("%s: version %s\n", argv[0], VERSION); + vprinted = 1; + break; + case 'd': + bind = 1; + break; + case 'r': + resolve = 1; + break; + case 'V': + /* print the version number of ld.so */ + if (uselib(LDSO_IMAGE)) + { + fprintf(stderr, "%s: can't load dynamic linker %s (%s)\n", + argv[0], LDSO_IMAGE, strerror(errno)); + exit(1); + } + loader(FUNC_VERS, NULL); + vprinted = 1; + break; + } + + /* must specify programs if -v or -V not used */ + if (optind >= argc && !vprinted) + { + printf("usage: %s [-vVdr] prog ...\n", argv[0]); + exit(0); + } + + /* setup the environment for ELF binaries */ + putenv("LD_TRACE_LOADED_OBJECTS=1"); + if (resolve || bind) + putenv("LD_BIND_NOW=1"); + if (resolve) + putenv("LD_WARN=1"); + ld_preload = getenv("LD_PRELOAD"); + + /* print the dependencies for each program */ + for (i = optind; i < argc; i++) + { + pid_t pid; + char buff[1024]; + + /* make sure it's a binary file */ + if (!(bintype = is_bin(argv[0], argv[i]))) + { + status = 1; + continue; + } + + /* print the program name if doing more than one */ + if (optind < argc-1) + { + printf("%s:\n", argv[i]); + fflush(stdout); + } + + /* no need to fork/exec for static ELF program */ + if (bintype == 3) + { + printf("\tstatically linked (ELF)\n"); + continue; + } + + /* now fork and exec prog with argc = 0 */ + if ((pid = fork()) < 0) + { + fprintf(stderr, "%s: can't fork (%s)\n", argv[0], strerror(errno)); + exit(1); + } + else if (pid == 0) + { + switch (bintype) + { + case 1: /* a.out */ + /* save the name in the enviroment, ld.so may need it */ + snprintf(buff, sizeof buff, "%s=%s", LDD_ARGV0, argv[i]); + putenv(buff); + execl(argv[i], NULL); + break; + case 2: /* ELF program */ + execl(argv[i], argv[i], NULL); + break; + case 4: /* ELF libc6 library */ + /* try to use /lib/ld-linux.so.2 first */ +#if !defined(__mc68000__) + execl("/lib/ld-linux.so.2", "/lib/ld-linux.so.2", + "--list", argv[i], NULL); +#else + execl("/lib/ld.so.1", "/lib/ld.so.1", + "--list", argv[i], NULL); +#endif + /* fall through */ + case 5: /* ELF libc5 library */ + /* if that fails, add library to LD_PRELOAD and + then execute lddstub */ + if (ld_preload && *ld_preload) + snprintf(buff, sizeof buff, "LD_PRELOAD=%s:%s%s", + ld_preload, *argv[i] == '/' ? "" : "./", argv[i]); + else + snprintf(buff, sizeof buff, "LD_PRELOAD=%s%s", + *argv[i] == '/' ? "" : "./", argv[i]); + putenv(buff); + execl(LDDSTUB, argv[i], NULL); + break; + default: + fprintf(stderr, "%s: internal error, bintype = %d\n", + argv[0], bintype); + exit(1); + } + fprintf(stderr, "%s: can't execute %s (%s)\n", argv[0], argv[i], + strerror(errno)); + exit(1); + } + else + { + /* then wait for it to complete */ + int status; + if (waitpid(pid, &status, 0) != pid) + { + fprintf(stderr, "%s: error waiting for %s (%s)\n", argv[0], + argv[i], strerror(errno)); + } + else if (WIFSIGNALED(status)) + { + fprintf(stderr, "%s: %s exited with signal %d\n", argv[0], + argv[i], WTERMSIG(status)); + } + } + } + + exit(status); +} diff --git a/ldso/util/lddstub.S b/ldso/util/lddstub.S new file mode 100644 index 000000000..33a67ee8d --- /dev/null +++ b/ldso/util/lddstub.S @@ -0,0 +1,42 @@ + .text +.globl main +.globl exit +main: +exit: +#if defined(__i386__) + movl $1,%eax + movl $0,%ebx + int $0x80 +#elif defined(__mc68000__) + movel #1,%d0 + clrl %d1 + trap #0 +#elif defined(__sparc__) + mov 1,%g1 + mov 0,%o0 + t 0x10 +#else +#error Only know how to support i386, m68k and sparc architectures +#endif + +.globl atexit +.globl __libc_init +.globl __setfpucw +atexit: +__libc_init: +__setfpucw: +#if defined(__i386__) + ret +#elif defined(__mc68000__) + rts +#elif defined(__sparc__) + ret + nop +#else +#error Only know how to support i386, m68k and sparc architectures +#endif + + .data +.globl __fpu_control +__fpu_control: + .long 0 diff --git a/ldso/util/readelf.c b/ldso/util/readelf.c new file mode 100644 index 000000000..ba93d2d31 --- /dev/null +++ b/ldso/util/readelf.c @@ -0,0 +1,61 @@ +/* adapted from Eric Youngdale's readelf program */ + +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <link.h> +#include <elf.h> +#include <unistd.h> +#include "../config.h" +#include "readelf.h" +#include <sys/types.h> + +void warn(char *fmt, ...); +char *xstrdup(char *); + +struct needed_tab +{ + char *soname; + int type; +}; + +struct needed_tab needed_tab[] = { + { "libc.so.5", LIB_ELF_LIBC5 }, + { "libm.so.5", LIB_ELF_LIBC5 }, + { "libdl.so.1", LIB_ELF_LIBC5 }, + { "libc.so.6", LIB_ELF_LIBC6 }, + { "libm.so.6", LIB_ELF_LIBC6 }, + { "libdl.so.2", LIB_ELF_LIBC6 }, + { NULL, LIB_ELF } +}; + +char *readsoname(char *name, FILE *infile, int expected_type, + int *type, int elfclass) +{ + char *res; + + if (elfclass == ELFCLASS32) + res = readsoname32(name, infile, expected_type, type); + else + { + res = readsoname64(name, infile, expected_type, type); +#if 0 + *type |= LIB_ELF64; +#endif + } + + return res; +} + +#undef __ELF_NATIVE_CLASS +#undef readsonameXX +#define readsonameXX readsoname32 +#define __ELF_NATIVE_CLASS 32 +#include "readelf2.c" + +#undef __ELF_NATIVE_CLASS +#undef readsonameXX +#define readsonameXX readsoname64 +#define __ELF_NATIVE_CLASS 64 +#include "readelf2.c" diff --git a/ldso/util/readelf.h b/ldso/util/readelf.h new file mode 100644 index 000000000..78d2216e0 --- /dev/null +++ b/ldso/util/readelf.h @@ -0,0 +1,4 @@ +char *readsoname(char *name, FILE *file, int expected_type, + int *type, int elfclass); +char *readsoname32(char *name, FILE *file, int expected_type, int *type); +char *readsoname64(char *name, FILE *file, int expected_type, int *type); diff --git a/ldso/util/readelf2.c b/ldso/util/readelf2.c new file mode 100644 index 000000000..1bf47b7c6 --- /dev/null +++ b/ldso/util/readelf2.c @@ -0,0 +1,115 @@ +char *readsonameXX(char *name, FILE *infile, int expected_type, int *type) +{ + ElfW(Ehdr) *epnt; + ElfW(Phdr) *ppnt; + int i, j; + char *header; + ElfW(Word) dynamic_addr = 0; + ElfW(Word) dynamic_size = 0; + unsigned long page_size = getpagesize(); + ElfW(Word) strtab_val = 0; + ElfW(Word) needed_val; + ElfW(Sword) loadaddr = -1; + ElfW(Dyn) *dpnt; + struct stat st; + char *needed; + char *soname = NULL; + int multi_libcs = 0; + + if(expected_type == LIB_DLL) + { + warn("%s does not match type specified for directory!", name); + expected_type = LIB_ANY; + } + + *type = LIB_ELF; + + if (fstat(fileno(infile), &st)) + return NULL; + header = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fileno(infile), 0); + if (header == (caddr_t)-1) + return NULL; + + epnt = (ElfW(Ehdr) *)header; + if ((char *)(epnt+1) > (char *)(header + st.st_size)) + goto skip; + + ppnt = (ElfW(Phdr) *)&header[epnt->e_phoff]; + if ((char *)ppnt < (char *)header || + (char *)(ppnt+epnt->e_phnum) > (char *)(header + st.st_size)) + goto skip; + + for(i = 0; i < epnt->e_phnum; i++) + { + if (loadaddr == -1 && ppnt->p_type == PT_LOAD) + loadaddr = (ppnt->p_vaddr & ~(page_size-1)) - + (ppnt->p_offset & ~(page_size-1)); + if(ppnt->p_type == 2) + { + dynamic_addr = ppnt->p_offset; + dynamic_size = ppnt->p_filesz; + }; + ppnt++; + }; + + dpnt = (ElfW(Dyn) *) &header[dynamic_addr]; + dynamic_size = dynamic_size / sizeof(ElfW(Dyn)); + if ((char *)dpnt < (char *)header || + (char *)(dpnt+dynamic_size) > (char *)(header + st.st_size)) + goto skip; + + while (dpnt->d_tag != DT_NULL) + { + if (dpnt->d_tag == DT_STRTAB) + strtab_val = dpnt->d_un.d_val; + dpnt++; + }; + + if (!strtab_val) + goto skip; + + dpnt = (ElfW(Dyn) *) &header[dynamic_addr]; + while (dpnt->d_tag != DT_NULL) + { + if (dpnt->d_tag == DT_SONAME || dpnt->d_tag == DT_NEEDED) + { + needed_val = dpnt->d_un.d_val; + if (needed_val + strtab_val - loadaddr >= 0 || + needed_val + strtab_val - loadaddr < st.st_size) + { + needed = (char *) (header - loadaddr + strtab_val + needed_val); + + if (dpnt->d_tag == DT_SONAME) + soname = xstrdup(needed); + + for (j = 0; needed_tab[j].soname != NULL; j++) + { + if (strcmp(needed, needed_tab[j].soname) == 0) + { + if (*type != LIB_ELF && *type != needed_tab[j].type) + multi_libcs = 1; + *type = needed_tab[j].type; + } + } + } + } + dpnt++; + }; + + if (multi_libcs) + warn("%s appears to be for multiple libc's", name); + + /* If we could not deduce the libc type, and we know what to expect, set the type */ + if(*type == LIB_ELF && expected_type != LIB_ANY) *type = expected_type; + + if(expected_type != LIB_ANY && expected_type != LIB_ELF && + expected_type != *type) + { + warn("%s does not match type specified for directory!", name); + } + + skip: + munmap(header, st.st_size); + + return soname; +} |