diff options
author | jdp <jdp@FreeBSD.org> | 1998-03-07 19:24:35 +0000 |
---|---|---|
committer | jdp <jdp@FreeBSD.org> | 1998-03-07 19:24:35 +0000 |
commit | 350f58aa8f1baac341d34d10b452ce3d34d25447 (patch) | |
tree | 5b100ebbd54fbed2d6eff4d0fc9ec38edacfb9c1 | |
parent | 8222bc81a24ad204d76d22477847c9e34a4bbac2 (diff) | |
download | FreeBSD-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>
-rw-r--r-- | libexec/rtld-elf/Makefile | 15 | ||||
-rw-r--r-- | libexec/rtld-elf/debug.c | 52 | ||||
-rw-r--r-- | libexec/rtld-elf/debug.h | 48 | ||||
-rw-r--r-- | libexec/rtld-elf/i386/rtld_start.S | 83 | ||||
-rw-r--r-- | libexec/rtld-elf/malloc.c | 485 | ||||
-rw-r--r-- | libexec/rtld-elf/map_object.c | 250 | ||||
-rw-r--r-- | libexec/rtld-elf/rtld.c | 1291 | ||||
-rw-r--r-- | libexec/rtld-elf/rtld.h | 122 | ||||
-rw-r--r-- | libexec/rtld-elf/xmalloc.c | 57 |
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; +} |