Implemented runtime dynamic linker to support ELF shared libraries.
authorroland <roland>
Tue, 2 May 1995 05:52:44 +0000 (05:52 +0000)
committerroland <roland>
Tue, 2 May 1995 05:52:44 +0000 (05:52 +0000)
15 files changed:
elf/Makefile
elf/dl-error.c [new file with mode: 0644]
elf/dl-fini.c [new file with mode: 0644]
elf/dl-init.c [new file with mode: 0644]
elf/dl-load.c [new file with mode: 0644]
elf/dl-lookup.c [new file with mode: 0644]
elf/dl-object.c [new file with mode: 0644]
elf/dl-reloc.c [new file with mode: 0644]
elf/dlclose.c [new file with mode: 0644]
elf/dlerror.c [new file with mode: 0644]
elf/dlopen.c [new file with mode: 0644]
elf/dlsym.c [new file with mode: 0644]
elf/dynamic-link.h [new file with mode: 0644]
elf/link.h [new file with mode: 0644]
elf/rtld.c [new file with mode: 0644]

index 7dfb9ac..f6e94fa 100644 (file)
@@ -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.
 
 # 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 (file)
index 0000000..5b5a616
--- /dev/null
@@ -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 (file)
index 0000000..cbc0525
--- /dev/null
@@ -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 (file)
index 0000000..e3bfc2c
--- /dev/null
@@ -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 (file)
index 0000000..0de7404
--- /dev/null
@@ -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 (file)
index 0000000..b4600b1
--- /dev/null
@@ -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 (file)
index 0000000..e7b1ce3
--- /dev/null
@@ -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 (file)
index 0000000..8efb3f0
--- /dev/null
@@ -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 (file)
index 0000000..4aa4085
--- /dev/null
@@ -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 (file)
index 0000000..0eed60a
--- /dev/null
@@ -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 (file)
index 0000000..c16cff9
--- /dev/null
@@ -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 (file)
index 0000000..6d87810
--- /dev/null
@@ -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 (file)
index 0000000..1c3af29
--- /dev/null
@@ -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 (file)
index 0000000..7b999dc
--- /dev/null
@@ -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.  */
+  };
+\f
+/* 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 (file)
index 0000000..fd75779
--- /dev/null
@@ -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)
+{
+}