summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>1995-05-02 06:35:55 +0000
committerRoland McGrath <roland@gnu.org>1995-05-02 06:35:55 +0000
commitd66e34cd423425c348bcc83df127dd19711b0b9a (patch)
tree97058e776d3b4fe06ef54bc4951596991652183f
parent08162fa888913a5153d2fcdc4e1a816b57b1fe56 (diff)
downloadglibc-d66e34cd423425c348bcc83df127dd19711b0b9a.tar.gz
* sysdeps/mach/i386/sysdep.h (SNARF_ARGS, CALL_WITH_SP): Rewritten.
* sysdeps/i386/dl-machine.h: New file. * sysdeps/stub/dl-machine.h: New file. * sysdeps/i386/dl-runtime.c: New file. * sysdeps/stub/dl-runtime.c: New file. * sysdeps/i386/elf/start.S: New file. * sysdeps/generic/dl-sysdep.c: New file. * sysdeps/mach/hurd/dl-sysdep.c: New file.
-rw-r--r--ChangeLog9
-rw-r--r--elf/Makefile32
-rw-r--r--elf/dl-error.c43
-rw-r--r--elf/dl-fini.c30
-rw-r--r--elf/dl-init.c86
-rw-r--r--elf/dl-load.c377
-rw-r--r--elf/dl-lookup.c129
-rw-r--r--elf/dl-object.c63
-rw-r--r--elf/dl-reloc.c115
-rw-r--r--elf/dlclose.c97
-rw-r--r--elf/dlerror.c64
-rw-r--r--elf/dlopen.c62
-rw-r--r--elf/dlsym.c46
-rw-r--r--elf/dynamic-link.h119
-rw-r--r--elf/link.h206
-rw-r--r--elf/rtld.c267
-rw-r--r--sysdeps/generic/dl-sysdep.c98
-rw-r--r--sysdeps/i386/dl-machine.h169
-rw-r--r--sysdeps/i386/dl-runtime.c92
-rw-r--r--sysdeps/i386/elf/start.S86
-rw-r--r--sysdeps/mach/i386/sysdep.h22
-rw-r--r--sysdeps/stub/dl-machine.h115
-rw-r--r--sysdeps/stub/dl-runtime.c64
23 files changed, 2379 insertions, 12 deletions
diff --git a/ChangeLog b/ChangeLog
index 2efcc824a9..f417e2878d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,7 @@
Tue May 2 01:52:58 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+ * sysdeps/mach/i386/sysdep.h (SNARF_ARGS, CALL_WITH_SP): Rewritten.
+
Implemented runtime dynamic linker to support ELF shared libraries.
* elf/Makefile: Added rules to make ld.so and libdl.
* elf/dl-error.c: New file.
@@ -16,6 +18,13 @@ Tue May 2 01:52:58 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
* elf/dynamic-link.h: New file.
* elf/link.h: New file.
* elf/rtld.c: New file.
+ * sysdeps/i386/dl-machine.h: New file.
+ * sysdeps/stub/dl-machine.h: New file.
+ * sysdeps/i386/dl-runtime.c: New file.
+ * sysdeps/stub/dl-runtime.c: New file.
+ * sysdeps/i386/elf/start.S: New file.
+ * sysdeps/generic/dl-sysdep.c: New file.
+ * sysdeps/mach/hurd/dl-sysdep.c: New file.
Mon May 1 18:48:30 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
diff --git a/elf/Makefile b/elf/Makefile
index 7dfb9ac37d..f6e94faf56 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -1,3 +1,5 @@
+# Makefile for elf subdirectory of GNU C Library.
+
# Copyright (C) 1995 Free Software Foundation, Inc.
# This file is part of the GNU C Library.
@@ -16,8 +18,34 @@
# not, write to the Free Software Foundation, Inc., 675 Mass Ave,
# Cambridge, MA 02139, USA.
-subdir := elf
+subdir := elf
+
+headers := elf.h libelf.h link.h dlfcn.h
+routines := init-first
+
+extra-libs = libelf libdl
+libelf-routines := elf_hash
+libdl-routines := dlopen dlclose dlsym dlerror
+libdl-inhibit-o = $(filter-out .so,$(object-suffixes)) # Build only shared.
+LDLIBS-dl.so := -lc -lld
+
+rtld-routines := rtld $(addprefix dl-,load lookup object reloc \
+ runtime sysdep error init fini)
+distribute = $(rtld-routines:=.c) dynamic-link.h
-headers := elf.h # libelf.h
+include ../Makeconfig
+
+ifeq (yes,$(build-shared))
+extra-objs = $(rtld-routines:=.so)
+install-lib = ld.so
+endif
include ../Rules
+
+$(objpfx)ld.so: $(rtld-routines:%=$(objpfx)%.so) \
+ $(patsubst %,$(common-objpfx)lib%_pic.a,\
+ elf c $(LDLIBS-c.so:-l%=%))
+ $(LINK.o) -nostdlib -shared -o $@ \
+ '-Wl,-(' $^ -lgcc '-Wl,-)'
+
+$(objpfx)libdl.so: $(common-objpfx)libc.so $(objpfx)ld.so
diff --git a/elf/dl-error.c b/elf/dl-error.c
new file mode 100644
index 0000000000..5b5a616d91
--- /dev/null
+++ b/elf/dl-error.c
@@ -0,0 +1,43 @@
+/* Error handling for runtime dynamic linker.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <stddef.h>
+#include <link.h>
+#include <setjmp.h>
+
+static jmp_buf catch_env;
+static const char *signalled_errstring;
+
+void
+_dl_signal_error (int errcode, const char *errstring)
+{
+ signalled_errstring = errstring ?: "DYNAMIC LINKER BUG!!!";
+ longjmp (catch_env, errcode ?: -1);
+}
+
+int
+_dl_catch_error (const char **errstring, void (*operate) (void))
+{
+ int errcode;
+
+ signalled_errstring = NULL;
+ errcode = setjmp (catch_env);
+ *errstring = signalled_errstring;
+ return *errstring ? errcode : 0;
+}
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
new file mode 100644
index 0000000000..cbc05252d2
--- /dev/null
+++ b/elf/dl-fini.c
@@ -0,0 +1,30 @@
+/* Call the termination functions of loaded shared objects.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <link.h>
+
+void
+_dl_fini (void)
+{
+ struct link_map *l;
+
+ for (l = _dl_loaded; l; l = l->l_next)
+ if (l->l_init_called && l->l_info[DT_FINI])
+ (*(void (*) (void)) l->l_info[DT_FINI]->d_un.d_ptr) ();
+}
diff --git a/elf/dl-init.c b/elf/dl-init.c
new file mode 100644
index 0000000000..e3bfc2ccea
--- /dev/null
+++ b/elf/dl-init.c
@@ -0,0 +1,86 @@
+/* Return the next shared object initializer function not yet run.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <stddef.h>
+#include <link.h>
+
+
+Elf32_Addr
+_dl_init_next (void)
+{
+ struct link_map *l;
+ Elf32_Addr init;
+
+ Elf32_Addr next_init (struct link_map *l)
+ {
+ if (l->l_init_called)
+ /* This object is all done. */
+ return 0;
+ if (l->l_init_running)
+ {
+ /* This object's initializer was just running.
+ Now mark it as having run, so this object
+ will be skipped in the future. */
+ l->l_init_called = 1;
+ l->l_init_running = 0;
+ return 0;
+ }
+
+ if (l->l_info[DT_NEEDED])
+ {
+ /* Find each dependency in order, and see if it
+ needs to run an initializer. */
+ const Elf32_Dyn *d;
+ for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
+ if (d->d_tag == DT_NEEDED)
+ {
+ struct link_map *needed = _dl_map_object
+ (l, (const char *) (l->l_addr + d->d_un.d_ptr), NULL);
+ Elf32_Addr init;
+ --needed->l_opencount;
+ init = next_init (l); /* Recurse on this dependency. */
+ if (init != 0)
+ return init;
+ }
+ }
+
+ if (l->l_info[DT_INIT])
+ {
+ /* Run this object's initializer. */
+ l->l_init_running = 1;
+ return l->l_addr + l->l_info[DT_INIT]->d_un.d_ptr;
+ }
+
+ /* No initializer for this object.
+ Mark it so we will skip it in the future. */
+ l->l_init_called = 1;
+ return 0;
+ }
+
+ /* Look for the first initializer not yet called. */
+ l = _dl_loaded;
+ do
+ {
+ init = next_init (l);
+ l = l->l_next;
+ }
+ while (init == 0 && l);
+
+ return init;
+}
diff --git a/elf/dl-load.c b/elf/dl-load.c
new file mode 100644
index 0000000000..0de7404a80
--- /dev/null
+++ b/elf/dl-load.c
@@ -0,0 +1,377 @@
+/* _dl_map_object -- Map in a shared object's segments from the file.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <link.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "dynamic-link.h"
+
+
+#include <endian.h>
+#if BYTE_ORDER == BIG_ENDIAN
+#define byteorder ELFDATA2MSB
+#define byteorder_name "big-endian"
+#elif BYTE_ORDER == LITTLE_ENDIAN
+#define byteorder ELFDATA2LSB
+#define byteorder_name "little-endian"
+#else
+#error "Unknown BYTE_ORDER " BYTE_ORDER
+#define byteorder ELFDATANONE
+#endif
+
+#define STRING(x) #x
+
+int _dl_zerofd = -1;
+
+
+/* Try to open NAME in one of the directories in DIRPATH.
+ Return the fd, or -1. If successful, fill in *REALNAME
+ with the malloc'd full directory name. */
+
+static int
+open_path (const char *name, size_t namelen,
+ const char *dirpath,
+ char **realname)
+{
+ char buf[strlen (dirpath) + 1 + namelen];
+ const char *p;
+ int fd;
+
+ p = dirpath;
+ if (p == NULL || *p == '\0')
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ do
+ {
+ dirpath = p;
+ p = strpbrk (dirpath, ":;");
+ if (p == NULL)
+ p = strchr (dirpath, '\0');
+
+ if (p == dirpath)
+ /* Two adjacent colons, or a colon at the beginning or the end of
+ the path means to search the current directory. */
+ (void) memcpy (buf, name, namelen);
+ else
+ {
+ /* Construct the pathname to try. */
+ (void) memcpy (buf, dirpath, p - dirpath);
+ buf[p - dirpath] = '/';
+ (void) memcpy (&buf[(p - dirpath) + 1], name, namelen);
+ }
+
+ fd = open (buf, O_RDONLY);
+ if (fd != -1)
+ {
+ *realname = strdup (buf);
+ return fd;
+ }
+ if (errno != ENOENT && errno != EACCES)
+ /* The file exists and is readable, but something went wrong. */
+ return -1;
+ }
+ while (*p++ != '\0');
+
+ return -1;
+}
+
+
+/* Map in the shared object file NAME. */
+
+struct link_map *
+_dl_map_object (struct link_map *loader, const char *name,
+ Elf32_Addr *entry_point)
+{
+ int fd;
+ char *realname;
+ const size_t pagesize = getpagesize ();
+ void *file_mapping = NULL;
+ size_t mapping_size = 0;
+ /* Make sure LOCATION is mapped in. */
+ void *map (off_t location, size_t size)
+ {
+ if ((off_t) mapping_size <= location + (off_t) size)
+ {
+ void *result;
+ if (file_mapping)
+ munmap (file_mapping, mapping_size);
+ mapping_size = (location + size + 1 + pagesize - 1);
+ mapping_size &= ~(pagesize - 1);
+ result = mmap (file_mapping, mapping_size, PROT_READ,
+ MAP_COPY|MAP_FILE, fd, 0);
+ if (result == (void *) -1)
+ return NULL;
+ file_mapping = result;
+ }
+ return file_mapping + location;
+ }
+
+ const Elf32_Ehdr *header;
+ struct link_map *l;
+
+ /* Look for this name among those already loaded. */
+ for (l = _dl_loaded; l; l = l->l_next)
+ if (! strcmp (name, l->l_libname))
+ {
+ /* The object is already loaded.
+ Just bump its reference count and return it. */
+ ++l->l_opencount;
+ return l;
+ }
+
+ if (strchr (name, '/') == NULL)
+ {
+ /* Search for NAME in several places. */
+
+ size_t namelen = strlen (name) + 1;
+
+ void trypath (const char *dirpath)
+ {
+ fd = open_path (name, namelen, dirpath, &realname);
+ }
+
+ if (loader && loader->l_info[DT_RPATH])
+ trypath ((const char *) (loader->l_addr +
+ loader->l_info[DT_RPATH]->d_un.d_ptr));
+ if (fd == -1 && ! _dl_secure)
+ trypath (getenv ("LD_LIBRARY_PATH"));
+ if (fd == -1)
+ trypath ("/lib:/usr/lib");
+ }
+ else
+ {
+ fd = open (name, O_RDONLY);
+ if (fd != -1)
+ realname = strdup (name);
+ }
+
+ if (fd == -1)
+ return NULL;
+
+ /* Look again to see if the real name matched another already loaded. */
+ for (l = _dl_loaded; l; l = l->l_next)
+ if (! strcmp (realname, l->l_name))
+ {
+ /* The object is already loaded.
+ Just bump its reference count and return it. */
+ close (fd);
+ ++l->l_opencount;
+ return l;
+ }
+
+
+ /* Map in the first page to read the header. */
+ header = map (0, sizeof *header);
+ if (! header)
+ {
+ lose:
+ (void) close (fd);
+ if (file_mapping)
+ munmap (file_mapping, mapping_size);
+ return NULL;
+ }
+
+#undef LOSE
+#define LOSE(s) _dl_signal_error (0, s)
+ /* Check the header for basic validity. */
+ if (*(Elf32_Word *) &header->e_ident != ((ELFMAG0 << (EI_MAG0 * 8)) |
+ (ELFMAG1 << (EI_MAG1 * 8)) |
+ (ELFMAG2 << (EI_MAG2 * 8)) |
+ (ELFMAG3 << (EI_MAG3 * 8))))
+ LOSE ("invalid ELF header");
+ if (header->e_ident[EI_CLASS] != ELFCLASS32)
+ LOSE ("ELF file class not 32-bit");
+ if (header->e_ident[EI_DATA] != byteorder)
+ LOSE ("ELF file data encoding not " byteorder_name);
+ if (header->e_ident[EI_VERSION] != EV_CURRENT)
+ LOSE ("ELF file version ident not " STRING(EV_CURRENT));
+ if (header->e_version != EV_CURRENT)
+ LOSE ("ELF file version not " STRING(EV_CURRENT));
+ if (! elf_machine_matches_host (header->e_machine))
+ LOSE ("ELF file machine architecture not " ELF_MACHINE_NAME);
+ if (header->e_phentsize != sizeof (Elf32_Phdr))
+ LOSE ("ELF file's phentsize not the expected size");
+
+ /* Enter the new object in the list of loaded objects. */
+ l = _dl_new_object (realname, name, lt_loaded);
+ l->l_opencount = 1;
+
+ if (_dl_zerofd == -1)
+ {
+ _dl_zerofd = _dl_sysdep_open_zero_fill ();
+ if (_dl_zerofd == -1)
+ _dl_signal_error (errno, "cannot open zero fill device");
+ }
+
+ {
+ /* Copy the program header table into stack space so we can then unmap
+ the headers. */
+ Elf32_Phdr phdr[header->e_phnum];
+ const Elf32_Phdr *ph;
+ int anywhere;
+
+ ph = map (header->e_phoff, header->e_phnum * sizeof (Elf32_Phdr));
+ if (! ph)
+ goto lose;
+ memcpy (phdr, ph, sizeof phdr);
+ l->l_phnum = header->e_phnum;
+
+ anywhere = header->e_type == ET_DYN || header->e_type == ET_REL;
+
+ if (entry_point)
+ *entry_point = header->e_entry;
+
+ /* We are done reading the file's headers now. Unmap them. */
+ munmap (file_mapping, mapping_size);
+
+ /* Scan the program header table, processing its load commands. */
+ l->l_addr = 0;
+ l->l_ld = 0;
+ for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph)
+ switch (ph->p_type)
+ {
+ /* These entries tell us where to find things once the file's
+ segments are mapped in. We record the addresses it says
+ verbatim, and later correct for the run-time load address. */
+ case PT_DYNAMIC:
+ l->l_ld = (void *) ph->p_vaddr;
+ break;
+ case PT_PHDR:
+ l->l_phdr = (void *) ph->p_vaddr;
+ break;
+
+ case PT_LOAD:
+ /* A load command tells us to map in part of the file. */
+ if (ph->p_align % pagesize != 0)
+ LOSE ("ELF load command alignment not page-aligned");
+ if ((ph->p_vaddr - ph->p_offset) % ph->p_align)
+ LOSE ("ELF load command address/offset not properly aligned");
+ {
+ Elf32_Addr mapstart = ph->p_vaddr & ~(ph->p_align - 1);
+ Elf32_Addr mapend = ((ph->p_vaddr + ph->p_filesz + ph->p_align - 1)
+ & ~(ph->p_align - 1));
+ off_t mapoff = ph->p_offset & ~(ph->p_align - 1);
+ caddr_t mapat;
+ int prot = 0;
+ if (ph->p_flags & PF_R)
+ prot |= PROT_READ;
+ if (ph->p_flags & PF_W)
+ prot |= PROT_WRITE;
+ if (ph->p_flags & PF_X)
+ prot |= PROT_EXEC;
+
+ if (anywhere)
+ {
+ /* XXX this loses if the first segment mmap call puts
+ it someplace where the later segments cannot fit. */
+ mapat = mmap ((caddr_t) l->l_addr + mapstart, mapend - mapstart,
+ prot, MAP_COPY|MAP_FILE|MAP_INHERIT |
+ /* Let the system choose any convenient
+ location if this is the first segment.
+ Following segments must be contiguous in
+ virtual space with the first. */
+ (l->l_addr == 0 ? 0 : MAP_FIXED),
+ fd, mapoff);
+ if (l->l_addr == 0)
+ /* This was the first segment mapped, so MAPAT is
+ the address the system chose for us. Record it. */
+ l->l_addr = (Elf32_Addr) mapat - mapstart;
+ }
+ else
+ {
+ mapat = mmap ((caddr_t) mapstart, mapend - mapstart,
+ prot, MAP_COPY|MAP_FILE|MAP_INHERIT|MAP_FIXED,
+ fd, mapoff);
+ /* This file refers to absolute addresses. So consider its
+ "load base" to be zero, since that is what we add to the
+ file's addresses to find them in our memory. */
+ l->l_addr = 0;
+ }
+ if (mapat == (caddr_t) -1)
+ _dl_signal_error (errno,
+ "failed to map region from shared object");
+
+ if (ph->p_memsz > ph->p_filesz)
+ {
+ /* Extra zero pages should appear at the end of this segment,
+ after the data mapped from the file. Adjust MAPEND to map
+ only the data from the file. We will later allocate zero
+ pages following the data mapping. */
+ caddr_t zero = mapat - mapstart + ph->p_filesz;
+ caddr_t zeroend = mapat - mapstart + ph->p_memsz;
+ caddr_t zeropage
+ = (caddr_t) ((Elf32_Addr) (zero + pagesize - 1)
+ & ~(pagesize - 1));
+
+ if (zeroend < zeropage)
+ /* All the extra data is in the last page of the segment.
+ We can just zero it. */
+ zeropage = zeroend;
+ if (zeropage > zero)
+ {
+ /* Zero the final part of the last page of the segment. */
+ if ((prot & PROT_WRITE) == 0)
+ {
+ /* Dag nab it. */
+ if (mprotect ((caddr_t) ((Elf32_Addr) zero
+ & ~(pagesize - 1)),
+ pagesize,
+ prot|PROT_WRITE) < 0)
+ _dl_signal_error (errno,
+ "cannot change protections");
+ }
+ memset (zero, 0, zeroend - zero);
+ if ((prot & PROT_WRITE) == 0)
+ mprotect ((caddr_t) ((Elf32_Addr) zero
+ & ~(pagesize - 1)),
+ pagesize, prot);
+ }
+
+ if (zeroend > zeropage)
+ /* Map the remaining zero pages in from the zero fill FD. */
+ mapat = mmap (zeropage, zeroend - zeropage,
+ prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
+ _dl_zerofd, 0);
+ }
+ }
+ }
+
+ if (l->l_ld == 0)
+ LOSE ("object file has no dynamic section");
+ (Elf32_Addr) l->l_ld += l->l_addr;
+
+ if (l->l_phdr == 0)
+ l->l_phdr = (void *) ((const Elf32_Ehdr *) l->l_addr)->e_phoff;
+ (Elf32_Addr) l->l_phdr += l->l_addr;
+ }
+
+ elf_get_dynamic_info (l->l_ld, l->l_info);
+ if (l->l_info[DT_HASH])
+ _dl_setup_hash (l);
+
+ return l;
+}
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
new file mode 100644
index 0000000000..b4600b1970
--- /dev/null
+++ b/elf/dl-lookup.c
@@ -0,0 +1,129 @@
+/* Look up a symbol in the loaded objects.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <stddef.h>
+#include <libelf.h>
+#include <link.h>
+
+/* Search loaded objects' symbol tables for a definition of
+ the symbol UNDEF_NAME. Don't use a PLT defn in UNDEF_MAP, since
+ that is the object making the reference. */
+
+Elf32_Addr
+_dl_lookup_symbol (const char *undef_name, const Elf32_Sym **ref,
+ struct link_map *symbol_scope)
+{
+ unsigned long int hash = elf_hash (undef_name);
+ struct link_map *map;
+ struct
+ {
+ Elf32_Addr a;
+ const Elf32_Sym *s;
+ } weak_value = { 0, NULL };
+
+ /* Search the relevant loaded objects for a definition. */
+ for (map = symbol_scope; map; map = map->l_next)
+ {
+ const Elf32_Sym *symtab;
+ const char *strtab;
+ Elf32_Word symidx;
+
+ symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
+ strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
+
+ /* Search the appropriate hash bucket in this object's symbol table
+ for a definition for the same symbol name. */
+ for (symidx = map->l_buckets[hash % map->l_nbuckets];
+ symidx != STN_UNDEF;
+ symidx = map->l_chain[symidx])
+ {
+ const Elf32_Sym *sym = &symtab[symidx];
+
+ if (sym->st_value == 0)
+ continue;
+
+ switch (ELF32_ST_TYPE (sym->st_info))
+ {
+ case STT_NOTYPE:
+ case STT_FUNC:
+ case STT_OBJECT:
+ break;
+ default:
+ /* Not a code/data definition. */
+ continue;
+ }
+
+ if (sym == *ref)
+ /* This is the same symbol we are looking for the value for.
+ If it is a PLT entry, it will have a value of its own;
+ but that is not what we are looking for. */
+ continue;
+
+ if (strcmp (strtab + sym->st_name, undef_name))
+ /* Not the symbol we are looking for. */
+ continue;
+
+ switch (ELF32_ST_BIND (sym->st_info))
+ {
+ case STB_GLOBAL:
+ /* Global definition. Just what we need. */
+ *ref = sym;
+ return map->l_addr;
+ case STB_WEAK:
+ /* Weak definition. Use this value if we don't find another. */
+ if (weak_value.a == 0)
+ {
+ weak_value.s = sym;
+ weak_value.a = map->l_addr;
+ }
+ break;
+ default:
+ /* Local symbols are ignored. */
+ break;
+ }
+ }
+ }
+
+ if (weak_value.s == NULL)
+ {
+ const char msg[] = "undefined symbol: ";
+ char buf[sizeof msg + strlen (undef_name)];
+ memcpy (buf, msg, sizeof msg - 1);
+ memcpy (&buf[sizeof msg - 1], undef_name, sizeof buf - sizeof msg);
+ _dl_signal_error (0, msg);
+ }
+
+ *ref = weak_value.s;
+ return weak_value.a;
+}
+
+
+/* Cache the location of MAP's hash table. */
+
+void
+_dl_setup_hash (struct link_map *map)
+{
+ Elf32_Word *hash = (void *) map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr;
+ Elf32_Word nchain;
+ map->l_nbuckets = *hash++;
+ nchain = *hash++;
+ map->l_buckets = hash;
+ hash += map->l_nbuckets;
+ map->l_chain = hash;
+}
diff --git a/elf/dl-object.c b/elf/dl-object.c
new file mode 100644
index 0000000000..e7b1ce3f45
--- /dev/null
+++ b/elf/dl-object.c
@@ -0,0 +1,63 @@
+/* Storage management for the chain of loaded shared objects.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <link.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+/* List of objects currently loaded. */
+struct link_map *_dl_loaded;
+
+/* Tail of that list which were loaded at startup. */
+struct link_map *_dl_startup_loaded;
+
+/* Allocate a `struct link_map' for a new object being loaded,
+ and enter it into the _dl_loaded list. */
+
+struct link_map *
+_dl_new_object (char *realname, const char *libname, int type)
+{
+ struct link_map *new = malloc (sizeof *new);
+ if (! new)
+ _dl_signal_error (ENOMEM, "can't open new object");
+
+ memset (new, 0, sizeof *new);
+ new->l_name = realname;
+ new->l_libname = libname;
+ new->l_type = type;
+
+ if (_dl_loaded == NULL)
+ {
+ new->l_prev = new->l_next = NULL;
+ _dl_loaded = new;
+ }
+ else
+ {
+ struct link_map *l = _dl_loaded;
+ while (l->l_next)
+ l = l->l_next;
+ new->l_prev = l;
+ new->l_next = NULL;
+ l->l_next = new;
+ }
+
+ return new;
+}
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
new file mode 100644
index 0000000000..8efb3f04a6
--- /dev/null
+++ b/elf/dl-reloc.c
@@ -0,0 +1,115 @@
+/* Relocate a shared object and resolve its references to other loaded objects.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <link.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <errno.h>
+#include "dynamic-link.h"
+
+
+void
+_dl_relocate_object (struct link_map *l, int lazy)
+{
+ const size_t pagesize = getpagesize ();
+
+ if (l->l_relocated)
+ return;
+
+ if (l->l_info[DT_TEXTREL])
+ {
+ /* Bletch. We must make read-only segments writable
+ long enough to relocate them. */
+ const Elf32_Phdr *ph;
+ for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
+ if (ph->p_type == PT_LOAD && (ph->p_flags & PF_W) == 0)
+ {
+ caddr_t mapstart = ((caddr_t) l->l_addr +
+ (ph->p_vaddr & ~(pagesize - 1)));
+ caddr_t mapend = ((caddr_t) l->l_addr +
+ ((ph->p_vaddr + ph->p_memsz + pagesize - 1)
+ & ~(pagesize - 1)));
+ if (mprotect (mapstart, mapend - mapstart,
+ PROT_READ|PROT_WRITE) < 0)
+ _dl_signal_error (errno,
+ "cannot make segment writable for relocation");
+ }
+ }
+
+ {
+ struct link_map *real_next, *scope;
+
+ const char *strtab
+ = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
+
+
+ Elf32_Addr resolve (const Elf32_Sym **ref)
+ {
+ return _dl_lookup_symbol (strtab + (*ref)->st_name, ref, scope);
+ }
+
+ real_next = l->l_next;
+ if (l->l_info[DT_SYMBOLIC])
+ {
+ l->l_prev->l_next = real_next;
+ l->l_next = _dl_loaded;
+ scope = l;
+ }
+ else
+ scope = _dl_loaded;
+
+ elf_dynamic_relocate (l->l_info, l->l_addr, lazy, resolve);
+
+ /* Restore list frobnication done above for DT_SYMBOLIC. */
+ l->l_next = real_next;
+ l->l_prev->l_next = l;
+ }
+
+ if (l->l_info[DT_JMPREL] && ! lazy)
+ /* Set up the PLT so its unrelocated entries will
+ jump to _dl_runtime_resolve, which will relocate them. */
+ elf_machine_runtime_setup (l);
+
+ l->l_relocated = 1;
+
+ if (l->l_info[DT_TEXTREL])
+ {
+ /* Undo the protection change we made before relocating. */
+ const Elf32_Phdr *ph;
+ for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
+ if (ph->p_type == PT_LOAD && (ph->p_flags & PF_W) == 0)
+ {
+ caddr_t mapstart = ((caddr_t) l->l_addr +
+ (ph->p_vaddr & ~(pagesize - 1)));
+ caddr_t mapend = ((caddr_t) l->l_addr +
+ ((ph->p_vaddr + ph->p_memsz + pagesize - 1)
+ & ~(pagesize - 1)));
+ int prot = 0;
+ if (ph->p_flags & PF_R)
+ prot |= PROT_READ;
+ if (ph->p_flags & PF_X)
+ prot |= PROT_EXEC;
+ if (mprotect (mapstart, mapend - mapstart, prot) < 0)
+ _dl_signal_error (errno,
+ "can't restore segment prot after reloc");
+ }
+ }
+
+}
diff --git a/elf/dlclose.c b/elf/dlclose.c
new file mode 100644
index 0000000000..4aa4085f66
--- /dev/null
+++ b/elf/dlclose.c
@@ -0,0 +1,97 @@
+/* dlclose -- Close a handle opened by `dlopen'.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <link.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+
+#define LOSE(s) _dl_signal_error (0, s)
+
+int
+dlclose (void *handle)
+{
+ void doit (void)
+ {
+ struct link_map *map = handle;
+
+ if (map->l_opencount == 0)
+ LOSE ("shared object not open");
+
+ /* Decrement the reference count. */
+ --map->l_opencount;
+
+ if (map->l_opencount == 0 && map->l_type == lt_loaded)
+ {
+ /* That was the last reference, and this was a dlopen-loaded
+ object. We can unmap it. */
+ const Elf32_Phdr *ph;
+
+ if (map->l_info[DT_FINI])
+ /* Call its termination function. */
+ (*(void (*) (void)) ((void *) map->l_addr +
+ map->l_info[DT_FINI]->d_un.d_ptr)) ();
+
+ if (map->l_info[DT_NEEDED])
+ {
+ /* Also close all the dependencies. */
+ const char *strtab
+ = (void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr;
+ const Elf32_Dyn *d;
+ for (d = map->l_ld; d->d_tag != DT_NULL; ++d)
+ if (d->d_tag == DT_NEEDED)
+ {
+ /* It must already be open, since this one needed it;
+ so dlopen will just find us its `struct link_map'
+ and bump its reference count. */
+ struct link_map *o, *dep
+ = dlopen (strtab + d->d_un.d_val, RTLD_LAZY);
+ --dep->l_opencount; /* Lose the ref from that dlopen. */
+ /* Now we have the handle; we can close it for real. */
+ o = map;
+ map = dep;
+ doit ();
+ map = o;
+ }
+ }
+
+ /* Unmap the segments. */
+ for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
+ if (ph->p_type == PT_LOAD)
+ {
+ Elf32_Addr mapstart = ph->p_vaddr & ~(ph->p_align - 1);
+ Elf32_Addr mapend = ((ph->p_vaddr + ph->p_memsz
+ + ph->p_align - 1)
+ & ~(ph->p_align - 1));
+ munmap ((caddr_t) mapstart, mapend - mapstart);
+ }
+
+ /* Finally, unlink the data structure and free it. */
+ map->l_prev->l_next = map->l_next;
+ if (map->l_next)
+ map->l_next->l_prev = map->l_prev;
+ free (map);
+ }
+ }
+
+ return _dlerror_run (doit) ? -1 : 0;
+}
+
diff --git a/elf/dlerror.c b/elf/dlerror.c
new file mode 100644
index 0000000000..0eed60a45d
--- /dev/null
+++ b/elf/dlerror.c
@@ -0,0 +1,64 @@
+/* dlerror -- Return error detail for failing <dlfcn.h> functions.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <link.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static int _dl_last_errcode;
+static const char *_dl_last_errstring;
+
+char *
+dlerror (void)
+{
+ char *ret;
+
+ if (! _dl_last_errstring)
+ return NULL;
+
+ if (_dl_last_errcode)
+ {
+ static char *buf;
+ if (buf)
+ {
+ free (buf);
+ buf = NULL;
+ }
+ if (asprintf (&buf, "%s: %s",
+ _dl_last_errstring, strerror (_dl_last_errcode)) == -1)
+ return NULL;
+ else
+ ret = buf;
+ }
+ else
+ ret = (char *) _dl_last_errstring;
+
+ /* Reset the error indicator. */
+ _dl_last_errstring = NULL;
+ return ret;
+}
+
+int
+_dlerror_run (void (*operate) (void))
+{
+ _dl_last_errcode = _dl_catch_error (&_dl_last_errstring, operate);
+ return _dl_last_errstring != NULL;
+}
diff --git a/elf/dlopen.c b/elf/dlopen.c
new file mode 100644
index 0000000000..c16cff9ae2
--- /dev/null
+++ b/elf/dlopen.c
@@ -0,0 +1,62 @@
+/* dlopen -- Load a shared object at run time.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <stddef.h>
+#include <link.h>
+#include <dlfcn.h>
+
+void *
+dlopen (const char *file, dl_open_mode mode)
+{
+ struct link_map *new, *l;
+
+ void doit (void)
+ {
+ Elf32_Addr init;
+
+ new = _dl_map_object (_dl_loaded, file, NULL);
+
+ /* Map in any dependencies. */
+ for (l = new; l; l = l->l_next)
+ if (! l->l_deps_loaded)
+ {
+ if (l->l_info[DT_NEEDED])
+ {
+ const char *strtab
+ = (void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr;
+ const Elf32_Dyn *d;
+ for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
+ if (d->d_tag == DT_NEEDED)
+ _dl_map_object (l, strtab + d->d_un.d_val, NULL);
+ }
+ l->l_deps_loaded = 1;
+ }
+
+ /* Relocate the objects loaded. */
+ for (l = new; l; l = l->l_next)
+ if (! l->l_relocated)
+ _dl_relocate_object (l, mode == RTLD_LAZY);
+
+ /* Run the initializer functions of new objects. */
+ while (init = _dl_init_next ())
+ (*(void (*) (void)) init) ();
+ }
+
+ return _dlerror_run (doit) ? NULL : new;
+}
diff --git a/elf/dlsym.c b/elf/dlsym.c
new file mode 100644
index 0000000000..6d8781053b
--- /dev/null
+++ b/elf/dlsym.c
@@ -0,0 +1,46 @@
+/* dlsym -- Look up a symbol in a shared object loaded by `dlopen'.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <stddef.h>
+#include <link.h>
+#include <dlfcn.h>
+#include <setjmp.h>
+
+
+void *
+dlsym (void *handle, const char *name)
+{
+ struct link_map *map = handle;
+ struct link_map *real_next;
+ Elf32_Addr value;
+ int lose;
+ void doit (void)
+ {
+ const Elf32_Sym *ref = NULL;
+ value = _dl_lookup_symbol (name, &ref, map);
+ }
+
+ /* Confine the symbol scope to just this map. */
+ real_next = map->l_next;
+ map->l_next = NULL;
+ lose = _dlerror_run (doit);
+ map->l_next = real_next;
+
+ return lose ? NULL : (void *) value;
+}
diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h
new file mode 100644
index 0000000000..1c3af29d6a
--- /dev/null
+++ b/elf/dynamic-link.h
@@ -0,0 +1,119 @@
+/* Inline functions for dynamic linking.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <elf.h>
+
+/* This machine-dependent file defines these inline functions. */
+
+static void elf_machine_rel (Elf32_Addr loadaddr, Elf32_Dyn *info[DT_NUM],
+ const Elf32_Rel *reloc,
+ Elf32_Addr sym_loadaddr, const Elf32_Sym *sym);
+static void elf_machine_rela (Elf32_Addr loadaddr, Elf32_Dyn *info[DT_NUM],
+ const Elf32_Rela *reloc,
+ Elf32_Addr sym_loadaddr, const Elf32_Sym *sym);
+static Elf32_Addr *elf_machine_got (void);
+static Elf32_Addr elf_machine_load_address (void);
+
+#include <dl-machine.h>
+
+
+#include <assert.h>
+
+/* Read the dynamic section at DYN and fill in INFO with indices DT_*. */
+
+static inline void
+elf_get_dynamic_info (Elf32_Dyn *dyn, Elf32_Dyn *info[DT_NUM])
+{
+ unsigned int i;
+
+ for (i = 0; i < DT_NUM; ++i)
+ info[i] = NULL;
+
+ while (dyn->d_tag != DT_NULL)
+ {
+ assert (dyn->d_tag < DT_NUM);
+ info[dyn->d_tag] = dyn++;
+ }
+
+ if (info[DT_RELA])
+ assert (info[DT_RELAENT]->d_un.d_val == sizeof (Elf32_Rela));
+ if (info[DT_REL])
+ assert (info[DT_RELENT]->d_un.d_val == sizeof (Elf32_Rel));
+ if (info[DT_PLTREL])
+ assert (info[DT_PLTREL]->d_un.d_val == DT_REL ||
+ info[DT_PLTREL]->d_un.d_val == DT_RELA);
+}
+
+/* Perform the relocations specified by DYNAMIC on the running program
+ image. If LAZY is nonzero, don't relocate PLT entries. *RESOLVE is
+ called to resolve symbol values; it modifies its argument pointer to
+ point to the defining symbol, and returns the base load address of the
+ defining object. */
+
+static inline void
+elf_dynamic_relocate (Elf32_Dyn *dynamic[DT_NUM], Elf32_Addr loadaddr,
+ int lazy, Elf32_Addr (*resolve) (const Elf32_Sym **))
+{
+ const Elf32_Sym *const symtab
+ = (const Elf32_Sym *) dynamic[DT_SYMTAB]->d_un.d_ptr;
+
+ inline Elf32_Addr symvalue (Elf32_Word info, const Elf32_Sym **definer)
+ {
+ if (ELF32_R_SYM (info) == STN_UNDEF)
+ return 0; /* This value will not be consulted. */
+ *definer = &symtab[ELF32_R_SYM (info)];
+ return (*resolve) (definer);
+ }
+
+ /* Perform Elf32_Rel relocations in the section found by RELTAG, SZTAG. */
+ inline void do_rel (Elf32_Word reltag, Elf32_Word sztag)
+ {
+ const Elf32_Rel *r = (const Elf32_Rel *) dynamic[reltag]->d_un.d_ptr;
+ const Elf32_Rel *end = &r[dynamic[sztag]->d_un.d_val / sizeof *r];
+ while (r < end)
+ {
+ const Elf32_Sym *definer;
+ Elf32_Addr loadbase = symvalue (r->r_info, &definer);
+ elf_machine_rel (loadaddr, dynamic, r, loadbase, definer);
+ ++r;
+ }
+ }
+ /* Perform Elf32_Rela relocations in the section found by RELTAG, SZTAG. */
+ inline void do_rela (Elf32_Word reltag, Elf32_Word sztag)
+ {
+ const Elf32_Rela *r = (const Elf32_Rela *) dynamic[reltag]->d_un.d_ptr;
+ const Elf32_Rela *end = &r[dynamic[sztag]->d_un.d_val / sizeof *r];
+ while (r < end)
+ {
+ const Elf32_Sym *definer;
+ Elf32_Addr loadbase = symvalue (r->r_info, &definer);
+ elf_machine_rela (loadaddr, dynamic, r, loadbase, definer);
+ ++r;
+ }
+ }
+
+ if (dynamic[DT_RELA])
+ do_rela (DT_RELA, DT_RELASZ);
+ if (dynamic[DT_REL])
+ do_rel (DT_REL, DT_RELSZ);
+ if (dynamic[DT_JMPREL] && ! lazy)
+ /* Relocate the PLT right now. */
+ (dynamic[DT_PLTREL]->d_un.d_val == DT_REL ? do_rel : do_rela)
+ (DT_JMPREL, DT_PLTRELSZ);
+}
diff --git a/elf/link.h b/elf/link.h
new file mode 100644
index 0000000000..7b999dc532
--- /dev/null
+++ b/elf/link.h
@@ -0,0 +1,206 @@
+/* Run-time dynamic linker data structures for loaded ELF shared objects.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#ifndef _LINK_H
+#define _LINK_H 1
+
+#include <elf.h>
+
+
+/* Rendezvous structure used by the run-time dynamic linker to communicate
+ details of shared object loading to the debugger. If the executable's
+ dynamic section has a DT_DEBUG element, the run-time linker sets that
+ element's value to the address where this structure can be found. */
+
+struct r_debug
+ {
+ int r_version; /* Version number for this protocol. */
+
+ struct link_map *r_map; /* Head of the chain of loaded objects. */
+
+ /* This is the address of a function internal to the run-time linker,
+ that will always be called when the linker begins to map in a
+ library or unmap it, and again when the mapping change is complete.
+ The debugger can set a breakpoint at this address if it wants to
+ notice shared object mapping changes. */
+ Elf32_Addr r_brk;
+ enum
+ {
+ /* This state value describes the mapping change taking place when
+ the `r_brk' address is called. */
+ RT_CONSISTENT, /* Mapping change is complete. */
+ RT_ADD, /* Beginning to add a new object. */
+ RT_DELETE, /* Beginning to remove an object mapping. */
+ } r_state;
+
+ Elf32_Addr r_ldbase; /* Base address the linker is loaded at. */
+ };
+
+/* This symbol refers to the "dynamic structure" in the `.dynamic' section
+ of whatever module refers to `_DYNAMIC'. So, to find its own
+ `struct r_debug', a program could do:
+ for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL)
+ if (dyn->d_tag == DT_DEBUG) r_debug = (struct r_debug) dyn->d_un.d_ptr;
+ */
+
+extern Elf32_Dyn _DYNAMIC[];
+
+
+/* Structure describing a loaded shared object. The `l_next' and `l_prev'
+ members form a chain of all the shared objects loaded at startup.
+
+ These data structures exist in space used by the run-time dynamic linker;
+ modifying them may have disastrous results. */
+
+struct link_map
+ {
+ /* These first few members are part of the protocol with the debugger.
+ This is the same format used in SVR4. */
+
+ Elf32_Addr l_addr; /* Base address shared object is loaded at. */
+ char *l_name; /* Absolute file name object was found in. */
+ Elf32_Dyn *l_ld; /* Dynamic section of the shared object. */
+ struct link_map *l_next, *l_prev; /* Chain of loaded objects. */
+
+ /* All following members are internal to the dynamic linker.
+ They may change without notice. */
+
+ const char *l_libname; /* Name requested (before search). */
+ Elf32_Dyn *l_info[DT_NUM]; /* Indexed pointers to dynamic section. */
+ const Elf32_Phdr *l_phdr; /* Pointer to program header table in core. */
+ Elf32_Word l_phnum; /* Number of program header entries. */
+
+ /* Symbol hash table. */
+ Elf32_Word l_nbuckets;
+ const Elf32_Word *l_buckets, *l_chain;
+
+ unsigned int l_opencount; /* Reference count for dlopen/dlclose. */
+ enum /* Where this object came from. */
+ {
+ lt_executable, /* The main executable program. */
+ lt_interpreter, /* The interpreter: the dynamic linker. */
+ lt_library, /* Library needed by main executable. */
+ lt_loaded, /* Extra run-time loaded shared object. */
+ } l_type:2;
+ unsigned int l_deps_loaded:1; /* Nonzero if DT_NEEDED items loaded. */
+ unsigned int l_relocated:1; /* Nonzero if object's relocations done. */
+ unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */
+ unsigned int l_init_running:1; /* Nonzero while DT_INIT function runs. */
+ };
+
+/* Internal functions of the run-time dynamic linker.
+ These can be accessed if you link again the dynamic linker
+ as a shared library, as in `-lld' or `/lib/ld.so' explicitly;
+ but are not normally of interest to user programs.
+
+ The `-ldl' library functions in <dlfcn.h> provide a simple
+ user interface to run-time dynamic linking. */
+
+
+/* File descriptor referring to the zero-fill device. */
+extern int _dl_zerofd;
+
+/* OS-dependent function to open the zero-fill device. */
+extern int _dl_sysdep_open_zero_fill (void); /* dl-sysdep.c */
+
+/* OS-dependent function to give a fatal error message and exit
+ when the dynamic linker fails before the program is fully linked.
+ All arguments are `const char *'; args until a null pointer
+ are concatenated to form the message to print. */
+extern void _dl_sysdep_fatal (const char *string, ...)
+ __attribute__ ((__noreturn__));
+
+/* Nonzero if the program should be "secure" (i.e. it's setuid or somesuch).
+ This tells the dynamic linker to ignore environment variables. */
+extern int _dl_secure;
+
+/* This function is called by all the internal dynamic linker functions
+ when they encounter an error. ERRCODE is either an `errno' code
+ or zero; ERRSTRING is a string describing the specific problem. */
+
+extern void _dl_signal_error (int errcode, const char *errstring)
+ __attribute__ ((__noreturn__));
+
+/* Call OPERATE, catching errors from `dl_signal_error'. If there is no
+ error, *ERRSTRING is set to null. If there is an error, *ERRSTRING is
+ set to the string passed to _dl_signal_error, and the error code passed
+ is the return value. */
+extern int _dl_catch_error (const char **errstring, void (*operate) (void));
+
+
+/* Helper function for <dlfcn.h> functions. Runs the OPERATE function via
+ _dl_catch_error. Returns zero for success, nonzero for failure; and
+ arranges for `dlerror' to return the error details. */
+extern int _dlerror_run (void (*operate) (void));
+
+
+/* Open the shared object NAME and map in its segments.
+ LOADER's DT_RPATH is used in searching for NAME.
+ If ENTRY_POINT is not null, fill it in with the object's entry point.
+ If the object is already opened, returns its existing map. */
+extern struct link_map *_dl_map_object (struct link_map *loader,
+ const char *name,
+ Elf32_Addr *entry_point);
+
+/* Cache the locations of MAP's hash table. */
+extern void _dl_setup_hash (struct link_map *map);
+
+
+/* Search loaded objects' symbol tables for a definition of the symbol
+ referred to by UNDEF. *SYM is the symbol table entry containing the
+ reference; it is replaced with the defining symbol, and the base load
+ address of the defining object is returned. SYMBOL_SCOPE is the head of
+ the chain used for searching. */
+extern Elf32_Addr _dl_lookup_symbol (const char *undef,
+ const Elf32_Sym **sym,
+ struct link_map *symbol_scope);
+
+
+/* List of objects currently loaded. */
+extern struct link_map *_dl_loaded;
+
+/* Tail of that list which were loaded at startup. */
+extern struct link_map *_dl_startup_loaded;
+
+/* Allocate a `struct link_map' for a new object being loaded,
+ and enter it into the _dl_loaded list. */
+extern struct link_map *_dl_new_object (char *realname, const char *libname,
+ int type);
+
+/* Relocate the given object (if it hasn't already been).
+ If LAZY is nonzero, don't relocate its PLT. */
+extern void _dl_relocate_object (struct link_map *map, int lazy);
+
+/* Return the address of the next initializer function not yet run.
+ When there are no more initializers to be run, this returns zero.
+ The functions are returned in the order they should be called. */
+extern Elf32_Addr _dl_init_next (void);
+
+/* Call the finalizer functions of all shared objects whose
+ initializer functions have completed. */
+extern void _dl_fini (void);
+
+/* The dynamic linker calls this function before and having changing
+ any shared object mappings. The `r_state' member of `struct r_debug'
+ says what change is taking place. This function's address is
+ the value of the `r_brk' member. */
+extern void _dl_r_debug_state (void);
+
+
+#endif /* link.h */
diff --git a/elf/rtld.c b/elf/rtld.c
new file mode 100644
index 0000000000..fd75779a01
--- /dev/null
+++ b/elf/rtld.c
@@ -0,0 +1,267 @@
+/* Run time dynamic linker.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <link.h>
+#include "dynamic-link.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+
+#ifdef RTLD_START
+RTLD_START
+#else
+#error "sysdeps/MACHINE/dl-machine.h fails to define RTLD_START"
+#endif
+
+/* System-specific function to do initial startup for the dynamic linker.
+ After this, file access calls and getenv must work. This is responsible
+ for setting _dl_secure if we need to be secure (e.g. setuid),
+ and for setting _dl_argc and _dl_argv, and then calling _dl_main. */
+extern Elf32_Addr _dl_sysdep_start (void **start_argptr,
+ void (*dl_main) (const Elf32_Phdr *phdr,
+ Elf32_Word phent,
+ Elf32_Addr *user_entry));
+
+int _dl_secure;
+int _dl_argc;
+char **_dl_argv;
+
+struct r_debug dl_r_debug;
+
+static void dl_main (const Elf32_Phdr *phdr,
+ Elf32_Word phent,
+ Elf32_Addr *user_entry);
+
+Elf32_Addr
+_dl_start (void *arg)
+{
+ Elf32_Addr rtld_loadaddr;
+ Elf32_Dyn *dynamic_section;
+ Elf32_Dyn *dynamic_info[DT_NUM];
+
+ /* Figure out the run-time load address of the dynamic linker itself. */
+ rtld_loadaddr = elf_machine_load_address ();
+
+ /* Read our own dynamic section and fill in the info array.
+ Conveniently, the first element of the GOT contains the
+ offset of _DYNAMIC relative to the run-time load address. */
+ dynamic_section = (void *) rtld_loadaddr + *elf_machine_got ();
+ elf_get_dynamic_info (dynamic_section, dynamic_info);
+
+#ifdef ELF_MACHINE_BEFORE_RTLD_RELOC
+ ELF_MACHINE_BEFORE_RTLD_RELOC (dynamic_info);
+#endif
+
+ /* Relocate ourselves so we can do normal function calls and
+ data access using the global offset table. */
+ {
+ Elf32_Addr resolve (const Elf32_Sym **ref)
+ {
+ assert ((*ref)->st_shndx != SHN_UNDEF);
+ return rtld_loadaddr;
+ }
+ elf_dynamic_relocate (dynamic_info, rtld_loadaddr, 0, resolve);
+ }
+
+ /* Now life is sane; we can call functions and access global data.
+ Set up to use the operating system facilities, and find out from
+ the operating system's program loader where to find the program
+ header table in core. */
+
+ dl_r_debug.r_ldbase = rtld_loadaddr; /* Record our load address. */
+
+ /* Call the OS-dependent function to set up life so we can do things like
+ file access. It will call `dl_main' (below) to do all the real work
+ of the dynamic linker, and then unwind our frame and run the user
+ entry point on the same stack we entered on. */
+ return _dl_sysdep_start (&arg, &dl_main);
+}
+
+
+/* Now life is peachy; we can do all normal operations.
+ On to the real work. */
+
+void _start (void);
+
+static void
+dl_main (const Elf32_Phdr *phdr,
+ Elf32_Word phent,
+ Elf32_Addr *user_entry)
+{
+ void doit (void)
+ {
+ const Elf32_Phdr *ph;
+ struct link_map *l;
+ const char *interpreter_name;
+ int lazy;
+
+ if (*user_entry == (Elf32_Addr) &_start)
+ {
+ /* Ho ho. We are not the program interpreter! We are the program
+ itself! This means someone ran ld.so as a command. Well, that
+ might be convenient to do sometimes. We support it by
+ interpreting the args like this:
+
+ ld.so PROGRAM ARGS...
+
+ The first argument is the name of a file containing an ELF
+ executable we will load and run with the following arguments. To
+ simplify life here, PROGRAM is searched for using the normal rules
+ for shared objects, rather than $PATH or anything like that. We
+ just load it and use its entry point; we don't pay attention to
+ its PT_INTERP command (we are the interpreter ourselves). This is
+ an easy way to test a new ld.so before installing it. */
+ if (_dl_argc < 2)
+ _dl_sysdep_fatal ("\
+Usage: ld.so EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
+You have invoked `ld.so', the helper program for shared library executables.\n\
+This program usually lives in the file `/lib/ld.so', and special directives\n\
+in executable files using ELF shared libraries tell the system's program\n\
+loader to load the helper program from this file. This helper program loads\n\
+the shared libraries needed by the program executable, prepares the program\n\
+to run, and runs it. You may invoke this helper program directly from the\n\
+command line to load and run an ELF executable file; this is like executing\n\
+that file itself, but always uses this helper program from the file you\n\
+specified, instead of the helper program file specified in the executable\n\
+file you run. This is mostly of use for maintainers to test new versions\n\
+of this helper program; chances are you did not intend to run this program.\n"
+ );
+
+ interpreter_name = _dl_argv[0];
+ --_dl_argc;
+ ++_dl_argv;
+ l = _dl_map_object (NULL, _dl_argv[0], user_entry);
+ phdr = l->l_phdr;
+ phent = l->l_phnum;
+ l->l_type = lt_executable;
+ l->l_libname = (char *) "";
+ }
+ else
+ {
+ /* Create a link_map for the executable itself.
+ This will be what dlopen on "" returns. */
+ l = _dl_new_object ((char *) "", "", lt_executable);
+ l->l_phdr = phdr;
+ l->l_phnum = phent;
+ interpreter_name = 0;
+ }
+
+ /* Scan the program header table for the dynamic section. */
+ for (ph = phdr; ph < &phdr[phent]; ++ph)
+ switch (ph->p_type)
+ {
+ case PT_DYNAMIC:
+ /* This tells us where to find the dynamic section,
+ which tells us everything we need to do. */
+ l->l_ld = (void *) ph->p_vaddr;
+ break;
+ case PT_INTERP:
+ /* This "interpreter segment" was used by the program loader to
+ find the program interpreter, which is this program itself, the
+ dynamic linker. We note what name finds us, so that a future
+ dlopen call or DT_NEEDED entry, for something that wants to link
+ against the dynamic linker as a shared library, will know that
+ the shared object is already loaded. */
+ interpreter_name = (void *) ph->p_vaddr;
+ break;
+ }
+ assert (interpreter_name); /* How else did we get here? */
+
+ /* Extract the contents of the dynamic section for easy access. */
+ elf_get_dynamic_info (l->l_ld, l->l_info);
+ /* Set up our cache of pointers into the hash table. */
+ _dl_setup_hash (l);
+
+ if (l->l_info[DT_DEBUG])
+ /* There is a DT_DEBUG entry in the dynamic section. Fill it in
+ with the run-time address of the r_debug structure, which we
+ will set up later to communicate with the debugger. */
+ l->l_info[DT_DEBUG]->d_un.d_ptr = (Elf32_Addr) &dl_r_debug;
+
+ l = _dl_new_object ((char *) interpreter_name, interpreter_name,
+ lt_interpreter);
+
+ /* Now process all the DT_NEEDED entries and map in the objects.
+ Each new link_map will go on the end of the chain, so we will
+ come across it later in the loop to map in its dependencies. */
+ for (l = _dl_loaded; l; l = l->l_next)
+ {
+ if (l->l_info[DT_NEEDED])
+ {
+ const char *strtab
+ = (void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr;
+ const Elf32_Dyn *d;
+ for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
+ if (d->d_tag == DT_NEEDED)
+ _dl_map_object (l, strtab + d->d_un.d_val, NULL);
+ }
+ l->l_deps_loaded = 1;
+ }
+
+ l = _dl_loaded->l_next;
+ assert (l->l_type == lt_interpreter);
+ if (l->l_opencount == 0)
+ {
+ /* No DT_NEEDED entry referred to the interpreter object itself.
+ Remove it from the maps we will use for symbol resolution. */
+ l->l_prev->l_next = l->l_next;
+ if (l->l_next)
+ l->l_next->l_prev = l->l_prev;
+ }
+
+ lazy = _dl_secure || *(getenv ("LD_BIND_NOW") ?: "");
+
+ /* Now we have all the objects loaded. Relocate them all.
+ We do this in reverse order so that copy relocs of earlier
+ objects overwrite the data written by later objects. */
+ l = _dl_loaded;
+ while (l->l_next)
+ l = l->l_next;
+ do
+ {
+ _dl_relocate_object (l, lazy);
+ l = l->l_prev;
+ } while (l);
+
+ /* Tell the debugger where to find the map of loaded objects. */
+ dl_r_debug.r_version = 1 /* R_DEBUG_VERSION XXX */;
+ dl_r_debug.r_map = _dl_loaded;
+ dl_r_debug.r_brk = (Elf32_Addr) &_dl_r_debug_state;
+}
+ const char *errstring;
+ int err;
+
+ err = _dl_catch_error (&errstring, &doit);
+ if (errstring)
+ _dl_sysdep_fatal (_dl_argv[0] ?: "<program name unknown>",
+ ": error in loading shared libraries\n",
+ errstring, err ? ": " : NULL,
+ err ? strerror (err) : NULL, NULL);
+
+ /* Once we return, _dl_sysdep_start will invoke
+ the DT_INIT functions and then *USER_ENTRY. */
+}
+
+/* This function exists solely to have a breakpoint set on it by the
+ debugger. */
+void
+_dl_r_debug_state (void)
+{
+}
diff --git a/sysdeps/generic/dl-sysdep.c b/sysdeps/generic/dl-sysdep.c
new file mode 100644
index 0000000000..9b86f6367b
--- /dev/null
+++ b/sysdeps/generic/dl-sysdep.c
@@ -0,0 +1,98 @@
+/* Operating system support for run-time dynamic linker. Generic Unix version.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <elf.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <link.h>
+
+Elf32_Addr
+_dl_sysdep_start (void **start_argptr,
+ void (*dl_main) (const Elf32_Phdr *phdr, Elf32_Word phnum,
+ Elf32_Addr *user_entry))
+{
+ const Elf32_Phdr *phdr;
+ Elf32_Word phnum;
+ Elf32_Addr user_entry;
+ Elf32_auxv_t *av;
+ uid_t uid, euid;
+ gid_t gid, egid;
+
+ _dl_argc = *(int *) start_argptr;
+ _dl_argv = start_argptr + 1;
+ _environ = &_dl_argv[_dl_argc + 1];
+ start_argptr = (void **) _environ;
+ while (*start_argptr)
+ ++start_argptr;
+
+ for (av = ++start_argptr; av->a_type != AT_NULL; ++av)
+ switch (av->a_type)
+ {
+ case AT_PHDR:
+ phdr = av->a_un.a_ptr;
+ break;
+ case AT_PHNUM:
+ phnum = av->a_un.a_val;
+ break;
+ case AT_ENTRY:
+ user_entry = av->a_un.a_val;
+ case AT_UID:
+ uid = av->a_un.a_val;
+ break;
+ case AT_GID:
+ gid = av->a_un.a_val;
+ break;
+ case AT_EUID:
+ euid = av->a_un.a_val;
+ break;
+ case AT_EGID:
+ egid = av->a_un.a_val;
+ break;
+ }
+
+ _dl_secure = uid != euid || gid != egid;
+
+ (*dl_main) (phdr, phnum, &user_entry);
+ start_argptr[-1] = (void *) user_entry;
+}
+
+int
+_dl_sysdep_open_zero_fill (void)
+{
+ return open ("/dev/zero", O_RDONLY);
+}
+
+#include <stdarg.h>
+
+void
+_dl_sysdep_fatal (const char *msg, ...)
+{
+ va_list ap;
+
+ va_start (ap, msg);
+ do
+ {
+ size_t len = strlen (msg);
+ write (STDERR_FILENO, msg, len);
+ msg = va_arg (ap, const char *);
+ } while (msg);
+ va_end (ap);
+
+ _exit (127);
+}
diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h
new file mode 100644
index 0000000000..5e1ee2a413
--- /dev/null
+++ b/sysdeps/i386/dl-machine.h
@@ -0,0 +1,169 @@
+/* Machine-dependent ELF dynamic relocation inline functions. i386 version.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#define ELF_MACHINE_NAME "i386"
+
+#include <assert.h>
+#include <string.h>
+#include <link.h>
+
+
+/* Return nonzero iff E_MACHINE is compatible with the running host. */
+static inline int
+elf_machine_matches_host (Elf32_Half e_machine)
+{
+ switch (e_machine)
+ {
+ case EM_386:
+ case EM_486:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+/* Return the run-time address of the _GLOBAL_OFFSET_TABLE_.
+ Must be inlined in a function which uses global data. */
+static inline Elf32_Addr *
+elf_machine_got (void)
+{
+ register Elf32_Addr *got asm ("%ebx");
+ return got;
+}
+
+
+/* Return the run-time load address of the shared object. */
+static inline Elf32_Addr
+elf_machine_load_address (void)
+{
+ Elf32_Addr addr;
+ asm (" call here\n"
+ "here: popl %0\n"
+ " subl $here, %0"
+ : "=r" (addr));
+ return addr;
+}
+/* The `subl' insn above will contain an R_386_32 relocation entry
+ intended to insert the run-time address of the label `here'.
+ This will be the first relocation in the text of the dynamic linker;
+ we skip it to avoid trying to modify read-only text in this early stage. */
+#define ELF_MACHINE_BEFORE_RTLD_RELOC(dynamic_info) \
+ ++(const Elf32_Rel *) (dynamic_info)[DT_REL]->d_un.d_ptr;
+
+/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
+ LOADADDR is the load address of the object; INFO is an array indexed
+ by DT_* of the .dynamic section info. */
+
+static inline void
+elf_machine_rel (Elf32_Addr loadaddr, Elf32_Dyn *info[DT_NUM],
+ const Elf32_Rel *reloc,
+ Elf32_Addr sym_loadaddr, const Elf32_Sym *sym)
+{
+ Elf32_Addr *const reloc_addr = (Elf32_Addr *) reloc->r_offset;
+ const Elf32_Addr sym_value = sym_loadaddr + sym->st_value;
+
+ switch (ELF32_R_TYPE (reloc->r_info))
+ {
+ case R_386_COPY:
+ memcpy (reloc_addr, (void *) sym_value, sym->st_size);
+ break;
+ case R_386_GLOB_DAT:
+ case R_386_JMP_SLOT:
+ *reloc_addr = sym_value;
+ break;
+ case R_386_32:
+ *reloc_addr += sym_value;
+ break;
+ case R_386_RELATIVE:
+ *reloc_addr += loadaddr;
+ break;
+ case R_386_PC32:
+ *reloc_addr = sym_value - (Elf32_Addr) reloc_addr;
+ break;
+ default:
+ assert (! "unexpected dynamic reloc type");
+ break;
+ }
+}
+
+
+/* The i386 never uses Elf32_Rela relocations. */
+static inline void
+elf_machine_rela (Elf32_Addr loadaddr, Elf32_Dyn *info[DT_NUM],
+ const Elf32_Rela *reloc,
+ Elf32_Addr sym_loadaddr, const Elf32_Sym *sym)
+{
+ _dl_signal_error (0, "Elf32_Rela relocation requested -- unused on i386");
+}
+
+
+/* Set up the loaded object described by L so its unrelocated PLT
+ entries will jump to the on-demand fixup code in dl-runtime.c. */
+
+static inline void
+elf_machine_runtime_setup (struct link_map *l)
+{
+ extern void _dl_runtime_resolve (Elf32_Word);
+ /* The GOT entries for functions in the PLT have not yet been filled
+ in. Their initial contents will arrange when called to push an
+ offset into the .rel.plt section, push _GLOBAL_OFFSET_TABLE_[1],
+ and then jump to _GLOBAL_OFFSET_TABLE[2]. */
+ Elf32_Addr *got = (Elf32_Addr *) l->l_info[DT_PLTGOT]->d_un.d_ptr;
+ got[1] = (Elf32_Addr) l; /* Identify this shared object. */
+ /* This function will get called to fix up the GOT entry indicated by
+ the offset on the stack, and then jump to the resolved address. */
+ got[2] = (Elf32_Addr) &_dl_runtime_resolve;
+}
+
+
+/* Initial entry point code for the dynamic linker.
+ The C function `_dl_start' is the real entry point;
+ its return value is the user program's entry point. */
+
+#define RTLD_START asm ("\
+.text\n\
+.globl _start\n\
+_start: call _dl_start\n\
+ # Save the user entry point address in %ebx.\n\
+ movl %eax, %ebx\n\
+ # Call _dl_init_next to return the address of an initializer\n\
+ # function to run.\n\
+0: call _dl_init_next@PLT\n\
+ # Check for zero return, when out of initializers.\n\
+ testl %eax,%eax\n\
+ jz 1f\n\
+ # Call the shared object initializer function.\n\
+ # NOTE: We depend only on the registers (%ebx)\n\
+ # and the return address pushed by this call;\n\
+ # the initializer is called with the stack just\n\
+ # as it appears on entry, and it is free to move\n\
+ # the stack around, as long as it winds up jumping to\n\
+ # the return address on the top of the stack.\n\
+ call *%eax\n\
+ # Loop to call _dl_init_next for the next initializer.\n\
+ jmp 0b\n\
+ # Pass our finalizer function to the user in %edx, as per ELF ABI.\n\
+1: call 2f\n\
+2: popl %eax\n\
+ addl $_GLOBAL_OFFSET_TABLE_+[.-2b], %eax\n\
+ leal _dl_fini@GOT(%eax), %edx\n\
+ # Jump to the user entry point.\n\
+ jmp *%ebx\n\
+");
diff --git a/sysdeps/i386/dl-runtime.c b/sysdeps/i386/dl-runtime.c
new file mode 100644
index 0000000000..897524fdd3
--- /dev/null
+++ b/sysdeps/i386/dl-runtime.c
@@ -0,0 +1,92 @@
+/* On-demand PLT fixup for shared objects. i386 version.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+register void *sp asm ("%esp");
+
+#include <link.h>
+#include "dynamic-link.h"
+
+/* This function is not called in the normal way. The PLT jumps here, not
+ using a call. The stack looks like this:
+
+-->8(%esp) address to return to the caller of the function in the PLT
+ 4(%esp) relocation offset for this PLT entry
+ 0(%esp) identifier for this shared object (struct link_map *)
+
+ The user expects the real function the PLT refers to to be entered
+ 8(%esp) as the top of stack. */
+
+void
+_dl_runtime_resolve (Elf32_Word reloc_offset)
+{
+ __label__ return_insn;
+ struct link_map *l = (void *) &(&reloc_offset)[-1];
+
+ const Elf32_Sym *const symtab
+ = (const Elf32_Sym *) l->l_info[DT_SYMTAB]->d_un.d_ptr;
+ const char *strtab
+ = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
+
+ const Elf32_Rel *const reloc = (void *) (l->l_info[DT_JMPREL]->d_un.d_ptr
+ + reloc_offset);
+
+ const Elf32_Sym *definer;
+ Elf32_Addr loadbase;
+ struct link_map *scope, *real_next;
+
+ /* Look up the symbol's run-time value. */
+
+ real_next = l->l_next;
+ if (l->l_info[DT_SYMBOLIC])
+ {
+ l->l_prev->l_next = real_next;
+ l->l_next = _dl_loaded;
+ scope = l;
+ }
+ else
+ scope = _dl_loaded;
+
+ definer = &symtab[ELF32_R_SYM (reloc->r_info)];
+ loadbase = _dl_lookup_symbol (strtab + definer->st_name, &definer, scope);
+
+ /* Restore list frobnication done above for DT_SYMBOLIC. */
+ l->l_next = real_next;
+ l->l_prev->l_next = l;
+
+ /* Apply the relocation with that value. */
+ elf_machine_rel (l->l_addr, l->l_info, reloc, loadbase, definer);
+
+ /* The top of the stack is the word we set L from; but this location
+ holds the address we will return to. Store there the address of a
+ "ret" instruction, which will pop the stack and run the code at the
+ address in the next stack word. */
+ (&reloc_offset)[-1] = (Elf32_Word) &&return_insn;
+
+ /* The next stack word is our argument RELOC_OFFSET; but that "ret" will
+ pop and jump to this location, and the next stack word is the user's
+ return address. So store here the resolved address of the function
+ referred to by this PLT entry; once "ret" pops this address, the
+ function in the shared object will run with the stack arranged just as
+ when the user entered the PLT. */
+ (&reloc_offset)[0] = *(Elf32_Word *) reloc->r_offset;
+
+ return;
+
+ return_insn: asm volatile ("ret");
+}
diff --git a/sysdeps/i386/elf/start.S b/sysdeps/i386/elf/start.S
new file mode 100644
index 0000000000..5c29ce412a
--- /dev/null
+++ b/sysdeps/i386/elf/start.S
@@ -0,0 +1,86 @@
+/* Startup code compliant to the ELF i386 ABI.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+/* This is the canonical entry point, usually the first thing in the text
+ segment. The SVR4/i386 ABI (pages 3-31, 3-32) says that when the entry
+ point runs, most registers' values are unspecified, except for:
+
+ %edx Contains a function pointer to be registered with `atexit'.
+ This is how the dynamic linker arranges to have DT_FINI
+ functions called for shared libraries that have been loaded
+ before this code runs.
+
+ %esp The stack contains the arguments and environment:
+ 0(%esp) argc
+ 4(%esp) argv[0]
+ ...
+ (4*argc)(%esp) NULL
+ (4*(argc+1))(%esp) envp[0]
+ ...
+ NULL
+*/
+
+ .text
+ .globl _start
+_start:
+ /* Clear the frame pointer. The ABI suggests this be done, to mark
+ the outermost frame obviously. */
+ movl $0, %ebp
+
+ /* %edx contains the address of the shared library termination
+ function, which we will register with `atexit' to be called by
+ `exit'. I suspect that on some systems, and when statically
+ linked, this will not be set by anything to any function
+ pointer; hopefully it will be zero so we don't try to call
+ random pointers. */
+ testl %edx
+ jeq nofini
+ pushl %edx
+ call atexit
+ addl $4, %esp
+nofini:
+
+ /* Do essential libc initialization. In statically linked
+ programs under the GNU Hurd, this is what sets up the
+ arguments on the stack for the code below. */
+ call __libc_init_first
+
+ /* Extract the arguments and environment as encoded on the stack
+ and set up the arguments for `main': argc, argv, envp. */
+ popl %esi /* Pop the argument count. */
+ leal 4(%esp,%esi,4), %eax /* envp = &argv[argc + 1] */
+ movl %eax, _environ /* Store it in the global variable. */
+ pushl %eax /* Push third argument: envp. */
+ leal 4(%esp), %eax /* argv starts just above that word. */
+ pushl %eax /* Push second argument: argv. */
+ pushl %esi /* Push first argument: argc. */
+
+ /* Call `_init', which is the entry point to our own `.init'
+ section; and register with `atexit' to have `exit' call
+ `_fini', which is the entry point to our own `.fini' section. */
+ call _init
+ pushl $_fini
+ call atexit
+ addl $4, %esp
+
+ /* Call the user's main function, and exit with its value. */
+ call main
+ pushl %eax
+ call exit /* This should never return. */
+ hlt /* Crash if somehow it does return. */
diff --git a/sysdeps/mach/i386/sysdep.h b/sysdeps/mach/i386/sysdep.h
index 8d482a0485..692310b320 100644
--- a/sysdeps/mach/i386/sysdep.h
+++ b/sysdeps/mach/i386/sysdep.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -20,15 +20,11 @@ Cambridge, MA 02139, USA. */
#define LOSE asm volatile ("hlt")
-#define SNARF_ARGS(argc, argv, envp) \
+#define SNARF_ARGS(entry_sp, argc, argv, envp) \
do \
{ \
- int *entry_sp; \
register char **p; \
- \
- asm ("leal 4(%%ebp), %0" : "=r" (entry_sp)); \
- \
- argc = *entry_sp; \
+ argc = (int) *entry_sp; \
argv = (char **) (entry_sp + 1); \
p = argv; \
while (*p++ != NULL) \
@@ -38,9 +34,15 @@ Cambridge, MA 02139, USA. */
envp = p; \
} while (0)
-#define CALL_WITH_SP(fn, sp) \
- asm volatile ("movl %0, %%esp; jmp %1" : : \
- "g" (sp), "m" (*(long int *) (fn)) : "%esp")
+#define CALL_WITH_SP(fn, info, sp) \
+ do { \
+ void **ptr = (void **) sp; \
+ *--(__typeof (info) *) ptr = info; \
+ ptr[-1] = ptr; \
+ --ptr; \
+ asm volatile ("movl %0, %%esp; call %1" : : \
+ "g" (ptr), "m" (*(long int *) (fn)) : "%esp"); \
+ } while (0)
#define STACK_GROWTH_DOWN
diff --git a/sysdeps/stub/dl-machine.h b/sysdeps/stub/dl-machine.h
new file mode 100644
index 0000000000..d9227233e4
--- /dev/null
+++ b/sysdeps/stub/dl-machine.h
@@ -0,0 +1,115 @@
+/* Machine-dependent ELF dynamic relocation inline functions. Stub version.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#define ELF_MACHINE_NAME "stub"
+
+#include <assert.h>
+#include <string.h>
+#include <link.h>
+
+
+/* Return nonzero iff E_MACHINE is compatible with the running host. */
+static inline int
+elf_machine_matches_host (Elf32_Half e_machine)
+{
+ switch (e_machine)
+ {
+ default:
+ return 0;
+ }
+}
+
+
+/* Return the run-time address of the _GLOBAL_OFFSET_TABLE_. */
+static inline Elf32_Addr *
+elf_machine_got (void)
+{
+#error "GOT not got"
+}
+
+
+/* Return the run-time load address of the shared object. */
+static inline Elf32_Addr
+elf_machine_load_address (void)
+{
+#error "Where am I?"
+}
+
+/* This can modify DYNAMIC_INFO to avoid relocating code in
+ the functions above if they are doing bizarre magic. */
+#define ELF_MACHINE_BEFORE_RTLD_RELOC(dynamic_info) /* nothing */
+
+/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
+ LOADADDR is the load address of the object; INFO is an array indexed
+ by DT_* of the .dynamic section info. */
+
+static inline void
+elf_machine_rel (Elf32_Addr loadaddr, Elf32_Dyn *info[DT_NUM],
+ const Elf32_Rel *reloc,
+ Elf32_Addr sym_loadaddr, const Elf32_Sym *sym)
+{
+ Elf32_Addr *const reloc_addr = (Elf32_Addr *) reloc->r_offset;
+ const Elf32_Addr sym_value = sym_loadaddr + sym->st_value;
+
+ switch (ELF32_R_TYPE (reloc->r_info))
+ {
+ case R_MACHINE_COPY:
+ memcpy (reloc_addr, (void *) sym_value, sym->st_size);
+ break;
+ default:
+ assert (! "unexpected dynamic reloc type");
+ break;
+ }
+}
+
+
+static inline void
+elf_machine_rela (Elf32_Addr loadaddr, Elf32_Dyn *info[DT_NUM],
+ const Elf32_Rela *reloc,
+ Elf32_Addr sym_loadaddr, const Elf32_Sym *sym)
+{
+ _dl_signal_error (0, "Elf32_Rela relocation requested -- unused on "
+ ELF_MACHINE_NAME);
+}
+
+
+/* Set up the loaded object described by L so its unrelocated PLT
+ entries will jump to the on-demand fixup code in dl-runtime.c. */
+
+static inline void
+elf_machine_runtime_setup (struct link_map *l)
+{
+ extern void _dl_runtime_resolve (Elf32_Word);
+ /* The GOT entries for functions in the PLT have not yet been filled
+ in. Their initial contents will arrange when called to push an
+ offset into the .rel.plt section, push _GLOBAL_OFFSET_TABLE_[1],
+ and then jump to _GLOBAL_OFFSET_TABLE[2]. */
+ Elf32_Addr *got = (Elf32_Addr *) l->l_info[DT_PLTGOT]->d_un.d_ptr;
+ got[1] = (Elf32_Addr) l; /* Identify this shared object. */
+ /* This function will get called to fix up the GOT entry indicated by
+ the offset on the stack, and then jump to the resolved address. */
+ got[2] = (Elf32_Addr) &_dl_runtime_resolve;
+}
+
+
+/* Initial entry point code for the dynamic linker.
+ The C function `_dl_start' is the real entry point;
+ its return value is the user program's entry point. */
+
+#define RTLD_START #error need some startup code
diff --git a/sysdeps/stub/dl-runtime.c b/sysdeps/stub/dl-runtime.c
new file mode 100644
index 0000000000..f660ef2197
--- /dev/null
+++ b/sysdeps/stub/dl-runtime.c
@@ -0,0 +1,64 @@
+/* On-demand PLT fixup for shared objects. Stub version.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <link.h>
+#include "dynamic-link.h"
+
+/* This function is not called in the normal way. The PLT jumps here, not
+ using a call. */
+void
+_dl_runtime_resolve ()
+{
+ struct link_map *l = ???;
+ Elf32_Word reloc_offset = ???;
+
+ const Elf32_Sym *const symtab
+ = (const Elf32_Sym *) l->l_info[DT_SYMTAB]->d_un.d_ptr;
+ const char *strtab
+ = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
+
+ const Elf32_Rel *const reloc = (void *) (l->l_info[DT_JMPREL]->d_un.d_ptr
+ + reloc_offset);
+
+ const Elf32_Sym *definer;
+ Elf32_Addr loadbase;
+ struct link_map *scope, *real_next;
+
+ /* Look up the symbol's run-time value. */
+
+ real_next = l->l_next;
+ if (l->l_info[DT_SYMBOLIC])
+ {
+ l->l_prev->l_next = real_next;
+ l->l_next = _dl_loaded;
+ scope = l;
+ }
+ else
+ scope = _dl_loaded;
+
+ definer = &symtab[ELF32_R_SYM (reloc->r_info)];
+ loadbase = _dl_lookup_symbol (strtab + definer->st_name, &definer, scope);
+
+ /* Restore list frobnication done above for DT_SYMBOLIC. */
+ l->l_next = real_next;
+ l->l_prev->l_next = l;
+
+ /* Apply the relocation with that value. */
+ elf_machine_rel (l->l_addr, l->l_info, reloc, loadbase, definer);
+}