summaryrefslogtreecommitdiffstats
path: root/libexec/rtld-elf
diff options
context:
space:
mode:
authorjdp <jdp@FreeBSD.org>1998-03-07 19:24:35 +0000
committerjdp <jdp@FreeBSD.org>1998-03-07 19:24:35 +0000
commit350f58aa8f1baac341d34d10b452ce3d34d25447 (patch)
tree5b100ebbd54fbed2d6eff4d0fc9ec38edacfb9c1 /libexec/rtld-elf
parent8222bc81a24ad204d76d22477847c9e34a4bbac2 (diff)
downloadFreeBSD-src-350f58aa8f1baac341d34d10b452ce3d34d25447.zip
FreeBSD-src-350f58aa8f1baac341d34d10b452ce3d34d25447.tar.gz
Import the ELF dynamic linker. This is the ElfKit version with
quite a few enhancements and bug fixes. There are still some known deficiencies, but it should be adequate to get us started with ELF. Submitted by: John Polstra <jdp@polstra.com>
Diffstat (limited to 'libexec/rtld-elf')
-rw-r--r--libexec/rtld-elf/Makefile15
-rw-r--r--libexec/rtld-elf/debug.c52
-rw-r--r--libexec/rtld-elf/debug.h48
-rw-r--r--libexec/rtld-elf/i386/rtld_start.S83
-rw-r--r--libexec/rtld-elf/malloc.c485
-rw-r--r--libexec/rtld-elf/map_object.c250
-rw-r--r--libexec/rtld-elf/rtld.c1291
-rw-r--r--libexec/rtld-elf/rtld.h122
-rw-r--r--libexec/rtld-elf/xmalloc.c57
9 files changed, 2403 insertions, 0 deletions
diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile
new file mode 100644
index 0000000..cc6dfbe
--- /dev/null
+++ b/libexec/rtld-elf/Makefile
@@ -0,0 +1,15 @@
+#
+# $Id: Makefile,v 1.5 1998/03/05 21:05:47 jdp Exp $
+#
+
+PROG= ld-elf.so.1
+SRCS= rtld_start.S rtld.c map_object.c malloc.c xmalloc.c debug.c
+NOMAN= true
+CFLAGS+= -elf -fpic
+CFLAGS+= -Wall
+LDFLAGS+= -elf -nostdlib -Wl,-Bshareable,-Bsymbolic
+LDADD+= -lc_pic
+
+.PATH: ${.CURDIR}/${MACHINE}
+
+.include <bsd.prog.mk>
diff --git a/libexec/rtld-elf/debug.c b/libexec/rtld-elf/debug.c
new file mode 100644
index 0000000..dc31382
--- /dev/null
+++ b/libexec/rtld-elf/debug.c
@@ -0,0 +1,52 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: debug.c,v 1.1 1998/03/05 21:05:48 jdp Exp $
+ */
+
+/*
+ * Support for printing debugging messages.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "debug.h"
+
+int debug = 0;
+
+void
+debug_printf(const char *format, ...)
+{
+ if (debug) {
+ va_list ap;
+ va_start(ap, format);
+
+ fflush(stdout);
+ vfprintf(stderr, format, ap);
+ putc('\n', stderr);
+
+ va_end(ap);
+ }
+}
diff --git a/libexec/rtld-elf/debug.h b/libexec/rtld-elf/debug.h
new file mode 100644
index 0000000..be11f54
--- /dev/null
+++ b/libexec/rtld-elf/debug.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: debug.h,v 1.1 1998/03/05 21:05:49 jdp Exp $
+ */
+
+/*
+ * Support for printing debugging messages.
+ */
+
+#ifndef DEBUG_H
+#define DEBUG_H 1
+
+#ifndef __GNUC__
+#error "This file must be compiled with GCC"
+#endif
+
+extern void debug_printf(const char *, ...);
+extern int debug;
+
+#ifdef DEBUG
+#define dbg(format, args...) debug_printf(format , ## args)
+#else
+#define dbg(format, args...) ((void) 0)
+#endif
+
+#endif /* DEBUG_H */
diff --git a/libexec/rtld-elf/i386/rtld_start.S b/libexec/rtld-elf/i386/rtld_start.S
new file mode 100644
index 0000000..f82e564
--- /dev/null
+++ b/libexec/rtld-elf/i386/rtld_start.S
@@ -0,0 +1,83 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: rtld_start.S,v 1.6 1998/03/05 21:05:53 jdp Exp $
+ */
+
+ .text
+ .align 4
+ .globl .rtld_start
+ .type .rtld_start,@function
+.rtld_start:
+ xorl %ebp,%ebp # Clear frame pointer for good form
+ movl %esp,%eax # Save initial stack pointer
+ subl $4,%esp # A place to store exit procedure addr
+ pushl %esp # Pass its address to rtld
+ pushl %eax # Pass initial stack pointer to rtld
+ call _rtld@PLT # Call rtld(sp); returns entry point
+ addl $8,%esp # Remove arguments from stack
+ popl %edx # Get exit procedure address
+/*
+ * At this point, %eax contains the entry point of the main program, and
+ * %edx contains a pointer to a termination function that should be
+ * registered with atexit(). (crt1.o registers it.)
+ */
+.globl .rtld_goto_main
+.rtld_goto_main: # This symbol exists just to make debugging easier.
+ jmp *%eax # Enter main program
+
+
+/*
+ * Binder entry point. Control is transferred to here by code in the PLT.
+ * On entry, there are two arguments on the stack. In ascending address
+ * order, they are (1) "obj", a pointer to the calling object's Obj_Entry,
+ * and (2) "reloff", the byte offset of the appropriate relocation entry
+ * in the PLT relocation table.
+ *
+ * We are careful to preserve all registers, even the the caller-save
+ * registers. That is because this code may be invoked by low-level
+ * assembly-language code that is not ABI-compliant.
+ */
+ .align 4
+ .globl _rtld_bind_start
+ .type _rtld_bind_start,@function
+_rtld_bind_start:
+ pushf # Save eflags
+ pushl %eax # Save %eax
+ pushl %edx # Save %edx
+ pushl %ecx # Save %ecx
+ pushl 20(%esp) # Copy reloff argument
+ pushl 20(%esp) # Copy obj argument
+
+ call _rtld_bind@PLT # Transfer control to the binder
+ /* Now %eax contains the entry point of the function being called. */
+
+ addl $8,%esp # Discard binder arguments
+ movl %eax,20(%esp) # Store target over obj argument
+ popl %ecx # Restore %ecx
+ popl %edx # Restore %edx
+ popl %eax # Restore %eax
+ popf # Restore eflags
+ leal 4(%esp),%esp # Discard reloff, do not change eflags
+ ret # "Return" to target address
diff --git a/libexec/rtld-elf/malloc.c b/libexec/rtld-elf/malloc.c
new file mode 100644
index 0000000..a451700
--- /dev/null
+++ b/libexec/rtld-elf/malloc.c
@@ -0,0 +1,485 @@
+/*-
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91";*/
+static char *rcsid = "$Id: malloc.c,v 1.2 1996/01/19 18:36:54 jdp Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * malloc.c (Caltech) 2/21/82
+ * Chris Kingsley, kingsley@cit-20.
+ *
+ * This is a very fast storage allocator. It allocates blocks of a small
+ * number of different sizes, and keeps free lists of each size. Blocks that
+ * don't exactly fit are passed up to the next larger size. In this
+ * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long.
+ * This is designed for use in a virtual memory environment.
+ */
+
+#include <sys/types.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#ifndef BSD
+#define MAP_COPY MAP_PRIVATE
+#define MAP_FILE 0
+#define MAP_ANON 0
+#endif
+
+#ifndef BSD /* Need do better than this */
+#define NEED_DEV_ZERO 1
+#endif
+
+#define NULL 0
+
+static void morecore();
+static int findbucket();
+
+/*
+ * Pre-allocate mmap'ed pages
+ */
+#define NPOOLPAGES (32*1024/pagesz)
+static caddr_t pagepool_start, pagepool_end;
+static int morepages();
+
+/*
+ * The overhead on a block is at least 4 bytes. When free, this space
+ * contains a pointer to the next free block, and the bottom two bits must
+ * be zero. When in use, the first byte is set to MAGIC, and the second
+ * byte is the size index. The remaining bytes are for alignment.
+ * If range checking is enabled then a second word holds the size of the
+ * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC).
+ * The order of elements is critical: ov_magic must overlay the low order
+ * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern.
+ */
+union overhead {
+ union overhead *ov_next; /* when free */
+ struct {
+ u_char ovu_magic; /* magic number */
+ u_char ovu_index; /* bucket # */
+#ifdef RCHECK
+ u_short ovu_rmagic; /* range magic number */
+ u_int ovu_size; /* actual block size */
+#endif
+ } ovu;
+#define ov_magic ovu.ovu_magic
+#define ov_index ovu.ovu_index
+#define ov_rmagic ovu.ovu_rmagic
+#define ov_size ovu.ovu_size
+};
+
+#define MAGIC 0xef /* magic # on accounting info */
+#define RMAGIC 0x5555 /* magic # on range info */
+
+#ifdef RCHECK
+#define RSLOP sizeof (u_short)
+#else
+#define RSLOP 0
+#endif
+
+/*
+ * nextf[i] is the pointer to the next free block of size 2^(i+3). The
+ * smallest allocatable block is 8 bytes. The overhead information
+ * precedes the data area returned to the user.
+ */
+#define NBUCKETS 30
+static union overhead *nextf[NBUCKETS];
+extern char *sbrk();
+
+static int pagesz; /* page size */
+static int pagebucket; /* page size bucket */
+
+#ifdef MSTATS
+/*
+ * nmalloc[i] is the difference between the number of mallocs and frees
+ * for a given block size.
+ */
+static u_int nmalloc[NBUCKETS];
+#include <stdio.h>
+#endif
+
+#if defined(MALLOC_DEBUG) || defined(RCHECK)
+#define ASSERT(p) if (!(p)) botch("p")
+#include <stdio.h>
+static void
+botch(s)
+ char *s;
+{
+ fprintf(stderr, "\r\nassertion botched: %s\r\n", s);
+ (void) fflush(stderr); /* just in case user buffered it */
+ abort();
+}
+#else
+#define ASSERT(p)
+#endif
+
+/* Debugging stuff */
+extern void xprintf(const char *, ...);
+#define TRACE() xprintf("TRACE %s:%d\n", __FILE__, __LINE__)
+
+void *
+malloc(nbytes)
+ size_t nbytes;
+{
+ register union overhead *op;
+ register int bucket, n;
+ register unsigned amt;
+
+ /*
+ * First time malloc is called, setup page size and
+ * align break pointer so all data will be page aligned.
+ */
+ if (pagesz == 0) {
+ pagesz = n = getpagesize();
+ if (morepages(NPOOLPAGES) == 0)
+ return NULL;
+ op = (union overhead *)(pagepool_start);
+ n = n - sizeof (*op) - ((int)op & (n - 1));
+ if (n < 0)
+ n += pagesz;
+ if (n) {
+ pagepool_start += n;
+ }
+ bucket = 0;
+ amt = 8;
+ while (pagesz > amt) {
+ amt <<= 1;
+ bucket++;
+ }
+ pagebucket = bucket;
+ }
+ /*
+ * Convert amount of memory requested into closest block size
+ * stored in hash buckets which satisfies request.
+ * Account for space used per block for accounting.
+ */
+ if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) {
+#ifndef RCHECK
+ amt = 8; /* size of first bucket */
+ bucket = 0;
+#else
+ amt = 16; /* size of first bucket */
+ bucket = 1;
+#endif
+ n = -(sizeof (*op) + RSLOP);
+ } else {
+ amt = pagesz;
+ bucket = pagebucket;
+ }
+ while (nbytes > amt + n) {
+ amt <<= 1;
+ if (amt == 0)
+ return (NULL);
+ bucket++;
+ }
+ /*
+ * If nothing in hash bucket right now,
+ * request more memory from the system.
+ */
+ if ((op = nextf[bucket]) == NULL) {
+ morecore(bucket);
+ if ((op = nextf[bucket]) == NULL)
+ return (NULL);
+ }
+ /* remove from linked list */
+ nextf[bucket] = op->ov_next;
+ op->ov_magic = MAGIC;
+ op->ov_index = bucket;
+#ifdef MSTATS
+ nmalloc[bucket]++;
+#endif
+#ifdef RCHECK
+ /*
+ * Record allocated size of block and
+ * bound space with magic numbers.
+ */
+ op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
+ op->ov_rmagic = RMAGIC;
+ *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
+#endif
+ return ((char *)(op + 1));
+}
+
+/*
+ * Allocate more memory to the indicated bucket.
+ */
+static void
+morecore(bucket)
+ int bucket;
+{
+ register union overhead *op;
+ register int sz; /* size of desired block */
+ int amt; /* amount to allocate */
+ int nblks; /* how many blocks we get */
+
+ /*
+ * sbrk_size <= 0 only for big, FLUFFY, requests (about
+ * 2^30 bytes on a VAX, I think) or for a negative arg.
+ */
+ sz = 1 << (bucket + 3);
+#ifdef MALLOC_DEBUG
+ ASSERT(sz > 0);
+#else
+ if (sz <= 0)
+ return;
+#endif
+ if (sz < pagesz) {
+ amt = pagesz;
+ nblks = amt / sz;
+ } else {
+ amt = sz + pagesz;
+ nblks = 1;
+ }
+ if (amt > pagepool_end - pagepool_start)
+ if (morepages(amt/pagesz + NPOOLPAGES) == 0)
+ return;
+ op = (union overhead *)pagepool_start;
+ pagepool_start += amt;
+
+ /*
+ * Add new memory allocated to that on
+ * free list for this hash bucket.
+ */
+ nextf[bucket] = op;
+ while (--nblks > 0) {
+ op->ov_next = (union overhead *)((caddr_t)op + sz);
+ op = (union overhead *)((caddr_t)op + sz);
+ }
+}
+
+void
+free(cp)
+ void *cp;
+{
+ register int size;
+ register union overhead *op;
+
+ if (cp == NULL)
+ return;
+ op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
+#ifdef MALLOC_DEBUG
+ ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */
+#else
+ if (op->ov_magic != MAGIC)
+ return; /* sanity */
+#endif
+#ifdef RCHECK
+ ASSERT(op->ov_rmagic == RMAGIC);
+ ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
+#endif
+ size = op->ov_index;
+ ASSERT(size < NBUCKETS);
+ op->ov_next = nextf[size]; /* also clobbers ov_magic */
+ nextf[size] = op;
+#ifdef MSTATS
+ nmalloc[size]--;
+#endif
+}
+
+/*
+ * When a program attempts "storage compaction" as mentioned in the
+ * old malloc man page, it realloc's an already freed block. Usually
+ * this is the last block it freed; occasionally it might be farther
+ * back. We have to search all the free lists for the block in order
+ * to determine its bucket: 1st we make one pass thru the lists
+ * checking only the first block in each; if that fails we search
+ * ``realloc_srchlen'' blocks in each list for a match (the variable
+ * is extern so the caller can modify it). If that fails we just copy
+ * however many bytes was given to realloc() and hope it's not huge.
+ */
+int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */
+
+void *
+realloc(cp, nbytes)
+ void *cp;
+ size_t nbytes;
+{
+ register u_int onb;
+ register int i;
+ union overhead *op;
+ char *res;
+ int was_alloced = 0;
+
+ if (cp == NULL)
+ return (malloc(nbytes));
+ op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
+ if (op->ov_magic == MAGIC) {
+ was_alloced++;
+ i = op->ov_index;
+ } else {
+ /*
+ * Already free, doing "compaction".
+ *
+ * Search for the old block of memory on the
+ * free list. First, check the most common
+ * case (last element free'd), then (this failing)
+ * the last ``realloc_srchlen'' items free'd.
+ * If all lookups fail, then assume the size of
+ * the memory block being realloc'd is the
+ * largest possible (so that all "nbytes" of new
+ * memory are copied into). Note that this could cause
+ * a memory fault if the old area was tiny, and the moon
+ * is gibbous. However, that is very unlikely.
+ */
+ if ((i = findbucket(op, 1)) < 0 &&
+ (i = findbucket(op, realloc_srchlen)) < 0)
+ i = NBUCKETS;
+ }
+ onb = 1 << (i + 3);
+ if (onb < pagesz)
+ onb -= sizeof (*op) + RSLOP;
+ else
+ onb += pagesz - sizeof (*op) - RSLOP;
+ /* avoid the copy if same size block */
+ if (was_alloced) {
+ if (i) {
+ i = 1 << (i + 2);
+ if (i < pagesz)
+ i -= sizeof (*op) + RSLOP;
+ else
+ i += pagesz - sizeof (*op) - RSLOP;
+ }
+ if (nbytes <= onb && nbytes > i) {
+#ifdef RCHECK
+ op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
+ *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
+#endif
+ return(cp);
+ } else
+ free(cp);
+ }
+ if ((res = malloc(nbytes)) == NULL)
+ return (NULL);
+ if (cp != res) /* common optimization if "compacting" */
+ bcopy(cp, res, (nbytes < onb) ? nbytes : onb);
+ return (res);
+}
+
+/*
+ * Search ``srchlen'' elements of each free list for a block whose
+ * header starts at ``freep''. If srchlen is -1 search the whole list.
+ * Return bucket number, or -1 if not found.
+ */
+static int
+findbucket(freep, srchlen)
+ union overhead *freep;
+ int srchlen;
+{
+ register union overhead *p;
+ register int i, j;
+
+ for (i = 0; i < NBUCKETS; i++) {
+ j = 0;
+ for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
+ if (p == freep)
+ return (i);
+ j++;
+ }
+ }
+ return (-1);
+}
+
+#ifdef MSTATS
+/*
+ * mstats - print out statistics about malloc
+ *
+ * Prints two lines of numbers, one showing the length of the free list
+ * for each size category, the second showing the number of mallocs -
+ * frees for each size category.
+ */
+mstats(s)
+ char *s;
+{
+ register int i, j;
+ register union overhead *p;
+ int totfree = 0,
+ totused = 0;
+
+ fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
+ for (i = 0; i < NBUCKETS; i++) {
+ for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
+ ;
+ fprintf(stderr, " %d", j);
+ totfree += j * (1 << (i + 3));
+ }
+ fprintf(stderr, "\nused:\t");
+ for (i = 0; i < NBUCKETS; i++) {
+ fprintf(stderr, " %d", nmalloc[i]);
+ totused += nmalloc[i] * (1 << (i + 3));
+ }
+ fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n",
+ totused, totfree);
+}
+#endif
+
+
+static int
+morepages(n)
+int n;
+{
+ int fd = -1;
+ int offset;
+
+#ifdef NEED_DEV_ZERO
+ fd = open("/dev/zero", O_RDWR, 0);
+ if (fd == -1)
+ perror("/dev/zero");
+#endif
+
+ if (pagepool_end - pagepool_start > pagesz) {
+ caddr_t addr = (caddr_t)
+ (((int)pagepool_start + pagesz - 1) & ~(pagesz - 1));
+ if (munmap(addr, pagepool_end - addr) != 0)
+ warn("morepages: munmap %p", addr);
+ }
+
+ offset = (int)pagepool_start - ((int)pagepool_start & ~(pagesz - 1));
+
+ if ((pagepool_start = mmap(0, n * pagesz,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_COPY, fd, 0)) == (caddr_t)-1) {
+ xprintf("Cannot map anonymous memory");
+ return 0;
+ }
+ pagepool_end = pagepool_start + n * pagesz;
+ pagepool_start += offset;
+
+#ifdef NEED_DEV_ZERO
+ close(fd);
+#endif
+ return n;
+}
diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c
new file mode 100644
index 0000000..0e33b3c
--- /dev/null
+++ b/libexec/rtld-elf/map_object.c
@@ -0,0 +1,250 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: map_object.c,v 1.2 1998/03/06 22:14:53 jdp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "rtld.h"
+
+static int protflags(int); /* Elf flags -> mmap protection */
+
+/*
+ * Map a shared object into memory. The argument is a file descriptor,
+ * which must be open on the object and positioned at its beginning.
+ *
+ * The return value is a pointer to a newly-allocated Obj_Entry structure
+ * for the shared object. Returns NULL on failure.
+ */
+Obj_Entry *
+map_object(int fd)
+{
+ Obj_Entry *obj;
+ union {
+ Elf32_Ehdr hdr;
+ char buf[PAGE_SIZE];
+ } u;
+ int nbytes;
+ Elf32_Phdr *phdr;
+ Elf32_Phdr *phlimit;
+ Elf32_Phdr *segs[2];
+ int nsegs;
+ Elf32_Phdr *phdyn;
+ Elf32_Phdr *phphdr;
+ caddr_t mapbase;
+ size_t mapsize;
+ Elf32_Off base_offset;
+ Elf32_Addr base_vaddr;
+ Elf32_Addr base_vlimit;
+ caddr_t base_addr;
+ Elf32_Off data_offset;
+ Elf32_Addr data_vaddr;
+ Elf32_Addr data_vlimit;
+ caddr_t data_addr;
+ Elf32_Addr clear_vaddr;
+ caddr_t clear_addr;
+ size_t nclear;
+ Elf32_Addr bss_vaddr;
+ Elf32_Addr bss_vlimit;
+ caddr_t bss_addr;
+
+ if ((nbytes = read(fd, u.buf, PAGE_SIZE)) == -1) {
+ _rtld_error("Read error: %s", strerror(errno));
+ return NULL;
+ }
+
+ /* Make sure the file is valid */
+ if (nbytes < sizeof(Elf32_Ehdr)
+ || u.hdr.e_ident[EI_MAG0] != ELFMAG0
+ || u.hdr.e_ident[EI_MAG1] != ELFMAG1
+ || u.hdr.e_ident[EI_MAG2] != ELFMAG2
+ || u.hdr.e_ident[EI_MAG3] != ELFMAG3) {
+ _rtld_error("Invalid file format");
+ return NULL;
+ }
+ if (u.hdr.e_ident[EI_CLASS] != ELFCLASS32
+ || u.hdr.e_ident[EI_DATA] != ELFDATA2LSB) {
+ _rtld_error("Unsupported file layout");
+ return NULL;
+ }
+ if (u.hdr.e_ident[EI_VERSION] != EV_CURRENT
+ || u.hdr.e_version != EV_CURRENT) {
+ _rtld_error("Unsupported file version");
+ return NULL;
+ }
+ if (u.hdr.e_type != ET_EXEC && u.hdr.e_type != ET_DYN) {
+ _rtld_error("Unsupported file type");
+ return NULL;
+ }
+ if (u.hdr.e_machine != EM_386) {
+ _rtld_error("Unsupported machine");
+ return NULL;
+ }
+
+ /*
+ * We rely on the program header being in the first page. This is
+ * not strictly required by the ABI specification, but it seems to
+ * always true in practice. And, it simplifies things considerably.
+ */
+ assert(u.hdr.e_phentsize == sizeof(Elf32_Phdr));
+ assert(u.hdr.e_phoff + u.hdr.e_phnum*sizeof(Elf32_Phdr) <= PAGE_SIZE);
+ assert(u.hdr.e_phoff + u.hdr.e_phnum*sizeof(Elf32_Phdr) <= nbytes);
+
+ /*
+ * Scan the program header entries, and save key information.
+ *
+ * We rely on there being exactly two load segments, text and data,
+ * in that order.
+ */
+ phdr = (Elf32_Phdr *) (u.buf + u.hdr.e_phoff);
+ phlimit = phdr + u.hdr.e_phnum;
+ nsegs = 0;
+ phdyn = NULL;
+ phphdr = NULL;
+ while (phdr < phlimit) {
+ switch (phdr->p_type) {
+
+ case PT_LOAD:
+ assert(nsegs < 2);
+ segs[nsegs] = phdr;
+ ++nsegs;
+ break;
+
+ case PT_PHDR:
+ phphdr = phdr;
+ break;
+
+ case PT_DYNAMIC:
+ phdyn = phdr;
+ break;
+ }
+
+ ++phdr;
+ }
+ if (phdyn == NULL) {
+ _rtld_error("Object is not dynamically-linked");
+ return NULL;
+ }
+
+ assert(nsegs == 2);
+ assert(segs[0]->p_align <= PAGE_SIZE);
+ assert(segs[1]->p_align <= PAGE_SIZE);
+
+ /*
+ * Map the entire address space of the object, to stake out our
+ * contiguous region, and to establish the base address for relocation.
+ */
+ base_offset = trunc_page(segs[0]->p_offset);
+ base_vaddr = trunc_page(segs[0]->p_vaddr);
+ base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz);
+ mapsize = base_vlimit - base_vaddr;
+ base_addr = u.hdr.e_type == ET_EXEC ? (caddr_t) base_vaddr : NULL;
+
+ mapbase = mmap(base_addr, mapsize, protflags(segs[0]->p_flags),
+ MAP_PRIVATE, fd, base_offset);
+ if (mapbase == (caddr_t) -1) {
+ _rtld_error("mmap of entire address space failed: %s",
+ strerror(errno));
+ return NULL;
+ }
+ if (base_addr != NULL && mapbase != base_addr) {
+ _rtld_error("mmap returned wrong address: wanted %p, got %p",
+ base_addr, mapbase);
+ munmap(mapbase, mapsize);
+ return NULL;
+ }
+
+ /* Overlay the data segment onto the proper region. */
+ data_offset = trunc_page(segs[1]->p_offset);
+ data_vaddr = trunc_page(segs[1]->p_vaddr);
+ data_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_filesz);
+ data_addr = mapbase + (data_vaddr - base_vaddr);
+ if (mmap(data_addr, data_vlimit - data_vaddr, protflags(segs[1]->p_flags),
+ MAP_PRIVATE|MAP_FIXED, fd, data_offset) == (caddr_t) -1) {
+ _rtld_error("mmap of data failed: %s", strerror(errno));
+ return NULL;
+ }
+
+ /* Clear any BSS in the last page of the data segment. */
+ clear_vaddr = segs[1]->p_vaddr + segs[1]->p_filesz;
+ clear_addr = mapbase + (clear_vaddr - base_vaddr);
+ if ((nclear = data_vlimit - clear_vaddr) > 0)
+ memset(clear_addr, 0, nclear);
+
+ /* Overlay the BSS segment onto the proper region. */
+ bss_vaddr = data_vlimit;
+ bss_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz);
+ bss_addr = mapbase + (bss_vaddr - base_vaddr);
+ if (bss_vlimit > bss_vaddr) { /* There is something to do */
+ if (mmap(bss_addr, bss_vlimit - bss_vaddr, protflags(segs[1]->p_flags),
+ MAP_PRIVATE|MAP_FIXED|MAP_ANON, -1, 0) == (caddr_t) -1) {
+ _rtld_error("mmap of bss failed: %s", strerror(errno));
+ return NULL;
+ }
+ }
+
+ obj = CNEW(Obj_Entry);
+ obj->mapbase = mapbase;
+ obj->mapsize = mapsize;
+ obj->textsize = round_page(segs[0]->p_vaddr + segs[0]->p_memsz) -
+ base_vaddr;
+ obj->vaddrbase = base_vaddr;
+ obj->relocbase = mapbase - base_vaddr;
+ obj->dynamic = (const Elf32_Dyn *)
+ (mapbase + (phdyn->p_vaddr - base_vaddr));
+ if (u.hdr.e_entry != 0)
+ obj->entry = (caddr_t) (mapbase + (u.hdr.e_entry - base_vaddr));
+ if (phphdr != NULL) {
+ obj->phdr = (const Elf32_Phdr *)
+ (mapbase + (phphdr->p_vaddr - base_vaddr));
+ obj->phsize = phphdr->p_memsz;
+ }
+
+ return obj;
+}
+
+/*
+ * Given a set of ELF protection flags, return the corresponding protection
+ * flags for MMAP.
+ */
+static int
+protflags(int elfflags)
+{
+ int prot = 0;
+ if (elfflags & PF_R)
+ prot |= PROT_READ;
+ if (elfflags & PF_W)
+ prot |= PROT_WRITE;
+ if (elfflags & PF_X)
+ prot |= PROT_EXEC;
+ return prot;
+}
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
new file mode 100644
index 0000000..73194e0
--- /dev/null
+++ b/libexec/rtld-elf/rtld.c
@@ -0,0 +1,1291 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: rtld.c,v 1.18 1998/03/06 22:14:54 jdp Exp $
+ */
+
+/*
+ * Dynamic linker for ELF.
+ *
+ * John Polstra <jdp@polstra.com>.
+ */
+
+#ifndef __GNUC__
+#error "GCC is needed to compile this file"
+#endif
+
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+/*
+ * Debugging support.
+ */
+
+#define assert(cond) ((cond) ? (void) 0 :\
+ (msg("oops: " __XSTRING(__LINE__) "\n"), abort()))
+#define msg(s) (write(1, s, strlen(s)))
+#define trace() msg("trace: " __XSTRING(__LINE__) "\n");
+
+#define END_SYM "end"
+
+/* Types. */
+typedef void (*func_ptr_type)();
+
+/*
+ * Function declarations.
+ */
+static void call_fini_functions(Obj_Entry *);
+static void call_init_functions(Obj_Entry *);
+static void die(void);
+static void digest_dynamic(Obj_Entry *);
+static Obj_Entry *digest_phdr(const Elf32_Phdr *, int, caddr_t);
+static Obj_Entry *dlcheck(void *);
+static int do_copy_relocations(Obj_Entry *);
+static unsigned long elf_hash(const char *);
+static char *find_library(const char *, const Obj_Entry *);
+static const Elf32_Sym *find_symdef(unsigned long, const Obj_Entry *,
+ const Obj_Entry **, bool);
+static void init_rtld(caddr_t);
+static bool is_exported(const Elf32_Sym *);
+static int load_needed_objects(Obj_Entry *);
+static Obj_Entry *load_object(char *);
+static Obj_Entry *obj_from_addr(const void *);
+static int relocate_objects(Obj_Entry *, bool);
+static void rtld_exit(void);
+static char *search_library_path(const char *, const char *);
+static const Elf32_Sym *symlook_obj(const char *, unsigned long,
+ const Obj_Entry *, bool);
+static void unref_object_dag(Obj_Entry *);
+
+void xprintf(const char *, ...);
+
+#ifdef DEBUG
+static const char *basename(const char *);
+#endif
+
+/* Assembly language entry point for lazy binding. */
+extern void _rtld_bind_start(void);
+
+/*
+ * Assembly language macro for getting the GOT pointer.
+ */
+#ifdef __i386__
+#define get_got_address() \
+ ({ Elf32_Addr *thegot; \
+ __asm__("movl %%ebx,%0" : "=rm"(thegot)); \
+ thegot; })
+#else
+#error "This file only supports the i386 architecture"
+#endif
+
+/*
+ * Data declarations.
+ */
+static char *error_message; /* Message for dlerror(), or NULL */
+static bool trust; /* False for setuid and setgid programs */
+static char *ld_bind_now; /* Environment variable for immediate binding */
+static char *ld_debug; /* Environment variable for debugging */
+static char *ld_library_path; /* Environment variable for search path */
+static Obj_Entry *obj_list; /* Head of linked list of shared objects */
+static Obj_Entry **obj_tail; /* Link field of last object in list */
+static Obj_Entry *obj_main; /* The main program shared object */
+static Obj_Entry obj_rtld; /* The dynamic linker shared object */
+
+/*
+ * These are the functions the dynamic linker exports to application
+ * programs. They are the only symbols the dynamic linker is willing
+ * to export from itself.
+ */
+static func_ptr_type exports[] = {
+ (func_ptr_type) &_rtld_error,
+ (func_ptr_type) &dlclose,
+ (func_ptr_type) &dlerror,
+ (func_ptr_type) &dlopen,
+ (func_ptr_type) &dlsym,
+ NULL
+};
+
+/*
+ * Global declarations normally provided by crt1. The dynamic linker is
+ * not build with crt1, so we have to provide them ourselves.
+ */
+char *__progname;
+char **environ;
+
+/*
+ * Main entry point for dynamic linking. The first argument is the
+ * stack pointer. The stack is expected to be laid out as described
+ * in the SVR4 ABI specification, Intel 386 Processor Supplement.
+ * Specifically, the stack pointer points to a word containing
+ * ARGC. Following that in the stack is a null-terminated sequence
+ * of pointers to argument strings. Then comes a null-terminated
+ * sequence of pointers to environment strings. Finally, there is a
+ * sequence of "auxiliary vector" entries.
+ *
+ * The second argument points to a place to store the dynamic linker's
+ * exit procedure pointer.
+ *
+ * The return value is the main program's entry point.
+ */
+func_ptr_type
+_rtld(Elf32_Word *sp, func_ptr_type *exit_proc)
+{
+ Elf32_Auxinfo *aux_info[AT_COUNT];
+ int i;
+ int argc;
+ char **argv;
+ char **env;
+ Elf32_Auxinfo *aux;
+ Elf32_Auxinfo *auxp;
+
+ /*
+ * On entry, the dynamic linker itself has not been relocated yet.
+ * Be very careful not to reference any global data until after
+ * init_rtld has returned. It is OK to reference file-scope statics
+ * and string constants, and to call static and global functions.
+ */
+
+ /* Find the auxiliary vector on the stack. */
+ argc = *sp++;
+ argv = (char **) sp;
+ sp += argc + 1; /* Skip over arguments and NULL terminator */
+ env = (char **) sp;
+ while (*sp++ != 0) /* Skip over environment, and NULL terminator */
+ ;
+ aux = (Elf32_Auxinfo *) sp;
+
+ /* Digest the auxiliary vector. */
+ for (i = 0; i < AT_COUNT; i++)
+ aux_info[i] = NULL;
+ for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
+ if (auxp->a_type < AT_COUNT)
+ aux_info[auxp->a_type] = auxp;
+ }
+
+ /* Initialize and relocate ourselves. */
+ assert(aux_info[AT_BASE] != NULL);
+ init_rtld((caddr_t) aux_info[AT_BASE]->a_un.a_ptr);
+
+ __progname = obj_rtld.path;
+ environ = env;
+
+ trust = geteuid() == getuid() && getegid() == getgid();
+
+ ld_bind_now = getenv("LD_BIND_NOW");
+ if (trust) {
+ ld_debug = getenv("LD_DEBUG");
+ ld_library_path = getenv("LD_LIBRARY_PATH");
+ }
+
+ if (ld_debug != NULL && *ld_debug != '\0')
+ debug = 1;
+ dbg("%s is initialized, base address = %p", __progname,
+ (caddr_t) aux_info[AT_BASE]->a_un.a_ptr);
+
+ /*
+ * Load the main program, or process its program header if it is
+ * already loaded.
+ */
+ if (aux_info[AT_EXECFD] != NULL) { /* Load the main program. */
+ int fd = aux_info[AT_EXECFD]->a_un.a_val;
+ dbg("loading main program");
+ obj_main = map_object(fd);
+ close(fd);
+ if (obj_main == NULL)
+ die();
+ } else { /* Main program already loaded. */
+ const Elf32_Phdr *phdr;
+ int phnum;
+ caddr_t entry;
+
+ dbg("processing main program's program header");
+ assert(aux_info[AT_PHDR] != NULL);
+ phdr = (const Elf32_Phdr *) aux_info[AT_PHDR]->a_un.a_ptr;
+ assert(aux_info[AT_PHNUM] != NULL);
+ phnum = aux_info[AT_PHNUM]->a_un.a_val;
+ assert(aux_info[AT_PHENT] != NULL);
+ assert(aux_info[AT_PHENT]->a_un.a_val == sizeof(Elf32_Phdr));
+ assert(aux_info[AT_ENTRY] != NULL);
+ entry = (caddr_t) aux_info[AT_ENTRY]->a_un.a_ptr;
+ obj_main = digest_phdr(phdr, phnum, entry);
+ }
+
+ obj_main->path = xstrdup(argv[0]);
+ obj_main->mainprog = true;
+ digest_dynamic(obj_main);
+
+ /* Link the main program into the list of objects. */
+ *obj_tail = obj_main;
+ obj_tail = &obj_main->next;
+ obj_main->refcount++;
+
+ dbg("loading needed objects");
+ if (load_needed_objects(obj_main) == -1)
+ die();
+
+ dbg("relocating objects");
+ if (relocate_objects(obj_main,
+ ld_bind_now != NULL && *ld_bind_now != '\0') == -1)
+ die();
+
+ dbg("doing copy relocations");
+ if (do_copy_relocations(obj_main) == -1)
+ die();
+
+ dbg("calling _init functions");
+ call_init_functions(obj_main->next);
+
+ dbg("transferring control to program entry point = %p", obj_main->entry);
+
+ /* Return the exit procedure and the program entry point. */
+ *exit_proc = (func_ptr_type) rtld_exit;
+ return (func_ptr_type) obj_main->entry;
+}
+
+caddr_t
+_rtld_bind(const Obj_Entry *obj, Elf32_Word reloff)
+{
+ const Elf32_Rel *rel;
+ const Elf32_Sym *def;
+ const Obj_Entry *defobj;
+ Elf32_Addr *where;
+ caddr_t target;
+
+ rel = (const Elf32_Rel *) ((caddr_t) obj->pltrel + reloff);
+ assert(ELF32_R_TYPE(rel->r_info) == R_386_JMP_SLOT);
+
+ where = (Elf32_Addr *) (obj->relocbase + rel->r_offset);
+ def = find_symdef(ELF32_R_SYM(rel->r_info), obj, &defobj, true);
+ if (def == NULL)
+ die();
+
+ target = (caddr_t) (defobj->relocbase + def->st_value);
+
+ dbg("\"%s\" in \"%s\" ==> %p in \"%s\"",
+ defobj->strtab + def->st_name, basename(obj->path),
+ target, basename(defobj->path));
+
+ *where = (Elf32_Addr) target;
+ return target;
+}
+
+/*
+ * Error reporting function. Use it like printf. If formats the message
+ * into a buffer, and sets things up so that the next call to dlerror()
+ * will return the message.
+ */
+void
+_rtld_error(const char *fmt, ...)
+{
+ static char buf[512];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof buf, fmt, ap);
+ error_message = buf;
+ va_end(ap);
+}
+
+#ifdef DEBUG
+static const char *
+basename(const char *name)
+{
+ const char *p = strrchr(name, '/');
+ return p != NULL ? p + 1 : name;
+}
+#endif
+
+static void
+call_fini_functions(Obj_Entry *first)
+{
+ Obj_Entry *obj;
+
+ for (obj = first; obj != NULL; obj = obj->next)
+ if (obj->fini != NULL)
+ (*obj->fini)();
+}
+
+static void
+call_init_functions(Obj_Entry *first)
+{
+ if (first != NULL) {
+ call_init_functions(first->next);
+ if (first->init != NULL)
+ (*first->init)();
+ }
+}
+
+static void
+die(void)
+{
+ const char *msg = dlerror();
+
+ if (msg == NULL)
+ msg = "Fatal error";
+ errx(1, "%s", msg);
+}
+
+/*
+ * Process a shared object's DYNAMIC section, and save the important
+ * information in its Obj_Entry structure.
+ */
+static void
+digest_dynamic(Obj_Entry *obj)
+{
+ const Elf32_Dyn *dynp;
+ Needed_Entry **needed_tail = &obj->needed;
+ const Elf32_Dyn *dyn_rpath = NULL;
+
+ for (dynp = obj->dynamic; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+
+ case DT_REL:
+ obj->rel = (const Elf32_Rel *) (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_RELSZ:
+ obj->relsize = dynp->d_un.d_val;
+ break;
+
+ case DT_RELENT:
+ assert(dynp->d_un.d_val == sizeof(Elf32_Rel));
+ break;
+
+ case DT_JMPREL:
+ obj->pltrel = (const Elf32_Rel *)
+ (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_PLTRELSZ:
+ obj->pltrelsize = dynp->d_un.d_val;
+ break;
+
+ case DT_RELA:
+ case DT_RELASZ:
+ case DT_RELAENT:
+ assert(0); /* Should never appear for i386 */
+ break;
+
+ case DT_PLTREL:
+ assert(dynp->d_un.d_val == DT_REL); /* For the i386 */
+ break;
+
+ case DT_SYMTAB:
+ obj->symtab = (const Elf32_Sym *)
+ (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_SYMENT:
+ assert(dynp->d_un.d_val == sizeof(Elf32_Sym));
+ break;
+
+ case DT_STRTAB:
+ obj->strtab = (const char *) (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_STRSZ:
+ obj->strsize = dynp->d_un.d_val;
+ break;
+
+ case DT_HASH:
+ {
+ const Elf32_Word *hashtab = (const Elf32_Word *)
+ (obj->relocbase + dynp->d_un.d_ptr);
+ obj->nbuckets = hashtab[0];
+ obj->nchains = hashtab[1];
+ obj->buckets = hashtab + 2;
+ obj->chains = obj->buckets + obj->nbuckets;
+ }
+ break;
+
+ case DT_NEEDED:
+ assert(!obj->rtld);
+ {
+ Needed_Entry *nep = NEW(Needed_Entry);
+ nep->name = dynp->d_un.d_val;
+ nep->obj = NULL;
+ nep->next = NULL;
+
+ *needed_tail = nep;
+ needed_tail = &nep->next;
+ }
+ break;
+
+ case DT_PLTGOT:
+ obj->got = (Elf32_Addr *) (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_TEXTREL:
+ obj->textrel = true;
+ break;
+
+ case DT_SYMBOLIC:
+ obj->symbolic = true;
+ break;
+
+ case DT_RPATH:
+ /*
+ * We have to wait until later to process this, because we
+ * might not have gotten the address of the string table yet.
+ */
+ dyn_rpath = dynp;
+ break;
+
+ case DT_SONAME:
+ /* Not used by the dynamic linker. */
+ break;
+
+ case DT_INIT:
+ obj->init = (void (*)(void)) (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_FINI:
+ obj->fini = (void (*)(void)) (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_DEBUG:
+ /* XXX - not implemented yet */
+ break;
+ }
+ }
+
+ if (dyn_rpath != NULL)
+ obj->rpath = obj->strtab + dyn_rpath->d_un.d_val;
+}
+
+/*
+ * Process a shared object's program header. This is used only for the
+ * main program, when the kernel has already loaded the main program
+ * into memory before calling the dynamic linker. It creates and
+ * returns an Obj_Entry structure.
+ */
+static Obj_Entry *
+digest_phdr(const Elf32_Phdr *phdr, int phnum, caddr_t entry)
+{
+ Obj_Entry *obj = CNEW(Obj_Entry);
+ const Elf32_Phdr *phlimit = phdr + phnum;
+ const Elf32_Phdr *ph;
+ int nsegs = 0;
+
+ for (ph = phdr; ph < phlimit; ph++) {
+ switch (ph->p_type) {
+
+ case PT_PHDR:
+ assert((const Elf32_Phdr *) ph->p_vaddr == phdr);
+ obj->phdr = (const Elf32_Phdr *) ph->p_vaddr;
+ obj->phsize = ph->p_memsz;
+ break;
+
+ case PT_LOAD:
+ assert(nsegs < 2);
+ if (nsegs == 0) { /* First load segment */
+ obj->vaddrbase = trunc_page(ph->p_vaddr);
+ obj->mapbase = (caddr_t) obj->vaddrbase;
+ obj->relocbase = obj->mapbase - obj->vaddrbase;
+ obj->textsize = round_page(ph->p_vaddr + ph->p_memsz) -
+ obj->vaddrbase;
+ } else { /* Last load segment */
+ obj->mapsize = round_page(ph->p_vaddr + ph->p_memsz) -
+ obj->vaddrbase;
+ }
+ nsegs++;
+ break;
+
+ case PT_DYNAMIC:
+ obj->dynamic = (const Elf32_Dyn *) ph->p_vaddr;
+ break;
+ }
+ }
+ assert(nsegs == 2);
+
+ obj->entry = entry;
+ return obj;
+}
+
+static Obj_Entry *
+dlcheck(void *handle)
+{
+ Obj_Entry *obj;
+
+ for (obj = obj_list; obj != NULL; obj = obj->next)
+ if (obj == (Obj_Entry *) handle)
+ break;
+
+ if (obj == NULL || obj->dl_refcount == 0) {
+ _rtld_error("Invalid shared object handle %p", handle);
+ return NULL;
+ }
+ return obj;
+}
+
+/*
+ * Process the special R_386_COPY relocations in the main program. These
+ * copy data from a shared object into a region in the main program's BSS
+ * segment.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf32_Rel *rellim;
+ const Elf32_Rel *rel;
+
+ assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */
+
+ rellim = (const Elf32_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize);
+ for (rel = dstobj->rel; rel < rellim; rel++) {
+ if (ELF32_R_TYPE(rel->r_info) == R_386_COPY) {
+ void *dstaddr;
+ const Elf32_Sym *dstsym;
+ const char *name;
+ unsigned long hash;
+ size_t size;
+ const void *srcaddr;
+ const Elf32_Sym *srcsym;
+ Obj_Entry *srcobj;
+
+ dstaddr = (void *) (dstobj->relocbase + rel->r_offset);
+ dstsym = dstobj->symtab + ELF32_R_SYM(rel->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ hash = elf_hash(name);
+ size = dstsym->st_size;
+
+ for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next)
+ if ((srcsym = symlook_obj(name, hash, srcobj, false)) != NULL)
+ break;
+
+ if (srcobj == NULL) {
+ _rtld_error("Undefined symbol \"%s\" referenced from COPY"
+ " relocation in %s", name, dstobj->path);
+ return -1;
+ }
+
+ srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Hash function for symbol table lookup. Don't even think about changing
+ * this. It is specified by the System V ABI.
+ */
+static unsigned long
+elf_hash(const char *name)
+{
+ const unsigned char *p = (const unsigned char *) name;
+ unsigned long h = 0;
+ unsigned long g;
+
+ while (*p != '\0') {
+ h = (h << 4) + *p++;
+ if ((g = h & 0xf0000000) != 0)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+/*
+ * Find the library with the given name, and return its full pathname.
+ * The returned string is dynamically allocated. Generates an error
+ * message and returns NULL if the library cannot be found.
+ *
+ * If the second argument is non-NULL, then it refers to an already-
+ * loaded shared object, whose library search path will be searched.
+ */
+static char *
+find_library(const char *name, const Obj_Entry *refobj)
+{
+ char *pathname;
+
+ if (strchr(name, '/') != NULL) { /* Hard coded pathname */
+ if (name[0] != '/' && !trust) {
+ _rtld_error("Absolute pathname required for shared object \"%s\"",
+ name);
+ return NULL;
+ }
+ return xstrdup(name);
+ }
+
+ dbg(" Searching for \"%s\"", name);
+
+ if ((refobj != NULL &&
+ (pathname = search_library_path(name, refobj->rpath)) != NULL) ||
+ (pathname = search_library_path(name, ld_library_path)) != NULL ||
+ (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL)
+ return pathname;
+
+ _rtld_error("Shared object \"%s\" not found", name);
+ return NULL;
+}
+
+/*
+ * Given a symbol number in a referencing object, find the corresponding
+ * definition of the symbol. Returns a pointer to the symbol, or NULL if
+ * no definition was found. Returns a pointer to the Obj_Entry of the
+ * defining object via the reference parameter DEFOBJ_OUT.
+ */
+static const Elf32_Sym *
+find_symdef(unsigned long symnum, const Obj_Entry *refobj,
+ const Obj_Entry **defobj_out, bool in_plt)
+{
+ const Elf32_Sym *ref;
+ const Elf32_Sym *strongdef;
+ const Elf32_Sym *weakdef;
+ const Obj_Entry *obj;
+ const Obj_Entry *strongobj;
+ const Obj_Entry *weakobj;
+ const char *name;
+ unsigned long hash;
+
+ ref = refobj->symtab + symnum;
+ name = refobj->strtab + ref->st_name;
+ hash = elf_hash(name);
+
+ if (refobj->symbolic) { /* Look first in the referencing object */
+ const Elf32_Sym *def = symlook_obj(name, hash, refobj, in_plt);
+ if (def != NULL) {
+ *defobj_out = refobj;
+ return def;
+ }
+ }
+
+ /*
+ * Look in all loaded objects. Skip the referencing object, if
+ * we have already searched it. We keep track of the first weak
+ * definition and the first strong definition we encounter. If
+ * we find a strong definition we stop searching, because there
+ * won't be anything better than that.
+ */
+ strongdef = weakdef = NULL;
+ strongobj = weakobj = NULL;
+ for (obj = obj_list; obj != NULL; obj = obj->next) {
+ if (obj != refobj || !refobj->symbolic) {
+ const Elf32_Sym *def = symlook_obj(name, hash, obj, in_plt);
+ if (def != NULL) {
+ if (ELF32_ST_BIND(def->st_info) == STB_WEAK) {
+ if (weakdef == NULL) {
+ weakdef = def;
+ weakobj = obj;
+ }
+ } else {
+ strongdef = def;
+ strongobj = obj;
+ break; /* We are done. */
+ }
+ }
+ }
+ }
+
+ /*
+ * If we still don't have a strong definition, search the dynamic
+ * linker itself, and possibly resolve the symbol from there.
+ * This is how the application links to dynamic linker services
+ * such as dlopen. Only the values listed in the "exports" array
+ * can be resolved from the dynamic linker.
+ */
+ if (strongdef == NULL) {
+ const Elf32_Sym *def = symlook_obj(name, hash, &obj_rtld, in_plt);
+ if (def != NULL && is_exported(def)) {
+ if (ELF32_ST_BIND(def->st_info) == STB_WEAK) {
+ if (weakdef == NULL) {
+ weakdef = def;
+ weakobj = &obj_rtld;
+ }
+ } else {
+ strongdef = def;
+ strongobj = &obj_rtld;
+ }
+ }
+ }
+
+ if (strongdef != NULL) {
+ *defobj_out = strongobj;
+ return strongdef;
+ }
+ if (weakdef != NULL) {
+ *defobj_out = weakobj;
+ return weakdef;
+ }
+
+ _rtld_error("%s: Undefined symbol \"%s\"", refobj->path, name);
+ return NULL;
+}
+
+/*
+ * Initialize the dynamic linker. The argument is the address at which
+ * the dynamic linker has been mapped into memory. The primary task of
+ * this function is to relocate the dynamic linker.
+ */
+static void
+init_rtld(caddr_t mapbase)
+{
+ /* Conjure up an Obj_Entry structure for the dynamic linker. */
+
+ obj_rtld.path = "/usr/libexec/ld-elf.so.1";
+ obj_rtld.rtld = true;
+ obj_rtld.mapbase = mapbase;
+ obj_rtld.relocbase = mapbase;
+ obj_rtld.got = get_got_address();
+ obj_rtld.dynamic = (const Elf32_Dyn *) (obj_rtld.mapbase + obj_rtld.got[0]);
+
+ digest_dynamic(&obj_rtld);
+ assert(obj_rtld.needed == NULL);
+ assert(!obj_rtld.textrel);
+
+ /*
+ * Temporarily put the dynamic linker entry into the object list, so
+ * that symbols can be found.
+ */
+ obj_list = &obj_rtld;
+ obj_tail = &obj_rtld.next;
+
+ relocate_objects(&obj_rtld, true);
+
+ /* Make the object list empty again. */
+ obj_list = NULL;
+ obj_tail = &obj_list;
+}
+
+static bool
+is_exported(const Elf32_Sym *def)
+{
+ func_ptr_type value;
+ const func_ptr_type *p;
+
+ value = (func_ptr_type)(obj_rtld.relocbase + def->st_value);
+ for (p = exports; *p != NULL; p++)
+ if (*p == value)
+ return true;
+ return false;
+}
+
+/*
+ * Given a shared object, traverse its list of needed objects, and load
+ * each of them. Returns 0 on success. Generates an error message and
+ * returns -1 on failure.
+ */
+static int
+load_needed_objects(Obj_Entry *first)
+{
+ Obj_Entry *obj;
+
+ for (obj = first; obj != NULL; obj = obj->next) {
+ Needed_Entry *needed;
+
+ for (needed = obj->needed; needed != NULL; needed = needed->next) {
+ const char *name = obj->strtab + needed->name;
+ char *path = find_library(name, obj);
+
+ if (path == NULL)
+ return -1;
+
+ needed->obj = load_object(path);
+ if (needed->obj == NULL)
+ return -1; /* XXX - cleanup */
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Load a shared object into memory, if it is not already loaded. The
+ * argument must be a string allocated on the heap. This function assumes
+ * responsibility for freeing it when necessary.
+ *
+ * Returns a pointer to the Obj_Entry for the object. Returns NULL
+ * on failure.
+ */
+static Obj_Entry *
+load_object(char *path)
+{
+ Obj_Entry *obj;
+
+ for (obj = obj_list->next; obj != NULL; obj = obj->next)
+ if (strcmp(obj->path, path) == 0)
+ break;
+
+ if (obj == NULL) { /* First use of this object, so we must map it in */
+ int fd;
+
+ if ((fd = open(path, O_RDONLY)) == -1) {
+ _rtld_error("Cannot open \"%s\"", path);
+ return NULL;
+ }
+ obj = map_object(fd);
+ close(fd);
+ if (obj == NULL) {
+ free(path);
+ return NULL;
+ }
+
+ obj->path = path;
+ digest_dynamic(obj);
+
+ *obj_tail = obj;
+ obj_tail = &obj->next;
+
+ dbg(" %p .. %p: %s", obj->mapbase,
+ obj->mapbase + obj->mapsize - 1, obj->path);
+ if (obj->textrel)
+ dbg(" WARNING: %s has impure text", obj->path);
+ } else
+ free(path);
+
+ obj->refcount++;
+ return obj;
+}
+
+static Obj_Entry *
+obj_from_addr(const void *addr)
+{
+ unsigned long endhash;
+ Obj_Entry *obj;
+
+ endhash = elf_hash(END_SYM);
+ for (obj = obj_list; obj != NULL; obj = obj->next) {
+ const Elf32_Sym *endsym;
+
+ if (addr < (void *) obj->mapbase)
+ continue;
+ if ((endsym = symlook_obj(END_SYM, endhash, obj, true)) == NULL)
+ continue; /* No "end" symbol?! */
+ if (addr < (void *) (obj->relocbase + endsym->st_value))
+ return obj;
+ }
+ return NULL;
+}
+
+/*
+ * Relocate newly-loaded shared objects. The argument is a pointer to
+ * the Obj_Entry for the first such object. All objects from the first
+ * to the end of the list of objects are relocated. Returns 0 on success,
+ * or -1 on failure.
+ */
+static int
+relocate_objects(Obj_Entry *first, bool bind_now)
+{
+ Obj_Entry *obj;
+
+ for (obj = first; obj != NULL; obj = obj->next) {
+ const Elf32_Rel *rellim;
+ const Elf32_Rel *rel;
+
+ if (obj->nbuckets == 0 || obj->nchains == 0 || obj->buckets == NULL ||
+ obj->symtab == NULL || obj->strtab == NULL) {
+ _rtld_error("%s: Shared object has no run-time symbol table",
+ obj->path);
+ return -1;
+ }
+
+ if (obj->textrel) {
+ /* There are relocations to the write-protected text segment. */
+ if (mprotect(obj->mapbase, obj->textsize,
+ PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
+ _rtld_error("%s: Cannot write-enable text segment: %s",
+ obj->path, strerror(errno));
+ return -1;
+ }
+ }
+
+ /* Process the non-PLT relocations. */
+ rellim = (const Elf32_Rel *) ((caddr_t) obj->rel + obj->relsize);
+ for (rel = obj->rel; rel < rellim; rel++) {
+ Elf32_Addr *where = (Elf32_Addr *) (obj->relocbase + rel->r_offset);
+
+ switch (ELF32_R_TYPE(rel->r_info)) {
+
+ case R_386_NONE:
+ break;
+
+ case R_386_32:
+ {
+ const Elf32_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF32_R_SYM(rel->r_info), obj, &defobj,
+ false);
+ if (def == NULL)
+ return -1;
+
+ *where += (Elf32_Addr) (defobj->relocbase + def->st_value);
+ }
+ break;
+
+ case R_386_PC32:
+ /*
+ * I don't think the dynamic linker should ever see this
+ * type of relocation. But the binutils-2.6 tools sometimes
+ * generate it.
+ */
+ {
+ const Elf32_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF32_R_SYM(rel->r_info), obj, &defobj,
+ false);
+ if (def == NULL)
+ return -1;
+
+ *where +=
+ (Elf32_Addr) (defobj->relocbase + def->st_value) -
+ (Elf32_Addr) where;
+ }
+ break;
+
+ case R_386_COPY:
+ /*
+ * These are deferred until all other relocations have
+ * been done. All we do here is make sure that the COPY
+ * relocation is not in a shared library. They are allowed
+ * only in executable files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error("%s: Unexpected R_386_COPY relocation"
+ " in shared library", obj->path);
+ return -1;
+ }
+ break;
+
+ case R_386_GLOB_DAT:
+ {
+ const Elf32_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF32_R_SYM(rel->r_info), obj, &defobj,
+ false);
+ if (def == NULL)
+ return -1;
+
+ *where = (Elf32_Addr) (defobj->relocbase + def->st_value);
+ }
+ break;
+
+ case R_386_RELATIVE:
+ *where += (Elf32_Addr) obj->relocbase;
+ break;
+
+ default:
+ _rtld_error("%s: Unsupported relocation type %d"
+ " in non-PLT relocations\n", obj->path,
+ ELF32_R_TYPE(rel->r_info));
+ return -1;
+ }
+ }
+
+ if (obj->textrel) { /* Re-protected the text segment. */
+ if (mprotect(obj->mapbase, obj->textsize,
+ PROT_READ|PROT_EXEC) == -1) {
+ _rtld_error("%s: Cannot write-protect text segment: %s",
+ obj->path, strerror(errno));
+ return -1;
+ }
+ }
+
+ /* Process the PLT relocations. */
+ rellim = (const Elf32_Rel *) ((caddr_t) obj->pltrel + obj->pltrelsize);
+ if (bind_now) {
+ /* Fully resolve procedure addresses now */
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf32_Addr *where = (Elf32_Addr *)
+ (obj->relocbase + rel->r_offset);
+ const Elf32_Sym *def;
+ const Obj_Entry *defobj;
+
+ assert(ELF32_R_TYPE(rel->r_info) == R_386_JMP_SLOT);
+
+ def = find_symdef(ELF32_R_SYM(rel->r_info), obj, &defobj, true);
+ if (def == NULL)
+ return -1;
+
+ *where = (Elf32_Addr) (defobj->relocbase + def->st_value);
+ }
+ } else { /* Just relocate the GOT slots pointing into the PLT */
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf32_Addr *where = (Elf32_Addr *)
+ (obj->relocbase + rel->r_offset);
+ *where += (Elf32_Addr) obj->relocbase;
+ }
+ }
+
+ /*
+ * Set up the magic number and version in the Obj_Entry. These
+ * were checked in the crt1.o from the original ElfKit, so we
+ * set them for backward compatibility.
+ */
+ obj->magic = RTLD_MAGIC;
+ obj->version = RTLD_VERSION;
+
+ /* Set the special GOT entries. */
+ obj->got[1] = (Elf32_Addr) obj;
+ obj->got[2] = (Elf32_Addr) &_rtld_bind_start;
+ }
+
+ return 0;
+}
+
+/*
+ * Cleanup procedure. It will be called (by the atexit mechanism) just
+ * before the process exits.
+ */
+static void
+rtld_exit(void)
+{
+ dbg("rtld_exit()");
+ call_fini_functions(obj_list->next);
+}
+
+static char *
+search_library_path(const char *name, const char *path)
+{
+ size_t namelen = strlen(name);
+ const char *p = path;
+
+ if (p == NULL)
+ return NULL;
+
+ p += strspn(p, ":;");
+ while (*p != '\0') {
+ size_t len = strcspn(p, ":;");
+
+ if (*p == '/' || trust) {
+ char *pathname;
+ const char *dir = p;
+ size_t dirlen = len;
+
+ pathname = xmalloc(dirlen + 1 + namelen + 1);
+ strncpy(pathname, dir, dirlen);
+ pathname[dirlen] = '/';
+ strcpy(pathname + dirlen + 1, name);
+
+ dbg(" Trying \"%s\"", pathname);
+ if (access(pathname, F_OK) == 0) /* We found it */
+ return pathname;
+
+ free(pathname);
+ }
+ p += len;
+ p += strspn(p, ":;");
+ }
+
+ return NULL;
+}
+
+int
+dlclose(void *handle)
+{
+ Obj_Entry *root = dlcheck(handle);
+
+ if (root == NULL)
+ return -1;
+
+ root->dl_refcount--;
+ unref_object_dag(root);
+ if (root->refcount == 0) { /* We are finished with some objects. */
+ Obj_Entry *obj;
+ Obj_Entry **linkp;
+
+ /* Finalize objects that are about to be unmapped. */
+ for (obj = obj_list->next; obj != NULL; obj = obj->next)
+ if (obj->refcount == 0 && obj->fini != NULL)
+ (*obj->fini)();
+
+ /* Unmap all objects that are no longer referenced. */
+ linkp = &obj_list->next;
+ while ((obj = *linkp) != NULL) {
+ if (obj->refcount == 0) {
+ munmap(obj->mapbase, obj->mapsize);
+ free(obj->path);
+ while (obj->needed != NULL) {
+ Needed_Entry *needed = obj->needed;
+ obj->needed = needed->next;
+ free(needed);
+ }
+ *linkp = obj->next;
+ free(obj);
+ } else
+ linkp = &obj->next;
+ }
+ }
+
+ return 0;
+}
+
+const char *
+dlerror(void)
+{
+ char *msg = error_message;
+ error_message = NULL;
+ return msg;
+}
+
+void *
+dlopen(const char *name, int mode)
+{
+ Obj_Entry **old_obj_tail = obj_tail;
+ Obj_Entry *obj;
+
+ if (name == NULL)
+ obj = obj_main;
+ else {
+ char *path = find_library(name, NULL);
+ if (path == NULL)
+ return NULL;
+ obj = load_object(path);
+ if (obj == NULL)
+ return NULL;
+ }
+
+ obj->dl_refcount++;
+ if (*old_obj_tail != NULL) { /* We loaded something new. */
+ assert(*old_obj_tail == obj);
+
+ /* XXX - Clean up properly after an error. */
+ if (load_needed_objects(obj) == -1)
+ return NULL;
+ if (relocate_objects(obj, mode == RTLD_NOW) == -1)
+ return NULL;
+ call_init_functions(obj);
+ }
+ return obj;
+}
+
+void *
+dlsym(void *handle, const char *name)
+{
+ const Obj_Entry *obj;
+ unsigned long hash;
+ const Elf32_Sym *def;
+
+ hash = elf_hash(name);
+
+ if (handle == RTLD_NEXT) {
+ void *retaddr;
+
+ retaddr = __builtin_return_address(0); /* __GNUC__ only */
+ if ((obj = obj_from_addr(retaddr)) == NULL) {
+ _rtld_error("Cannot determine caller's shared object");
+ return NULL;
+ }
+ def = NULL;
+ while ((obj = obj->next) != NULL)
+ if ((def = symlook_obj(name, hash, obj, true)) != NULL)
+ break;
+ } else {
+ if ((obj = dlcheck(handle)) == NULL)
+ return NULL;
+ /*
+ * XXX - This isn't correct. The search should include the whole
+ * DAG rooted at the given object.
+ */
+ def = symlook_obj(name, hash, obj, true);
+ }
+
+ if (def != NULL)
+ return obj->relocbase + def->st_value;
+
+ _rtld_error("Undefined symbol \"%s\"", name);
+ return NULL;
+}
+
+/*
+ * Search the symbol table of a single shared object for a symbol of
+ * the given name. Returns a pointer to the symbol, or NULL if no
+ * definition was found.
+ *
+ * The symbol's hash value is passed in for efficiency reasons; that
+ * eliminates many recomputations of the hash value.
+ */
+static const Elf32_Sym *
+symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj,
+ bool in_plt)
+{
+ unsigned long symnum = obj->buckets[hash % obj->nbuckets];
+
+ while (symnum != STN_UNDEF) {
+ const Elf32_Sym *symp;
+ const char *strp;
+
+ assert(symnum < obj->nchains);
+ symp = obj->symtab + symnum;
+ assert(symp->st_name != 0);
+ strp = obj->strtab + symp->st_name;
+
+ if (strcmp(name, strp) == 0)
+ return symp->st_shndx != SHN_UNDEF ||
+ (!in_plt && symp->st_value != 0 &&
+ ELF32_ST_TYPE(symp->st_info) == STT_FUNC) ? symp : NULL;
+
+ symnum = obj->chains[symnum];
+ }
+
+ return NULL;
+}
+
+static void
+unref_object_dag(Obj_Entry *root)
+{
+ assert(root->refcount != 0);
+ root->refcount--;
+ if (root->refcount == 0) {
+ const Needed_Entry *needed;
+
+ for (needed = root->needed; needed != NULL; needed = needed->next)
+ unref_object_dag(needed->obj);
+ }
+}
+
+/*
+ * Non-mallocing printf, for use by malloc itself.
+ * XXX - This doesn't belong in this module.
+ */
+void
+xprintf(const char *fmt, ...)
+{
+ char buf[256];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ (void)write(1, buf, strlen(buf));
+ va_end(ap);
+}
diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
new file mode 100644
index 0000000..cbd7d8b
--- /dev/null
+++ b/libexec/rtld-elf/rtld.h
@@ -0,0 +1,122 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: rtld.h,v 1.3 1998/03/06 14:00:09 jdp Exp $
+ */
+
+#ifndef RTLD_H /* { */
+#define RTLD_H 1
+
+#include <sys/types.h>
+
+#include <elf.h>
+#include <stddef.h>
+
+#ifndef STANDARD_LIBRARY_PATH
+#define STANDARD_LIBRARY_PATH "/usr/lib/elf:/usr/lib"
+#endif
+
+#define NEW(type) ((type *) xmalloc(sizeof(type)))
+#define CNEW(type) ((type *) xcalloc(sizeof(type)))
+
+/* We might as well do booleans like C++. */
+typedef unsigned char bool;
+#define false 0
+#define true 1
+
+struct Struct_Obj_Entry;
+
+typedef struct Struct_Needed_Entry {
+ struct Struct_Needed_Entry *next;
+ struct Struct_Obj_Entry *obj;
+ unsigned long name; /* Offset of name in string table */
+} Needed_Entry;
+
+/*
+ * Shared object descriptor.
+ *
+ * Items marked with "(%)" are dynamically allocated, and must be freed
+ * when the structure is destroyed.
+ */
+typedef struct Struct_Obj_Entry {
+ /*
+ * These two items have to be set right for compatibility with the
+ * original ElfKit crt1.o.
+ */
+ Elf32_Word magic; /* Magic number (sanity check) */
+ Elf32_Word version; /* Version number of struct format */
+
+ struct Struct_Obj_Entry *next;
+ char *path; /* Pathname of underlying file (%) */
+ int refcount;
+ int dl_refcount; /* Number of times loaded by dlopen */
+
+ /* These items are computed by map_object() or by digest_phdr(). */
+ caddr_t mapbase; /* Base address of mapped region */
+ size_t mapsize; /* Size of mapped region in bytes */
+ size_t textsize; /* Size of text segment in bytes */
+ Elf32_Addr vaddrbase; /* Base address in shared object file */
+ caddr_t relocbase; /* Relocation constant = mapbase - vaddrbase */
+ const Elf32_Dyn *dynamic; /* Dynamic section */
+ caddr_t entry; /* Entry point */
+ const Elf32_Phdr *phdr; /* Program header if it is mapped, else NULL */
+ size_t phsize; /* Size of program header in bytes */
+
+ /* Items from the dynamic section. */
+ Elf32_Addr *got; /* GOT table */
+ const Elf32_Rel *rel; /* Relocation entries */
+ unsigned long relsize; /* Size in bytes of relocation info */
+ const Elf32_Rel *pltrel; /* PLT relocation entries */
+ unsigned long pltrelsize; /* Size in bytes of PLT relocation info */
+ const Elf32_Sym *symtab; /* Symbol table */
+ const char *strtab; /* String table */
+ unsigned long strsize; /* Size in bytes of string table */
+
+ const Elf32_Word *buckets; /* Hash table buckets array */
+ unsigned long nbuckets; /* Number of buckets */
+ const Elf32_Word *chains; /* Hash table chain array */
+ unsigned long nchains; /* Number of chains */
+
+ const char *rpath; /* Search path specified in object */
+ Needed_Entry *needed; /* Shared objects needed by this one (%) */
+
+ void (*init)(void); /* Initialization function to call */
+ void (*fini)(void); /* Termination function to call */
+
+ bool mainprog; /* True if this is the main program */
+ bool rtld; /* True if this is the dynamic linker */
+ bool textrel; /* True if there are relocations to text seg */
+ bool symbolic; /* True if generated with "-Bsymbolic" */
+} Obj_Entry;
+
+#define RTLD_MAGIC 0xd550b87a
+#define RTLD_VERSION 1
+
+extern void _rtld_error(const char *, ...);
+extern Obj_Entry *map_object(int);
+extern void *xcalloc(size_t);
+extern void *xmalloc(size_t);
+extern char *xstrdup(const char *);
+
+#endif /* } */
diff --git a/libexec/rtld-elf/xmalloc.c b/libexec/rtld-elf/xmalloc.c
new file mode 100644
index 0000000..18a073b
--- /dev/null
+++ b/libexec/rtld-elf/xmalloc.c
@@ -0,0 +1,57 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: xmalloc.c,v 1.1 1998/03/05 21:05:54 jdp Exp $
+ */
+
+#include <err.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+void *xmalloc(size_t);
+
+void *
+xcalloc(size_t size)
+{
+ return memset(xmalloc(size), 0, size);
+}
+
+void *
+xmalloc(size_t size)
+{
+ void *p = malloc(size);
+ if (p == NULL)
+ err(1, "Out of memory");
+ return p;
+}
+
+char *
+xstrdup(const char *s)
+{
+ char *p = strdup(s);
+ if (p == NULL)
+ err(1, "Out of memory");
+ return p;
+}
OpenPOWER on IntegriCloud