diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /gnu/usr.bin/ld/rtld |
initial import of NetBSD tree
Diffstat (limited to 'gnu/usr.bin/ld/rtld')
-rw-r--r-- | gnu/usr.bin/ld/rtld/Makefile | 25 | ||||
-rw-r--r-- | gnu/usr.bin/ld/rtld/malloc.c | 481 | ||||
-rw-r--r-- | gnu/usr.bin/ld/rtld/md-prologue.c | 39 | ||||
-rw-r--r-- | gnu/usr.bin/ld/rtld/rtld.1 | 183 | ||||
-rw-r--r-- | gnu/usr.bin/ld/rtld/rtld.c | 1476 |
5 files changed, 2204 insertions, 0 deletions
diff --git a/gnu/usr.bin/ld/rtld/Makefile b/gnu/usr.bin/ld/rtld/Makefile new file mode 100644 index 00000000000..538281d964e --- /dev/null +++ b/gnu/usr.bin/ld/rtld/Makefile @@ -0,0 +1,25 @@ +# $NetBSD: Makefile,v 1.11 1995/10/09 00:11:24 pk Exp $ + +PROG= ld.so +SRCS= mdprologue.S rtld.c malloc.c shlib.c etc.c md.c vfprintf.c +#NOMAN= noman +MAN= rtld.1 +LDDIR?= $(.CURDIR)/.. +#PICFLAG=-pic +PICFLAG=-fpic +CFLAGS+=-I$(LDDIR) -I$(.CURDIR) -I$(LDDIR)/$(MACHINE_ARCH) $(PICFLAG) -DRTLD -DLIBC_SCCS +LDFLAGS+=-Bshareable -Bsymbolic -assert nosymbolic +ASFLAGS+=-k +LDADD+= -lc_pic +BINDIR= /usr/libexec +MLINKS= rtld.1 ld.so.1 + +.PATH: $(LDDIR) $(LDDIR)/$(MACHINE_ARCH) ${.CURDIR}/../../../../lib/libc/stdio + +$(PROG): + $(LD) -o $(PROG) $(LDFLAGS) $(OBJS) $(LDADD) + +.S.o: + ${CPP} ${.IMPSRC} | ${AS} ${ASFLAGS} -o ${.TARGET} - + +.include <bsd.prog.mk> diff --git a/gnu/usr.bin/ld/rtld/malloc.c b/gnu/usr.bin/ld/rtld/malloc.c new file mode 100644 index 00000000000..c48415b48e4 --- /dev/null +++ b/gnu/usr.bin/ld/rtld/malloc.c @@ -0,0 +1,481 @@ +/* + * 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.1 1995/10/18 08:40:58 deraadt 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(DEBUG) || defined(RCHECK) +#define ASSERT(p) if (!(p)) botch("p") +#include <stdio.h> +static +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 + +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 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 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 +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/gnu/usr.bin/ld/rtld/md-prologue.c b/gnu/usr.bin/ld/rtld/md-prologue.c new file mode 100644 index 00000000000..dae455e0237 --- /dev/null +++ b/gnu/usr.bin/ld/rtld/md-prologue.c @@ -0,0 +1,39 @@ +/* + * rtld entry pseudo code - turn into assembler and tweak it + */ + +#include <sys/types.h> +#include <sys/types.h> +#include <a.out.h> +#include "link.h" +#include "md.h" + +extern long _GOT_[]; +extern void (*rtld)(); +extern void (*binder())(); + +void +rtld_entry(version, crtp) +int version; +struct crt *crtp; +{ + register struct link_dynamic *dp; + register void (*f)(); + + /* __DYNAMIC is first entry in GOT */ + dp = (struct link_dynamic *) (_GOT_[0]+crtp->crt_ba); + + f = (void (*)())((long)rtld + crtp->crt_ba); + (*f)(version, crtp, dp); +} + +void +binder_entry() +{ + extern int PC; + struct jmpslot *sp; + void (*func)(); + + func = binder(PC, sp->reloc_index & 0x003fffff); + (*func)(); +} diff --git a/gnu/usr.bin/ld/rtld/rtld.1 b/gnu/usr.bin/ld/rtld/rtld.1 new file mode 100644 index 00000000000..1b4e7320632 --- /dev/null +++ b/gnu/usr.bin/ld/rtld/rtld.1 @@ -0,0 +1,183 @@ +.\" $NetBSD: rtld.1,v 1.2 1995/10/08 23:43:28 pk Exp $ +.\" +.\" Copyright (c) 1995 Paul Kranenburg +.\" 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 Paul Kranenburg. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" 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. +.\" +.Dd June 27, 1995 +.Dt RTLD 1 +.Os NetBSD +.Sh NAME +.Nm ld.so +.Nd run-time link-editor +.Sh DESCRIPTION +.Nm +is a self-contained, position independent program image providing run-time +support for loading and link-editing shared objects into a process' +address space. It uses the data structures +.Po +see +.Xr link 5 +.Pc +contained within dynamically linked programs to determine which shared +libraries are needed and loads them at a convenient virtual address +using the +.Xr mmap 2 +system call. +.Pp +After all shared libraries have been succesfully loaded, +.Nm +proceeds to resolve external references from both the main program and +all objects loaded. A mechanism is provided for initialisation routines +to be called, on a per-object basis, giving a shared object an opportunity +to perfrom any extra set-up, before execution of the program proper begins. +This is useful for C++ libraries that contain static constrictors. +.Pp +.Nm +is itself a shared object that is initially loaded by the startup module +.Em crt0 . +Since +.Xr a.out 5 +formats do not provide easy access to the file header from within a running +process, +.Em crt0 +uses the special symbol +.Va _DYNAMIC +to determine whether a program is in fact dynamically linked or not. Whenever +the linker +.Xr ld 1 +has relocated this symbol to a location other then 0, +.Em crt0 +assumes the services of +.Nm +are needed +.Po +see +.Xr link 5 +for details +.Pc \&. +.Em crt0 +passes control to +.Nm rtld Ns 's +entry point before the program's +.Fn main +routine is called. Thus, +.Nm +can complete the link-editing process before the dynamic program calls upon +services of any dynamic library. +.Pp +To quickly locate the required shared objects in the filesystem, +.Nm +may use a +.Dq hints +file, prepared by the +.Xr ldconfig 8 +utility, in which the full path specification of the shared objects can be +looked up by hashing on the 3-tuple +.Ao +library-name, major-version-number, minor-version-number +.Ac \&. +.Pp +.Nm +recognises a number of environment variables that can be used to modify +its behaviour as follows: +.Pp +.Bl -tag -width "LD_TRACE_LOADED_OBJECTS_PROGNAME" +.It Ev LD_LIBRARY_PATH +A colon separated list of directories, overriding the default search path +for shared libraries. +.It Ev LD_WARN_NON_PURE_CODE +When set, issue a warning whenever a link-editing operation requires +modification of the text segment of some loaded object. This is usually +indicative of an incorrectly built library. +.It Ev LD_SUPPRESS_WARNINGS +When set, no warning messages of any kind are issued. Normally, a warning +is given if satisfactorily versioned library could not be found. +.It Ev LD_TRACE_LOADED_OBJECTS +When set, causes +.Nm +to exit after loading the shared objects and printing a summary which includes +the absolute pathnames of all objects, to standard output. +.It Ev LD_TRACE_LOADED_OBJECTS_FMT1 +.It Ev LD_TRACE_LOADED_OBJECTS_FMT2 +When set, these variables are interpreted as format strings a la +.Xr printf 3 +to customize the trace output and are used by +.Xr ldd 1 's +.Fl f +option and allows +.Xr ldd 1 +to be operated as a filter more conveniently. +The following conversions can be used: +.Bl -tag -indent "LD_TRACE_LOADED_OBJECTS_FMT1 " -width "xxxx" +.It \&%a +The main program's name +.Po also known as +.Dq __progname +.Pc . +.It \&%A +The value of the environment variable +.Ev LD_TRACE_LOADED_OBJECTS_PROGNAME +.It \&%o +The libary name. +.It \&%m +The library's major version numer. +.It \&%n +The library's minor version numer. +.It \&%p +The full pathname as determined by +.Nm rtld Ns 's +library search rules. +.It \&%x +The library's load address. +.El +.Pp +Additionally, +.Sy \en +and +.Sy \et +are recognised and have their usual meaning. +.It Ev LD_NO_INTERN_SEARCH +When set, +.Nm +does not process any internal search paths that were recorded in the +executable. +.It Ev LD_NOSTD_PATH +When set, do not include a set of built-in standard directory paths for +searching. This might be useful when running on a system with a completely +non-standard filesystem layout. +.El +.Pp +.Sh FILES +/var/run/ld.so.hints +.Pp +.Sh SEE ALSO +.Xr ld 1 +.Xr ldconfig 8 +.Xr link 5 +.Sh HISTORY +The shared library model employed first appeared in SunOS 4.0 diff --git a/gnu/usr.bin/ld/rtld/rtld.c b/gnu/usr.bin/ld/rtld/rtld.c new file mode 100644 index 00000000000..72194af2d7d --- /dev/null +++ b/gnu/usr.bin/ld/rtld/rtld.c @@ -0,0 +1,1476 @@ +/* $NetBSD: rtld.c,v 1.40 1995/10/09 09:24:59 pk Exp $ */ +/* + * Copyright (c) 1993 Paul Kranenburg + * 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 Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * 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. + * + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/errno.h> +#include <sys/mman.h> +#ifndef MAP_COPY +#define MAP_COPY MAP_PRIVATE +#endif +#include <err.h> +#include <dlfcn.h> +#include <fcntl.h> +#include <a.out.h> +#include <stab.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "ld.h" + +#ifndef MAP_ANON +#define MAP_ANON 0 +#define anon_open() do { \ + if ((anon_fd = open("/dev/zero", O_RDWR, 0)) == -1) \ + err("open: %s", "/dev/zero"); \ +} while (0) +#define anon_close() do { \ + (void)close(anon_fd); \ + anon_fd = -1; \ +} while (0) +#else +#define anon_open() +#define anon_close() +#endif + +/* + * Loader private data, hung off <so_map>->som_spd + */ +struct somap_private { + int spd_version; + struct so_map *spd_parent; + int spd_refcount; + int spd_flags; +#define RTLD_MAIN 1 +#define RTLD_RTLD 2 +#define RTLD_DL 4 + +#ifdef SUN_COMPAT + long spd_offset; /* Correction for Sun main programs */ +#endif +}; + +#define LM_PRIVATE(smp) ((struct somap_private *)(smp)->som_spd) + +#ifdef SUN_COMPAT +#define LM_OFFSET(smp) (LM_PRIVATE(smp)->spd_offset) +#else +#define LM_OFFSET(smp) (0) +#endif + +/* Base address for section_dispatch_table entries */ +#define LM_LDBASE(smp) (smp->som_addr + LM_OFFSET(smp)) + +/* Start of text segment */ +#define LM_TXTADDR(smp) (smp->som_addr == (caddr_t)0 ? PAGSIZ : 0) + +/* Start of run-time relocation_info */ +#define LM_REL(smp) ((struct relocation_info *) \ + (smp->som_addr + LM_OFFSET(smp) + LD_REL((smp)->som_dynamic))) + +/* Start of symbols */ +#define LM_SYMBOL(smp, i) ((struct nzlist *) \ + (smp->som_addr + LM_OFFSET(smp) + LD_SYMBOL((smp)->som_dynamic) + \ + i * (LD_VERSION_NZLIST_P(smp->som_dynamic->d_version) ? \ + sizeof(struct nzlist) : sizeof(struct nlist)))) + +/* Start of hash table */ +#define LM_HASH(smp) ((struct rrs_hash *) \ + ((smp)->som_addr + LM_OFFSET(smp) + LD_HASH((smp)->som_dynamic))) + +/* Start of strings */ +#define LM_STRINGS(smp) ((char *) \ + ((smp)->som_addr + LM_OFFSET(smp) + LD_STRINGS((smp)->som_dynamic))) + +/* Start of search paths */ +#define LM_PATHS(smp) ((char *) \ + ((smp)->som_addr + LM_OFFSET(smp) + LD_PATHS((smp)->som_dynamic))) + +/* End of text */ +#define LM_ETEXT(smp) ((char *) \ + ((smp)->som_addr + LM_TXTADDR(smp) + LD_TEXTSZ((smp)->som_dynamic))) + +/* PLT is in data segment, so don't use LM_OFFSET here */ +#define LM_PLT(smp) ((jmpslot_t *) \ + ((smp)->som_addr + LD_PLT((smp)->som_dynamic))) + +/* Parent of link map */ +#define LM_PARENT(smp) (LM_PRIVATE(smp)->spd_parent) + +static char __main_progname[] = "main"; +static char *main_progname = __main_progname; +static char us[] = "/usr/libexec/ld.so"; + +char **environ; +char *__progname = us; +int errno; + +static uid_t uid, euid; +static gid_t gid, egid; +static int careful; +static int anon_fd = -1; + +struct so_map *link_map_head, *main_map; +struct so_map **link_map_tail = &link_map_head; +struct rt_symbol *rt_symbol_head; + +static char *ld_library_path; +static char *ld_preload_path; +static int no_intern_search; +static int ld_suppress_warnings; +static int ld_warn_non_pure_code; + +static int ld_tracing; + +static void *__dlopen __P((char *, int)); +static int __dlclose __P((void *)); +static void *__dlsym __P((void *, char *)); +static int __dlctl __P((void *, int, void *)); +static void __dlexit __P((void)); + +static struct ld_entry ld_entry = { + __dlopen, __dlclose, __dlsym, __dlctl, __dlexit +}; + + void xprintf __P((char *, ...)); + int rtld __P((int, struct crt_ldso *, struct _dynamic *)); + void binder_entry __P((void)); + long binder __P((jmpslot_t *)); +static int load_subs __P((struct so_map *)); +static struct so_map *map_object __P((char *, struct sod *, + struct so_map *)); +static struct so_map *alloc_link_map __P(( char *, struct sod *, + struct so_map *, caddr_t, + struct _dynamic *)); +static inline void check_text_reloc __P(( struct relocation_info *, + struct so_map *, + caddr_t)); +static void init_maps __P((struct so_map *)); +static void reloc_map __P((struct so_map *)); +static void reloc_copy __P((struct so_map *)); +static void call_map __P((struct so_map *, char *)); +static char *rtfindlib __P((char *, int, int, int *, char *)); +static struct nzlist *lookup __P((char *, struct so_map **, int)); +static inline struct rt_symbol *lookup_rts __P((char *)); +static struct rt_symbol *enter_rts __P((char *, long, int, caddr_t, + long, struct so_map *)); +static void maphints __P((void)); +static void unmaphints __P((void)); + +static void preload __P((char *)); +static void ld_trace __P((struct so_map *)); + +static inline int +strcmp (register const char *s1, register const char *s2) +{ + while (*s1 == *s2++) + if (*s1++ == 0) + return (0); + return (*(unsigned char *)s1 - *(unsigned char *)--s2); +} + +#include "md-static-funcs.c" + +/* + * Called from assembler stub that has set up crtp (passed from crt0) + * and dp (our __DYNAMIC). + */ +int +rtld(version, crtp, dp) + int version; + struct crt_ldso *crtp; + struct _dynamic *dp; +{ + int n; + int nreloc; /* # of ld.so relocations */ + struct relocation_info *reloc; + struct so_debug *ddp; + struct so_map *smp; + + /* Check version */ + if ( version != CRT_VERSION_BSD_2 && + version != CRT_VERSION_BSD_3 && + version != CRT_VERSION_BSD_4 && + version != CRT_VERSION_SUN) + return -1; + + /* Fixup __DYNAMIC structure */ + (long)dp->d_un.d_sdt += crtp->crt_ba; + + /* Divide by hand to avoid possible use of library division routine */ + for ( nreloc = 0, n = LD_RELSZ(dp); + n > 0; + n -= sizeof(struct relocation_info) ) nreloc++; + + + /* Relocate ourselves */ + for ( reloc = (struct relocation_info *)(LD_REL(dp) + crtp->crt_ba); + nreloc; + nreloc--, reloc++) { + + register long addr = reloc->r_address + crtp->crt_ba; + + md_relocate_simple(reloc, crtp->crt_ba, addr); + } + + if (version >= CRT_VERSION_BSD_4) + __progname = crtp->crt_ldso; + + if (version >= CRT_VERSION_BSD_3) + main_progname = crtp->crt_prog; + + /* Setup out (private) environ variable */ + environ = crtp->crt_ep; + + /* Get user and group identifiers */ + uid = getuid(); euid = geteuid(); + gid = getgid(); egid = getegid(); + + careful = (uid != euid) || (gid != egid); + + if (careful) { + unsetenv("LD_LIBRARY_PATH"); + unsetenv("LD_PRELOAD"); + } + + /* Setup directory search */ + ld_library_path = getenv("LD_LIBRARY_PATH"); + add_search_path(ld_library_path); + if (getenv("LD_NOSTD_PATH") == NULL) + std_search_path(); + + ld_suppress_warnings = getenv("LD_SUPPRESS_WARNINGS") != NULL; + ld_warn_non_pure_code = getenv("LD_WARN_NON_PURE_CODE") != NULL; + + no_intern_search = careful || getenv("LD_NO_INTERN_SEARCH") != 0; + + anon_open(); + + /* + * Init object administration. We start off with a map description + * for `main' and `rtld'. + */ + smp = alloc_link_map(main_progname, (struct sod *)0, (struct so_map *)0, + (caddr_t)0, crtp->crt_dp); + LM_PRIVATE(smp)->spd_refcount++; + LM_PRIVATE(smp)->spd_flags |= RTLD_MAIN; + + smp = alloc_link_map(us, (struct sod *)0, (struct so_map *)0, + (caddr_t)crtp->crt_ba, dp); + LM_PRIVATE(smp)->spd_refcount++; + LM_PRIVATE(smp)->spd_flags |= RTLD_RTLD; + + /* Handle LD_PRELOAD's here */ + ld_preload_path = getenv("LD_PRELOAD"); + if (ld_preload_path != NULL) + preload(ld_preload_path); + + /* Load subsidiary objects into the process address space */ + ld_tracing = (int)getenv("LD_TRACE_LOADED_OBJECTS"); + load_subs(link_map_head); + if (ld_tracing) { + ld_trace(link_map_head); + exit(0); + } + + init_maps(link_map_head); + + /* Fill in some field in main's __DYNAMIC structure */ + if (version >= CRT_VERSION_BSD_4) + crtp->crt_ldentry = &ld_entry; + else + crtp->crt_dp->d_entry = &ld_entry; + + crtp->crt_dp->d_un.d_sdt->sdt_loaded = link_map_head->som_next; + + ddp = crtp->crt_dp->d_debug; + ddp->dd_cc = rt_symbol_head; + if (ddp->dd_in_debugger) { + caddr_t addr = (caddr_t)((long)crtp->crt_bp & (~(PAGSIZ - 1))); + + /* Set breakpoint for the benefit of debuggers */ + if (mprotect(addr, PAGSIZ, + PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { + err(1, "Cannot set breakpoint (%s)", main_progname); + } + md_set_breakpoint((long)crtp->crt_bp, (long *)&ddp->dd_bpt_shadow); + if (mprotect(addr, PAGSIZ, PROT_READ|PROT_EXEC) == -1) { + err(1, "Cannot re-protect breakpoint (%s)", + main_progname); + } + + ddp->dd_bpt_addr = crtp->crt_bp; + if (link_map_head) + ddp->dd_sym_loaded = 1; + } + + /* Close the hints file */ + unmaphints(); + + /* Close our file descriptor */ + (void)close(crtp->crt_ldfd); + anon_close(); + return 0; +} + + +static int +load_subs(smp) + struct so_map *smp; +{ + + for (; smp; smp = smp->som_next) { + struct sod *sodp; + long next = 0; + + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + + if (smp->som_dynamic) + next = LD_NEED(smp->som_dynamic); + + while (next) { + struct so_map *newmap; + char *name; + + sodp = (struct sod *)(LM_LDBASE(smp) + next); + name = (char *)(sodp->sod_name + LM_LDBASE(smp)); + + if ((newmap = map_object(name, sodp, smp)) == NULL) { + if (!ld_tracing) { + char *fmt = sodp->sod_library ? + "%s: lib%s.so.%d.%d" : + "%s: %s"; + err(1, fmt, main_progname, name, + sodp->sod_major, + sodp->sod_minor); + } + newmap = alloc_link_map(NULL, sodp, smp, 0, 0); + } + LM_PRIVATE(newmap)->spd_refcount++; + LM_PARENT(newmap) = smp; + next = sodp->sod_next; + } + } + return 0; +} + +void +ld_trace(smp) + struct so_map *smp; +{ + char *fmt1, *fmt2, *fmt, *main_local; + int c; + + if ((main_local = getenv("LD_TRACE_LOADED_OBJECTS_PROGNAME")) == NULL) + main_local = ""; + + if ((fmt1 = getenv("LD_TRACE_LOADED_OBJECTS_FMT1")) == NULL) + fmt1 = "\t-l%o.%m => %p (%x)\n"; + + if ((fmt2 = getenv("LD_TRACE_LOADED_OBJECTS_FMT2")) == NULL) + fmt2 = "\t%o (%x)\n"; + + for (; smp; smp = smp->som_next) { + struct sod *sodp; + char *name, *path; + + if ((sodp = smp->som_sod) == NULL) + continue; + + name = sodp->sod_name + LM_LDBASE(LM_PARENT(smp)); + + if ((path = smp->som_path) == NULL) + path = "not found"; + + fmt = sodp->sod_library ? fmt1 : fmt2; + while ((c = *fmt++) != '\0') { + switch (c) { + default: + putchar(c); + continue; + case '\\': + switch (c = *fmt) { + case '\0': + continue; + case 'n': + putchar('\n'); + break; + case 't': + putchar('\t'); + break; + } + break; + case '%': + switch (c = *fmt) { + case '\0': + continue; + case '%': + default: + putchar(c); + break; + case 'A': + printf("%s", main_local); + break; + case 'a': + printf("%s", main_progname); + break; + case 'o': + printf("%s", name); + break; + case 'm': + printf("%d", sodp->sod_major); + break; + case 'n': + printf("%d", sodp->sod_minor); + break; + case 'p': + printf("%s", path); + break; + case 'x': + printf("%p", smp->som_addr); + break; + } + break; + } + ++fmt; + } + } +} + +/* + * Allocate a new link map for shared object NAME loaded at ADDR as a + * result of the presence of link object LOP in the link map PARENT. + */ +static struct so_map * +alloc_link_map(path, sodp, parent, addr, dp) + char *path; + struct sod *sodp; + struct so_map *parent; + caddr_t addr; + struct _dynamic *dp; +{ + struct so_map *smp; + struct somap_private *smpp; + + smpp = (struct somap_private *)xmalloc(sizeof(struct somap_private)); + smp = (struct so_map *)xmalloc(sizeof(struct so_map)); + smp->som_next = NULL; + *link_map_tail = smp; + link_map_tail = &smp->som_next; + + /*smp->som_sodbase = 0; NOT USED */ + smp->som_write = 0; + smp->som_addr = addr; + smp->som_path = path?strdup(path):NULL; + smp->som_sod = sodp; + smp->som_dynamic = dp; + smp->som_spd = (caddr_t)smpp; + +/*XXX*/ if (addr == 0) main_map = smp; + + smpp->spd_refcount = 0; + smpp->spd_flags = 0; + smpp->spd_parent = parent; + +#ifdef SUN_COMPAT + smpp->spd_offset = + (addr==0 && dp && dp->d_version==LD_VERSION_SUN) ? PAGSIZ : 0; +#endif + return smp; +} + +/* + * Map object identified by link object SODP which was found + * in link map SMP. + */ +static struct so_map * +map_object(name, sodp, smp) + char *name; + struct sod *sodp; + struct so_map *smp; +{ + struct _dynamic *dp; + char *path, *ipath; + int fd; + caddr_t addr; + struct exec hdr; + int usehints = 0; + struct so_map *p; + + if (sodp->sod_library) { + usehints = 1; +again: + if (smp == NULL || no_intern_search || + LD_PATHS(smp->som_dynamic) == 0) { + ipath = NULL; + } else { + ipath = LM_PATHS(smp); + add_search_path(ipath); + } + + path = rtfindlib(name, sodp->sod_major, + sodp->sod_minor, &usehints, ipath); + if (ipath) + remove_search_path(ipath); + + if (path == NULL) { + errno = ENOENT; + return NULL; + } + } else { + if (careful && *name != '/') { + errno = EACCES; + return NULL; + } + path = name; + } + + /* Check if already loaded */ + for (p = link_map_head; p; p = p->som_next) + if (p->som_path && strcmp(p->som_path, path) == 0) + break; + + if (p != NULL) + return p; + + if ((fd = open(path, O_RDONLY, 0)) == -1) { + if (usehints) { + usehints = 0; + goto again; + } + return NULL; + } + + if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { + (void)close(fd); + /*errno = x;*/ + return NULL; + } + + if (N_BADMAG(hdr)) { + (void)close(fd); + errno = EFTYPE; + return NULL; + } + + if ((addr = mmap(0, hdr.a_text + hdr.a_data + hdr.a_bss, + PROT_READ|PROT_EXEC, + MAP_COPY, fd, 0)) == (caddr_t)-1) { + (void)close(fd); + return NULL; + } + + if (mprotect(addr + hdr.a_text, hdr.a_data, + PROT_READ|PROT_WRITE|PROT_EXEC) != 0) { + (void)close(fd); + return NULL; + } + + if (mmap(addr + hdr.a_text + hdr.a_data, hdr.a_bss, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_ANON|MAP_COPY|MAP_FIXED, + anon_fd, 0) == (caddr_t)-1) { + (void)close(fd); + return NULL; + } + + (void)close(fd); + + /* Assume _DYNAMIC is the first data item */ + dp = (struct _dynamic *)(addr+hdr.a_text); + + /* Fixup __DYNAMIC structure */ + (long)dp->d_un.d_sdt += (long)addr; + + return alloc_link_map(path, sodp, smp, addr, dp); +} + +void +init_maps(head) + struct so_map *head; +{ + struct so_map *smp; + + /* Relocate all loaded objects according to their RRS segments */ + for (smp = head; smp; smp = smp->som_next) { + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + reloc_map(smp); + } + + /* Copy any relocated initialized data. */ + for (smp = head; smp; smp = smp->som_next) { + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + reloc_copy(smp); + } + + /* Call any object initialization routines. */ + for (smp = head; smp; smp = smp->som_next) { + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + call_map(smp, ".init"); + call_map(smp, "__init"); + } +} + +static inline void +check_text_reloc(r, smp, addr) + struct relocation_info *r; + struct so_map *smp; + caddr_t addr; +{ + char *sym; + + if (addr >= LM_ETEXT(smp)) + return; + + if (RELOC_EXTERN_P(r)) + sym = LM_STRINGS(smp) + + LM_SYMBOL(smp, RELOC_SYMBOL(r))->nz_strx; + else + sym = ""; + + if (ld_warn_non_pure_code && !ld_suppress_warnings) + warnx("warning: non pure code in %s at %x (%s)", + smp->som_path, r->r_address, sym); + + if (smp->som_write == 0 && + mprotect(smp->som_addr + LM_TXTADDR(smp), + LD_TEXTSZ(smp->som_dynamic), + PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { + + err(1, "Cannot enable writes to %s:%s", + main_progname, smp->som_path); + } + + smp->som_write = 1; +} + +static void +reloc_map(smp) + struct so_map *smp; +{ + struct _dynamic *dp = smp->som_dynamic; + struct relocation_info *r = LM_REL(smp); + struct relocation_info *rend = r + LD_RELSZ(dp)/sizeof(*r); + long symbolbase = (long)LM_SYMBOL(smp, 0); + char *stringbase = LM_STRINGS(smp); + int symsize = LD_VERSION_NZLIST_P(dp->d_version) ? + sizeof(struct nzlist) : + sizeof(struct nlist); + + if (LD_PLTSZ(dp)) + md_fix_jmpslot(LM_PLT(smp), + (long)LM_PLT(smp), (long)binder_entry); + + for (; r < rend; r++) { + char *sym; + caddr_t addr = smp->som_addr + r->r_address; + + check_text_reloc(r, smp, addr); + + if (RELOC_EXTERN_P(r)) { + struct so_map *src_map = NULL; + struct nzlist *p, *np; + long relocation = md_get_addend(r, addr); + + if (RELOC_LAZY_P(r)) + continue; + + p = (struct nzlist *) + (symbolbase + symsize * RELOC_SYMBOL(r)); + + if (p->nz_type == (N_SETV + N_EXT)) + src_map = smp; + + sym = stringbase + p->nz_strx; + + np = lookup(sym, &src_map, 0/*XXX-jumpslots!*/); + if (np == NULL) + errx(1, "Undefined symbol \"%s\" in %s:%s\n", + sym, main_progname, smp->som_path); + + /* + * Found symbol definition. + * If it's in a link map, adjust value + * according to the load address of that map. + * Otherwise it's a run-time allocated common + * whose value is already up-to-date. + */ + relocation += np->nz_value; + if (src_map) + relocation += (long)src_map->som_addr; + + if (RELOC_PCREL_P(r)) + relocation -= (long)smp->som_addr; + + if (RELOC_COPY_P(r) && src_map) { + (void)enter_rts(sym, + (long)addr, + N_DATA + N_EXT, + src_map->som_addr + np->nz_value, + np->nz_size, src_map); + continue; + } + md_relocate(r, relocation, addr, 0); + + } else { + md_relocate(r, +#ifdef SUN_COMPAT + md_get_rt_segment_addend(r, addr) +#else + md_get_addend(r, addr) +#endif + + (long)smp->som_addr, addr, 0); + } + + } + + if (smp->som_write) { + if (mprotect(smp->som_addr + LM_TXTADDR(smp), + LD_TEXTSZ(smp->som_dynamic), + PROT_READ|PROT_EXEC) == -1) { + + err(1, "Cannot disable writes to %s:%s\n", + main_progname, smp->som_path); + } + smp->som_write = 0; + } +} + +static void +reloc_copy(smp) + struct so_map *smp; +{ + struct rt_symbol *rtsp; + + for (rtsp = rt_symbol_head; rtsp; rtsp = rtsp->rt_next) + if ((rtsp->rt_smp == NULL || rtsp->rt_smp == smp) && + rtsp->rt_sp->nz_type == N_DATA + N_EXT) { + bcopy(rtsp->rt_srcaddr, (caddr_t)rtsp->rt_sp->nz_value, + rtsp->rt_sp->nz_size); + } +} + +static void +call_map(smp, sym) + struct so_map *smp; + char *sym; +{ + struct so_map *src_map = smp; + struct nzlist *np; + + np = lookup(sym, &src_map, 1); + if (np) + (*(void (*) __P((void)))(src_map->som_addr + np->nz_value))(); +} + +/* + * Run-time common symbol table. + */ + +#define RTC_TABSIZE 57 +static struct rt_symbol *rt_symtab[RTC_TABSIZE]; + +/* + * Compute hash value for run-time symbol table + */ +static inline int +hash_string(key) + char *key; +{ + register char *cp; + register int k; + + cp = key; + k = 0; + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + return k; +} + +/* + * Lookup KEY in the run-time common symbol table. + */ + +static inline struct rt_symbol * +lookup_rts(key) + char *key; +{ + register int hashval; + register struct rt_symbol *rtsp; + + /* Determine which bucket. */ + + hashval = hash_string(key) % RTC_TABSIZE; + + /* Search the bucket. */ + + for (rtsp = rt_symtab[hashval]; rtsp; rtsp = rtsp->rt_link) + if (strcmp(key, rtsp->rt_sp->nz_name) == 0) + return rtsp; + + return NULL; +} + +static struct rt_symbol * +enter_rts(name, value, type, srcaddr, size, smp) + char *name; + long value; + int type; + caddr_t srcaddr; + long size; + struct so_map *smp; +{ + register int hashval; + register struct rt_symbol *rtsp, **rpp; + + /* Determine which bucket */ + hashval = hash_string(name) % RTC_TABSIZE; + + /* Find end of bucket */ + for (rpp = &rt_symtab[hashval]; *rpp; rpp = &(*rpp)->rt_link) + ; + + /* Allocate new common symbol */ + rtsp = (struct rt_symbol *)malloc(sizeof(struct rt_symbol)); + rtsp->rt_sp = (struct nzlist *)malloc(sizeof(struct nzlist)); + rtsp->rt_sp->nz_name = strdup(name); + rtsp->rt_sp->nz_value = value; + rtsp->rt_sp->nz_type = type; + rtsp->rt_sp->nz_size = size; + rtsp->rt_srcaddr = srcaddr; + rtsp->rt_smp = smp; + rtsp->rt_link = NULL; + + /* Link onto linear list as well */ + rtsp->rt_next = rt_symbol_head; + rt_symbol_head = rtsp; + + *rpp = rtsp; + + return rtsp; +} + + +/* + * Lookup NAME in the link maps. The link map producing a definition + * is returned in SRC_MAP. If SRC_MAP is not NULL on entry the search is + * confined to that map. If STRONG is set, the symbol returned must + * have a proper type (used by binder()). + */ +static struct nzlist * +lookup(name, src_map, strong) + char *name; + struct so_map **src_map; /* IN/OUT */ + int strong; +{ + long common_size = 0; + struct so_map *smp; + struct rt_symbol *rtsp; + + if ((rtsp = lookup_rts(name)) != NULL) + return rtsp->rt_sp; + + /* + * Search all maps for a definition of NAME + */ + for (smp = link_map_head; smp; smp = smp->som_next) { + int buckets; + long hashval; + struct rrs_hash *hp; + char *cp; + struct nzlist *np; + + /* Some local caching */ + long symbolbase; + struct rrs_hash *hashbase; + char *stringbase; + int symsize; + + if (*src_map && smp != *src_map) + continue; + + if ((buckets = LD_BUCKETS(smp->som_dynamic)) == 0) + continue; + + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + +restart: + /* + * Compute bucket in which the symbol might be found. + */ + for (hashval = 0, cp = name; *cp; cp++) + hashval = (hashval << 1) + *cp; + + hashval = (hashval & 0x7fffffff) % buckets; + + hashbase = LM_HASH(smp); + hp = hashbase + hashval; + if (hp->rh_symbolnum == -1) + /* Nothing in this bucket */ + continue; + + symbolbase = (long)LM_SYMBOL(smp, 0); + stringbase = LM_STRINGS(smp); + symsize = LD_VERSION_NZLIST_P(smp->som_dynamic->d_version)? + sizeof(struct nzlist) : + sizeof(struct nlist); + while (hp) { + np = (struct nzlist *) + (symbolbase + hp->rh_symbolnum * symsize); + cp = stringbase + np->nz_strx; + if (strcmp(cp, name) == 0) + break; + if (hp->rh_next == 0) + hp = NULL; + else + hp = hashbase + hp->rh_next; + } + if (hp == NULL) + /* Nothing in this bucket */ + continue; + + /* + * We have a symbol with the name we're looking for. + */ + if (np->nz_type == N_INDR+N_EXT) { + /* + * Next symbol gives the aliased name. Restart + * search with new name and confine to this map. + */ + name = stringbase + (++np)->nz_strx; + *src_map = smp; + goto restart; + } + + if (np->nz_value == 0) + /* It's not a definition */ + continue; + + if (np->nz_type == N_UNDF+N_EXT && np->nz_value != 0) { + if (np->nz_other == AUX_FUNC) { + /* It's a weak function definition */ + if (strong) + continue; + } else { + /* It's a common, note value and continue search */ + if (common_size < np->nz_value) + common_size = np->nz_value; + continue; + } + } + + *src_map = smp; + return np; + } + + if (common_size == 0) + /* Not found */ + return NULL; + + /* + * It's a common, enter into run-time common symbol table. + */ + rtsp = enter_rts(name, (long)calloc(1, common_size), + N_UNDF + N_EXT, 0, common_size, NULL); + +#if DEBUG +xprintf("Allocating common: %s size %d at %#x\n", name, common_size, rtsp->rt_sp->nz_value); +#endif + + return rtsp->rt_sp; +} + + +/* + * This routine is called from the jumptable to resolve + * procedure calls to shared objects. + */ +long +binder(jsp) + jmpslot_t *jsp; +{ + struct so_map *smp, *src_map = NULL; + long addr; + char *sym; + struct nzlist *np; + int index; + + /* + * Find the PLT map that contains JSP. + */ + for (smp = link_map_head; smp; smp = smp->som_next) { + if (LM_PLT(smp) < jsp && + jsp < LM_PLT(smp) + LD_PLTSZ(smp->som_dynamic)/sizeof(*jsp)) + break; + } + + if (smp == NULL) + errx(1, "Call to binder from unknown location: %#x\n", jsp); + + index = jsp->reloc_index & JMPSLOT_RELOC_MASK; + + /* Get the local symbol this jmpslot refers to */ + sym = LM_STRINGS(smp) + + LM_SYMBOL(smp,RELOC_SYMBOL(&LM_REL(smp)[index]))->nz_strx; + + np = lookup(sym, &src_map, 1); + if (np == NULL) + errx(1, "Undefined symbol \"%s\" called from %s:%s at %#x", + sym, main_progname, smp->som_path, jsp); + + /* Fixup jmpslot so future calls transfer directly to target */ + addr = np->nz_value; + if (src_map) + addr += (long)src_map->som_addr; + + md_fix_jmpslot(jsp, (long)jsp, addr); + +#if DEBUG +xprintf(" BINDER: %s located at = %#x in %s\n", sym, addr, src_map->som_path); +#endif + return addr; +} + + +static int hfd; +static long hsize; +static struct hints_header *hheader; +static struct hints_bucket *hbuckets; +static char *hstrtab; + +#define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1) + +static void +maphints() +{ + caddr_t addr; + + if ((hfd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) { + hheader = (struct hints_header *)-1; + return; + } + + hsize = PAGSIZ; + addr = mmap(0, hsize, PROT_READ, MAP_COPY, hfd, 0); + + if (addr == (caddr_t)-1) { + close(hfd); + hheader = (struct hints_header *)-1; + return; + } + + hheader = (struct hints_header *)addr; + if (HH_BADMAG(*hheader)) { + munmap(addr, hsize); + close(hfd); + hheader = (struct hints_header *)-1; + return; + } + + if (hheader->hh_version != LD_HINTS_VERSION_1) { + munmap(addr, hsize); + close(hfd); + hheader = (struct hints_header *)-1; + return; + } + + if (hheader->hh_ehints > hsize) { + if (mmap(addr+hsize, hheader->hh_ehints - hsize, + PROT_READ, MAP_COPY|MAP_FIXED, + hfd, hsize) != (caddr_t)(addr+hsize)) { + + munmap((caddr_t)hheader, hsize); + close(hfd); + hheader = (struct hints_header *)-1; + return; + } + } + + hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab); + hstrtab = (char *)(addr + hheader->hh_strtab); +} + +static void +unmaphints() +{ + + if (HINTS_VALID) { + munmap((caddr_t)hheader, hsize); + close(hfd); + hheader = NULL; + } +} + +int +hinthash(cp, vmajor, vminor) + char *cp; + int vmajor, vminor; +{ + int k = 0; + + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff; + k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff; + + return k; +} + +#undef major +#undef minor + +static char * +findhint(name, major, minor, prefered_path) + char *name; + int major, minor; + char *prefered_path; +{ + struct hints_bucket *bp; + + bp = hbuckets + (hinthash(name, major, minor) % hheader->hh_nbucket); + + while (1) { + /* Sanity check */ + if (bp->hi_namex >= hheader->hh_strtab_sz) { + warnx("Bad name index: %#x\n", bp->hi_namex); + break; + } + if (bp->hi_pathx >= hheader->hh_strtab_sz) { + warnx("Bad path index: %#x\n", bp->hi_pathx); + break; + } + + if (strcmp(name, hstrtab + bp->hi_namex) == 0) { + /* It's `name', check version numbers */ + if (bp->hi_major == major && + (bp->hi_ndewey < 2 || bp->hi_minor == minor)) { + if (prefered_path == NULL || + strncmp(prefered_path, + hstrtab + bp->hi_pathx, + strlen(prefered_path)) == 0) { + return hstrtab + bp->hi_pathx; + } + } + } + + if (bp->hi_next == -1) + break; + + /* Move on to next in bucket */ + bp = &hbuckets[bp->hi_next]; + } + + /* No hints available for name */ + return NULL; +} + +static char * +rtfindlib(name, major, minor, usehints, ipath) + char *name; + int major, minor; + int *usehints; + char *ipath; +{ + register char *cp; + int realminor; + + if (hheader == NULL) + maphints(); + + if (!HINTS_VALID || !(*usehints)) + goto lose; + + /* NOTE: `ipath' may reside in a piece of read-only memory */ + + if (ld_library_path || ipath) { + /* Prefer paths from some explicit LD_LIBRARY_PATH */ + register char *lpath; + char *dp; + + dp = lpath = concat(ld_library_path ? ld_library_path : "", + (ld_library_path && ipath) ? ":" : "", + ipath ? ipath : ""); + + while ((cp = strsep(&dp, ":")) != NULL) { + cp = findhint(name, major, minor, cp); + if (cp) { + free(lpath); + return cp; + } + } + free(lpath); + + /* + * Not found in hints; try directory search now, before + * we get a spurious hint match below (i.e. a match not + * on one of the paths we're supposed to search first. + */ + realminor = -1; + cp = (char *)findshlib(name, &major, &realminor, 0); + if (cp && realminor >= minor) + return cp; + } + + /* No LD_LIBRARY_PATH or lib not found in there; check default */ + cp = findhint(name, major, minor, NULL); + if (cp) + return cp; + +lose: + /* No hints available for name */ + *usehints = 0; + realminor = -1; + cp = (char *)findshlib(name, &major, &realminor, 0); + if (cp) { + if (realminor < minor && !ld_suppress_warnings) + warnx("warning: lib%s.so.%d.%d: " + "minor version >= %d expected, using it anyway", + name, major, realminor, minor); + return cp; + } + return NULL; +} + +void +preload(paths) + char *paths; +{ + struct so_map *nsmp; + struct sod *sodp; + char *cp, *dp; + + dp = paths = strdup(paths); + if (dp == NULL) { + errx(1, "preload: out of memory"); + } + + while ((cp = strsep(&dp, ":")) != NULL) { + if ((sodp = (struct sod *)malloc(sizeof(struct sod))) == NULL) { + errx(1, "preload: %s: out of memory", cp); + return; + } + + sodp->sod_name = (long)strdup(cp); + sodp->sod_library = 0; + sodp->sod_major = sodp->sod_minor = 0; + + if ((nsmp = map_object(cp, sodp, 0)) == NULL) { + errx(1, "preload: %s: cannot map object", cp); + } + LM_PRIVATE(nsmp)->spd_refcount++; + } + free(paths); + return; +} + +static struct somap_private dlmap_private = { + 0, + (struct so_map *)0, + 0, +#ifdef SUN_COMPAT + 0, +#endif +}; + +static struct so_map dlmap = { + (caddr_t)0, + "internal", + (struct so_map *)0, + (struct sod *)0, + (caddr_t)0, + (u_int)0, + (struct _dynamic *)0, + (caddr_t)&dlmap_private +}; +static int dlerrno; + +static void * +__dlopen(name, mode) + char *name; + int mode; +{ + struct sod *sodp; + struct so_map *smp; + + /* + * A NULL argument returns the current set of mapped objects. + */ + if (name == NULL) { + LM_PRIVATE(link_map_head)->spd_refcount++; + return link_map_head; + } + + if ((sodp = (struct sod *)malloc(sizeof(struct sod))) == NULL) { + dlerrno = ENOMEM; + return NULL; + } + + sodp->sod_name = (long)strdup(name); + sodp->sod_library = 0; + sodp->sod_major = sodp->sod_minor = 0; + + if ((smp = map_object(name, sodp, 0)) == NULL) { +#ifdef DEBUG +xprintf("%s: %s\n", name, strerror(errno)); +#endif + dlerrno = errno; + return NULL; + } + + if (LM_PRIVATE(smp)->spd_refcount++ > 0) + return smp; + + if (load_subs(smp) != 0) + return NULL; + + init_maps(smp); + return smp; +} + +static int +__dlclose(fd) + void *fd; +{ + struct so_map *smp = (struct so_map *)fd; + +#ifdef DEBUG +xprintf("dlclose(%s): refcount = %d\n", smp->som_path, LM_PRIVATE(smp)->spd_refcount); +#endif + if (--LM_PRIVATE(smp)->spd_refcount != 0) + return 0; + + /* Dismantle shared object map and descriptor */ + call_map(smp, "__fini"); +#if 0 + unmap_object(smp); + free(smp->som_sod->sod_name); + free(smp->som_sod); + free(smp); +#endif + + return 0; +} + +static void * +__dlsym(fd, sym) + void *fd; + char *sym; +{ + struct so_map *smp = (struct so_map *)fd, *src_map = NULL; + struct nzlist *np; + long addr; + + /* + * Restrict search to passed map if dlopen()ed. + */ + if (LM_PRIVATE(smp)->spd_flags & RTLD_DL) + src_map = smp; + + np = lookup(sym, &src_map, 1); + if (np == NULL) + return NULL; + + /* Fixup jmpslot so future calls transfer directly to target */ + addr = np->nz_value; + if (src_map) + addr += (long)src_map->som_addr; + + return (void *)addr; +} + +static int +__dlctl(fd, cmd, arg) + void *fd, *arg; + int cmd; +{ + switch (cmd) { + case DL_GETERRNO: + *(int *)arg = dlerrno; + return 0; + default: + dlerrno = EOPNOTSUPP; + return -1; + } + return 0; +} + +static void +__dlexit() +{ + struct so_map *smp; + + /* Call any object initialization routines. */ + for (smp = link_map_head; smp; smp = smp->som_next) { + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + call_map(smp, ".fini"); + } +} + +void +#if __STDC__ +xprintf(char *fmt, ...) +#else +xprintf(fmt, va_alist) +char *fmt; +#endif +{ + char buf[256]; + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + + vsprintf(buf, fmt, ap); + (void)write(1, buf, strlen(buf)); + va_end(ap); +} |