summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-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
15 files changed, 1734 insertions, 2 deletions
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)
+{
+}