From 41fe218b25225823844f2b8353c69a4186fdcd0a Mon Sep 17 00:00:00 2001 From: cgd Date: Mon, 16 Dec 1996 20:37:55 +0000 Subject: [PATCH] First cut at an ELF shared loader. Originally from John Polstra's FreeBSD elf kit, then hacked on by Matt Thomas , then by me (to make it work with new versions of the toolchain, etc.). This runs, but it's in serious need of cleaning and/or a fair bit of reworking. See the README file for more information, and a list of things to do. --- include/link_elf.h | 29 ++ libexec/ld.elf_so/Makefile | 38 ++ libexec/ld.elf_so/README | 49 ++ libexec/ld.elf_so/alpha/Makefile.inc | 6 + libexec/ld.elf_so/alpha/rtld_start.S | 169 +++++++ libexec/ld.elf_so/arch/alpha/Makefile.inc | 6 + libexec/ld.elf_so/arch/alpha/rtld_start.S | 169 +++++++ libexec/ld.elf_so/debug.c | 59 +++ libexec/ld.elf_so/debug.h | 56 +++ libexec/ld.elf_so/headers.c | 266 ++++++++++ libexec/ld.elf_so/link.h | 29 ++ libexec/ld.elf_so/load.c | 141 ++++++ libexec/ld.elf_so/malloc.c | 472 ++++++++++++++++++ libexec/ld.elf_so/map_object.c | 264 ++++++++++ libexec/ld.elf_so/paths.c | 106 ++++ libexec/ld.elf_so/reloc.c | 431 ++++++++++++++++ libexec/ld.elf_so/rtld.c | 568 ++++++++++++++++++++++ libexec/ld.elf_so/rtld.h | 216 ++++++++ libexec/ld.elf_so/rtldenv.h | 70 +++ libexec/ld.elf_so/search.c | 347 +++++++++++++ libexec/ld.elf_so/symbol.c | 171 +++++++ libexec/ld.elf_so/xmalloc.c | 61 +++ libexec/ld.elf_so/xprintf.c | 272 +++++++++++ 23 files changed, 3995 insertions(+) create mode 100644 include/link_elf.h create mode 100644 libexec/ld.elf_so/Makefile create mode 100644 libexec/ld.elf_so/README create mode 100644 libexec/ld.elf_so/alpha/Makefile.inc create mode 100644 libexec/ld.elf_so/alpha/rtld_start.S create mode 100644 libexec/ld.elf_so/arch/alpha/Makefile.inc create mode 100644 libexec/ld.elf_so/arch/alpha/rtld_start.S create mode 100644 libexec/ld.elf_so/debug.c create mode 100644 libexec/ld.elf_so/debug.h create mode 100644 libexec/ld.elf_so/headers.c create mode 100644 libexec/ld.elf_so/link.h create mode 100644 libexec/ld.elf_so/load.c create mode 100644 libexec/ld.elf_so/malloc.c create mode 100644 libexec/ld.elf_so/map_object.c create mode 100644 libexec/ld.elf_so/paths.c create mode 100644 libexec/ld.elf_so/reloc.c create mode 100644 libexec/ld.elf_so/rtld.c create mode 100644 libexec/ld.elf_so/rtld.h create mode 100644 libexec/ld.elf_so/rtldenv.h create mode 100644 libexec/ld.elf_so/search.c create mode 100644 libexec/ld.elf_so/symbol.c create mode 100644 libexec/ld.elf_so/xmalloc.c create mode 100644 libexec/ld.elf_so/xprintf.c diff --git a/include/link_elf.h b/include/link_elf.h new file mode 100644 index 000000000000..6725cacc745f --- /dev/null +++ b/include/link_elf.h @@ -0,0 +1,29 @@ +/* $NetBSD: link_elf.h,v 1.1 1996/12/16 20:37:59 cgd Exp $ */ + +/* + * This only exists for GDB. + */ +#ifndef _LINK_H +#define _LINK_H + +#include + +struct link_map { + caddr_t l_addr; /* Base Address of library */ + const char *l_name; /* Absolute Path to Library */ + void *l_ld; /* Pointer to .dynamic in memory */ + struct link_map *l_next, *l_prev; /* linked list of of mapped libs */ +}; + +struct r_debug { + int r_version; /* not used */ + struct link_map *r_map; /* list of loaded images */ + void (*r_brk)(void); /* pointer to break point */ + enum { + RT_CONSISTENT, /* things are stable */ + RT_ADD, /* adding a shared library */ + RT_DELETE /* removing a shared library */ + } r_state; +}; +#endif /* _LINK_H */ + diff --git a/libexec/ld.elf_so/Makefile b/libexec/ld.elf_so/Makefile new file mode 100644 index 000000000000..eacfdef3f7a0 --- /dev/null +++ b/libexec/ld.elf_so/Makefile @@ -0,0 +1,38 @@ +# $NetBSD: Makefile,v 1.1 1996/12/16 20:37:55 cgd Exp $ + +PROG= ld.elf_so + +# Adds SRCS, CFLAGS, LDFLAGS, etc. Must go first so MD startup source +# is first. +.include "${.CURDIR}/${MACHINE_ARCH}/Makefile.inc" + +SRCS+= rtld.c reloc.c symbol.c malloc.c xmalloc.c xprintf.c debug.c \ + map_object.c load.c search.c headers.c paths.c +CFLAGS+= -Wall -DLIBDIR=\"${LIBDIR}\" -D_PATH_RTLD=\"${BINDIR}/${PROG}\" +CFLAGS+= -DDEBUG -DRTLD_LOADER +#CFLAGS+= -DRTLD_DEBUG + +LDADD+= -L${LIBDIR} -non_shared -lc_pic +DPADD+= ${LIBDIR}/libc_pic.a + +# to be installed +HDRS= link.h + +NOMAN= +STRIP= + +.PATH: ${.CURDIR}/${MACHINE_ARCH} + +${PROG}: ${OBJS} ${DPADD} + ${LD} ${LDFLAGS} -o ${PROG} ${OBJS} ${LDADD} + +includes: + @cd ${.CURDIR}; for i in $(HDRS); do \ + j="cmp -s $$i ${DESTDIR}/usr/include/$$i || \ + ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 $$i \ + ${DESTDIR}/usr/include"; \ + echo $$j; \ + eval "$$j"; \ + done + +.include diff --git a/libexec/ld.elf_so/README b/libexec/ld.elf_so/README new file mode 100644 index 000000000000..8b0b496f569b --- /dev/null +++ b/libexec/ld.elf_so/README @@ -0,0 +1,49 @@ +$NetBSD: README,v 1.1 1996/12/16 20:37:56 cgd Exp $ + +BUGS/PROBLEMS: + +* rtld.c around line 147: + + #iffdef __alpha__ + /* XXX XXX XXX */ + _rtld_objself.pltgot = NULL; + #endif + + It's a workaround for a linker bug, and will go away when the linker is + fixed. + +* Formerly, _DYNAMIC was _GLOBAL_OFFSET_TABLE_[0], but that isn't true with + newer snapshots of gas/binutils (at least on the Alpha), which support + multiple GOTs. That's what the OLD_GOT #ifdefs are about. + +* The method used to relocate ld.so is shoddy and fragile. Currently it's + something like: + + (1) assembly code relocates RELATIVE relocations in the GOT + (on some ports, all relative relocations; this will probably + have to be #ifdef'd in reloc.c), assuming that everything + in the GOT is a RELATIVE relocation. + + (2) C code relocates the rest of the loader, excluding those + RELATIVE relocations already done (via an ... iffy heuristic). + + Something better (like that the glibc ELF ld.so does) should be done + eventually, where a single set of C code is compiled twice, once for + normal use, and once to be used to relocate the shared linker. No + relocations should be processed in assembly language. + +* On the Alpha, this code Really needs to be checked to be sure that + it doesn't make calls that could use FP, while doing lazy binding. + + +TO DO: + +* Support for other platforms (e.g. mips) + +* Support for coexistance of 32-bit and 64-bit ELF on platforms that can + do that. + +* KNF + +* Possible support for ldd (standalone operation) and program loading + a la the glibc shared loader. diff --git a/libexec/ld.elf_so/alpha/Makefile.inc b/libexec/ld.elf_so/alpha/Makefile.inc new file mode 100644 index 000000000000..904b2f51e27e --- /dev/null +++ b/libexec/ld.elf_so/alpha/Makefile.inc @@ -0,0 +1,6 @@ +# $NetBSD: Makefile.inc,v 1.1 1996/12/16 20:38:09 cgd Exp $ + +SRCS+= rtld_start.S + +CFLAGS+= -fpic -mno-fp-regs -DELFSIZE=64 +LDFLAGS+= -Bshareable -Bsymbolic -e .rtld_start diff --git a/libexec/ld.elf_so/alpha/rtld_start.S b/libexec/ld.elf_so/alpha/rtld_start.S new file mode 100644 index 000000000000..4b4400115e11 --- /dev/null +++ b/libexec/ld.elf_so/alpha/rtld_start.S @@ -0,0 +1,169 @@ +/* $NetBSD: rtld_start.S,v 1.1 1996/12/16 20:38:09 cgd Exp $ */ + +/* + * Copyright 1996 Matt Thomas + * 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. 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 + +.extern _GLOBAL_OFFSET_TABLE_ + +LEAF(_rtld_start, 0) /* XXX */ + .set noreorder + br pv, $33 +$33: LDGP(pv) + + /* save away the stack pointer */ + + lda s0, 0(sp) /* get argc from stack */ + lda sp, -16(sp) + + /* save ps_strings pointer */ + mov a3, s1 + + /* Step 1 -- Figure out the displacement */ + + br t2, $34 /* get our PC */ +$34: ldiq t3, $34 /* get where the linker thought we were */ + subq t2, t3, t8 /* calculate the displacement */ + + + /* Step 2 -- Find bounds of global offset table */ + + lda t5, _GLOBAL_OFFSET_TABLE_ + addq t8, t5, t9 /* add the displacement */ +#if defined(OLD_GOT) + ldq t4, 0(t9) /* Get the address of dynamic table */ +#else + lda t4, _DYNAMIC +#endif + addq t8, t4, t10 /* add the displacement */ + + /* + * Step 3 -- Every entry in the global offset table needs to + * modified for the displacement before any code will work. + */ + +$35: ldq t1, 0(t9) /* load the value */ + addq t8, t1, t1 /* add the displacement */ + stq t1, 0(t9) /* save the new value */ + lda t9, 8(t9) /* point to next entry */ + cmpult t9, t10, t1 /* are we done? */ + bne t1, $35 /* no, do more */ + + /* + * Ya! Things are far enough so we can do some dynamic linking! + */ + + lda a0, -16(s0) + CALL(_rtld) /* v0 = _rtld(sp); */ + + ldq a1, -16(s0) /* our atexit function */ + ldq a2, -8(s0) /* obj_main entry */ + lda sp, 16(sp) /* readjust our stack */ + mov s0, a0 /* stack pointer */ + mov s1, a3 /* ps_strings pointer */ + mov v0, t12 + jsr ra, (v0), 0 /* (*_start)(sp, cleanup, obj); */ + ldgp gp, 0(ra) + + CALL(exit) + halt +END(_rtld_start) + + .set noat + .globl _rtld_bind_start + .ent _rtld_bind_start +_rtld_bind_start: + + lda sp, -168(sp) + .frame sp, 168, $26 + /* Preserve all registers that C normally doesn't. */ + stq $26, 0(sp) + stq $0, 8(sp) + stq $1, 16(sp) + stq $2, 24(sp) + stq $3, 32(sp) + stq $4, 40(sp) + stq $5, 48(sp) + stq $6, 56(sp) + stq $7, 64(sp) + stq $8, 72(sp) + stq $16, 80(sp) + stq $17, 88(sp) + stq $18, 96(sp) + stq $19, 104(sp) + stq $20, 112(sp) + stq $21, 120(sp) + stq $22, 128(sp) + stq $23, 136(sp) + stq $24, 144(sp) + stq $25, 152(sp) + stq $29, 160(sp) + .mask 0x27ff01ff, -168 + /* Set up our $gp */ + br gp, $100 +$100: ldgp gp, 0(gp) + .prologue 1 + /* Set up the arguments for _rtld_bind. */ + ldq a0, 8(t12) /* object structure */ + mov at_reg, a1 /* offset of reloc entry */ + CALL(_rtld_bind) + + /* Move the destination address into position. */ + mov $0, $27 + /* Restore program registers. */ + ldq $26, 0(sp) + ldq $0, 8(sp) + ldq $1, 16(sp) + ldq $2, 24(sp) + ldq $3, 32(sp) + ldq $4, 40(sp) + ldq $5, 48(sp) + ldq $6, 56(sp) + ldq $7, 64(sp) + ldq $8, 72(sp) + ldq $16, 80(sp) + ldq $17, 88(sp) + ldq $18, 96(sp) + ldq $19, 104(sp) + ldq $20, 112(sp) + ldq $21, 120(sp) + ldq $22, 128(sp) + ldq $23, 136(sp) + ldq $24, 144(sp) + ldq $25, 152(sp) + ldq $29, 160(sp) + /* Flush the Icache after having modified the .plt code. */ + imb + /* Clean up and turn control to the destination */ + lda sp, 168(sp) + jmp $31, ($27) + .end _rtld_bind_start + + + + diff --git a/libexec/ld.elf_so/arch/alpha/Makefile.inc b/libexec/ld.elf_so/arch/alpha/Makefile.inc new file mode 100644 index 000000000000..904b2f51e27e --- /dev/null +++ b/libexec/ld.elf_so/arch/alpha/Makefile.inc @@ -0,0 +1,6 @@ +# $NetBSD: Makefile.inc,v 1.1 1996/12/16 20:38:09 cgd Exp $ + +SRCS+= rtld_start.S + +CFLAGS+= -fpic -mno-fp-regs -DELFSIZE=64 +LDFLAGS+= -Bshareable -Bsymbolic -e .rtld_start diff --git a/libexec/ld.elf_so/arch/alpha/rtld_start.S b/libexec/ld.elf_so/arch/alpha/rtld_start.S new file mode 100644 index 000000000000..4b4400115e11 --- /dev/null +++ b/libexec/ld.elf_so/arch/alpha/rtld_start.S @@ -0,0 +1,169 @@ +/* $NetBSD: rtld_start.S,v 1.1 1996/12/16 20:38:09 cgd Exp $ */ + +/* + * Copyright 1996 Matt Thomas + * 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. 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 + +.extern _GLOBAL_OFFSET_TABLE_ + +LEAF(_rtld_start, 0) /* XXX */ + .set noreorder + br pv, $33 +$33: LDGP(pv) + + /* save away the stack pointer */ + + lda s0, 0(sp) /* get argc from stack */ + lda sp, -16(sp) + + /* save ps_strings pointer */ + mov a3, s1 + + /* Step 1 -- Figure out the displacement */ + + br t2, $34 /* get our PC */ +$34: ldiq t3, $34 /* get where the linker thought we were */ + subq t2, t3, t8 /* calculate the displacement */ + + + /* Step 2 -- Find bounds of global offset table */ + + lda t5, _GLOBAL_OFFSET_TABLE_ + addq t8, t5, t9 /* add the displacement */ +#if defined(OLD_GOT) + ldq t4, 0(t9) /* Get the address of dynamic table */ +#else + lda t4, _DYNAMIC +#endif + addq t8, t4, t10 /* add the displacement */ + + /* + * Step 3 -- Every entry in the global offset table needs to + * modified for the displacement before any code will work. + */ + +$35: ldq t1, 0(t9) /* load the value */ + addq t8, t1, t1 /* add the displacement */ + stq t1, 0(t9) /* save the new value */ + lda t9, 8(t9) /* point to next entry */ + cmpult t9, t10, t1 /* are we done? */ + bne t1, $35 /* no, do more */ + + /* + * Ya! Things are far enough so we can do some dynamic linking! + */ + + lda a0, -16(s0) + CALL(_rtld) /* v0 = _rtld(sp); */ + + ldq a1, -16(s0) /* our atexit function */ + ldq a2, -8(s0) /* obj_main entry */ + lda sp, 16(sp) /* readjust our stack */ + mov s0, a0 /* stack pointer */ + mov s1, a3 /* ps_strings pointer */ + mov v0, t12 + jsr ra, (v0), 0 /* (*_start)(sp, cleanup, obj); */ + ldgp gp, 0(ra) + + CALL(exit) + halt +END(_rtld_start) + + .set noat + .globl _rtld_bind_start + .ent _rtld_bind_start +_rtld_bind_start: + + lda sp, -168(sp) + .frame sp, 168, $26 + /* Preserve all registers that C normally doesn't. */ + stq $26, 0(sp) + stq $0, 8(sp) + stq $1, 16(sp) + stq $2, 24(sp) + stq $3, 32(sp) + stq $4, 40(sp) + stq $5, 48(sp) + stq $6, 56(sp) + stq $7, 64(sp) + stq $8, 72(sp) + stq $16, 80(sp) + stq $17, 88(sp) + stq $18, 96(sp) + stq $19, 104(sp) + stq $20, 112(sp) + stq $21, 120(sp) + stq $22, 128(sp) + stq $23, 136(sp) + stq $24, 144(sp) + stq $25, 152(sp) + stq $29, 160(sp) + .mask 0x27ff01ff, -168 + /* Set up our $gp */ + br gp, $100 +$100: ldgp gp, 0(gp) + .prologue 1 + /* Set up the arguments for _rtld_bind. */ + ldq a0, 8(t12) /* object structure */ + mov at_reg, a1 /* offset of reloc entry */ + CALL(_rtld_bind) + + /* Move the destination address into position. */ + mov $0, $27 + /* Restore program registers. */ + ldq $26, 0(sp) + ldq $0, 8(sp) + ldq $1, 16(sp) + ldq $2, 24(sp) + ldq $3, 32(sp) + ldq $4, 40(sp) + ldq $5, 48(sp) + ldq $6, 56(sp) + ldq $7, 64(sp) + ldq $8, 72(sp) + ldq $16, 80(sp) + ldq $17, 88(sp) + ldq $18, 96(sp) + ldq $19, 104(sp) + ldq $20, 112(sp) + ldq $21, 120(sp) + ldq $22, 128(sp) + ldq $23, 136(sp) + ldq $24, 144(sp) + ldq $25, 152(sp) + ldq $29, 160(sp) + /* Flush the Icache after having modified the .plt code. */ + imb + /* Clean up and turn control to the destination */ + lda sp, 168(sp) + jmp $31, ($27) + .end _rtld_bind_start + + + + diff --git a/libexec/ld.elf_so/debug.c b/libexec/ld.elf_so/debug.c new file mode 100644 index 000000000000..c4e277238753 --- /dev/null +++ b/libexec/ld.elf_so/debug.c @@ -0,0 +1,59 @@ +/* $NetBSD: debug.c,v 1.1 1996/12/16 20:37:57 cgd Exp $ */ + +/* + * Copyright 1996 John D. Polstra. + * Copyright 1996 Matt Thomas + * 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 John Polstra. + * 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. + */ + +/* + * Support for printing debugging messages. + */ + +#include + +#include "debug.h" +#include "rtldenv.h" + +#ifdef DEBUG +int debug = 0; + +void +debug_printf(const char *format, ...) +{ + if(debug) { + va_list ap; + va_start(ap, format); + + xvprintf(format, ap); + + va_end(ap); + xprintf("\n"); + } +} +#endif diff --git a/libexec/ld.elf_so/debug.h b/libexec/ld.elf_so/debug.h new file mode 100644 index 000000000000..4c1db55c1130 --- /dev/null +++ b/libexec/ld.elf_so/debug.h @@ -0,0 +1,56 @@ +/* $NetBSD: debug.h,v 1.1 1996/12/16 20:37:57 cgd Exp $ */ + +/* + * Copyright 1996 John D. Polstra. + * Copyright 1996 Matt Thomas + * 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 John Polstra. + * 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. + */ + +/* + * Support for printing debugging messages. + */ + +#ifndef DEBUG_H /* { */ +#define DEBUG_H 1 + +#ifndef __GNUC__ /* { */ +#error "Sorry, this module relies on some GNU extensions" +#endif /* } */ + +extern void xprintf(const char *fmt, ...); +extern void xvprintf(const char *fmt, va_list ap); +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 /* } */ diff --git a/libexec/ld.elf_so/headers.c b/libexec/ld.elf_so/headers.c new file mode 100644 index 000000000000..6f860df57b65 --- /dev/null +++ b/libexec/ld.elf_so/headers.c @@ -0,0 +1,266 @@ +/* $NetBSD: headers.c,v 1.1 1996/12/16 20:37:58 cgd Exp $ */ + +/* + * Copyright 1996 John D. Polstra. + * Copyright 1996 Matt Thomas + * 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 John Polstra. + * 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. + */ + +/* + * Dynamic linker for ELF. + * + * John Polstra . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +/* + * Process a shared object's DYNAMIC section, and save the important + * information in its Obj_Entry structure. + */ +void +_rtld_digest_dynamic( + Obj_Entry *obj) +{ + Elf_Dyn *dynp; + Needed_Entry **needed_tail = &obj->needed; + const Elf_Dyn *dyn_rpath = NULL; + enum Elf_e_dynamic_type plttype = Elf_edt_rel; + Elf_Word relsize = 0, relasize = 0, pltrelsize = 0, pltrelasize = 0; + + for (dynp = obj->dynamic; dynp->d_tag != Elf_edt_null; ++dynp) { + switch(dynp->d_tag) { + + case Elf_edt_rel: + obj->rel = (const Elf_Rel *) (obj->relocbase + dynp->d_un.d_ptr); + break; + + case Elf_edt_relsz: + relsize = dynp->d_un.d_val; + break; + + case Elf_edt_relent: + assert(dynp->d_un.d_val == sizeof(Elf_Rel)); + break; + + case Elf_edt_jmprel: + if (plttype == Elf_edt_rel) { + obj->pltrel = (const Elf_Rel *) + (obj->relocbase + dynp->d_un.d_ptr); + } else { + obj->pltrela = (const Elf_RelA *) + (obj->relocbase + dynp->d_un.d_ptr); + } + break; + + case Elf_edt_pltrelsz: + if (plttype == Elf_edt_rel) { + pltrelsize = dynp->d_un.d_val; + } else { + pltrelasize = dynp->d_un.d_val; + } + break; + + case Elf_edt_rela: + obj->rela = (const Elf_RelA *) (obj->relocbase + dynp->d_un.d_ptr); + break; + + case Elf_edt_relasz: + relasize = dynp->d_un.d_val; + break; + + case Elf_edt_relaent: + assert(dynp->d_un.d_val == sizeof(Elf_RelA)); + break; + + case Elf_edt_pltrel: + plttype = dynp->d_un.d_val; + assert(plttype == Elf_edt_rel || plttype == Elf_edt_rela); + if (plttype == Elf_edt_rela) { + obj->pltrela = (const Elf_RelA *) obj->pltrel; + obj->pltrel = NULL; + pltrelasize = pltrelsize; + pltrelsize = 0; + } + break; + + case Elf_edt_symtab: + obj->symtab = (const Elf_Sym *) + (obj->relocbase + dynp->d_un.d_ptr); + break; + + case Elf_edt_syment: + assert(dynp->d_un.d_val == sizeof(Elf_Sym)); + break; + + case Elf_edt_strtab: + obj->strtab = (const char *) (obj->relocbase + dynp->d_un.d_ptr); + break; + + case Elf_edt_strsz: + obj->strsize = dynp->d_un.d_val; + break; + + case Elf_edt_hash: + { + const Elf_Word *hashtab = (const Elf_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 Elf_edt_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 Elf_edt_pltgot: + obj->pltgot = (Elf_Addr *) (obj->relocbase + dynp->d_un.d_ptr); + break; + + case Elf_edt_textrel: + obj->textrel = true; + break; + + case Elf_edt_symbolic: + obj->symbolic = true; + break; + + case Elf_edt_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 Elf_edt_soname: + /* Not used by the dynamic linker. */ + break; + + case Elf_edt_init: + obj->init = (void (*)(void)) (obj->relocbase + dynp->d_un.d_ptr); + break; + + case Elf_edt_fini: + obj->fini = (void (*)(void)) (obj->relocbase + dynp->d_un.d_ptr); + break; + + case Elf_edt_debug: +#ifdef RTLD_LOADER + dynp->d_un.d_ptr = (Elf_Addr) &_rtld_debug; +#endif + break; + } + } + + obj->rellim = (const Elf_Rel *) ((caddr_t) obj->rel + relsize); + obj->relalim = (const Elf_RelA *) ((caddr_t) obj->rela + relasize); + obj->pltrellim = (const Elf_Rel *) ((caddr_t) obj->pltrel + pltrelsize); + obj->pltrelalim = (const Elf_RelA *) ((caddr_t) obj->pltrela + pltrelasize); + + if (dyn_rpath != NULL) { + _rtld_add_paths(&obj->rpaths, 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. + */ +Obj_Entry * +_rtld_digest_phdr( + const Elf_Phdr *phdr, + int phnum, + caddr_t entry) +{ + Obj_Entry *obj = CNEW(Obj_Entry); + const Elf_Phdr *phlimit = phdr + phnum; + const Elf_Phdr *ph; + int nsegs = 0; + + for (ph = phdr; ph < phlimit; ++ph) { + switch(ph->p_type) { + + case Elf_pt_phdr: + assert((const Elf_Phdr *) ph->p_vaddr == phdr); + obj->phdr = (const Elf_Phdr *) ph->p_vaddr; + obj->phsize = ph->p_memsz; + break; + + case Elf_pt_load: + assert(nsegs < 2); + if (nsegs == 0) { /* First load segment */ + obj->vaddrbase = round_down(ph->p_vaddr); + obj->mapbase = (caddr_t) obj->vaddrbase; + obj->relocbase = obj->mapbase - obj->vaddrbase; + obj->textsize = round_up(ph->p_vaddr + ph->p_memsz) - + obj->vaddrbase; + } else { /* Last load segment */ + obj->mapsize = round_up(ph->p_vaddr + ph->p_memsz) - + obj->vaddrbase; + } + ++nsegs; + break; + + case Elf_pt_dynamic: + obj->dynamic = (Elf_Dyn *) ph->p_vaddr; + break; + } + } + assert(nsegs == 2); + + obj->entry = entry; + return obj; +} diff --git a/libexec/ld.elf_so/link.h b/libexec/ld.elf_so/link.h new file mode 100644 index 000000000000..afd609ae2215 --- /dev/null +++ b/libexec/ld.elf_so/link.h @@ -0,0 +1,29 @@ +/* $NetBSD: link.h,v 1.1 1996/12/16 20:37:59 cgd Exp $ */ + +/* + * This only exists for GDB. + */ +#ifndef _LINK_H +#define _LINK_H + +#include + +struct link_map { + caddr_t l_addr; /* Base Address of library */ + const char *l_name; /* Absolute Path to Library */ + void *l_ld; /* Pointer to .dynamic in memory */ + struct link_map *l_next, *l_prev; /* linked list of of mapped libs */ +}; + +struct r_debug { + int r_version; /* not used */ + struct link_map *r_map; /* list of loaded images */ + void (*r_brk)(void); /* pointer to break point */ + enum { + RT_CONSISTENT, /* things are stable */ + RT_ADD, /* adding a shared library */ + RT_DELETE /* removing a shared library */ + } r_state; +}; +#endif /* _LINK_H */ + diff --git a/libexec/ld.elf_so/load.c b/libexec/ld.elf_so/load.c new file mode 100644 index 000000000000..a8fe0a5d6cd1 --- /dev/null +++ b/libexec/ld.elf_so/load.c @@ -0,0 +1,141 @@ +/* $NetBSD: load.c,v 1.1 1996/12/16 20:37:59 cgd Exp $ */ + +/* + * Copyright 1996 John D. Polstra. + * Copyright 1996 Matt Thomas + * 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 John Polstra. + * 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. + */ + +/* + * Dynamic linker for ELF. + * + * John Polstra . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +/* + * 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. + */ +Obj_Entry * +_rtld_load_object( + char *filepath) +{ + Obj_Entry *obj; + + for (obj = _rtld_objlist->next; obj != NULL; obj = obj->next) + if (strcmp(obj->path, filepath) == 0) + break; + + if (obj == NULL) { /* First use of this object, so we must map it in */ + int fd; + + if ((fd = open(filepath, O_RDONLY)) == -1) { + _rtld_error("Cannot open \"%s\"", filepath); + return NULL; + } + obj = _rtld_map_object(filepath, fd); + close(fd); + if (obj == NULL) { + free(filepath); + return NULL; + } + + obj->path = filepath; + _rtld_digest_dynamic(obj); + + *_rtld_objtail = obj; + _rtld_objtail = &obj->next; +#ifdef RTLD_LOADER + _rtld_linkmap_add(obj); /* for GDB */ +#endif + + 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(filepath); + + ++obj->refcount; + return obj; +} + +/* + * 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. + */ +int +_rtld_load_needed_objects( + Obj_Entry *first) +{ + Obj_Entry *obj; + int status = 0; + + 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 *libpath = _rtld_find_library(name, obj); + + if (libpath == NULL) { + status = -1; + } else { + needed->obj = _rtld_load_object(libpath); + if (needed->obj == NULL) + status = -1; /* FIXME - cleanup */ + } +#ifdef RTLD_LOADER + if (status == -1) + return status; +#endif + } + } + + return status; +} diff --git a/libexec/ld.elf_so/malloc.c b/libexec/ld.elf_so/malloc.c new file mode 100644 index 000000000000..e1bdd5fc07e8 --- /dev/null +++ b/libexec/ld.elf_so/malloc.c @@ -0,0 +1,472 @@ +/* $NetBSD: malloc.c,v 1.1 1996/12/16 20:38:00 cgd Exp $ */ + +/* + * 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";*/ +#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 "rtldenv.h" +#include +#include +#include +#include +#include +#include + +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]; + +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]; +#endif + +#if defined(MALLOC_DEBUG) || defined(RCHECK) +#define ASSERT(p) if (!(p)) botch("p") +static void +botch( + const char *s) +{ + xwarnx("\r\nassertion botched: %s\r\n", s); + abort(); +} +#else +#define ASSERT(p) +#endif + +/* Polstra's 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; + register long 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) - (((char *)op - (char *)NULL) & (n - 1)); + if (n < 0) + n += pagesz; + if (n) { + pagepool_start += n; + } + bucket = 0; + amt = sizeof(union overhead); + 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)) { + if (sizeof(union overhead) & (sizeof(union overhead) - 1)) { + amt = sizeof(union overhead) * 2; + bucket = 1; + } else { + amt = sizeof(union overhead); /* size of first bucket */ + bucket = 0; + } + 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" */ + memcpy(res, cp, (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; + + xprintf("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++) + ; + xprintf(" %d", j); + totfree += j * (1 << (i + 3)); + } + xprintf("\nused:\t"); + for (i = 0; i < NBUCKETS; i++) { + xprintf(" %d", nmalloc[i]); + totused += nmalloc[i] * (1 << (i + 3)); + } + xprintf("\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) + xerr(1, "/dev/zero"); +#endif + + if (pagepool_end - pagepool_start > pagesz) { + caddr_t addr = (caddr_t) + (((long)pagepool_start + pagesz - 1) & ~(pagesz - 1)); + if (munmap(addr, pagepool_end - addr) != 0) + xwarn("morepages: munmap %p", addr); + } + + offset = (long)pagepool_start - ((long)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/ld.elf_so/map_object.c b/libexec/ld.elf_so/map_object.c new file mode 100644 index 000000000000..34b5c5e8fb34 --- /dev/null +++ b/libexec/ld.elf_so/map_object.c @@ -0,0 +1,264 @@ +/* $NetBSD: map_object.c,v 1.1 1996/12/16 20:38:01 cgd Exp $ */ + +/* + * Copyright 1996 John D. Polstra. + * Copyright 1996 Matt Thomas + * 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 John Polstra. + * 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 +#include +#include +#include +#include +#include + +#include "rtld.h" + +#define CONCAT(x,y) __CONCAT(x,y) +#define ELFNAME(x) CONCAT(elf,CONCAT(ELFSIZE,CONCAT(_,x))) +#define ELFNAME2(x,y) CONCAT(x,CONCAT(_elf,CONCAT(ELFSIZE,CONCAT(_,y)))) +#define ELFNAMEEND(x) CONCAT(x,CONCAT(_elf,ELFSIZE)) +#define ELFDEFNNAME(x) CONCAT(ELF,CONCAT(ELFSIZE,CONCAT(_,x))) + +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 * +_rtld_map_object( + const char *path, + int fd) +{ + Obj_Entry *obj; + union { + Elf_Ehdr hdr; + char buf[PAGESIZE]; + } u; + int nbytes; + Elf_Phdr *phdr; + Elf_Phdr *phlimit; + Elf_Phdr *segs[2]; + int nsegs; + Elf_Phdr *phdyn; + Elf_Phdr *phphdr; + caddr_t mapbase; + size_t mapsize; + Elf_Off base_offset; + Elf_Addr base_vaddr; + Elf_Addr base_vlimit; + caddr_t base_addr; + Elf_Off data_offset; + Elf_Addr data_vaddr; + Elf_Addr data_vlimit; + caddr_t data_addr; +#ifdef RTLD_LOADER + Elf_Addr clear_vaddr; + caddr_t clear_addr; + size_t nclear; + Elf_Addr bss_vaddr; + Elf_Addr bss_vlimit; + caddr_t bss_addr; +#endif + + if ((nbytes = read(fd, u.buf, PAGESIZE)) == -1) { + _rtld_error("%s: read error: %s", path, xstrerror(errno)); + return NULL; + } + + /* Make sure the file is valid */ + if (nbytes < sizeof(Elf_Ehdr) + || memcmp(Elf_e_ident, u.hdr.e_ident, Elf_e_siz) != 0) { + _rtld_error("%s: unrecognized file format", path); + return NULL; + } + /* Elf_e_ident includes class */ + if (u.hdr.e_ident[Elf_ei_version] != Elf_ev_current + || u.hdr.e_version != Elf_ev_current + || u.hdr.e_ident[Elf_ei_data] != ELFDEFNNAME(MACHDEP_ENDIANNESS)) { + _rtld_error("%s: Unsupported file version", path); + return NULL; + } + if (u.hdr.e_type != Elf_et_exec && u.hdr.e_type != Elf_et_dyn) { + _rtld_error("%s: Unsupported file type", path); + return NULL; + } + switch (u.hdr.e_machine) { + ELFDEFNNAME(MACHDEP_ID_CASES) + + default: + _rtld_error("%s: Unsupported machine", path); + 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(Elf_Phdr)); + assert(u.hdr.e_phoff + u.hdr.e_phnum*sizeof(Elf_Phdr) <= PAGESIZE); + assert(u.hdr.e_phoff + u.hdr.e_phnum*sizeof(Elf_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 = (Elf_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 Elf_pt_load: + assert(nsegs < 2); + segs[nsegs] = phdr; + ++nsegs; + break; + + case Elf_pt_phdr: + phphdr = phdr; + break; + + case Elf_pt_dynamic: + phdyn = phdr; + break; + } + + ++phdr; + } + if (phdyn == NULL) { + _rtld_error("%s: not dynamically-linked", path); + return NULL; + } + + assert(nsegs == 2); +#ifdef __i386__ + assert(segs[0]->p_align <= PAGESIZE); + assert(segs[1]->p_align <= PAGESIZE); +#endif + + /* + * Map the entire address space of the object, to stake out our + * contiguous region, and to establish the base address for relocation. + */ + base_offset = round_down(segs[0]->p_offset); + base_vaddr = round_down(segs[0]->p_vaddr); + base_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_memsz); + mapsize = base_vlimit - base_vaddr; +#ifdef RTLD_LOADER + base_addr = u.hdr.e_type == Elf_et_exec ? (caddr_t) base_vaddr : NULL; +#else + base_addr = NULL; +#endif + + 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", xstrerror(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 = round_down(segs[1]->p_offset); + data_vaddr = round_down(segs[1]->p_vaddr); + data_vlimit = round_up(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", xstrerror(errno)); + return NULL; + } + +#ifdef RTLD_LOADER + /* 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_up(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", xstrerror(errno)); + return NULL; + } + } +#endif + + obj = CNEW(Obj_Entry); + obj->mapbase = mapbase; + obj->mapsize = mapsize; + obj->textsize = round_up(segs[0]->p_vaddr + segs[0]->p_memsz) - base_vaddr; + obj->vaddrbase = base_vaddr; + obj->relocbase = mapbase - base_vaddr; + obj->dynamic = (Elf_Dyn *) (obj->relocbase + phdyn->p_vaddr); + if (u.hdr.e_entry != 0) + obj->entry = (caddr_t) (obj->relocbase + u.hdr.e_entry); + if (phphdr != NULL) { + obj->phdr = (const Elf_Phdr *) (obj->relocbase + phphdr->p_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 & Elf_pf_r) prot |= PROT_READ; +#ifdef RTLD_LOADER + if (elfflags & Elf_pf_w) prot |= PROT_WRITE; +#endif + if (elfflags & Elf_pf_x) prot |= PROT_EXEC; + return prot; +} diff --git a/libexec/ld.elf_so/paths.c b/libexec/ld.elf_so/paths.c new file mode 100644 index 000000000000..cc78f70e2c69 --- /dev/null +++ b/libexec/ld.elf_so/paths.c @@ -0,0 +1,106 @@ +/* $NetBSD: paths.c,v 1.1 1996/12/16 20:38:01 cgd Exp $ */ + +/* + * Copyright 1996 Matt Thomas + * 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. 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +Search_Path * +_rtld_find_path( + Search_Path *path, + const char *pathstr, + size_t pathlen) +{ + for (; path != NULL; path = path->sp_next) { + if (pathlen == path->sp_pathlen + && memcmp(path->sp_path, pathstr, pathlen) == 0) + return path; + } + return NULL; +} + +void +_rtld_add_paths( + Search_Path **path_p, + const char *pathstr) +{ + Search_Path *path, **head_p = path_p; + + if (pathstr == NULL) + return; + + if (pathstr[0] == ':') { + /* + * Leading colon means append to current path + */ + while ((*path_p) != NULL) + path_p = &(*path_p)->sp_next; + pathstr++; + } + + for (;;) { + const char *bp = pathstr; + const char *ep = strchr(bp, ':'); + if (ep == NULL) + ep = &pathstr[strlen(pathstr)]; + + if (bp < ep && (path = _rtld_find_path(*head_p, bp, ep - bp)) == NULL) { + char *cp; + path = CNEW(Search_Path); + path->sp_pathlen = ep - bp; + cp = xmalloc(path->sp_pathlen + 1); + strncpy(cp, bp, path->sp_pathlen); + cp[path->sp_pathlen] = '\0'; + path->sp_path = cp; + path->sp_next = (*path_p); + (*path_p) = path; + path_p = &path->sp_next; + + dbg(" added path \"%s\"", path->sp_path); + } + + if (ep[0] == '\0') + break; + pathstr = ep + 1; + } +} + + diff --git a/libexec/ld.elf_so/reloc.c b/libexec/ld.elf_so/reloc.c new file mode 100644 index 000000000000..a10f58f75612 --- /dev/null +++ b/libexec/ld.elf_so/reloc.c @@ -0,0 +1,431 @@ +/* $NetBSD: reloc.c,v 1.1 1996/12/16 20:38:02 cgd Exp $ */ + +/* + * Copyright 1996 John D. Polstra. + * Copyright 1996 Matt Thomas + * 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 John Polstra. + * 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. + */ + +/* + * Dynamic linker for ELF. + * + * John Polstra . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +static int +_rtld_do_copy_relocation( + const Obj_Entry *dstobj, + const Elf_RelA *rela) +{ + void *dstaddr = (void *) (dstobj->relocbase + rela->r_offset); + const Elf_Sym *dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); + const char *name = dstobj->strtab + dstsym->st_name; + unsigned long hash = _rtld_elf_hash(name); + size_t size = dstsym->st_size; + const void *srcaddr; + const Elf_Sym *srcsym; + Obj_Entry *srcobj; + + for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) + if ((srcsym = _rtld_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; +} + +/* + * Process the special R_xxx_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. + */ +int +_rtld_do_copy_relocations( + const Obj_Entry *dstobj) +{ + assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ + + if (dstobj->rel != NULL) { + const Elf_Rel *rel; + for (rel = dstobj->rel; rel < dstobj->rellim; ++rel) { + if (ELF_R_TYPE(rel->r_info) == R_TYPE(COPY)) { + Elf_RelA ourrela; + ourrela.r_info = rel->r_info; + ourrela.r_offset = rel->r_offset; + ourrela.r_addend = 0; + if (_rtld_do_copy_relocation(dstobj, &ourrela) < 0) + return -1; + } + } + } + + if (dstobj->rela != NULL) { + const Elf_RelA *rela; + for (rela = dstobj->rela; rela < dstobj->relalim; ++rela) { + if (ELF_R_TYPE(rela->r_info) == R_TYPE(COPY)) { + if (_rtld_do_copy_relocation(dstobj, rela) < 0) + return -1; + } + } + } + + return 0; +} + +static int +_rtld_relocate_nonplt_object( + const Obj_Entry *obj, + const Elf_RelA *rela) +{ + Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rela->r_offset); + + switch (ELF_R_TYPE(rela->r_info)) { + + case R_TYPE(NONE): + break; + +#ifdef __i386__ + case R_386_GOT32: { + const Elf_Sym *def; + const Obj_Entry *defobj; + + def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false); + if (def == NULL) + return -1; + + if (*where != (Elf_Addr) (defobj->relocbase + def->st_value + rela->r_addend)) + *where = (Elf_Addr) (defobj->relocbase + def->st_value + rela->r_addend); + 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 Elf_Sym *def; + const Obj_Entry *defobj; + + def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false); + if (def == NULL) + return -1; + + *where += (Elf_Addr) (defobj->relocbase + def->st_value) + - (Elf_Addr) where; + break; + } +#endif +#ifdef __alpha__ + case R_ALPHA_REFQUAD: { + const Elf_Sym *def; + const Obj_Entry *defobj; + Elf_Addr tmp_value; + + def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false); + if (def == NULL) + return -1; + + tmp_value = (Elf_Addr) (defobj->relocbase + def->st_value) + + *where + rela->r_addend; + if (*where != tmp_value) + *where = tmp_value; + break; + } +#endif + + case R_TYPE(GLOB_DAT): + { + const Elf_Sym *def; + const Obj_Entry *defobj; + + def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, false); + if (def == NULL) + return -1; + + if (*where != (Elf_Addr) (defobj->relocbase + def->st_value)) + *where = (Elf_Addr) (defobj->relocbase + def->st_value); + break; + } + + case R_TYPE(RELATIVE): { + extern Elf_Addr _GLOBAL_OFFSET_TABLE_[]; + extern Elf_Dyn _DYNAMIC; + + if (obj != &_rtld_objself || + (caddr_t)where < (caddr_t)_GLOBAL_OFFSET_TABLE_ || + (caddr_t)where >= (caddr_t)&_DYNAMIC) + *where += (Elf_Addr) obj->relocbase; + break; + } + + + case R_TYPE(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_COPY relocation in shared library", + obj->path); + return -1; + } + break; + } + + default: { + const Elf_Sym *def; + const Obj_Entry *defobj; + + def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, true); + dbg("sym = %d, type = %d, offset = %p, addend = %p, contents = %p, symbol = %s", + ELF_R_SYM(rela->r_info), ELF_R_TYPE(rela->r_info), + rela->r_offset, rela->r_addend, *where, + def ? defobj->strtab + def->st_name : "??"); + _rtld_error("%s: Unsupported relocation type %d in non-PLT relocations\n", + obj->path, ELF_R_TYPE(rela->r_info)); + return -1; + } + } + return 0; +} + +static int +_rtld_relocate_plt_object( + const Obj_Entry *obj, + const Elf_RelA *rela, + bool bind_now) +{ + Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rela->r_offset); + Elf_Addr new_value; + + /* Fully resolve procedure addresses now */ + if (bind_now || obj->pltgot == NULL) { + const Elf_Sym *def; + const Obj_Entry *defobj; + + assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT)); + + def = _rtld_find_symdef(_rtld_objlist, rela->r_info, NULL, obj, &defobj, true); + if (def == NULL) + return -1; + + new_value = (Elf_Addr) (defobj->relocbase + def->st_value); +#if 0 + dbg("fixup %s in %s --> %p in %s", + defobj->strtab + def->st_name, obj->path, + new_value, defobj->path); +#endif + } else if (!obj->mainprog) { + /* Just relocate the GOT slots pointing into the PLT */ + new_value = *where + (Elf_Addr) (obj->relocbase); + } else { + return 0; + } + /* + * Since this page is probably copy-on-write, let's not write + * it unless we really really have to. + */ + if (*where != new_value) + *where = new_value; + return 0; +} + +caddr_t +_rtld_bind( + const Obj_Entry *obj, + Elf_Word reloff) +{ + const Elf_RelA *rela; + Elf_RelA ourrela; + + if (obj->pltrel != NULL) { + ourrela.r_info = ((const Elf_Rel *) ((caddr_t) obj->pltrel + reloff))->r_info; + ourrela.r_offset = ((const Elf_Rel *) ((caddr_t) obj->pltrel + reloff))->r_offset; + rela = &ourrela; + } else { + rela = (const Elf_RelA *) ((caddr_t) obj->pltrela + reloff); + } + + + if (_rtld_relocate_plt_object(obj, rela, true) < 0) + _rtld_die(); + + return *(caddr_t *)(obj->relocbase + rela->r_offset); +} + +/* + * 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. + */ +int +_rtld_relocate_objects( + Obj_Entry *first, + bool bind_now) +{ + Obj_Entry *obj; + int ok = 1; + + for (obj = first; obj != NULL; obj = obj->next) { + + 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; + } + + dbg(" relocating %s (%d/%d rel/rela, %d/%d plt rel/rela)", + obj->path, + obj->rellim - obj->rel, obj->relalim - obj->rela, + obj->pltrellim - obj->pltrel, obj->pltrelalim - obj->pltrela); + + 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, xstrerror(errno)); + return -1; + } + } + + if (obj->rel != NULL) { + /* Process the non-PLT relocations. */ + const Elf_Rel *rel; + for (rel = obj->rel; rel < obj->rellim; ++rel) { + Elf_RelA ourrela; + ourrela.r_info = rel->r_info; + ourrela.r_offset = rel->r_offset; + ourrela.r_addend = *(Elf_Word *) (obj->relocbase + rel->r_offset); + + if (_rtld_relocate_nonplt_object(obj, &ourrela) < 0) + ok = 0; + } + } + + if (obj->rela != NULL) { + /* Process the non-PLT relocations. */ + const Elf_RelA *rela; + for (rela = obj->rela; rela < obj->relalim; ++rela) { + if (_rtld_relocate_nonplt_object(obj, rela) < 0) + ok = 0; + } + } + + 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, xstrerror(errno)); + return -1; + } + } + + /* Process the PLT relocations. */ + if (obj->pltrel != NULL) { + const Elf_Rel *rel; + for (rel = obj->pltrel; rel < obj->pltrellim; ++rel) { + Elf_RelA ourrela; + ourrela.r_info = rel->r_info; + ourrela.r_offset = rel->r_offset; + ourrela.r_addend = *(Elf_Word *) (obj->relocbase + rel->r_offset); + if (_rtld_relocate_plt_object(obj, &ourrela, bind_now) < 0) + ok = 0; + } + } + + if (obj->pltrela != NULL) { + const Elf_RelA *rela; + for (rela = obj->pltrela; rela < obj->pltrelalim; ++rela) { + if (_rtld_relocate_plt_object(obj, rela, bind_now) < 0) + ok = 0; + } + } + + if (!ok) + return -1; + + + /* Set some sanity-checking numbers in the Obj_Entry. */ + obj->magic = RTLD_MAGIC; + obj->version = RTLD_VERSION; + + /* Fill in the dynamic linker entry points. */ + obj->dlopen = _rtld_dlopen; + obj->dlsym = _rtld_dlsym; + obj->dlerror = _rtld_dlerror; + obj->dlclose = _rtld_dlclose; + + /* Set the special PLTGOT entries. */ + if (obj->pltgot != NULL) { +#if defined(__i386__) + obj->pltgot[1] = (Elf_Addr) obj; + obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; +#endif +#if defined(__alpha__) + /* This function will be called to perform the relocation. */ + obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; + /* Identify this shared object */ + obj->pltgot[3] = (Elf_Addr) obj; +#endif + } + } + + return 0; +} diff --git a/libexec/ld.elf_so/rtld.c b/libexec/ld.elf_so/rtld.c new file mode 100644 index 000000000000..302f04d34cc7 --- /dev/null +++ b/libexec/ld.elf_so/rtld.c @@ -0,0 +1,568 @@ +/* $NetBSD: rtld.c,v 1.1 1996/12/16 20:38:03 cgd Exp $ */ + +/* + * Copyright 1996 John D. Polstra. + * Copyright 1996 Matt Thomas + * 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 John Polstra. + * 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. + */ + +/* + * Dynamic linker for ELF. + * + * John Polstra . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "debug.h" +#include "rtld.h" + +/* FIXME - Theses don't belong here. */ +#define RTLD_LAZY 1 +#define RTLD_NOW 2 + +/* + * Debugging support. + */ + +typedef void (*funcptr)(void); + +/* + * Function declarations. + */ +static void _rtld_init(caddr_t); +static void _rtld_exit(void); + +/* + * Data declarations. + */ +static char *error_message; /* Message for dlopen(), or NULL */ + +struct r_debug _rtld_debug; /* for GDB; */ +bool _rtld_trust; /* False for setuid and setgid programs */ +Obj_Entry *_rtld_objlist; /* Head of linked list of shared objects */ +Obj_Entry **_rtld_objtail; /* Link field of last object in list */ +Obj_Entry *_rtld_objmain; /* The main program shared object */ +Obj_Entry _rtld_objself; /* The dynamic linker shared object */ + +Search_Path *_rtld_paths; +/* + * Global declarations normally provided by crt0. + */ +char *__progname; +char **environ; + +#ifdef OLD_GOT +extern Elf_Addr _GLOBAL_OFFSET_TABLE_[]; +#else +extern Elf_Addr _GLOBAL_OFFSET_TABLE_[]; +extern Elf_Dyn _DYNAMIC; +#endif + +static void +_rtld_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 +_rtld_call_init_functions( + Obj_Entry *first) +{ + if (first != NULL) { + _rtld_call_init_functions(first->next); + if (first->init != NULL) + (*first->init)(); + } +} + +/* + * 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 +_rtld_init( + caddr_t mapbase) +{ + _rtld_add_paths(&_rtld_paths, RTLD_DEFAULT_LIBRARY_PATH); + + /* Conjure up an Obj_Entry structure for the dynamic linker. */ + + _rtld_objself.path = _PATH_RTLD; + _rtld_objself.rtld = true; + _rtld_objself.mapbase = mapbase; + _rtld_objself.relocbase = mapbase; + _rtld_objself.pltgot = NULL; +#ifdef OLD_GOT + _rtld_objself.dynamic = (Elf_Dyn *) _GLOBAL_OFFSET_TABLE_[0]; +#else + _rtld_objself.dynamic = &_DYNAMIC; +#endif + + _rtld_digest_dynamic(&_rtld_objself); +#ifdef __alpha__ +/* XXX XXX XXX */ +_rtld_objself.pltgot = NULL; +#endif + assert(_rtld_objself.needed == NULL); + assert(!_rtld_objself.textrel); + + /* Set up the _rtld_objlist pointer, so that rtld symbols can be found. */ + _rtld_objlist = &_rtld_objself; + + _rtld_relocate_objects(&_rtld_objself, true); + + /* Make the object list empty again. */ + _rtld_objlist = NULL; + _rtld_objtail = &_rtld_objlist; + + _rtld_debug.r_brk = _rtld_debug_state; + _rtld_debug.r_state = RT_CONSISTENT; +} + +/* + * Cleanup procedure. It will be called (by the atexit() mechanism) just + * before the process exits. + */ +static void +_rtld_exit(void) +{ + dbg("rtld_exit()"); + + _rtld_call_fini_functions(_rtld_objlist->next); +} + +/* + * Main entry point for dynamic linking. The 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. + * + * This function returns the entry point for the main program in %eax, + * and the dynamic linker's exit procedure in %edx. We accomplish this + * by declaring the return value to have the 64-bit type "long long". + * Such values are returned with their most-significant 32 bits in %edx, + * and their least-significant 32 bits in %eax. + */ +Elf_Addr +_rtld( + Elf_Word *sp) +{ + const AuxInfo *aux_info[AUX_count]; + int i = 0; + char **env; + const AuxInfo *aux; + const AuxInfo *auxp; + Elf_Word * const osp = sp; + bool bind_now = 0; + const char *ld_bind_now; + const char **argv; + + /* + * On entry, the dynamic linker itself has not been relocated yet. + * Be very careful not to reference any global data until after + * _rtld_init 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. */ + /* first Elf_Word reserved to address of exit routine */ +#ifdef RTLD_DEBUG + xprintf("sp = %p, argc = %d, argv = %p <%s>\n", sp, sp[2], &sp[3], sp[3]); + xprintf("got is at %p, dynamic is at %p\n", _GLOBAL_OFFSET_TABLE_, &_DYNAMIC); + debug = 1; + xprintf("_ctype_ is %p\n", _ctype_); +#endif + + sp += 2; /* skip over return argument space */ + argv = (const char **) &sp[1]; + sp += sp[0] + 2; /* Skip over argc, arguments, and NULL terminator */ + env = (char **) sp; + while (*sp++ != 0) { /* Skip over environment, and NULL terminator */ +#ifdef RTLD_DEBUG + xprintf("env[%d] = %p\n", i++, sp[-1]); +#endif + } + aux = (const AuxInfo *) sp; + + /* Digest the auxiliary vector. */ + for (i = 0; i < AUX_count; ++i) + aux_info[i] = NULL; + for (auxp = aux; auxp->au_id != AUX_null; ++auxp) { + if (auxp->au_id < AUX_count) + aux_info[auxp->au_id] = auxp; + } + + /* Initialize and relocate ourselves. */ + assert(aux_info[AUX_base] != NULL); + _rtld_init((caddr_t) aux_info[AUX_base]->au_v); + +#ifdef RTLD_DEBUG + xprintf("_ctype_ is %p\n", _ctype_); +#endif + if (aux_info[AUX_debug] != NULL) /* Set debugging level */ + debug = aux_info[AUX_debug]->au_v; + + __progname = _rtld_objself.path; + environ = env; + + _rtld_trust = geteuid() == getuid() && getegid() == getgid(); + + ld_bind_now = getenv("LD_BIND_NOW"); + if (ld_bind_now != NULL && *ld_bind_now != '\0') + bind_now = true; + if (_rtld_trust) { + const char *ld_debug = getenv("LD_DEBUG"); + if (ld_debug != NULL && *ld_debug != '\0') + debug = 1; + _rtld_add_paths(&_rtld_paths, getenv("LD_LIBRARY_PATH")); + } + + dbg("%s is initialized, base address = %p", __progname, + (caddr_t) aux_info[AUX_base]->au_v); + + /* + * Load the main program, or process its program header if it is + * already loaded. + */ + if (aux_info[AUX_execfd] != NULL) { /* Load the main program. */ + int fd = aux_info[AUX_execfd]->au_v; + dbg("loading main program"); + _rtld_objmain = _rtld_map_object(argv[0], fd); + close(fd); + if (_rtld_objmain == NULL) + _rtld_die(); + } else { /* Main program already loaded. */ + const Elf_Phdr *phdr; + int phnum; + caddr_t entry; + + dbg("processing main program's program header"); + assert(aux_info[AUX_phdr] != NULL); + phdr = (const Elf_Phdr *) aux_info[AUX_phdr]->au_v; + assert(aux_info[AUX_phnum] != NULL); + phnum = aux_info[AUX_phnum]->au_v; + assert(aux_info[AUX_phent] != NULL); + assert(aux_info[AUX_phent]->au_v == sizeof(Elf_Phdr)); + assert(aux_info[AUX_entry] != NULL); + entry = (caddr_t) aux_info[AUX_entry]->au_v; + _rtld_objmain = _rtld_digest_phdr(phdr, phnum, entry); + } + + _rtld_objmain->path = xstrdup("main program"); + _rtld_objmain->mainprog = true; + _rtld_digest_dynamic(_rtld_objmain); + + _rtld_linkmap_add(_rtld_objmain); + _rtld_linkmap_add(&_rtld_objself); + + /* Link the main program into the list of objects. */ + *_rtld_objtail = _rtld_objmain; + _rtld_objtail = &_rtld_objmain->next; + ++_rtld_objmain->refcount; + + dbg("loading needed objects"); + if (_rtld_load_needed_objects(_rtld_objmain) == -1) + _rtld_die(); + + dbg("relocating objects"); + if (_rtld_relocate_objects(_rtld_objmain, bind_now) == -1) + _rtld_die(); + + dbg("doing copy relocations"); + if (_rtld_do_copy_relocations(_rtld_objmain) == -1) + _rtld_die(); + + dbg("calling _init functions"); + _rtld_call_init_functions(_rtld_objmain->next); + + dbg("transferring control to program entry point = %p", + _rtld_objmain->entry); + + /* Return with the entry point and the exit procedure in at the top of + * stack. + */ + + _rtld_debug_state(); /* say hello to gdb! */ + + ((void **) osp)[0] = _rtld_exit; + ((void **) osp)[1] = _rtld_objmain; + return (Elf_Addr) _rtld_objmain->entry; +} + +void +_rtld_die( + void) +{ + const char *msg = _rtld_dlerror(); + + if (msg == NULL) + msg = "Fatal error"; + xerrx(1, "%s\n", msg); +} + +static Obj_Entry * +_rtld_dlcheck( + void *handle) +{ + Obj_Entry *obj; + + for (obj = _rtld_objlist; obj != NULL; obj = obj->next) + if (obj == (Obj_Entry *) handle) + break; + + if (obj == NULL || obj->dl_refcount == 0) { + xwarnx("Invalid shared object handle %p", handle); + return NULL; + } + return obj; +} + +static void +_rtld_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) + _rtld_unref_object_dag(needed->obj); + } +} + +int +_rtld_dlclose( + void *handle) +{ + Obj_Entry *root = _rtld_dlcheck(handle); + + if (root == NULL) + return -1; + + _rtld_debug.r_state = RT_DELETE; + _rtld_debug_state(); + + --root->dl_refcount; + _rtld_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 = _rtld_objlist->next; obj != NULL; obj = obj->next) + if (obj->refcount == 0 && obj->fini != NULL) + (*obj->fini)(); + + /* Unmap all objects that are no longer referenced. */ + linkp = &_rtld_objlist->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); + } + _rtld_linkmap_delete(obj); + *linkp = obj->next; + free(obj); + } else + linkp = &obj->next; + } + } + + _rtld_debug.r_state = RT_CONSISTENT; + _rtld_debug_state(); + + return 0; +} + +char * +_rtld_dlerror( + void) +{ + char *msg = error_message; + error_message = NULL; + return msg; +} + +void * +_rtld_dlopen( + const char *name, + int mode) +{ + Obj_Entry **old_obj_tail = _rtld_objtail; + Obj_Entry *obj = NULL; + + _rtld_debug.r_state = RT_ADD; + _rtld_debug_state(); + + if (name == NULL) { + obj = _rtld_objmain; + } else { + char *path = _rtld_find_library(name, NULL); + if (path != NULL) + obj = _rtld_load_object(path); + } + + if (obj != NULL) { + ++obj->dl_refcount; + if (*old_obj_tail != NULL) { /* We loaded something new. */ + assert(*old_obj_tail == obj); + + /* FIXME - Clean up properly after an error. */ + if (_rtld_load_needed_objects(obj) == -1) { + --obj->dl_refcount; + obj = NULL; + } else if (_rtld_relocate_objects(obj, mode == RTLD_NOW) == -1) { + --obj->dl_refcount; + obj = NULL; + } else { + _rtld_call_init_functions(obj); + } + } + } + + _rtld_debug.r_state = RT_CONSISTENT; + _rtld_debug_state(); + + return obj; +} + +void * +_rtld_dlsym( + void *handle, + const char *name) +{ + const Obj_Entry *obj = _rtld_dlcheck(handle); + const Elf_Sym *def; + const Obj_Entry *defobj; + + if (obj == NULL) + return NULL; + + /* + * FIXME - This isn't correct. The search should include the whole + * DAG rooted at the given object. + */ + def = _rtld_find_symdef(_rtld_objlist, 0, name, obj, &defobj, false); + if (def != NULL) + return defobj->relocbase + def->st_value; + + _rtld_error("Undefined symbol \"%s\"", name); + return NULL; +} + +/* + * 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); + xvsnprintf(buf, sizeof buf, fmt, ap); + error_message = buf; + va_end(ap); +} + +void +_rtld_debug_state( + void) +{ + /* do nothing */ +} + +void +_rtld_linkmap_add( + Obj_Entry *obj) +{ + struct link_map *l = &obj->linkmap; + struct link_map *prev; + + obj->linkmap.l_name = obj->path; + obj->linkmap.l_addr = obj->mapbase; + obj->linkmap.l_ld = obj->dynamic; + + if (_rtld_debug.r_map == NULL) { + _rtld_debug.r_map = l; + return; + } + + for (prev = _rtld_debug.r_map; prev->l_next != NULL; prev = prev->l_next) + ; + l->l_prev = prev; + prev->l_next = l; + l->l_next = NULL; +} + +void +_rtld_linkmap_delete( + Obj_Entry *obj) +{ + struct link_map *l = &obj->linkmap; + + if (l->l_prev == NULL) { + if ((_rtld_debug.r_map = l->l_next) != NULL) + l->l_next->l_prev = NULL; + return; + } + + if ((l->l_prev->l_next = l->l_next) != NULL) + l->l_next->l_prev = l->l_prev; +} + diff --git a/libexec/ld.elf_so/rtld.h b/libexec/ld.elf_so/rtld.h new file mode 100644 index 000000000000..3e16f3f6b4c0 --- /dev/null +++ b/libexec/ld.elf_so/rtld.h @@ -0,0 +1,216 @@ +/* $NetBSD: rtld.h,v 1.1 1996/12/16 20:38:04 cgd Exp $ */ + +/* + * Copyright 1996 John D. Polstra. + * Copyright 1996 Matt Thomas + * 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 John Polstra. + * 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. + */ + +#ifndef RTLD_H /* { */ +#define RTLD_H 1 + +#include +#include +#include +#include +#include "rtldenv.h" +#ifdef RTLD_LOADER +#include "link.h" +#endif + +#define RTLD_DEFAULT_LIBRARY_PATH \ + "/usr/localelf/lib:/usr/lib:/usr/local/lib" + +#if 0 +#define SVR4_LIBDIR "/usr/lib" +#endif + +#define LIBDIRLEN (sizeof LIBDIR - 1) +#define SVR4_LIBDIRLEN (sizeof SVR4_LIBDIR - 1) + +#define PAGESIZE CLBYTES +#define round_down(x) ((x) & ~(PAGESIZE-1)) +#define round_up(x) round_down((x) + PAGESIZE - 1) + +#define NEW(type) ((type *) xmalloc(sizeof(type))) +#define CNEW(type) ((type *) xcalloc(sizeof(type))) + +/* + * C++ has mandated the use of the following keywords for its new boolean + * type. We might as well follow their lead. + */ +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; + +typedef struct _rtld_search_path_t { + struct _rtld_search_path_t *sp_next; + const char *sp_path; + size_t sp_pathlen; +} Search_Path; + +/* + * Shared object descriptor. + * + * Items marked with "(%)" are dynamically allocated, and must be freed + * when the structure is destroyed. + */ + +#define RTLD_MAGIC 0xd550b87a +#define RTLD_VERSION 1 + +typedef struct Struct_Obj_Entry { + 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 */ + Elf_Addr vaddrbase; /* Base address in shared object file */ + caddr_t relocbase; /* Relocation constant = mapbase - vaddrbase */ + Elf_Dyn *dynamic; /* Dynamic section */ + caddr_t entry; /* Entry point */ + const Elf_Phdr *phdr; /* Program header if it is mapped, else NULL */ + size_t phsize; /* Size of program header in bytes */ + + /* Items from the dynamic section. */ + Elf_Addr *pltgot; /* PLTGOT table */ + const Elf_Rel *rel; /* Relocation entries */ + const Elf_Rel *rellim; /* Limit of Relocation entries */ + const Elf_RelA *rela; /* Relocation entries */ + const Elf_RelA *relalim; /* Limit of Relocation entries */ + const Elf_Rel *pltrel; /* PLT relocation entries */ + const Elf_Rel *pltrellim; /* Limit of PLT relocation entries */ + const Elf_RelA *pltrela; /* PLT relocation entries */ + const Elf_RelA *pltrelalim; /* Limit of PLT relocation entries */ + const Elf_Sym *symtab; /* Symbol table */ + const char *strtab; /* String table */ + unsigned long strsize; /* Size in bytes of string table */ + + const Elf_Word *buckets; /* Hash table buckets array */ + unsigned long nbuckets; /* Number of buckets */ + const Elf_Word *chains; /* Hash table chain array */ + unsigned long nchains; /* Number of chains */ + + Search_Path *rpaths; /* 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 */ + + /* Entry points for dlopen() and friends. */ + void *(*dlopen)(const char *, int); + void *(*dlsym)(void *, const char *); + char *(*dlerror)(void); + int (*dlclose)(void *); + + int mainprog : 1; /* True if this is the main program */ + int rtld : 1; /* True if this is the dynamic linker */ + int textrel : 1; /* True if there are relocations to text seg */ + int symbolic : 1; /* True if generated with "-Bsymbolic" */ + int printed : 1; /* True if ldd has printed it */ + +#ifdef RTLD_LOADER + struct link_map linkmap; /* for GDB */ +#endif +} Obj_Entry; + +extern struct r_debug _rtld_debug; +extern Obj_Entry *_rtld_objlist; +extern Obj_Entry **_rtld_objtail; +extern Obj_Entry _rtld_objself; +extern Search_Path *_rtld_paths; +extern bool _rtld_trust; +extern const char *_rtld_error_message; + +/* rtld_start.S */ +extern void _rtld_bind_start(void); + +/* rtld.c */ + +extern void _rtld_error(const char *, ...); +extern void _rtld_die(void); + +extern char *_rtld_dlerror(void); +extern void *_rtld_dlopen(const char *, int); +extern void *_rtld_dlsym(void *, const char *); +extern int _rtld_dlclose(void *); + +extern void _rtld_debug_state(void); + +extern void _rtld_linkmap_add(Obj_Entry *); +extern void _rtld_linkmap_delete(Obj_Entry *); + +/* headers.c */ + +extern void _rtld_digest_dynamic(Obj_Entry *); +extern Obj_Entry *_rtld_digest_phdr(const Elf_Phdr *, int, caddr_t); + +/* load.c */ + +extern Obj_Entry *_rtld_load_object(char *path); +extern int _rtld_load_needed_objects(Obj_Entry *); + +/* path.c */ + +extern void _rtld_add_paths(Search_Path **, const char *); + +/* reloc.c */ +extern int _rtld_do_copy_relocations(const Obj_Entry *); +extern caddr_t _rtld_bind(const Obj_Entry *, Elf_Word); +extern int _rtld_relocate_objects(Obj_Entry *, bool); + +/* search.c */ + +extern char *_rtld_find_library(const char *, const Obj_Entry *); + +/* symbol.c */ +extern unsigned long _rtld_elf_hash(const char *); +extern const Elf_Sym *_rtld_symlook_obj(const char *, unsigned long, + const Obj_Entry *, bool); +extern const Elf_Sym *_rtld_find_symdef(const Obj_Entry *, Elf_Word, + const char *, const Obj_Entry *, const Obj_Entry **, bool); + +/* map_object.c */ +extern Obj_Entry *_rtld_map_object(const char *, int); + +#endif /* } */ diff --git a/libexec/ld.elf_so/rtldenv.h b/libexec/ld.elf_so/rtldenv.h new file mode 100644 index 000000000000..453faae08522 --- /dev/null +++ b/libexec/ld.elf_so/rtldenv.h @@ -0,0 +1,70 @@ +/* $NetBSD: rtldenv.h,v 1.1 1996/12/16 20:38:05 cgd Exp $ */ + +/* + * Copyright 1996 Matt Thomas + * 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. 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. + */ + +#ifndef _RTLDENV_H +#define _RTLDENV_H + +#include +#include + +extern void *xcalloc(size_t); +extern void *xmalloc(size_t); +extern char *xstrdup(const char *); + +#ifdef RTLD_LOADER +extern void xprintf(const char *fmt, ...); +extern void xvprintf(const char *fmt, va_list ap); +extern size_t xvsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap); +extern void xwarn(const char *fmt, ...); +extern void xwarnx(const char *fmt, ...); +extern void xerr(int eval, const char *fmt, ...); +extern void xerrx(int eval, const char *fmt, ...); +extern void xassert(const char *file, int line, const char *failedexpr); +extern const char *xstrerror(int error); + +#define assert(cond) ((cond) \ + ? (void) 0 :\ + (xassert(__FILE__, __LINE__, #cond "\n"), abort())) +#else +#include +#include +#include + +#define xprintf printf +#define xvprintf vprintf +#define xvsnprintf vsnprintf +#define xwarn warn +#define xwarnx warnx +#define xerr err +#define xerrx errx +#define xassert assert +#define xstrerror strerror +#endif + +#endif /* _RTLDENV_H */ diff --git a/libexec/ld.elf_so/search.c b/libexec/ld.elf_so/search.c new file mode 100644 index 000000000000..0e621141a039 --- /dev/null +++ b/libexec/ld.elf_so/search.c @@ -0,0 +1,347 @@ +/* $NetBSD: search.c,v 1.1 1996/12/16 20:38:05 cgd Exp $ */ + +/* + * Copyright 1996 Matt Thomas + * 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 John Polstra. + * 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. + */ + +/* + * Dynamic linker for ELF. + * + * John Polstra . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +#define CONCAT(x,y) __CONCAT(x,y) +#define ELFNAME(x) CONCAT(elf,CONCAT(ELFSIZE,CONCAT(_,x))) +#define ELFNAME2(x,y) CONCAT(x,CONCAT(_elf,CONCAT(ELFSIZE,CONCAT(_,y)))) +#define ELFNAMEEND(x) CONCAT(x,CONCAT(_elf,ELFSIZE)) +#define ELFDEFNNAME(x) CONCAT(ELF,CONCAT(ELFSIZE,CONCAT(_,x))) + +/* + * Data declarations. + */ + +typedef struct { + const char *si_name; + const char *si_best_name; + char *si_best_fullpath; + const Search_Path *si_best_path; + size_t si_namelen; + int si_desired_major; + int si_desired_minor; + int si_best_major; + int si_best_minor; + unsigned si_exact : 1; +} Search_Info; + +typedef enum { + Search_FoundNothing, + Search_FoundLower, + Search_FoundHigher, + Search_FoundExact +} Search_Result; + +static bool +_rtld_check_library( + const Search_Path *sp, + const char *name, + size_t namelen, + char **fullpath_p) +{ + struct stat mystat; + char *fullpath; + Elf_Ehdr ehdr; + int fd; + + fullpath = xmalloc(sp->sp_pathlen + 1 + namelen + 1); + strncpy(fullpath, sp->sp_path, sp->sp_pathlen); + fullpath[sp->sp_pathlen] = '/'; + strcpy(&fullpath[sp->sp_pathlen + 1], name); + + dbg(" Trying \"%s\"", fullpath); + if (stat(fullpath, &mystat) >= 0 && S_ISREG(mystat.st_mode)) { + if ((fd = open(fullpath, O_RDONLY)) >= 0) { + if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) + goto lose; + + /* Elf_e_ident includes class */ + if (memcmp(Elf_e_ident, ehdr.e_ident, Elf_e_siz) != 0) + goto lose; + + switch (ehdr.e_machine) { + ELFDEFNNAME(MACHDEP_ID_CASES) + + default: + goto lose; + } + + if (ehdr.e_ident[Elf_ei_version] != Elf_ev_current || + ehdr.e_version != Elf_ev_current || + ehdr.e_ident[Elf_ei_data] != ELFDEFNNAME(MACHDEP_ENDIANNESS) || + ehdr.e_type != Elf_et_dyn) + goto lose; + + if (*fullpath_p != NULL) + free(*fullpath_p); + *fullpath_p = fullpath; + return true; + +lose: + close(fd); + } + } + + free(fullpath); + return false; +} + +static Search_Result +_rtld_search_directory( + const Search_Path *sp, + Search_Info *si) +{ + struct dirent *entry; + DIR *dirp; + Search_Result result = Search_FoundNothing; + +dbg("_rtld_search_directory"); + if (sp->sp_path == NULL || sp->sp_path[0] == '\0') + return result; + +dbg("_rtld_search_directory 2"); + if ((dirp = opendir(sp->sp_path)) == NULL) { + dbg("_rtld_search_directory 2.1"); + return result; + } + +dbg("_rtld_search_directory 3"); + while ((entry = readdir(dirp)) != NULL) { + long major = -1; + long minor = -1; + if (strncmp(entry->d_name, si->si_name, si->si_namelen)) + continue; + /* + * We are matching libfoo.so only (no more info). Only take + * it as a last resort. + */ + if (si->si_exact) { + if (strcmp(entry->d_name, si->si_name)) + continue; +#ifdef notyet + } else if (entry->d_namlen == si->si_namelen) { + if (si->si_best_path != NULL || si->si_best_major != -1) + continue; +#endif + } else { + char *cp; + /* + * We expect (demand!) that it be of the form + * "libfoo.so." + */ + if (entry->d_name[si->si_namelen] != '.') + continue; + /* + * This file has a least a major number (well, maybe not if it + * has a name of "libfoo.so." but treat that as equivalent to 0. + * It had better match what we are looking for. + */ + major = strtol(&entry->d_name[si->si_namelen+1], &cp, 10); + if (major < 0 || (cp[0] != '\0' && cp[0] != '.') + || &entry->d_name[si->si_namelen+1] == cp) + continue; + if (cp[0] == '.') { + char *cp2; + minor = strtol(&cp[1], &cp2, 10); + if (minor < 0 || cp2[0] != '\0' || cp == cp2) + continue; + } else { + minor = 0; + } + if (major != si->si_desired_major || minor <= si->si_best_minor) + continue; + } + /* + * We have a better candidate... + */ + if (!_rtld_check_library(sp, entry->d_name, entry->d_namlen, + &si->si_best_fullpath)) + continue; + + si->si_best_name = &si->si_best_fullpath[sp->sp_pathlen + 1]; + si->si_best_major = major; + si->si_best_minor = minor; + si->si_best_path = sp; + + if (si->si_exact || si->si_best_minor == si->si_desired_minor) + result = Search_FoundExact; + else if (si->si_best_minor > si->si_desired_minor) + result = Search_FoundHigher; + else + result = Search_FoundLower; + + /* + * We were looking for, and found, an exact match. We're done. + */ + if (si->si_exact) + break; + } + + dbg("found %s (%d.%d) match for %s (%d.%d) -> %s", + result == Search_FoundNothing ? "no" + : result == Search_FoundLower ? "lower" + : result == Search_FoundExact ? "exact" : "higher", + si->si_best_major, si->si_best_minor, + si->si_name, + si->si_desired_major, si->si_desired_minor, + si->si_best_fullpath ? si->si_best_fullpath : sp->sp_path); + + closedir(dirp); + return result; +} + +static char * +_rtld_search_library_paths( + const char *name, + Search_Path *paths, + const Search_Path *rpaths) +{ + Search_Info info; + Search_Path *path; + const char *cp; + Search_Result result = Search_FoundNothing; + + memset(&info, 0, sizeof(info)); + info.si_name = name; + info.si_desired_major = -1; + info.si_desired_minor = -1; + info.si_best_major = -1; + info.si_best_minor = -1; + + cp = strstr(name, ".so"); + if (cp == NULL) { + info.si_exact = true; + } else { + cp += sizeof(".so") - 1; + info.si_namelen = cp - name; + if (cp[0] != '.') { + info.si_exact = true; + } else { + info.si_desired_major = atoi(&cp[1]); + if ((cp = strchr(&cp[1], '.')) != NULL) { + info.si_desired_minor = atoi(&cp[1]); + } else { + info.si_desired_minor = 0; + } + } + } + + if (rpaths != NULL && result < Search_FoundHigher) { /* Exact? */ + dbg(" checking rpaths.."); + for (; rpaths != NULL; rpaths = rpaths->sp_next) { + dbg(" in \"%s\"", rpaths->sp_path); + result = _rtld_search_directory(rpaths, &info); + if (result >= Search_FoundHigher) /* Exact? */ + break; + } + } + if (result < Search_FoundHigher) { /* Exact? */ + dbg(" checking default paths.."); + for (path = paths; path != NULL; path = path->sp_next) { + dbg(" in \"%s\"", path->sp_path); + result = _rtld_search_directory(path, &info); + if (result >= Search_FoundHigher) /* Exact? */ + break; + } + } + + if (result >= Search_FoundHigher) + return info.si_best_fullpath; + + if (info.si_best_fullpath != NULL) + free(info.si_best_fullpath); + return NULL; +} + +/* + * 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. + */ +char * +_rtld_find_library( + const char *name, + const Obj_Entry *refobj) +{ + char *pathname; + + if (strchr(name, '/') != NULL) { /* Hard coded pathname */ + if (name[0] != '/' && !_rtld_trust) { + _rtld_error("Absolute pathname required for shared object \"%s\"", + name); + return NULL; + } +#ifdef SVR4_LIBDIR + if (strncmp(name, SVR4_LIBDIR, SVR4_LIBDIRLEN) == 0 + && name[SVR4_LIBDIRLEN] == '/') { /* In "/usr/lib" */ + /* Map hard-coded "/usr/lib" onto our ELF library directory. */ + pathname = xmalloc(strlen(name) + LIBDIRLEN - SVR4_LIBDIRLEN + 1); + strcpy(pathname, LIBDIR); + strcpy(pathname + LIBDIRLEN, name + SVR4_LIBDIRLEN); + return pathname; + } +#endif /* SVR4_LIBDIR */ + return xstrdup(name); + } + + dbg(" Searching for \"%s\" (%p)", name, refobj); + + pathname = _rtld_search_library_paths(name, _rtld_paths, + refobj ? refobj->rpaths : NULL); + if (pathname == NULL) + _rtld_error("Shared object \"%s\" not found", name); + return pathname; +} diff --git a/libexec/ld.elf_so/symbol.c b/libexec/ld.elf_so/symbol.c new file mode 100644 index 000000000000..e3bc4c0706d2 --- /dev/null +++ b/libexec/ld.elf_so/symbol.c @@ -0,0 +1,171 @@ +/* $NetBSD: symbol.c,v 1.1 1996/12/16 20:38:06 cgd Exp $ */ + +/* + * Copyright 1996 John D. Polstra. + * Copyright 1996 Matt Thomas + * 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 John Polstra. + * 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. + */ + +/* + * Dynamic linker for ELF. + * + * John Polstra . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +/* + * Hash function for symbol table lookup. Don't even think about changing + * this. It is specified by the System V ABI. + */ +unsigned long +_rtld_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; +} + +/* + * 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. + */ +const Elf_Sym * +_rtld_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 != ELF_SYM_UNDEFINED) { + const Elf_Sym *symp; + const char *strp; + + assert(symnum < obj->nchains); + symp = obj->symtab + symnum; + strp = obj->strtab + symp->st_name; +#if 0 + assert(symp->st_name != 0); +#endif + if (strcmp(name, strp) == 0) { + if (symp->st_shndx != Elf_eshn_undefined + || (!in_plt && symp->st_value != 0 && + ELF_SYM_TYPE(symp->st_info) == Elf_estt_func)) { + return symp; + } + } + + symnum = obj->chains[symnum]; + } + + 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. + */ +const Elf_Sym * +_rtld_find_symdef( + const Obj_Entry *obj_list, + Elf_Word r_info, + const char *name, + const Obj_Entry *refobj, + const Obj_Entry **defobj_out, + bool in_plt) +{ + Elf_Word symnum = ELF_R_SYM(r_info); + const Elf_Sym *ref; + const Obj_Entry *obj; + unsigned long hash; + + if (name == NULL) { + ref = refobj->symtab + symnum; + name = refobj->strtab + ref->st_name; + } + hash = _rtld_elf_hash(name); + + if (refobj->symbolic) { /* Look first in the referencing object */ + const Elf_Sym *def = _rtld_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. + */ + for (obj = obj_list; obj != NULL; obj = obj->next) { + if (obj != refobj || !refobj->symbolic) { + const Elf_Sym *def = _rtld_symlook_obj(name, hash, obj, in_plt); + if (def != NULL) { + *defobj_out = obj; + return def; + } + } + } + + if (ELF_R_TYPE(r_info) != R_TYPE(NONE)) { + _rtld_error("%s: Undefined %ssymbol \"%s\" (reloc type = %d, symnum = %d)", + refobj->path, in_plt ? "PLT " : "", name, + ELF_R_TYPE(r_info), symnum); + } + return NULL; +} diff --git a/libexec/ld.elf_so/xmalloc.c b/libexec/ld.elf_so/xmalloc.c new file mode 100644 index 000000000000..260312e353d3 --- /dev/null +++ b/libexec/ld.elf_so/xmalloc.c @@ -0,0 +1,61 @@ +/* $NetBSD: xmalloc.c,v 1.1 1996/12/16 20:38:07 cgd Exp $ */ + +/* + * Copyright 1996 John D. Polstra. + * Copyright 1996 Matt Thomas + * 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 John Polstra. + * 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 "rtldenv.h" +#include +#include +#include + +void * +xcalloc(size_t size) +{ + return memset(xmalloc(size), 0, size); +} + +void * +xmalloc(size_t size) +{ + void *p = malloc(size); + if(p == NULL) + xerr(1, "Out of memory"); + return p; +} + +char * +xstrdup(const char *s) +{ + char *p = strdup(s); + if(p == NULL) + xerr(1, "Out of memory"); + return p; +} diff --git a/libexec/ld.elf_so/xprintf.c b/libexec/ld.elf_so/xprintf.c new file mode 100644 index 000000000000..b40c760fce9e --- /dev/null +++ b/libexec/ld.elf_so/xprintf.c @@ -0,0 +1,272 @@ +/* $NetBSD: xprintf.c,v 1.1 1996/12/16 20:38:07 cgd Exp $ */ + +/* + * Copyright 1996 Matt Thomas + * 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. 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 "rtldenv.h" +#include +#include +#include + +/* + * Non-mallocing printf, for use by malloc and rtld itself. + * This avoids putting in most of stdio. + * + * deals withs formats %x, %p, %s, and %d. + */ +size_t +xvsnprintf( + char *buf, + size_t buflen, + const char *fmt, + va_list ap) +{ + char *bp = buf; + char * const ep = buf + buflen - 4; + + while (*fmt != NULL && bp < ep) { + switch (*fmt) { + case '\\': { + if (fmt[1] != '\0') + *bp++ = *++fmt; + continue; + } + case '%': { + switch (fmt[1]) { + case 'd': case 'u': { + int ival; + unsigned uval; + char digits[sizeof(int) * 3], *dp = digits; + + if (fmt[1] == 'd') { + ival = va_arg(ap, int); + if (ival < 0) { + if ((ival << 1) == 0) { + /* + * We can't flip the sign of this since + * it's can't represented as a postive + * number in two complement, handle the + * first digit. After that, it can be + * flipped since it is now not 2^(n-1). + */ + *dp++ = '0' - (ival % 10); + ival /= 10; + } + *bp++ = '-'; + uval = -ival; + } else { + uval = ival; + } + } else { + uval = va_arg(ap, unsigned); + } + do { + *dp++ = '0' + (uval % 10); + uval /= 10; + } while (uval != 0); + do { + *bp++ = *--dp; + } while (dp != digits && bp < ep); + fmt += 2; + break; + } + case 'x': case 'p': { + unsigned long val = va_arg(ap, unsigned long); + unsigned long mask = ~(~0UL >> 4); + int bits = sizeof(val) * 8 - 4; + const char hexdigits[] = "0123456789abcdef"; + if (fmt[1] == 'p') { + *bp++ = '0'; + *bp++ = 'x'; + } + /* handle the border case */ + if (val == 0) { + *bp++ = '0'; + fmt += 2; + break; + } + /* suppress 0s */ + while ((val & mask) == 0) + bits -= 4, mask >>= 4; + + /* emit the hex digits */ + while (bits >= 0 && bp < ep) { + *bp++ = hexdigits[(val & mask) >> bits]; + bits -= 4, mask >>= 4; + } + fmt += 2; + break; + } + case 's': { + const char *str = va_arg(ap, const char *); + int len; + if (str == NULL) + str = "(null)"; + + len = strlen(str); + if (ep - bp < len) + len = ep - bp; + memcpy(bp, str, len); + bp += len; + fmt += 2; + break; + } + default: + *bp++ = *fmt; + break; + } + break; + } + default: + *bp++ = *fmt++; + break; + } + } + + + *bp = '\0'; + return bp - buf; +} + +void +xvprintf( + const char *fmt, + va_list ap) +{ + char buf[256]; + (void) write(2, buf, xvsnprintf(buf, sizeof(buf), fmt, ap)); +} + +void +xprintf( + const char *fmt, + ...) +{ + va_list ap; + va_start(ap, fmt); + + xvprintf(fmt, ap); + + va_end(ap); +} + +void +xsnprintf( + char *buf, + size_t buflen, + const char *fmt, + ...) +{ + va_list ap; + va_start(ap, fmt); + + xvprintf(fmt, ap); + + va_end(ap); +} + +const char * +xstrerror( + int error) +{ + if (error >= sys_nerr) { + static char buf[128]; + xsnprintf(buf, sizeof(buf), "Unknown error: %d", error); + return buf; + } + return sys_errlist[error]; +} + +void +xerrx( + int eval, + const char *fmt, + ...) +{ + va_list ap; + va_start(ap, fmt); + xvprintf(fmt, ap); + va_end(ap); + + exit(eval); +} + +void +xerr( + int eval, + const char *fmt, + ...) +{ + int saved_errno = errno; + + va_list ap; + va_start(ap, fmt); + xvprintf(fmt, ap); + va_end(ap); + + xprintf(": %s\n", xstrerror(saved_errno)); + exit(eval); +} + +void +xwarn( + const char *fmt, + ...) +{ + int saved_errno = errno; + + va_list ap; + va_start(ap, fmt); + xvprintf(fmt, ap); + va_end(ap); + + xprintf(": %s\n", xstrerror(saved_errno)); + errno = saved_errno; +} + +void +xwarnx( + const char *fmt, + ...) +{ + va_list ap; + va_start(ap, fmt); + xvprintf(fmt, ap); + va_end(ap); +} + +void +xassert( + const char *file, + int line, + const char *failedexpr) +{ + xprintf("assertion \"%s\" failed: file \"%s\", line %d\n", + failedexpr, file, line); + abort(); + /* NOTREACHED */ +}