diff options
author | Roland McGrath <roland@gnu.org> | 1995-05-02 06:35:55 +0000 |
---|---|---|
committer | Roland McGrath <roland@gnu.org> | 1995-05-02 06:35:55 +0000 |
commit | d66e34cd423425c348bcc83df127dd19711b0b9a (patch) | |
tree | 97058e776d3b4fe06ef54bc4951596991652183f /elf | |
parent | 08162fa888913a5153d2fcdc4e1a816b57b1fe56 (diff) |
* sysdeps/mach/i386/sysdep.h (SNARF_ARGS, CALL_WITH_SP): Rewritten.
* sysdeps/i386/dl-machine.h: New file.
* sysdeps/stub/dl-machine.h: New file.
* sysdeps/i386/dl-runtime.c: New file.
* sysdeps/stub/dl-runtime.c: New file.
* sysdeps/i386/elf/start.S: New file.
* sysdeps/generic/dl-sysdep.c: New file.
* sysdeps/mach/hurd/dl-sysdep.c: New file.
Diffstat (limited to 'elf')
-rw-r--r-- | elf/Makefile | 32 | ||||
-rw-r--r-- | elf/dl-error.c | 43 | ||||
-rw-r--r-- | elf/dl-fini.c | 30 | ||||
-rw-r--r-- | elf/dl-init.c | 86 | ||||
-rw-r--r-- | elf/dl-load.c | 377 | ||||
-rw-r--r-- | elf/dl-lookup.c | 129 | ||||
-rw-r--r-- | elf/dl-object.c | 63 | ||||
-rw-r--r-- | elf/dl-reloc.c | 115 | ||||
-rw-r--r-- | elf/dlclose.c | 97 | ||||
-rw-r--r-- | elf/dlerror.c | 64 | ||||
-rw-r--r-- | elf/dlopen.c | 62 | ||||
-rw-r--r-- | elf/dlsym.c | 46 | ||||
-rw-r--r-- | elf/dynamic-link.h | 119 | ||||
-rw-r--r-- | elf/link.h | 206 | ||||
-rw-r--r-- | elf/rtld.c | 267 |
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) +{ +} |